@@ -8,12 +8,9 @@ import { StateMigrationService } from "../../deps/bw/libs/shared/dist/src/servic
8import { TokenService } from "../../deps/bw/libs/shared/dist/src/services/token.service";
8import { TokenService } from "../../deps/bw/libs/shared/dist/src/services/token.service";
9import { WebCryptoFunctionService } from "../../deps/bw/libs/shared/dist/src/services/webCryptoFunction.service";
9import { WebCryptoFunctionService } from "../../deps/bw/libs/shared/dist/src/services/webCryptoFunction.service";
11−import { BrowserCryptoService } from "../../deps/bw/libs/shared/dist/src/services/browserCrypto.service";
12−import BrowserMessagingService from "../../deps/bw/libs/shared/dist/src/services/browserMessaging.service";
13−import BrowserMessagingPrivateModeBackgroundService from "../../deps/bw/libs/shared/dist/src/services/browserMessagingPrivateModeBackground.service";
14−import BrowserPlatformUtilsService from "../../deps/bw/libs/shared/dist/src/services/browserPlatformUtils.service";
15−import BrowserStorageService from "../../deps/bw/libs/shared/dist/src/services/browserStorage.service";
11+// import BrowserStorageService from "../../deps/bw/libs/shared/dist/src/services/browserStorage.service";
12+import { CryptoService } from "../../deps/bw/libs/shared/dist/src/abstractions/crypto.service";
16−import I18nService from "../../deps/bw/libs/shared/dist/src/services/i18n.service";
13+import { I18nService } from "../../deps/bw/libs/shared/dist/src/services/i18n.service";
17import { StateService } from "../../deps/bw/libs/shared/dist/src/services/state.service";
14import { StateService } from "../../deps/bw/libs/shared/dist/src/services/state.service";
19export function getAPI() {
16export function getAPI() {
@@ -159,3 +156,345 @@ class BrowserStorageService {
159 this.storage.removeItem(key)
156 this.storage.removeItem(key)
160+class BrowserCryptoService extends CryptoService {
161+ async retrieveKeyFromStorage(keySuffix) {
162+ if (keySuffix === "biometric") {
163+ await this.platformUtilService.authenticateBiometric();
164+ return (await this.getKey())?.keyB64;
167+ return await super.retrieveKeyFromStorage(keySuffix);
171+class BrowserMessagingService {
172+ send(subscriber, arg = {}) {
173+ const message = Object.assign({}, { command: subscriber }, arg);
174+ chrome.runtime.sendMessage(message);
178+class BrowserMessagingPrivateModeBackgroundService {
179+ send(subscriber, arg = {}) {
180+ const message = Object.assign({}, { command: subscriber }, arg);
181+ window.bitwardenPopupMainMessageListener(message);
185+class BrowserPlatformUtilsService {
186+ constructor(messagingService, stateService, clipboardWriteCallback, biometricCallback) {
187+ this.messagingService = messagingService;
188+ this.stateService = stateService;
189+ this.clipboardWriteCallback = clipboardWriteCallback;
190+ this.biometricCallback = biometricCallback;
191+ this.showDialogResolves = new Map();
192+ this.passwordDialogResolves = new Map();
193+ this.deviceCache = null;
194+ this.prefersColorSchemeDark = window.matchMedia("(prefers-color-scheme: dark)");
197+ if (this.deviceCache) {
198+ return this.deviceCache;
200+ if (navigator.userAgent.indexOf(" Firefox/") !== -1 ||
201+ navigator.userAgent.indexOf(" Gecko/") !== -1) {
202+ this.deviceCache = DeviceType.FirefoxExtension;
204+ else if ((!!window.opr && !!opr.addons) ||
206+ navigator.userAgent.indexOf(" OPR/") >= 0) {
207+ this.deviceCache = DeviceType.OperaExtension;
209+ else if (navigator.userAgent.indexOf(" Edg/") !== -1) {
210+ this.deviceCache = DeviceType.EdgeExtension;
212+ else if (navigator.userAgent.indexOf(" Vivaldi/") !== -1) {
213+ this.deviceCache = DeviceType.VivaldiExtension;
215+ else if (window.chrome && navigator.userAgent.indexOf(" Chrome/") !== -1) {
216+ this.deviceCache = DeviceType.ChromeExtension;
218+ else if (navigator.userAgent.indexOf(" Safari/") !== -1) {
219+ this.deviceCache = DeviceType.SafariExtension;
221+ return this.deviceCache;
224+ const device = DeviceType[this.getDevice()].toLowerCase();
225+ return device.replace("extension", "");
228+ return ClientType.Browser;
231+ return this.getDevice() === DeviceType.FirefoxExtension;
234+ return this.getDevice() === DeviceType.ChromeExtension;
237+ return this.getDevice() === DeviceType.EdgeExtension;
240+ return this.getDevice() === DeviceType.OperaExtension;
243+ return this.getDevice() === DeviceType.VivaldiExtension;
246+ return this.getDevice() === DeviceType.SafariExtension;
255+ return __awaiter(this, void 0, void 0, function* () {
256+ if (yield BrowserApi.isPopupOpen()) {
259+ if (this.isSafari()) {
262+ const sidebarView = this.sidebarViewName();
263+ const sidebarOpen = sidebarView != null && chrome.extension.getViews({ type: sidebarView }).length > 0;
267+ const tabOpen = chrome.extension.getViews({ type: "tab" }).length > 0;
274+ launchUri(uri, options) {
275+ BrowserApi.createNewTab(uri, options && options.extensionPage === true);
277+ saveFile(win, blobData, blobOptions, fileName) {
278+ BrowserApi.downloadFile(win, blobData, blobOptions, fileName);
280+ getApplicationVersion() {
281+ return Promise.resolve(BrowserApi.getApplicationVersion());
283+ supportsWebAuthn(win) {
284+ return typeof PublicKeyCredential !== "undefined";
289+ showToast(type, title, text, options) {
290+ this.messagingService.send("showToast", {
297+ showDialog(body, title, confirmText, cancelText, type, bodyIsHtml = false) {
298+ const dialogId = Math.floor(Math.random() * Number.MAX_SAFE_INTEGER);
299+ this.messagingService.send("showDialog", {
300+ text: bodyIsHtml ? null : body,
301+ html: bodyIsHtml ? body : null,
303+ confirmText: confirmText,
304+ cancelText: cancelText,
308+ return new Promise((resolve) => {
309+ this.showDialogResolves.set(dialogId, { resolve: resolve, date: new Date() });
313+ return process.env.ENV === "development";
318+ copyToClipboard(text, options) {
320+ let doc = window.document;
321+ if (options && (options.window || options.win)) {
322+ win = options.window || options.win;
325+ else if (options && options.doc) {
328+ const clearing = options ? !!options.clearing : false;
329+ const clearMs = options && options.clearMs ? options.clearMs : null;
330+ if (this.isSafari()) {
331+ SafariApp.sendMessageToApp("copyToClipboard", text).then(() => {
332+ if (!clearing && this.clipboardWriteCallback != null) {
333+ this.clipboardWriteCallback(text, clearMs);
337+ else if (this.isFirefox() &&
338+ win.navigator.clipboard &&
339+ win.navigator.clipboard.writeText) {
340+ win.navigator.clipboard.writeText(text).then(() => {
341+ if (!clearing && this.clipboardWriteCallback != null) {
342+ this.clipboardWriteCallback(text, clearMs);
346+ else if (win.clipboardData && win.clipboardData.setData) {
347+ // IE specific code path to prevent textarea being shown while dialog is visible.
348+ win.clipboardData.setData("Text", text);
349+ if (!clearing && this.clipboardWriteCallback != null) {
350+ this.clipboardWriteCallback(text, clearMs);
353+ else if (doc.queryCommandSupported && doc.queryCommandSupported("copy")) {
354+ if (this.isChrome() && text === "") {
357+ const textarea = doc.createElement("textarea");
358+ textarea.textContent = text == null || text === "" ? " " : text;
359+ // Prevent scrolling to bottom of page in MS Edge.
360+ textarea.style.position = "fixed";
361+ doc.body.appendChild(textarea);
364+ // Security exception may be thrown by some browsers.
365+ if (doc.execCommand("copy") && !clearing && this.clipboardWriteCallback != null) {
366+ this.clipboardWriteCallback(text, clearMs);
370+ // eslint-disable-next-line
371+ console.warn("Copy to clipboard failed.", e);
374+ doc.body.removeChild(textarea);
378+ readFromClipboard(options) {
379+ return __awaiter(this, void 0, void 0, function* () {
381+ let doc = window.document;
382+ if (options && (options.window || options.win)) {
383+ win = options.window || options.win;
386+ else if (options && options.doc) {
389+ if (this.isSafari()) {
390+ return yield SafariApp.sendMessageToApp("readFromClipboard");
392+ else if (this.isFirefox() &&
393+ win.navigator.clipboard &&
394+ win.navigator.clipboard.readText) {
395+ return yield win.navigator.clipboard.readText();
397+ else if (doc.queryCommandSupported && doc.queryCommandSupported("paste")) {
398+ const textarea = doc.createElement("textarea");
399+ // Prevent scrolling to bottom of page in MS Edge.
400+ textarea.style.position = "fixed";
401+ doc.body.appendChild(textarea);
404+ // Security exception may be thrown by some browsers.
405+ if (doc.execCommand("paste")) {
406+ return textarea.value;
410+ // eslint-disable-next-line
411+ console.warn("Read from clipboard failed.", e);
414+ doc.body.removeChild(textarea);
420+ resolveDialogPromise(dialogId, confirmed) {
421+ if (this.showDialogResolves.has(dialogId)) {
422+ const resolveObj = this.showDialogResolves.get(dialogId);
423+ resolveObj.resolve(confirmed);
424+ this.showDialogResolves.delete(dialogId);
426+ // Clean up old promises
427+ this.showDialogResolves.forEach((val, key) => {
428+ const age = new Date().getTime() - val.date.getTime();
429+ if (age > DialogPromiseExpiration) {
430+ this.showDialogResolves.delete(key);
434+ resolvePasswordDialogPromise(dialogId, canceled, password) {
435+ return __awaiter(this, void 0, void 0, function* () {
437+ if (this.passwordDialogResolves.has(dialogId)) {
438+ const resolveObj = this.passwordDialogResolves.get(dialogId);
439+ if (yield resolveObj.tryResolve(canceled, password)) {
440+ this.passwordDialogResolves.delete(dialogId);
444+ // Clean up old promises
445+ this.passwordDialogResolves.forEach((val, key) => {
446+ const age = new Date().getTime() - val.date.getTime();
447+ if (age > DialogPromiseExpiration) {
448+ this.passwordDialogResolves.delete(key);
454+ supportsBiometric() {
455+ return __awaiter(this, void 0, void 0, function* () {
456+ const platformInfo = yield BrowserApi.getPlatformInfo();
457+ if (platformInfo.os === "android") {
460+ if (this.isFirefox()) {
461+ return parseInt((yield browser.runtime.getBrowserInfo()).version.split(".")[0], 10) >= 87;
466+ authenticateBiometric() {
467+ return this.biometricCallback();
470+ if (window.chrome.sidebarAction && this.isFirefox()) {
473+ else if (this.isOpera() && typeof opr !== "undefined" && opr.sidebarAction) {
474+ return "sidebar_panel";
478+ supportsSecureStorage() {
481+ getDefaultSystemTheme() {
482+ return Promise.resolve(this.prefersColorSchemeDark.matches ? ThemeType.Dark : ThemeType.Light);
484+ onDefaultSystemThemeChange(callback) {
485+ this.prefersColorSchemeDark.addEventListener("change", ({ matches }) => {
486+ callback(matches ? ThemeType.Dark : ThemeType.Light);
489+ getEffectiveTheme() {
490+ return __awaiter(this, void 0, void 0, function* () {
491+ const theme = (yield this.stateService.getTheme());
492+ if (theme == null || theme === ThemeType.System) {
493+ return this.getDefaultSystemTheme();