1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-23 11:43:46 +00:00

[PM-17479] Load-sdk-once (#12764)

* create service to load sdk on application init

* Eagerly load CLI SDK

* Remove wasm logging to api

* Fix imports

* Eagerly load Desktop renderer SDK

Note: If the main process ever requires an SDK, we'll need to load it there, too.
In that event, it's probably a good idea to move to IPC for all SDK functions to avoid
loading the SDK for every window.

* init wasm module from sdk load service

* Use default client factory

* Fix type imports

* Resolve jest module import errors

A CLI sdk load service that async imports our wasm binary doesn't seem to be needed to run, but jest isn't dealing with the ESM import properly.

* Fix linting

* remove example code
This commit is contained in:
Matt Gibson
2025-01-23 11:34:22 -08:00
committed by GitHub
parent 620affd3d5
commit f9f30f8ec4
19 changed files with 151 additions and 97 deletions

View File

@@ -92,6 +92,7 @@ import { I18nService as I18nServiceAbstraction } from "@bitwarden/common/platfor
import { KeyGenerationService as KeyGenerationServiceAbstraction } from "@bitwarden/common/platform/abstractions/key-generation.service";
import { LogService as LogServiceAbstraction } from "@bitwarden/common/platform/abstractions/log.service";
import { PlatformUtilsService as PlatformUtilsServiceAbstraction } from "@bitwarden/common/platform/abstractions/platform-utils.service";
import { SdkLoadService } from "@bitwarden/common/platform/abstractions/sdk/sdk-load.service";
import { SdkService } from "@bitwarden/common/platform/abstractions/sdk/sdk.service";
import { StateService as StateServiceAbstraction } from "@bitwarden/common/platform/abstractions/state.service";
import {
@@ -125,6 +126,7 @@ import { FileUploadService } from "@bitwarden/common/platform/services/file-uplo
import { KeyGenerationService } from "@bitwarden/common/platform/services/key-generation.service";
import { MigrationBuilderService } from "@bitwarden/common/platform/services/migration-builder.service";
import { MigrationRunner } from "@bitwarden/common/platform/services/migration-runner";
import { DefaultSdkClientFactory } from "@bitwarden/common/platform/services/sdk/default-sdk-client-factory";
import { DefaultSdkService } from "@bitwarden/common/platform/services/sdk/default-sdk.service";
import { NoopSdkClientFactory } from "@bitwarden/common/platform/services/sdk/noop-sdk-client-factory";
import { StateService } from "@bitwarden/common/platform/services/state.service";
@@ -260,7 +262,7 @@ import { LocalBackedSessionStorageService } from "../platform/services/local-bac
import { BackgroundPlatformUtilsService } from "../platform/services/platform-utils/background-platform-utils.service";
import { BrowserPlatformUtilsService } from "../platform/services/platform-utils/browser-platform-utils.service";
import { PopupViewCacheBackgroundService } from "../platform/services/popup-view-cache-background.service";
import { BrowserSdkClientFactory } from "../platform/services/sdk/browser-sdk-client-factory";
import { BrowserSdkLoadService } from "../platform/services/sdk/browser-sdk-load.service";
import { BackgroundTaskSchedulerService } from "../platform/services/task-scheduler/background-task-scheduler.service";
import { BackgroundMemoryStorageService } from "../platform/storage/background-memory-storage.service";
import { BrowserStorageServiceProvider } from "../platform/storage/browser-storage-service.provider";
@@ -379,6 +381,7 @@ export default class MainBackground {
themeStateService: DefaultThemeStateService;
autoSubmitLoginBackground: AutoSubmitLoginBackground;
sdkService: SdkService;
sdkLoadService: SdkLoadService;
cipherAuthorizationService: CipherAuthorizationService;
inlineMenuFieldQualificationService: InlineMenuFieldQualificationService;
@@ -730,8 +733,9 @@ export default class MainBackground {
);
const sdkClientFactory = flagEnabled("sdk")
? new BrowserSdkClientFactory(this.logService)
? new DefaultSdkClientFactory()
: new NoopSdkClientFactory();
this.sdkLoadService = new BrowserSdkLoadService(this.logService);
this.sdkService = new DefaultSdkService(
sdkClientFactory,
this.environmentService,
@@ -1257,6 +1261,7 @@ export default class MainBackground {
async bootstrap() {
this.containerService.attachToGlobal(self);
await this.sdkLoadService.load();
// Only the "true" background should run migrations
await this.stateService.init({ runMigrations: true });

View File

@@ -1,11 +1,12 @@
// FIXME: Update this file to be type safe and remove this and next line
// @ts-strict-ignore
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
import { SdkClientFactory } from "@bitwarden/common/platform/abstractions/sdk/sdk-client-factory";
import type { BitwardenClient } from "@bitwarden/sdk-internal";
import { SdkLoadService } from "@bitwarden/common/platform/abstractions/sdk/sdk-load.service";
import { BrowserApi } from "../../browser/browser-api";
export type GlobalWithWasmInit = typeof globalThis & {
initSdk: () => void;
};
// https://stackoverflow.com/a/47880734
const supported = (() => {
try {
@@ -17,9 +18,7 @@ const supported = (() => {
return new WebAssembly.Instance(module) instanceof WebAssembly.Instance;
}
}
// FIXME: Remove when updating file. Eslint update
// eslint-disable-next-line @typescript-eslint/no-unused-vars
} catch (e) {
} catch {
// ignore
}
return false;
@@ -33,54 +32,42 @@ let loadingPromise: Promise<any> | undefined;
if (BrowserApi.isManifestVersion(3)) {
if (supported) {
// eslint-disable-next-line no-console
console.debug("WebAssembly is supported in this environment");
console.info("WebAssembly is supported in this environment");
loadingPromise = import("./wasm");
} else {
// eslint-disable-next-line no-console
console.debug("WebAssembly is not supported in this environment");
console.info("WebAssembly is not supported in this environment");
loadingPromise = import("./fallback");
}
}
// Manifest v2 expects dynamic imports to prevent timing issues.
async function load() {
async function importModule(): Promise<GlobalWithWasmInit["initSdk"]> {
if (BrowserApi.isManifestVersion(3)) {
// Ensure we have loaded the module
await loadingPromise;
return;
}
if (supported) {
} else if (supported) {
// eslint-disable-next-line no-console
console.debug("WebAssembly is supported in this environment");
console.info("WebAssembly is supported in this environment");
await import("./wasm");
} else {
// eslint-disable-next-line no-console
console.debug("WebAssembly is not supported in this environment");
console.info("WebAssembly is not supported in this environment");
await import("./fallback");
}
// the wasm and fallback imports mutate globalThis to add the initSdk function
return (globalThis as GlobalWithWasmInit).initSdk;
}
/**
* SDK client factory with a js fallback for when WASM is not supported.
*
* Works both in popup and service worker.
*/
export class BrowserSdkClientFactory implements SdkClientFactory {
constructor(private logService: LogService) {}
export class BrowserSdkLoadService implements SdkLoadService {
constructor(readonly logService: LogService) {}
async createSdkClient(
...args: ConstructorParameters<typeof BitwardenClient>
): Promise<BitwardenClient> {
async load(): Promise<void> {
const startTime = performance.now();
await load();
await importModule().then((initSdk) => initSdk());
const endTime = performance.now();
const instance = (globalThis as any).init_sdk(...args);
this.logService.info("WASM SDK loaded in", Math.round(endTime - startTime), "ms");
return instance;
this.logService.info(`WASM SDK loaded in ${Math.round(endTime - startTime)}ms`);
}
}

View File

@@ -1,8 +1,8 @@
import * as sdk from "@bitwarden/sdk-internal";
import * as wasm from "@bitwarden/sdk-internal/bitwarden_wasm_internal_bg.wasm.js";
(globalThis as any).init_sdk = (...args: ConstructorParameters<typeof sdk.BitwardenClient>) => {
(sdk as any).init(wasm);
import { GlobalWithWasmInit } from "./browser-sdk-load.service";
return new sdk.BitwardenClient(...args);
(globalThis as GlobalWithWasmInit).initSdk = () => {
(sdk as any).init(wasm);
};

View File

@@ -1,8 +1,8 @@
import * as sdk from "@bitwarden/sdk-internal";
import * as wasm from "@bitwarden/sdk-internal/bitwarden_wasm_internal_bg.wasm";
(globalThis as any).init_sdk = (...args: ConstructorParameters<typeof sdk.BitwardenClient>) => {
(sdk as any).init(wasm);
import { GlobalWithWasmInit } from "./browser-sdk-load.service";
return new sdk.BitwardenClient(...args);
(globalThis as GlobalWithWasmInit).initSdk = () => {
(sdk as any).init(wasm);
};

View File

@@ -6,6 +6,7 @@ import { TwoFactorService } from "@bitwarden/common/auth/abstractions/two-factor
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { LogService as LogServiceAbstraction } from "@bitwarden/common/platform/abstractions/log.service";
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
import { SdkLoadService } from "@bitwarden/common/platform/abstractions/sdk/sdk-load.service";
import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
import { BrowserApi } from "../../platform/browser/browser-api";
@@ -22,11 +23,13 @@ export class InitService {
private twoFactorService: TwoFactorService,
private logService: LogServiceAbstraction,
private themingService: AbstractThemingService,
private sdkLoadService: SdkLoadService,
@Inject(DOCUMENT) private document: Document,
) {}
init() {
return async () => {
await this.sdkLoadService.load();
await this.stateService.init({ runMigrations: false }); // Browser background is responsible for migrations
await this.i18nService.init();
this.twoFactorService.init();

View File

@@ -70,6 +70,7 @@ import { LogService } from "@bitwarden/common/platform/abstractions/log.service"
import { MessagingService as MessagingServiceAbstraction } from "@bitwarden/common/platform/abstractions/messaging.service";
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
import { SdkClientFactory } from "@bitwarden/common/platform/abstractions/sdk/sdk-client-factory";
import { SdkLoadService } from "@bitwarden/common/platform/abstractions/sdk/sdk-load.service";
import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
import {
AbstractStorageService,
@@ -82,6 +83,7 @@ import { flagEnabled } from "@bitwarden/common/platform/misc/flags";
import { TaskSchedulerService } from "@bitwarden/common/platform/scheduling";
import { ConsoleLogService } from "@bitwarden/common/platform/services/console-log.service";
import { ContainerService } from "@bitwarden/common/platform/services/container.service";
import { DefaultSdkClientFactory } from "@bitwarden/common/platform/services/sdk/default-sdk-client-factory";
import { NoopSdkClientFactory } from "@bitwarden/common/platform/services/sdk/noop-sdk-client-factory";
import { StorageServiceProvider } from "@bitwarden/common/platform/services/storage-service.provider";
import { WebCryptoFunctionService } from "@bitwarden/common/platform/services/web-crypto-function.service";
@@ -144,7 +146,7 @@ import BrowserMemoryStorageService from "../../platform/services/browser-memory-
import { BrowserScriptInjectorService } from "../../platform/services/browser-script-injector.service";
import I18nService from "../../platform/services/i18n.service";
import { ForegroundPlatformUtilsService } from "../../platform/services/platform-utils/foreground-platform-utils.service";
import { BrowserSdkClientFactory } from "../../platform/services/sdk/browser-sdk-client-factory";
import { BrowserSdkLoadService } from "../../platform/services/sdk/browser-sdk-load.service";
import { ForegroundTaskSchedulerService } from "../../platform/services/task-scheduler/foreground-task-scheduler.service";
import { BrowserStorageServiceProvider } from "../../platform/storage/browser-storage-service.provider";
import { ForegroundMemoryStorageService } from "../../platform/storage/foreground-memory-storage.service";
@@ -566,11 +568,16 @@ const safeProviders: SafeProvider[] = [
deps: [MessageSender, MessageListener],
}),
safeProvider({
provide: SdkClientFactory,
useFactory: (logService: LogService) =>
flagEnabled("sdk") ? new BrowserSdkClientFactory(logService) : new NoopSdkClientFactory(),
provide: SdkLoadService,
useClass: BrowserSdkLoadService,
deps: [LogService],
}),
safeProvider({
provide: SdkClientFactory,
useFactory: () =>
flagEnabled("sdk") ? new DefaultSdkClientFactory() : new NoopSdkClientFactory(),
deps: [],
}),
safeProvider({
provide: LoginEmailService,
useClass: LoginEmailService,