@@ -8,12 +8,9 @@ import { StateMigrationService } from "../../deps/bw/libs/shared/dist/src/servic
88import { TokenService } from "../../deps/bw/libs/shared/dist/src/services/token.service";
99import { WebCryptoFunctionService } from "../../deps/bw/libs/shared/dist/src/services/webCryptoFunction.service";
1010
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";
16−import I18nService from "../../deps/bw/libs/shared/dist/src/services/i18n.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";
13+import { I18nService } from "../../deps/bw/libs/shared/dist/src/services/i18n.service";
1714import { StateService } from "../../deps/bw/libs/shared/dist/src/services/state.service";
1815
1916export function getAPI() {
@@ -159,3 +156,345 @@ class BrowserStorageService {
159156 this.storage.removeItem(key)
160157 }
161158}
159+
160+class BrowserCryptoService extends CryptoService {
161+ async retrieveKeyFromStorage(keySuffix) {
162+ if (keySuffix === "biometric") {
163+ await this.platformUtilService.authenticateBiometric();
164+ return (await this.getKey())?.keyB64;
165+ }
166+
167+ return await super.retrieveKeyFromStorage(keySuffix);
168+ }
169+}
170+
171+class BrowserMessagingService {
172+ send(subscriber, arg = {}) {
173+ const message = Object.assign({}, { command: subscriber }, arg);
174+ chrome.runtime.sendMessage(message);
175+ }
176+}
177+
178+class BrowserMessagingPrivateModeBackgroundService {
179+ send(subscriber, arg = {}) {
180+ const message = Object.assign({}, { command: subscriber }, arg);
181+ window.bitwardenPopupMainMessageListener(message);
182+ }
183+}
184+
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)");
195+ }
196+ getDevice() {
197+ if (this.deviceCache) {
198+ return this.deviceCache;
199+ }
200+ if (navigator.userAgent.indexOf(" Firefox/") !== -1 ||
201+ navigator.userAgent.indexOf(" Gecko/") !== -1) {
202+ this.deviceCache = DeviceType.FirefoxExtension;
203+ }
204+ else if ((!!window.opr && !!opr.addons) ||
205+ !!window.opera ||
206+ navigator.userAgent.indexOf(" OPR/") >= 0) {
207+ this.deviceCache = DeviceType.OperaExtension;
208+ }
209+ else if (navigator.userAgent.indexOf(" Edg/") !== -1) {
210+ this.deviceCache = DeviceType.EdgeExtension;
211+ }
212+ else if (navigator.userAgent.indexOf(" Vivaldi/") !== -1) {
213+ this.deviceCache = DeviceType.VivaldiExtension;
214+ }
215+ else if (window.chrome && navigator.userAgent.indexOf(" Chrome/") !== -1) {
216+ this.deviceCache = DeviceType.ChromeExtension;
217+ }
218+ else if (navigator.userAgent.indexOf(" Safari/") !== -1) {
219+ this.deviceCache = DeviceType.SafariExtension;
220+ }
221+ return this.deviceCache;
222+ }
223+ getDeviceString() {
224+ const device = DeviceType[this.getDevice()].toLowerCase();
225+ return device.replace("extension", "");
226+ }
227+ getClientType() {
228+ return ClientType.Browser;
229+ }
230+ isFirefox() {
231+ return this.getDevice() === DeviceType.FirefoxExtension;
232+ }
233+ isChrome() {
234+ return this.getDevice() === DeviceType.ChromeExtension;
235+ }
236+ isEdge() {
237+ return this.getDevice() === DeviceType.EdgeExtension;
238+ }
239+ isOpera() {
240+ return this.getDevice() === DeviceType.OperaExtension;
241+ }
242+ isVivaldi() {
243+ return this.getDevice() === DeviceType.VivaldiExtension;
244+ }
245+ isSafari() {
246+ return this.getDevice() === DeviceType.SafariExtension;
247+ }
248+ isIE() {
249+ return false;
250+ }
251+ isMacAppStore() {
252+ return false;
253+ }
254+ isViewOpen() {
255+ return __awaiter(this, void 0, void 0, function* () {
256+ if (yield BrowserApi.isPopupOpen()) {
257+ return true;
258+ }
259+ if (this.isSafari()) {
260+ return false;
261+ }
262+ const sidebarView = this.sidebarViewName();
263+ const sidebarOpen = sidebarView != null && chrome.extension.getViews({ type: sidebarView }).length > 0;
264+ if (sidebarOpen) {
265+ return true;
266+ }
267+ const tabOpen = chrome.extension.getViews({ type: "tab" }).length > 0;
268+ return tabOpen;
269+ });
270+ }
271+ lockTimeout() {
272+ return null;
273+ }
274+ launchUri(uri, options) {
275+ BrowserApi.createNewTab(uri, options && options.extensionPage === true);
276+ }
277+ saveFile(win, blobData, blobOptions, fileName) {
278+ BrowserApi.downloadFile(win, blobData, blobOptions, fileName);
279+ }
280+ getApplicationVersion() {
281+ return Promise.resolve(BrowserApi.getApplicationVersion());
282+ }
283+ supportsWebAuthn(win) {
284+ return typeof PublicKeyCredential !== "undefined";
285+ }
286+ supportsDuo() {
287+ return true;
288+ }
289+ showToast(type, title, text, options) {
290+ this.messagingService.send("showToast", {
291+ text: text,
292+ title: title,
293+ type: type,
294+ options: options,
295+ });
296+ }
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,
302+ title: title,
303+ confirmText: confirmText,
304+ cancelText: cancelText,
305+ type: type,
306+ dialogId: dialogId,
307+ });
308+ return new Promise((resolve) => {
309+ this.showDialogResolves.set(dialogId, { resolve: resolve, date: new Date() });
310+ });
311+ }
312+ isDev() {
313+ return process.env.ENV === "development";
314+ }
315+ isSelfHost() {
316+ return false;
317+ }
318+ copyToClipboard(text, options) {
319+ let win = window;
320+ let doc = window.document;
321+ if (options && (options.window || options.win)) {
322+ win = options.window || options.win;
323+ doc = win.document;
324+ }
325+ else if (options && options.doc) {
326+ doc = options.doc;
327+ }
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);
334+ }
335+ });
336+ }
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);
343+ }
344+ });
345+ }
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);
351+ }
352+ }
353+ else if (doc.queryCommandSupported && doc.queryCommandSupported("copy")) {
354+ if (this.isChrome() && text === "") {
355+ text = "\u0000";
356+ }
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);
362+ textarea.select();
363+ try {
364+ // Security exception may be thrown by some browsers.
365+ if (doc.execCommand("copy") && !clearing && this.clipboardWriteCallback != null) {
366+ this.clipboardWriteCallback(text, clearMs);
367+ }
368+ }
369+ catch (e) {
370+ // eslint-disable-next-line
371+ console.warn("Copy to clipboard failed.", e);
372+ }
373+ finally {
374+ doc.body.removeChild(textarea);
375+ }
376+ }
377+ }
378+ readFromClipboard(options) {
379+ return __awaiter(this, void 0, void 0, function* () {
380+ let win = window;
381+ let doc = window.document;
382+ if (options && (options.window || options.win)) {
383+ win = options.window || options.win;
384+ doc = win.document;
385+ }
386+ else if (options && options.doc) {
387+ doc = options.doc;
388+ }
389+ if (this.isSafari()) {
390+ return yield SafariApp.sendMessageToApp("readFromClipboard");
391+ }
392+ else if (this.isFirefox() &&
393+ win.navigator.clipboard &&
394+ win.navigator.clipboard.readText) {
395+ return yield win.navigator.clipboard.readText();
396+ }
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);
402+ textarea.focus();
403+ try {
404+ // Security exception may be thrown by some browsers.
405+ if (doc.execCommand("paste")) {
406+ return textarea.value;
407+ }
408+ }
409+ catch (e) {
410+ // eslint-disable-next-line
411+ console.warn("Read from clipboard failed.", e);
412+ }
413+ finally {
414+ doc.body.removeChild(textarea);
415+ }
416+ }
417+ return null;
418+ });
419+ }
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);
425+ }
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);
431+ }
432+ });
433+ }
434+ resolvePasswordDialogPromise(dialogId, canceled, password) {
435+ return __awaiter(this, void 0, void 0, function* () {
436+ let result = false;
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);
441+ result = true;
442+ }
443+ }
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);
449+ }
450+ });
451+ return result;
452+ });
453+ }
454+ supportsBiometric() {
455+ return __awaiter(this, void 0, void 0, function* () {
456+ const platformInfo = yield BrowserApi.getPlatformInfo();
457+ if (platformInfo.os === "android") {
458+ return false;
459+ }
460+ if (this.isFirefox()) {
461+ return parseInt((yield browser.runtime.getBrowserInfo()).version.split(".")[0], 10) >= 87;
462+ }
463+ return true;
464+ });
465+ }
466+ authenticateBiometric() {
467+ return this.biometricCallback();
468+ }
469+ sidebarViewName() {
470+ if (window.chrome.sidebarAction && this.isFirefox()) {
471+ return "sidebar";
472+ }
473+ else if (this.isOpera() && typeof opr !== "undefined" && opr.sidebarAction) {
474+ return "sidebar_panel";
475+ }
476+ return null;
477+ }
478+ supportsSecureStorage() {
479+ return false;
480+ }
481+ getDefaultSystemTheme() {
482+ return Promise.resolve(this.prefersColorSchemeDark.matches ? ThemeType.Dark : ThemeType.Light);
483+ }
484+ onDefaultSystemThemeChange(callback) {
485+ this.prefersColorSchemeDark.addEventListener("change", ({ matches }) => {
486+ callback(matches ? ThemeType.Dark : ThemeType.Light);
487+ });
488+ }
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();
494+ }
495+ else {
496+ return theme;
497+ }
498+ });
499+ }
500+}