mirror of
https://github.com/bitwarden/browser
synced 2025-12-15 15:53:27 +00:00
Ps 1291 fix extension icon updates (#3571)
* Add needed factories for AuthService WIP: Allow console logs * Add badge updates * Init by listener * Improve tab identification * Define MV3 background init * Init services in factories. Requires conversion of all factories to promises. We need to initialize in factory since the requester of a service doesn't necessarily know all dependencies for that service. The only alternative is to create an out parameter for a generated init function, which isn't ideal. * Improve badge setting * Use `update-badge` in mv2 and mv3 Separates menu and badge updates * Use update-badge everywhere * Use BrowserApi where possible * Update factories * Merge duplicated methods * Continue using private mode messager for now * Add static platform determination. * Break down methods and extract BrowserApi Concerns * Prefer strict equals * Init two-factor service in factory * Use globalThis types * Prefer `globalThis` * Use Window type definition updated with Opera Co-authored-by: Justin Baur <justindbaur@users.noreply.github.com> * Distinguish Opera from Safari Opera includes Gecko, Chrome, Safari, and Opera in its user agent. We need to make sure that we're not in Opera prior to testing Safari. * Update import * Initialize search-service for update badge context * Build all browser MV3 artifacts only uploading Chrome, Edge and Opera artifacts for now, as those support manifest V3 Also corrects build artifact to lower case. * Remove individual dist Co-authored-by: Justin Baur <justindbaur@users.noreply.github.com> Co-authored-by: Justin Baur <19896123+justindbaur@users.noreply.github.com>
This commit is contained in:
@@ -1,17 +1,31 @@
|
|||||||
import MainBackground from "./background/main.background";
|
import MainBackground from "./background/main.background";
|
||||||
|
import { BrowserApi } from "./browser/browserApi";
|
||||||
import { ClearClipboard } from "./clipboard";
|
import { ClearClipboard } from "./clipboard";
|
||||||
import { onCommandListener } from "./listeners/onCommandListener";
|
import { onCommandListener } from "./listeners/onCommandListener";
|
||||||
import { onInstallListener } from "./listeners/onInstallListener";
|
import { onInstallListener } from "./listeners/onInstallListener";
|
||||||
|
import { UpdateBadge } from "./listeners/update-badge";
|
||||||
|
|
||||||
|
const manifestV3MessageListeners: ((
|
||||||
|
serviceCache: Record<string, unknown>,
|
||||||
|
message: { command: string }
|
||||||
|
) => void | Promise<void>)[] = [UpdateBadge.messageListener];
|
||||||
type AlarmAction = (executionTime: Date, serviceCache: Record<string, unknown>) => void;
|
type AlarmAction = (executionTime: Date, serviceCache: Record<string, unknown>) => void;
|
||||||
|
|
||||||
const AlarmActions: AlarmAction[] = [ClearClipboard.run];
|
const AlarmActions: AlarmAction[] = [ClearClipboard.run];
|
||||||
|
|
||||||
const manifest = chrome.runtime.getManifest();
|
if (BrowserApi.manifestVersion === 3) {
|
||||||
|
|
||||||
if (manifest.manifest_version === 3) {
|
|
||||||
chrome.commands.onCommand.addListener(onCommandListener);
|
chrome.commands.onCommand.addListener(onCommandListener);
|
||||||
chrome.runtime.onInstalled.addListener(onInstallListener);
|
chrome.runtime.onInstalled.addListener(onInstallListener);
|
||||||
|
chrome.tabs.onActivated.addListener(UpdateBadge.tabsOnActivatedListener);
|
||||||
|
chrome.tabs.onReplaced.addListener(UpdateBadge.tabsOnReplacedListener);
|
||||||
|
chrome.tabs.onUpdated.addListener(UpdateBadge.tabsOnUpdatedListener);
|
||||||
|
BrowserApi.messageListener("runtime.background", (message) => {
|
||||||
|
const serviceCache = {};
|
||||||
|
|
||||||
|
manifestV3MessageListeners.forEach((listener) => {
|
||||||
|
listener(serviceCache, message);
|
||||||
|
});
|
||||||
|
});
|
||||||
chrome.alarms.onAlarm.addListener((_alarm) => {
|
chrome.alarms.onAlarm.addListener((_alarm) => {
|
||||||
const executionTime = new Date();
|
const executionTime = new Date();
|
||||||
const serviceCache = {};
|
const serviceCache = {};
|
||||||
|
|||||||
@@ -82,6 +82,7 @@ import { WebCryptoFunctionService } from "@bitwarden/common/services/webCryptoFu
|
|||||||
|
|
||||||
import { BrowserApi } from "../browser/browserApi";
|
import { BrowserApi } from "../browser/browserApi";
|
||||||
import { SafariApp } from "../browser/safariApp";
|
import { SafariApp } from "../browser/safariApp";
|
||||||
|
import { UpdateBadge } from "../listeners/update-badge";
|
||||||
import { Account } from "../models/account";
|
import { Account } from "../models/account";
|
||||||
import { PopupUtilsService } from "../popup/services/popup-utils.service";
|
import { PopupUtilsService } from "../popup/services/popup-utils.service";
|
||||||
import { AutofillService as AutofillServiceAbstraction } from "../services/abstractions/autofill.service";
|
import { AutofillService as AutofillServiceAbstraction } from "../services/abstractions/autofill.service";
|
||||||
@@ -183,15 +184,18 @@ export default class MainBackground {
|
|||||||
private syncTimeout: any;
|
private syncTimeout: any;
|
||||||
private isSafari: boolean;
|
private isSafari: boolean;
|
||||||
private nativeMessagingBackground: NativeMessagingBackground;
|
private nativeMessagingBackground: NativeMessagingBackground;
|
||||||
|
popupOnlyContext: boolean;
|
||||||
|
|
||||||
constructor(public isPrivateMode: boolean = false) {
|
constructor(public isPrivateMode: boolean = false) {
|
||||||
|
this.popupOnlyContext = isPrivateMode || BrowserApi.manifestVersion === 3;
|
||||||
|
|
||||||
// Services
|
// Services
|
||||||
const lockedCallback = async (userId?: string) => {
|
const lockedCallback = async (userId?: string) => {
|
||||||
if (this.notificationsService != null) {
|
if (this.notificationsService != null) {
|
||||||
this.notificationsService.updateConnection(false);
|
this.notificationsService.updateConnection(false);
|
||||||
}
|
}
|
||||||
await this.setIcon();
|
await this.refreshBadge();
|
||||||
await this.refreshBadgeAndMenu(true);
|
await this.refreshMenu(true);
|
||||||
if (this.systemService != null) {
|
if (this.systemService != null) {
|
||||||
await this.systemService.clearPendingClipboard();
|
await this.systemService.clearPendingClipboard();
|
||||||
await this.systemService.startProcessReload(this.authService);
|
await this.systemService.startProcessReload(this.authService);
|
||||||
@@ -201,7 +205,7 @@ export default class MainBackground {
|
|||||||
const logoutCallback = async (expired: boolean, userId?: string) =>
|
const logoutCallback = async (expired: boolean, userId?: string) =>
|
||||||
await this.logout(expired, userId);
|
await this.logout(expired, userId);
|
||||||
|
|
||||||
this.messagingService = isPrivateMode
|
this.messagingService = this.popupOnlyContext
|
||||||
? new BrowserMessagingPrivateModeBackgroundService()
|
? new BrowserMessagingPrivateModeBackgroundService()
|
||||||
: new BrowserMessagingService();
|
: new BrowserMessagingService();
|
||||||
this.logService = new ConsoleLogService(false);
|
this.logService = new ConsoleLogService(false);
|
||||||
@@ -209,7 +213,7 @@ export default class MainBackground {
|
|||||||
this.storageService = new BrowserLocalStorageService();
|
this.storageService = new BrowserLocalStorageService();
|
||||||
this.secureStorageService = new BrowserLocalStorageService();
|
this.secureStorageService = new BrowserLocalStorageService();
|
||||||
this.memoryStorageService =
|
this.memoryStorageService =
|
||||||
chrome.runtime.getManifest().manifest_version == 3
|
BrowserApi.manifestVersion === 3
|
||||||
? new LocalBackedSessionStorageService(
|
? new LocalBackedSessionStorageService(
|
||||||
new EncryptService(this.cryptoFunctionService, this.logService, false),
|
new EncryptService(this.cryptoFunctionService, this.logService, false),
|
||||||
new KeyGenerationService(this.cryptoFunctionService)
|
new KeyGenerationService(this.cryptoFunctionService)
|
||||||
@@ -562,14 +566,12 @@ export default class MainBackground {
|
|||||||
// Set Private Mode windows to the default icon - they do not share state with the background page
|
// Set Private Mode windows to the default icon - they do not share state with the background page
|
||||||
const privateWindows = await BrowserApi.getPrivateModeWindows();
|
const privateWindows = await BrowserApi.getPrivateModeWindows();
|
||||||
privateWindows.forEach(async (win) => {
|
privateWindows.forEach(async (win) => {
|
||||||
await this.actionSetIcon(chrome.browserAction, "", win.id);
|
await new UpdateBadge(self).setBadgeIcon("", win.id);
|
||||||
await this.actionSetIcon(this.sidebarAction, "", win.id);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
BrowserApi.onWindowCreated(async (win) => {
|
BrowserApi.onWindowCreated(async (win) => {
|
||||||
if (win.incognito) {
|
if (win.incognito) {
|
||||||
await this.actionSetIcon(chrome.browserAction, "", win.id);
|
await new UpdateBadge(self).setBadgeIcon("", win.id);
|
||||||
await this.actionSetIcon(this.sidebarAction, "", win.id);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -577,7 +579,7 @@ export default class MainBackground {
|
|||||||
return new Promise<void>((resolve) => {
|
return new Promise<void>((resolve) => {
|
||||||
setTimeout(async () => {
|
setTimeout(async () => {
|
||||||
await this.environmentService.setUrlsFromStorage();
|
await this.environmentService.setUrlsFromStorage();
|
||||||
await this.setIcon();
|
await this.refreshBadge();
|
||||||
this.fullSync(true);
|
this.fullSync(true);
|
||||||
setTimeout(() => this.notificationsService.init(), 2500);
|
setTimeout(() => this.notificationsService.init(), 2500);
|
||||||
resolve();
|
resolve();
|
||||||
@@ -585,25 +587,11 @@ export default class MainBackground {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async setIcon() {
|
async refreshBadge() {
|
||||||
if ((!chrome.browserAction && !this.sidebarAction) || this.isPrivateMode) {
|
await new UpdateBadge(self).run({ existingServices: this as any });
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const authStatus = await this.authService.getAuthStatus();
|
async refreshMenu(forLocked = false) {
|
||||||
|
|
||||||
let suffix = "";
|
|
||||||
if (authStatus === AuthenticationStatus.LoggedOut) {
|
|
||||||
suffix = "_gray";
|
|
||||||
} else if (authStatus === AuthenticationStatus.Locked) {
|
|
||||||
suffix = "_locked";
|
|
||||||
}
|
|
||||||
|
|
||||||
await this.actionSetIcon(chrome.browserAction, suffix);
|
|
||||||
await this.actionSetIcon(this.sidebarAction, suffix);
|
|
||||||
}
|
|
||||||
|
|
||||||
async refreshBadgeAndMenu(forLocked = false) {
|
|
||||||
if (!chrome.windows || !chrome.contextMenus) {
|
if (!chrome.windows || !chrome.contextMenus) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -616,7 +604,7 @@ export default class MainBackground {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (forLocked) {
|
if (forLocked) {
|
||||||
await this.loadMenuAndUpdateBadgeForNoAccessState(!menuDisabled);
|
await this.loadMenuForNoAccessState(!menuDisabled);
|
||||||
this.onUpdatedRan = this.onReplacedRan = false;
|
this.onUpdatedRan = this.onReplacedRan = false;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -652,8 +640,11 @@ export default class MainBackground {
|
|||||||
this.messagingService.send("doneLoggingOut", { expired: expired, userId: userId });
|
this.messagingService.send("doneLoggingOut", { expired: expired, userId: userId });
|
||||||
}
|
}
|
||||||
|
|
||||||
await this.setIcon();
|
if (BrowserApi.manifestVersion === 3) {
|
||||||
await this.refreshBadgeAndMenu(true);
|
BrowserApi.sendMessage("updateBadge");
|
||||||
|
}
|
||||||
|
await this.refreshBadge();
|
||||||
|
await this.refreshMenu(true);
|
||||||
await this.reseedStorage();
|
await this.reseedStorage();
|
||||||
this.notificationsService.updateConnection(false);
|
this.notificationsService.updateConnection(false);
|
||||||
await this.systemService.clearPendingClipboard();
|
await this.systemService.clearPendingClipboard();
|
||||||
@@ -801,18 +792,15 @@ export default class MainBackground {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async contextMenuReady(tab: any, contextMenuEnabled: boolean) {
|
private async contextMenuReady(tab: any, contextMenuEnabled: boolean) {
|
||||||
await this.loadMenuAndUpdateBadge(tab.url, tab.id, contextMenuEnabled);
|
await this.loadMenu(tab.url, tab.id, contextMenuEnabled);
|
||||||
this.onUpdatedRan = this.onReplacedRan = false;
|
this.onUpdatedRan = this.onReplacedRan = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async loadMenuAndUpdateBadge(url: string, tabId: number, contextMenuEnabled: boolean) {
|
private async loadMenu(url: string, tabId: number, contextMenuEnabled: boolean) {
|
||||||
if (!url || (!chrome.browserAction && !this.sidebarAction)) {
|
if (!url || (!chrome.browserAction && !this.sidebarAction)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.actionSetBadgeBackgroundColor(chrome.browserAction);
|
|
||||||
this.actionSetBadgeBackgroundColor(this.sidebarAction);
|
|
||||||
|
|
||||||
this.menuOptionsLoaded = [];
|
this.menuOptionsLoaded = [];
|
||||||
const authStatus = await this.authService.getAuthStatus();
|
const authStatus = await this.authService.getAuthStatus();
|
||||||
if (authStatus === AuthenticationStatus.Unlocked) {
|
if (authStatus === AuthenticationStatus.Unlocked) {
|
||||||
@@ -826,50 +814,26 @@ export default class MainBackground {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const disableBadgeCounter = await this.stateService.getDisableBadgeCounter();
|
|
||||||
let theText = "";
|
|
||||||
|
|
||||||
if (!disableBadgeCounter) {
|
|
||||||
if (ciphers.length > 0 && ciphers.length <= 9) {
|
|
||||||
theText = ciphers.length.toString();
|
|
||||||
} else if (ciphers.length > 0) {
|
|
||||||
theText = "9+";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (contextMenuEnabled && ciphers.length === 0) {
|
if (contextMenuEnabled && ciphers.length === 0) {
|
||||||
await this.loadNoLoginsContextMenuOptions(this.i18nService.t("noMatchingLogins"));
|
await this.loadNoLoginsContextMenuOptions(this.i18nService.t("noMatchingLogins"));
|
||||||
}
|
}
|
||||||
|
|
||||||
this.sidebarActionSetBadgeText(theText, tabId);
|
|
||||||
this.browserActionSetBadgeText(theText, tabId);
|
|
||||||
|
|
||||||
return;
|
return;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
this.logService.error(e);
|
this.logService.error(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
await this.loadMenuAndUpdateBadgeForNoAccessState(contextMenuEnabled);
|
await this.loadMenuForNoAccessState(contextMenuEnabled);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async loadMenuAndUpdateBadgeForNoAccessState(contextMenuEnabled: boolean) {
|
private async loadMenuForNoAccessState(contextMenuEnabled: boolean) {
|
||||||
if (contextMenuEnabled) {
|
if (contextMenuEnabled) {
|
||||||
const authed = await this.stateService.getIsAuthenticated();
|
const authed = await this.stateService.getIsAuthenticated();
|
||||||
await this.loadNoLoginsContextMenuOptions(
|
await this.loadNoLoginsContextMenuOptions(
|
||||||
this.i18nService.t(authed ? "unlockVaultMenu" : "loginToVaultMenu")
|
this.i18nService.t(authed ? "unlockVaultMenu" : "loginToVaultMenu")
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const tabs = await BrowserApi.getActiveTabs();
|
|
||||||
if (tabs != null) {
|
|
||||||
tabs.forEach((tab) => {
|
|
||||||
if (tab.id != null) {
|
|
||||||
this.browserActionSetBadgeText("", tab.id);
|
|
||||||
this.sidebarActionSetBadgeText("", tab.id);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async loadLoginContextMenuOptions(cipher: any) {
|
private async loadLoginContextMenuOptions(cipher: any) {
|
||||||
@@ -1026,42 +990,4 @@ export default class MainBackground {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private actionSetBadgeBackgroundColor(action: any) {
|
|
||||||
if (action && action.setBadgeBackgroundColor) {
|
|
||||||
action.setBadgeBackgroundColor({ color: "#294e5f" });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private browserActionSetBadgeText(text: string, tabId: number) {
|
|
||||||
if (chrome.browserAction && chrome.browserAction.setBadgeText) {
|
|
||||||
chrome.browserAction.setBadgeText({
|
|
||||||
text: text,
|
|
||||||
tabId: tabId,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private sidebarActionSetBadgeText(text: string, tabId: number) {
|
|
||||||
if (!this.sidebarAction) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.sidebarAction.setBadgeText) {
|
|
||||||
this.sidebarAction.setBadgeText({
|
|
||||||
text: text,
|
|
||||||
tabId: tabId,
|
|
||||||
});
|
|
||||||
} else if (this.sidebarAction.setTitle) {
|
|
||||||
let title = "Bitwarden";
|
|
||||||
if (text && text !== "") {
|
|
||||||
title += " [" + text + "]";
|
|
||||||
}
|
|
||||||
|
|
||||||
this.sidebarAction.setTitle({
|
|
||||||
title: title,
|
|
||||||
tabId: tabId,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ export default class RuntimeBackground {
|
|||||||
};
|
};
|
||||||
|
|
||||||
BrowserApi.messageListener("runtime.background", backgroundMessageListener);
|
BrowserApi.messageListener("runtime.background", backgroundMessageListener);
|
||||||
if (this.main.isPrivateMode) {
|
if (this.main.popupOnlyContext) {
|
||||||
(window as any).bitwardenBackgroundMessageListener = backgroundMessageListener;
|
(window as any).bitwardenBackgroundMessageListener = backgroundMessageListener;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -71,8 +71,8 @@ export default class RuntimeBackground {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
await this.main.setIcon();
|
await this.main.refreshBadge();
|
||||||
await this.main.refreshBadgeAndMenu(false);
|
await this.main.refreshMenu(false);
|
||||||
this.notificationsService.updateConnection(msg.command === "unlocked");
|
this.notificationsService.updateConnection(msg.command === "unlocked");
|
||||||
this.systemService.cancelProcessReload();
|
this.systemService.cancelProcessReload();
|
||||||
|
|
||||||
@@ -93,7 +93,10 @@ export default class RuntimeBackground {
|
|||||||
break;
|
break;
|
||||||
case "syncCompleted":
|
case "syncCompleted":
|
||||||
if (msg.successfully) {
|
if (msg.successfully) {
|
||||||
setTimeout(async () => await this.main.refreshBadgeAndMenu(), 2000);
|
setTimeout(async () => {
|
||||||
|
await this.main.refreshBadge();
|
||||||
|
await this.main.refreshMenu();
|
||||||
|
}, 2000);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case "openPopup":
|
case "openPopup":
|
||||||
@@ -112,7 +115,8 @@ export default class RuntimeBackground {
|
|||||||
case "editedCipher":
|
case "editedCipher":
|
||||||
case "addedCipher":
|
case "addedCipher":
|
||||||
case "deletedCipher":
|
case "deletedCipher":
|
||||||
await this.main.refreshBadgeAndMenu();
|
await this.main.refreshBadge();
|
||||||
|
await this.main.refreshMenu();
|
||||||
break;
|
break;
|
||||||
case "bgReseedStorage":
|
case "bgReseedStorage":
|
||||||
await this.main.reseedStorage();
|
await this.main.reseedStorage();
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import { CachedServices, factory, FactoryOptions } from "./factory-options";
|
|||||||
|
|
||||||
type CryptoFunctionServiceFactoryOptions = FactoryOptions & {
|
type CryptoFunctionServiceFactoryOptions = FactoryOptions & {
|
||||||
cryptoFunctionServiceOptions: {
|
cryptoFunctionServiceOptions: {
|
||||||
win: Window | typeof global;
|
win: Window | typeof globalThis;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { AbstractStorageService } from "@bitwarden/common/abstractions/storage.service";
|
import { AbstractStorageService } from "@bitwarden/common/abstractions/storage.service";
|
||||||
import { MemoryStorageService } from "@bitwarden/common/services/memoryStorage.service";
|
import { MemoryStorageService } from "@bitwarden/common/services/memoryStorage.service";
|
||||||
|
|
||||||
|
import { BrowserApi } from "../../browser/browserApi";
|
||||||
import BrowserLocalStorageService from "../../services/browserLocalStorage.service";
|
import BrowserLocalStorageService from "../../services/browserLocalStorage.service";
|
||||||
import { LocalBackedSessionStorageService } from "../../services/localBackedSessionStorage.service";
|
import { LocalBackedSessionStorageService } from "../../services/localBackedSessionStorage.service";
|
||||||
|
|
||||||
@@ -38,7 +39,7 @@ export function memoryStorageServiceFactory(
|
|||||||
opts: MemoryStorageServiceInitOptions
|
opts: MemoryStorageServiceInitOptions
|
||||||
): Promise<AbstractStorageService> {
|
): Promise<AbstractStorageService> {
|
||||||
return factory(cache, "memoryStorageService", opts, async () => {
|
return factory(cache, "memoryStorageService", opts, async () => {
|
||||||
if (chrome.runtime.getManifest().manifest_version == 3) {
|
if (BrowserApi.manifestVersion === 3) {
|
||||||
return new LocalBackedSessionStorageService(
|
return new LocalBackedSessionStorageService(
|
||||||
await encryptServiceFactory(cache, opts),
|
await encryptServiceFactory(cache, opts),
|
||||||
await keyGenerationServiceFactory(cache, opts)
|
await keyGenerationServiceFactory(cache, opts)
|
||||||
|
|||||||
@@ -28,6 +28,6 @@ export async function twoFactorServiceFactory(
|
|||||||
await platformUtilsServiceFactory(cache, opts)
|
await platformUtilsServiceFactory(cache, opts)
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
await service.init();
|
service.init();
|
||||||
return service;
|
return service;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,7 +24,8 @@ export default class TabsBackground {
|
|||||||
});
|
});
|
||||||
|
|
||||||
chrome.tabs.onActivated.addListener(async (activeInfo: chrome.tabs.TabActiveInfo) => {
|
chrome.tabs.onActivated.addListener(async (activeInfo: chrome.tabs.TabActiveInfo) => {
|
||||||
await this.main.refreshBadgeAndMenu();
|
await this.main.refreshBadge();
|
||||||
|
await this.main.refreshMenu();
|
||||||
this.main.messagingService.send("tabChanged");
|
this.main.messagingService.send("tabChanged");
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -35,7 +36,8 @@ export default class TabsBackground {
|
|||||||
this.main.onReplacedRan = true;
|
this.main.onReplacedRan = true;
|
||||||
|
|
||||||
await this.notificationBackground.checkNotificationQueue();
|
await this.notificationBackground.checkNotificationQueue();
|
||||||
await this.main.refreshBadgeAndMenu();
|
await this.main.refreshBadge();
|
||||||
|
await this.main.refreshMenu();
|
||||||
this.main.messagingService.send("tabChanged");
|
this.main.messagingService.send("tabChanged");
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -55,7 +57,8 @@ export default class TabsBackground {
|
|||||||
this.main.onUpdatedRan = true;
|
this.main.onUpdatedRan = true;
|
||||||
|
|
||||||
await this.notificationBackground.checkNotificationQueue(tab);
|
await this.notificationBackground.checkNotificationQueue(tab);
|
||||||
await this.main.refreshBadgeAndMenu();
|
await this.main.refreshBadge();
|
||||||
|
await this.main.refreshMenu();
|
||||||
this.main.messagingService.send("tabChanged");
|
this.main.messagingService.send("tabChanged");
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ import { PlatformUtilsService } from "@bitwarden/common/abstractions/platformUti
|
|||||||
import { AuthenticationStatus } from "@bitwarden/common/enums/authenticationStatus";
|
import { AuthenticationStatus } from "@bitwarden/common/enums/authenticationStatus";
|
||||||
import { UriMatchType } from "@bitwarden/common/enums/uriMatchType";
|
import { UriMatchType } from "@bitwarden/common/enums/uriMatchType";
|
||||||
|
|
||||||
|
import { BrowserApi } from "../browser/browserApi";
|
||||||
|
|
||||||
export default class WebRequestBackground {
|
export default class WebRequestBackground {
|
||||||
private pendingAuthRequests: any[] = [];
|
private pendingAuthRequests: any[] = [];
|
||||||
private webRequest: any;
|
private webRequest: any;
|
||||||
@@ -14,8 +16,7 @@ export default class WebRequestBackground {
|
|||||||
private cipherService: CipherService,
|
private cipherService: CipherService,
|
||||||
private authService: AuthService
|
private authService: AuthService
|
||||||
) {
|
) {
|
||||||
const manifest = chrome.runtime.getManifest();
|
if (BrowserApi.manifestVersion === 2) {
|
||||||
if (manifest.manifest_version === 2) {
|
|
||||||
this.webRequest = (window as any).chrome.webRequest;
|
this.webRequest = (window as any).chrome.webRequest;
|
||||||
}
|
}
|
||||||
this.isFirefox = platformUtilsService.isFirefox();
|
this.isFirefox = platformUtilsService.isFirefox();
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import BrowserPlatformUtilsService from "../services/browserPlatformUtils.service";
|
||||||
import { TabMessage } from "../types/tab-messages";
|
import { TabMessage } from "../types/tab-messages";
|
||||||
|
|
||||||
export class BrowserApi {
|
export class BrowserApi {
|
||||||
@@ -10,6 +11,10 @@ export class BrowserApi {
|
|||||||
static isFirefoxOnAndroid: boolean =
|
static isFirefoxOnAndroid: boolean =
|
||||||
navigator.userAgent.indexOf("Firefox/") !== -1 && navigator.userAgent.indexOf("Android") !== -1;
|
navigator.userAgent.indexOf("Firefox/") !== -1 && navigator.userAgent.indexOf("Android") !== -1;
|
||||||
|
|
||||||
|
static get manifestVersion() {
|
||||||
|
return chrome.runtime.getManifest().manifest_version;
|
||||||
|
}
|
||||||
|
|
||||||
static async getTabFromCurrentWindowId(): Promise<chrome.tabs.Tab> | null {
|
static async getTabFromCurrentWindowId(): Promise<chrome.tabs.Tab> | null {
|
||||||
return await BrowserApi.tabsQueryFirst({
|
return await BrowserApi.tabsQueryFirst({
|
||||||
active: true,
|
active: true,
|
||||||
@@ -17,6 +22,13 @@ export class BrowserApi {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static async getTab(tabId: number) {
|
||||||
|
if (tabId == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return await chrome.tabs.get(tabId);
|
||||||
|
}
|
||||||
|
|
||||||
static async getTabFromCurrentWindow(): Promise<chrome.tabs.Tab> | null {
|
static async getTabFromCurrentWindow(): Promise<chrome.tabs.Tab> | null {
|
||||||
return await BrowserApi.tabsQueryFirst({
|
return await BrowserApi.tabsQueryFirst({
|
||||||
active: true,
|
active: true,
|
||||||
@@ -211,4 +223,16 @@ export class BrowserApi {
|
|||||||
chrome.runtime.getPlatformInfo(resolve);
|
chrome.runtime.getPlatformInfo(resolve);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static getBrowserAction() {
|
||||||
|
return BrowserApi.manifestVersion === 3 ? chrome.action : chrome.browserAction;
|
||||||
|
}
|
||||||
|
|
||||||
|
static getSidebarAction(win: Window & typeof globalThis) {
|
||||||
|
return BrowserPlatformUtilsService.isSafari(win)
|
||||||
|
? null
|
||||||
|
: typeof win.opr !== "undefined" && win.opr.sidebarAction
|
||||||
|
? win.opr.sidebarAction
|
||||||
|
: win.chrome.sidebarAction;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ export class SessionSyncer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
init() {
|
init() {
|
||||||
if (chrome.runtime.getManifest().manifest_version != 3) {
|
if (BrowserApi.manifestVersion !== 3) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
24
apps/browser/src/globals.d.ts
vendored
24
apps/browser/src/globals.d.ts
vendored
@@ -100,19 +100,12 @@ type OperaSidebarAction = {
|
|||||||
onBlur: OperaEvent<Window>;
|
onBlur: OperaEvent<Window>;
|
||||||
};
|
};
|
||||||
|
|
||||||
type Opera = {
|
/**
|
||||||
addons: OperaAddons;
|
* This is for firefox's sidebar action and it is based on the opera one but with a few less methods
|
||||||
sidebarAction: OperaSidebarAction;
|
|
||||||
};
|
|
||||||
|
|
||||||
declare namespace chrome {
|
|
||||||
/**
|
|
||||||
* This is for firefoxes sidebar action and it is based on the opera one but with a few less methods
|
|
||||||
*
|
*
|
||||||
* @link https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/sidebarAction
|
* @link https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/sidebarAction
|
||||||
*/
|
*/
|
||||||
let sidebarAction:
|
type FirefoxSidebarAction = Omit<
|
||||||
| Omit<
|
|
||||||
OperaSidebarAction,
|
OperaSidebarAction,
|
||||||
| "setBadgeText"
|
| "setBadgeText"
|
||||||
| "getBadgeText"
|
| "getBadgeText"
|
||||||
@@ -120,8 +113,15 @@ declare namespace chrome {
|
|||||||
| "getBadgeBackgroundColor"
|
| "getBadgeBackgroundColor"
|
||||||
| "onFocus"
|
| "onFocus"
|
||||||
| "onBlur"
|
| "onBlur"
|
||||||
>
|
>;
|
||||||
| undefined;
|
|
||||||
|
type Opera = {
|
||||||
|
addons: OperaAddons;
|
||||||
|
sidebarAction: OperaSidebarAction;
|
||||||
|
};
|
||||||
|
|
||||||
|
declare namespace chrome {
|
||||||
|
let sidebarAction: FirefoxSidebarAction | undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Window {
|
interface Window {
|
||||||
|
|||||||
276
apps/browser/src/listeners/update-badge.ts
Normal file
276
apps/browser/src/listeners/update-badge.ts
Normal file
@@ -0,0 +1,276 @@
|
|||||||
|
import { AbstractEncryptService } from "@bitwarden/common/abstractions/abstractEncrypt.service";
|
||||||
|
import { AuthService } from "@bitwarden/common/abstractions/auth.service";
|
||||||
|
import { CipherService } from "@bitwarden/common/abstractions/cipher.service";
|
||||||
|
import { CryptoService } from "@bitwarden/common/abstractions/crypto.service";
|
||||||
|
import { AuthenticationStatus } from "@bitwarden/common/enums/authenticationStatus";
|
||||||
|
import { StateFactory } from "@bitwarden/common/factories/stateFactory";
|
||||||
|
import { Utils } from "@bitwarden/common/misc/utils";
|
||||||
|
import { GlobalState } from "@bitwarden/common/models/domain/global-state";
|
||||||
|
import { ContainerService } from "@bitwarden/common/services/container.service";
|
||||||
|
|
||||||
|
import IconDetails from "../background/models/iconDetails";
|
||||||
|
import { authServiceFactory } from "../background/service_factories/auth-service.factory";
|
||||||
|
import { cipherServiceFactory } from "../background/service_factories/cipher-service.factory";
|
||||||
|
import { searchServiceFactory } from "../background/service_factories/search-service.factory";
|
||||||
|
import { stateServiceFactory } from "../background/service_factories/state-service.factory";
|
||||||
|
import { BrowserApi } from "../browser/browserApi";
|
||||||
|
import { Account } from "../models/account";
|
||||||
|
import { StateService } from "../services/abstractions/state.service";
|
||||||
|
import BrowserPlatformUtilsService from "../services/browserPlatformUtils.service";
|
||||||
|
|
||||||
|
export type BadgeOptions = {
|
||||||
|
tab?: chrome.tabs.Tab;
|
||||||
|
windowId?: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
export class UpdateBadge {
|
||||||
|
private authService: AuthService;
|
||||||
|
private stateService: StateService;
|
||||||
|
private cipherService: CipherService;
|
||||||
|
private badgeAction: typeof chrome.action;
|
||||||
|
private sidebarAction: OperaSidebarAction | FirefoxSidebarAction;
|
||||||
|
private inited = false;
|
||||||
|
private win: Window & typeof globalThis;
|
||||||
|
|
||||||
|
private static readonly listenedToCommands = [
|
||||||
|
"updateBadge",
|
||||||
|
"loggedIn",
|
||||||
|
"unlocked",
|
||||||
|
"syncCompleted",
|
||||||
|
"bgUpdateContextMenu",
|
||||||
|
"editedCipher",
|
||||||
|
"addedCipher",
|
||||||
|
"deletedCipher",
|
||||||
|
];
|
||||||
|
|
||||||
|
static async tabsOnActivatedListener(activeInfo: chrome.tabs.TabActiveInfo) {
|
||||||
|
await new UpdateBadge(self).run({ tabId: activeInfo.tabId });
|
||||||
|
}
|
||||||
|
|
||||||
|
static async tabsOnReplacedListener(addedTabId: number, removedTabId: number) {
|
||||||
|
await new UpdateBadge(self).run({ tabId: addedTabId });
|
||||||
|
}
|
||||||
|
|
||||||
|
static async tabsOnUpdatedListener(tabId: number) {
|
||||||
|
await new UpdateBadge(self).run({ tabId });
|
||||||
|
}
|
||||||
|
|
||||||
|
static async messageListener(
|
||||||
|
serviceCache: Record<string, unknown>,
|
||||||
|
message: { command: string; tabId: number }
|
||||||
|
) {
|
||||||
|
if (!UpdateBadge.listenedToCommands.includes(message.command)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await new UpdateBadge(self).run();
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(win: Window & typeof globalThis) {
|
||||||
|
this.badgeAction = BrowserApi.getBrowserAction();
|
||||||
|
this.sidebarAction = BrowserApi.getSidebarAction(self);
|
||||||
|
this.win = win;
|
||||||
|
}
|
||||||
|
|
||||||
|
async run(opts?: {
|
||||||
|
tabId?: number;
|
||||||
|
windowId?: number;
|
||||||
|
existingServices?: Record<string, unknown>;
|
||||||
|
}): Promise<void> {
|
||||||
|
await this.initServices(opts?.existingServices);
|
||||||
|
|
||||||
|
const authStatus = await this.authService.getAuthStatus();
|
||||||
|
|
||||||
|
const tab = await this.getTab(opts?.tabId, opts?.windowId);
|
||||||
|
const windowId = tab?.windowId;
|
||||||
|
|
||||||
|
await this.setBadgeBackgroundColor();
|
||||||
|
|
||||||
|
switch (authStatus) {
|
||||||
|
case AuthenticationStatus.LoggedOut: {
|
||||||
|
await this.setLoggedOut({ tab, windowId });
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case AuthenticationStatus.Locked: {
|
||||||
|
await this.setLocked({ tab, windowId });
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case AuthenticationStatus.Unlocked: {
|
||||||
|
await this.setUnlocked({ tab, windowId });
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async setLoggedOut(opts: BadgeOptions): Promise<void> {
|
||||||
|
await this.setBadgeIcon("_gray", opts?.windowId);
|
||||||
|
await this.setBadgeText("", opts?.tab?.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
async setLocked(opts: BadgeOptions) {
|
||||||
|
await this.setBadgeIcon("_locked", opts?.windowId);
|
||||||
|
await this.setBadgeText("", opts?.tab?.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
async setUnlocked(opts: BadgeOptions) {
|
||||||
|
await this.initServices();
|
||||||
|
|
||||||
|
await this.setBadgeIcon("", opts?.windowId);
|
||||||
|
|
||||||
|
const disableBadgeCounter = await this.stateService.getDisableBadgeCounter();
|
||||||
|
if (disableBadgeCounter) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ciphers = await this.cipherService.getAllDecryptedForUrl(opts?.tab?.url);
|
||||||
|
let countText = ciphers.length == 0 ? "" : ciphers.length.toString();
|
||||||
|
if (ciphers.length > 9) {
|
||||||
|
countText = "9+";
|
||||||
|
}
|
||||||
|
await this.setBadgeText(countText, opts?.tab?.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
setBadgeBackgroundColor(color = "#294e5f") {
|
||||||
|
if (this.badgeAction?.setBadgeBackgroundColor) {
|
||||||
|
this.badgeAction.setBadgeBackgroundColor({ color });
|
||||||
|
}
|
||||||
|
if (this.isOperaSidebar(this.sidebarAction)) {
|
||||||
|
this.sidebarAction.setBadgeBackgroundColor({ color });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setBadgeText(text: string, tabId?: number) {
|
||||||
|
this.setActionText(text, tabId);
|
||||||
|
this.setSideBarText(text, tabId);
|
||||||
|
}
|
||||||
|
|
||||||
|
async setBadgeIcon(iconSuffix: string, windowId?: number) {
|
||||||
|
const options: IconDetails = {
|
||||||
|
path: {
|
||||||
|
19: "/images/icon19" + iconSuffix + ".png",
|
||||||
|
38: "/images/icon38" + iconSuffix + ".png",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
if (BrowserPlatformUtilsService.isFirefox()) {
|
||||||
|
options.windowId = windowId;
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.setActionIcon(options);
|
||||||
|
await this.setSidebarActionIcon(options);
|
||||||
|
}
|
||||||
|
|
||||||
|
private setActionText(text: string, tabId?: number) {
|
||||||
|
if (this.badgeAction?.setBadgeText) {
|
||||||
|
this.badgeAction.setBadgeText({ text, tabId });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private setSideBarText(text: string, tabId?: number) {
|
||||||
|
if (this.isOperaSidebar(this.sidebarAction)) {
|
||||||
|
this.sidebarAction.setBadgeText({ text, tabId });
|
||||||
|
} else if (this.sidebarAction) {
|
||||||
|
// Firefox
|
||||||
|
const title = `Bitwarden${Utils.isNullOrEmpty(text) ? "" : ` [${text}]`}`;
|
||||||
|
this.sidebarAction.setTitle({ title, tabId });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async setActionIcon(options: IconDetails) {
|
||||||
|
if (!this.badgeAction?.setIcon) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.useSyncApiCalls) {
|
||||||
|
this.badgeAction.setIcon(options);
|
||||||
|
} else {
|
||||||
|
await new Promise<void>((resolve) => this.badgeAction.setIcon(options, () => resolve()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async setSidebarActionIcon(options: IconDetails) {
|
||||||
|
if (!this.sidebarAction?.setIcon) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.useSyncApiCalls) {
|
||||||
|
this.sidebarAction.setIcon(options);
|
||||||
|
} else {
|
||||||
|
await new Promise<void>((resolve) => this.sidebarAction.setIcon(options, () => resolve()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async getTab(tabId?: number, windowId?: number) {
|
||||||
|
return (
|
||||||
|
(await BrowserApi.getTab(tabId)) ??
|
||||||
|
(await BrowserApi.tabsQueryFirst({ active: true, windowId })) ??
|
||||||
|
(await BrowserApi.tabsQueryFirst({ active: true, lastFocusedWindow: true })) ??
|
||||||
|
(await BrowserApi.tabsQueryFirst({ active: true }))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private get useSyncApiCalls() {
|
||||||
|
return (
|
||||||
|
BrowserPlatformUtilsService.isFirefox() || BrowserPlatformUtilsService.isSafari(this.win)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async initServices(existingServiceCache?: Record<string, unknown>): Promise<UpdateBadge> {
|
||||||
|
if (this.inited) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
const serviceCache: Record<string, unknown> = existingServiceCache || {};
|
||||||
|
const opts = {
|
||||||
|
cryptoFunctionServiceOptions: { win: self },
|
||||||
|
encryptServiceOptions: { logMacFailures: false },
|
||||||
|
logServiceOptions: { isDev: false },
|
||||||
|
platformUtilsServiceOptions: {
|
||||||
|
clipboardWriteCallback: (clipboardValue: string, clearMs: number) =>
|
||||||
|
Promise.reject("not implemented"),
|
||||||
|
biometricCallback: () => Promise.reject("not implemented"),
|
||||||
|
win: self,
|
||||||
|
},
|
||||||
|
stateServiceOptions: {
|
||||||
|
stateFactory: new StateFactory(GlobalState, Account),
|
||||||
|
},
|
||||||
|
stateMigrationServiceOptions: {
|
||||||
|
stateFactory: new StateFactory(GlobalState, Account),
|
||||||
|
},
|
||||||
|
apiServiceOptions: {
|
||||||
|
logoutCallback: () => Promise.reject("not implemented"),
|
||||||
|
},
|
||||||
|
keyConnectorServiceOptions: {
|
||||||
|
logoutCallback: () => Promise.reject("not implemented"),
|
||||||
|
},
|
||||||
|
i18nServiceOptions: {
|
||||||
|
systemLanguage: BrowserApi.getUILanguage(self),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
this.stateService = await stateServiceFactory(serviceCache, opts);
|
||||||
|
this.authService = await authServiceFactory(serviceCache, opts);
|
||||||
|
const searchService = await searchServiceFactory(serviceCache, opts);
|
||||||
|
|
||||||
|
this.cipherService = await cipherServiceFactory(serviceCache, {
|
||||||
|
...opts,
|
||||||
|
cipherServiceOptions: { searchServiceFactory: () => searchService },
|
||||||
|
});
|
||||||
|
|
||||||
|
// Needed for cipher decryption
|
||||||
|
if (!self.bitwardenContainerService) {
|
||||||
|
new ContainerService(
|
||||||
|
serviceCache.cryptoService as CryptoService,
|
||||||
|
serviceCache.encryptService as AbstractEncryptService
|
||||||
|
).attachToGlobal(self);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.inited = true;
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
private isOperaSidebar(
|
||||||
|
action: OperaSidebarAction | FirefoxSidebarAction
|
||||||
|
): action is OperaSidebarAction {
|
||||||
|
return action != null && (action as OperaSidebarAction).setBadgeText != null;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -68,13 +68,14 @@ import { PopupSearchService } from "./popup-search.service";
|
|||||||
import { PopupUtilsService } from "./popup-utils.service";
|
import { PopupUtilsService } from "./popup-utils.service";
|
||||||
import { UnauthGuardService } from "./unauth-guard.service";
|
import { UnauthGuardService } from "./unauth-guard.service";
|
||||||
|
|
||||||
const isPrivateMode = BrowserApi.getBackgroundPage() == null;
|
const needsBackgroundInit = BrowserApi.getBackgroundPage() == null;
|
||||||
const mainBackground: MainBackground = isPrivateMode
|
const isPrivateMode = needsBackgroundInit && BrowserApi.manifestVersion !== 3;
|
||||||
|
const mainBackground: MainBackground = needsBackgroundInit
|
||||||
? createLocalBgService()
|
? createLocalBgService()
|
||||||
: BrowserApi.getBackgroundPage().bitwardenMain;
|
: BrowserApi.getBackgroundPage().bitwardenMain;
|
||||||
|
|
||||||
function createLocalBgService() {
|
function createLocalBgService() {
|
||||||
const localBgService = new MainBackground(true);
|
const localBgService = new MainBackground(isPrivateMode);
|
||||||
localBgService.bootstrap();
|
localBgService.bootstrap();
|
||||||
return localBgService;
|
return localBgService;
|
||||||
}
|
}
|
||||||
@@ -108,7 +109,7 @@ function getBgService<T>(service: keyof MainBackground) {
|
|||||||
{
|
{
|
||||||
provide: MessagingService,
|
provide: MessagingService,
|
||||||
useFactory: () => {
|
useFactory: () => {
|
||||||
return isPrivateMode
|
return needsBackgroundInit
|
||||||
? new BrowserMessagingPrivateModePopupService()
|
? new BrowserMessagingPrivateModePopupService()
|
||||||
: new BrowserMessagingService();
|
: new BrowserMessagingService();
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ describe("Browser Utils Service", () => {
|
|||||||
let browserPlatformUtilsService: BrowserPlatformUtilsService;
|
let browserPlatformUtilsService: BrowserPlatformUtilsService;
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
(window as any).matchMedia = jest.fn().mockReturnValueOnce({});
|
(window as any).matchMedia = jest.fn().mockReturnValueOnce({});
|
||||||
browserPlatformUtilsService = new BrowserPlatformUtilsService(null, null, null, self);
|
browserPlatformUtilsService = new BrowserPlatformUtilsService(null, null, null, window);
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
|
|||||||
@@ -28,24 +28,17 @@ export default class BrowserPlatformUtilsService implements PlatformUtilsService
|
|||||||
return this.deviceCache;
|
return this.deviceCache;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (BrowserPlatformUtilsService.isFirefox()) {
|
||||||
navigator.userAgent.indexOf(" Firefox/") !== -1 ||
|
|
||||||
navigator.userAgent.indexOf(" Gecko/") !== -1
|
|
||||||
) {
|
|
||||||
this.deviceCache = DeviceType.FirefoxExtension;
|
this.deviceCache = DeviceType.FirefoxExtension;
|
||||||
} else if (
|
} else if (BrowserPlatformUtilsService.isOpera(this.win)) {
|
||||||
(!!this.win.opr && !!opr.addons) ||
|
|
||||||
!!this.win.opera ||
|
|
||||||
navigator.userAgent.indexOf(" OPR/") >= 0
|
|
||||||
) {
|
|
||||||
this.deviceCache = DeviceType.OperaExtension;
|
this.deviceCache = DeviceType.OperaExtension;
|
||||||
} else if (navigator.userAgent.indexOf(" Edg/") !== -1) {
|
} else if (BrowserPlatformUtilsService.isEdge()) {
|
||||||
this.deviceCache = DeviceType.EdgeExtension;
|
this.deviceCache = DeviceType.EdgeExtension;
|
||||||
} else if (navigator.userAgent.indexOf(" Vivaldi/") !== -1) {
|
} else if (BrowserPlatformUtilsService.isVivaldi()) {
|
||||||
this.deviceCache = DeviceType.VivaldiExtension;
|
this.deviceCache = DeviceType.VivaldiExtension;
|
||||||
} else if (this.win.chrome && navigator.userAgent.indexOf(" Chrome/") !== -1) {
|
} else if (BrowserPlatformUtilsService.isChrome(this.win)) {
|
||||||
this.deviceCache = DeviceType.ChromeExtension;
|
this.deviceCache = DeviceType.ChromeExtension;
|
||||||
} else if (navigator.userAgent.indexOf(" Safari/") !== -1) {
|
} else if (BrowserPlatformUtilsService.isSafari(this.win)) {
|
||||||
this.deviceCache = DeviceType.SafariExtension;
|
this.deviceCache = DeviceType.SafariExtension;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -61,26 +54,58 @@ export default class BrowserPlatformUtilsService implements PlatformUtilsService
|
|||||||
return ClientType.Browser;
|
return ClientType.Browser;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static isFirefox(): boolean {
|
||||||
|
return (
|
||||||
|
navigator.userAgent.indexOf(" Firefox/") !== -1 ||
|
||||||
|
navigator.userAgent.indexOf(" Gecko/") !== -1
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
isFirefox(): boolean {
|
isFirefox(): boolean {
|
||||||
return this.getDevice() === DeviceType.FirefoxExtension;
|
return this.getDevice() === DeviceType.FirefoxExtension;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static isChrome(win: Window & typeof globalThis): boolean {
|
||||||
|
return win.chrome && navigator.userAgent.indexOf(" Chrome/") !== -1;
|
||||||
|
}
|
||||||
|
|
||||||
isChrome(): boolean {
|
isChrome(): boolean {
|
||||||
return this.getDevice() === DeviceType.ChromeExtension;
|
return this.getDevice() === DeviceType.ChromeExtension;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static isEdge(): boolean {
|
||||||
|
return navigator.userAgent.indexOf(" Edg/") !== -1;
|
||||||
|
}
|
||||||
|
|
||||||
isEdge(): boolean {
|
isEdge(): boolean {
|
||||||
return this.getDevice() === DeviceType.EdgeExtension;
|
return this.getDevice() === DeviceType.EdgeExtension;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static isOpera(win: Window & typeof globalThis): boolean {
|
||||||
|
return (
|
||||||
|
(!!win.opr && !!win.opr.addons) || !!win.opera || navigator.userAgent.indexOf(" OPR/") >= 0
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
isOpera(): boolean {
|
isOpera(): boolean {
|
||||||
return this.getDevice() === DeviceType.OperaExtension;
|
return this.getDevice() === DeviceType.OperaExtension;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static isVivaldi(): boolean {
|
||||||
|
return navigator.userAgent.indexOf(" Vivaldi/") !== -1;
|
||||||
|
}
|
||||||
|
|
||||||
isVivaldi(): boolean {
|
isVivaldi(): boolean {
|
||||||
return this.getDevice() === DeviceType.VivaldiExtension;
|
return this.getDevice() === DeviceType.VivaldiExtension;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static isSafari(win: Window & typeof globalThis): boolean {
|
||||||
|
// Opera masquerades as Safari, so make sure we're not there first
|
||||||
|
return (
|
||||||
|
!BrowserPlatformUtilsService.isOpera(win) && navigator.userAgent.indexOf(" Safari/") !== -1
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
isSafari(): boolean {
|
isSafari(): boolean {
|
||||||
return this.getDevice() === DeviceType.SafariExtension;
|
return this.getDevice() === DeviceType.SafariExtension;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user