1
0
mirror of https://github.com/bitwarden/browser.git synced 2024-12-27 17:18:04 +01:00

[BEEEP] Firefox Private Mode (#2294)

* Create background in popup if private mode, remove gates

* Add messaging support to runtime in private mode

* Fix messaging services and general bootstrap logic

* Add private mode warning, remove old component

* Deprecate launchGuardService

* Require in memory account for user to be considered authenticated

* Don't change icon for private mode windows

* Set all icons from background page
This commit is contained in:
Thomas Rittson 2022-02-16 08:06:35 +10:00 committed by GitHub
parent 7371ee344e
commit 06ba30fc6b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 192 additions and 135 deletions

View File

@ -980,8 +980,8 @@
"commandLockVaultDesc": { "commandLockVaultDesc": {
"message": "Lock the vault" "message": "Lock the vault"
}, },
"privateModeMessage": { "privateModeWarning": {
"message": "Unfortunately this window is not available in private mode for this browser." "message": "Private mode support is experimental and some features are limited."
}, },
"customFields": { "customFields": {
"message": "Custom Fields" "message": "Custom Fields"

View File

@ -92,6 +92,7 @@ import { PopupUtilsService } from "../popup/services/popup-utils.service";
import AutofillService from "../services/autofill.service"; import AutofillService from "../services/autofill.service";
import { BrowserCryptoService } from "../services/browserCrypto.service"; import { BrowserCryptoService } from "../services/browserCrypto.service";
import BrowserMessagingService from "../services/browserMessaging.service"; import BrowserMessagingService from "../services/browserMessaging.service";
import BrowserMessagingPrivateModeBackgroundService from "../services/browserMessagingPrivateModeBackground.service";
import BrowserPlatformUtilsService from "../services/browserPlatformUtils.service"; import BrowserPlatformUtilsService from "../services/browserPlatformUtils.service";
import BrowserStorageService from "../services/browserStorage.service"; import BrowserStorageService from "../services/browserStorage.service";
import I18nService from "../services/i18n.service"; import I18nService from "../services/i18n.service";
@ -100,7 +101,6 @@ import VaultTimeoutService from "../services/vaultTimeout.service";
import { Account } from "../models/account"; import { Account } from "../models/account";
import { GlobalStateFactory } from "jslib-common/factories/globalStateFactory";
import { StateFactory } from "jslib-common/factories/stateFactory"; import { StateFactory } from "jslib-common/factories/stateFactory";
export default class MainBackground { export default class MainBackground {
@ -165,9 +165,11 @@ export default class MainBackground {
private isSafari: boolean; private isSafari: boolean;
private nativeMessagingBackground: NativeMessagingBackground; private nativeMessagingBackground: NativeMessagingBackground;
constructor() { constructor(public isPrivateMode: boolean = false) {
// Services // Services
this.messagingService = new BrowserMessagingService(); this.messagingService = isPrivateMode
? new BrowserMessagingPrivateModeBackgroundService()
: new BrowserMessagingService();
this.storageService = new BrowserStorageService(); this.storageService = new BrowserStorageService();
this.secureStorageService = new BrowserStorageService(); this.secureStorageService = new BrowserStorageService();
this.logService = new ConsoleLogService(false); this.logService = new ConsoleLogService(false);
@ -362,7 +364,7 @@ export default class MainBackground {
this.logService, this.logService,
this.stateService this.stateService
); );
this.popupUtilsService = new PopupUtilsService(this.platformUtilsService); this.popupUtilsService = new PopupUtilsService(isPrivateMode);
this.userVerificationService = new UserVerificationService( this.userVerificationService = new UserVerificationService(
this.cryptoService, this.cryptoService,
@ -404,7 +406,6 @@ export default class MainBackground {
this.systemService, this.systemService,
this.environmentService, this.environmentService,
this.messagingService, this.messagingService,
this.stateService,
this.logService this.logService
); );
this.nativeMessagingBackground = new NativeMessagingBackground( this.nativeMessagingBackground = new NativeMessagingBackground(
@ -502,6 +503,16 @@ export default class MainBackground {
await this.webRequestBackground.init(); await this.webRequestBackground.init();
await this.windowsBackground.init(); await this.windowsBackground.init();
if (this.platformUtilsService.isFirefox && !this.isPrivateMode) {
// Set new Private Mode windows to the default icon - they do not share state with the background page
BrowserApi.onWindowCreated(async (win) => {
if (win.incognito) {
await this.actionSetIcon(chrome.browserAction, "", win.id);
await this.actionSetIcon(this.sidebarAction, "", win.id);
}
});
}
return new Promise<void>((resolve) => { return new Promise<void>((resolve) => {
setTimeout(async () => { setTimeout(async () => {
await this.environmentService.setUrlsFromStorage(); await this.environmentService.setUrlsFromStorage();
@ -514,7 +525,7 @@ export default class MainBackground {
} }
async setIcon() { async setIcon() {
if (!chrome.browserAction && !this.sidebarAction) { if ((!chrome.browserAction && !this.sidebarAction) || this.isPrivateMode) {
return; return;
} }
@ -928,7 +939,7 @@ export default class MainBackground {
}); });
} }
private async actionSetIcon(theAction: any, suffix: string): Promise<any> { private async actionSetIcon(theAction: any, suffix: string, windowId?: number): Promise<any> {
if (!theAction || !theAction.setIcon) { if (!theAction || !theAction.setIcon) {
return; return;
} }
@ -938,6 +949,7 @@ export default class MainBackground {
19: "images/icon19" + suffix + ".png", 19: "images/icon19" + suffix + ".png",
38: "images/icon38" + suffix + ".png", 38: "images/icon38" + suffix + ".png",
}, },
windowId: windowId,
}; };
if (this.platformUtilsService.isFirefox()) { if (this.platformUtilsService.isFirefox()) {

View File

@ -31,7 +31,6 @@ export default class RuntimeBackground {
private systemService: SystemService, private systemService: SystemService,
private environmentService: EnvironmentService, private environmentService: EnvironmentService,
private messagingService: MessagingService, private messagingService: MessagingService,
private stateService: StateService,
private logService: LogService private logService: LogService
) { ) {
// onInstalled listener must be wired up before anything else, so we do it in the ctor // onInstalled listener must be wired up before anything else, so we do it in the ctor
@ -46,12 +45,18 @@ export default class RuntimeBackground {
} }
await this.checkOnInstalled(); await this.checkOnInstalled();
BrowserApi.messageListener( const backgroundMessageListener = async (
"runtime.background", msg: any,
async (msg: any, sender: chrome.runtime.MessageSender, sendResponse: any) => { sender: chrome.runtime.MessageSender,
await this.processMessage(msg, sender, sendResponse); sendResponse: any
} ) => {
); await this.processMessage(msg, sender, sendResponse);
};
BrowserApi.messageListener("runtime.background", backgroundMessageListener);
if (this.main.isPrivateMode) {
(window as any).bitwardenBackgroundMessageListener = backgroundMessageListener;
}
} }
async processMessage(msg: any, sender: any, sendResponse: any) { async processMessage(msg: any, sender: any, sendResponse: any) {
@ -60,7 +65,7 @@ export default class RuntimeBackground {
case "unlocked": case "unlocked":
let item: LockedVaultPendingNotificationsItem; let item: LockedVaultPendingNotificationsItem;
if (this.lockedVaultPendingNotifications.length > 0) { if (this.lockedVaultPendingNotifications?.length > 0) {
await BrowserApi.closeLoginTab(); await BrowserApi.closeLoginTab();
item = this.lockedVaultPendingNotifications.pop(); item = this.lockedVaultPendingNotifications.pop();

View File

@ -84,6 +84,14 @@ export class BrowserApi {
}); });
} }
static async getPrivateModeWindows(): Promise<browser.windows.Window[]> {
return (await browser.windows.getAll()).filter((win) => win.incognito);
}
static async onWindowCreated(callback: (win: chrome.windows.Window) => any) {
return chrome.windows.onCreated.addListener(callback);
}
static getBackgroundPage(): any { static getBackgroundPage(): any {
return chrome.extension.getBackgroundPage(); return chrome.extension.getBackgroundPage();
} }

View File

@ -70,5 +70,6 @@
<p class="text-center"> <p class="text-center">
<button type="button" appStopClick (click)="logOut()">{{ "logOut" | i18n }}</button> <button type="button" appStopClick (click)="logOut()">{{ "logOut" | i18n }}</button>
</p> </p>
<app-private-mode-warning></app-private-mode-warning>
</content> </content>
</form> </form>

View File

@ -67,5 +67,6 @@
<p class="text-center"> <p class="text-center">
<a routerLink="/hint">{{ "getMasterPasswordHint" | i18n }}</a> <a routerLink="/hint">{{ "getMasterPasswordHint" | i18n }}</a>
</p> </p>
<app-private-mode-warning></app-private-mode-warning>
</content> </content>
</form> </form>

View File

@ -3,9 +3,9 @@ import { ActivatedRouteSnapshot, RouteReuseStrategy, RouterModule, Routes } from
import { AuthGuardService } from "jslib-angular/services/auth-guard.service"; import { AuthGuardService } from "jslib-angular/services/auth-guard.service";
import { LockGuardService } from "jslib-angular/services/lock-guard.service"; import { LockGuardService } from "jslib-angular/services/lock-guard.service";
import { UnauthGuardService } from "jslib-angular/services/unauth-guard.service";
import { DebounceNavigationService } from "./services/debounceNavigationService"; import { DebounceNavigationService } from "./services/debounceNavigationService";
import { LaunchGuardService } from "./services/launch-guard.service";
import { EnvironmentComponent } from "./accounts/environment.component"; import { EnvironmentComponent } from "./accounts/environment.component";
import { HintComponent } from "./accounts/hint.component"; import { HintComponent } from "./accounts/hint.component";
@ -23,7 +23,6 @@ import { UpdateTempPasswordComponent } from "./accounts/update-temp-password.com
import { PasswordGeneratorHistoryComponent } from "./generator/password-generator-history.component"; import { PasswordGeneratorHistoryComponent } from "./generator/password-generator-history.component";
import { PasswordGeneratorComponent } from "./generator/password-generator.component"; import { PasswordGeneratorComponent } from "./generator/password-generator.component";
import { PrivateModeComponent } from "./private-mode.component";
import { TabsComponent } from "./tabs.component"; import { TabsComponent } from "./tabs.component";
import { ExcludedDomainsComponent } from "./settings/excluded-domains.component"; import { ExcludedDomainsComponent } from "./settings/excluded-domains.component";
@ -63,13 +62,13 @@ const routes: Routes = [
{ {
path: "home", path: "home",
component: HomeComponent, component: HomeComponent,
canActivate: [LaunchGuardService], canActivate: [UnauthGuardService],
data: { state: "home" }, data: { state: "home" },
}, },
{ {
path: "login", path: "login",
component: LoginComponent, component: LoginComponent,
canActivate: [LaunchGuardService], canActivate: [UnauthGuardService],
data: { state: "login" }, data: { state: "login" },
}, },
{ {
@ -81,19 +80,19 @@ const routes: Routes = [
{ {
path: "2fa", path: "2fa",
component: TwoFactorComponent, component: TwoFactorComponent,
canActivate: [LaunchGuardService], canActivate: [UnauthGuardService],
data: { state: "2fa" }, data: { state: "2fa" },
}, },
{ {
path: "2fa-options", path: "2fa-options",
component: TwoFactorOptionsComponent, component: TwoFactorOptionsComponent,
canActivate: [LaunchGuardService], canActivate: [UnauthGuardService],
data: { state: "2fa-options" }, data: { state: "2fa-options" },
}, },
{ {
path: "sso", path: "sso",
component: SsoComponent, component: SsoComponent,
canActivate: [LaunchGuardService], canActivate: [UnauthGuardService],
data: { state: "sso" }, data: { state: "sso" },
}, },
{ {
@ -110,19 +109,19 @@ const routes: Routes = [
{ {
path: "register", path: "register",
component: RegisterComponent, component: RegisterComponent,
canActivate: [LaunchGuardService], canActivate: [UnauthGuardService],
data: { state: "register" }, data: { state: "register" },
}, },
{ {
path: "hint", path: "hint",
component: HintComponent, component: HintComponent,
canActivate: [LaunchGuardService], canActivate: [UnauthGuardService],
data: { state: "hint" }, data: { state: "hint" },
}, },
{ {
path: "environment", path: "environment",
component: EnvironmentComponent, component: EnvironmentComponent,
canActivate: [LaunchGuardService], canActivate: [UnauthGuardService],
data: { state: "environment" }, data: { state: "environment" },
}, },
{ {
@ -235,11 +234,6 @@ const routes: Routes = [
canActivate: [AuthGuardService], canActivate: [AuthGuardService],
data: { state: "options" }, data: { state: "options" },
}, },
{
path: "private-mode",
component: PrivateModeComponent,
data: { state: "private-mode" },
},
{ {
path: "clone-cipher", path: "clone-cipher",
component: AddEditComponent, component: AddEditComponent,

View File

@ -8,7 +8,6 @@ import { BrowserApi } from "../browser/browserApi";
import { AuthService } from "jslib-common/abstractions/auth.service"; import { AuthService } from "jslib-common/abstractions/auth.service";
import { BroadcasterService } from "jslib-common/abstractions/broadcaster.service"; import { BroadcasterService } from "jslib-common/abstractions/broadcaster.service";
import { I18nService } from "jslib-common/abstractions/i18n.service"; import { I18nService } from "jslib-common/abstractions/i18n.service";
import { KeyConnectorService } from "jslib-common/abstractions/keyConnector.service";
import { MessagingService } from "jslib-common/abstractions/messaging.service"; import { MessagingService } from "jslib-common/abstractions/messaging.service";
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service"; import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
@ -39,14 +38,10 @@ export class AppComponent implements OnInit {
private changeDetectorRef: ChangeDetectorRef, private changeDetectorRef: ChangeDetectorRef,
private ngZone: NgZone, private ngZone: NgZone,
private sanitizer: DomSanitizer, private sanitizer: DomSanitizer,
private platformUtilsService: PlatformUtilsService, private platformUtilsService: PlatformUtilsService
private keyConnectoService: KeyConnectorService
) {} ) {}
ngOnInit() { ngOnInit() {
if (BrowserApi.getBackgroundPage() == null) {
return;
}
this.stateService.activeAccount.subscribe((userId) => { this.stateService.activeAccount.subscribe((userId) => {
this.activeUserId = userId; this.activeUserId = userId;
}); });

View File

@ -27,7 +27,6 @@ import { PasswordGeneratorHistoryComponent } from "./generator/password-generato
import { PasswordGeneratorComponent } from "./generator/password-generator.component"; import { PasswordGeneratorComponent } from "./generator/password-generator.component";
import { AppComponent } from "./app.component"; import { AppComponent } from "./app.component";
import { PrivateModeComponent } from "./private-mode.component";
import { TabsComponent } from "./tabs.component"; import { TabsComponent } from "./tabs.component";
import { ExcludedDomainsComponent } from "./settings/excluded-domains.component"; import { ExcludedDomainsComponent } from "./settings/excluded-domains.component";
@ -78,6 +77,7 @@ import { ActionButtonsComponent } from "./components/action-buttons.component";
import { CipherRowComponent } from "./components/cipher-row.component"; import { CipherRowComponent } from "./components/cipher-row.component";
import { PasswordRepromptComponent } from "./components/password-reprompt.component"; import { PasswordRepromptComponent } from "./components/password-reprompt.component";
import { PopOutComponent } from "./components/pop-out.component"; import { PopOutComponent } from "./components/pop-out.component";
import { PrivateModeWarningComponent } from "./components/private-mode-warning.component";
import { SendListComponent } from "./components/send-list.component"; import { SendListComponent } from "./components/send-list.component";
import { SetPinComponent } from "./components/set-pin.component"; import { SetPinComponent } from "./components/set-pin.component";
import { VerifyMasterPasswordComponent } from "./components/verify-master-password.component"; import { VerifyMasterPasswordComponent } from "./components/verify-master-password.component";
@ -230,7 +230,7 @@ registerLocaleData(localeZhTw, "zh-TW");
PasswordRepromptComponent, PasswordRepromptComponent,
PopOutComponent, PopOutComponent,
PremiumComponent, PremiumComponent,
PrivateModeComponent, PrivateModeWarningComponent,
RegisterComponent, RegisterComponent,
SearchCiphersPipe, SearchCiphersPipe,
SelectCopyDirective, SelectCopyDirective,

View File

@ -0,0 +1,6 @@
<app-callout type="warning" *ngIf="showWarning">
{{ "privateModeWarning" | i18n }}
<a href="https://bitwarden.com/help/article/private-mode/" target="_blank" rel="noopener">{{
"learnMore" | i18n
}}</a>
</app-callout>

View File

@ -0,0 +1,16 @@
import { Component, OnInit } from "@angular/core";
import { PopupUtilsService } from "../services/popup-utils.service";
@Component({
selector: "app-private-mode-warning",
templateUrl: "private-mode-warning.component.html",
})
export class PrivateModeWarningComponent implements OnInit {
showWarning = false;
constructor(private popupUtilsService: PopupUtilsService) {}
ngOnInit() {
this.showWarning = this.popupUtilsService.inPrivateMode();
}
}

View File

@ -1,6 +0,0 @@
<div class="content">
<p class="text-center">{{ privateModeMessage }}</p>
<button type="button" class="btn primary block" (click)="learnMore()">
<b>{{ learnMoreMessage }}</b>
</button>
</div>

View File

@ -1,21 +0,0 @@
import { BrowserApi } from "../browser/browserApi";
import { Component, OnInit } from "@angular/core";
@Component({
selector: "app-private-mode",
templateUrl: "private-mode.component.html",
})
export class PrivateModeComponent implements OnInit {
privateModeMessage: string;
learnMoreMessage: string;
ngOnInit() {
this.privateModeMessage = chrome.i18n.getMessage("privateModeMessage");
this.learnMoreMessage = chrome.i18n.getMessage("learnMore");
}
learnMore() {
BrowserApi.createNewTab("https://bitwarden.com/help/extension-wont-load-in-private-mode/");
}
}

View File

@ -73,6 +73,11 @@ app-home {
} }
} }
app-private-mode-warning {
display: block;
padding-top: 1rem;
}
body.body-sm, body.body-sm,
body.body-xs { body.body-xs {
app-home { app-home {

View File

@ -1,19 +0,0 @@
import { BrowserApi } from "../../browser/browserApi";
import { Injectable } from "@angular/core";
import { CanActivate, Router } from "@angular/router";
import { UnauthGuardService } from "jslib-angular/services/unauth-guard.service";
@Injectable()
export class LaunchGuardService implements CanActivate {
constructor(private router: Router, private unauthGuardService: UnauthGuardService) {}
async canActivate() {
if (BrowserApi.getBackgroundPage() == null) {
this.router.navigate(["private-mode"]);
return false;
}
return await this.unauthGuardService.canActivate();
}
}

View File

@ -2,11 +2,9 @@ import { Injectable } from "@angular/core";
import { BrowserApi } from "../../browser/browserApi"; import { BrowserApi } from "../../browser/browserApi";
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
@Injectable() @Injectable()
export class PopupUtilsService { export class PopupUtilsService {
constructor(private platformUtilsService: PlatformUtilsService) {} constructor(private privateMode: boolean = false) {}
inSidebar(win: Window): boolean { inSidebar(win: Window): boolean {
return win.location.search !== "" && win.location.search.indexOf("uilocation=sidebar") > -1; return win.location.search !== "" && win.location.search.indexOf("uilocation=sidebar") > -1;
@ -28,6 +26,10 @@ export class PopupUtilsService {
); );
} }
inPrivateMode(): boolean {
return this.privateMode;
}
getContentScrollY(win: Window, scrollingContainer: string = "content"): number { getContentScrollY(win: Window, scrollingContainer: string = "content"): number {
const content = win.document.getElementsByTagName(scrollingContainer)[0]; const content = win.document.getElementsByTagName(scrollingContainer)[0];
return content.scrollTop; return content.scrollTop;

View File

@ -1,7 +1,6 @@
import { APP_INITIALIZER, LOCALE_ID, NgModule } from "@angular/core"; import { APP_INITIALIZER, LOCALE_ID, NgModule } from "@angular/core";
import { DebounceNavigationService } from "./debounceNavigationService"; import { DebounceNavigationService } from "./debounceNavigationService";
import { LaunchGuardService } from "./launch-guard.service";
import { LockGuardService } from "./lock-guard.service"; import { LockGuardService } from "./lock-guard.service";
import { PasswordRepromptService } from "./password-reprompt.service"; import { PasswordRepromptService } from "./password-reprompt.service";
import { UnauthGuardService } from "./unauth-guard.service"; import { UnauthGuardService } from "./unauth-guard.service";
@ -49,7 +48,9 @@ import { UserVerificationService } from "jslib-common/abstractions/userVerificat
import { VaultTimeoutService } from "jslib-common/abstractions/vaultTimeout.service"; import { VaultTimeoutService } from "jslib-common/abstractions/vaultTimeout.service";
import { AutofillService } from "../../services/abstractions/autofill.service"; import { AutofillService } from "../../services/abstractions/autofill.service";
import BrowserMessagingService from "../../services/browserMessaging.service"; import BrowserMessagingService from "../../services/browserMessaging.service";
import BrowserMessagingPrivateModePopupService from "../../services/browserMessagingPrivateModePopup.service";
import { AuthService } from "jslib-common/services/auth.service"; import { AuthService } from "jslib-common/services/auth.service";
import { ConsoleLogService } from "jslib-common/services/consoleLog.service"; import { ConsoleLogService } from "jslib-common/services/consoleLog.service";
@ -62,14 +63,24 @@ import { ThemeType } from "jslib-common/enums/themeType";
import { StateService as StateServiceAbstraction } from "../../services/abstractions/state.service"; import { StateService as StateServiceAbstraction } from "../../services/abstractions/state.service";
function getBgService<T>(service: string) { import MainBackground from "../../background/main.background";
return (): T => {
const page = BrowserApi.getBackgroundPage();
return page ? (page.bitwardenMain[service] as T) : null;
};
}
const isPrivateMode = BrowserApi.getBackgroundPage() == null; const isPrivateMode = BrowserApi.getBackgroundPage() == null;
const mainBackground: MainBackground = isPrivateMode
? createLocalBgService()
: BrowserApi.getBackgroundPage().bitwardenMain;
function createLocalBgService() {
const localBgService = new MainBackground(true);
localBgService.bootstrap();
return localBgService;
}
function getBgService<T>(service: keyof MainBackground) {
return (): T => {
return mainBackground ? (mainBackground[service] as any as T) : null;
};
}
export function initFactory( export function initFactory(
platformUtilsService: PlatformUtilsService, platformUtilsService: PlatformUtilsService,
@ -89,33 +100,47 @@ export function initFactory(
window.document.body.classList.add("body-sm"); window.document.body.classList.add("body-sm");
} }
if (!isPrivateMode) { const htmlEl = window.document.documentElement;
const htmlEl = window.document.documentElement; const theme = await platformUtilsService.getEffectiveTheme();
const theme = await platformUtilsService.getEffectiveTheme(); htmlEl.classList.add("theme_" + theme);
htmlEl.classList.add("theme_" + theme); platformUtilsService.onDefaultSystemThemeChange(async (sysTheme) => {
platformUtilsService.onDefaultSystemThemeChange(async (sysTheme) => { const bwTheme = await stateService.getTheme();
const bwTheme = await stateService.getTheme(); if (bwTheme == null || bwTheme === ThemeType.System) {
if (bwTheme == null || bwTheme === ThemeType.System) { htmlEl.classList.remove("theme_" + ThemeType.Light, "theme_" + ThemeType.Dark);
htmlEl.classList.remove("theme_" + ThemeType.Light, "theme_" + ThemeType.Dark); htmlEl.classList.add("theme_" + sysTheme);
htmlEl.classList.add("theme_" + sysTheme);
}
});
htmlEl.classList.add("locale_" + i18nService.translationLocale);
// Workaround for slow performance on external monitors on Chrome + MacOS
// See: https://bugs.chromium.org/p/chromium/issues/detail?id=971701#c64
if (
platformUtilsService.isChrome() &&
navigator.platform.indexOf("Mac") > -1 &&
popupUtilsService.inPopup(window) &&
(window.screenLeft < 0 ||
window.screenTop < 0 ||
window.screenLeft > window.screen.width ||
window.screenTop > window.screen.height)
) {
htmlEl.classList.add("force_redraw");
logService.info("Force redraw is on");
} }
});
htmlEl.classList.add("locale_" + i18nService.translationLocale);
// Workaround for slow performance on external monitors on Chrome + MacOS
// See: https://bugs.chromium.org/p/chromium/issues/detail?id=971701#c64
if (
platformUtilsService.isChrome() &&
navigator.platform.indexOf("Mac") > -1 &&
popupUtilsService.inPopup(window) &&
(window.screenLeft < 0 ||
window.screenTop < 0 ||
window.screenLeft > window.screen.width ||
window.screenTop > window.screen.height)
) {
htmlEl.classList.add("force_redraw");
logService.info("Force redraw is on");
}
htmlEl.classList.add("locale_" + i18nService.translationLocale);
// Workaround for slow performance on external monitors on Chrome + MacOS
// See: https://bugs.chromium.org/p/chromium/issues/detail?id=971701#c64
if (
platformUtilsService.isChrome() &&
navigator.platform.indexOf("Mac") > -1 &&
popupUtilsService.inPopup(window) &&
(window.screenLeft < 0 ||
window.screenTop < 0 ||
window.screenLeft > window.screen.width ||
window.screenTop > window.screen.height)
) {
htmlEl.classList.add("force_redraw");
logService.info("Force redraw is on");
} }
}; };
} }
@ -126,8 +151,7 @@ export function initFactory(
providers: [ providers: [
{ {
provide: LOCALE_ID, provide: LOCALE_ID,
useFactory: () => useFactory: () => getBgService<I18nService>("i18nService")().translationLocale,
isPrivateMode ? null : getBgService<I18nService>("i18nService")().translationLocale,
deps: [], deps: [],
}, },
{ {
@ -142,12 +166,23 @@ export function initFactory(
], ],
multi: true, multi: true,
}, },
LaunchGuardService,
{ provide: BaseLockGuardService, useClass: LockGuardService }, { provide: BaseLockGuardService, useClass: LockGuardService },
{ provide: BaseUnauthGuardService, useClass: UnauthGuardService }, { provide: BaseUnauthGuardService, useClass: UnauthGuardService },
DebounceNavigationService, DebounceNavigationService,
PopupUtilsService, { provide: PopupUtilsService, useFactory: () => new PopupUtilsService(isPrivateMode) },
{ provide: MessagingService, useClass: BrowserMessagingService }, {
provide: MessagingService,
useFactory: () => {
return isPrivateMode
? new BrowserMessagingPrivateModePopupService()
: new BrowserMessagingService();
},
},
{
provide: TwoFactorService,
useFactory: getBgService<TwoFactorService>("twoFactorService"),
deps: [],
},
{ {
provide: TwoFactorService, provide: TwoFactorService,
useFactory: getBgService<TwoFactorService>("twoFactorService"), useFactory: getBgService<TwoFactorService>("twoFactorService"),
@ -165,14 +200,12 @@ export function initFactory(
logService: ConsoleLogService, logService: ConsoleLogService,
i18nService: I18nService i18nService: I18nService
) => { ) => {
return isPrivateMode return new PopupSearchService(
? null getBgService<SearchService>("searchService")(),
: new PopupSearchService( cipherService,
getBgService<SearchService>("searchService")(), logService,
cipherService, i18nService
logService, );
i18nService
);
}, },
deps: [CipherService, LogServiceAbstraction, I18nService], deps: [CipherService, LogServiceAbstraction, I18nService],
}, },

View File

@ -0,0 +1,8 @@
import { MessagingService } from "jslib-common/abstractions/messaging.service";
export default class BrowserMessagingPrivateModeBackgroundService implements MessagingService {
send(subscriber: string, arg: any = {}) {
const message = Object.assign({}, { command: subscriber }, arg);
(window as any).bitwardenPopupMainMessageListener(message);
}
}

View File

@ -0,0 +1,8 @@
import { MessagingService } from "jslib-common/abstractions/messaging.service";
export default class BrowserMessagingPrivateModePopupService implements MessagingService {
send(subscriber: string, arg: any = {}) {
const message = Object.assign({}, { command: subscriber }, arg);
(window as any).bitwardenBackgroundMessageListener(message);
}
}

View File

@ -18,6 +18,15 @@ export class StateService
await super.addAccount(account); await super.addAccount(account);
} }
async getIsAuthenticated(options?: StorageOptions): Promise<boolean> {
// Firefox Private Mode can clash with non-Private Mode because they both read from the same onDiskOptions
// Check that there is an account in memory before considering the user authenticated
return (
(await super.getIsAuthenticated(options)) &&
(await this.getAccount(this.defaultInMemoryOptions)) != null
);
}
async getBrowserGroupingComponentState( async getBrowserGroupingComponentState(
options?: StorageOptions options?: StorageOptions
): Promise<BrowserGroupingsComponentState> { ): Promise<BrowserGroupingsComponentState> {