Skip to content

Commit 4f060d8

Browse files
authored
[CL-509][PM-16190] Avoid double scrollbar appearing when default zoom is >100% (bitwarden#12427)
1 parent 6d65ce9 commit 4f060d8

File tree

10 files changed

+84
-37
lines changed

10 files changed

+84
-37
lines changed

apps/browser/src/platform/browser/browser-api.ts

+15
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,21 @@ export class BrowserApi {
165165
});
166166
}
167167

168+
/**
169+
* Fetch the currently open browser tab
170+
*/
171+
static async getCurrentTab(): Promise<chrome.tabs.Tab> | null {
172+
if (BrowserApi.isManifestVersion(3)) {
173+
return await chrome.tabs.getCurrent();
174+
}
175+
176+
return new Promise((resolve) =>
177+
chrome.tabs.getCurrent((tab) => {
178+
resolve(tab);
179+
}),
180+
);
181+
}
182+
168183
static async tabsQuery(options: chrome.tabs.QueryInfo): Promise<chrome.tabs.Tab[]> {
169184
return new Promise((resolve) => {
170185
chrome.tabs.query(options, (tabs) => {

apps/browser/src/platform/popup/browser-popup-utils.ts

+17-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import { BrowserApi } from "../browser/browser-api";
44

55
import { ScrollOptions } from "./abstractions/browser-popup-utils.abstractions";
6-
import { PopupWidthOptions } from "./layout/popup-width.service";
6+
import { PopupWidthOptions } from "./layout/popup-size.service";
77

88
class BrowserPopupUtils {
99
/**
@@ -24,6 +24,22 @@ class BrowserPopupUtils {
2424
return BrowserPopupUtils.urlContainsSearchParams(win, "uilocation", "popout");
2525
}
2626

27+
/**
28+
* Check if the current popup view is open inside of the current browser tab
29+
* (it is possible in Chrome to open the extension in a tab)
30+
*/
31+
static async isInTab() {
32+
const tabId = (await BrowserApi.getCurrentTab())?.id;
33+
34+
if (tabId === undefined || tabId === null) {
35+
return false;
36+
}
37+
38+
const result = BrowserApi.getExtensionViews({ tabId, type: "tab" });
39+
40+
return result.length > 0;
41+
}
42+
2743
/**
2844
* Identifies if the popup is within the single action popout.
2945
*

apps/browser/src/platform/popup/layout/popup-page.component.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.servic
88
templateUrl: "popup-page.component.html",
99
standalone: true,
1010
host: {
11-
class: "tw-h-full tw-flex tw-flex-col tw-flex-1 tw-overflow-y-hidden",
11+
class: "tw-h-full tw-flex tw-flex-col tw-overflow-y-hidden",
1212
},
1313
imports: [CommonModule],
1414
})

apps/browser/src/platform/popup/layout/popup-width.service.ts apps/browser/src/platform/popup/layout/popup-size.service.ts

+26-6
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ import {
77
POPUP_STYLE_DISK,
88
} from "@bitwarden/common/platform/state";
99

10+
import BrowserPopupUtils from "../browser-popup-utils";
11+
1012
/**
1113
*
1214
* Value represents width in pixels
@@ -25,10 +27,12 @@ const POPUP_WIDTH_KEY_DEF = new KeyDefinition<PopupWidthOption>(POPUP_STYLE_DISK
2527
});
2628

2729
/**
28-
* Updates the extension popup width based on a user setting
30+
* Handles sizing the popup based on available width/height, which can be affected by
31+
* user default zoom level.
32+
* Updates the extension popup width based on a user setting.
2933
**/
3034
@Injectable({ providedIn: "root" })
31-
export class PopupWidthService {
35+
export class PopupSizeService {
3236
private static readonly LocalStorageKey = "bw-popup-width";
3337
private readonly state = inject(GlobalStateProvider).get(POPUP_WIDTH_KEY_DEF);
3438

@@ -41,23 +45,39 @@ export class PopupWidthService {
4145
}
4246

4347
/** Begin listening for state changes */
44-
init() {
48+
async init() {
4549
this.width$.subscribe((width: PopupWidthOption) => {
46-
PopupWidthService.setStyle(width);
47-
localStorage.setItem(PopupWidthService.LocalStorageKey, width);
50+
PopupSizeService.setStyle(width);
51+
localStorage.setItem(PopupSizeService.LocalStorageKey, width);
4852
});
53+
54+
const isInChromeTab = await BrowserPopupUtils.isInTab();
55+
56+
if (!BrowserPopupUtils.inPopup(window) || isInChromeTab) {
57+
window.document.body.classList.add("body-full");
58+
} else if (window.innerHeight < 400) {
59+
window.document.body.classList.add("body-xxs");
60+
} else if (window.innerHeight < 500) {
61+
window.document.body.classList.add("body-xs");
62+
} else if (window.innerHeight < 600) {
63+
window.document.body.classList.add("body-sm");
64+
}
4965
}
5066

5167
private static setStyle(width: PopupWidthOption) {
68+
if (!BrowserPopupUtils.inPopup(window)) {
69+
return;
70+
}
5271
const pxWidth = PopupWidthOptions[width] ?? PopupWidthOptions.default;
72+
5373
document.body.style.minWidth = `${pxWidth}px`;
5474
}
5575

5676
/**
5777
* To keep the popup size from flickering on bootstrap, we store the width in `localStorage` so we can quickly & synchronously reference it.
5878
**/
5979
static initBodyWidthFromLocalStorage() {
60-
const storedValue = localStorage.getItem(PopupWidthService.LocalStorageKey);
80+
const storedValue = localStorage.getItem(PopupSizeService.LocalStorageKey);
6181
this.setStyle(storedValue as any);
6282
}
6383
}

apps/browser/src/popup/app.component.ts

-3
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@ import {
2323
} from "@bitwarden/components";
2424

2525
import { PopupCompactModeService } from "../platform/popup/layout/popup-compact-mode.service";
26-
import { PopupWidthService } from "../platform/popup/layout/popup-width.service";
2726
import { PopupViewCacheService } from "../platform/popup/view-cache/popup-view-cache.service";
2827
import { initPopupClosedListener } from "../platform/services/popup-view-cache-background.service";
2928
import { VaultBrowserStateService } from "../vault/services/vault-browser-state.service";
@@ -42,7 +41,6 @@ import { DesktopSyncVerificationDialogComponent } from "./components/desktop-syn
4241
export class AppComponent implements OnInit, OnDestroy {
4342
private viewCacheService = inject(PopupViewCacheService);
4443
private compactModeService = inject(PopupCompactModeService);
45-
private widthService = inject(PopupWidthService);
4644

4745
private lastActivity: Date;
4846
private activeUserId: UserId;
@@ -73,7 +71,6 @@ export class AppComponent implements OnInit, OnDestroy {
7371
await this.viewCacheService.init();
7472

7573
this.compactModeService.init();
76-
this.widthService.init();
7774

7875
// Component states must not persist between closing and reopening the popup, otherwise they become dead objects
7976
// Clear them aggressively to make sure this doesn't occur

apps/browser/src/popup/main.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { enableProdMode } from "@angular/core";
22
import { platformBrowserDynamic } from "@angular/platform-browser-dynamic";
33

4-
import { PopupWidthService } from "../platform/popup/layout/popup-width.service";
4+
import { PopupSizeService } from "../platform/popup/layout/popup-size.service";
55
import { BrowserPlatformUtilsService } from "../platform/services/platform-utils/browser-platform-utils.service";
66

77
require("./scss/popup.scss");
@@ -10,7 +10,7 @@ require("./scss/tailwind.css");
1010
import { AppModule } from "./app.module";
1111

1212
// We put these first to minimize the delay in window changing.
13-
PopupWidthService.initBodyWidthFromLocalStorage();
13+
PopupSizeService.initBodyWidthFromLocalStorage();
1414
// Should be removed once we deprecate support for Safari 16.0 and older. See Jira ticket [PM-1861]
1515
if (BrowserPlatformUtilsService.shouldApplySafariHeightFix(window)) {
1616
document.documentElement.classList.add("safari_height_fix");

apps/browser/src/popup/scss/base.scss

+10-8
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,8 @@ body {
1919
}
2020

2121
body {
22-
min-width: 380px;
23-
height: 600px !important;
22+
width: 380px;
23+
height: 600px;
2424
position: relative;
2525
min-height: 100vh;
2626
overflow: hidden;
@@ -33,18 +33,20 @@ body {
3333
}
3434

3535
&.body-sm {
36-
width: 375px !important;
37-
height: 500px !important;
36+
height: 500px;
3837
}
3938

4039
&.body-xs {
41-
width: 375px !important;
42-
height: 300px !important;
40+
height: 400px;
41+
}
42+
43+
&.body-xxs {
44+
height: 300px;
4345
}
4446

4547
&.body-full {
46-
width: 100% !important;
47-
height: 100% !important;
48+
width: 100%;
49+
height: 100%;
4850
}
4951
}
5052

apps/browser/src/popup/services/init.service.ts

+5-8
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { DOCUMENT } from "@angular/common";
2-
import { Inject, Injectable } from "@angular/core";
2+
import { inject, Inject, Injectable } from "@angular/core";
33

44
import { AbstractThemingService } from "@bitwarden/angular/platform/services/theming/theming.service.abstraction";
55
import { TwoFactorService } from "@bitwarden/common/auth/abstractions/two-factor.service";
@@ -10,8 +10,11 @@ import { StateService } from "@bitwarden/common/platform/abstractions/state.serv
1010

1111
import { BrowserApi } from "../../platform/browser/browser-api";
1212
import BrowserPopupUtils from "../../platform/popup/browser-popup-utils";
13+
import { PopupSizeService } from "../../platform/popup/layout/popup-size.service";
1314
@Injectable()
1415
export class InitService {
16+
private sizeService = inject(PopupSizeService);
17+
1518
constructor(
1619
private platformUtilsService: PlatformUtilsService,
1720
private i18nService: I18nService,
@@ -28,13 +31,7 @@ export class InitService {
2831
await this.i18nService.init();
2932
this.twoFactorService.init();
3033

31-
if (!BrowserPopupUtils.inPopup(window)) {
32-
window.document.body.classList.add("body-full");
33-
} else if (window.screen.availHeight < 600) {
34-
window.document.body.classList.add("body-xs");
35-
} else if (window.screen.availHeight <= 800) {
36-
window.document.body.classList.add("body-sm");
37-
}
34+
await this.sizeService.init();
3835

3936
const htmlEl = window.document.documentElement;
4037
this.themingService.applyThemeChangesTo(this.document);

apps/browser/src/vault/popup/settings/appearance-v2.component.spec.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import { ThemeStateService } from "@bitwarden/common/platform/theming/theme-stat
1616
import { PopupCompactModeService } from "../../../platform/popup/layout/popup-compact-mode.service";
1717
import { PopupHeaderComponent } from "../../../platform/popup/layout/popup-header.component";
1818
import { PopupPageComponent } from "../../../platform/popup/layout/popup-page.component";
19-
import { PopupWidthService } from "../../../platform/popup/layout/popup-width.service";
19+
import { PopupSizeService } from "../../../platform/popup/layout/popup-size.service";
2020
import { VaultPopupCopyButtonsService } from "../services/vault-popup-copy-buttons.service";
2121

2222
import { AppearanceV2Component } from "./appearance-v2.component";
@@ -55,7 +55,7 @@ describe("AppearanceV2Component", () => {
5555
const setEnableCompactMode = jest.fn().mockResolvedValue(undefined);
5656
const setShowQuickCopyActions = jest.fn().mockResolvedValue(undefined);
5757

58-
const mockWidthService: Partial<PopupWidthService> = {
58+
const mockWidthService: Partial<PopupSizeService> = {
5959
width$: new BehaviorSubject("default"),
6060
setWidth: jest.fn().mockResolvedValue(undefined),
6161
};
@@ -95,7 +95,7 @@ describe("AppearanceV2Component", () => {
9595
} as Partial<VaultPopupCopyButtonsService>,
9696
},
9797
{
98-
provide: PopupWidthService,
98+
provide: PopupSizeService,
9999
useValue: mockWidthService,
100100
},
101101
],

apps/browser/src/vault/popup/settings/appearance-v2.component.ts

+5-5
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,8 @@ import { PopupHeaderComponent } from "../../../platform/popup/layout/popup-heade
2525
import { PopupPageComponent } from "../../../platform/popup/layout/popup-page.component";
2626
import {
2727
PopupWidthOption,
28-
PopupWidthService,
29-
} from "../../../platform/popup/layout/popup-width.service";
28+
PopupSizeService,
29+
} from "../../../platform/popup/layout/popup-size.service";
3030
import { VaultPopupCopyButtonsService } from "../services/vault-popup-copy-buttons.service";
3131

3232
@Component({
@@ -49,7 +49,7 @@ import { VaultPopupCopyButtonsService } from "../services/vault-popup-copy-butto
4949
export class AppearanceV2Component implements OnInit {
5050
private compactModeService = inject(PopupCompactModeService);
5151
private copyButtonsService = inject(VaultPopupCopyButtonsService);
52-
private popupWidthService = inject(PopupWidthService);
52+
private popupSizeService = inject(PopupSizeService);
5353
private i18nService = inject(I18nService);
5454

5555
appearanceForm = this.formBuilder.group({
@@ -103,7 +103,7 @@ export class AppearanceV2Component implements OnInit {
103103
const showQuickCopyActions = await firstValueFrom(
104104
this.copyButtonsService.showQuickCopyActions$,
105105
);
106-
const width = await firstValueFrom(this.popupWidthService.width$);
106+
const width = await firstValueFrom(this.popupSizeService.width$);
107107

108108
// Set initial values for the form
109109
this.appearanceForm.setValue({
@@ -187,6 +187,6 @@ export class AppearanceV2Component implements OnInit {
187187
}
188188

189189
async updateWidth(width: PopupWidthOption) {
190-
await this.popupWidthService.setWidth(width);
190+
await this.popupSizeService.setWidth(width);
191191
}
192192
}

0 commit comments

Comments
 (0)