diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs index 9dd7b5c2e6f..c0d1e0eb8ab 100644 --- a/.git-blame-ignore-revs +++ b/.git-blame-ignore-revs @@ -22,3 +22,6 @@ 193434461dbd9c48fe5dcbad95693470aec422ac # Jslib: Monorepository https://github.com/bitwarden/clients/pull/2824/commits/d7492e3cf320410e74ebd0e0675ab994e64bd01a d7492e3cf320410e74ebd0e0675ab994e64bd01a + +# All Clients: Apply Prettier https://github.com/bitwarden/clients/pull/7014 +28de9439beb87133c8683434df952a0c0be94100 diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 7d216ac257e..38eb90b1b07 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1,11 +1,9 @@ -# Please sort lines alphabetically, this will ensure we don't accidentally add duplicates. +# Please sort into logical groups with comment headers. Sort groups in order of specificity. +# For example, default owners should always be the first group. +# Sort lines alphabetically within these groups to avoid accidentally adding duplicates. # # https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/about-code-owners -# The following owners will be the default owners for everything in the repo. -# Unless a later match takes precedence -* @bitwarden/tech-leads - ## Secrets Manager team files ## bitwarden_license/bit-web/src/app/secrets-manager @bitwarden/team-secrets-manager-dev @@ -91,30 +89,6 @@ libs/components @bitwarden/team-component-library ## Desktop native module ## apps/desktop/desktop_native @bitwarden/team-platform-dev -## Multiple file owners ## -apps/browser/package.json -apps/browser/src/manifest.json -apps/browser/src/manifest.v3.json - -apps/cli/package.json - -apps/desktop/package.json -apps/desktop/src/package-lock.json -apps/desktop/src/package.json - -/apps/web/config -/apps/web/package.json - -package.json -package-lock.json - -## Locales ## -apps/browser/src/_locales/en/messages.json -apps/browser/store/locales/en -apps/cli/src/locales/en/messages.json -apps/desktop/src/locales/en/messages.json -apps/web/src/locales/en/messages.json - ## DevOps team files ## /.github/workflows @bitwarden/dept-devops @@ -123,3 +97,10 @@ apps/web/src/locales/en/messages.json **/*.Dockerfile @bitwarden/dept-devops **/.dockerignore @bitwarden/dept-devops **/entrypoint.sh @bitwarden/dept-devops + +## Locales ## +apps/browser/src/_locales/en/messages.json +apps/browser/store/locales/en +apps/cli/src/locales/en/messages.json +apps/desktop/src/locales/en/messages.json +apps/web/src/locales/en/messages.json diff --git a/.github/renovate.json b/.github/renovate.json index bb5816d4ce3..ae86a4c5c05 100644 --- a/.github/renovate.json +++ b/.github/renovate.json @@ -79,7 +79,6 @@ "matchPackageNames": [ "@types/duo_web_sdk", "@types/node-ipc", - "@types/zxcvbn", "duo_web_sdk", "node-ipc", "qrious", @@ -107,6 +106,7 @@ "prettier-plugin-tailwindcss", "rimraf", "tabbable", + "tldts", "wait-on" ], "description": "Autofill owned dependencies", @@ -212,17 +212,21 @@ "@electron/rebuild", "@microsoft/signalr", "@microsoft/signalr-protocol-msgpack", + "@types/jsdom", "@types/papaparse", + "@types/zxcvbn", "electron", "electron-builder", "electron-log", "electron-reload", "electron-store", "electron-updater", + "jsdom", "jszip", + "oidc-client-ts", "papaparse", - "tldts", - "utf-8-validate" + "utf-8-validate", + "zxcvbn" ], "description": "Tools owned dependencies", "commitMessagePrefix": "[deps] Tools:", @@ -233,7 +237,6 @@ "@koa/multer", "@koa/router", "@types/inquirer", - "@types/jsdom", "@types/koa", "@types/koa__multer", "@types/koa__router", @@ -249,7 +252,6 @@ "form-data", "https-proxy-agent", "inquirer", - "jsdom", "koa", "koa-bodyparser", "koa-json", diff --git a/.github/whitelist-capital-letters.txt b/.github/whitelist-capital-letters.txt index dcabbd22263..ef2ff29ed8d 100644 --- a/.github/whitelist-capital-letters.txt +++ b/.github/whitelist-capital-letters.txt @@ -40,15 +40,6 @@ ./apps/browser/README.md ./apps/browser/store/windows/AppxManifest.xml ./apps/browser/src/background/nativeMessaging.background.ts -./apps/browser/src/background/models/addLoginRuntimeMessage.ts -./apps/browser/src/background/models/addChangePasswordQueueMessage.ts -./apps/browser/src/background/models/addLoginQueueMessage.ts -./apps/browser/src/background/models/changePasswordRuntimeMessage.ts -./apps/browser/src/background/models/notificationQueueMessage.ts -./apps/browser/src/background/models/notificationQueueMessageType.ts -./apps/browser/src/background/models/lockedVaultPendingNotificationsItem.ts -./apps/browser/src/background/webRequest.background.ts -./apps/browser/src/popup/services/debounceNavigationService.ts ./apps/browser/src/models/browserComponentState.ts ./apps/browser/src/models/browserSendComponentState.ts ./apps/browser/src/models/browserGroupingsComponentState.ts diff --git a/.github/workflows/chromatic.yml b/.github/workflows/chromatic.yml index cb2b929d0e7..6eb00437ad5 100644 --- a/.github/workflows/chromatic.yml +++ b/.github/workflows/chromatic.yml @@ -44,7 +44,7 @@ jobs: run: npm run build-storybook:ci - name: Publish to Chromatic - uses: chromaui/action@d726e4e790a99e876f71b8e09d3053bfe783d6b8 + uses: chromaui/action@2f12dc37555ffc9ed980d883e96b6d03724a2d6a # v10.0.0 with: token: ${{ secrets.GITHUB_TOKEN }} projectToken: ${{ secrets.CHROMATIC_PROJECT_TOKEN }} diff --git a/.github/workflows/deploy-non-prod-web.yml b/.github/workflows/deploy-non-prod-web.yml index 651b6a83726..a3e6547ff49 100644 --- a/.github/workflows/deploy-non-prod-web.yml +++ b/.github/workflows/deploy-non-prod-web.yml @@ -71,6 +71,21 @@ jobs: echo "environment-url=http://vault.bitwarden.eu" >> $GITHUB_OUTPUT fi + notify-start: + name: Notify Slack with start message + runs-on: ubuntu-22.04 + if: always() + steps: + - uses: bitwarden/gh-actions/report-deployment-status-to-slack@main + with: + project: Web + environment: US ${{ inputs.environment }} Cloud + tag: ${{ github.ref_name }} + slack-channel: team-eng-qa-devops + event: 'start' + url: https://github.com/bitwarden/clients/actions/runs/${{ github.run_id }} + AZURE_KV_CI_SERVICE_PRINCIPAL: ${{ secrets.AZURE_KV_CI_SERVICE_PRINCIPAL }} + artifact-check: name: Check if Web artifact is present runs-on: ubuntu-22.04 @@ -209,6 +224,7 @@ jobs: project: Web environment: ${{ needs.setup.outputs.environment-name }} tag: ${{ github.event.inputs.tag }} - slack-channel: devops-alerts-test - failure: ${{ needs.azure-deploy.result == 'failure' }} + slack-channel: team-eng-qa-devops + event: ${{ needs.azure-deploy.result }} + url: https://github.com/bitwarden/clients/actions/runs/${{ github.run_id }} AZURE_KV_CI_SERVICE_PRINCIPAL: ${{ secrets.AZURE_KV_CI_SERVICE_PRINCIPAL }} diff --git a/.storybook/preview.tsx b/.storybook/preview.tsx index 0bc9fc5bacb..af20dcad74e 100644 --- a/.storybook/preview.tsx +++ b/.storybook/preview.tsx @@ -48,7 +48,7 @@ const decorator = componentWrapperDecorator( }, ({ globals }) => { return { theme: `${globals["theme"]}` }; - } + }, ); const preview: Preview = { diff --git a/apps/browser/config/development.json b/apps/browser/config/development.json index eafd0ffd878..1b628c173ce 100644 --- a/apps/browser/config/development.json +++ b/apps/browser/config/development.json @@ -7,6 +7,7 @@ }, "flags": { "showPasswordless": true, - "enableCipherKeyEncryption": false + "enableCipherKeyEncryption": false, + "accountSwitching": true } } diff --git a/apps/browser/config/production.json b/apps/browser/config/production.json index f57c3d9bc38..027003f6c75 100644 --- a/apps/browser/config/production.json +++ b/apps/browser/config/production.json @@ -1,5 +1,6 @@ { "flags": { - "enableCipherKeyEncryption": false + "enableCipherKeyEncryption": false, + "accountSwitching": true } } diff --git a/apps/browser/gulpfile.js b/apps/browser/gulpfile.js index af1a2d8c7db..a8f55cdee80 100644 --- a/apps/browser/gulpfile.js +++ b/apps/browser/gulpfile.js @@ -1,13 +1,11 @@ const child = require("child_process"); const fs = require("fs"); -const del = require("del"); +const { rimraf } = require("rimraf"); const gulp = require("gulp"); -const filter = require("gulp-filter"); const gulpif = require("gulp-if"); const jeditor = require("gulp-json-editor"); const replace = require("gulp-replace"); -const zip = require("gulp-zip"); const manifest = require("./src/manifest.json"); @@ -47,7 +45,10 @@ function distFileName(browserName, ext) { return `dist-${browserName}${buildString()}.${ext}`; } -function dist(browserName, manifest) { +async function dist(browserName, manifest) { + const { default: zip } = await import("gulp-zip"); + const { default: filter } = await import("gulp-filter"); + return gulp .src(paths.build + "**/*") .pipe(filter(["**"].concat(filters.fonts).concat(filters.safari))) @@ -130,7 +131,7 @@ function distSafariApp(cb, subBuildPath) { ]; } - return del([buildPath + "**/*"]) + return rimraf([buildPath + "**/*"], { glob: true }) .then(() => safariCopyAssets(paths.safari + "**/*", buildPath)) .then(() => safariCopyBuild(paths.build + "**/*", buildPath + "safari/app")) .then(() => { @@ -144,7 +145,9 @@ function distSafariApp(cb, subBuildPath) { stdOutProc(proc); return new Promise((resolve) => proc.on("close", resolve)); }) - .then(() => { + .then(async () => { + const { default: filter } = await import("gulp-filter"); + const libs = fs .readdirSync(builtAppexFrameworkPath) .filter((p) => p.endsWith(".dylib")) @@ -168,7 +171,7 @@ function distSafariApp(cb, subBuildPath) { }, () => { return cb; - } + }, ); } @@ -179,7 +182,7 @@ function safariCopyAssets(source, dest) { .on("error", reject) .pipe(gulpif("safari/Info.plist", replace("0.0.1", manifest.version))) .pipe( - gulpif("safari/Info.plist", replace("0.0.2", process.env.BUILD_NUMBER || manifest.version)) + gulpif("safari/Info.plist", replace("0.0.2", process.env.BUILD_NUMBER || manifest.version)), ) .pipe(gulpif("desktop.xcodeproj/project.pbxproj", replace("../../../build", "../safari/app"))) .pipe(gulp.dest(dest)) @@ -187,7 +190,9 @@ function safariCopyAssets(source, dest) { }); } -function safariCopyBuild(source, dest) { +async function safariCopyBuild(source, dest) { + const { default: filter } = await import("gulp-filter"); + return new Promise((resolve, reject) => { gulp .src(source) @@ -203,8 +208,8 @@ function safariCopyBuild(source, dest) { delete manifest.optional_permissions; manifest.permissions.push("nativeMessaging"); return manifest; - }) - ) + }), + ), ) .pipe(gulp.dest(dest)) .on("end", resolve); @@ -216,7 +221,10 @@ function stdOutProc(proc) { proc.stderr.on("data", (data) => console.error(data.toString())); } -function ciCoverage(cb) { +async function ciCoverage(cb) { + const { default: zip } = await import("gulp-zip"); + const { default: filter } = await import("gulp-filter"); + return gulp .src(paths.coverage + "**/*") .pipe(filter(["**", "!coverage/coverage*.zip"])) diff --git a/apps/browser/package.json b/apps/browser/package.json index fcadb309f36..dbb2aec9bf9 100644 --- a/apps/browser/package.json +++ b/apps/browser/package.json @@ -1,6 +1,6 @@ { "name": "@bitwarden/browser", - "version": "2023.10.2", + "version": "2023.12.0", "scripts": { "build": "webpack", "build:mv3": "cross-env MANIFEST_VERSION=3 webpack", diff --git a/apps/browser/src/_locales/ar/messages.json b/apps/browser/src/_locales/ar/messages.json index 183602d4ed4..ed1a97a7fa5 100644 --- a/apps/browser/src/_locales/ar/messages.json +++ b/apps/browser/src/_locales/ar/messages.json @@ -365,6 +365,9 @@ "unlockMethodNeededToChangeTimeoutActionDesc": { "message": "أعدنّ طريقة إلغاء القُفْل لتغيير إجراء مهلة المخزن الخاص بك." }, + "unlockMethodNeeded": { + "message": "Set up an unlock method in Settings" + }, "rateExtension": { "message": "قيِّم هذه الإضافة" }, @@ -405,6 +408,9 @@ "lockNow": { "message": "إقفل الآن" }, + "lockAll": { + "message": "Lock all" + }, "immediately": { "message": "حالاً" }, @@ -1027,6 +1033,12 @@ "showAutoFillMenuOnFormFieldsDescAlt": { "message": "Applies to all logged in accounts." }, + "turnOffBrowserBuiltInPasswordManagerSettings": { + "message": "Turn off your browser’s built in password manager settings to avoid conflicts." + }, + "turnOffBrowserBuiltInPasswordManagerSettingsLink": { + "message": "Edit browser settings." + }, "autofillOverlayVisibilityOff": { "message": "Off", "description": "Overlay setting select option for disabling autofill overlay" @@ -1131,6 +1143,9 @@ "faviconDesc": { "message": "إظهار صورة قابلة للتعرف بجانب كل تسجيل دخول." }, + "faviconDescAlt": { + "message": "Show a recognizable image next to each login. Applies to all logged in accounts." + }, "enableBadgeCounter": { "message": "إظهار عداد الشارات" }, @@ -2538,36 +2553,12 @@ "message": "Bitwarden auto-fill menu available. Press the down arrow key to select.", "description": "Screen reader text for announcing when the overlay opens on the page" }, - "overrideBrowserAutofillTitle": { - "message": "Override browser auto-fill?", - "description": "Dialog title facilitating the ability to override a chrome browser's default autofill behavior" - }, - "overrideBrowserAutofillDescription": { - "message": "Leaving this setting off may cause conflicts between the Bitwarden auto-fill menu and your browser’s.", - "description": "Dialog message facilitating the ability to override a chrome browser's default autofill behavior" - }, - "overrideBrowserAutofillPrivacyRequiredDescription": { - "message": "Leaving this setting off may cause conflicts between the Bitwarden auto-fill menu and your browser’s. Turning this on will restart the Bitwarden extension.", - "description": "Dialog message facilitating the ability to override a chrome browser's default autofill behavior" - }, "turnOn": { "message": "Turn on" }, "ignore": { "message": "Ignore" }, - "overrideBrowserAutoFillSettings": { - "message": "Override browser auto-fill settings", - "description": "Label for the setting that allows overriding the default browser autofill settings" - }, - "extensionPrivacyPermissionNotGrantedTitle": { - "message": "Unable to override browser auto-fill", - "description": "Title for the dialog that appears when the user has not granted the extension permission to set privacy settings" - }, - "extensionPrivacyPermissionNotGrantedDescription": { - "message": "Bitwarden must have access to the extension's privacy permission to override browser auto-fill settings.", - "description": "Description for the dialog that appears when the user has not granted the extension permission to set privacy settings" - }, "importData": { "message": "Import data", "description": "Used for the header of the import dialog, the import button and within the file-password-prompt" @@ -2679,6 +2670,9 @@ "confirmFilePassword": { "message": "Confirm file password" }, + "typePasskey": { + "message": "Passkey" + }, "passkeyNotCopied": { "message": "Passkey will not be copied" }, @@ -2793,5 +2787,35 @@ }, "lastPassYubikeyDesc": { "message": "Insert the YubiKey associated with your LastPass account into your computer's USB port, then touch its button." + }, + "switchAccount": { + "message": "Switch account" + }, + "switchAccounts": { + "message": "Switch accounts" + }, + "switchToAccount": { + "message": "Switch to account" + }, + "activeAccount": { + "message": "Active account" + }, + "accountLimitReached": { + "message": "Account limit reached. Log out of an account to add another." + }, + "active": { + "message": "active" + }, + "locked": { + "message": "locked" + }, + "unlocked": { + "message": "unlocked" + }, + "server": { + "message": "server" + }, + "hostedAt": { + "message": "hosted at" } } diff --git a/apps/browser/src/_locales/az/messages.json b/apps/browser/src/_locales/az/messages.json index e72e1e4192b..17ec5d4354d 100644 --- a/apps/browser/src/_locales/az/messages.json +++ b/apps/browser/src/_locales/az/messages.json @@ -365,6 +365,9 @@ "unlockMethodNeededToChangeTimeoutActionDesc": { "message": "Anbar vaxt bitməsi əməliyyatınızı dəyişdirmək üçün bir kilid açma üsulu qurun." }, + "unlockMethodNeeded": { + "message": "Ayarlarda bir kilid açma üsulu qurun" + }, "rateExtension": { "message": "Uzantını qiymətləndir" }, @@ -405,6 +408,9 @@ "lockNow": { "message": "İndi kilidlə" }, + "lockAll": { + "message": "Hamısını kilidlə" + }, "immediately": { "message": "Dərhal" }, @@ -1027,6 +1033,12 @@ "showAutoFillMenuOnFormFieldsDescAlt": { "message": "Giriş etmiş bütün hesablara aiddir." }, + "turnOffBrowserBuiltInPasswordManagerSettings": { + "message": "Turn off your browser’s built in password manager settings to avoid conflicts." + }, + "turnOffBrowserBuiltInPasswordManagerSettingsLink": { + "message": "Edit browser settings." + }, "autofillOverlayVisibilityOff": { "message": "Bağlı", "description": "Overlay setting select option for disabling autofill overlay" @@ -1131,6 +1143,9 @@ "faviconDesc": { "message": "Hər girişin yanında tanına bilən təsvir göstər." }, + "faviconDescAlt": { + "message": "Hər bir giriş elementinin yanında tanına bilən bir təsvir göstər. Giriş etmiş bütün hesablara aiddir." + }, "enableBadgeCounter": { "message": "Nişan sayğacını göstər" }, @@ -2538,36 +2553,12 @@ "message": "Bitwarden avto-doldurma menyusu mövcuddur. Seçmək üçün aşağı ox düyməsinə basın.", "description": "Screen reader text for announcing when the overlay opens on the page" }, - "overrideBrowserAutofillTitle": { - "message": "Brauzerin avto-doldurması yararsız edilsin?", - "description": "Dialog title facilitating the ability to override a chrome browser's default autofill behavior" - }, - "overrideBrowserAutofillDescription": { - "message": "Bu ayarın söndürülməsi, \"Bitwarden\"in və brauzerinizin avto-doldurma menyuları arasında ziddiyyətlərə səbəb ola bilər.", - "description": "Dialog message facilitating the ability to override a chrome browser's default autofill behavior" - }, - "overrideBrowserAutofillPrivacyRequiredDescription": { - "message": "Bu ayarın söndürülməsi, \"Bitwarden\"in və brauzerinizin avto-doldurma menyuları arasında ziddiyyətlərə səbəb ola bilər. Bunu işə salmaq, Bitwarden uzantısını yenidən başladacaq.", - "description": "Dialog message facilitating the ability to override a chrome browser's default autofill behavior" - }, "turnOn": { "message": "İşə sal" }, "ignore": { "message": "Yox say" }, - "overrideBrowserAutoFillSettings": { - "message": "Brauzerin avto-doldurma ayarlarını yararsız et", - "description": "Label for the setting that allows overriding the default browser autofill settings" - }, - "extensionPrivacyPermissionNotGrantedTitle": { - "message": "Brauzerin avto-doldurması yararsız edilə bilmir", - "description": "Title for the dialog that appears when the user has not granted the extension permission to set privacy settings" - }, - "extensionPrivacyPermissionNotGrantedDescription": { - "message": "Bitwarden, brauzerin avto-doldurma ayarlarını yararsız etmək üçün uzantının gizlilik icazəsinə müraciət etməlidir.", - "description": "Description for the dialog that appears when the user has not granted the extension permission to set privacy settings" - }, "importData": { "message": "Datanı daxilə köçür", "description": "Used for the header of the import dialog, the import button and within the file-password-prompt" @@ -2679,6 +2670,9 @@ "confirmFilePassword": { "message": "Fayl parolunu təsdiqlə" }, + "typePasskey": { + "message": "Keçid açarı" + }, "passkeyNotCopied": { "message": "Keçid açarı kopyalanmır" }, @@ -2793,5 +2787,35 @@ }, "lastPassYubikeyDesc": { "message": "LastPass hesabınızla əlaqələndirilmiş \"YubiKey\"i kompüterinizin USB yuvasına taxın, daha sonra düyməsinə basın." + }, + "switchAccount": { + "message": "Hesabı dəyişdir" + }, + "switchAccounts": { + "message": "Hesabları dəyişdir" + }, + "switchToAccount": { + "message": "Hesaba keç" + }, + "activeAccount": { + "message": "Aktiv hesab" + }, + "accountLimitReached": { + "message": "Hesab limiti keçildi. Başqa bir hesab əlavə etmək üçün bir hesabdan çıxış edin." + }, + "active": { + "message": "aktiv" + }, + "locked": { + "message": "kilidli" + }, + "unlocked": { + "message": "kilidi açılmış" + }, + "server": { + "message": "server" + }, + "hostedAt": { + "message": "sahiblik edən" } } diff --git a/apps/browser/src/_locales/be/messages.json b/apps/browser/src/_locales/be/messages.json index a8ed4693c68..029288a7dea 100644 --- a/apps/browser/src/_locales/be/messages.json +++ b/apps/browser/src/_locales/be/messages.json @@ -365,6 +365,9 @@ "unlockMethodNeededToChangeTimeoutActionDesc": { "message": "Set up an unlock method to change your vault timeout action." }, + "unlockMethodNeeded": { + "message": "Set up an unlock method in Settings" + }, "rateExtension": { "message": "Ацаніць пашырэнне" }, @@ -405,6 +408,9 @@ "lockNow": { "message": "Заблакіраваць зараз" }, + "lockAll": { + "message": "Lock all" + }, "immediately": { "message": "Адразу" }, @@ -1027,6 +1033,12 @@ "showAutoFillMenuOnFormFieldsDescAlt": { "message": "Applies to all logged in accounts." }, + "turnOffBrowserBuiltInPasswordManagerSettings": { + "message": "Turn off your browser’s built in password manager settings to avoid conflicts." + }, + "turnOffBrowserBuiltInPasswordManagerSettingsLink": { + "message": "Edit browser settings." + }, "autofillOverlayVisibilityOff": { "message": "Off", "description": "Overlay setting select option for disabling autofill overlay" @@ -1131,6 +1143,9 @@ "faviconDesc": { "message": "Паказваць распазнавальны відарыс побач з кожным лагінам." }, + "faviconDescAlt": { + "message": "Show a recognizable image next to each login. Applies to all logged in accounts." + }, "enableBadgeCounter": { "message": "Паказваць лічыльнік на значку" }, @@ -2538,36 +2553,12 @@ "message": "Bitwarden auto-fill menu available. Press the down arrow key to select.", "description": "Screen reader text for announcing when the overlay opens on the page" }, - "overrideBrowserAutofillTitle": { - "message": "Override browser auto-fill?", - "description": "Dialog title facilitating the ability to override a chrome browser's default autofill behavior" - }, - "overrideBrowserAutofillDescription": { - "message": "Leaving this setting off may cause conflicts between the Bitwarden auto-fill menu and your browser’s.", - "description": "Dialog message facilitating the ability to override a chrome browser's default autofill behavior" - }, - "overrideBrowserAutofillPrivacyRequiredDescription": { - "message": "Leaving this setting off may cause conflicts between the Bitwarden auto-fill menu and your browser’s. Turning this on will restart the Bitwarden extension.", - "description": "Dialog message facilitating the ability to override a chrome browser's default autofill behavior" - }, "turnOn": { "message": "Turn on" }, "ignore": { "message": "Ignore" }, - "overrideBrowserAutoFillSettings": { - "message": "Override browser auto-fill settings", - "description": "Label for the setting that allows overriding the default browser autofill settings" - }, - "extensionPrivacyPermissionNotGrantedTitle": { - "message": "Unable to override browser auto-fill", - "description": "Title for the dialog that appears when the user has not granted the extension permission to set privacy settings" - }, - "extensionPrivacyPermissionNotGrantedDescription": { - "message": "Bitwarden must have access to the extension's privacy permission to override browser auto-fill settings.", - "description": "Description for the dialog that appears when the user has not granted the extension permission to set privacy settings" - }, "importData": { "message": "Import data", "description": "Used for the header of the import dialog, the import button and within the file-password-prompt" @@ -2679,6 +2670,9 @@ "confirmFilePassword": { "message": "Пацвердзіць пароль файла" }, + "typePasskey": { + "message": "Passkey" + }, "passkeyNotCopied": { "message": "Passkey will not be copied" }, @@ -2793,5 +2787,35 @@ }, "lastPassYubikeyDesc": { "message": "Insert the YubiKey associated with your LastPass account into your computer's USB port, then touch its button." + }, + "switchAccount": { + "message": "Switch account" + }, + "switchAccounts": { + "message": "Switch accounts" + }, + "switchToAccount": { + "message": "Switch to account" + }, + "activeAccount": { + "message": "Active account" + }, + "accountLimitReached": { + "message": "Account limit reached. Log out of an account to add another." + }, + "active": { + "message": "active" + }, + "locked": { + "message": "locked" + }, + "unlocked": { + "message": "unlocked" + }, + "server": { + "message": "server" + }, + "hostedAt": { + "message": "hosted at" } } diff --git a/apps/browser/src/_locales/bg/messages.json b/apps/browser/src/_locales/bg/messages.json index 0ee3b95aad7..e2c0346dca4 100644 --- a/apps/browser/src/_locales/bg/messages.json +++ b/apps/browser/src/_locales/bg/messages.json @@ -365,6 +365,9 @@ "unlockMethodNeededToChangeTimeoutActionDesc": { "message": "Задайте метод за отключване, за да може да промените действието при изтичане на времето за достъп до трезора." }, + "unlockMethodNeeded": { + "message": "Задайте метод за отключване в Настройките" + }, "rateExtension": { "message": "Оценяване на разширението" }, @@ -405,6 +408,9 @@ "lockNow": { "message": "Заключване сега" }, + "lockAll": { + "message": "Заключване на всички" + }, "immediately": { "message": "Незабавно" }, @@ -1027,6 +1033,12 @@ "showAutoFillMenuOnFormFieldsDescAlt": { "message": "Прилага се за всички регистрации, в които сте вписан(а)." }, + "turnOffBrowserBuiltInPasswordManagerSettings": { + "message": "Turn off your browser’s built in password manager settings to avoid conflicts." + }, + "turnOffBrowserBuiltInPasswordManagerSettingsLink": { + "message": "Edit browser settings." + }, "autofillOverlayVisibilityOff": { "message": "Изключено", "description": "Overlay setting select option for disabling autofill overlay" @@ -1131,6 +1143,9 @@ "faviconDesc": { "message": "Показване на разпознаваемо изображение до всеки запис." }, + "faviconDescAlt": { + "message": "Показване на разпознаваемо изображение до всеки елемент. Прилага се към всички акаунти, в които сте Вписан(а)." + }, "enableBadgeCounter": { "message": "Показване на брояч в значка" }, @@ -2264,7 +2279,7 @@ "message": "Как се ползва автоматичното попълване" }, "autofillSelectInfoWithCommand": { - "message": "Select an item from this screen, use the shortcut $COMMAND$, or explore other options in settings.", + "message": "Изберете елемент на този екран, използвайте комбинацията $COMMAND$ или разгледайте други опции в настройките.", "placeholders": { "command": { "content": "$1", @@ -2273,7 +2288,7 @@ } }, "autofillSelectInfoWithoutCommand": { - "message": "Select an item from this screen, or explore other options in settings." + "message": "Изберете елемент на този екран или разгледайте други опции в настройките." }, "gotIt": { "message": "Разбрано" @@ -2376,7 +2391,7 @@ "message": "Устройството е доверено" }, "inputRequired": { - "message": "Полето е задължтелно да бъде попълнено." + "message": "Полето е задължително да бъде попълнено." }, "required": { "message": "задължително" @@ -2538,36 +2553,12 @@ "message": "Има налично меню за авт. попълване на Битуорден. Натиснете стрелката надолу, за да го изберете.", "description": "Screen reader text for announcing when the overlay opens on the page" }, - "overrideBrowserAutofillTitle": { - "message": "Замяна на автоматичното попълване на браузъра?", - "description": "Dialog title facilitating the ability to override a chrome browser's default autofill behavior" - }, - "overrideBrowserAutofillDescription": { - "message": "Ако оставите тази настройка изключена, може да има конфликт между менюто за автоматично попълване на Битуорден и това на браузъра.", - "description": "Dialog message facilitating the ability to override a chrome browser's default autofill behavior" - }, - "overrideBrowserAutofillPrivacyRequiredDescription": { - "message": "Ако оставите тази настройка изключена, може да има конфликт между менюто за автоматично попълване на Битуорден и това на браузъра. Ако я включите, разширението на Битуорден ще трябва да бъде рестартирано.", - "description": "Dialog message facilitating the ability to override a chrome browser's default autofill behavior" - }, "turnOn": { "message": "Включване" }, "ignore": { "message": "Пренебрегване" }, - "overrideBrowserAutoFillSettings": { - "message": "Настройки за замяна на автоматичното попълване на браузъра", - "description": "Label for the setting that allows overriding the default browser autofill settings" - }, - "extensionPrivacyPermissionNotGrantedTitle": { - "message": "Автоматичното попълване на браузъра не може да бъде заменено", - "description": "Title for the dialog that appears when the user has not granted the extension permission to set privacy settings" - }, - "extensionPrivacyPermissionNotGrantedDescription": { - "message": "Битуорден трябва да има разрешение за достъп до поверителността на разширението, за да може да замени настройките за автоматично попълване на браузъра.", - "description": "Description for the dialog that appears when the user has not granted the extension permission to set privacy settings" - }, "importData": { "message": "Внасяне на данни", "description": "Used for the header of the import dialog, the import button and within the file-password-prompt" @@ -2679,6 +2670,9 @@ "confirmFilePassword": { "message": "Потвърждаване на паролата на файла" }, + "typePasskey": { + "message": "Секретен ключ" + }, "passkeyNotCopied": { "message": "Секретният ключ няма да бъде копиран" }, @@ -2793,5 +2787,35 @@ }, "lastPassYubikeyDesc": { "message": "Поставете устройството на YubiKey, което е свързано с регистрацията Ви в LastPass, в USB порт и натиснете бутона на устройството." + }, + "switchAccount": { + "message": "Превключване на регистрацията" + }, + "switchAccounts": { + "message": "Превключване на регистрациите" + }, + "switchToAccount": { + "message": "Превключване към регистрацията" + }, + "activeAccount": { + "message": "Активиране на регистрацията" + }, + "accountLimitReached": { + "message": "Достигнато е ограничението на броя регистрации. Излезте от някоя, за да добавите друга." + }, + "active": { + "message": "активно" + }, + "locked": { + "message": "заключено" + }, + "unlocked": { + "message": "отключено" + }, + "server": { + "message": "сървър" + }, + "hostedAt": { + "message": "hosted at" } } diff --git a/apps/browser/src/_locales/bn/messages.json b/apps/browser/src/_locales/bn/messages.json index 5827f01cde8..35e972a0edf 100644 --- a/apps/browser/src/_locales/bn/messages.json +++ b/apps/browser/src/_locales/bn/messages.json @@ -365,6 +365,9 @@ "unlockMethodNeededToChangeTimeoutActionDesc": { "message": "Set up an unlock method to change your vault timeout action." }, + "unlockMethodNeeded": { + "message": "Set up an unlock method in Settings" + }, "rateExtension": { "message": "এক্সটেনশনটি মূল্যায়ন করুন" }, @@ -405,6 +408,9 @@ "lockNow": { "message": "এখনই লক করুন" }, + "lockAll": { + "message": "Lock all" + }, "immediately": { "message": "সঙ্গে সঙ্গে" }, @@ -1027,6 +1033,12 @@ "showAutoFillMenuOnFormFieldsDescAlt": { "message": "Applies to all logged in accounts." }, + "turnOffBrowserBuiltInPasswordManagerSettings": { + "message": "Turn off your browser’s built in password manager settings to avoid conflicts." + }, + "turnOffBrowserBuiltInPasswordManagerSettingsLink": { + "message": "Edit browser settings." + }, "autofillOverlayVisibilityOff": { "message": "Off", "description": "Overlay setting select option for disabling autofill overlay" @@ -1131,6 +1143,9 @@ "faviconDesc": { "message": "Show a recognizable image next to each login." }, + "faviconDescAlt": { + "message": "Show a recognizable image next to each login. Applies to all logged in accounts." + }, "enableBadgeCounter": { "message": "Show badge counter" }, @@ -2538,36 +2553,12 @@ "message": "Bitwarden auto-fill menu available. Press the down arrow key to select.", "description": "Screen reader text for announcing when the overlay opens on the page" }, - "overrideBrowserAutofillTitle": { - "message": "Override browser auto-fill?", - "description": "Dialog title facilitating the ability to override a chrome browser's default autofill behavior" - }, - "overrideBrowserAutofillDescription": { - "message": "Leaving this setting off may cause conflicts between the Bitwarden auto-fill menu and your browser’s.", - "description": "Dialog message facilitating the ability to override a chrome browser's default autofill behavior" - }, - "overrideBrowserAutofillPrivacyRequiredDescription": { - "message": "Leaving this setting off may cause conflicts between the Bitwarden auto-fill menu and your browser’s. Turning this on will restart the Bitwarden extension.", - "description": "Dialog message facilitating the ability to override a chrome browser's default autofill behavior" - }, "turnOn": { "message": "Turn on" }, "ignore": { "message": "Ignore" }, - "overrideBrowserAutoFillSettings": { - "message": "Override browser auto-fill settings", - "description": "Label for the setting that allows overriding the default browser autofill settings" - }, - "extensionPrivacyPermissionNotGrantedTitle": { - "message": "Unable to override browser auto-fill", - "description": "Title for the dialog that appears when the user has not granted the extension permission to set privacy settings" - }, - "extensionPrivacyPermissionNotGrantedDescription": { - "message": "Bitwarden must have access to the extension's privacy permission to override browser auto-fill settings.", - "description": "Description for the dialog that appears when the user has not granted the extension permission to set privacy settings" - }, "importData": { "message": "Import data", "description": "Used for the header of the import dialog, the import button and within the file-password-prompt" @@ -2679,6 +2670,9 @@ "confirmFilePassword": { "message": "Confirm file password" }, + "typePasskey": { + "message": "Passkey" + }, "passkeyNotCopied": { "message": "Passkey will not be copied" }, @@ -2793,5 +2787,35 @@ }, "lastPassYubikeyDesc": { "message": "Insert the YubiKey associated with your LastPass account into your computer's USB port, then touch its button." + }, + "switchAccount": { + "message": "Switch account" + }, + "switchAccounts": { + "message": "Switch accounts" + }, + "switchToAccount": { + "message": "Switch to account" + }, + "activeAccount": { + "message": "Active account" + }, + "accountLimitReached": { + "message": "Account limit reached. Log out of an account to add another." + }, + "active": { + "message": "active" + }, + "locked": { + "message": "locked" + }, + "unlocked": { + "message": "unlocked" + }, + "server": { + "message": "server" + }, + "hostedAt": { + "message": "hosted at" } } diff --git a/apps/browser/src/_locales/bs/messages.json b/apps/browser/src/_locales/bs/messages.json index 87509233dda..6a6ccb9e0ee 100644 --- a/apps/browser/src/_locales/bs/messages.json +++ b/apps/browser/src/_locales/bs/messages.json @@ -365,6 +365,9 @@ "unlockMethodNeededToChangeTimeoutActionDesc": { "message": "Set up an unlock method to change your vault timeout action." }, + "unlockMethodNeeded": { + "message": "Set up an unlock method in Settings" + }, "rateExtension": { "message": "Rate the extension" }, @@ -405,6 +408,9 @@ "lockNow": { "message": "Lock now" }, + "lockAll": { + "message": "Lock all" + }, "immediately": { "message": "Immediately" }, @@ -1027,6 +1033,12 @@ "showAutoFillMenuOnFormFieldsDescAlt": { "message": "Applies to all logged in accounts." }, + "turnOffBrowserBuiltInPasswordManagerSettings": { + "message": "Turn off your browser’s built in password manager settings to avoid conflicts." + }, + "turnOffBrowserBuiltInPasswordManagerSettingsLink": { + "message": "Edit browser settings." + }, "autofillOverlayVisibilityOff": { "message": "Off", "description": "Overlay setting select option for disabling autofill overlay" @@ -1131,6 +1143,9 @@ "faviconDesc": { "message": "Show a recognizable image next to each login." }, + "faviconDescAlt": { + "message": "Show a recognizable image next to each login. Applies to all logged in accounts." + }, "enableBadgeCounter": { "message": "Show badge counter" }, @@ -2538,36 +2553,12 @@ "message": "Bitwarden auto-fill menu available. Press the down arrow key to select.", "description": "Screen reader text for announcing when the overlay opens on the page" }, - "overrideBrowserAutofillTitle": { - "message": "Override browser auto-fill?", - "description": "Dialog title facilitating the ability to override a chrome browser's default autofill behavior" - }, - "overrideBrowserAutofillDescription": { - "message": "Leaving this setting off may cause conflicts between the Bitwarden auto-fill menu and your browser’s.", - "description": "Dialog message facilitating the ability to override a chrome browser's default autofill behavior" - }, - "overrideBrowserAutofillPrivacyRequiredDescription": { - "message": "Leaving this setting off may cause conflicts between the Bitwarden auto-fill menu and your browser’s. Turning this on will restart the Bitwarden extension.", - "description": "Dialog message facilitating the ability to override a chrome browser's default autofill behavior" - }, "turnOn": { "message": "Turn on" }, "ignore": { "message": "Ignore" }, - "overrideBrowserAutoFillSettings": { - "message": "Override browser auto-fill settings", - "description": "Label for the setting that allows overriding the default browser autofill settings" - }, - "extensionPrivacyPermissionNotGrantedTitle": { - "message": "Unable to override browser auto-fill", - "description": "Title for the dialog that appears when the user has not granted the extension permission to set privacy settings" - }, - "extensionPrivacyPermissionNotGrantedDescription": { - "message": "Bitwarden must have access to the extension's privacy permission to override browser auto-fill settings.", - "description": "Description for the dialog that appears when the user has not granted the extension permission to set privacy settings" - }, "importData": { "message": "Import data", "description": "Used for the header of the import dialog, the import button and within the file-password-prompt" @@ -2679,6 +2670,9 @@ "confirmFilePassword": { "message": "Confirm file password" }, + "typePasskey": { + "message": "Passkey" + }, "passkeyNotCopied": { "message": "Passkey will not be copied" }, @@ -2793,5 +2787,35 @@ }, "lastPassYubikeyDesc": { "message": "Insert the YubiKey associated with your LastPass account into your computer's USB port, then touch its button." + }, + "switchAccount": { + "message": "Switch account" + }, + "switchAccounts": { + "message": "Switch accounts" + }, + "switchToAccount": { + "message": "Switch to account" + }, + "activeAccount": { + "message": "Active account" + }, + "accountLimitReached": { + "message": "Account limit reached. Log out of an account to add another." + }, + "active": { + "message": "active" + }, + "locked": { + "message": "locked" + }, + "unlocked": { + "message": "unlocked" + }, + "server": { + "message": "server" + }, + "hostedAt": { + "message": "hosted at" } } diff --git a/apps/browser/src/_locales/ca/messages.json b/apps/browser/src/_locales/ca/messages.json index f29c2532fab..aa242a632d9 100644 --- a/apps/browser/src/_locales/ca/messages.json +++ b/apps/browser/src/_locales/ca/messages.json @@ -365,6 +365,9 @@ "unlockMethodNeededToChangeTimeoutActionDesc": { "message": "Configura un mètode de desbloqueig per canviar l'acció del temps d'espera de la caixa forta." }, + "unlockMethodNeeded": { + "message": "Set up an unlock method in Settings" + }, "rateExtension": { "message": "Valora aquesta extensió" }, @@ -405,6 +408,9 @@ "lockNow": { "message": "Bloqueja ara" }, + "lockAll": { + "message": "Lock all" + }, "immediately": { "message": "Immediatament" }, @@ -1027,6 +1033,12 @@ "showAutoFillMenuOnFormFieldsDescAlt": { "message": "S'aplica a tots els comptes connectats." }, + "turnOffBrowserBuiltInPasswordManagerSettings": { + "message": "Turn off your browser’s built in password manager settings to avoid conflicts." + }, + "turnOffBrowserBuiltInPasswordManagerSettingsLink": { + "message": "Edit browser settings." + }, "autofillOverlayVisibilityOff": { "message": "Desactivat", "description": "Overlay setting select option for disabling autofill overlay" @@ -1131,6 +1143,9 @@ "faviconDesc": { "message": "Mostra una imatge reconeixible al costat de cada inici de sessió." }, + "faviconDescAlt": { + "message": "Show a recognizable image next to each login. Applies to all logged in accounts." + }, "enableBadgeCounter": { "message": "Mostra el comptador insígnia" }, @@ -2264,7 +2279,7 @@ "message": "Com emplenar automàticament" }, "autofillSelectInfoWithCommand": { - "message": "Select an item from this screen, use the shortcut $COMMAND$, or explore other options in settings.", + "message": "Seleccioneu un element d'aquesta pantalla, utilitzeu la drecera $COMMAND$ o exploreu altres opcions a la configuració.", "placeholders": { "command": { "content": "$1", @@ -2273,7 +2288,7 @@ } }, "autofillSelectInfoWithoutCommand": { - "message": "Select an item from this screen, or explore other options in settings." + "message": "Seleccioneu un element d'aquesta pantalla, o exploreu altres opcions a la configuració." }, "gotIt": { "message": "D'acord" @@ -2538,36 +2553,12 @@ "message": "El menú d'emplenament automàtic de Bitwarden està disponible. Premeu la tecla de fletxa avall per seleccionar-lo.", "description": "Screen reader text for announcing when the overlay opens on the page" }, - "overrideBrowserAutofillTitle": { - "message": "Voleu substituir l'emplenament automàtic del navegador?", - "description": "Dialog title facilitating the ability to override a chrome browser's default autofill behavior" - }, - "overrideBrowserAutofillDescription": { - "message": "Si deixeu aquesta configuració desactivada, és possible que hi haja conflictes entre el menú d'emplenament automàtic de Bitwarden i el del navegador.", - "description": "Dialog message facilitating the ability to override a chrome browser's default autofill behavior" - }, - "overrideBrowserAutofillPrivacyRequiredDescription": { - "message": "Si deixeu aquesta configuració desactivada, és possible que hi haja conflictes entre el menú d'emplenament automàtic de Bitwarden i el del navegador. En activar-la, es reiniciarà l'extensió Bitwarden.", - "description": "Dialog message facilitating the ability to override a chrome browser's default autofill behavior" - }, "turnOn": { "message": "Activa" }, "ignore": { "message": "Ignora" }, - "overrideBrowserAutoFillSettings": { - "message": "Anul·la la configuració d'emplenament automàtic del navegador", - "description": "Label for the setting that allows overriding the default browser autofill settings" - }, - "extensionPrivacyPermissionNotGrantedTitle": { - "message": "No es pot anul·lar l'emplenament automàtic del navegador", - "description": "Title for the dialog that appears when the user has not granted the extension permission to set privacy settings" - }, - "extensionPrivacyPermissionNotGrantedDescription": { - "message": "Bitwarden ha de tenir accés al permís de privadesa de l'extensió per anul·lar la configuració d'emplenament automàtic del navegador.", - "description": "Description for the dialog that appears when the user has not granted the extension permission to set privacy settings" - }, "importData": { "message": "Importa dades", "description": "Used for the header of the import dialog, the import button and within the file-password-prompt" @@ -2679,6 +2670,9 @@ "confirmFilePassword": { "message": "Confirma la contrasenya del fitxer" }, + "typePasskey": { + "message": "Clau de pas" + }, "passkeyNotCopied": { "message": "La clau de pas no es copiarà" }, @@ -2793,5 +2787,35 @@ }, "lastPassYubikeyDesc": { "message": "Inseriu la YubiKey associada al compte del LastPass al port USB de l'ordinador i, després, toqueu-ne el botó." + }, + "switchAccount": { + "message": "Switch account" + }, + "switchAccounts": { + "message": "Switch accounts" + }, + "switchToAccount": { + "message": "Switch to account" + }, + "activeAccount": { + "message": "Active account" + }, + "accountLimitReached": { + "message": "Account limit reached. Log out of an account to add another." + }, + "active": { + "message": "active" + }, + "locked": { + "message": "locked" + }, + "unlocked": { + "message": "unlocked" + }, + "server": { + "message": "server" + }, + "hostedAt": { + "message": "hosted at" } } diff --git a/apps/browser/src/_locales/cs/messages.json b/apps/browser/src/_locales/cs/messages.json index fbb18f5aadd..6310e145d3a 100644 --- a/apps/browser/src/_locales/cs/messages.json +++ b/apps/browser/src/_locales/cs/messages.json @@ -365,6 +365,9 @@ "unlockMethodNeededToChangeTimeoutActionDesc": { "message": "Nastavte metodu odemknutí, abyste změnili časový limit Vašeho trezoru." }, + "unlockMethodNeeded": { + "message": "Nastavit metodu odemknutí v Nastavení" + }, "rateExtension": { "message": "Ohodnotit rozšíření" }, @@ -405,6 +408,9 @@ "lockNow": { "message": "Zamknout nyní" }, + "lockAll": { + "message": "Zamknout vše" + }, "immediately": { "message": "Okamžitě" }, @@ -1027,6 +1033,12 @@ "showAutoFillMenuOnFormFieldsDescAlt": { "message": "Použije se na všechny přihlášené účty." }, + "turnOffBrowserBuiltInPasswordManagerSettings": { + "message": "Abyste se vyhnuli konfliktům, vypněte v nastavení Vašeho prohlížeče zabudovaný správce hesel." + }, + "turnOffBrowserBuiltInPasswordManagerSettingsLink": { + "message": "Upravit nastavení prohlížeče" + }, "autofillOverlayVisibilityOff": { "message": "VYP.", "description": "Overlay setting select option for disabling autofill overlay" @@ -1131,6 +1143,9 @@ "faviconDesc": { "message": "Zobrazí rozeznatelný obrázek vedle každého přihlášení." }, + "faviconDescAlt": { + "message": "Zobrazí rozeznatelný obrázek vedle každého přihlášení. Platí pro všechny přihlášené účty." + }, "enableBadgeCounter": { "message": "Zobrazovat počet uložených přihlašovacích údajů na stránce" }, @@ -2538,36 +2553,12 @@ "message": "Nabídka automatického vyplňování Bitwardenu. Pro výběr stiskněte šipku dolů.", "description": "Screen reader text for announcing when the overlay opens on the page" }, - "overrideBrowserAutofillTitle": { - "message": "Přepsat automatické vyplnění prohlížeče?", - "description": "Dialog title facilitating the ability to override a chrome browser's default autofill behavior" - }, - "overrideBrowserAutofillDescription": { - "message": "Ponechání tohoto nastavení ve vypnutém stavu může způsobit konflikty mezi nabídkou automatického vyplňování Bitwardenu a Vaším prohlížečem.", - "description": "Dialog message facilitating the ability to override a chrome browser's default autofill behavior" - }, - "overrideBrowserAutofillPrivacyRequiredDescription": { - "message": "Ponechání tohoto nastavení ve vypnutém stavu může způsobit konflikty mezi nabídkou automatického vyplňování Bitwardenu a Vaším prohlížečem. Zapnutím tohoto rozšíření dojde k restartování doplňku Bitwardenu.", - "description": "Dialog message facilitating the ability to override a chrome browser's default autofill behavior" - }, "turnOn": { "message": "Zapnout" }, "ignore": { "message": "Ignorovat" }, - "overrideBrowserAutoFillSettings": { - "message": "Přepsat nastavení automatického vyplnění prohlížeče", - "description": "Label for the setting that allows overriding the default browser autofill settings" - }, - "extensionPrivacyPermissionNotGrantedTitle": { - "message": "Nelze přepsat automatické vyplnění prohlížeče", - "description": "Title for the dialog that appears when the user has not granted the extension permission to set privacy settings" - }, - "extensionPrivacyPermissionNotGrantedDescription": { - "message": "Bitwarden musí mít přístup k ochraně osobních údajů doplňku pro přepsání nastavení automatického vyplňování prohlížeče.", - "description": "Description for the dialog that appears when the user has not granted the extension permission to set privacy settings" - }, "importData": { "message": "Importovat data", "description": "Used for the header of the import dialog, the import button and within the file-password-prompt" @@ -2679,6 +2670,9 @@ "confirmFilePassword": { "message": "Potvrzení hesla souboru" }, + "typePasskey": { + "message": "Přístupový klíč" + }, "passkeyNotCopied": { "message": "Přístupový klíč nebude zkopírován" }, @@ -2793,5 +2787,35 @@ }, "lastPassYubikeyDesc": { "message": "Vložte YubiKey spojený s Vaším účtem LastPass do USB portu Vašeho počítače a stiskněte jeho tlačítko." + }, + "switchAccount": { + "message": "Přepnout účet" + }, + "switchAccounts": { + "message": "Přepnout účty" + }, + "switchToAccount": { + "message": "Přepnout na účet" + }, + "activeAccount": { + "message": "Aktivní účet" + }, + "accountLimitReached": { + "message": "Byl dosažen limit účtu. Pro přidání dalšího účtu se odhlaste." + }, + "active": { + "message": "aktivní" + }, + "locked": { + "message": "uzamčeno" + }, + "unlocked": { + "message": "odemčeno" + }, + "server": { + "message": "server" + }, + "hostedAt": { + "message": "hostováno na" } } diff --git a/apps/browser/src/_locales/cy/messages.json b/apps/browser/src/_locales/cy/messages.json index 23c6d581569..5b1a8d90104 100644 --- a/apps/browser/src/_locales/cy/messages.json +++ b/apps/browser/src/_locales/cy/messages.json @@ -365,6 +365,9 @@ "unlockMethodNeededToChangeTimeoutActionDesc": { "message": "Set up an unlock method to change your vault timeout action." }, + "unlockMethodNeeded": { + "message": "Set up an unlock method in Settings" + }, "rateExtension": { "message": "Rate the extension" }, @@ -405,6 +408,9 @@ "lockNow": { "message": "Cloi nawr" }, + "lockAll": { + "message": "Lock all" + }, "immediately": { "message": "ar unwaith" }, @@ -1027,6 +1033,12 @@ "showAutoFillMenuOnFormFieldsDescAlt": { "message": "Applies to all logged in accounts." }, + "turnOffBrowserBuiltInPasswordManagerSettings": { + "message": "Turn off your browser’s built in password manager settings to avoid conflicts." + }, + "turnOffBrowserBuiltInPasswordManagerSettingsLink": { + "message": "Edit browser settings." + }, "autofillOverlayVisibilityOff": { "message": "Off", "description": "Overlay setting select option for disabling autofill overlay" @@ -1131,6 +1143,9 @@ "faviconDesc": { "message": "Dangos delwedd adnabyddadwy wrth ymyl pob eitem." }, + "faviconDescAlt": { + "message": "Show a recognizable image next to each login. Applies to all logged in accounts." + }, "enableBadgeCounter": { "message": "Show badge counter" }, @@ -2538,36 +2553,12 @@ "message": "Bitwarden auto-fill menu available. Press the down arrow key to select.", "description": "Screen reader text for announcing when the overlay opens on the page" }, - "overrideBrowserAutofillTitle": { - "message": "Override browser auto-fill?", - "description": "Dialog title facilitating the ability to override a chrome browser's default autofill behavior" - }, - "overrideBrowserAutofillDescription": { - "message": "Leaving this setting off may cause conflicts between the Bitwarden auto-fill menu and your browser’s.", - "description": "Dialog message facilitating the ability to override a chrome browser's default autofill behavior" - }, - "overrideBrowserAutofillPrivacyRequiredDescription": { - "message": "Leaving this setting off may cause conflicts between the Bitwarden auto-fill menu and your browser’s. Turning this on will restart the Bitwarden extension.", - "description": "Dialog message facilitating the ability to override a chrome browser's default autofill behavior" - }, "turnOn": { "message": "Turn on" }, "ignore": { "message": "Ignore" }, - "overrideBrowserAutoFillSettings": { - "message": "Override browser auto-fill settings", - "description": "Label for the setting that allows overriding the default browser autofill settings" - }, - "extensionPrivacyPermissionNotGrantedTitle": { - "message": "Unable to override browser auto-fill", - "description": "Title for the dialog that appears when the user has not granted the extension permission to set privacy settings" - }, - "extensionPrivacyPermissionNotGrantedDescription": { - "message": "Bitwarden must have access to the extension's privacy permission to override browser auto-fill settings.", - "description": "Description for the dialog that appears when the user has not granted the extension permission to set privacy settings" - }, "importData": { "message": "Import data", "description": "Used for the header of the import dialog, the import button and within the file-password-prompt" @@ -2679,6 +2670,9 @@ "confirmFilePassword": { "message": "Confirm file password" }, + "typePasskey": { + "message": "Passkey" + }, "passkeyNotCopied": { "message": "Passkey will not be copied" }, @@ -2793,5 +2787,35 @@ }, "lastPassYubikeyDesc": { "message": "Insert the YubiKey associated with your LastPass account into your computer's USB port, then touch its button." + }, + "switchAccount": { + "message": "Switch account" + }, + "switchAccounts": { + "message": "Switch accounts" + }, + "switchToAccount": { + "message": "Switch to account" + }, + "activeAccount": { + "message": "Active account" + }, + "accountLimitReached": { + "message": "Account limit reached. Log out of an account to add another." + }, + "active": { + "message": "active" + }, + "locked": { + "message": "locked" + }, + "unlocked": { + "message": "unlocked" + }, + "server": { + "message": "server" + }, + "hostedAt": { + "message": "hosted at" } } diff --git a/apps/browser/src/_locales/da/messages.json b/apps/browser/src/_locales/da/messages.json index d3463f567c0..922f4ca5f2e 100644 --- a/apps/browser/src/_locales/da/messages.json +++ b/apps/browser/src/_locales/da/messages.json @@ -365,6 +365,9 @@ "unlockMethodNeededToChangeTimeoutActionDesc": { "message": "Opsæt en oplåsningsmetode til at ændre bokstimeouthandlingen." }, + "unlockMethodNeeded": { + "message": "Opsæt en oplåsningsmetode i Indstillinger" + }, "rateExtension": { "message": "Bedøm udvidelsen" }, @@ -405,6 +408,9 @@ "lockNow": { "message": "Lås nu" }, + "lockAll": { + "message": "Lås alle" + }, "immediately": { "message": "Straks" }, @@ -1027,6 +1033,12 @@ "showAutoFillMenuOnFormFieldsDescAlt": { "message": "Gælder for alle indloggede konti." }, + "turnOffBrowserBuiltInPasswordManagerSettings": { + "message": "Turn off your browser’s built in password manager settings to avoid conflicts." + }, + "turnOffBrowserBuiltInPasswordManagerSettingsLink": { + "message": "Edit browser settings." + }, "autofillOverlayVisibilityOff": { "message": "Fra", "description": "Overlay setting select option for disabling autofill overlay" @@ -1131,6 +1143,9 @@ "faviconDesc": { "message": "Vis et genkendeligt billede ud for hvert login." }, + "faviconDescAlt": { + "message": "Vis et genkendeligt billede ved siden af hvert login. Gælder for alle indloggede konti." + }, "enableBadgeCounter": { "message": "Vis badge-tæller" }, @@ -2538,36 +2553,12 @@ "message": "Bitwarden autoudfyld-menu tilgængelig. Tryk på pil ned-tasten for at vælge.", "description": "Screen reader text for announcing when the overlay opens on the page" }, - "overrideBrowserAutofillTitle": { - "message": "Tilsidesæt browser-autoudfyldning?", - "description": "Dialog title facilitating the ability to override a chrome browser's default autofill behavior" - }, - "overrideBrowserAutofillDescription": { - "message": "Lades denne indstilling deaktiveret, kan det forårsage konflikter mellem Bitwardens autoudfyld-menu og webbrowsere.", - "description": "Dialog message facilitating the ability to override a chrome browser's default autofill behavior" - }, - "overrideBrowserAutofillPrivacyRequiredDescription": { - "message": "Lades denne indstilling deaktiveret, kan det forårsage konflikter mellem Bitwardens autoudfyld-menu og webbrowsere. Aktiveres indstillingen, genstartes Bitwarden-udvidelsen.", - "description": "Dialog message facilitating the ability to override a chrome browser's default autofill behavior" - }, "turnOn": { "message": "Slå til" }, "ignore": { "message": "Ignorér" }, - "overrideBrowserAutoFillSettings": { - "message": "Tilsidesæt webbrowser autoudfyld-indstillinger", - "description": "Label for the setting that allows overriding the default browser autofill settings" - }, - "extensionPrivacyPermissionNotGrantedTitle": { - "message": "Kan ikke tilsidesætte webbrowser-autoudfyld", - "description": "Title for the dialog that appears when the user has not granted the extension permission to set privacy settings" - }, - "extensionPrivacyPermissionNotGrantedDescription": { - "message": "Bitwarden skal kunne tilgå udvidelsens fortrolighedstilladelse for at tilsidesætte indstillinger for webbrowser-autoudfyld.", - "description": "Description for the dialog that appears when the user has not granted the extension permission to set privacy settings" - }, "importData": { "message": "Importér data", "description": "Used for the header of the import dialog, the import button and within the file-password-prompt" @@ -2679,6 +2670,9 @@ "confirmFilePassword": { "message": "Bekræft filadgangskode" }, + "typePasskey": { + "message": "Adgangsnøgle" + }, "passkeyNotCopied": { "message": "Adgangsnøglen kopieres ikke" }, @@ -2793,5 +2787,35 @@ }, "lastPassYubikeyDesc": { "message": "Indsæt den til LastPass-kontoen tilknyttede YubiKey i computers USB-port, og tryk dernæst på dens knap." + }, + "switchAccount": { + "message": "Skift konto" + }, + "switchAccounts": { + "message": "Skift konti" + }, + "switchToAccount": { + "message": "Skift til konto" + }, + "activeAccount": { + "message": "Aktiv konto" + }, + "accountLimitReached": { + "message": "Kontokvote nået. Log ud af en konto for at tilføje en anden." + }, + "active": { + "message": "aktiv" + }, + "locked": { + "message": "låst" + }, + "unlocked": { + "message": "oplåst" + }, + "server": { + "message": "server" + }, + "hostedAt": { + "message": "hostet hos" } } diff --git a/apps/browser/src/_locales/de/messages.json b/apps/browser/src/_locales/de/messages.json index 1b94bf0270f..70ca03db882 100644 --- a/apps/browser/src/_locales/de/messages.json +++ b/apps/browser/src/_locales/de/messages.json @@ -365,6 +365,9 @@ "unlockMethodNeededToChangeTimeoutActionDesc": { "message": "Richte eine Entsperrmethode ein, um deine Aktion bei Timeout-Timeout zu ändern." }, + "unlockMethodNeeded": { + "message": "Set up an unlock method in Settings" + }, "rateExtension": { "message": "Erweiterung bewerten" }, @@ -405,6 +408,9 @@ "lockNow": { "message": "Jetzt sperren" }, + "lockAll": { + "message": "Lock all" + }, "immediately": { "message": "Sofort" }, @@ -1027,6 +1033,12 @@ "showAutoFillMenuOnFormFieldsDescAlt": { "message": "Gilt für alle angemeldeten Konten." }, + "turnOffBrowserBuiltInPasswordManagerSettings": { + "message": "Turn off your browser’s built in password manager settings to avoid conflicts." + }, + "turnOffBrowserBuiltInPasswordManagerSettingsLink": { + "message": "Edit browser settings." + }, "autofillOverlayVisibilityOff": { "message": "Aus", "description": "Overlay setting select option for disabling autofill overlay" @@ -1131,6 +1143,9 @@ "faviconDesc": { "message": "Ein wiedererkennbares Bild neben jeden Zugangsdaten anzeigen." }, + "faviconDescAlt": { + "message": "Show a recognizable image next to each login. Applies to all logged in accounts." + }, "enableBadgeCounter": { "message": "Badge-Zähler anzeigen" }, @@ -2538,36 +2553,12 @@ "message": "Bitwarden Auto-Ausfüllen Menü verfügbar. Drücke die Pfeiltaste nach unten zum Auswählen.", "description": "Screen reader text for announcing when the overlay opens on the page" }, - "overrideBrowserAutofillTitle": { - "message": "Auto-Ausfüllen Funktion des Browsers überschreiben?", - "description": "Dialog title facilitating the ability to override a chrome browser's default autofill behavior" - }, - "overrideBrowserAutofillDescription": { - "message": "Wenn du diese Einstellung deaktivierst, kann dies zu Konflikten zwischen dem Bitwarden Auto-Ausfüllen Menü und dem des Browsers führen.", - "description": "Dialog message facilitating the ability to override a chrome browser's default autofill behavior" - }, - "overrideBrowserAutofillPrivacyRequiredDescription": { - "message": "Wenn du diese Einstellung deaktivierst, kann es zu Konflikten zwischen dem Bitwarden Auto-Ausfüllen Menü und dem des Browsers kommen. Wenn du diese Einstellung aktivierst, wird die Bitwarden-Erweiterung neu gestartet.", - "description": "Dialog message facilitating the ability to override a chrome browser's default autofill behavior" - }, "turnOn": { "message": "Einschalten" }, "ignore": { "message": "Ignorieren" }, - "overrideBrowserAutoFillSettings": { - "message": "Auto-Ausfüllen Einstellungen des Browsers überschreiben", - "description": "Label for the setting that allows overriding the default browser autofill settings" - }, - "extensionPrivacyPermissionNotGrantedTitle": { - "message": "Konnte Auto-Ausfüllen Einstellungen des Browsers nicht überschreiben", - "description": "Title for the dialog that appears when the user has not granted the extension permission to set privacy settings" - }, - "extensionPrivacyPermissionNotGrantedDescription": { - "message": "Bitwarden muss Zugriff auf die Datenschutz-Berechtigung der Erweiterung haben, um die Auto-Ausfüllen-Einstellungen des Browsers zu überschreiben.", - "description": "Description for the dialog that appears when the user has not granted the extension permission to set privacy settings" - }, "importData": { "message": "Daten importieren", "description": "Used for the header of the import dialog, the import button and within the file-password-prompt" @@ -2679,6 +2670,9 @@ "confirmFilePassword": { "message": "Dateipasswort bestätigen" }, + "typePasskey": { + "message": "Passkey" + }, "passkeyNotCopied": { "message": "Passkey wird nicht kopiert" }, @@ -2793,5 +2787,35 @@ }, "lastPassYubikeyDesc": { "message": "Stecke den YubiKey, der mit deinem LastPass Konto verknüpft ist, in den USB-Port deines Computers und drücke dann den Knopf des YubiKey." + }, + "switchAccount": { + "message": "Switch account" + }, + "switchAccounts": { + "message": "Switch accounts" + }, + "switchToAccount": { + "message": "Switch to account" + }, + "activeAccount": { + "message": "Active account" + }, + "accountLimitReached": { + "message": "Account limit reached. Log out of an account to add another." + }, + "active": { + "message": "active" + }, + "locked": { + "message": "locked" + }, + "unlocked": { + "message": "unlocked" + }, + "server": { + "message": "server" + }, + "hostedAt": { + "message": "hosted at" } } diff --git a/apps/browser/src/_locales/el/messages.json b/apps/browser/src/_locales/el/messages.json index 8eb2367779a..e6a920d3e90 100644 --- a/apps/browser/src/_locales/el/messages.json +++ b/apps/browser/src/_locales/el/messages.json @@ -365,6 +365,9 @@ "unlockMethodNeededToChangeTimeoutActionDesc": { "message": "Ρυθμίστε μια μέθοδο ξεκλειδώματος για να αλλάξετε την ενέργεια χρονικού ορίου θησαυ/κιου." }, + "unlockMethodNeeded": { + "message": "Set up an unlock method in Settings" + }, "rateExtension": { "message": "Βαθμολογήστε την επέκταση" }, @@ -405,6 +408,9 @@ "lockNow": { "message": "Κλείδωμα Τώρα" }, + "lockAll": { + "message": "Lock all" + }, "immediately": { "message": "Άμεσα" }, @@ -1027,6 +1033,12 @@ "showAutoFillMenuOnFormFieldsDescAlt": { "message": "Applies to all logged in accounts." }, + "turnOffBrowserBuiltInPasswordManagerSettings": { + "message": "Turn off your browser’s built in password manager settings to avoid conflicts." + }, + "turnOffBrowserBuiltInPasswordManagerSettingsLink": { + "message": "Edit browser settings." + }, "autofillOverlayVisibilityOff": { "message": "Off", "description": "Overlay setting select option for disabling autofill overlay" @@ -1131,6 +1143,9 @@ "faviconDesc": { "message": "Εμφάνιση μιας αναγνωρίσιμης εικόνας δίπλα σε κάθε σύνδεση." }, + "faviconDescAlt": { + "message": "Show a recognizable image next to each login. Applies to all logged in accounts." + }, "enableBadgeCounter": { "message": "Εμφάνιση μετρητή εμβλημάτων" }, @@ -2538,36 +2553,12 @@ "message": "Bitwarden auto-fill menu available. Press the down arrow key to select.", "description": "Screen reader text for announcing when the overlay opens on the page" }, - "overrideBrowserAutofillTitle": { - "message": "Override browser auto-fill?", - "description": "Dialog title facilitating the ability to override a chrome browser's default autofill behavior" - }, - "overrideBrowserAutofillDescription": { - "message": "Leaving this setting off may cause conflicts between the Bitwarden auto-fill menu and your browser’s.", - "description": "Dialog message facilitating the ability to override a chrome browser's default autofill behavior" - }, - "overrideBrowserAutofillPrivacyRequiredDescription": { - "message": "Leaving this setting off may cause conflicts between the Bitwarden auto-fill menu and your browser’s. Turning this on will restart the Bitwarden extension.", - "description": "Dialog message facilitating the ability to override a chrome browser's default autofill behavior" - }, "turnOn": { "message": "Turn on" }, "ignore": { "message": "Ignore" }, - "overrideBrowserAutoFillSettings": { - "message": "Override browser auto-fill settings", - "description": "Label for the setting that allows overriding the default browser autofill settings" - }, - "extensionPrivacyPermissionNotGrantedTitle": { - "message": "Unable to override browser auto-fill", - "description": "Title for the dialog that appears when the user has not granted the extension permission to set privacy settings" - }, - "extensionPrivacyPermissionNotGrantedDescription": { - "message": "Bitwarden must have access to the extension's privacy permission to override browser auto-fill settings.", - "description": "Description for the dialog that appears when the user has not granted the extension permission to set privacy settings" - }, "importData": { "message": "Εισαγωγή δεδομένων", "description": "Used for the header of the import dialog, the import button and within the file-password-prompt" @@ -2679,6 +2670,9 @@ "confirmFilePassword": { "message": "Επιβεβαίωση κωδικού πρόσβασης αρχείου" }, + "typePasskey": { + "message": "Passkey" + }, "passkeyNotCopied": { "message": "Passkey will not be copied" }, @@ -2793,5 +2787,35 @@ }, "lastPassYubikeyDesc": { "message": "Insert the YubiKey associated with your LastPass account into your computer's USB port, then touch its button." + }, + "switchAccount": { + "message": "Switch account" + }, + "switchAccounts": { + "message": "Switch accounts" + }, + "switchToAccount": { + "message": "Switch to account" + }, + "activeAccount": { + "message": "Active account" + }, + "accountLimitReached": { + "message": "Account limit reached. Log out of an account to add another." + }, + "active": { + "message": "active" + }, + "locked": { + "message": "locked" + }, + "unlocked": { + "message": "unlocked" + }, + "server": { + "message": "server" + }, + "hostedAt": { + "message": "hosted at" } } diff --git a/apps/browser/src/_locales/en/messages.json b/apps/browser/src/_locales/en/messages.json index b07f9307255..a6aff9e455b 100644 --- a/apps/browser/src/_locales/en/messages.json +++ b/apps/browser/src/_locales/en/messages.json @@ -365,6 +365,9 @@ "unlockMethodNeededToChangeTimeoutActionDesc": { "message": "Set up an unlock method to change your vault timeout action." }, + "unlockMethodNeeded": { + "message": "Set up an unlock method in Settings" + }, "rateExtension": { "message": "Rate the extension" }, @@ -405,6 +408,9 @@ "lockNow": { "message": "Lock now" }, + "lockAll": { + "message": "Lock all" + }, "immediately": { "message": "Immediately" }, @@ -1027,6 +1033,12 @@ "showAutoFillMenuOnFormFieldsDescAlt": { "message": "Applies to all logged in accounts." }, + "turnOffBrowserBuiltInPasswordManagerSettings": { + "message": "Turn off your browser’s built in password manager settings to avoid conflicts." + }, + "turnOffBrowserBuiltInPasswordManagerSettingsLink": { + "message": "Edit browser settings." + }, "autofillOverlayVisibilityOff": { "message": "Off", "description": "Overlay setting select option for disabling autofill overlay" @@ -1131,6 +1143,9 @@ "faviconDesc": { "message": "Show a recognizable image next to each login." }, + "faviconDescAlt": { + "message": "Show a recognizable image next to each login. Applies to all logged in accounts." + }, "enableBadgeCounter": { "message": "Show badge counter" }, @@ -2538,36 +2553,12 @@ "message": "Bitwarden auto-fill menu available. Press the down arrow key to select.", "description": "Screen reader text for announcing when the overlay opens on the page" }, - "overrideBrowserAutofillTitle": { - "message": "Override browser auto-fill?", - "description": "Dialog title facilitating the ability to override a chrome browser's default autofill behavior" - }, - "overrideBrowserAutofillDescription": { - "message": "Leaving this setting off may cause conflicts between the Bitwarden auto-fill menu and your browser’s.", - "description": "Dialog message facilitating the ability to override a chrome browser's default autofill behavior" - }, - "overrideBrowserAutofillPrivacyRequiredDescription": { - "message": "Leaving this setting off may cause conflicts between the Bitwarden auto-fill menu and your browser’s. Turning this on will restart the Bitwarden extension.", - "description": "Dialog message facilitating the ability to override a chrome browser's default autofill behavior" - }, "turnOn": { "message": "Turn on" }, "ignore": { "message": "Ignore" }, - "overrideBrowserAutoFillSettings": { - "message": "Override browser auto-fill settings", - "description": "Label for the setting that allows overriding the default browser autofill settings" - }, - "extensionPrivacyPermissionNotGrantedTitle": { - "message": "Unable to override browser auto-fill", - "description": "Title for the dialog that appears when the user has not granted the extension permission to set privacy settings" - }, - "extensionPrivacyPermissionNotGrantedDescription": { - "message": "Bitwarden must have access to the extension's privacy permission to override browser auto-fill settings.", - "description": "Description for the dialog that appears when the user has not granted the extension permission to set privacy settings" - }, "importData": { "message": "Import data", "description": "Used for the header of the import dialog, the import button and within the file-password-prompt" @@ -2796,5 +2787,35 @@ }, "lastPassYubikeyDesc": { "message": "Insert the YubiKey associated with your LastPass account into your computer's USB port, then touch its button." + }, + "switchAccount": { + "message": "Switch account" + }, + "switchAccounts": { + "message": "Switch accounts" + }, + "switchToAccount": { + "message": "Switch to account" + }, + "activeAccount": { + "message": "Active account" + }, + "accountLimitReached": { + "message": "Account limit reached. Log out of an account to add another." + }, + "active": { + "message": "active" + }, + "locked": { + "message": "locked" + }, + "unlocked": { + "message": "unlocked" + }, + "server": { + "message": "server" + }, + "hostedAt": { + "message": "hosted at" } } diff --git a/apps/browser/src/_locales/en_GB/messages.json b/apps/browser/src/_locales/en_GB/messages.json index ae38637c278..b2b927b0243 100644 --- a/apps/browser/src/_locales/en_GB/messages.json +++ b/apps/browser/src/_locales/en_GB/messages.json @@ -365,6 +365,9 @@ "unlockMethodNeededToChangeTimeoutActionDesc": { "message": "Set up an unlock method to change your vault timeout action." }, + "unlockMethodNeeded": { + "message": "Set up an unlock method in Settings" + }, "rateExtension": { "message": "Rate the extension" }, @@ -405,6 +408,9 @@ "lockNow": { "message": "Lock now" }, + "lockAll": { + "message": "Lock all" + }, "immediately": { "message": "Immediately" }, @@ -1027,6 +1033,12 @@ "showAutoFillMenuOnFormFieldsDescAlt": { "message": "Applies to all logged in accounts." }, + "turnOffBrowserBuiltInPasswordManagerSettings": { + "message": "Turn off your browser’s built in password manager settings to avoid conflicts." + }, + "turnOffBrowserBuiltInPasswordManagerSettingsLink": { + "message": "Edit browser settings." + }, "autofillOverlayVisibilityOff": { "message": "Off", "description": "Overlay setting select option for disabling autofill overlay" @@ -1131,6 +1143,9 @@ "faviconDesc": { "message": "Show a recognisable image next to each login." }, + "faviconDescAlt": { + "message": "Show a recognisable image next to each login. Applies to all logged in accounts." + }, "enableBadgeCounter": { "message": "Show badge counter" }, @@ -2538,36 +2553,12 @@ "message": "Bitwarden auto-fill menu available. Press the down arrow key to select.", "description": "Screen reader text for announcing when the overlay opens on the page" }, - "overrideBrowserAutofillTitle": { - "message": "Override browser auto-fill?", - "description": "Dialog title facilitating the ability to override a chrome browser's default autofill behavior" - }, - "overrideBrowserAutofillDescription": { - "message": "Leaving this setting off may cause conflicts between the Bitwarden auto-fill menu and your browser’s.", - "description": "Dialog message facilitating the ability to override a chrome browser's default autofill behavior" - }, - "overrideBrowserAutofillPrivacyRequiredDescription": { - "message": "Leaving this setting off may cause conflicts between the Bitwarden auto-fill menu and your browser’s. Turning this on will restart the Bitwarden extension.", - "description": "Dialog message facilitating the ability to override a chrome browser's default autofill behavior" - }, "turnOn": { "message": "Turn on" }, "ignore": { "message": "Ignore" }, - "overrideBrowserAutoFillSettings": { - "message": "Override browser auto-fill settings", - "description": "Label for the setting that allows overriding the default browser autofill settings" - }, - "extensionPrivacyPermissionNotGrantedTitle": { - "message": "Unable to override browser auto-fill", - "description": "Title for the dialog that appears when the user has not granted the extension permission to set privacy settings" - }, - "extensionPrivacyPermissionNotGrantedDescription": { - "message": "Bitwarden must have access to the extension's privacy permission to override browser auto-fill settings.", - "description": "Description for the dialog that appears when the user has not granted the extension permission to set privacy settings" - }, "importData": { "message": "Import data", "description": "Used for the header of the import dialog, the import button and within the file-password-prompt" @@ -2679,6 +2670,9 @@ "confirmFilePassword": { "message": "Confirm file password" }, + "typePasskey": { + "message": "Passkey" + }, "passkeyNotCopied": { "message": "Passkey will not be copied" }, @@ -2793,5 +2787,35 @@ }, "lastPassYubikeyDesc": { "message": "Insert the YubiKey associated with your LastPass account into your computer's USB port, then touch its button." + }, + "switchAccount": { + "message": "Switch account" + }, + "switchAccounts": { + "message": "Switch accounts" + }, + "switchToAccount": { + "message": "Switch to account" + }, + "activeAccount": { + "message": "Active account" + }, + "accountLimitReached": { + "message": "Account limit reached. Log out of an account to add another." + }, + "active": { + "message": "active" + }, + "locked": { + "message": "locked" + }, + "unlocked": { + "message": "unlocked" + }, + "server": { + "message": "server" + }, + "hostedAt": { + "message": "hosted at" } } diff --git a/apps/browser/src/_locales/en_IN/messages.json b/apps/browser/src/_locales/en_IN/messages.json index 9848dad0ed3..26272877ad2 100644 --- a/apps/browser/src/_locales/en_IN/messages.json +++ b/apps/browser/src/_locales/en_IN/messages.json @@ -365,6 +365,9 @@ "unlockMethodNeededToChangeTimeoutActionDesc": { "message": "Set up an unlock method to change your vault timeout action." }, + "unlockMethodNeeded": { + "message": "Set up an unlock method in Settings" + }, "rateExtension": { "message": "Rate the extension" }, @@ -405,6 +408,9 @@ "lockNow": { "message": "Lock now" }, + "lockAll": { + "message": "Lock all" + }, "immediately": { "message": "Immediately" }, @@ -1027,6 +1033,12 @@ "showAutoFillMenuOnFormFieldsDescAlt": { "message": "Applies to all logged in accounts." }, + "turnOffBrowserBuiltInPasswordManagerSettings": { + "message": "Turn off your browser’s built in password manager settings to avoid conflicts." + }, + "turnOffBrowserBuiltInPasswordManagerSettingsLink": { + "message": "Edit browser settings." + }, "autofillOverlayVisibilityOff": { "message": "Off", "description": "Overlay setting select option for disabling autofill overlay" @@ -1131,6 +1143,9 @@ "faviconDesc": { "message": "Show a recognizable image next to each login." }, + "faviconDescAlt": { + "message": "Show a recognizable image next to each login. Applies to all logged in accounts." + }, "enableBadgeCounter": { "message": "Show badge counter" }, @@ -2538,36 +2553,12 @@ "message": "Bitwarden auto-fill menu available. Press the down arrow key to select.", "description": "Screen reader text for announcing when the overlay opens on the page" }, - "overrideBrowserAutofillTitle": { - "message": "Override browser auto-fill?", - "description": "Dialog title facilitating the ability to override a chrome browser's default autofill behavior" - }, - "overrideBrowserAutofillDescription": { - "message": "Leaving this setting off may cause conflicts between the Bitwarden auto-fill menu and your browser’s.", - "description": "Dialog message facilitating the ability to override a chrome browser's default autofill behavior" - }, - "overrideBrowserAutofillPrivacyRequiredDescription": { - "message": "Leaving this setting off may cause conflicts between the Bitwarden auto-fill menu and your browser’s. Turning this on will restart the Bitwarden extension.", - "description": "Dialog message facilitating the ability to override a chrome browser's default autofill behavior" - }, "turnOn": { "message": "Turn on" }, "ignore": { "message": "Ignore" }, - "overrideBrowserAutoFillSettings": { - "message": "Override browser auto-fill settings", - "description": "Label for the setting that allows overriding the default browser autofill settings" - }, - "extensionPrivacyPermissionNotGrantedTitle": { - "message": "Unable to override browser auto-fill", - "description": "Title for the dialog that appears when the user has not granted the extension permission to set privacy settings" - }, - "extensionPrivacyPermissionNotGrantedDescription": { - "message": "Bitwarden must have access to the extension's privacy permission to override browser auto-fill settings.", - "description": "Description for the dialog that appears when the user has not granted the extension permission to set privacy settings" - }, "importData": { "message": "Import data", "description": "Used for the header of the import dialog, the import button and within the file-password-prompt" @@ -2679,6 +2670,9 @@ "confirmFilePassword": { "message": "Confirm file password" }, + "typePasskey": { + "message": "Passkey" + }, "passkeyNotCopied": { "message": "Passkey will not be copied" }, @@ -2793,5 +2787,35 @@ }, "lastPassYubikeyDesc": { "message": "Insert the YubiKey associated with your LastPass account into your computer's USB port, then touch its button." + }, + "switchAccount": { + "message": "Switch account" + }, + "switchAccounts": { + "message": "Switch accounts" + }, + "switchToAccount": { + "message": "Switch to account" + }, + "activeAccount": { + "message": "Active account" + }, + "accountLimitReached": { + "message": "Account limit reached. Log out of an account to add another." + }, + "active": { + "message": "active" + }, + "locked": { + "message": "locked" + }, + "unlocked": { + "message": "unlocked" + }, + "server": { + "message": "server" + }, + "hostedAt": { + "message": "hosted at" } } diff --git a/apps/browser/src/_locales/es/messages.json b/apps/browser/src/_locales/es/messages.json index f3da2ad615d..58cd79f2559 100644 --- a/apps/browser/src/_locales/es/messages.json +++ b/apps/browser/src/_locales/es/messages.json @@ -365,6 +365,9 @@ "unlockMethodNeededToChangeTimeoutActionDesc": { "message": "Configura un método de desbloqueo para cambiar tu acción de cierre de la bóveda." }, + "unlockMethodNeeded": { + "message": "Set up an unlock method in Settings" + }, "rateExtension": { "message": "Valora la extensión" }, @@ -405,6 +408,9 @@ "lockNow": { "message": "Bloquear" }, + "lockAll": { + "message": "Lock all" + }, "immediately": { "message": "Inmediatamente" }, @@ -1027,6 +1033,12 @@ "showAutoFillMenuOnFormFieldsDescAlt": { "message": "Applies to all logged in accounts." }, + "turnOffBrowserBuiltInPasswordManagerSettings": { + "message": "Turn off your browser’s built in password manager settings to avoid conflicts." + }, + "turnOffBrowserBuiltInPasswordManagerSettingsLink": { + "message": "Edit browser settings." + }, "autofillOverlayVisibilityOff": { "message": "Off", "description": "Overlay setting select option for disabling autofill overlay" @@ -1131,6 +1143,9 @@ "faviconDesc": { "message": "Mostrar una imagen reconocible junto a cada inicio de sesión." }, + "faviconDescAlt": { + "message": "Show a recognizable image next to each login. Applies to all logged in accounts." + }, "enableBadgeCounter": { "message": "Mostrar el contador numérico" }, @@ -2538,36 +2553,12 @@ "message": "Bitwarden auto-fill menu available. Press the down arrow key to select.", "description": "Screen reader text for announcing when the overlay opens on the page" }, - "overrideBrowserAutofillTitle": { - "message": "Override browser auto-fill?", - "description": "Dialog title facilitating the ability to override a chrome browser's default autofill behavior" - }, - "overrideBrowserAutofillDescription": { - "message": "Leaving this setting off may cause conflicts between the Bitwarden auto-fill menu and your browser’s.", - "description": "Dialog message facilitating the ability to override a chrome browser's default autofill behavior" - }, - "overrideBrowserAutofillPrivacyRequiredDescription": { - "message": "Leaving this setting off may cause conflicts between the Bitwarden auto-fill menu and your browser’s. Turning this on will restart the Bitwarden extension.", - "description": "Dialog message facilitating the ability to override a chrome browser's default autofill behavior" - }, "turnOn": { "message": "Turn on" }, "ignore": { "message": "Ignore" }, - "overrideBrowserAutoFillSettings": { - "message": "Override browser auto-fill settings", - "description": "Label for the setting that allows overriding the default browser autofill settings" - }, - "extensionPrivacyPermissionNotGrantedTitle": { - "message": "Unable to override browser auto-fill", - "description": "Title for the dialog that appears when the user has not granted the extension permission to set privacy settings" - }, - "extensionPrivacyPermissionNotGrantedDescription": { - "message": "Bitwarden must have access to the extension's privacy permission to override browser auto-fill settings.", - "description": "Description for the dialog that appears when the user has not granted the extension permission to set privacy settings" - }, "importData": { "message": "Importar datos", "description": "Used for the header of the import dialog, the import button and within the file-password-prompt" @@ -2679,6 +2670,9 @@ "confirmFilePassword": { "message": "Confirmar contraseña del archivo" }, + "typePasskey": { + "message": "Passkey" + }, "passkeyNotCopied": { "message": "La clave de acceso no se copiará" }, @@ -2793,5 +2787,35 @@ }, "lastPassYubikeyDesc": { "message": "Insert the YubiKey associated with your LastPass account into your computer's USB port, then touch its button." + }, + "switchAccount": { + "message": "Switch account" + }, + "switchAccounts": { + "message": "Switch accounts" + }, + "switchToAccount": { + "message": "Switch to account" + }, + "activeAccount": { + "message": "Active account" + }, + "accountLimitReached": { + "message": "Account limit reached. Log out of an account to add another." + }, + "active": { + "message": "active" + }, + "locked": { + "message": "locked" + }, + "unlocked": { + "message": "unlocked" + }, + "server": { + "message": "server" + }, + "hostedAt": { + "message": "hosted at" } } diff --git a/apps/browser/src/_locales/et/messages.json b/apps/browser/src/_locales/et/messages.json index 561d5e9fbca..618c52757be 100644 --- a/apps/browser/src/_locales/et/messages.json +++ b/apps/browser/src/_locales/et/messages.json @@ -365,6 +365,9 @@ "unlockMethodNeededToChangeTimeoutActionDesc": { "message": "Hoidla ajalõpu tegevuse muutmiseks vali esmalt lahtilukustamise meetod." }, + "unlockMethodNeeded": { + "message": "Set up an unlock method in Settings" + }, "rateExtension": { "message": "Hinda seda laiendust" }, @@ -405,6 +408,9 @@ "lockNow": { "message": "Lukusta paroolihoidla" }, + "lockAll": { + "message": "Lock all" + }, "immediately": { "message": "Koheselt" }, @@ -1027,6 +1033,12 @@ "showAutoFillMenuOnFormFieldsDescAlt": { "message": "Applies to all logged in accounts." }, + "turnOffBrowserBuiltInPasswordManagerSettings": { + "message": "Turn off your browser’s built in password manager settings to avoid conflicts." + }, + "turnOffBrowserBuiltInPasswordManagerSettingsLink": { + "message": "Edit browser settings." + }, "autofillOverlayVisibilityOff": { "message": "Off", "description": "Overlay setting select option for disabling autofill overlay" @@ -1131,6 +1143,9 @@ "faviconDesc": { "message": "Kuvab iga kirje kõrval lehekülje ikooni." }, + "faviconDescAlt": { + "message": "Show a recognizable image next to each login. Applies to all logged in accounts." + }, "enableBadgeCounter": { "message": "Kuva kirjete arvu" }, @@ -2538,36 +2553,12 @@ "message": "Bitwarden auto-fill menu available. Press the down arrow key to select.", "description": "Screen reader text for announcing when the overlay opens on the page" }, - "overrideBrowserAutofillTitle": { - "message": "Override browser auto-fill?", - "description": "Dialog title facilitating the ability to override a chrome browser's default autofill behavior" - }, - "overrideBrowserAutofillDescription": { - "message": "Leaving this setting off may cause conflicts between the Bitwarden auto-fill menu and your browser’s.", - "description": "Dialog message facilitating the ability to override a chrome browser's default autofill behavior" - }, - "overrideBrowserAutofillPrivacyRequiredDescription": { - "message": "Leaving this setting off may cause conflicts between the Bitwarden auto-fill menu and your browser’s. Turning this on will restart the Bitwarden extension.", - "description": "Dialog message facilitating the ability to override a chrome browser's default autofill behavior" - }, "turnOn": { "message": "Turn on" }, "ignore": { "message": "Ignore" }, - "overrideBrowserAutoFillSettings": { - "message": "Override browser auto-fill settings", - "description": "Label for the setting that allows overriding the default browser autofill settings" - }, - "extensionPrivacyPermissionNotGrantedTitle": { - "message": "Unable to override browser auto-fill", - "description": "Title for the dialog that appears when the user has not granted the extension permission to set privacy settings" - }, - "extensionPrivacyPermissionNotGrantedDescription": { - "message": "Bitwarden must have access to the extension's privacy permission to override browser auto-fill settings.", - "description": "Description for the dialog that appears when the user has not granted the extension permission to set privacy settings" - }, "importData": { "message": "Import data", "description": "Used for the header of the import dialog, the import button and within the file-password-prompt" @@ -2679,6 +2670,9 @@ "confirmFilePassword": { "message": "Confirm file password" }, + "typePasskey": { + "message": "Passkey" + }, "passkeyNotCopied": { "message": "Pääsukoodi ei kopeerita" }, @@ -2793,5 +2787,35 @@ }, "lastPassYubikeyDesc": { "message": "Insert the YubiKey associated with your LastPass account into your computer's USB port, then touch its button." + }, + "switchAccount": { + "message": "Switch account" + }, + "switchAccounts": { + "message": "Switch accounts" + }, + "switchToAccount": { + "message": "Switch to account" + }, + "activeAccount": { + "message": "Active account" + }, + "accountLimitReached": { + "message": "Account limit reached. Log out of an account to add another." + }, + "active": { + "message": "active" + }, + "locked": { + "message": "locked" + }, + "unlocked": { + "message": "unlocked" + }, + "server": { + "message": "server" + }, + "hostedAt": { + "message": "hosted at" } } diff --git a/apps/browser/src/_locales/eu/messages.json b/apps/browser/src/_locales/eu/messages.json index 1da1301e48b..483c40a9a8f 100644 --- a/apps/browser/src/_locales/eu/messages.json +++ b/apps/browser/src/_locales/eu/messages.json @@ -365,6 +365,9 @@ "unlockMethodNeededToChangeTimeoutActionDesc": { "message": "Set up an unlock method to change your vault timeout action." }, + "unlockMethodNeeded": { + "message": "Set up an unlock method in Settings" + }, "rateExtension": { "message": "Baloratu gehigarria" }, @@ -405,6 +408,9 @@ "lockNow": { "message": "Blokeatu orain" }, + "lockAll": { + "message": "Lock all" + }, "immediately": { "message": "Berehala" }, @@ -1027,6 +1033,12 @@ "showAutoFillMenuOnFormFieldsDescAlt": { "message": "Applies to all logged in accounts." }, + "turnOffBrowserBuiltInPasswordManagerSettings": { + "message": "Turn off your browser’s built in password manager settings to avoid conflicts." + }, + "turnOffBrowserBuiltInPasswordManagerSettingsLink": { + "message": "Edit browser settings." + }, "autofillOverlayVisibilityOff": { "message": "Off", "description": "Overlay setting select option for disabling autofill overlay" @@ -1131,6 +1143,9 @@ "faviconDesc": { "message": "Erakutsi irudi bat saio-hasiera bakoitzaren ondoan." }, + "faviconDescAlt": { + "message": "Show a recognizable image next to each login. Applies to all logged in accounts." + }, "enableBadgeCounter": { "message": "Erakutsi txartelen kontagailua" }, @@ -2538,36 +2553,12 @@ "message": "Bitwarden auto-fill menu available. Press the down arrow key to select.", "description": "Screen reader text for announcing when the overlay opens on the page" }, - "overrideBrowserAutofillTitle": { - "message": "Override browser auto-fill?", - "description": "Dialog title facilitating the ability to override a chrome browser's default autofill behavior" - }, - "overrideBrowserAutofillDescription": { - "message": "Leaving this setting off may cause conflicts between the Bitwarden auto-fill menu and your browser’s.", - "description": "Dialog message facilitating the ability to override a chrome browser's default autofill behavior" - }, - "overrideBrowserAutofillPrivacyRequiredDescription": { - "message": "Leaving this setting off may cause conflicts between the Bitwarden auto-fill menu and your browser’s. Turning this on will restart the Bitwarden extension.", - "description": "Dialog message facilitating the ability to override a chrome browser's default autofill behavior" - }, "turnOn": { "message": "Turn on" }, "ignore": { "message": "Ignore" }, - "overrideBrowserAutoFillSettings": { - "message": "Override browser auto-fill settings", - "description": "Label for the setting that allows overriding the default browser autofill settings" - }, - "extensionPrivacyPermissionNotGrantedTitle": { - "message": "Unable to override browser auto-fill", - "description": "Title for the dialog that appears when the user has not granted the extension permission to set privacy settings" - }, - "extensionPrivacyPermissionNotGrantedDescription": { - "message": "Bitwarden must have access to the extension's privacy permission to override browser auto-fill settings.", - "description": "Description for the dialog that appears when the user has not granted the extension permission to set privacy settings" - }, "importData": { "message": "Import data", "description": "Used for the header of the import dialog, the import button and within the file-password-prompt" @@ -2679,6 +2670,9 @@ "confirmFilePassword": { "message": "Confirm file password" }, + "typePasskey": { + "message": "Passkey" + }, "passkeyNotCopied": { "message": "Passkey will not be copied" }, @@ -2793,5 +2787,35 @@ }, "lastPassYubikeyDesc": { "message": "Insert the YubiKey associated with your LastPass account into your computer's USB port, then touch its button." + }, + "switchAccount": { + "message": "Switch account" + }, + "switchAccounts": { + "message": "Switch accounts" + }, + "switchToAccount": { + "message": "Switch to account" + }, + "activeAccount": { + "message": "Active account" + }, + "accountLimitReached": { + "message": "Account limit reached. Log out of an account to add another." + }, + "active": { + "message": "active" + }, + "locked": { + "message": "locked" + }, + "unlocked": { + "message": "unlocked" + }, + "server": { + "message": "server" + }, + "hostedAt": { + "message": "hosted at" } } diff --git a/apps/browser/src/_locales/fa/messages.json b/apps/browser/src/_locales/fa/messages.json index 4ab6946cbaf..d024a4b3ae3 100644 --- a/apps/browser/src/_locales/fa/messages.json +++ b/apps/browser/src/_locales/fa/messages.json @@ -365,6 +365,9 @@ "unlockMethodNeededToChangeTimeoutActionDesc": { "message": "یک روش بازگشایی برای پایان زمان مجاز تنظیم کنید." }, + "unlockMethodNeeded": { + "message": "Set up an unlock method in Settings" + }, "rateExtension": { "message": "به این افزونه امتیاز دهید" }, @@ -405,6 +408,9 @@ "lockNow": { "message": "الان قفل شود" }, + "lockAll": { + "message": "Lock all" + }, "immediately": { "message": "بلافاصله" }, @@ -1027,6 +1033,12 @@ "showAutoFillMenuOnFormFieldsDescAlt": { "message": "Applies to all logged in accounts." }, + "turnOffBrowserBuiltInPasswordManagerSettings": { + "message": "Turn off your browser’s built in password manager settings to avoid conflicts." + }, + "turnOffBrowserBuiltInPasswordManagerSettingsLink": { + "message": "Edit browser settings." + }, "autofillOverlayVisibilityOff": { "message": "Off", "description": "Overlay setting select option for disabling autofill overlay" @@ -1131,6 +1143,9 @@ "faviconDesc": { "message": "یک تصویر قابل تشخیص در کنار هر ورود نشان دهید." }, + "faviconDescAlt": { + "message": "Show a recognizable image next to each login. Applies to all logged in accounts." + }, "enableBadgeCounter": { "message": "نمایش شمارنده نشان" }, @@ -2538,36 +2553,12 @@ "message": "Bitwarden auto-fill menu available. Press the down arrow key to select.", "description": "Screen reader text for announcing when the overlay opens on the page" }, - "overrideBrowserAutofillTitle": { - "message": "Override browser auto-fill?", - "description": "Dialog title facilitating the ability to override a chrome browser's default autofill behavior" - }, - "overrideBrowserAutofillDescription": { - "message": "Leaving this setting off may cause conflicts between the Bitwarden auto-fill menu and your browser’s.", - "description": "Dialog message facilitating the ability to override a chrome browser's default autofill behavior" - }, - "overrideBrowserAutofillPrivacyRequiredDescription": { - "message": "Leaving this setting off may cause conflicts between the Bitwarden auto-fill menu and your browser’s. Turning this on will restart the Bitwarden extension.", - "description": "Dialog message facilitating the ability to override a chrome browser's default autofill behavior" - }, "turnOn": { "message": "Turn on" }, "ignore": { "message": "Ignore" }, - "overrideBrowserAutoFillSettings": { - "message": "Override browser auto-fill settings", - "description": "Label for the setting that allows overriding the default browser autofill settings" - }, - "extensionPrivacyPermissionNotGrantedTitle": { - "message": "Unable to override browser auto-fill", - "description": "Title for the dialog that appears when the user has not granted the extension permission to set privacy settings" - }, - "extensionPrivacyPermissionNotGrantedDescription": { - "message": "Bitwarden must have access to the extension's privacy permission to override browser auto-fill settings.", - "description": "Description for the dialog that appears when the user has not granted the extension permission to set privacy settings" - }, "importData": { "message": "Import data", "description": "Used for the header of the import dialog, the import button and within the file-password-prompt" @@ -2679,6 +2670,9 @@ "confirmFilePassword": { "message": "Confirm file password" }, + "typePasskey": { + "message": "Passkey" + }, "passkeyNotCopied": { "message": "کلید عبور کپی نمی‌شود" }, @@ -2793,5 +2787,35 @@ }, "lastPassYubikeyDesc": { "message": "Insert the YubiKey associated with your LastPass account into your computer's USB port, then touch its button." + }, + "switchAccount": { + "message": "Switch account" + }, + "switchAccounts": { + "message": "Switch accounts" + }, + "switchToAccount": { + "message": "Switch to account" + }, + "activeAccount": { + "message": "Active account" + }, + "accountLimitReached": { + "message": "Account limit reached. Log out of an account to add another." + }, + "active": { + "message": "active" + }, + "locked": { + "message": "locked" + }, + "unlocked": { + "message": "unlocked" + }, + "server": { + "message": "server" + }, + "hostedAt": { + "message": "hosted at" } } diff --git a/apps/browser/src/_locales/fi/messages.json b/apps/browser/src/_locales/fi/messages.json index d1d186200dd..bf98a2fa5e9 100644 --- a/apps/browser/src/_locales/fi/messages.json +++ b/apps/browser/src/_locales/fi/messages.json @@ -365,6 +365,9 @@ "unlockMethodNeededToChangeTimeoutActionDesc": { "message": "Muuta holvisi aikakatkaisutoimintoa määrittämällä lukituksen avaustapa." }, + "unlockMethodNeeded": { + "message": "Määritä avaustapa asetuksista" + }, "rateExtension": { "message": "Arvioi laajennus" }, @@ -405,6 +408,9 @@ "lockNow": { "message": "Lukitse nyt" }, + "lockAll": { + "message": "Lukitse kaikki" + }, "immediately": { "message": "Välittömästi" }, @@ -658,10 +664,10 @@ "message": "Ask to update a login's password when a change is detected on a website. Applies to all logged in accounts." }, "enableUsePasskeys": { - "message": "Ask to save and use passkeys" + "message": "Pyydä tallentamaan ja käyttämään salausavaimia" }, "usePasskeysDesc": { - "message": "Ask to save new passkeys or log in with passkeys stored in your vault. Applies to all logged in accounts." + "message": "Pyydä tallentamaan uusia salausavaimia tai kirjaudu sisään holviisi tallennetuilla salausavaimilla. Koskee kaikkia sisäänkirjautuneita tilejä." }, "notificationChangeDesc": { "message": "Haluatko päivittää salasanan Bitwardeniin?" @@ -1027,6 +1033,12 @@ "showAutoFillMenuOnFormFieldsDescAlt": { "message": "Applies to all logged in accounts." }, + "turnOffBrowserBuiltInPasswordManagerSettings": { + "message": "Turn off your browser’s built in password manager settings to avoid conflicts." + }, + "turnOffBrowserBuiltInPasswordManagerSettingsLink": { + "message": "Edit browser settings." + }, "autofillOverlayVisibilityOff": { "message": "Off", "description": "Overlay setting select option for disabling autofill overlay" @@ -1131,6 +1143,9 @@ "faviconDesc": { "message": "Näytä tunnistettava kuva jokaiselle kirjautumistiedolle." }, + "faviconDescAlt": { + "message": "Näytä tunnistettava kuva jokaisen kirjautumistiedon vieressä. Koskee kaikkia kirjautuneita tilejä." + }, "enableBadgeCounter": { "message": "Näytä laskuri" }, @@ -2511,7 +2526,7 @@ "description": "Text to display in overlay when the account is locked." }, "unlockAccount": { - "message": "Unlock account", + "message": "Avaa tili", "description": "Button text to display in overlay when the account is locked." }, "fillCredentialsFor": { @@ -2519,54 +2534,30 @@ "description": "Screen reader text for when overlay item is in focused" }, "partialUsername": { - "message": "Partial username", + "message": "Osittainen käyttäjätunnus", "description": "Screen reader text for when a login item is focused where a partial username is displayed. SR will announce this phrase before reading the text of the partial username" }, "noItemsToShow": { - "message": "No items to show", + "message": "Ei näytettäviä kohteita", "description": "Text to show in overlay if there are no matching items" }, "newItem": { - "message": "New item", + "message": "Uusi kohde", "description": "Button text to display in overlay when there are no matching items" }, "addNewVaultItem": { - "message": "Add new vault item", + "message": "Lisää uusi holvikohde", "description": "Screen reader text (aria-label) for new item button in overlay" }, "bitwardenOverlayMenuAvailable": { "message": "Bitwarden auto-fill menu available. Press the down arrow key to select.", "description": "Screen reader text for announcing when the overlay opens on the page" }, - "overrideBrowserAutofillTitle": { - "message": "Override browser auto-fill?", - "description": "Dialog title facilitating the ability to override a chrome browser's default autofill behavior" - }, - "overrideBrowserAutofillDescription": { - "message": "Leaving this setting off may cause conflicts between the Bitwarden auto-fill menu and your browser’s.", - "description": "Dialog message facilitating the ability to override a chrome browser's default autofill behavior" - }, - "overrideBrowserAutofillPrivacyRequiredDescription": { - "message": "Leaving this setting off may cause conflicts between the Bitwarden auto-fill menu and your browser’s. Turning this on will restart the Bitwarden extension.", - "description": "Dialog message facilitating the ability to override a chrome browser's default autofill behavior" - }, "turnOn": { - "message": "Turn on" + "message": "Ota käyttöön" }, "ignore": { - "message": "Ignore" - }, - "overrideBrowserAutoFillSettings": { - "message": "Override browser auto-fill settings", - "description": "Label for the setting that allows overriding the default browser autofill settings" - }, - "extensionPrivacyPermissionNotGrantedTitle": { - "message": "Unable to override browser auto-fill", - "description": "Title for the dialog that appears when the user has not granted the extension permission to set privacy settings" - }, - "extensionPrivacyPermissionNotGrantedDescription": { - "message": "Bitwarden must have access to the extension's privacy permission to override browser auto-fill settings.", - "description": "Description for the dialog that appears when the user has not granted the extension permission to set privacy settings" + "message": "Ohita" }, "importData": { "message": "Tuo tietoja", @@ -2679,6 +2670,9 @@ "confirmFilePassword": { "message": "Vahvista tiedoston salasana" }, + "typePasskey": { + "message": "Suojausavain" + }, "passkeyNotCopied": { "message": "Suojausavainta ei kopioida" }, @@ -2793,5 +2787,35 @@ }, "lastPassYubikeyDesc": { "message": "Kytke LastPass-tiliisi liitetty YubiKey tietokoneen USB-porttiin ja kosketa sen painiketta." + }, + "switchAccount": { + "message": "Vaihda tiliä" + }, + "switchAccounts": { + "message": "Vaihda tilejä" + }, + "switchToAccount": { + "message": "Vaihda tiliin" + }, + "activeAccount": { + "message": "Aktiivinen tili" + }, + "accountLimitReached": { + "message": "Tilien enimmäismäärä on saavutettu. Kirjaa jokin niistä ulos lisätäksesi tilin." + }, + "active": { + "message": "aktiivinen" + }, + "locked": { + "message": "lukittu" + }, + "unlocked": { + "message": "avoin" + }, + "server": { + "message": "palvelin" + }, + "hostedAt": { + "message": "palvelimelta" } } diff --git a/apps/browser/src/_locales/fil/messages.json b/apps/browser/src/_locales/fil/messages.json index fe689bf797c..f54a20d88ae 100644 --- a/apps/browser/src/_locales/fil/messages.json +++ b/apps/browser/src/_locales/fil/messages.json @@ -365,6 +365,9 @@ "unlockMethodNeededToChangeTimeoutActionDesc": { "message": "Mag-set up ng paraan ng pag-unlock upang baguhin ang iyong pagkilos sa pag-timeout ng vault." }, + "unlockMethodNeeded": { + "message": "Set up an unlock method in Settings" + }, "rateExtension": { "message": "I-rate ang extension" }, @@ -405,6 +408,9 @@ "lockNow": { "message": "Mag-kandado Na" }, + "lockAll": { + "message": "Lock all" + }, "immediately": { "message": "Kaagad" }, @@ -1027,6 +1033,12 @@ "showAutoFillMenuOnFormFieldsDescAlt": { "message": "Applies to all logged in accounts." }, + "turnOffBrowserBuiltInPasswordManagerSettings": { + "message": "Turn off your browser’s built in password manager settings to avoid conflicts." + }, + "turnOffBrowserBuiltInPasswordManagerSettingsLink": { + "message": "Edit browser settings." + }, "autofillOverlayVisibilityOff": { "message": "Nakasara", "description": "Overlay setting select option for disabling autofill overlay" @@ -1131,6 +1143,9 @@ "faviconDesc": { "message": "Ipakita ang isang kilalang larawan sa tabi ng bawat login." }, + "faviconDescAlt": { + "message": "Show a recognizable image next to each login. Applies to all logged in accounts." + }, "enableBadgeCounter": { "message": "Ipakita ang badge counter" }, @@ -2538,36 +2553,12 @@ "message": "Bitwarden auto-fill menu available. Press the down arrow key to select.", "description": "Screen reader text for announcing when the overlay opens on the page" }, - "overrideBrowserAutofillTitle": { - "message": "Override browser auto-fill?", - "description": "Dialog title facilitating the ability to override a chrome browser's default autofill behavior" - }, - "overrideBrowserAutofillDescription": { - "message": "Leaving this setting off may cause conflicts between the Bitwarden auto-fill menu and your browser’s.", - "description": "Dialog message facilitating the ability to override a chrome browser's default autofill behavior" - }, - "overrideBrowserAutofillPrivacyRequiredDescription": { - "message": "Leaving this setting off may cause conflicts between the Bitwarden auto-fill menu and your browser’s. Turning this on will restart the Bitwarden extension.", - "description": "Dialog message facilitating the ability to override a chrome browser's default autofill behavior" - }, "turnOn": { "message": "Turn on" }, "ignore": { "message": "Ignore" }, - "overrideBrowserAutoFillSettings": { - "message": "Override browser auto-fill settings", - "description": "Label for the setting that allows overriding the default browser autofill settings" - }, - "extensionPrivacyPermissionNotGrantedTitle": { - "message": "Unable to override browser auto-fill", - "description": "Title for the dialog that appears when the user has not granted the extension permission to set privacy settings" - }, - "extensionPrivacyPermissionNotGrantedDescription": { - "message": "Bitwarden must have access to the extension's privacy permission to override browser auto-fill settings.", - "description": "Description for the dialog that appears when the user has not granted the extension permission to set privacy settings" - }, "importData": { "message": "Import data", "description": "Used for the header of the import dialog, the import button and within the file-password-prompt" @@ -2679,6 +2670,9 @@ "confirmFilePassword": { "message": "Confirm file password" }, + "typePasskey": { + "message": "Passkey" + }, "passkeyNotCopied": { "message": "Passkey will not be copied" }, @@ -2793,5 +2787,35 @@ }, "lastPassYubikeyDesc": { "message": "Insert the YubiKey associated with your LastPass account into your computer's USB port, then touch its button." + }, + "switchAccount": { + "message": "Switch account" + }, + "switchAccounts": { + "message": "Switch accounts" + }, + "switchToAccount": { + "message": "Switch to account" + }, + "activeAccount": { + "message": "Active account" + }, + "accountLimitReached": { + "message": "Account limit reached. Log out of an account to add another." + }, + "active": { + "message": "active" + }, + "locked": { + "message": "locked" + }, + "unlocked": { + "message": "unlocked" + }, + "server": { + "message": "server" + }, + "hostedAt": { + "message": "hosted at" } } diff --git a/apps/browser/src/_locales/fr/messages.json b/apps/browser/src/_locales/fr/messages.json index eff9cad4687..84991609bda 100644 --- a/apps/browser/src/_locales/fr/messages.json +++ b/apps/browser/src/_locales/fr/messages.json @@ -365,6 +365,9 @@ "unlockMethodNeededToChangeTimeoutActionDesc": { "message": "Configurez une méthode de déverrouillage pour changer le délai d'attente de votre coffre." }, + "unlockMethodNeeded": { + "message": "Configurer une méthode de déverrouillage dans Paramètres" + }, "rateExtension": { "message": "Noter l'extension" }, @@ -405,6 +408,9 @@ "lockNow": { "message": "Verrouiller maintenant" }, + "lockAll": { + "message": "Tout verrouiller" + }, "immediately": { "message": "Immédiatement" }, @@ -698,7 +704,7 @@ "message": "Modifier le thème de couleur de l'application." }, "themeDescAlt": { - "message": "Change the application's color theme. Applies to all logged in accounts." + "message": "Modifier le thème de couleur de l'application. S'applique à tous les comptes connectés." }, "dark": { "message": "Sombre", @@ -1021,12 +1027,18 @@ "message": "Les URLs d'environnement ont été enregistrées." }, "showAutoFillMenuOnFormFields": { - "message": "Show auto-fill menu on form fields", + "message": "Afficher le menu de saisie automatique sur les champs du formulaire", "description": "Represents the message for allowing the user to enable the auto-fill overlay" }, "showAutoFillMenuOnFormFieldsDescAlt": { "message": "Appliquer à tous les comptes connectés." }, + "turnOffBrowserBuiltInPasswordManagerSettings": { + "message": "Turn off your browser’s built in password manager settings to avoid conflicts." + }, + "turnOffBrowserBuiltInPasswordManagerSettingsLink": { + "message": "Edit browser settings." + }, "autofillOverlayVisibilityOff": { "message": "Off", "description": "Overlay setting select option for disabling autofill overlay" @@ -1036,7 +1048,7 @@ "description": "Overlay appearance select option for showing the field on focus of the input element" }, "autofillOverlayVisibilityOnButtonClick": { - "message": "When auto-fill icon is selected", + "message": "Lorsque l'icône de saisie automatique est sélectionnée", "description": "Overlay appearance select option for showing the field on click of the overlay icon" }, "enableAutoFillOnPageLoad": { @@ -1131,6 +1143,9 @@ "faviconDesc": { "message": "Afficher une image reconnaissable à côté de chaque identifiant." }, + "faviconDescAlt": { + "message": "Show a recognizable image next to each login. Applies to all logged in accounts." + }, "enableBadgeCounter": { "message": "Afficher le compteur de badge" }, @@ -2495,7 +2510,7 @@ "description": "Message appearing below the autofill on load message when master password reprompt is set for a vault item." }, "bitwardenOverlayButton": { - "message": "Bitwarden auto-fill menu button", + "message": "Bouton menu de saisie automatique Bitwarden", "description": "Page title for the iframe containing the overlay button" }, "toggleBitwardenVaultOverlay": { @@ -2519,7 +2534,7 @@ "description": "Screen reader text for when overlay item is in focused" }, "partialUsername": { - "message": "Partial username", + "message": "Nom d'utilisateur partiel", "description": "Screen reader text for when a login item is focused where a partial username is displayed. SR will announce this phrase before reading the text of the partial username" }, "noItemsToShow": { @@ -2538,36 +2553,12 @@ "message": "Bitwarden auto-fill menu available. Press the down arrow key to select.", "description": "Screen reader text for announcing when the overlay opens on the page" }, - "overrideBrowserAutofillTitle": { - "message": "Override browser auto-fill?", - "description": "Dialog title facilitating the ability to override a chrome browser's default autofill behavior" - }, - "overrideBrowserAutofillDescription": { - "message": "Leaving this setting off may cause conflicts between the Bitwarden auto-fill menu and your browser’s.", - "description": "Dialog message facilitating the ability to override a chrome browser's default autofill behavior" - }, - "overrideBrowserAutofillPrivacyRequiredDescription": { - "message": "Leaving this setting off may cause conflicts between the Bitwarden auto-fill menu and your browser’s. Turning this on will restart the Bitwarden extension.", - "description": "Dialog message facilitating the ability to override a chrome browser's default autofill behavior" - }, "turnOn": { "message": "Activer" }, "ignore": { "message": "Ignorer" }, - "overrideBrowserAutoFillSettings": { - "message": "Override browser auto-fill settings", - "description": "Label for the setting that allows overriding the default browser autofill settings" - }, - "extensionPrivacyPermissionNotGrantedTitle": { - "message": "Unable to override browser auto-fill", - "description": "Title for the dialog that appears when the user has not granted the extension permission to set privacy settings" - }, - "extensionPrivacyPermissionNotGrantedDescription": { - "message": "Bitwarden must have access to the extension's privacy permission to override browser auto-fill settings.", - "description": "Description for the dialog that appears when the user has not granted the extension permission to set privacy settings" - }, "importData": { "message": "Import data", "description": "Used for the header of the import dialog, the import button and within the file-password-prompt" @@ -2679,6 +2670,9 @@ "confirmFilePassword": { "message": "Confirmez le mot de passe du fichier" }, + "typePasskey": { + "message": "Clé d'accès" + }, "passkeyNotCopied": { "message": "La clé d'accès ne sera pas copiée" }, @@ -2793,5 +2787,35 @@ }, "lastPassYubikeyDesc": { "message": "Insérez la YubiKey associée à votre compte LastPass dans le port USB de votre ordinateur, puis appuyez sur son bouton." + }, + "switchAccount": { + "message": "Switch account" + }, + "switchAccounts": { + "message": "Switch accounts" + }, + "switchToAccount": { + "message": "Switch to account" + }, + "activeAccount": { + "message": "Active account" + }, + "accountLimitReached": { + "message": "Account limit reached. Log out of an account to add another." + }, + "active": { + "message": "active" + }, + "locked": { + "message": "locked" + }, + "unlocked": { + "message": "unlocked" + }, + "server": { + "message": "server" + }, + "hostedAt": { + "message": "hosted at" } } diff --git a/apps/browser/src/_locales/gl/messages.json b/apps/browser/src/_locales/gl/messages.json index 5f243afe040..ab3933bf5b7 100644 --- a/apps/browser/src/_locales/gl/messages.json +++ b/apps/browser/src/_locales/gl/messages.json @@ -365,6 +365,9 @@ "unlockMethodNeededToChangeTimeoutActionDesc": { "message": "Set up an unlock method to change your vault timeout action." }, + "unlockMethodNeeded": { + "message": "Set up an unlock method in Settings" + }, "rateExtension": { "message": "Rate the extension" }, @@ -405,6 +408,9 @@ "lockNow": { "message": "Lock now" }, + "lockAll": { + "message": "Lock all" + }, "immediately": { "message": "Immediately" }, @@ -1027,6 +1033,12 @@ "showAutoFillMenuOnFormFieldsDescAlt": { "message": "Applies to all logged in accounts." }, + "turnOffBrowserBuiltInPasswordManagerSettings": { + "message": "Turn off your browser’s built in password manager settings to avoid conflicts." + }, + "turnOffBrowserBuiltInPasswordManagerSettingsLink": { + "message": "Edit browser settings." + }, "autofillOverlayVisibilityOff": { "message": "Off", "description": "Overlay setting select option for disabling autofill overlay" @@ -1131,6 +1143,9 @@ "faviconDesc": { "message": "Show a recognizable image next to each login." }, + "faviconDescAlt": { + "message": "Show a recognizable image next to each login. Applies to all logged in accounts." + }, "enableBadgeCounter": { "message": "Show badge counter" }, @@ -2538,36 +2553,12 @@ "message": "Bitwarden auto-fill menu available. Press the down arrow key to select.", "description": "Screen reader text for announcing when the overlay opens on the page" }, - "overrideBrowserAutofillTitle": { - "message": "Override browser auto-fill?", - "description": "Dialog title facilitating the ability to override a chrome browser's default autofill behavior" - }, - "overrideBrowserAutofillDescription": { - "message": "Leaving this setting off may cause conflicts between the Bitwarden auto-fill menu and your browser’s.", - "description": "Dialog message facilitating the ability to override a chrome browser's default autofill behavior" - }, - "overrideBrowserAutofillPrivacyRequiredDescription": { - "message": "Leaving this setting off may cause conflicts between the Bitwarden auto-fill menu and your browser’s. Turning this on will restart the Bitwarden extension.", - "description": "Dialog message facilitating the ability to override a chrome browser's default autofill behavior" - }, "turnOn": { "message": "Turn on" }, "ignore": { "message": "Ignore" }, - "overrideBrowserAutoFillSettings": { - "message": "Override browser auto-fill settings", - "description": "Label for the setting that allows overriding the default browser autofill settings" - }, - "extensionPrivacyPermissionNotGrantedTitle": { - "message": "Unable to override browser auto-fill", - "description": "Title for the dialog that appears when the user has not granted the extension permission to set privacy settings" - }, - "extensionPrivacyPermissionNotGrantedDescription": { - "message": "Bitwarden must have access to the extension's privacy permission to override browser auto-fill settings.", - "description": "Description for the dialog that appears when the user has not granted the extension permission to set privacy settings" - }, "importData": { "message": "Import data", "description": "Used for the header of the import dialog, the import button and within the file-password-prompt" @@ -2679,6 +2670,9 @@ "confirmFilePassword": { "message": "Confirm file password" }, + "typePasskey": { + "message": "Passkey" + }, "passkeyNotCopied": { "message": "Passkey will not be copied" }, @@ -2793,5 +2787,35 @@ }, "lastPassYubikeyDesc": { "message": "Insert the YubiKey associated with your LastPass account into your computer's USB port, then touch its button." + }, + "switchAccount": { + "message": "Switch account" + }, + "switchAccounts": { + "message": "Switch accounts" + }, + "switchToAccount": { + "message": "Switch to account" + }, + "activeAccount": { + "message": "Active account" + }, + "accountLimitReached": { + "message": "Account limit reached. Log out of an account to add another." + }, + "active": { + "message": "active" + }, + "locked": { + "message": "locked" + }, + "unlocked": { + "message": "unlocked" + }, + "server": { + "message": "server" + }, + "hostedAt": { + "message": "hosted at" } } diff --git a/apps/browser/src/_locales/he/messages.json b/apps/browser/src/_locales/he/messages.json index 2bd5216f861..c195d4cb960 100644 --- a/apps/browser/src/_locales/he/messages.json +++ b/apps/browser/src/_locales/he/messages.json @@ -365,6 +365,9 @@ "unlockMethodNeededToChangeTimeoutActionDesc": { "message": "Set up an unlock method to change your vault timeout action." }, + "unlockMethodNeeded": { + "message": "Set up an unlock method in Settings" + }, "rateExtension": { "message": "דירוג הרחבה" }, @@ -405,6 +408,9 @@ "lockNow": { "message": "נעל עכשיו" }, + "lockAll": { + "message": "Lock all" + }, "immediately": { "message": "באופן מיידי" }, @@ -1027,6 +1033,12 @@ "showAutoFillMenuOnFormFieldsDescAlt": { "message": "Applies to all logged in accounts." }, + "turnOffBrowserBuiltInPasswordManagerSettings": { + "message": "Turn off your browser’s built in password manager settings to avoid conflicts." + }, + "turnOffBrowserBuiltInPasswordManagerSettingsLink": { + "message": "Edit browser settings." + }, "autofillOverlayVisibilityOff": { "message": "Off", "description": "Overlay setting select option for disabling autofill overlay" @@ -1131,6 +1143,9 @@ "faviconDesc": { "message": "Show a recognizable image next to each login." }, + "faviconDescAlt": { + "message": "Show a recognizable image next to each login. Applies to all logged in accounts." + }, "enableBadgeCounter": { "message": "Show badge counter" }, @@ -2538,36 +2553,12 @@ "message": "Bitwarden auto-fill menu available. Press the down arrow key to select.", "description": "Screen reader text for announcing when the overlay opens on the page" }, - "overrideBrowserAutofillTitle": { - "message": "Override browser auto-fill?", - "description": "Dialog title facilitating the ability to override a chrome browser's default autofill behavior" - }, - "overrideBrowserAutofillDescription": { - "message": "Leaving this setting off may cause conflicts between the Bitwarden auto-fill menu and your browser’s.", - "description": "Dialog message facilitating the ability to override a chrome browser's default autofill behavior" - }, - "overrideBrowserAutofillPrivacyRequiredDescription": { - "message": "Leaving this setting off may cause conflicts between the Bitwarden auto-fill menu and your browser’s. Turning this on will restart the Bitwarden extension.", - "description": "Dialog message facilitating the ability to override a chrome browser's default autofill behavior" - }, "turnOn": { "message": "Turn on" }, "ignore": { "message": "Ignore" }, - "overrideBrowserAutoFillSettings": { - "message": "Override browser auto-fill settings", - "description": "Label for the setting that allows overriding the default browser autofill settings" - }, - "extensionPrivacyPermissionNotGrantedTitle": { - "message": "Unable to override browser auto-fill", - "description": "Title for the dialog that appears when the user has not granted the extension permission to set privacy settings" - }, - "extensionPrivacyPermissionNotGrantedDescription": { - "message": "Bitwarden must have access to the extension's privacy permission to override browser auto-fill settings.", - "description": "Description for the dialog that appears when the user has not granted the extension permission to set privacy settings" - }, "importData": { "message": "ייבוא נתונים", "description": "Used for the header of the import dialog, the import button and within the file-password-prompt" @@ -2679,6 +2670,9 @@ "confirmFilePassword": { "message": "Confirm file password" }, + "typePasskey": { + "message": "Passkey" + }, "passkeyNotCopied": { "message": "Passkey will not be copied" }, @@ -2793,5 +2787,35 @@ }, "lastPassYubikeyDesc": { "message": "Insert the YubiKey associated with your LastPass account into your computer's USB port, then touch its button." + }, + "switchAccount": { + "message": "Switch account" + }, + "switchAccounts": { + "message": "Switch accounts" + }, + "switchToAccount": { + "message": "Switch to account" + }, + "activeAccount": { + "message": "Active account" + }, + "accountLimitReached": { + "message": "Account limit reached. Log out of an account to add another." + }, + "active": { + "message": "active" + }, + "locked": { + "message": "locked" + }, + "unlocked": { + "message": "unlocked" + }, + "server": { + "message": "server" + }, + "hostedAt": { + "message": "hosted at" } } diff --git a/apps/browser/src/_locales/hi/messages.json b/apps/browser/src/_locales/hi/messages.json index d24eacb14b0..99b9dfa3cbf 100644 --- a/apps/browser/src/_locales/hi/messages.json +++ b/apps/browser/src/_locales/hi/messages.json @@ -365,6 +365,9 @@ "unlockMethodNeededToChangeTimeoutActionDesc": { "message": "Set up an unlock method to change your vault timeout action." }, + "unlockMethodNeeded": { + "message": "Set up an unlock method in Settings" + }, "rateExtension": { "message": "Rate the Extension" }, @@ -405,6 +408,9 @@ "lockNow": { "message": "Lock Now" }, + "lockAll": { + "message": "Lock all" + }, "immediately": { "message": "तत्‍काल" }, @@ -1027,6 +1033,12 @@ "showAutoFillMenuOnFormFieldsDescAlt": { "message": "Applies to all logged in accounts." }, + "turnOffBrowserBuiltInPasswordManagerSettings": { + "message": "Turn off your browser’s built in password manager settings to avoid conflicts." + }, + "turnOffBrowserBuiltInPasswordManagerSettingsLink": { + "message": "Edit browser settings." + }, "autofillOverlayVisibilityOff": { "message": "Off", "description": "Overlay setting select option for disabling autofill overlay" @@ -1131,6 +1143,9 @@ "faviconDesc": { "message": "प्रत्येक लॉगिन के आगे एक पहचानने योग्य छवि दिखाएं।" }, + "faviconDescAlt": { + "message": "Show a recognizable image next to each login. Applies to all logged in accounts." + }, "enableBadgeCounter": { "message": "बैज काउंटर दिखाएं" }, @@ -2538,36 +2553,12 @@ "message": "Bitwarden auto-fill menu available. Press the down arrow key to select.", "description": "Screen reader text for announcing when the overlay opens on the page" }, - "overrideBrowserAutofillTitle": { - "message": "Override browser auto-fill?", - "description": "Dialog title facilitating the ability to override a chrome browser's default autofill behavior" - }, - "overrideBrowserAutofillDescription": { - "message": "Leaving this setting off may cause conflicts between the Bitwarden auto-fill menu and your browser’s.", - "description": "Dialog message facilitating the ability to override a chrome browser's default autofill behavior" - }, - "overrideBrowserAutofillPrivacyRequiredDescription": { - "message": "Leaving this setting off may cause conflicts between the Bitwarden auto-fill menu and your browser’s. Turning this on will restart the Bitwarden extension.", - "description": "Dialog message facilitating the ability to override a chrome browser's default autofill behavior" - }, "turnOn": { "message": "Turn on" }, "ignore": { "message": "Ignore" }, - "overrideBrowserAutoFillSettings": { - "message": "Override browser auto-fill settings", - "description": "Label for the setting that allows overriding the default browser autofill settings" - }, - "extensionPrivacyPermissionNotGrantedTitle": { - "message": "Unable to override browser auto-fill", - "description": "Title for the dialog that appears when the user has not granted the extension permission to set privacy settings" - }, - "extensionPrivacyPermissionNotGrantedDescription": { - "message": "Bitwarden must have access to the extension's privacy permission to override browser auto-fill settings.", - "description": "Description for the dialog that appears when the user has not granted the extension permission to set privacy settings" - }, "importData": { "message": "Import data", "description": "Used for the header of the import dialog, the import button and within the file-password-prompt" @@ -2679,6 +2670,9 @@ "confirmFilePassword": { "message": "Confirm file password" }, + "typePasskey": { + "message": "Passkey" + }, "passkeyNotCopied": { "message": "Passkey will not be copied" }, @@ -2793,5 +2787,35 @@ }, "lastPassYubikeyDesc": { "message": "Insert the YubiKey associated with your LastPass account into your computer's USB port, then touch its button." + }, + "switchAccount": { + "message": "Switch account" + }, + "switchAccounts": { + "message": "Switch accounts" + }, + "switchToAccount": { + "message": "Switch to account" + }, + "activeAccount": { + "message": "Active account" + }, + "accountLimitReached": { + "message": "Account limit reached. Log out of an account to add another." + }, + "active": { + "message": "active" + }, + "locked": { + "message": "locked" + }, + "unlocked": { + "message": "unlocked" + }, + "server": { + "message": "server" + }, + "hostedAt": { + "message": "hosted at" } } diff --git a/apps/browser/src/_locales/hr/messages.json b/apps/browser/src/_locales/hr/messages.json index 571351bfd46..c338a11edfe 100644 --- a/apps/browser/src/_locales/hr/messages.json +++ b/apps/browser/src/_locales/hr/messages.json @@ -365,6 +365,9 @@ "unlockMethodNeededToChangeTimeoutActionDesc": { "message": "Za promjenu vremena isteka trezora, odredi način otključavanja." }, + "unlockMethodNeeded": { + "message": "Set up an unlock method in Settings" + }, "rateExtension": { "message": "Ocijeni proširenje" }, @@ -405,6 +408,9 @@ "lockNow": { "message": "Zaključaj sada" }, + "lockAll": { + "message": "Lock all" + }, "immediately": { "message": "Odmah" }, @@ -620,7 +626,7 @@ "message": "Upit za dodavanje prijave pojavljuje se kada se otkrije prva prijava na neko web mjesto. Bitwarden će te pitatati želiš li uneseno korisničko ime i lozinku spremiti u svoj trezor." }, "addLoginNotificationDescAlt": { - "message": "Ask to add an item if one isn't found in your vault. Applies to all logged in accounts." + "message": "Pitaj za dodavanje stavke ako nije pronađena u tvojem trezoru. Primjenjuje se na sve prijavljene račune." }, "showCardsCurrentTab": { "message": "Prikaži platne kartice" @@ -655,13 +661,13 @@ "message": "Upitaj za ažuriranje lozinke prijave ako se otkrije promjena na web stranici." }, "changedPasswordNotificationDescAlt": { - "message": "Ask to update a login's password when a change is detected on a website. Applies to all logged in accounts." + "message": "Pitaj za ažuriranje lozinke za prijavu kada se otkrije promjena na web stranici. Primjenjuje se na sve prijavljene račune." }, "enableUsePasskeys": { - "message": "Ask to save and use passkeys" + "message": "Pitaj za spremanje i korištenje pristupnih ključeva" }, "usePasskeysDesc": { - "message": "Ask to save new passkeys or log in with passkeys stored in your vault. Applies to all logged in accounts." + "message": "Pitaj za spremanje novih pristupnih ključeva ili se prijavi pomoću pristupnih ključeva pohranjenih u tvojem trezoru. Primjenjuje se na sve prijavljene račune." }, "notificationChangeDesc": { "message": "Želiš li ovu lozinku ažurirati u Bitwarden-u?" @@ -682,7 +688,7 @@ "message": "Koristi sekundarni klik za pristup generatoru lozinki i pripadajućim prijavama trenunte web stranice. " }, "contextMenuItemDescAlt": { - "message": "Use a secondary click to access password generation and matching logins for the website. Applies to all logged in accounts." + "message": "Koristi sekundarni klik za pristup generiranju lozinki i odgovarajućim prijavama za mrežno mjesto. Primjenjuje se na sve prijavljene račune." }, "defaultUriMatchDetection": { "message": "Zadano otkrivanje URI podudaranja", @@ -698,7 +704,7 @@ "message": "Promijeni temu boja." }, "themeDescAlt": { - "message": "Change the application's color theme. Applies to all logged in accounts." + "message": "Promijeni boju aplikacije. Primjenjuje se na sve prijavljene račune." }, "dark": { "message": "Tamna", @@ -1021,22 +1027,28 @@ "message": "URL-ovi okoline su spremljeni." }, "showAutoFillMenuOnFormFields": { - "message": "Show auto-fill menu on form fields", + "message": "Prikaži izbornik za auto-ispunu u poljima obrasca", "description": "Represents the message for allowing the user to enable the auto-fill overlay" }, "showAutoFillMenuOnFormFieldsDescAlt": { - "message": "Applies to all logged in accounts." + "message": "Primjenjuje se na sve prijavljene račune." + }, + "turnOffBrowserBuiltInPasswordManagerSettings": { + "message": "Turn off your browser’s built in password manager settings to avoid conflicts." + }, + "turnOffBrowserBuiltInPasswordManagerSettingsLink": { + "message": "Edit browser settings." }, "autofillOverlayVisibilityOff": { - "message": "Off", + "message": "Isključeno", "description": "Overlay setting select option for disabling autofill overlay" }, "autofillOverlayVisibilityOnFieldFocus": { - "message": "When field is selected (on focus)", + "message": "Kada je odabrano polje (u fokusu)", "description": "Overlay appearance select option for showing the field on focus of the input element" }, "autofillOverlayVisibilityOnButtonClick": { - "message": "When auto-fill icon is selected", + "message": "Kada je odabrana ikona auto-ispune", "description": "Overlay appearance select option for showing the field on click of the overlay icon" }, "enableAutoFillOnPageLoad": { @@ -1131,6 +1143,9 @@ "faviconDesc": { "message": "Prikaži prepoznatljivu sliku pored svake prijave." }, + "faviconDescAlt": { + "message": "Show a recognizable image next to each login. Applies to all logged in accounts." + }, "enableBadgeCounter": { "message": "Prikaži značku brojača" }, @@ -1700,7 +1715,7 @@ "message": "Bitwarden neće pitati treba li spremiti prijavne podatke za ove domene. Za primjenu promjena, potrebno je osvježiti stranicu." }, "excludedDomainsDescAlt": { - "message": "Bitwarden will not ask to save login details for these domains for all logged in accounts. You must refresh the page for changes to take effect." + "message": "Bitwarden neće tražiti spremanje podataka za prijavu za ove domene za sve prijavljene račune. Moraš osvježiti stranicu kako bi promjene stupile na snagu." }, "excludedDomainsInvalidDomain": { "message": "$DOMAIN$ nije valjana domena", @@ -2264,7 +2279,7 @@ "message": "Kako auto-ispuniti" }, "autofillSelectInfoWithCommand": { - "message": "Select an item from this screen, use the shortcut $COMMAND$, or explore other options in settings.", + "message": "Odaberi stavku s ovog zaslona, upotrijebi prečac $COMMAND$ ili istraži druge opcije u postavkama.", "placeholders": { "command": { "content": "$1", @@ -2273,7 +2288,7 @@ } }, "autofillSelectInfoWithoutCommand": { - "message": "Select an item from this screen, or explore other options in settings." + "message": "Odaberi stavku s ovog zaslona ili istraži druge opcije u postavkama." }, "gotIt": { "message": "U redu" @@ -2495,78 +2510,54 @@ "description": "Message appearing below the autofill on load message when master password reprompt is set for a vault item." }, "bitwardenOverlayButton": { - "message": "Bitwarden auto-fill menu button", + "message": "Tipka izbornika Bitwarden auto-ispune", "description": "Page title for the iframe containing the overlay button" }, "toggleBitwardenVaultOverlay": { - "message": "Toggle Bitwarden auto-fill menu", + "message": "U/isključivanje izbornika Bitwarden auto-ispune", "description": "Screen reader and tool tip label for the overlay button" }, "bitwardenVault": { - "message": "Bitwarden auto-fill menu", + "message": "Izbornik Bitwarden auto-ispune", "description": "Page title in overlay" }, "unlockYourAccountToViewMatchingLogins": { - "message": "Unlock your account to view matching logins", + "message": "Otklučaj svoj račun za prikaz podudarnih prijava", "description": "Text to display in overlay when the account is locked." }, "unlockAccount": { - "message": "Unlock account", + "message": "Otključaj račun", "description": "Button text to display in overlay when the account is locked." }, "fillCredentialsFor": { - "message": "Fill credentials for", + "message": "Unesi vjerodajnice za", "description": "Screen reader text for when overlay item is in focused" }, "partialUsername": { - "message": "Partial username", + "message": "Djelomično korisničko ime", "description": "Screen reader text for when a login item is focused where a partial username is displayed. SR will announce this phrase before reading the text of the partial username" }, "noItemsToShow": { - "message": "No items to show", + "message": "Nema stavki za prikaz", "description": "Text to show in overlay if there are no matching items" }, "newItem": { - "message": "New item", + "message": "Nova stavka", "description": "Button text to display in overlay when there are no matching items" }, "addNewVaultItem": { - "message": "Add new vault item", + "message": "Dodaj novu stavku trezora", "description": "Screen reader text (aria-label) for new item button in overlay" }, "bitwardenOverlayMenuAvailable": { - "message": "Bitwarden auto-fill menu available. Press the down arrow key to select.", + "message": "Dostupan je Bitwarden izbornik auto-ispune. Pritisni tipku sa strelicom prema dolje za odabir.", "description": "Screen reader text for announcing when the overlay opens on the page" }, - "overrideBrowserAutofillTitle": { - "message": "Override browser auto-fill?", - "description": "Dialog title facilitating the ability to override a chrome browser's default autofill behavior" - }, - "overrideBrowserAutofillDescription": { - "message": "Leaving this setting off may cause conflicts between the Bitwarden auto-fill menu and your browser’s.", - "description": "Dialog message facilitating the ability to override a chrome browser's default autofill behavior" - }, - "overrideBrowserAutofillPrivacyRequiredDescription": { - "message": "Leaving this setting off may cause conflicts between the Bitwarden auto-fill menu and your browser’s. Turning this on will restart the Bitwarden extension.", - "description": "Dialog message facilitating the ability to override a chrome browser's default autofill behavior" - }, "turnOn": { - "message": "Turn on" + "message": "Uključi" }, "ignore": { - "message": "Ignore" - }, - "overrideBrowserAutoFillSettings": { - "message": "Override browser auto-fill settings", - "description": "Label for the setting that allows overriding the default browser autofill settings" - }, - "extensionPrivacyPermissionNotGrantedTitle": { - "message": "Unable to override browser auto-fill", - "description": "Title for the dialog that appears when the user has not granted the extension permission to set privacy settings" - }, - "extensionPrivacyPermissionNotGrantedDescription": { - "message": "Bitwarden must have access to the extension's privacy permission to override browser auto-fill settings.", - "description": "Description for the dialog that appears when the user has not granted the extension permission to set privacy settings" + "message": "Zanemari" }, "importData": { "message": "Uvezi podatke", @@ -2679,6 +2670,9 @@ "confirmFilePassword": { "message": "Potvrdi lozinku datoteke" }, + "typePasskey": { + "message": "Pristupni ključ" + }, "passkeyNotCopied": { "message": "Pristupni ključ neće biti kopiran" }, @@ -2793,5 +2787,35 @@ }, "lastPassYubikeyDesc": { "message": "Umetni YubiKey pridružen svojem LastPass računu u USB priključak račuanala, a zatim dodirni njegovu tipku." + }, + "switchAccount": { + "message": "Switch account" + }, + "switchAccounts": { + "message": "Switch accounts" + }, + "switchToAccount": { + "message": "Switch to account" + }, + "activeAccount": { + "message": "Active account" + }, + "accountLimitReached": { + "message": "Account limit reached. Log out of an account to add another." + }, + "active": { + "message": "active" + }, + "locked": { + "message": "locked" + }, + "unlocked": { + "message": "unlocked" + }, + "server": { + "message": "server" + }, + "hostedAt": { + "message": "hosted at" } } diff --git a/apps/browser/src/_locales/hu/messages.json b/apps/browser/src/_locales/hu/messages.json index db8ec6ec104..7bd6558daa7 100644 --- a/apps/browser/src/_locales/hu/messages.json +++ b/apps/browser/src/_locales/hu/messages.json @@ -365,6 +365,9 @@ "unlockMethodNeededToChangeTimeoutActionDesc": { "message": "Állítsunk be egy feloldási módot a széf időkifutási műveletének módosításához." }, + "unlockMethodNeeded": { + "message": "Feloldási mód beállítása a Beállításokban" + }, "rateExtension": { "message": "Bővítmény értékelése" }, @@ -405,6 +408,9 @@ "lockNow": { "message": "Zárolás most" }, + "lockAll": { + "message": "Összes zárolása" + }, "immediately": { "message": "Azonnal" }, @@ -658,10 +664,10 @@ "message": "A bejelentkezési jelszó frissítésének kérése, ha változást lett érzékelve egy webhelyen. Minden bejelentkezett fiókra vonatkozik." }, "enableUsePasskeys": { - "message": "Kérés a jelszó mentésére és használatára" + "message": "Kérés a jhozzáférési kulcs mentésére és használatára" }, "usePasskeysDesc": { - "message": "Kérés az új jelszavak mentésére vagy bejelentkezés a széfben tárolt jelszavakkal. Minden bejelentkezett fiókra vonatkozik." + "message": "Kérés az új hozzáféréi kulcsok mentésére vagy bejelentkezés a széfben tárolt hozzáférési kulcsokkal. Minden bejelentkezett fiókra vonatkozik." }, "notificationChangeDesc": { "message": "Frissítésre kerüljön a jelszó a Bitwardenben?" @@ -1021,14 +1027,20 @@ "message": "A környezeti webcímek mentésre kerültek." }, "showAutoFillMenuOnFormFields": { - "message": "Show auto-fill menu on form fields", + "message": "Automatikus kitöltés menü megjelenítése az űrlapmezőkön", "description": "Represents the message for allowing the user to enable the auto-fill overlay" }, "showAutoFillMenuOnFormFieldsDescAlt": { "message": "Minden bejelentkezett fiókra vonatkozik." }, + "turnOffBrowserBuiltInPasswordManagerSettings": { + "message": "Turn off your browser’s built in password manager settings to avoid conflicts." + }, + "turnOffBrowserBuiltInPasswordManagerSettingsLink": { + "message": "Edit browser settings." + }, "autofillOverlayVisibilityOff": { - "message": "Off", + "message": "Ki", "description": "Overlay setting select option for disabling autofill overlay" }, "autofillOverlayVisibilityOnFieldFocus": { @@ -1036,7 +1048,7 @@ "description": "Overlay appearance select option for showing the field on focus of the input element" }, "autofillOverlayVisibilityOnButtonClick": { - "message": "When auto-fill icon is selected", + "message": "Ha az automatikus kitöltés menü került kiválasztásra", "description": "Overlay appearance select option for showing the field on click of the overlay icon" }, "enableAutoFillOnPageLoad": { @@ -1131,6 +1143,9 @@ "faviconDesc": { "message": "Felismerhető kép megjelenítése minden bejelentkezés mellett." }, + "faviconDescAlt": { + "message": "Minden bejelentkezés mellett egy felismerhető kép megjelenítése. Minden bejelentkezett fiókra vonatkozik." + }, "enableBadgeCounter": { "message": "Számláló jelvény megjelenítése" }, @@ -2503,7 +2518,7 @@ "description": "Screen reader and tool tip label for the overlay button" }, "bitwardenVault": { - "message": "Bitwarden auto-fill menu", + "message": "Bitwarden automatikus kitöltési menü", "description": "Page title in overlay" }, "unlockYourAccountToViewMatchingLogins": { @@ -2511,62 +2526,38 @@ "description": "Text to display in overlay when the account is locked." }, "unlockAccount": { - "message": "Unlock account", + "message": "Fiók feloldása", "description": "Button text to display in overlay when the account is locked." }, "fillCredentialsFor": { - "message": "Fill credentials for", + "message": "Töltse kia hitelesítő adatokat", "description": "Screen reader text for when overlay item is in focused" }, "partialUsername": { - "message": "Partial username", + "message": "Részleges felhasználónév", "description": "Screen reader text for when a login item is focused where a partial username is displayed. SR will announce this phrase before reading the text of the partial username" }, "noItemsToShow": { - "message": "No items to show", + "message": "Nincsenek megjeleníthető elemek", "description": "Text to show in overlay if there are no matching items" }, "newItem": { - "message": "New item", + "message": "Új elem", "description": "Button text to display in overlay when there are no matching items" }, "addNewVaultItem": { - "message": "Add new vault item", + "message": "Elem hozzáadása", "description": "Screen reader text (aria-label) for new item button in overlay" }, "bitwardenOverlayMenuAvailable": { "message": "Bitwarden auto-fill menu available. Press the down arrow key to select.", "description": "Screen reader text for announcing when the overlay opens on the page" }, - "overrideBrowserAutofillTitle": { - "message": "Override browser auto-fill?", - "description": "Dialog title facilitating the ability to override a chrome browser's default autofill behavior" - }, - "overrideBrowserAutofillDescription": { - "message": "Leaving this setting off may cause conflicts between the Bitwarden auto-fill menu and your browser’s.", - "description": "Dialog message facilitating the ability to override a chrome browser's default autofill behavior" - }, - "overrideBrowserAutofillPrivacyRequiredDescription": { - "message": "Leaving this setting off may cause conflicts between the Bitwarden auto-fill menu and your browser’s. Turning this on will restart the Bitwarden extension.", - "description": "Dialog message facilitating the ability to override a chrome browser's default autofill behavior" - }, "turnOn": { - "message": "Turn on" + "message": "Bekapcsolás" }, "ignore": { - "message": "Ignore" - }, - "overrideBrowserAutoFillSettings": { - "message": "Override browser auto-fill settings", - "description": "Label for the setting that allows overriding the default browser autofill settings" - }, - "extensionPrivacyPermissionNotGrantedTitle": { - "message": "Unable to override browser auto-fill", - "description": "Title for the dialog that appears when the user has not granted the extension permission to set privacy settings" - }, - "extensionPrivacyPermissionNotGrantedDescription": { - "message": "Bitwarden must have access to the extension's privacy permission to override browser auto-fill settings.", - "description": "Description for the dialog that appears when the user has not granted the extension permission to set privacy settings" + "message": "Mellőz" }, "importData": { "message": "Adatok importálása", @@ -2679,6 +2670,9 @@ "confirmFilePassword": { "message": "Fájl jelszó megerősítés" }, + "typePasskey": { + "message": "Hozzáférési kulcs" + }, "passkeyNotCopied": { "message": "A hozzáférési kulcs nem kerül másolásra." }, @@ -2793,5 +2787,35 @@ }, "lastPassYubikeyDesc": { "message": "Illesszük be a LastPass-fiókhoz társított YubiKey eszközt a számítógép USB portjába, majd érintsük meg annak gombját." + }, + "switchAccount": { + "message": "Fiók váltása" + }, + "switchAccounts": { + "message": "Fiókok váltása" + }, + "switchToAccount": { + "message": "Váltás fiókra" + }, + "activeAccount": { + "message": "Aktív fiók" + }, + "accountLimitReached": { + "message": "A fiók korlát elérésre került. Jelentkezzünk ki egy fiókból egy másik hozzáadásához." + }, + "active": { + "message": "aktív" + }, + "locked": { + "message": "zárolt" + }, + "unlocked": { + "message": "feloldott" + }, + "server": { + "message": "szerver" + }, + "hostedAt": { + "message": "tárolva:" } } diff --git a/apps/browser/src/_locales/id/messages.json b/apps/browser/src/_locales/id/messages.json index 0108ec505c4..3b3a39da4f9 100644 --- a/apps/browser/src/_locales/id/messages.json +++ b/apps/browser/src/_locales/id/messages.json @@ -365,6 +365,9 @@ "unlockMethodNeededToChangeTimeoutActionDesc": { "message": "Set up an unlock method to change your vault timeout action." }, + "unlockMethodNeeded": { + "message": "Set up an unlock method in Settings" + }, "rateExtension": { "message": "Nilai Ekstensi" }, @@ -405,6 +408,9 @@ "lockNow": { "message": "Kunci Sekarang" }, + "lockAll": { + "message": "Lock all" + }, "immediately": { "message": "Segera" }, @@ -1027,6 +1033,12 @@ "showAutoFillMenuOnFormFieldsDescAlt": { "message": "Applies to all logged in accounts." }, + "turnOffBrowserBuiltInPasswordManagerSettings": { + "message": "Turn off your browser’s built in password manager settings to avoid conflicts." + }, + "turnOffBrowserBuiltInPasswordManagerSettingsLink": { + "message": "Edit browser settings." + }, "autofillOverlayVisibilityOff": { "message": "Off", "description": "Overlay setting select option for disabling autofill overlay" @@ -1131,6 +1143,9 @@ "faviconDesc": { "message": "Show a recognizable image next to each login." }, + "faviconDescAlt": { + "message": "Show a recognizable image next to each login. Applies to all logged in accounts." + }, "enableBadgeCounter": { "message": "Show badge counter" }, @@ -2538,36 +2553,12 @@ "message": "Bitwarden auto-fill menu available. Press the down arrow key to select.", "description": "Screen reader text for announcing when the overlay opens on the page" }, - "overrideBrowserAutofillTitle": { - "message": "Override browser auto-fill?", - "description": "Dialog title facilitating the ability to override a chrome browser's default autofill behavior" - }, - "overrideBrowserAutofillDescription": { - "message": "Leaving this setting off may cause conflicts between the Bitwarden auto-fill menu and your browser’s.", - "description": "Dialog message facilitating the ability to override a chrome browser's default autofill behavior" - }, - "overrideBrowserAutofillPrivacyRequiredDescription": { - "message": "Leaving this setting off may cause conflicts between the Bitwarden auto-fill menu and your browser’s. Turning this on will restart the Bitwarden extension.", - "description": "Dialog message facilitating the ability to override a chrome browser's default autofill behavior" - }, "turnOn": { "message": "Turn on" }, "ignore": { "message": "Ignore" }, - "overrideBrowserAutoFillSettings": { - "message": "Override browser auto-fill settings", - "description": "Label for the setting that allows overriding the default browser autofill settings" - }, - "extensionPrivacyPermissionNotGrantedTitle": { - "message": "Unable to override browser auto-fill", - "description": "Title for the dialog that appears when the user has not granted the extension permission to set privacy settings" - }, - "extensionPrivacyPermissionNotGrantedDescription": { - "message": "Bitwarden must have access to the extension's privacy permission to override browser auto-fill settings.", - "description": "Description for the dialog that appears when the user has not granted the extension permission to set privacy settings" - }, "importData": { "message": "Import data", "description": "Used for the header of the import dialog, the import button and within the file-password-prompt" @@ -2679,6 +2670,9 @@ "confirmFilePassword": { "message": "Confirm file password" }, + "typePasskey": { + "message": "Passkey" + }, "passkeyNotCopied": { "message": "Passkey will not be copied" }, @@ -2793,5 +2787,35 @@ }, "lastPassYubikeyDesc": { "message": "Insert the YubiKey associated with your LastPass account into your computer's USB port, then touch its button." + }, + "switchAccount": { + "message": "Switch account" + }, + "switchAccounts": { + "message": "Switch accounts" + }, + "switchToAccount": { + "message": "Switch to account" + }, + "activeAccount": { + "message": "Active account" + }, + "accountLimitReached": { + "message": "Account limit reached. Log out of an account to add another." + }, + "active": { + "message": "active" + }, + "locked": { + "message": "locked" + }, + "unlocked": { + "message": "unlocked" + }, + "server": { + "message": "server" + }, + "hostedAt": { + "message": "hosted at" } } diff --git a/apps/browser/src/_locales/it/messages.json b/apps/browser/src/_locales/it/messages.json index 841d8b249fc..f7b7b09dd8b 100644 --- a/apps/browser/src/_locales/it/messages.json +++ b/apps/browser/src/_locales/it/messages.json @@ -365,6 +365,9 @@ "unlockMethodNeededToChangeTimeoutActionDesc": { "message": "Imposta un metodo di sblocco per modificare l'azione timeout cassaforte." }, + "unlockMethodNeeded": { + "message": "Imposta un metodo di sblocco in Impostazioni" + }, "rateExtension": { "message": "Valuta l'estensione" }, @@ -405,6 +408,9 @@ "lockNow": { "message": "Blocca ora" }, + "lockAll": { + "message": "Blocca tutto" + }, "immediately": { "message": "Immediatamente" }, @@ -1027,6 +1033,12 @@ "showAutoFillMenuOnFormFieldsDescAlt": { "message": "Applies to all logged in accounts." }, + "turnOffBrowserBuiltInPasswordManagerSettings": { + "message": "Turn off your browser’s built in password manager settings to avoid conflicts." + }, + "turnOffBrowserBuiltInPasswordManagerSettingsLink": { + "message": "Edit browser settings." + }, "autofillOverlayVisibilityOff": { "message": "No", "description": "Overlay setting select option for disabling autofill overlay" @@ -1131,6 +1143,9 @@ "faviconDesc": { "message": "Mostra un piccolo logo riconoscibile accanto a ogni login." }, + "faviconDescAlt": { + "message": "Mostra un piccolo logo riconoscibile accanto a ogni login. Si applica a tutti gli account connessi." + }, "enableBadgeCounter": { "message": "Mostra badge contatore" }, @@ -2538,36 +2553,12 @@ "message": "Il menu di riempimento automatico di Bitwarden è disponibile. Premi il tasto freccia giù per selezionare.", "description": "Screen reader text for announcing when the overlay opens on the page" }, - "overrideBrowserAutofillTitle": { - "message": "Ignora il riempimento automatico del browser?", - "description": "Dialog title facilitating the ability to override a chrome browser's default autofill behavior" - }, - "overrideBrowserAutofillDescription": { - "message": "Se questa opzione è disattivata potrebbe causare conflitti tra il menu di riempimento automatico di Bitwarden e quello del tuo browser.", - "description": "Dialog message facilitating the ability to override a chrome browser's default autofill behavior" - }, - "overrideBrowserAutofillPrivacyRequiredDescription": { - "message": "Se questa opzione è disattivata potrebbe causare conflitti tra il menu di riempimento automatico di Bitwarden e quello del tuo browser. Attivandola, l'estensione di Bitwarden si riavvierà.", - "description": "Dialog message facilitating the ability to override a chrome browser's default autofill behavior" - }, "turnOn": { "message": "Attiva" }, "ignore": { "message": "Ignora" }, - "overrideBrowserAutoFillSettings": { - "message": "Ignora il riempimento automatico del browser", - "description": "Label for the setting that allows overriding the default browser autofill settings" - }, - "extensionPrivacyPermissionNotGrantedTitle": { - "message": "Impossibile ignorare il riempimento automatico del browser", - "description": "Title for the dialog that appears when the user has not granted the extension permission to set privacy settings" - }, - "extensionPrivacyPermissionNotGrantedDescription": { - "message": "Bitwarden deve avere accesso all'autorizzazione alla privacy dell'estensione per modificare le impostazioni di riempimento automatico del browser.", - "description": "Description for the dialog that appears when the user has not granted the extension permission to set privacy settings" - }, "importData": { "message": "Importa dati", "description": "Used for the header of the import dialog, the import button and within the file-password-prompt" @@ -2679,6 +2670,9 @@ "confirmFilePassword": { "message": "Conferma password del file" }, + "typePasskey": { + "message": "Passkey" + }, "passkeyNotCopied": { "message": "La passkey non sarà copiata" }, @@ -2793,5 +2787,35 @@ }, "lastPassYubikeyDesc": { "message": "Inserisci la tua YubiKey associata al tuo account LastPass nella porta USB del computer, poi premi il suo bottone." + }, + "switchAccount": { + "message": "Cambia account" + }, + "switchAccounts": { + "message": "Cambia account" + }, + "switchToAccount": { + "message": "Passa all'account" + }, + "activeAccount": { + "message": "Account attivo" + }, + "accountLimitReached": { + "message": "Limite di account raggiunto. Esci da un account per aggiungerne un altro." + }, + "active": { + "message": "attivo" + }, + "locked": { + "message": "bloccato" + }, + "unlocked": { + "message": "sbloccato" + }, + "server": { + "message": "server" + }, + "hostedAt": { + "message": "hosted in" } } diff --git a/apps/browser/src/_locales/ja/messages.json b/apps/browser/src/_locales/ja/messages.json index 0572e2b6462..400ee49b33c 100644 --- a/apps/browser/src/_locales/ja/messages.json +++ b/apps/browser/src/_locales/ja/messages.json @@ -365,6 +365,9 @@ "unlockMethodNeededToChangeTimeoutActionDesc": { "message": "保管庫のタイムアウト動作を変更するには、ロック解除方法を設定してください。" }, + "unlockMethodNeeded": { + "message": "設定でロック解除方法をセットアップ" + }, "rateExtension": { "message": "拡張機能の評価" }, @@ -405,6 +408,9 @@ "lockNow": { "message": "今すぐロック" }, + "lockAll": { + "message": "すべてロック" + }, "immediately": { "message": "すぐに" }, @@ -1027,6 +1033,12 @@ "showAutoFillMenuOnFormFieldsDescAlt": { "message": "ログインしているすべてのアカウントに適用されます。" }, + "turnOffBrowserBuiltInPasswordManagerSettings": { + "message": "競合を避けるために、ブラウザーのパスワード管理設定をオフにしてください。" + }, + "turnOffBrowserBuiltInPasswordManagerSettingsLink": { + "message": "ブラウザーの設定を編集してください。" + }, "autofillOverlayVisibilityOff": { "message": "オフ", "description": "Overlay setting select option for disabling autofill overlay" @@ -1131,6 +1143,9 @@ "faviconDesc": { "message": "ログイン情報の隣にアイコン画像を表示します" }, + "faviconDescAlt": { + "message": "各ログインの横に認識可能な画像を表示します。すべてのログイン済みアカウントに適用されます。" + }, "enableBadgeCounter": { "message": "バッジカウンターを表示" }, @@ -2538,36 +2553,12 @@ "message": "Bitwarden 自動入力メニューがあります。下矢印キーを押すと選択できます。", "description": "Screen reader text for announcing when the overlay opens on the page" }, - "overrideBrowserAutofillTitle": { - "message": "ブラウザの自動入力を上書きしますか?", - "description": "Dialog title facilitating the ability to override a chrome browser's default autofill behavior" - }, - "overrideBrowserAutofillDescription": { - "message": "この設定をオフにすると、Bitwarden 自動入力メニューとブラウザの間で競合が発生する可能性があります。", - "description": "Dialog message facilitating the ability to override a chrome browser's default autofill behavior" - }, - "overrideBrowserAutofillPrivacyRequiredDescription": { - "message": "この設定をオフにすると、Bitwarden 自動入力メニューとブラウザの間で競合が発生する可能性があります。これをオンにすると、 Bitwarden 拡張機能は再起動します。", - "description": "Dialog message facilitating the ability to override a chrome browser's default autofill behavior" - }, "turnOn": { "message": "オンにする" }, "ignore": { "message": "無視" }, - "overrideBrowserAutoFillSettings": { - "message": "ブラウザの自動入力設定を上書きする", - "description": "Label for the setting that allows overriding the default browser autofill settings" - }, - "extensionPrivacyPermissionNotGrantedTitle": { - "message": "ブラウザの自動入力を上書きできません", - "description": "Title for the dialog that appears when the user has not granted the extension permission to set privacy settings" - }, - "extensionPrivacyPermissionNotGrantedDescription": { - "message": "Bitwarden はブラウザの自動入力設定を上書きするために、拡張機能のプライバシー権限にアクセスする必要があります。", - "description": "Description for the dialog that appears when the user has not granted the extension permission to set privacy settings" - }, "importData": { "message": "データのインポート", "description": "Used for the header of the import dialog, the import button and within the file-password-prompt" @@ -2679,6 +2670,9 @@ "confirmFilePassword": { "message": "ファイルパスワードの確認" }, + "typePasskey": { + "message": "パスキー" + }, "passkeyNotCopied": { "message": "パスキーはコピーされません" }, @@ -2793,5 +2787,35 @@ }, "lastPassYubikeyDesc": { "message": "LastPass アカウントに関連付けられた YubiKey を USB ポートに挿入し、ボタンをタッチしてください。" + }, + "switchAccount": { + "message": "アカウントの切り替え" + }, + "switchAccounts": { + "message": "アカウントの切り替え" + }, + "switchToAccount": { + "message": "アカウントに切り替え" + }, + "activeAccount": { + "message": "アクティブなアカウント" + }, + "accountLimitReached": { + "message": "アカウントの制限に達しました。別のアカウントを追加するにはまずログアウトしてください。" + }, + "active": { + "message": "アクティブ" + }, + "locked": { + "message": "ロック中" + }, + "unlocked": { + "message": "ロック解除済み" + }, + "server": { + "message": "サーバー" + }, + "hostedAt": { + "message": "ホスト" } } diff --git a/apps/browser/src/_locales/ka/messages.json b/apps/browser/src/_locales/ka/messages.json index 1f5e2d81400..edd30fd436e 100644 --- a/apps/browser/src/_locales/ka/messages.json +++ b/apps/browser/src/_locales/ka/messages.json @@ -365,6 +365,9 @@ "unlockMethodNeededToChangeTimeoutActionDesc": { "message": "Set up an unlock method to change your vault timeout action." }, + "unlockMethodNeeded": { + "message": "Set up an unlock method in Settings" + }, "rateExtension": { "message": "Rate the extension" }, @@ -405,6 +408,9 @@ "lockNow": { "message": "Lock now" }, + "lockAll": { + "message": "Lock all" + }, "immediately": { "message": "დაუყონებლივ" }, @@ -1027,6 +1033,12 @@ "showAutoFillMenuOnFormFieldsDescAlt": { "message": "Applies to all logged in accounts." }, + "turnOffBrowserBuiltInPasswordManagerSettings": { + "message": "Turn off your browser’s built in password manager settings to avoid conflicts." + }, + "turnOffBrowserBuiltInPasswordManagerSettingsLink": { + "message": "Edit browser settings." + }, "autofillOverlayVisibilityOff": { "message": "Off", "description": "Overlay setting select option for disabling autofill overlay" @@ -1131,6 +1143,9 @@ "faviconDesc": { "message": "Show a recognizable image next to each login." }, + "faviconDescAlt": { + "message": "Show a recognizable image next to each login. Applies to all logged in accounts." + }, "enableBadgeCounter": { "message": "Show badge counter" }, @@ -2538,36 +2553,12 @@ "message": "Bitwarden auto-fill menu available. Press the down arrow key to select.", "description": "Screen reader text for announcing when the overlay opens on the page" }, - "overrideBrowserAutofillTitle": { - "message": "Override browser auto-fill?", - "description": "Dialog title facilitating the ability to override a chrome browser's default autofill behavior" - }, - "overrideBrowserAutofillDescription": { - "message": "Leaving this setting off may cause conflicts between the Bitwarden auto-fill menu and your browser’s.", - "description": "Dialog message facilitating the ability to override a chrome browser's default autofill behavior" - }, - "overrideBrowserAutofillPrivacyRequiredDescription": { - "message": "Leaving this setting off may cause conflicts between the Bitwarden auto-fill menu and your browser’s. Turning this on will restart the Bitwarden extension.", - "description": "Dialog message facilitating the ability to override a chrome browser's default autofill behavior" - }, "turnOn": { "message": "Turn on" }, "ignore": { "message": "Ignore" }, - "overrideBrowserAutoFillSettings": { - "message": "Override browser auto-fill settings", - "description": "Label for the setting that allows overriding the default browser autofill settings" - }, - "extensionPrivacyPermissionNotGrantedTitle": { - "message": "Unable to override browser auto-fill", - "description": "Title for the dialog that appears when the user has not granted the extension permission to set privacy settings" - }, - "extensionPrivacyPermissionNotGrantedDescription": { - "message": "Bitwarden must have access to the extension's privacy permission to override browser auto-fill settings.", - "description": "Description for the dialog that appears when the user has not granted the extension permission to set privacy settings" - }, "importData": { "message": "Import data", "description": "Used for the header of the import dialog, the import button and within the file-password-prompt" @@ -2679,6 +2670,9 @@ "confirmFilePassword": { "message": "Confirm file password" }, + "typePasskey": { + "message": "Passkey" + }, "passkeyNotCopied": { "message": "Passkey will not be copied" }, @@ -2793,5 +2787,35 @@ }, "lastPassYubikeyDesc": { "message": "Insert the YubiKey associated with your LastPass account into your computer's USB port, then touch its button." + }, + "switchAccount": { + "message": "Switch account" + }, + "switchAccounts": { + "message": "Switch accounts" + }, + "switchToAccount": { + "message": "Switch to account" + }, + "activeAccount": { + "message": "Active account" + }, + "accountLimitReached": { + "message": "Account limit reached. Log out of an account to add another." + }, + "active": { + "message": "active" + }, + "locked": { + "message": "locked" + }, + "unlocked": { + "message": "unlocked" + }, + "server": { + "message": "server" + }, + "hostedAt": { + "message": "hosted at" } } diff --git a/apps/browser/src/_locales/km/messages.json b/apps/browser/src/_locales/km/messages.json index 5f243afe040..ab3933bf5b7 100644 --- a/apps/browser/src/_locales/km/messages.json +++ b/apps/browser/src/_locales/km/messages.json @@ -365,6 +365,9 @@ "unlockMethodNeededToChangeTimeoutActionDesc": { "message": "Set up an unlock method to change your vault timeout action." }, + "unlockMethodNeeded": { + "message": "Set up an unlock method in Settings" + }, "rateExtension": { "message": "Rate the extension" }, @@ -405,6 +408,9 @@ "lockNow": { "message": "Lock now" }, + "lockAll": { + "message": "Lock all" + }, "immediately": { "message": "Immediately" }, @@ -1027,6 +1033,12 @@ "showAutoFillMenuOnFormFieldsDescAlt": { "message": "Applies to all logged in accounts." }, + "turnOffBrowserBuiltInPasswordManagerSettings": { + "message": "Turn off your browser’s built in password manager settings to avoid conflicts." + }, + "turnOffBrowserBuiltInPasswordManagerSettingsLink": { + "message": "Edit browser settings." + }, "autofillOverlayVisibilityOff": { "message": "Off", "description": "Overlay setting select option for disabling autofill overlay" @@ -1131,6 +1143,9 @@ "faviconDesc": { "message": "Show a recognizable image next to each login." }, + "faviconDescAlt": { + "message": "Show a recognizable image next to each login. Applies to all logged in accounts." + }, "enableBadgeCounter": { "message": "Show badge counter" }, @@ -2538,36 +2553,12 @@ "message": "Bitwarden auto-fill menu available. Press the down arrow key to select.", "description": "Screen reader text for announcing when the overlay opens on the page" }, - "overrideBrowserAutofillTitle": { - "message": "Override browser auto-fill?", - "description": "Dialog title facilitating the ability to override a chrome browser's default autofill behavior" - }, - "overrideBrowserAutofillDescription": { - "message": "Leaving this setting off may cause conflicts between the Bitwarden auto-fill menu and your browser’s.", - "description": "Dialog message facilitating the ability to override a chrome browser's default autofill behavior" - }, - "overrideBrowserAutofillPrivacyRequiredDescription": { - "message": "Leaving this setting off may cause conflicts between the Bitwarden auto-fill menu and your browser’s. Turning this on will restart the Bitwarden extension.", - "description": "Dialog message facilitating the ability to override a chrome browser's default autofill behavior" - }, "turnOn": { "message": "Turn on" }, "ignore": { "message": "Ignore" }, - "overrideBrowserAutoFillSettings": { - "message": "Override browser auto-fill settings", - "description": "Label for the setting that allows overriding the default browser autofill settings" - }, - "extensionPrivacyPermissionNotGrantedTitle": { - "message": "Unable to override browser auto-fill", - "description": "Title for the dialog that appears when the user has not granted the extension permission to set privacy settings" - }, - "extensionPrivacyPermissionNotGrantedDescription": { - "message": "Bitwarden must have access to the extension's privacy permission to override browser auto-fill settings.", - "description": "Description for the dialog that appears when the user has not granted the extension permission to set privacy settings" - }, "importData": { "message": "Import data", "description": "Used for the header of the import dialog, the import button and within the file-password-prompt" @@ -2679,6 +2670,9 @@ "confirmFilePassword": { "message": "Confirm file password" }, + "typePasskey": { + "message": "Passkey" + }, "passkeyNotCopied": { "message": "Passkey will not be copied" }, @@ -2793,5 +2787,35 @@ }, "lastPassYubikeyDesc": { "message": "Insert the YubiKey associated with your LastPass account into your computer's USB port, then touch its button." + }, + "switchAccount": { + "message": "Switch account" + }, + "switchAccounts": { + "message": "Switch accounts" + }, + "switchToAccount": { + "message": "Switch to account" + }, + "activeAccount": { + "message": "Active account" + }, + "accountLimitReached": { + "message": "Account limit reached. Log out of an account to add another." + }, + "active": { + "message": "active" + }, + "locked": { + "message": "locked" + }, + "unlocked": { + "message": "unlocked" + }, + "server": { + "message": "server" + }, + "hostedAt": { + "message": "hosted at" } } diff --git a/apps/browser/src/_locales/kn/messages.json b/apps/browser/src/_locales/kn/messages.json index a743076b158..289cc9c36d8 100644 --- a/apps/browser/src/_locales/kn/messages.json +++ b/apps/browser/src/_locales/kn/messages.json @@ -365,6 +365,9 @@ "unlockMethodNeededToChangeTimeoutActionDesc": { "message": "Set up an unlock method to change your vault timeout action." }, + "unlockMethodNeeded": { + "message": "Set up an unlock method in Settings" + }, "rateExtension": { "message": "ವಿಸ್ತರಣೆಯನ್ನು ರೇಟ್ ಮಾಡಿ" }, @@ -405,6 +408,9 @@ "lockNow": { "message": "ಈಗ ಲಾಕ್ ಮಾಡಿ" }, + "lockAll": { + "message": "Lock all" + }, "immediately": { "message": "ತಕ್ಷಣ" }, @@ -1027,6 +1033,12 @@ "showAutoFillMenuOnFormFieldsDescAlt": { "message": "Applies to all logged in accounts." }, + "turnOffBrowserBuiltInPasswordManagerSettings": { + "message": "Turn off your browser’s built in password manager settings to avoid conflicts." + }, + "turnOffBrowserBuiltInPasswordManagerSettingsLink": { + "message": "Edit browser settings." + }, "autofillOverlayVisibilityOff": { "message": "Off", "description": "Overlay setting select option for disabling autofill overlay" @@ -1131,6 +1143,9 @@ "faviconDesc": { "message": "Show a recognizable image next to each login." }, + "faviconDescAlt": { + "message": "Show a recognizable image next to each login. Applies to all logged in accounts." + }, "enableBadgeCounter": { "message": "Show badge counter" }, @@ -2538,36 +2553,12 @@ "message": "Bitwarden auto-fill menu available. Press the down arrow key to select.", "description": "Screen reader text for announcing when the overlay opens on the page" }, - "overrideBrowserAutofillTitle": { - "message": "Override browser auto-fill?", - "description": "Dialog title facilitating the ability to override a chrome browser's default autofill behavior" - }, - "overrideBrowserAutofillDescription": { - "message": "Leaving this setting off may cause conflicts between the Bitwarden auto-fill menu and your browser’s.", - "description": "Dialog message facilitating the ability to override a chrome browser's default autofill behavior" - }, - "overrideBrowserAutofillPrivacyRequiredDescription": { - "message": "Leaving this setting off may cause conflicts between the Bitwarden auto-fill menu and your browser’s. Turning this on will restart the Bitwarden extension.", - "description": "Dialog message facilitating the ability to override a chrome browser's default autofill behavior" - }, "turnOn": { "message": "Turn on" }, "ignore": { "message": "Ignore" }, - "overrideBrowserAutoFillSettings": { - "message": "Override browser auto-fill settings", - "description": "Label for the setting that allows overriding the default browser autofill settings" - }, - "extensionPrivacyPermissionNotGrantedTitle": { - "message": "Unable to override browser auto-fill", - "description": "Title for the dialog that appears when the user has not granted the extension permission to set privacy settings" - }, - "extensionPrivacyPermissionNotGrantedDescription": { - "message": "Bitwarden must have access to the extension's privacy permission to override browser auto-fill settings.", - "description": "Description for the dialog that appears when the user has not granted the extension permission to set privacy settings" - }, "importData": { "message": "Import data", "description": "Used for the header of the import dialog, the import button and within the file-password-prompt" @@ -2679,6 +2670,9 @@ "confirmFilePassword": { "message": "Confirm file password" }, + "typePasskey": { + "message": "Passkey" + }, "passkeyNotCopied": { "message": "Passkey will not be copied" }, @@ -2793,5 +2787,35 @@ }, "lastPassYubikeyDesc": { "message": "Insert the YubiKey associated with your LastPass account into your computer's USB port, then touch its button." + }, + "switchAccount": { + "message": "Switch account" + }, + "switchAccounts": { + "message": "Switch accounts" + }, + "switchToAccount": { + "message": "Switch to account" + }, + "activeAccount": { + "message": "Active account" + }, + "accountLimitReached": { + "message": "Account limit reached. Log out of an account to add another." + }, + "active": { + "message": "active" + }, + "locked": { + "message": "locked" + }, + "unlocked": { + "message": "unlocked" + }, + "server": { + "message": "server" + }, + "hostedAt": { + "message": "hosted at" } } diff --git a/apps/browser/src/_locales/ko/messages.json b/apps/browser/src/_locales/ko/messages.json index 4837698c64c..309ed96aff0 100644 --- a/apps/browser/src/_locales/ko/messages.json +++ b/apps/browser/src/_locales/ko/messages.json @@ -365,6 +365,9 @@ "unlockMethodNeededToChangeTimeoutActionDesc": { "message": "Set up an unlock method to change your vault timeout action." }, + "unlockMethodNeeded": { + "message": "Set up an unlock method in Settings" + }, "rateExtension": { "message": "확장 프로그램 평가" }, @@ -405,6 +408,9 @@ "lockNow": { "message": "지금 잠그기" }, + "lockAll": { + "message": "Lock all" + }, "immediately": { "message": "즉시" }, @@ -445,7 +451,7 @@ "message": "브라우저 다시 시작 시" }, "never": { - "message": "잠그지 않음" + "message": "안함" }, "security": { "message": "보안" @@ -1027,6 +1033,12 @@ "showAutoFillMenuOnFormFieldsDescAlt": { "message": "Applies to all logged in accounts." }, + "turnOffBrowserBuiltInPasswordManagerSettings": { + "message": "Turn off your browser’s built in password manager settings to avoid conflicts." + }, + "turnOffBrowserBuiltInPasswordManagerSettingsLink": { + "message": "Edit browser settings." + }, "autofillOverlayVisibilityOff": { "message": "Off", "description": "Overlay setting select option for disabling autofill overlay" @@ -1131,6 +1143,9 @@ "faviconDesc": { "message": "Show a recognizable image next to each login." }, + "faviconDescAlt": { + "message": "Show a recognizable image next to each login. Applies to all logged in accounts." + }, "enableBadgeCounter": { "message": "Show badge counter" }, @@ -2538,36 +2553,12 @@ "message": "Bitwarden auto-fill menu available. Press the down arrow key to select.", "description": "Screen reader text for announcing when the overlay opens on the page" }, - "overrideBrowserAutofillTitle": { - "message": "Override browser auto-fill?", - "description": "Dialog title facilitating the ability to override a chrome browser's default autofill behavior" - }, - "overrideBrowserAutofillDescription": { - "message": "Leaving this setting off may cause conflicts between the Bitwarden auto-fill menu and your browser’s.", - "description": "Dialog message facilitating the ability to override a chrome browser's default autofill behavior" - }, - "overrideBrowserAutofillPrivacyRequiredDescription": { - "message": "Leaving this setting off may cause conflicts between the Bitwarden auto-fill menu and your browser’s. Turning this on will restart the Bitwarden extension.", - "description": "Dialog message facilitating the ability to override a chrome browser's default autofill behavior" - }, "turnOn": { "message": "Turn on" }, "ignore": { "message": "Ignore" }, - "overrideBrowserAutoFillSettings": { - "message": "Override browser auto-fill settings", - "description": "Label for the setting that allows overriding the default browser autofill settings" - }, - "extensionPrivacyPermissionNotGrantedTitle": { - "message": "Unable to override browser auto-fill", - "description": "Title for the dialog that appears when the user has not granted the extension permission to set privacy settings" - }, - "extensionPrivacyPermissionNotGrantedDescription": { - "message": "Bitwarden must have access to the extension's privacy permission to override browser auto-fill settings.", - "description": "Description for the dialog that appears when the user has not granted the extension permission to set privacy settings" - }, "importData": { "message": "Import data", "description": "Used for the header of the import dialog, the import button and within the file-password-prompt" @@ -2679,6 +2670,9 @@ "confirmFilePassword": { "message": "Confirm file password" }, + "typePasskey": { + "message": "Passkey" + }, "passkeyNotCopied": { "message": "Passkey will not be copied" }, @@ -2793,5 +2787,35 @@ }, "lastPassYubikeyDesc": { "message": "Insert the YubiKey associated with your LastPass account into your computer's USB port, then touch its button." + }, + "switchAccount": { + "message": "Switch account" + }, + "switchAccounts": { + "message": "Switch accounts" + }, + "switchToAccount": { + "message": "Switch to account" + }, + "activeAccount": { + "message": "Active account" + }, + "accountLimitReached": { + "message": "Account limit reached. Log out of an account to add another." + }, + "active": { + "message": "active" + }, + "locked": { + "message": "locked" + }, + "unlocked": { + "message": "unlocked" + }, + "server": { + "message": "server" + }, + "hostedAt": { + "message": "hosted at" } } diff --git a/apps/browser/src/_locales/lt/messages.json b/apps/browser/src/_locales/lt/messages.json index 2b3e5c13ebd..11b6b88a245 100644 --- a/apps/browser/src/_locales/lt/messages.json +++ b/apps/browser/src/_locales/lt/messages.json @@ -365,6 +365,9 @@ "unlockMethodNeededToChangeTimeoutActionDesc": { "message": "Nustatyk atrakinimo būdą, kad pakeistum saugyklos laiko limito veiksmą." }, + "unlockMethodNeeded": { + "message": "Set up an unlock method in Settings" + }, "rateExtension": { "message": "Įvertinkite šį plėtinį" }, @@ -405,6 +408,9 @@ "lockNow": { "message": "Užrakinti dabar" }, + "lockAll": { + "message": "Lock all" + }, "immediately": { "message": "Nedelsiant" }, @@ -1027,6 +1033,12 @@ "showAutoFillMenuOnFormFieldsDescAlt": { "message": "Applies to all logged in accounts." }, + "turnOffBrowserBuiltInPasswordManagerSettings": { + "message": "Turn off your browser’s built in password manager settings to avoid conflicts." + }, + "turnOffBrowserBuiltInPasswordManagerSettingsLink": { + "message": "Edit browser settings." + }, "autofillOverlayVisibilityOff": { "message": "Off", "description": "Overlay setting select option for disabling autofill overlay" @@ -1131,6 +1143,9 @@ "faviconDesc": { "message": "Show a recognizable image next to each login." }, + "faviconDescAlt": { + "message": "Show a recognizable image next to each login. Applies to all logged in accounts." + }, "enableBadgeCounter": { "message": "Rodyti ženkliukų skaitiklį" }, @@ -2538,36 +2553,12 @@ "message": "Bitwarden auto-fill menu available. Press the down arrow key to select.", "description": "Screen reader text for announcing when the overlay opens on the page" }, - "overrideBrowserAutofillTitle": { - "message": "Override browser auto-fill?", - "description": "Dialog title facilitating the ability to override a chrome browser's default autofill behavior" - }, - "overrideBrowserAutofillDescription": { - "message": "Leaving this setting off may cause conflicts between the Bitwarden auto-fill menu and your browser’s.", - "description": "Dialog message facilitating the ability to override a chrome browser's default autofill behavior" - }, - "overrideBrowserAutofillPrivacyRequiredDescription": { - "message": "Leaving this setting off may cause conflicts between the Bitwarden auto-fill menu and your browser’s. Turning this on will restart the Bitwarden extension.", - "description": "Dialog message facilitating the ability to override a chrome browser's default autofill behavior" - }, "turnOn": { "message": "Turn on" }, "ignore": { "message": "Ignore" }, - "overrideBrowserAutoFillSettings": { - "message": "Override browser auto-fill settings", - "description": "Label for the setting that allows overriding the default browser autofill settings" - }, - "extensionPrivacyPermissionNotGrantedTitle": { - "message": "Unable to override browser auto-fill", - "description": "Title for the dialog that appears when the user has not granted the extension permission to set privacy settings" - }, - "extensionPrivacyPermissionNotGrantedDescription": { - "message": "Bitwarden must have access to the extension's privacy permission to override browser auto-fill settings.", - "description": "Description for the dialog that appears when the user has not granted the extension permission to set privacy settings" - }, "importData": { "message": "Import data", "description": "Used for the header of the import dialog, the import button and within the file-password-prompt" @@ -2679,6 +2670,9 @@ "confirmFilePassword": { "message": "Confirm file password" }, + "typePasskey": { + "message": "Prieigos raktas" + }, "passkeyNotCopied": { "message": "Passkey will not be copied" }, @@ -2793,5 +2787,35 @@ }, "lastPassYubikeyDesc": { "message": "Su LastPass paskyra susietą YubiKey įkišk į kompiuterio USB jungtį, tada paliesk jo mygtuką." + }, + "switchAccount": { + "message": "Switch account" + }, + "switchAccounts": { + "message": "Switch accounts" + }, + "switchToAccount": { + "message": "Switch to account" + }, + "activeAccount": { + "message": "Active account" + }, + "accountLimitReached": { + "message": "Account limit reached. Log out of an account to add another." + }, + "active": { + "message": "active" + }, + "locked": { + "message": "locked" + }, + "unlocked": { + "message": "unlocked" + }, + "server": { + "message": "server" + }, + "hostedAt": { + "message": "hosted at" } } diff --git a/apps/browser/src/_locales/lv/messages.json b/apps/browser/src/_locales/lv/messages.json index 21163a395ad..a8cdcae5c4a 100644 --- a/apps/browser/src/_locales/lv/messages.json +++ b/apps/browser/src/_locales/lv/messages.json @@ -365,6 +365,9 @@ "unlockMethodNeededToChangeTimeoutActionDesc": { "message": "Jāuzstāda atslēgšanas veids, lai mainītu glabātavas noildzes darbību." }, + "unlockMethodNeeded": { + "message": "Jāuzstāda atslēgšanas veids iestatījumos" + }, "rateExtension": { "message": "Novērtēt paplašinājumu" }, @@ -405,6 +408,9 @@ "lockNow": { "message": "Aizslēgt" }, + "lockAll": { + "message": "Aizslēgt visu" + }, "immediately": { "message": "Nekavējoties" }, @@ -1027,6 +1033,12 @@ "showAutoFillMenuOnFormFieldsDescAlt": { "message": "Attiecas uz visiem pieslēgtajiem kontiem." }, + "turnOffBrowserBuiltInPasswordManagerSettings": { + "message": "Jāizslēdz sava pārlūka iebūvētā paroļu pārvaldnieka iestatījumi, lai izvairītos no nesaderībām." + }, + "turnOffBrowserBuiltInPasswordManagerSettingsLink": { + "message": "Mainīt pārlūka iestatījumus." + }, "autofillOverlayVisibilityOff": { "message": "Izslēgts", "description": "Overlay setting select option for disabling autofill overlay" @@ -1131,6 +1143,9 @@ "faviconDesc": { "message": "Attēlot atpazīstamu attēlu pie katra pieteikšanās vienuma." }, + "faviconDescAlt": { + "message": "Parādīt atpazīstamu attēlu pie katra pieteikšanās vienuma. Attiecas uz visiem kontiem, kuros ir notikusi pieteikšanās." + }, "enableBadgeCounter": { "message": "Rādīt skaita nozīmīti" }, @@ -2538,36 +2553,12 @@ "message": "Ir pieejama Bitwarden automātiskās aizpildes izvēlne. Jānospiež poga ar bultu uz leju, lai atlasītu.", "description": "Screen reader text for announcing when the overlay opens on the page" }, - "overrideBrowserAutofillTitle": { - "message": "Aizvietot pārlūka automātisko aizpildi?", - "description": "Dialog title facilitating the ability to override a chrome browser's default autofill behavior" - }, - "overrideBrowserAutofillDescription": { - "message": "Šī iestatījuma neieslēgšana var radīt nesaderības starp Bitwarden automātiskās aizpildes izvēlni un pārlūku.", - "description": "Dialog message facilitating the ability to override a chrome browser's default autofill behavior" - }, - "overrideBrowserAutofillPrivacyRequiredDescription": { - "message": "Šī iestatījuma neieslēgšana var radīt nesaderības starp Bitwarden automātiskās aizpildes izvēlni un pārlūku. Ieslēgšana pārsāknēs Bitwarden paplašinājumu.", - "description": "Dialog message facilitating the ability to override a chrome browser's default autofill behavior" - }, "turnOn": { "message": "Ieslēgt" }, "ignore": { "message": "Neņemt vērā" }, - "overrideBrowserAutoFillSettings": { - "message": "Aizvietot pārlūka automātiskās aizpildes iestatījumus", - "description": "Label for the setting that allows overriding the default browser autofill settings" - }, - "extensionPrivacyPermissionNotGrantedTitle": { - "message": "Nebija iespējams aizvietot pārlūka automātisko aizpildi", - "description": "Title for the dialog that appears when the user has not granted the extension permission to set privacy settings" - }, - "extensionPrivacyPermissionNotGrantedDescription": { - "message": "Bitwarden ir jābūt piekļuvei paplašinājuma privātuma atļaujai, lai aizvietotu pārlūka automātiskās aizpildes iestatījumus.", - "description": "Description for the dialog that appears when the user has not granted the extension permission to set privacy settings" - }, "importData": { "message": "Ievietot datus", "description": "Used for the header of the import dialog, the import button and within the file-password-prompt" @@ -2679,6 +2670,9 @@ "confirmFilePassword": { "message": "Apstiprināt datnes paroli" }, + "typePasskey": { + "message": "Passkey" + }, "passkeyNotCopied": { "message": "Piekļuves atslēga netiks ievietota starpliktuvē" }, @@ -2793,5 +2787,35 @@ }, "lastPassYubikeyDesc": { "message": "Jāievieto ar LastPass kontu sasaistītā YubiKey datora USB ligzdā, tad jāpieskaras tās pogai." + }, + "switchAccount": { + "message": "Pārslēgties starp kontiem" + }, + "switchAccounts": { + "message": "Pārslēgties starp kontiem" + }, + "switchToAccount": { + "message": "Pārslēgties uz kontu" + }, + "activeAccount": { + "message": "Pašlaik izmantotais konts" + }, + "accountLimitReached": { + "message": "Sasniegti konta ierobežojumi. Jāizrakstās no konta, lai pievienotu citu." + }, + "active": { + "message": "izmantots" + }, + "locked": { + "message": "aizslēgts" + }, + "unlocked": { + "message": "atslēgta" + }, + "server": { + "message": "serveris" + }, + "hostedAt": { + "message": "izvietots" } } diff --git a/apps/browser/src/_locales/ml/messages.json b/apps/browser/src/_locales/ml/messages.json index 129952598d8..466c6d519d6 100644 --- a/apps/browser/src/_locales/ml/messages.json +++ b/apps/browser/src/_locales/ml/messages.json @@ -365,6 +365,9 @@ "unlockMethodNeededToChangeTimeoutActionDesc": { "message": "Set up an unlock method to change your vault timeout action." }, + "unlockMethodNeeded": { + "message": "Set up an unlock method in Settings" + }, "rateExtension": { "message": "എക്സ്റ്റൻഷൻ റേറ്റ് ചെയ്യുക " }, @@ -405,6 +408,9 @@ "lockNow": { "message": "ഇപ്പോൾ ലോക്കുചെയ്യുക" }, + "lockAll": { + "message": "Lock all" + }, "immediately": { "message": "ഉടന്‍തന്നെ" }, @@ -1027,6 +1033,12 @@ "showAutoFillMenuOnFormFieldsDescAlt": { "message": "Applies to all logged in accounts." }, + "turnOffBrowserBuiltInPasswordManagerSettings": { + "message": "Turn off your browser’s built in password manager settings to avoid conflicts." + }, + "turnOffBrowserBuiltInPasswordManagerSettingsLink": { + "message": "Edit browser settings." + }, "autofillOverlayVisibilityOff": { "message": "Off", "description": "Overlay setting select option for disabling autofill overlay" @@ -1131,6 +1143,9 @@ "faviconDesc": { "message": "Show a recognizable image next to each login." }, + "faviconDescAlt": { + "message": "Show a recognizable image next to each login. Applies to all logged in accounts." + }, "enableBadgeCounter": { "message": "Show badge counter" }, @@ -2538,36 +2553,12 @@ "message": "Bitwarden auto-fill menu available. Press the down arrow key to select.", "description": "Screen reader text for announcing when the overlay opens on the page" }, - "overrideBrowserAutofillTitle": { - "message": "Override browser auto-fill?", - "description": "Dialog title facilitating the ability to override a chrome browser's default autofill behavior" - }, - "overrideBrowserAutofillDescription": { - "message": "Leaving this setting off may cause conflicts between the Bitwarden auto-fill menu and your browser’s.", - "description": "Dialog message facilitating the ability to override a chrome browser's default autofill behavior" - }, - "overrideBrowserAutofillPrivacyRequiredDescription": { - "message": "Leaving this setting off may cause conflicts between the Bitwarden auto-fill menu and your browser’s. Turning this on will restart the Bitwarden extension.", - "description": "Dialog message facilitating the ability to override a chrome browser's default autofill behavior" - }, "turnOn": { "message": "Turn on" }, "ignore": { "message": "Ignore" }, - "overrideBrowserAutoFillSettings": { - "message": "Override browser auto-fill settings", - "description": "Label for the setting that allows overriding the default browser autofill settings" - }, - "extensionPrivacyPermissionNotGrantedTitle": { - "message": "Unable to override browser auto-fill", - "description": "Title for the dialog that appears when the user has not granted the extension permission to set privacy settings" - }, - "extensionPrivacyPermissionNotGrantedDescription": { - "message": "Bitwarden must have access to the extension's privacy permission to override browser auto-fill settings.", - "description": "Description for the dialog that appears when the user has not granted the extension permission to set privacy settings" - }, "importData": { "message": "Import data", "description": "Used for the header of the import dialog, the import button and within the file-password-prompt" @@ -2679,6 +2670,9 @@ "confirmFilePassword": { "message": "Confirm file password" }, + "typePasskey": { + "message": "Passkey" + }, "passkeyNotCopied": { "message": "Passkey will not be copied" }, @@ -2793,5 +2787,35 @@ }, "lastPassYubikeyDesc": { "message": "Insert the YubiKey associated with your LastPass account into your computer's USB port, then touch its button." + }, + "switchAccount": { + "message": "Switch account" + }, + "switchAccounts": { + "message": "Switch accounts" + }, + "switchToAccount": { + "message": "Switch to account" + }, + "activeAccount": { + "message": "Active account" + }, + "accountLimitReached": { + "message": "Account limit reached. Log out of an account to add another." + }, + "active": { + "message": "active" + }, + "locked": { + "message": "locked" + }, + "unlocked": { + "message": "unlocked" + }, + "server": { + "message": "server" + }, + "hostedAt": { + "message": "hosted at" } } diff --git a/apps/browser/src/_locales/mr/messages.json b/apps/browser/src/_locales/mr/messages.json index 54908aec4cf..c3747416e90 100644 --- a/apps/browser/src/_locales/mr/messages.json +++ b/apps/browser/src/_locales/mr/messages.json @@ -365,6 +365,9 @@ "unlockMethodNeededToChangeTimeoutActionDesc": { "message": "Set up an unlock method to change your vault timeout action." }, + "unlockMethodNeeded": { + "message": "Set up an unlock method in Settings" + }, "rateExtension": { "message": "विस्तारकाचे मूल्यांकन करा" }, @@ -405,6 +408,9 @@ "lockNow": { "message": "Lock now" }, + "lockAll": { + "message": "Lock all" + }, "immediately": { "message": "Immediately" }, @@ -1027,6 +1033,12 @@ "showAutoFillMenuOnFormFieldsDescAlt": { "message": "Applies to all logged in accounts." }, + "turnOffBrowserBuiltInPasswordManagerSettings": { + "message": "Turn off your browser’s built in password manager settings to avoid conflicts." + }, + "turnOffBrowserBuiltInPasswordManagerSettingsLink": { + "message": "Edit browser settings." + }, "autofillOverlayVisibilityOff": { "message": "Off", "description": "Overlay setting select option for disabling autofill overlay" @@ -1131,6 +1143,9 @@ "faviconDesc": { "message": "Show a recognizable image next to each login." }, + "faviconDescAlt": { + "message": "Show a recognizable image next to each login. Applies to all logged in accounts." + }, "enableBadgeCounter": { "message": "Show badge counter" }, @@ -2538,36 +2553,12 @@ "message": "Bitwarden auto-fill menu available. Press the down arrow key to select.", "description": "Screen reader text for announcing when the overlay opens on the page" }, - "overrideBrowserAutofillTitle": { - "message": "Override browser auto-fill?", - "description": "Dialog title facilitating the ability to override a chrome browser's default autofill behavior" - }, - "overrideBrowserAutofillDescription": { - "message": "Leaving this setting off may cause conflicts between the Bitwarden auto-fill menu and your browser’s.", - "description": "Dialog message facilitating the ability to override a chrome browser's default autofill behavior" - }, - "overrideBrowserAutofillPrivacyRequiredDescription": { - "message": "Leaving this setting off may cause conflicts between the Bitwarden auto-fill menu and your browser’s. Turning this on will restart the Bitwarden extension.", - "description": "Dialog message facilitating the ability to override a chrome browser's default autofill behavior" - }, "turnOn": { "message": "Turn on" }, "ignore": { "message": "Ignore" }, - "overrideBrowserAutoFillSettings": { - "message": "Override browser auto-fill settings", - "description": "Label for the setting that allows overriding the default browser autofill settings" - }, - "extensionPrivacyPermissionNotGrantedTitle": { - "message": "Unable to override browser auto-fill", - "description": "Title for the dialog that appears when the user has not granted the extension permission to set privacy settings" - }, - "extensionPrivacyPermissionNotGrantedDescription": { - "message": "Bitwarden must have access to the extension's privacy permission to override browser auto-fill settings.", - "description": "Description for the dialog that appears when the user has not granted the extension permission to set privacy settings" - }, "importData": { "message": "Import data", "description": "Used for the header of the import dialog, the import button and within the file-password-prompt" @@ -2679,6 +2670,9 @@ "confirmFilePassword": { "message": "Confirm file password" }, + "typePasskey": { + "message": "Passkey" + }, "passkeyNotCopied": { "message": "Passkey will not be copied" }, @@ -2793,5 +2787,35 @@ }, "lastPassYubikeyDesc": { "message": "Insert the YubiKey associated with your LastPass account into your computer's USB port, then touch its button." + }, + "switchAccount": { + "message": "Switch account" + }, + "switchAccounts": { + "message": "Switch accounts" + }, + "switchToAccount": { + "message": "Switch to account" + }, + "activeAccount": { + "message": "Active account" + }, + "accountLimitReached": { + "message": "Account limit reached. Log out of an account to add another." + }, + "active": { + "message": "active" + }, + "locked": { + "message": "locked" + }, + "unlocked": { + "message": "unlocked" + }, + "server": { + "message": "server" + }, + "hostedAt": { + "message": "hosted at" } } diff --git a/apps/browser/src/_locales/my/messages.json b/apps/browser/src/_locales/my/messages.json index 5f243afe040..ab3933bf5b7 100644 --- a/apps/browser/src/_locales/my/messages.json +++ b/apps/browser/src/_locales/my/messages.json @@ -365,6 +365,9 @@ "unlockMethodNeededToChangeTimeoutActionDesc": { "message": "Set up an unlock method to change your vault timeout action." }, + "unlockMethodNeeded": { + "message": "Set up an unlock method in Settings" + }, "rateExtension": { "message": "Rate the extension" }, @@ -405,6 +408,9 @@ "lockNow": { "message": "Lock now" }, + "lockAll": { + "message": "Lock all" + }, "immediately": { "message": "Immediately" }, @@ -1027,6 +1033,12 @@ "showAutoFillMenuOnFormFieldsDescAlt": { "message": "Applies to all logged in accounts." }, + "turnOffBrowserBuiltInPasswordManagerSettings": { + "message": "Turn off your browser’s built in password manager settings to avoid conflicts." + }, + "turnOffBrowserBuiltInPasswordManagerSettingsLink": { + "message": "Edit browser settings." + }, "autofillOverlayVisibilityOff": { "message": "Off", "description": "Overlay setting select option for disabling autofill overlay" @@ -1131,6 +1143,9 @@ "faviconDesc": { "message": "Show a recognizable image next to each login." }, + "faviconDescAlt": { + "message": "Show a recognizable image next to each login. Applies to all logged in accounts." + }, "enableBadgeCounter": { "message": "Show badge counter" }, @@ -2538,36 +2553,12 @@ "message": "Bitwarden auto-fill menu available. Press the down arrow key to select.", "description": "Screen reader text for announcing when the overlay opens on the page" }, - "overrideBrowserAutofillTitle": { - "message": "Override browser auto-fill?", - "description": "Dialog title facilitating the ability to override a chrome browser's default autofill behavior" - }, - "overrideBrowserAutofillDescription": { - "message": "Leaving this setting off may cause conflicts between the Bitwarden auto-fill menu and your browser’s.", - "description": "Dialog message facilitating the ability to override a chrome browser's default autofill behavior" - }, - "overrideBrowserAutofillPrivacyRequiredDescription": { - "message": "Leaving this setting off may cause conflicts between the Bitwarden auto-fill menu and your browser’s. Turning this on will restart the Bitwarden extension.", - "description": "Dialog message facilitating the ability to override a chrome browser's default autofill behavior" - }, "turnOn": { "message": "Turn on" }, "ignore": { "message": "Ignore" }, - "overrideBrowserAutoFillSettings": { - "message": "Override browser auto-fill settings", - "description": "Label for the setting that allows overriding the default browser autofill settings" - }, - "extensionPrivacyPermissionNotGrantedTitle": { - "message": "Unable to override browser auto-fill", - "description": "Title for the dialog that appears when the user has not granted the extension permission to set privacy settings" - }, - "extensionPrivacyPermissionNotGrantedDescription": { - "message": "Bitwarden must have access to the extension's privacy permission to override browser auto-fill settings.", - "description": "Description for the dialog that appears when the user has not granted the extension permission to set privacy settings" - }, "importData": { "message": "Import data", "description": "Used for the header of the import dialog, the import button and within the file-password-prompt" @@ -2679,6 +2670,9 @@ "confirmFilePassword": { "message": "Confirm file password" }, + "typePasskey": { + "message": "Passkey" + }, "passkeyNotCopied": { "message": "Passkey will not be copied" }, @@ -2793,5 +2787,35 @@ }, "lastPassYubikeyDesc": { "message": "Insert the YubiKey associated with your LastPass account into your computer's USB port, then touch its button." + }, + "switchAccount": { + "message": "Switch account" + }, + "switchAccounts": { + "message": "Switch accounts" + }, + "switchToAccount": { + "message": "Switch to account" + }, + "activeAccount": { + "message": "Active account" + }, + "accountLimitReached": { + "message": "Account limit reached. Log out of an account to add another." + }, + "active": { + "message": "active" + }, + "locked": { + "message": "locked" + }, + "unlocked": { + "message": "unlocked" + }, + "server": { + "message": "server" + }, + "hostedAt": { + "message": "hosted at" } } diff --git a/apps/browser/src/_locales/nb/messages.json b/apps/browser/src/_locales/nb/messages.json index ea42337a129..5bccc8e6fd3 100644 --- a/apps/browser/src/_locales/nb/messages.json +++ b/apps/browser/src/_locales/nb/messages.json @@ -365,6 +365,9 @@ "unlockMethodNeededToChangeTimeoutActionDesc": { "message": "Set up an unlock method to change your vault timeout action." }, + "unlockMethodNeeded": { + "message": "Set up an unlock method in Settings" + }, "rateExtension": { "message": "Gi denne utvidelsen en vurdering" }, @@ -405,6 +408,9 @@ "lockNow": { "message": "Lås nå" }, + "lockAll": { + "message": "Lock all" + }, "immediately": { "message": "Umiddelbart" }, @@ -1027,6 +1033,12 @@ "showAutoFillMenuOnFormFieldsDescAlt": { "message": "Applies to all logged in accounts." }, + "turnOffBrowserBuiltInPasswordManagerSettings": { + "message": "Turn off your browser’s built in password manager settings to avoid conflicts." + }, + "turnOffBrowserBuiltInPasswordManagerSettingsLink": { + "message": "Edit browser settings." + }, "autofillOverlayVisibilityOff": { "message": "Off", "description": "Overlay setting select option for disabling autofill overlay" @@ -1131,6 +1143,9 @@ "faviconDesc": { "message": "Vis et gjenkjennelig bilde ved siden av hver innlogging." }, + "faviconDescAlt": { + "message": "Show a recognizable image next to each login. Applies to all logged in accounts." + }, "enableBadgeCounter": { "message": "Vis merke-teller" }, @@ -2538,36 +2553,12 @@ "message": "Bitwarden auto-fill menu available. Press the down arrow key to select.", "description": "Screen reader text for announcing when the overlay opens on the page" }, - "overrideBrowserAutofillTitle": { - "message": "Override browser auto-fill?", - "description": "Dialog title facilitating the ability to override a chrome browser's default autofill behavior" - }, - "overrideBrowserAutofillDescription": { - "message": "Leaving this setting off may cause conflicts between the Bitwarden auto-fill menu and your browser’s.", - "description": "Dialog message facilitating the ability to override a chrome browser's default autofill behavior" - }, - "overrideBrowserAutofillPrivacyRequiredDescription": { - "message": "Leaving this setting off may cause conflicts between the Bitwarden auto-fill menu and your browser’s. Turning this on will restart the Bitwarden extension.", - "description": "Dialog message facilitating the ability to override a chrome browser's default autofill behavior" - }, "turnOn": { "message": "Turn on" }, "ignore": { "message": "Ignore" }, - "overrideBrowserAutoFillSettings": { - "message": "Override browser auto-fill settings", - "description": "Label for the setting that allows overriding the default browser autofill settings" - }, - "extensionPrivacyPermissionNotGrantedTitle": { - "message": "Unable to override browser auto-fill", - "description": "Title for the dialog that appears when the user has not granted the extension permission to set privacy settings" - }, - "extensionPrivacyPermissionNotGrantedDescription": { - "message": "Bitwarden must have access to the extension's privacy permission to override browser auto-fill settings.", - "description": "Description for the dialog that appears when the user has not granted the extension permission to set privacy settings" - }, "importData": { "message": "Import data", "description": "Used for the header of the import dialog, the import button and within the file-password-prompt" @@ -2679,6 +2670,9 @@ "confirmFilePassword": { "message": "Confirm file password" }, + "typePasskey": { + "message": "Passkey" + }, "passkeyNotCopied": { "message": "Passkey will not be copied" }, @@ -2793,5 +2787,35 @@ }, "lastPassYubikeyDesc": { "message": "Insert the YubiKey associated with your LastPass account into your computer's USB port, then touch its button." + }, + "switchAccount": { + "message": "Switch account" + }, + "switchAccounts": { + "message": "Switch accounts" + }, + "switchToAccount": { + "message": "Switch to account" + }, + "activeAccount": { + "message": "Active account" + }, + "accountLimitReached": { + "message": "Account limit reached. Log out of an account to add another." + }, + "active": { + "message": "active" + }, + "locked": { + "message": "locked" + }, + "unlocked": { + "message": "unlocked" + }, + "server": { + "message": "server" + }, + "hostedAt": { + "message": "hosted at" } } diff --git a/apps/browser/src/_locales/ne/messages.json b/apps/browser/src/_locales/ne/messages.json index 5f243afe040..ab3933bf5b7 100644 --- a/apps/browser/src/_locales/ne/messages.json +++ b/apps/browser/src/_locales/ne/messages.json @@ -365,6 +365,9 @@ "unlockMethodNeededToChangeTimeoutActionDesc": { "message": "Set up an unlock method to change your vault timeout action." }, + "unlockMethodNeeded": { + "message": "Set up an unlock method in Settings" + }, "rateExtension": { "message": "Rate the extension" }, @@ -405,6 +408,9 @@ "lockNow": { "message": "Lock now" }, + "lockAll": { + "message": "Lock all" + }, "immediately": { "message": "Immediately" }, @@ -1027,6 +1033,12 @@ "showAutoFillMenuOnFormFieldsDescAlt": { "message": "Applies to all logged in accounts." }, + "turnOffBrowserBuiltInPasswordManagerSettings": { + "message": "Turn off your browser’s built in password manager settings to avoid conflicts." + }, + "turnOffBrowserBuiltInPasswordManagerSettingsLink": { + "message": "Edit browser settings." + }, "autofillOverlayVisibilityOff": { "message": "Off", "description": "Overlay setting select option for disabling autofill overlay" @@ -1131,6 +1143,9 @@ "faviconDesc": { "message": "Show a recognizable image next to each login." }, + "faviconDescAlt": { + "message": "Show a recognizable image next to each login. Applies to all logged in accounts." + }, "enableBadgeCounter": { "message": "Show badge counter" }, @@ -2538,36 +2553,12 @@ "message": "Bitwarden auto-fill menu available. Press the down arrow key to select.", "description": "Screen reader text for announcing when the overlay opens on the page" }, - "overrideBrowserAutofillTitle": { - "message": "Override browser auto-fill?", - "description": "Dialog title facilitating the ability to override a chrome browser's default autofill behavior" - }, - "overrideBrowserAutofillDescription": { - "message": "Leaving this setting off may cause conflicts between the Bitwarden auto-fill menu and your browser’s.", - "description": "Dialog message facilitating the ability to override a chrome browser's default autofill behavior" - }, - "overrideBrowserAutofillPrivacyRequiredDescription": { - "message": "Leaving this setting off may cause conflicts between the Bitwarden auto-fill menu and your browser’s. Turning this on will restart the Bitwarden extension.", - "description": "Dialog message facilitating the ability to override a chrome browser's default autofill behavior" - }, "turnOn": { "message": "Turn on" }, "ignore": { "message": "Ignore" }, - "overrideBrowserAutoFillSettings": { - "message": "Override browser auto-fill settings", - "description": "Label for the setting that allows overriding the default browser autofill settings" - }, - "extensionPrivacyPermissionNotGrantedTitle": { - "message": "Unable to override browser auto-fill", - "description": "Title for the dialog that appears when the user has not granted the extension permission to set privacy settings" - }, - "extensionPrivacyPermissionNotGrantedDescription": { - "message": "Bitwarden must have access to the extension's privacy permission to override browser auto-fill settings.", - "description": "Description for the dialog that appears when the user has not granted the extension permission to set privacy settings" - }, "importData": { "message": "Import data", "description": "Used for the header of the import dialog, the import button and within the file-password-prompt" @@ -2679,6 +2670,9 @@ "confirmFilePassword": { "message": "Confirm file password" }, + "typePasskey": { + "message": "Passkey" + }, "passkeyNotCopied": { "message": "Passkey will not be copied" }, @@ -2793,5 +2787,35 @@ }, "lastPassYubikeyDesc": { "message": "Insert the YubiKey associated with your LastPass account into your computer's USB port, then touch its button." + }, + "switchAccount": { + "message": "Switch account" + }, + "switchAccounts": { + "message": "Switch accounts" + }, + "switchToAccount": { + "message": "Switch to account" + }, + "activeAccount": { + "message": "Active account" + }, + "accountLimitReached": { + "message": "Account limit reached. Log out of an account to add another." + }, + "active": { + "message": "active" + }, + "locked": { + "message": "locked" + }, + "unlocked": { + "message": "unlocked" + }, + "server": { + "message": "server" + }, + "hostedAt": { + "message": "hosted at" } } diff --git a/apps/browser/src/_locales/nl/messages.json b/apps/browser/src/_locales/nl/messages.json index 7303f8f5080..696b808ec86 100644 --- a/apps/browser/src/_locales/nl/messages.json +++ b/apps/browser/src/_locales/nl/messages.json @@ -365,6 +365,9 @@ "unlockMethodNeededToChangeTimeoutActionDesc": { "message": "Stel een ontgrendelingsmethode in om je kluis time-out actie te wijzigen." }, + "unlockMethodNeeded": { + "message": "Set up an unlock method in Settings" + }, "rateExtension": { "message": "Deze extensie beoordelen" }, @@ -405,6 +408,9 @@ "lockNow": { "message": "Nu vergrendelen" }, + "lockAll": { + "message": "Lock all" + }, "immediately": { "message": "Onmiddellijk" }, @@ -1027,6 +1033,12 @@ "showAutoFillMenuOnFormFieldsDescAlt": { "message": "Applies to all logged in accounts." }, + "turnOffBrowserBuiltInPasswordManagerSettings": { + "message": "Schakel de ingebouwde wachtwoordbeheerinstellingen van je browser uit om conflicten te voorkomen." + }, + "turnOffBrowserBuiltInPasswordManagerSettingsLink": { + "message": "Browserinstellingen bewerken." + }, "autofillOverlayVisibilityOff": { "message": "Uit", "description": "Overlay setting select option for disabling autofill overlay" @@ -1131,6 +1143,9 @@ "faviconDesc": { "message": "Een herkenbare afbeelding naast iedere login weergeven." }, + "faviconDescAlt": { + "message": "Show a recognizable image next to each login. Applies to all logged in accounts." + }, "enableBadgeCounter": { "message": "Teller weergeven" }, @@ -2538,36 +2553,12 @@ "message": "Bitwarden auto-invulmenu beschikbaar. Druk op de pijltjestoets omlaag om te selecteren.", "description": "Screen reader text for announcing when the overlay opens on the page" }, - "overrideBrowserAutofillTitle": { - "message": "Auto-fill instellingen van browser overschrijven?", - "description": "Dialog title facilitating the ability to override a chrome browser's default autofill behavior" - }, - "overrideBrowserAutofillDescription": { - "message": "Als je deze instelling uitschakelt, kunnen er conflicten ontstaan tussen het automatisch invullen van Bitwarden en dat van je browser.", - "description": "Dialog message facilitating the ability to override a chrome browser's default autofill behavior" - }, - "overrideBrowserAutofillPrivacyRequiredDescription": { - "message": "Als je deze instelling uitschakelt, kunnen er conflicten ontstaan tussen het automatische invulmenu van Bitwarden en je browser. Als je dit inschakelt, wordt de Bitwarden-extensie opnieuw opgestart.", - "description": "Dialog message facilitating the ability to override a chrome browser's default autofill behavior" - }, "turnOn": { "message": "Inschakelen" }, "ignore": { "message": "Negeren" }, - "overrideBrowserAutoFillSettings": { - "message": "Autofill instellingen van browser overschrijven", - "description": "Label for the setting that allows overriding the default browser autofill settings" - }, - "extensionPrivacyPermissionNotGrantedTitle": { - "message": "Kan automatisch aanvullen van browser niet overschrijven", - "description": "Title for the dialog that appears when the user has not granted the extension permission to set privacy settings" - }, - "extensionPrivacyPermissionNotGrantedDescription": { - "message": "Bitwarden moet toegang hebben tot de privacy-rechten van de extensie om browser auto-fill instellingen te overschrijven.", - "description": "Description for the dialog that appears when the user has not granted the extension permission to set privacy settings" - }, "importData": { "message": "Data importeren", "description": "Used for the header of the import dialog, the import button and within the file-password-prompt" @@ -2679,6 +2670,9 @@ "confirmFilePassword": { "message": "Bestandswachtwoord bevestigen" }, + "typePasskey": { + "message": "Passkey" + }, "passkeyNotCopied": { "message": "Passkey wordt niet gekopieerd" }, @@ -2793,5 +2787,35 @@ }, "lastPassYubikeyDesc": { "message": "Insert the YubiKey associated with your LastPass account into your computer's USB port, then touch its button." + }, + "switchAccount": { + "message": "Switch account" + }, + "switchAccounts": { + "message": "Switch accounts" + }, + "switchToAccount": { + "message": "Switch to account" + }, + "activeAccount": { + "message": "Active account" + }, + "accountLimitReached": { + "message": "Account limit reached. Log out of an account to add another." + }, + "active": { + "message": "active" + }, + "locked": { + "message": "locked" + }, + "unlocked": { + "message": "unlocked" + }, + "server": { + "message": "server" + }, + "hostedAt": { + "message": "hosted at" } } diff --git a/apps/browser/src/_locales/nn/messages.json b/apps/browser/src/_locales/nn/messages.json index 5f243afe040..ab3933bf5b7 100644 --- a/apps/browser/src/_locales/nn/messages.json +++ b/apps/browser/src/_locales/nn/messages.json @@ -365,6 +365,9 @@ "unlockMethodNeededToChangeTimeoutActionDesc": { "message": "Set up an unlock method to change your vault timeout action." }, + "unlockMethodNeeded": { + "message": "Set up an unlock method in Settings" + }, "rateExtension": { "message": "Rate the extension" }, @@ -405,6 +408,9 @@ "lockNow": { "message": "Lock now" }, + "lockAll": { + "message": "Lock all" + }, "immediately": { "message": "Immediately" }, @@ -1027,6 +1033,12 @@ "showAutoFillMenuOnFormFieldsDescAlt": { "message": "Applies to all logged in accounts." }, + "turnOffBrowserBuiltInPasswordManagerSettings": { + "message": "Turn off your browser’s built in password manager settings to avoid conflicts." + }, + "turnOffBrowserBuiltInPasswordManagerSettingsLink": { + "message": "Edit browser settings." + }, "autofillOverlayVisibilityOff": { "message": "Off", "description": "Overlay setting select option for disabling autofill overlay" @@ -1131,6 +1143,9 @@ "faviconDesc": { "message": "Show a recognizable image next to each login." }, + "faviconDescAlt": { + "message": "Show a recognizable image next to each login. Applies to all logged in accounts." + }, "enableBadgeCounter": { "message": "Show badge counter" }, @@ -2538,36 +2553,12 @@ "message": "Bitwarden auto-fill menu available. Press the down arrow key to select.", "description": "Screen reader text for announcing when the overlay opens on the page" }, - "overrideBrowserAutofillTitle": { - "message": "Override browser auto-fill?", - "description": "Dialog title facilitating the ability to override a chrome browser's default autofill behavior" - }, - "overrideBrowserAutofillDescription": { - "message": "Leaving this setting off may cause conflicts between the Bitwarden auto-fill menu and your browser’s.", - "description": "Dialog message facilitating the ability to override a chrome browser's default autofill behavior" - }, - "overrideBrowserAutofillPrivacyRequiredDescription": { - "message": "Leaving this setting off may cause conflicts between the Bitwarden auto-fill menu and your browser’s. Turning this on will restart the Bitwarden extension.", - "description": "Dialog message facilitating the ability to override a chrome browser's default autofill behavior" - }, "turnOn": { "message": "Turn on" }, "ignore": { "message": "Ignore" }, - "overrideBrowserAutoFillSettings": { - "message": "Override browser auto-fill settings", - "description": "Label for the setting that allows overriding the default browser autofill settings" - }, - "extensionPrivacyPermissionNotGrantedTitle": { - "message": "Unable to override browser auto-fill", - "description": "Title for the dialog that appears when the user has not granted the extension permission to set privacy settings" - }, - "extensionPrivacyPermissionNotGrantedDescription": { - "message": "Bitwarden must have access to the extension's privacy permission to override browser auto-fill settings.", - "description": "Description for the dialog that appears when the user has not granted the extension permission to set privacy settings" - }, "importData": { "message": "Import data", "description": "Used for the header of the import dialog, the import button and within the file-password-prompt" @@ -2679,6 +2670,9 @@ "confirmFilePassword": { "message": "Confirm file password" }, + "typePasskey": { + "message": "Passkey" + }, "passkeyNotCopied": { "message": "Passkey will not be copied" }, @@ -2793,5 +2787,35 @@ }, "lastPassYubikeyDesc": { "message": "Insert the YubiKey associated with your LastPass account into your computer's USB port, then touch its button." + }, + "switchAccount": { + "message": "Switch account" + }, + "switchAccounts": { + "message": "Switch accounts" + }, + "switchToAccount": { + "message": "Switch to account" + }, + "activeAccount": { + "message": "Active account" + }, + "accountLimitReached": { + "message": "Account limit reached. Log out of an account to add another." + }, + "active": { + "message": "active" + }, + "locked": { + "message": "locked" + }, + "unlocked": { + "message": "unlocked" + }, + "server": { + "message": "server" + }, + "hostedAt": { + "message": "hosted at" } } diff --git a/apps/browser/src/_locales/or/messages.json b/apps/browser/src/_locales/or/messages.json index 5f243afe040..ab3933bf5b7 100644 --- a/apps/browser/src/_locales/or/messages.json +++ b/apps/browser/src/_locales/or/messages.json @@ -365,6 +365,9 @@ "unlockMethodNeededToChangeTimeoutActionDesc": { "message": "Set up an unlock method to change your vault timeout action." }, + "unlockMethodNeeded": { + "message": "Set up an unlock method in Settings" + }, "rateExtension": { "message": "Rate the extension" }, @@ -405,6 +408,9 @@ "lockNow": { "message": "Lock now" }, + "lockAll": { + "message": "Lock all" + }, "immediately": { "message": "Immediately" }, @@ -1027,6 +1033,12 @@ "showAutoFillMenuOnFormFieldsDescAlt": { "message": "Applies to all logged in accounts." }, + "turnOffBrowserBuiltInPasswordManagerSettings": { + "message": "Turn off your browser’s built in password manager settings to avoid conflicts." + }, + "turnOffBrowserBuiltInPasswordManagerSettingsLink": { + "message": "Edit browser settings." + }, "autofillOverlayVisibilityOff": { "message": "Off", "description": "Overlay setting select option for disabling autofill overlay" @@ -1131,6 +1143,9 @@ "faviconDesc": { "message": "Show a recognizable image next to each login." }, + "faviconDescAlt": { + "message": "Show a recognizable image next to each login. Applies to all logged in accounts." + }, "enableBadgeCounter": { "message": "Show badge counter" }, @@ -2538,36 +2553,12 @@ "message": "Bitwarden auto-fill menu available. Press the down arrow key to select.", "description": "Screen reader text for announcing when the overlay opens on the page" }, - "overrideBrowserAutofillTitle": { - "message": "Override browser auto-fill?", - "description": "Dialog title facilitating the ability to override a chrome browser's default autofill behavior" - }, - "overrideBrowserAutofillDescription": { - "message": "Leaving this setting off may cause conflicts between the Bitwarden auto-fill menu and your browser’s.", - "description": "Dialog message facilitating the ability to override a chrome browser's default autofill behavior" - }, - "overrideBrowserAutofillPrivacyRequiredDescription": { - "message": "Leaving this setting off may cause conflicts between the Bitwarden auto-fill menu and your browser’s. Turning this on will restart the Bitwarden extension.", - "description": "Dialog message facilitating the ability to override a chrome browser's default autofill behavior" - }, "turnOn": { "message": "Turn on" }, "ignore": { "message": "Ignore" }, - "overrideBrowserAutoFillSettings": { - "message": "Override browser auto-fill settings", - "description": "Label for the setting that allows overriding the default browser autofill settings" - }, - "extensionPrivacyPermissionNotGrantedTitle": { - "message": "Unable to override browser auto-fill", - "description": "Title for the dialog that appears when the user has not granted the extension permission to set privacy settings" - }, - "extensionPrivacyPermissionNotGrantedDescription": { - "message": "Bitwarden must have access to the extension's privacy permission to override browser auto-fill settings.", - "description": "Description for the dialog that appears when the user has not granted the extension permission to set privacy settings" - }, "importData": { "message": "Import data", "description": "Used for the header of the import dialog, the import button and within the file-password-prompt" @@ -2679,6 +2670,9 @@ "confirmFilePassword": { "message": "Confirm file password" }, + "typePasskey": { + "message": "Passkey" + }, "passkeyNotCopied": { "message": "Passkey will not be copied" }, @@ -2793,5 +2787,35 @@ }, "lastPassYubikeyDesc": { "message": "Insert the YubiKey associated with your LastPass account into your computer's USB port, then touch its button." + }, + "switchAccount": { + "message": "Switch account" + }, + "switchAccounts": { + "message": "Switch accounts" + }, + "switchToAccount": { + "message": "Switch to account" + }, + "activeAccount": { + "message": "Active account" + }, + "accountLimitReached": { + "message": "Account limit reached. Log out of an account to add another." + }, + "active": { + "message": "active" + }, + "locked": { + "message": "locked" + }, + "unlocked": { + "message": "unlocked" + }, + "server": { + "message": "server" + }, + "hostedAt": { + "message": "hosted at" } } diff --git a/apps/browser/src/_locales/pl/messages.json b/apps/browser/src/_locales/pl/messages.json index 6ed75cbf58f..7ffe729d951 100644 --- a/apps/browser/src/_locales/pl/messages.json +++ b/apps/browser/src/_locales/pl/messages.json @@ -365,6 +365,9 @@ "unlockMethodNeededToChangeTimeoutActionDesc": { "message": "Ustaw metodę odblokowania, aby zmienić czas blokowania sejfu." }, + "unlockMethodNeeded": { + "message": "Ustaw metodę odblokowania w Ustawieniach" + }, "rateExtension": { "message": "Oceń rozszerzenie" }, @@ -405,6 +408,9 @@ "lockNow": { "message": "Zablokuj" }, + "lockAll": { + "message": "Zablokuj wszystko" + }, "immediately": { "message": "Natychmiast" }, @@ -1027,6 +1033,12 @@ "showAutoFillMenuOnFormFieldsDescAlt": { "message": "Dotyczy wszystkich zalogowanych kont." }, + "turnOffBrowserBuiltInPasswordManagerSettings": { + "message": "Wyłącz wbudowany w przeglądarkę menedżera haseł, aby uniknąć konfliktów." + }, + "turnOffBrowserBuiltInPasswordManagerSettingsLink": { + "message": "Edytuj ustawienia przeglądarki." + }, "autofillOverlayVisibilityOff": { "message": "Wył.", "description": "Overlay setting select option for disabling autofill overlay" @@ -1131,6 +1143,9 @@ "faviconDesc": { "message": "Pokaż rozpoznawalny obraz obok danych logowania." }, + "faviconDescAlt": { + "message": "Pokaż rozpoznawalny obraz obok każdego logowania. Dotyczy wszystkich zalogowanych kont." + }, "enableBadgeCounter": { "message": "Pokaż licznik na ikonie" }, @@ -2538,36 +2553,12 @@ "message": "Dostępne menu autouzupełniania Bitwarden. Naciśnij przycisk strzałki w dół, aby wybrać.", "description": "Screen reader text for announcing when the overlay opens on the page" }, - "overrideBrowserAutofillTitle": { - "message": "Zastąpić automatyczne wypełnianie przeglądarki?", - "description": "Dialog title facilitating the ability to override a chrome browser's default autofill behavior" - }, - "overrideBrowserAutofillDescription": { - "message": "Pozostawienie tego ustawienia jako wyłączonego może spowodować konflikty pomiędzy menu autouzupełniania Bitwarden a przeglądarką.", - "description": "Dialog message facilitating the ability to override a chrome browser's default autofill behavior" - }, - "overrideBrowserAutofillPrivacyRequiredDescription": { - "message": "Pozostawienie tego ustawienia jako wyłączonego może spowodować konflikty pomiędzy menu autouzupełniania Bitwarden a przeglądarką. Włączenie tego spowoduje ponowne uruchomienie rozszerzenia Bitwarden.", - "description": "Dialog message facilitating the ability to override a chrome browser's default autofill behavior" - }, "turnOn": { "message": "Włącz" }, "ignore": { "message": "Ignoruj" }, - "overrideBrowserAutoFillSettings": { - "message": "Nadpisz ustawienia autouzupełniania przeglądarki", - "description": "Label for the setting that allows overriding the default browser autofill settings" - }, - "extensionPrivacyPermissionNotGrantedTitle": { - "message": "Nie można napisać autouzupełniania przeglądarki", - "description": "Title for the dialog that appears when the user has not granted the extension permission to set privacy settings" - }, - "extensionPrivacyPermissionNotGrantedDescription": { - "message": "Bitwarden musi mieć dostęp do uprawnień prywatności rozszerzenia, aby nadpisać ustawienia autouzupełniania przeglądarki.", - "description": "Description for the dialog that appears when the user has not granted the extension permission to set privacy settings" - }, "importData": { "message": "Importuj dane", "description": "Used for the header of the import dialog, the import button and within the file-password-prompt" @@ -2679,6 +2670,9 @@ "confirmFilePassword": { "message": "Potwierdź hasło pliku" }, + "typePasskey": { + "message": "Passkey" + }, "passkeyNotCopied": { "message": "Passkey nie zostanie skopiowany" }, @@ -2793,5 +2787,35 @@ }, "lastPassYubikeyDesc": { "message": "Włóż YubiKey powiązany z Twoim kontem LastPass do portu USB komputera, a następnie naciśnij jego przycisk." + }, + "switchAccount": { + "message": "Przełącz konto" + }, + "switchAccounts": { + "message": "Przełącz konta" + }, + "switchToAccount": { + "message": "Przełącz na konto" + }, + "activeAccount": { + "message": "Aktywne konto" + }, + "accountLimitReached": { + "message": "Limit kont został osiągnięty. Wyloguj się z konta, aby dodać inne." + }, + "active": { + "message": "aktywne" + }, + "locked": { + "message": "zablokowane" + }, + "unlocked": { + "message": "odblokowane" + }, + "server": { + "message": "serwer" + }, + "hostedAt": { + "message": "hostowany w" } } diff --git a/apps/browser/src/_locales/pt_BR/messages.json b/apps/browser/src/_locales/pt_BR/messages.json index 9d5bab1cd5c..0658070feeb 100644 --- a/apps/browser/src/_locales/pt_BR/messages.json +++ b/apps/browser/src/_locales/pt_BR/messages.json @@ -365,6 +365,9 @@ "unlockMethodNeededToChangeTimeoutActionDesc": { "message": "Set up an unlock method to change your vault timeout action." }, + "unlockMethodNeeded": { + "message": "Set up an unlock method in Settings" + }, "rateExtension": { "message": "Avaliar a Extensão" }, @@ -405,6 +408,9 @@ "lockNow": { "message": "Bloquear Agora" }, + "lockAll": { + "message": "Lock all" + }, "immediately": { "message": "Imediatamente" }, @@ -1027,6 +1033,12 @@ "showAutoFillMenuOnFormFieldsDescAlt": { "message": "Applies to all logged in accounts." }, + "turnOffBrowserBuiltInPasswordManagerSettings": { + "message": "Turn off your browser’s built in password manager settings to avoid conflicts." + }, + "turnOffBrowserBuiltInPasswordManagerSettingsLink": { + "message": "Edit browser settings." + }, "autofillOverlayVisibilityOff": { "message": "Off", "description": "Overlay setting select option for disabling autofill overlay" @@ -1131,6 +1143,9 @@ "faviconDesc": { "message": "Mostrar uma imagem reconhecível ao lado de cada login." }, + "faviconDescAlt": { + "message": "Show a recognizable image next to each login. Applies to all logged in accounts." + }, "enableBadgeCounter": { "message": "Mostrar contador de insígnia" }, @@ -2538,36 +2553,12 @@ "message": "Bitwarden auto-fill menu available. Press the down arrow key to select.", "description": "Screen reader text for announcing when the overlay opens on the page" }, - "overrideBrowserAutofillTitle": { - "message": "Override browser auto-fill?", - "description": "Dialog title facilitating the ability to override a chrome browser's default autofill behavior" - }, - "overrideBrowserAutofillDescription": { - "message": "Leaving this setting off may cause conflicts between the Bitwarden auto-fill menu and your browser’s.", - "description": "Dialog message facilitating the ability to override a chrome browser's default autofill behavior" - }, - "overrideBrowserAutofillPrivacyRequiredDescription": { - "message": "Leaving this setting off may cause conflicts between the Bitwarden auto-fill menu and your browser’s. Turning this on will restart the Bitwarden extension.", - "description": "Dialog message facilitating the ability to override a chrome browser's default autofill behavior" - }, "turnOn": { "message": "Turn on" }, "ignore": { "message": "Ignore" }, - "overrideBrowserAutoFillSettings": { - "message": "Override browser auto-fill settings", - "description": "Label for the setting that allows overriding the default browser autofill settings" - }, - "extensionPrivacyPermissionNotGrantedTitle": { - "message": "Unable to override browser auto-fill", - "description": "Title for the dialog that appears when the user has not granted the extension permission to set privacy settings" - }, - "extensionPrivacyPermissionNotGrantedDescription": { - "message": "Bitwarden must have access to the extension's privacy permission to override browser auto-fill settings.", - "description": "Description for the dialog that appears when the user has not granted the extension permission to set privacy settings" - }, "importData": { "message": "Import data", "description": "Used for the header of the import dialog, the import button and within the file-password-prompt" @@ -2679,6 +2670,9 @@ "confirmFilePassword": { "message": "Confirm file password" }, + "typePasskey": { + "message": "Passkey" + }, "passkeyNotCopied": { "message": "Passkey will not be copied" }, @@ -2793,5 +2787,35 @@ }, "lastPassYubikeyDesc": { "message": "Insert the YubiKey associated with your LastPass account into your computer's USB port, then touch its button." + }, + "switchAccount": { + "message": "Switch account" + }, + "switchAccounts": { + "message": "Switch accounts" + }, + "switchToAccount": { + "message": "Switch to account" + }, + "activeAccount": { + "message": "Active account" + }, + "accountLimitReached": { + "message": "Account limit reached. Log out of an account to add another." + }, + "active": { + "message": "active" + }, + "locked": { + "message": "locked" + }, + "unlocked": { + "message": "unlocked" + }, + "server": { + "message": "server" + }, + "hostedAt": { + "message": "hosted at" } } diff --git a/apps/browser/src/_locales/pt_PT/messages.json b/apps/browser/src/_locales/pt_PT/messages.json index 8759e14e70d..1467ee0b147 100644 --- a/apps/browser/src/_locales/pt_PT/messages.json +++ b/apps/browser/src/_locales/pt_PT/messages.json @@ -365,6 +365,9 @@ "unlockMethodNeededToChangeTimeoutActionDesc": { "message": "Configure um método de desbloqueio para alterar a ação de tempo limite do seu cofre." }, + "unlockMethodNeeded": { + "message": "Definir um método de desbloqueio nas Definições" + }, "rateExtension": { "message": "Avaliar a extensão" }, @@ -405,6 +408,9 @@ "lockNow": { "message": "Bloquear agora" }, + "lockAll": { + "message": "Bloquear tudo" + }, "immediately": { "message": "Imediatamente" }, @@ -1027,6 +1033,12 @@ "showAutoFillMenuOnFormFieldsDescAlt": { "message": "Aplica-se a todas as contas com sessão iniciada." }, + "turnOffBrowserBuiltInPasswordManagerSettings": { + "message": "Desative as definições do gestor de palavras-passe incorporado do seu navegador para evitar conflitos." + }, + "turnOffBrowserBuiltInPasswordManagerSettingsLink": { + "message": "Editar as definições do navegador." + }, "autofillOverlayVisibilityOff": { "message": "Desligado", "description": "Overlay setting select option for disabling autofill overlay" @@ -1131,6 +1143,9 @@ "faviconDesc": { "message": "Mostrar uma imagem reconhecível junto a cada credencial." }, + "faviconDescAlt": { + "message": "Mostrar uma imagem reconhecível junto a cada credencial. Aplica-se a todas as contas com sessão iniciada." + }, "enableBadgeCounter": { "message": "Mostrar distintivo de contador" }, @@ -2538,36 +2553,12 @@ "message": "Menu de preenchimento automático Bitwarden disponível. Prima a tecla de seta para baixo para selecionar.", "description": "Screen reader text for announcing when the overlay opens on the page" }, - "overrideBrowserAutofillTitle": { - "message": "Anular o preenchimento automático do navegador?", - "description": "Dialog title facilitating the ability to override a chrome browser's default autofill behavior" - }, - "overrideBrowserAutofillDescription": { - "message": "Deixar esta definição desativada pode causar conflitos entre o menu de preenchimento automático do Bitwarden e o do seu navegador.", - "description": "Dialog message facilitating the ability to override a chrome browser's default autofill behavior" - }, - "overrideBrowserAutofillPrivacyRequiredDescription": { - "message": "Deixar esta configuração desativada pode causar conflitos entre o menu de preenchimento automático do Bitwarden e o do seu navegador. Ativar esta opção reiniciará a extensão Bitwarden.", - "description": "Dialog message facilitating the ability to override a chrome browser's default autofill behavior" - }, "turnOn": { "message": "Ativar" }, "ignore": { "message": "Ignorar" }, - "overrideBrowserAutoFillSettings": { - "message": "Substituir as definições de preenchimento automático do navegador", - "description": "Label for the setting that allows overriding the default browser autofill settings" - }, - "extensionPrivacyPermissionNotGrantedTitle": { - "message": "Não é possível anular o preenchimento automático do navegador", - "description": "Title for the dialog that appears when the user has not granted the extension permission to set privacy settings" - }, - "extensionPrivacyPermissionNotGrantedDescription": { - "message": "O Bitwarden deve ter acesso à permissão de privacidade da extensão para substituir as definições de preenchimento automático do navegador.", - "description": "Description for the dialog that appears when the user has not granted the extension permission to set privacy settings" - }, "importData": { "message": "Importar dados", "description": "Used for the header of the import dialog, the import button and within the file-password-prompt" @@ -2679,6 +2670,9 @@ "confirmFilePassword": { "message": "Confirmar a palavra-passe do ficheiro" }, + "typePasskey": { + "message": "Chave de acesso" + }, "passkeyNotCopied": { "message": "A chave de acesso não será copiada" }, @@ -2793,5 +2787,35 @@ }, "lastPassYubikeyDesc": { "message": "Insira a YubiKey associada à sua conta LastPass na porta USB do seu computador e, em seguida, toque no respetivo botão." + }, + "switchAccount": { + "message": "Mudar de conta" + }, + "switchAccounts": { + "message": "Mudar de contas" + }, + "switchToAccount": { + "message": "Mudar para conta" + }, + "activeAccount": { + "message": "Conta ativa" + }, + "accountLimitReached": { + "message": "Limite de contas atingido. Termine a sessão de uma das contas para adicionar outra." + }, + "active": { + "message": "ativa" + }, + "locked": { + "message": "bloqueado" + }, + "unlocked": { + "message": "desbloqueado" + }, + "server": { + "message": "servidor" + }, + "hostedAt": { + "message": "hosted at" } } diff --git a/apps/browser/src/_locales/ro/messages.json b/apps/browser/src/_locales/ro/messages.json index 3c655c51e5d..20848362c2f 100644 --- a/apps/browser/src/_locales/ro/messages.json +++ b/apps/browser/src/_locales/ro/messages.json @@ -365,6 +365,9 @@ "unlockMethodNeededToChangeTimeoutActionDesc": { "message": "Configurați metoda de deblocare care să schimbe acțiunea de expirare a seifului." }, + "unlockMethodNeeded": { + "message": "Set up an unlock method in Settings" + }, "rateExtension": { "message": "Evaluare extensie" }, @@ -405,6 +408,9 @@ "lockNow": { "message": "Blocare imediată" }, + "lockAll": { + "message": "Lock all" + }, "immediately": { "message": "Imediat" }, @@ -1027,6 +1033,12 @@ "showAutoFillMenuOnFormFieldsDescAlt": { "message": "Applies to all logged in accounts." }, + "turnOffBrowserBuiltInPasswordManagerSettings": { + "message": "Turn off your browser’s built in password manager settings to avoid conflicts." + }, + "turnOffBrowserBuiltInPasswordManagerSettingsLink": { + "message": "Edit browser settings." + }, "autofillOverlayVisibilityOff": { "message": "Off", "description": "Overlay setting select option for disabling autofill overlay" @@ -1131,6 +1143,9 @@ "faviconDesc": { "message": "Afișează o imagine ușor de recunoscut lângă fiecare autentificare." }, + "faviconDescAlt": { + "message": "Show a recognizable image next to each login. Applies to all logged in accounts." + }, "enableBadgeCounter": { "message": "Afișați contorul de insigne" }, @@ -2538,36 +2553,12 @@ "message": "Bitwarden auto-fill menu available. Press the down arrow key to select.", "description": "Screen reader text for announcing when the overlay opens on the page" }, - "overrideBrowserAutofillTitle": { - "message": "Override browser auto-fill?", - "description": "Dialog title facilitating the ability to override a chrome browser's default autofill behavior" - }, - "overrideBrowserAutofillDescription": { - "message": "Leaving this setting off may cause conflicts between the Bitwarden auto-fill menu and your browser’s.", - "description": "Dialog message facilitating the ability to override a chrome browser's default autofill behavior" - }, - "overrideBrowserAutofillPrivacyRequiredDescription": { - "message": "Leaving this setting off may cause conflicts between the Bitwarden auto-fill menu and your browser’s. Turning this on will restart the Bitwarden extension.", - "description": "Dialog message facilitating the ability to override a chrome browser's default autofill behavior" - }, "turnOn": { "message": "Turn on" }, "ignore": { "message": "Ignore" }, - "overrideBrowserAutoFillSettings": { - "message": "Override browser auto-fill settings", - "description": "Label for the setting that allows overriding the default browser autofill settings" - }, - "extensionPrivacyPermissionNotGrantedTitle": { - "message": "Unable to override browser auto-fill", - "description": "Title for the dialog that appears when the user has not granted the extension permission to set privacy settings" - }, - "extensionPrivacyPermissionNotGrantedDescription": { - "message": "Bitwarden must have access to the extension's privacy permission to override browser auto-fill settings.", - "description": "Description for the dialog that appears when the user has not granted the extension permission to set privacy settings" - }, "importData": { "message": "Import data", "description": "Used for the header of the import dialog, the import button and within the file-password-prompt" @@ -2679,6 +2670,9 @@ "confirmFilePassword": { "message": "Confirm file password" }, + "typePasskey": { + "message": "Passkey" + }, "passkeyNotCopied": { "message": "Passkey will not be copied" }, @@ -2793,5 +2787,35 @@ }, "lastPassYubikeyDesc": { "message": "Insert the YubiKey associated with your LastPass account into your computer's USB port, then touch its button." + }, + "switchAccount": { + "message": "Switch account" + }, + "switchAccounts": { + "message": "Switch accounts" + }, + "switchToAccount": { + "message": "Switch to account" + }, + "activeAccount": { + "message": "Active account" + }, + "accountLimitReached": { + "message": "Account limit reached. Log out of an account to add another." + }, + "active": { + "message": "active" + }, + "locked": { + "message": "locked" + }, + "unlocked": { + "message": "unlocked" + }, + "server": { + "message": "server" + }, + "hostedAt": { + "message": "hosted at" } } diff --git a/apps/browser/src/_locales/ru/messages.json b/apps/browser/src/_locales/ru/messages.json index e12c2448949..4db426571f1 100644 --- a/apps/browser/src/_locales/ru/messages.json +++ b/apps/browser/src/_locales/ru/messages.json @@ -365,6 +365,9 @@ "unlockMethodNeededToChangeTimeoutActionDesc": { "message": "Настройте способ разблокировки для изменения действия по тайм-ауту хранилища." }, + "unlockMethodNeeded": { + "message": "Установите способ разблокировки в настройках" + }, "rateExtension": { "message": "Оценить расширение" }, @@ -405,6 +408,9 @@ "lockNow": { "message": "Заблокировать" }, + "lockAll": { + "message": "Заблокировать все" + }, "immediately": { "message": "Немедленно" }, @@ -1027,6 +1033,12 @@ "showAutoFillMenuOnFormFieldsDescAlt": { "message": "Применяется ко всем авторизованным аккаунтам." }, + "turnOffBrowserBuiltInPasswordManagerSettings": { + "message": "Turn off your browser’s built in password manager settings to avoid conflicts." + }, + "turnOffBrowserBuiltInPasswordManagerSettingsLink": { + "message": "Edit browser settings." + }, "autofillOverlayVisibilityOff": { "message": "Выкл", "description": "Overlay setting select option for disabling autofill overlay" @@ -1131,6 +1143,9 @@ "faviconDesc": { "message": "Отображать узнаваемое изображение рядом с каждым логином." }, + "faviconDescAlt": { + "message": "Показывать узнаваемое изображение рядом с каждым логином. Применяется ко всем авторизованным аккаунтам." + }, "enableBadgeCounter": { "message": "Показать счетчик на значке" }, @@ -2538,36 +2553,12 @@ "message": "Доступно меню автозаполнения Bitwarden. Для выбора нажмите клавишу со стрелкой вниз.", "description": "Screen reader text for announcing when the overlay opens on the page" }, - "overrideBrowserAutofillTitle": { - "message": "Переопределить автозаполнение браузера?", - "description": "Dialog title facilitating the ability to override a chrome browser's default autofill behavior" - }, - "overrideBrowserAutofillDescription": { - "message": "Отключение этого параметра может привести к конфликту между меню автозаполнения Bitwarden и меню вашего браузера.", - "description": "Dialog message facilitating the ability to override a chrome browser's default autofill behavior" - }, - "overrideBrowserAutofillPrivacyRequiredDescription": { - "message": "Отключение этого параметра может привести к конфликту между меню автозаполнения Bitwarden и меню браузера. Включение этого параметра приведет к перезапуску расширения Bitwarden.", - "description": "Dialog message facilitating the ability to override a chrome browser's default autofill behavior" - }, "turnOn": { "message": "Включить" }, "ignore": { "message": "Игнорировать" }, - "overrideBrowserAutoFillSettings": { - "message": "Переопределить настройки автозаполнения браузера", - "description": "Label for the setting that allows overriding the default browser autofill settings" - }, - "extensionPrivacyPermissionNotGrantedTitle": { - "message": "Невозможно переопределить автозаполнение браузера", - "description": "Title for the dialog that appears when the user has not granted the extension permission to set privacy settings" - }, - "extensionPrivacyPermissionNotGrantedDescription": { - "message": "Bitwarden должен иметь доступ к полномочиям конфиденциальности расширения для переопределения настроек автозаполнения браузера.", - "description": "Description for the dialog that appears when the user has not granted the extension permission to set privacy settings" - }, "importData": { "message": "Импорт данных", "description": "Used for the header of the import dialog, the import button and within the file-password-prompt" @@ -2679,6 +2670,9 @@ "confirmFilePassword": { "message": "Подтвердите пароль к файлу" }, + "typePasskey": { + "message": "Ключ доступа" + }, "passkeyNotCopied": { "message": "Ключ доступа не будет скопирован" }, @@ -2793,5 +2787,35 @@ }, "lastPassYubikeyDesc": { "message": "Вставьте YubiKey, связанный с аккаунтом LastPass, в USB-порт компьютера и нажмите на его кнопку." + }, + "switchAccount": { + "message": "Сменить аккаунт" + }, + "switchAccounts": { + "message": "Сменить аккаунты" + }, + "switchToAccount": { + "message": "Переключиться на аккаунт" + }, + "activeAccount": { + "message": "Активный аккаунт" + }, + "accountLimitReached": { + "message": "Достигнут лимит аккаунтов. Выйдите, чтобы добавить другой." + }, + "active": { + "message": "активный" + }, + "locked": { + "message": "заблокирован" + }, + "unlocked": { + "message": "разблокирован" + }, + "server": { + "message": "сервер" + }, + "hostedAt": { + "message": "под управлением" } } diff --git a/apps/browser/src/_locales/si/messages.json b/apps/browser/src/_locales/si/messages.json index c8c192f54e3..f0ca959e3b8 100644 --- a/apps/browser/src/_locales/si/messages.json +++ b/apps/browser/src/_locales/si/messages.json @@ -365,6 +365,9 @@ "unlockMethodNeededToChangeTimeoutActionDesc": { "message": "Set up an unlock method to change your vault timeout action." }, + "unlockMethodNeeded": { + "message": "Set up an unlock method in Settings" + }, "rateExtension": { "message": "දිගුව අනුපාතය" }, @@ -405,6 +408,9 @@ "lockNow": { "message": "දැන් අගුලුදමන්න" }, + "lockAll": { + "message": "Lock all" + }, "immediately": { "message": "වහාම" }, @@ -1027,6 +1033,12 @@ "showAutoFillMenuOnFormFieldsDescAlt": { "message": "Applies to all logged in accounts." }, + "turnOffBrowserBuiltInPasswordManagerSettings": { + "message": "Turn off your browser’s built in password manager settings to avoid conflicts." + }, + "turnOffBrowserBuiltInPasswordManagerSettingsLink": { + "message": "Edit browser settings." + }, "autofillOverlayVisibilityOff": { "message": "Off", "description": "Overlay setting select option for disabling autofill overlay" @@ -1131,6 +1143,9 @@ "faviconDesc": { "message": "Show a recognizable image next to each login." }, + "faviconDescAlt": { + "message": "Show a recognizable image next to each login. Applies to all logged in accounts." + }, "enableBadgeCounter": { "message": "Show badge counter" }, @@ -2538,36 +2553,12 @@ "message": "Bitwarden auto-fill menu available. Press the down arrow key to select.", "description": "Screen reader text for announcing when the overlay opens on the page" }, - "overrideBrowserAutofillTitle": { - "message": "Override browser auto-fill?", - "description": "Dialog title facilitating the ability to override a chrome browser's default autofill behavior" - }, - "overrideBrowserAutofillDescription": { - "message": "Leaving this setting off may cause conflicts between the Bitwarden auto-fill menu and your browser’s.", - "description": "Dialog message facilitating the ability to override a chrome browser's default autofill behavior" - }, - "overrideBrowserAutofillPrivacyRequiredDescription": { - "message": "Leaving this setting off may cause conflicts between the Bitwarden auto-fill menu and your browser’s. Turning this on will restart the Bitwarden extension.", - "description": "Dialog message facilitating the ability to override a chrome browser's default autofill behavior" - }, "turnOn": { "message": "Turn on" }, "ignore": { "message": "Ignore" }, - "overrideBrowserAutoFillSettings": { - "message": "Override browser auto-fill settings", - "description": "Label for the setting that allows overriding the default browser autofill settings" - }, - "extensionPrivacyPermissionNotGrantedTitle": { - "message": "Unable to override browser auto-fill", - "description": "Title for the dialog that appears when the user has not granted the extension permission to set privacy settings" - }, - "extensionPrivacyPermissionNotGrantedDescription": { - "message": "Bitwarden must have access to the extension's privacy permission to override browser auto-fill settings.", - "description": "Description for the dialog that appears when the user has not granted the extension permission to set privacy settings" - }, "importData": { "message": "Import data", "description": "Used for the header of the import dialog, the import button and within the file-password-prompt" @@ -2679,6 +2670,9 @@ "confirmFilePassword": { "message": "Confirm file password" }, + "typePasskey": { + "message": "Passkey" + }, "passkeyNotCopied": { "message": "Passkey will not be copied" }, @@ -2793,5 +2787,35 @@ }, "lastPassYubikeyDesc": { "message": "Insert the YubiKey associated with your LastPass account into your computer's USB port, then touch its button." + }, + "switchAccount": { + "message": "Switch account" + }, + "switchAccounts": { + "message": "Switch accounts" + }, + "switchToAccount": { + "message": "Switch to account" + }, + "activeAccount": { + "message": "Active account" + }, + "accountLimitReached": { + "message": "Account limit reached. Log out of an account to add another." + }, + "active": { + "message": "active" + }, + "locked": { + "message": "locked" + }, + "unlocked": { + "message": "unlocked" + }, + "server": { + "message": "server" + }, + "hostedAt": { + "message": "hosted at" } } diff --git a/apps/browser/src/_locales/sk/messages.json b/apps/browser/src/_locales/sk/messages.json index ca75f2eeefd..c120c23dd52 100644 --- a/apps/browser/src/_locales/sk/messages.json +++ b/apps/browser/src/_locales/sk/messages.json @@ -365,6 +365,9 @@ "unlockMethodNeededToChangeTimeoutActionDesc": { "message": "Nastavte metódu odomknutia, aby ste zmenili akciu pri vypršaní času trezoru." }, + "unlockMethodNeeded": { + "message": "Nastavte metódu odomknutia v Nastaveniach" + }, "rateExtension": { "message": "Ohodnotiť rozšírenie" }, @@ -405,6 +408,9 @@ "lockNow": { "message": "Uzamknúť teraz" }, + "lockAll": { + "message": "Uzamknúť všetky" + }, "immediately": { "message": "Okamžite" }, @@ -1027,6 +1033,12 @@ "showAutoFillMenuOnFormFieldsDescAlt": { "message": "Platí pre všetky prihlásené účty." }, + "turnOffBrowserBuiltInPasswordManagerSettings": { + "message": "Turn off your browser’s built in password manager settings to avoid conflicts." + }, + "turnOffBrowserBuiltInPasswordManagerSettingsLink": { + "message": "Edit browser settings." + }, "autofillOverlayVisibilityOff": { "message": "Vypnuté", "description": "Overlay setting select option for disabling autofill overlay" @@ -1131,6 +1143,9 @@ "faviconDesc": { "message": "Pri každom prihlásení zobrazí rozpoznateľný obrázok." }, + "faviconDescAlt": { + "message": "Vedľa každého účtu zobraziť rozpoznateľný obrázok. Použije sa na všetky prihlásené účty." + }, "enableBadgeCounter": { "message": "Zobraziť počítadlo na ikone" }, @@ -2538,36 +2553,12 @@ "message": "K dispozícii je ponuka automatického vyplňovania Bitwardenu. Stlačte tlačidlo so šípkou nadol a vyberte.", "description": "Screen reader text for announcing when the overlay opens on the page" }, - "overrideBrowserAutofillTitle": { - "message": "Prepísať automatické vypĺňanie prehliadača?", - "description": "Dialog title facilitating the ability to override a chrome browser's default autofill behavior" - }, - "overrideBrowserAutofillDescription": { - "message": "Ak necháte toto nastavenie vypnuté, môže to spôsobiť konflikty medzi ponukou automatického vypĺňania Bitwardenu a ponukou vášho prehliadača.", - "description": "Dialog message facilitating the ability to override a chrome browser's default autofill behavior" - }, - "overrideBrowserAutofillPrivacyRequiredDescription": { - "message": "Ak necháte toto nastavenie vypnuté, môže to spôsobiť konflikty medzi ponukou automatického vypĺňania Bitwardenu a ponukou vášho prehliadača. Zapnutím nastavenia sa rozšírenie Bitwarden reštartuje.", - "description": "Dialog message facilitating the ability to override a chrome browser's default autofill behavior" - }, "turnOn": { "message": "Zapnúť" }, "ignore": { "message": "Ignorovať" }, - "overrideBrowserAutoFillSettings": { - "message": "Prepísať automatické vypĺňanie prehliadača", - "description": "Label for the setting that allows overriding the default browser autofill settings" - }, - "extensionPrivacyPermissionNotGrantedTitle": { - "message": "Nedá sa prepísať automatické vypĺňanie prehliadača", - "description": "Title for the dialog that appears when the user has not granted the extension permission to set privacy settings" - }, - "extensionPrivacyPermissionNotGrantedDescription": { - "message": "Bitwarden musí mať povolenie rozšírenia na nastavenie súkromia, aby mohol zrušiť nastavenia automatického vypĺňania prehliadača.", - "description": "Description for the dialog that appears when the user has not granted the extension permission to set privacy settings" - }, "importData": { "message": "Import údajov", "description": "Used for the header of the import dialog, the import button and within the file-password-prompt" @@ -2679,6 +2670,9 @@ "confirmFilePassword": { "message": "Potvrdiť heslo súboru" }, + "typePasskey": { + "message": "Prístupový kľúč" + }, "passkeyNotCopied": { "message": "Prístupový kód sa neskopíruje" }, @@ -2793,5 +2787,35 @@ }, "lastPassYubikeyDesc": { "message": "Vložte kľúč YubiKey priradený k vášmu účtu LastPass do USB portu počítača a potom sa dotknite jeho tlačidla." + }, + "switchAccount": { + "message": "Prepnúť účet" + }, + "switchAccounts": { + "message": "Prepnúť účty" + }, + "switchToAccount": { + "message": "Prepnúť na účet" + }, + "activeAccount": { + "message": "Aktívny účet" + }, + "accountLimitReached": { + "message": "Dosiahnutý limit účtu. Odhláste sa z účtu, ak chcete pridať ďalší." + }, + "active": { + "message": "aktívne" + }, + "locked": { + "message": "zamknuté" + }, + "unlocked": { + "message": "odomknuté" + }, + "server": { + "message": "server" + }, + "hostedAt": { + "message": "hotované na" } } diff --git a/apps/browser/src/_locales/sl/messages.json b/apps/browser/src/_locales/sl/messages.json index a2cff114f7b..dc9f1b554e8 100644 --- a/apps/browser/src/_locales/sl/messages.json +++ b/apps/browser/src/_locales/sl/messages.json @@ -365,6 +365,9 @@ "unlockMethodNeededToChangeTimeoutActionDesc": { "message": "Da spremenite časovne omejitve trezorja, nastavite metodo odklepanja." }, + "unlockMethodNeeded": { + "message": "Set up an unlock method in Settings" + }, "rateExtension": { "message": "Ocenite to razširitev" }, @@ -405,6 +408,9 @@ "lockNow": { "message": "Zakleni zdaj" }, + "lockAll": { + "message": "Lock all" + }, "immediately": { "message": "Takoj" }, @@ -1027,6 +1033,12 @@ "showAutoFillMenuOnFormFieldsDescAlt": { "message": "Applies to all logged in accounts." }, + "turnOffBrowserBuiltInPasswordManagerSettings": { + "message": "Turn off your browser’s built in password manager settings to avoid conflicts." + }, + "turnOffBrowserBuiltInPasswordManagerSettingsLink": { + "message": "Edit browser settings." + }, "autofillOverlayVisibilityOff": { "message": "Off", "description": "Overlay setting select option for disabling autofill overlay" @@ -1131,6 +1143,9 @@ "faviconDesc": { "message": "Show a recognizable image next to each login." }, + "faviconDescAlt": { + "message": "Show a recognizable image next to each login. Applies to all logged in accounts." + }, "enableBadgeCounter": { "message": "Show badge counter" }, @@ -2538,36 +2553,12 @@ "message": "Bitwarden auto-fill menu available. Press the down arrow key to select.", "description": "Screen reader text for announcing when the overlay opens on the page" }, - "overrideBrowserAutofillTitle": { - "message": "Override browser auto-fill?", - "description": "Dialog title facilitating the ability to override a chrome browser's default autofill behavior" - }, - "overrideBrowserAutofillDescription": { - "message": "Leaving this setting off may cause conflicts between the Bitwarden auto-fill menu and your browser’s.", - "description": "Dialog message facilitating the ability to override a chrome browser's default autofill behavior" - }, - "overrideBrowserAutofillPrivacyRequiredDescription": { - "message": "Leaving this setting off may cause conflicts between the Bitwarden auto-fill menu and your browser’s. Turning this on will restart the Bitwarden extension.", - "description": "Dialog message facilitating the ability to override a chrome browser's default autofill behavior" - }, "turnOn": { "message": "Turn on" }, "ignore": { "message": "Ignore" }, - "overrideBrowserAutoFillSettings": { - "message": "Override browser auto-fill settings", - "description": "Label for the setting that allows overriding the default browser autofill settings" - }, - "extensionPrivacyPermissionNotGrantedTitle": { - "message": "Unable to override browser auto-fill", - "description": "Title for the dialog that appears when the user has not granted the extension permission to set privacy settings" - }, - "extensionPrivacyPermissionNotGrantedDescription": { - "message": "Bitwarden must have access to the extension's privacy permission to override browser auto-fill settings.", - "description": "Description for the dialog that appears when the user has not granted the extension permission to set privacy settings" - }, "importData": { "message": "Import data", "description": "Used for the header of the import dialog, the import button and within the file-password-prompt" @@ -2679,6 +2670,9 @@ "confirmFilePassword": { "message": "Confirm file password" }, + "typePasskey": { + "message": "Passkey" + }, "passkeyNotCopied": { "message": "Passkey will not be copied" }, @@ -2793,5 +2787,35 @@ }, "lastPassYubikeyDesc": { "message": "Insert the YubiKey associated with your LastPass account into your computer's USB port, then touch its button." + }, + "switchAccount": { + "message": "Switch account" + }, + "switchAccounts": { + "message": "Switch accounts" + }, + "switchToAccount": { + "message": "Switch to account" + }, + "activeAccount": { + "message": "Active account" + }, + "accountLimitReached": { + "message": "Account limit reached. Log out of an account to add another." + }, + "active": { + "message": "active" + }, + "locked": { + "message": "locked" + }, + "unlocked": { + "message": "unlocked" + }, + "server": { + "message": "server" + }, + "hostedAt": { + "message": "hosted at" } } diff --git a/apps/browser/src/_locales/sr/messages.json b/apps/browser/src/_locales/sr/messages.json index 5a7a76a728d..bf4280cee90 100644 --- a/apps/browser/src/_locales/sr/messages.json +++ b/apps/browser/src/_locales/sr/messages.json @@ -365,6 +365,9 @@ "unlockMethodNeededToChangeTimeoutActionDesc": { "message": "Подесите метод откључавања да бисте променили радњу временског ограничења сефа." }, + "unlockMethodNeeded": { + "message": "Set up an unlock method in Settings" + }, "rateExtension": { "message": "Оцени овај додатак" }, @@ -405,6 +408,9 @@ "lockNow": { "message": "Закључај одмах" }, + "lockAll": { + "message": "Lock all" + }, "immediately": { "message": "Одмах" }, @@ -1027,6 +1033,12 @@ "showAutoFillMenuOnFormFieldsDescAlt": { "message": "Односи се на све пријављене налоге." }, + "turnOffBrowserBuiltInPasswordManagerSettings": { + "message": "Turn off your browser’s built in password manager settings to avoid conflicts." + }, + "turnOffBrowserBuiltInPasswordManagerSettingsLink": { + "message": "Edit browser settings." + }, "autofillOverlayVisibilityOff": { "message": "Искључено", "description": "Overlay setting select option for disabling autofill overlay" @@ -1131,6 +1143,9 @@ "faviconDesc": { "message": "Прикажи препознатљиву слику поред сваке ставке за пријаву." }, + "faviconDescAlt": { + "message": "Show a recognizable image next to each login. Applies to all logged in accounts." + }, "enableBadgeCounter": { "message": "Прикажи бедж са бројачем" }, @@ -2538,36 +2553,12 @@ "message": "Мени ауто-попуњавања Bitwarden-а је доступан. Притисните тастер са стрелицом надоле да бисте изабрали.", "description": "Screen reader text for announcing when the overlay opens on the page" }, - "overrideBrowserAutofillTitle": { - "message": "Заменити ауто-попуњавање прегледача?", - "description": "Dialog title facilitating the ability to override a chrome browser's default autofill behavior" - }, - "overrideBrowserAutofillDescription": { - "message": "Ако ову поставку оставите искљученом, може доћи до сукоба између Bitwarden менија ауто-попуњавања и вашег претраживача.", - "description": "Dialog message facilitating the ability to override a chrome browser's default autofill behavior" - }, - "overrideBrowserAutofillPrivacyRequiredDescription": { - "message": "Ако ову поставку оставите искљученом, може доћи до сукоба између Битварден менија аутоматског попуњавања и вашег претраживача. Ако упалите, то ће поново покренути Bitwarden додатак.", - "description": "Dialog message facilitating the ability to override a chrome browser's default autofill behavior" - }, "turnOn": { "message": "Укључи" }, "ignore": { "message": "Игнориши" }, - "overrideBrowserAutoFillSettings": { - "message": "Заменити ауто-попуњавање прегледача", - "description": "Label for the setting that allows overriding the default browser autofill settings" - }, - "extensionPrivacyPermissionNotGrantedTitle": { - "message": "Није могуће заменити ауто-попуњавање прегледача", - "description": "Title for the dialog that appears when the user has not granted the extension permission to set privacy settings" - }, - "extensionPrivacyPermissionNotGrantedDescription": { - "message": "Bitwarden мора имати приступ дозволи приватности екстензије да би заменио подешавања ауто-попуњавања прегледача.", - "description": "Description for the dialog that appears when the user has not granted the extension permission to set privacy settings" - }, "importData": { "message": "Увези податке", "description": "Used for the header of the import dialog, the import button and within the file-password-prompt" @@ -2679,6 +2670,9 @@ "confirmFilePassword": { "message": "Потврдити лозинку датотеке" }, + "typePasskey": { + "message": "Приступачни кључ" + }, "passkeyNotCopied": { "message": "Приступачни кључ неће бити копиран" }, @@ -2793,5 +2787,35 @@ }, "lastPassYubikeyDesc": { "message": "Унети YubiKey који је асоциран са LastPass налогом у УСБ порт вашег рачунара, и затим додирните његово дугме." + }, + "switchAccount": { + "message": "Switch account" + }, + "switchAccounts": { + "message": "Switch accounts" + }, + "switchToAccount": { + "message": "Switch to account" + }, + "activeAccount": { + "message": "Active account" + }, + "accountLimitReached": { + "message": "Account limit reached. Log out of an account to add another." + }, + "active": { + "message": "active" + }, + "locked": { + "message": "locked" + }, + "unlocked": { + "message": "unlocked" + }, + "server": { + "message": "server" + }, + "hostedAt": { + "message": "hosted at" } } diff --git a/apps/browser/src/_locales/sv/messages.json b/apps/browser/src/_locales/sv/messages.json index 67dadff95ae..a4cda54c073 100644 --- a/apps/browser/src/_locales/sv/messages.json +++ b/apps/browser/src/_locales/sv/messages.json @@ -365,6 +365,9 @@ "unlockMethodNeededToChangeTimeoutActionDesc": { "message": "Set up an unlock method to change your vault timeout action." }, + "unlockMethodNeeded": { + "message": "Set up an unlock method in Settings" + }, "rateExtension": { "message": "Betygsätt tillägget" }, @@ -405,6 +408,9 @@ "lockNow": { "message": "Lås nu" }, + "lockAll": { + "message": "Lock all" + }, "immediately": { "message": "Omedelbart" }, @@ -1027,6 +1033,12 @@ "showAutoFillMenuOnFormFieldsDescAlt": { "message": "Applies to all logged in accounts." }, + "turnOffBrowserBuiltInPasswordManagerSettings": { + "message": "Turn off your browser’s built in password manager settings to avoid conflicts." + }, + "turnOffBrowserBuiltInPasswordManagerSettingsLink": { + "message": "Edit browser settings." + }, "autofillOverlayVisibilityOff": { "message": "Off", "description": "Overlay setting select option for disabling autofill overlay" @@ -1131,6 +1143,9 @@ "faviconDesc": { "message": "Visa en identifierbar bild bredvid varje inloggning." }, + "faviconDescAlt": { + "message": "Show a recognizable image next to each login. Applies to all logged in accounts." + }, "enableBadgeCounter": { "message": "Visa aktivitetsräknaren" }, @@ -2538,36 +2553,12 @@ "message": "Bitwarden auto-fill menu available. Press the down arrow key to select.", "description": "Screen reader text for announcing when the overlay opens on the page" }, - "overrideBrowserAutofillTitle": { - "message": "Override browser auto-fill?", - "description": "Dialog title facilitating the ability to override a chrome browser's default autofill behavior" - }, - "overrideBrowserAutofillDescription": { - "message": "Leaving this setting off may cause conflicts between the Bitwarden auto-fill menu and your browser’s.", - "description": "Dialog message facilitating the ability to override a chrome browser's default autofill behavior" - }, - "overrideBrowserAutofillPrivacyRequiredDescription": { - "message": "Leaving this setting off may cause conflicts between the Bitwarden auto-fill menu and your browser’s. Turning this on will restart the Bitwarden extension.", - "description": "Dialog message facilitating the ability to override a chrome browser's default autofill behavior" - }, "turnOn": { "message": "Turn on" }, "ignore": { "message": "Ignorera" }, - "overrideBrowserAutoFillSettings": { - "message": "Override browser auto-fill settings", - "description": "Label for the setting that allows overriding the default browser autofill settings" - }, - "extensionPrivacyPermissionNotGrantedTitle": { - "message": "Unable to override browser auto-fill", - "description": "Title for the dialog that appears when the user has not granted the extension permission to set privacy settings" - }, - "extensionPrivacyPermissionNotGrantedDescription": { - "message": "Bitwarden must have access to the extension's privacy permission to override browser auto-fill settings.", - "description": "Description for the dialog that appears when the user has not granted the extension permission to set privacy settings" - }, "importData": { "message": "Importera data", "description": "Used for the header of the import dialog, the import button and within the file-password-prompt" @@ -2679,6 +2670,9 @@ "confirmFilePassword": { "message": "Confirm file password" }, + "typePasskey": { + "message": "Passkey" + }, "passkeyNotCopied": { "message": "Passkey will not be copied" }, @@ -2793,5 +2787,35 @@ }, "lastPassYubikeyDesc": { "message": "Insert the YubiKey associated with your LastPass account into your computer's USB port, then touch its button." + }, + "switchAccount": { + "message": "Switch account" + }, + "switchAccounts": { + "message": "Switch accounts" + }, + "switchToAccount": { + "message": "Switch to account" + }, + "activeAccount": { + "message": "Active account" + }, + "accountLimitReached": { + "message": "Account limit reached. Log out of an account to add another." + }, + "active": { + "message": "active" + }, + "locked": { + "message": "locked" + }, + "unlocked": { + "message": "unlocked" + }, + "server": { + "message": "server" + }, + "hostedAt": { + "message": "hosted at" } } diff --git a/apps/browser/src/_locales/te/messages.json b/apps/browser/src/_locales/te/messages.json index 5f243afe040..ab3933bf5b7 100644 --- a/apps/browser/src/_locales/te/messages.json +++ b/apps/browser/src/_locales/te/messages.json @@ -365,6 +365,9 @@ "unlockMethodNeededToChangeTimeoutActionDesc": { "message": "Set up an unlock method to change your vault timeout action." }, + "unlockMethodNeeded": { + "message": "Set up an unlock method in Settings" + }, "rateExtension": { "message": "Rate the extension" }, @@ -405,6 +408,9 @@ "lockNow": { "message": "Lock now" }, + "lockAll": { + "message": "Lock all" + }, "immediately": { "message": "Immediately" }, @@ -1027,6 +1033,12 @@ "showAutoFillMenuOnFormFieldsDescAlt": { "message": "Applies to all logged in accounts." }, + "turnOffBrowserBuiltInPasswordManagerSettings": { + "message": "Turn off your browser’s built in password manager settings to avoid conflicts." + }, + "turnOffBrowserBuiltInPasswordManagerSettingsLink": { + "message": "Edit browser settings." + }, "autofillOverlayVisibilityOff": { "message": "Off", "description": "Overlay setting select option for disabling autofill overlay" @@ -1131,6 +1143,9 @@ "faviconDesc": { "message": "Show a recognizable image next to each login." }, + "faviconDescAlt": { + "message": "Show a recognizable image next to each login. Applies to all logged in accounts." + }, "enableBadgeCounter": { "message": "Show badge counter" }, @@ -2538,36 +2553,12 @@ "message": "Bitwarden auto-fill menu available. Press the down arrow key to select.", "description": "Screen reader text for announcing when the overlay opens on the page" }, - "overrideBrowserAutofillTitle": { - "message": "Override browser auto-fill?", - "description": "Dialog title facilitating the ability to override a chrome browser's default autofill behavior" - }, - "overrideBrowserAutofillDescription": { - "message": "Leaving this setting off may cause conflicts between the Bitwarden auto-fill menu and your browser’s.", - "description": "Dialog message facilitating the ability to override a chrome browser's default autofill behavior" - }, - "overrideBrowserAutofillPrivacyRequiredDescription": { - "message": "Leaving this setting off may cause conflicts between the Bitwarden auto-fill menu and your browser’s. Turning this on will restart the Bitwarden extension.", - "description": "Dialog message facilitating the ability to override a chrome browser's default autofill behavior" - }, "turnOn": { "message": "Turn on" }, "ignore": { "message": "Ignore" }, - "overrideBrowserAutoFillSettings": { - "message": "Override browser auto-fill settings", - "description": "Label for the setting that allows overriding the default browser autofill settings" - }, - "extensionPrivacyPermissionNotGrantedTitle": { - "message": "Unable to override browser auto-fill", - "description": "Title for the dialog that appears when the user has not granted the extension permission to set privacy settings" - }, - "extensionPrivacyPermissionNotGrantedDescription": { - "message": "Bitwarden must have access to the extension's privacy permission to override browser auto-fill settings.", - "description": "Description for the dialog that appears when the user has not granted the extension permission to set privacy settings" - }, "importData": { "message": "Import data", "description": "Used for the header of the import dialog, the import button and within the file-password-prompt" @@ -2679,6 +2670,9 @@ "confirmFilePassword": { "message": "Confirm file password" }, + "typePasskey": { + "message": "Passkey" + }, "passkeyNotCopied": { "message": "Passkey will not be copied" }, @@ -2793,5 +2787,35 @@ }, "lastPassYubikeyDesc": { "message": "Insert the YubiKey associated with your LastPass account into your computer's USB port, then touch its button." + }, + "switchAccount": { + "message": "Switch account" + }, + "switchAccounts": { + "message": "Switch accounts" + }, + "switchToAccount": { + "message": "Switch to account" + }, + "activeAccount": { + "message": "Active account" + }, + "accountLimitReached": { + "message": "Account limit reached. Log out of an account to add another." + }, + "active": { + "message": "active" + }, + "locked": { + "message": "locked" + }, + "unlocked": { + "message": "unlocked" + }, + "server": { + "message": "server" + }, + "hostedAt": { + "message": "hosted at" } } diff --git a/apps/browser/src/_locales/th/messages.json b/apps/browser/src/_locales/th/messages.json index cc03c106e31..938a206a62c 100644 --- a/apps/browser/src/_locales/th/messages.json +++ b/apps/browser/src/_locales/th/messages.json @@ -365,6 +365,9 @@ "unlockMethodNeededToChangeTimeoutActionDesc": { "message": "Set up an unlock method to change your vault timeout action." }, + "unlockMethodNeeded": { + "message": "Set up an unlock method in Settings" + }, "rateExtension": { "message": "Rate the Extension" }, @@ -405,6 +408,9 @@ "lockNow": { "message": "Lock Now" }, + "lockAll": { + "message": "Lock all" + }, "immediately": { "message": "ทันที" }, @@ -1027,6 +1033,12 @@ "showAutoFillMenuOnFormFieldsDescAlt": { "message": "Applies to all logged in accounts." }, + "turnOffBrowserBuiltInPasswordManagerSettings": { + "message": "Turn off your browser’s built in password manager settings to avoid conflicts." + }, + "turnOffBrowserBuiltInPasswordManagerSettingsLink": { + "message": "Edit browser settings." + }, "autofillOverlayVisibilityOff": { "message": "Off", "description": "Overlay setting select option for disabling autofill overlay" @@ -1131,6 +1143,9 @@ "faviconDesc": { "message": "Show a recognizable image next to each login." }, + "faviconDescAlt": { + "message": "Show a recognizable image next to each login. Applies to all logged in accounts." + }, "enableBadgeCounter": { "message": "Show badge counter" }, @@ -2538,36 +2553,12 @@ "message": "Bitwarden auto-fill menu available. Press the down arrow key to select.", "description": "Screen reader text for announcing when the overlay opens on the page" }, - "overrideBrowserAutofillTitle": { - "message": "Override browser auto-fill?", - "description": "Dialog title facilitating the ability to override a chrome browser's default autofill behavior" - }, - "overrideBrowserAutofillDescription": { - "message": "Leaving this setting off may cause conflicts between the Bitwarden auto-fill menu and your browser’s.", - "description": "Dialog message facilitating the ability to override a chrome browser's default autofill behavior" - }, - "overrideBrowserAutofillPrivacyRequiredDescription": { - "message": "Leaving this setting off may cause conflicts between the Bitwarden auto-fill menu and your browser’s. Turning this on will restart the Bitwarden extension.", - "description": "Dialog message facilitating the ability to override a chrome browser's default autofill behavior" - }, "turnOn": { "message": "Turn on" }, "ignore": { "message": "Ignore" }, - "overrideBrowserAutoFillSettings": { - "message": "Override browser auto-fill settings", - "description": "Label for the setting that allows overriding the default browser autofill settings" - }, - "extensionPrivacyPermissionNotGrantedTitle": { - "message": "Unable to override browser auto-fill", - "description": "Title for the dialog that appears when the user has not granted the extension permission to set privacy settings" - }, - "extensionPrivacyPermissionNotGrantedDescription": { - "message": "Bitwarden must have access to the extension's privacy permission to override browser auto-fill settings.", - "description": "Description for the dialog that appears when the user has not granted the extension permission to set privacy settings" - }, "importData": { "message": "Import data", "description": "Used for the header of the import dialog, the import button and within the file-password-prompt" @@ -2679,6 +2670,9 @@ "confirmFilePassword": { "message": "Confirm file password" }, + "typePasskey": { + "message": "Passkey" + }, "passkeyNotCopied": { "message": "Passkey will not be copied" }, @@ -2793,5 +2787,35 @@ }, "lastPassYubikeyDesc": { "message": "Insert the YubiKey associated with your LastPass account into your computer's USB port, then touch its button." + }, + "switchAccount": { + "message": "Switch account" + }, + "switchAccounts": { + "message": "Switch accounts" + }, + "switchToAccount": { + "message": "Switch to account" + }, + "activeAccount": { + "message": "Active account" + }, + "accountLimitReached": { + "message": "Account limit reached. Log out of an account to add another." + }, + "active": { + "message": "active" + }, + "locked": { + "message": "locked" + }, + "unlocked": { + "message": "unlocked" + }, + "server": { + "message": "server" + }, + "hostedAt": { + "message": "hosted at" } } diff --git a/apps/browser/src/_locales/tr/messages.json b/apps/browser/src/_locales/tr/messages.json index af6f2053972..04ec9976bf4 100644 --- a/apps/browser/src/_locales/tr/messages.json +++ b/apps/browser/src/_locales/tr/messages.json @@ -365,6 +365,9 @@ "unlockMethodNeededToChangeTimeoutActionDesc": { "message": "Kasa zaman aşımı eyleminizi değiştirmek için kilit açma yönteminizi ayarlayın." }, + "unlockMethodNeeded": { + "message": "Ayarlar'da bir kilit açma yöntemi ayarlayın" + }, "rateExtension": { "message": "Uzantıyı değerlendirin" }, @@ -405,6 +408,9 @@ "lockNow": { "message": "Şimdi kilitle" }, + "lockAll": { + "message": "Tümünü kilitle" + }, "immediately": { "message": "Hemen" }, @@ -1027,6 +1033,12 @@ "showAutoFillMenuOnFormFieldsDescAlt": { "message": "Oturum açmış tüm hesaplara uygulanır." }, + "turnOffBrowserBuiltInPasswordManagerSettings": { + "message": "Turn off your browser’s built in password manager settings to avoid conflicts." + }, + "turnOffBrowserBuiltInPasswordManagerSettingsLink": { + "message": "Edit browser settings." + }, "autofillOverlayVisibilityOff": { "message": "Kapalı", "description": "Overlay setting select option for disabling autofill overlay" @@ -1131,6 +1143,9 @@ "faviconDesc": { "message": "Hesapların yanında tanıdık görseller göster." }, + "faviconDescAlt": { + "message": "Her hesabın yanında tanınabilir bir resim göster. Oturum açmış tüm hesaplar için geçerlidir." + }, "enableBadgeCounter": { "message": "Rozet sayacını göster" }, @@ -2499,7 +2514,7 @@ "description": "Page title for the iframe containing the overlay button" }, "toggleBitwardenVaultOverlay": { - "message": "Toggle Bitwarden auto-fill menu", + "message": "Bitwarden otomatik doldurma menüsünü aç/kapat", "description": "Screen reader and tool tip label for the overlay button" }, "bitwardenVault": { @@ -2507,15 +2522,15 @@ "description": "Page title in overlay" }, "unlockYourAccountToViewMatchingLogins": { - "message": "Eşleşen giriş bilgilerini görüntülemek için hesabınızın kilidini açın", + "message": "Eşleşen hesaplarınızı görmek için hesabınızın kilidini açın", "description": "Text to display in overlay when the account is locked." }, "unlockAccount": { - "message": "Hesabın kilidini açın", + "message": "Hesap kilidini aç", "description": "Button text to display in overlay when the account is locked." }, "fillCredentialsFor": { - "message": "Kimlik bilgilerini doldurun", + "message": "Bilgileri doldur", "description": "Screen reader text for when overlay item is in focused" }, "partialUsername": { @@ -2523,50 +2538,26 @@ "description": "Screen reader text for when a login item is focused where a partial username is displayed. SR will announce this phrase before reading the text of the partial username" }, "noItemsToShow": { - "message": "Gösterilecek öğe yok", + "message": "Gösterilecek kayıt yok", "description": "Text to show in overlay if there are no matching items" }, "newItem": { - "message": "Yeni öge", + "message": "Yeni kayıt", "description": "Button text to display in overlay when there are no matching items" }, "addNewVaultItem": { - "message": "Add new vault item", + "message": "Kasaya yeni kayıt ekle", "description": "Screen reader text (aria-label) for new item button in overlay" }, "bitwardenOverlayMenuAvailable": { "message": "Bitwarden auto-fill menu available. Press the down arrow key to select.", "description": "Screen reader text for announcing when the overlay opens on the page" }, - "overrideBrowserAutofillTitle": { - "message": "Override browser auto-fill?", - "description": "Dialog title facilitating the ability to override a chrome browser's default autofill behavior" - }, - "overrideBrowserAutofillDescription": { - "message": "Bu ayarı kapalı bırakmak, Bitwarden otomatik doldurma menüsü ile tarayıcınızınki arasında çakışmalara neden olabilir.", - "description": "Dialog message facilitating the ability to override a chrome browser's default autofill behavior" - }, - "overrideBrowserAutofillPrivacyRequiredDescription": { - "message": "Leaving this setting off may cause conflicts between the Bitwarden auto-fill menu and your browser’s. Turning this on will restart the Bitwarden extension.", - "description": "Dialog message facilitating the ability to override a chrome browser's default autofill behavior" - }, "turnOn": { - "message": "Turn on" + "message": "Aç" }, "ignore": { - "message": "Yoksay" - }, - "overrideBrowserAutoFillSettings": { - "message": "Tarayıcının otomatik doldurma ayarlarını geçersiz kıl", - "description": "Label for the setting that allows overriding the default browser autofill settings" - }, - "extensionPrivacyPermissionNotGrantedTitle": { - "message": "Unable to override browser auto-fill", - "description": "Title for the dialog that appears when the user has not granted the extension permission to set privacy settings" - }, - "extensionPrivacyPermissionNotGrantedDescription": { - "message": "Bitwarden must have access to the extension's privacy permission to override browser auto-fill settings.", - "description": "Description for the dialog that appears when the user has not granted the extension permission to set privacy settings" + "message": "Yok say" }, "importData": { "message": "Verileri içe aktar", @@ -2679,6 +2670,9 @@ "confirmFilePassword": { "message": "Dosya parolasını onaylayın" }, + "typePasskey": { + "message": "Geçiş anahtarı" + }, "passkeyNotCopied": { "message": "Passkey will not be copied" }, @@ -2793,5 +2787,35 @@ }, "lastPassYubikeyDesc": { "message": "LastPass hesabınızla ilişkili YubiKey'i bilgisayarınızın USB bağlantı noktasına takın ve ardından düğmesine dokunun." + }, + "switchAccount": { + "message": "Hesabı değiştir" + }, + "switchAccounts": { + "message": "Hesapları değiştir" + }, + "switchToAccount": { + "message": "Hesaba geç" + }, + "activeAccount": { + "message": "Aktif hesap" + }, + "accountLimitReached": { + "message": "Hesap sınırına ulaştınız. Yeni hesap eklemek için hesaplardan birinden çıkış yapın." + }, + "active": { + "message": "aktif" + }, + "locked": { + "message": "kilitli" + }, + "unlocked": { + "message": "kilitli değil" + }, + "server": { + "message": "sunucu" + }, + "hostedAt": { + "message": "konum" } } diff --git a/apps/browser/src/_locales/uk/messages.json b/apps/browser/src/_locales/uk/messages.json index 295b51dc2fa..279bd8c28ba 100644 --- a/apps/browser/src/_locales/uk/messages.json +++ b/apps/browser/src/_locales/uk/messages.json @@ -365,6 +365,9 @@ "unlockMethodNeededToChangeTimeoutActionDesc": { "message": "Налаштуйте спосіб розблокування, щоб змінити час очікування сховища." }, + "unlockMethodNeeded": { + "message": "Встановіть спосіб розблокування в налаштуваннях" + }, "rateExtension": { "message": "Оцінити розширення" }, @@ -405,6 +408,9 @@ "lockNow": { "message": "Блокувати зараз" }, + "lockAll": { + "message": "Заблокувати все" + }, "immediately": { "message": "Негайно" }, @@ -1027,6 +1033,12 @@ "showAutoFillMenuOnFormFieldsDescAlt": { "message": "Застосовується для всіх облікових записів, до яких виконано вхід." }, + "turnOffBrowserBuiltInPasswordManagerSettings": { + "message": "Turn off your browser’s built in password manager settings to avoid conflicts." + }, + "turnOffBrowserBuiltInPasswordManagerSettingsLink": { + "message": "Edit browser settings." + }, "autofillOverlayVisibilityOff": { "message": "Вимк", "description": "Overlay setting select option for disabling autofill overlay" @@ -1131,6 +1143,9 @@ "faviconDesc": { "message": "Показувати впізнаване зображення біля кожного запису." }, + "faviconDescAlt": { + "message": "Показати впізнаване зображення поруч з кожним записом. Застосовується для всіх облікових записів, до яких виконано вхід." + }, "enableBadgeCounter": { "message": "Показувати лічильник" }, @@ -2538,36 +2553,12 @@ "message": "Доступне меню автозаповнення Bitwarden. Натисніть клавішу стрілки для вибору.", "description": "Screen reader text for announcing when the overlay opens on the page" }, - "overrideBrowserAutofillTitle": { - "message": "Перевизначити автозаповнення браузера?", - "description": "Dialog title facilitating the ability to override a chrome browser's default autofill behavior" - }, - "overrideBrowserAutofillDescription": { - "message": "Якщо залишити цей параметр вимкненим, може виникнути конфлікт між меню автозаповнення Bitwarden та вашого браузера.", - "description": "Dialog message facilitating the ability to override a chrome browser's default autofill behavior" - }, - "overrideBrowserAutofillPrivacyRequiredDescription": { - "message": "Якщо залишити цей параметр вимкненим, може виникнути конфлікт між меню автозаповнення Bitwarden та вашого браузера. Якщо увімкнути, розширення Bitwarden перезапуститься.", - "description": "Dialog message facilitating the ability to override a chrome browser's default autofill behavior" - }, "turnOn": { "message": "Увімкнути" }, "ignore": { "message": "Ігнорувати" }, - "overrideBrowserAutoFillSettings": { - "message": "Перевизначити налаштування автозаповнення браузера", - "description": "Label for the setting that allows overriding the default browser autofill settings" - }, - "extensionPrivacyPermissionNotGrantedTitle": { - "message": "Неможливо перевизначити автозаповнення браузера", - "description": "Title for the dialog that appears when the user has not granted the extension permission to set privacy settings" - }, - "extensionPrivacyPermissionNotGrantedDescription": { - "message": "Bitwarden повинен мати доступ до дозволу приватності розширення, щоб перевизначати налаштування автозаповнення браузера.", - "description": "Description for the dialog that appears when the user has not granted the extension permission to set privacy settings" - }, "importData": { "message": "Імпортувати дані", "description": "Used for the header of the import dialog, the import button and within the file-password-prompt" @@ -2679,6 +2670,9 @@ "confirmFilePassword": { "message": "Підтвердьте пароль файлу" }, + "typePasskey": { + "message": "Ключ доступу" + }, "passkeyNotCopied": { "message": "Ключ доступу не буде скопійовано" }, @@ -2793,5 +2787,35 @@ }, "lastPassYubikeyDesc": { "message": "Вставте YubiKey пов'язаний з обліковим записом LastPass в USB порт вашого комп'ютера, потім торкніться його кнопки." + }, + "switchAccount": { + "message": "Перемкнути обліковий запис" + }, + "switchAccounts": { + "message": "Перемкнути облікові записи" + }, + "switchToAccount": { + "message": "Перемкнути на обліковий запис" + }, + "activeAccount": { + "message": "Активний обліковий запис" + }, + "accountLimitReached": { + "message": "Досягнуто обмеження облікового запису. Вийдіть, щоб додати інший обліковий запис." + }, + "active": { + "message": "активний" + }, + "locked": { + "message": "заблокований" + }, + "unlocked": { + "message": "розблокований" + }, + "server": { + "message": "сервер" + }, + "hostedAt": { + "message": "розміщений на" } } diff --git a/apps/browser/src/_locales/vi/messages.json b/apps/browser/src/_locales/vi/messages.json index 46b9e71df67..4aa9aca6d4d 100644 --- a/apps/browser/src/_locales/vi/messages.json +++ b/apps/browser/src/_locales/vi/messages.json @@ -365,6 +365,9 @@ "unlockMethodNeededToChangeTimeoutActionDesc": { "message": "Set up an unlock method to change your vault timeout action." }, + "unlockMethodNeeded": { + "message": "Set up an unlock method in Settings" + }, "rateExtension": { "message": "Đánh giá tiện ích mở rộng" }, @@ -405,6 +408,9 @@ "lockNow": { "message": "Khóa ngay" }, + "lockAll": { + "message": "Lock all" + }, "immediately": { "message": "Ngay lập tức" }, @@ -1027,6 +1033,12 @@ "showAutoFillMenuOnFormFieldsDescAlt": { "message": "Applies to all logged in accounts." }, + "turnOffBrowserBuiltInPasswordManagerSettings": { + "message": "Turn off your browser’s built in password manager settings to avoid conflicts." + }, + "turnOffBrowserBuiltInPasswordManagerSettingsLink": { + "message": "Edit browser settings." + }, "autofillOverlayVisibilityOff": { "message": "Off", "description": "Overlay setting select option for disabling autofill overlay" @@ -1131,6 +1143,9 @@ "faviconDesc": { "message": "Hiển thị một ảnh nhận dạng bên cạnh mỗi lần đăng nhập." }, + "faviconDescAlt": { + "message": "Show a recognizable image next to each login. Applies to all logged in accounts." + }, "enableBadgeCounter": { "message": "Hiển thị biểu tượng bộ đếm" }, @@ -2538,36 +2553,12 @@ "message": "Bitwarden auto-fill menu available. Press the down arrow key to select.", "description": "Screen reader text for announcing when the overlay opens on the page" }, - "overrideBrowserAutofillTitle": { - "message": "Override browser auto-fill?", - "description": "Dialog title facilitating the ability to override a chrome browser's default autofill behavior" - }, - "overrideBrowserAutofillDescription": { - "message": "Leaving this setting off may cause conflicts between the Bitwarden auto-fill menu and your browser’s.", - "description": "Dialog message facilitating the ability to override a chrome browser's default autofill behavior" - }, - "overrideBrowserAutofillPrivacyRequiredDescription": { - "message": "Leaving this setting off may cause conflicts between the Bitwarden auto-fill menu and your browser’s. Turning this on will restart the Bitwarden extension.", - "description": "Dialog message facilitating the ability to override a chrome browser's default autofill behavior" - }, "turnOn": { "message": "Turn on" }, "ignore": { "message": "Ignore" }, - "overrideBrowserAutoFillSettings": { - "message": "Override browser auto-fill settings", - "description": "Label for the setting that allows overriding the default browser autofill settings" - }, - "extensionPrivacyPermissionNotGrantedTitle": { - "message": "Unable to override browser auto-fill", - "description": "Title for the dialog that appears when the user has not granted the extension permission to set privacy settings" - }, - "extensionPrivacyPermissionNotGrantedDescription": { - "message": "Bitwarden must have access to the extension's privacy permission to override browser auto-fill settings.", - "description": "Description for the dialog that appears when the user has not granted the extension permission to set privacy settings" - }, "importData": { "message": "Import data", "description": "Used for the header of the import dialog, the import button and within the file-password-prompt" @@ -2679,6 +2670,9 @@ "confirmFilePassword": { "message": "Confirm file password" }, + "typePasskey": { + "message": "Passkey" + }, "passkeyNotCopied": { "message": "Passkey will not be copied" }, @@ -2793,5 +2787,35 @@ }, "lastPassYubikeyDesc": { "message": "Insert the YubiKey associated with your LastPass account into your computer's USB port, then touch its button." + }, + "switchAccount": { + "message": "Switch account" + }, + "switchAccounts": { + "message": "Switch accounts" + }, + "switchToAccount": { + "message": "Switch to account" + }, + "activeAccount": { + "message": "Active account" + }, + "accountLimitReached": { + "message": "Account limit reached. Log out of an account to add another." + }, + "active": { + "message": "active" + }, + "locked": { + "message": "locked" + }, + "unlocked": { + "message": "unlocked" + }, + "server": { + "message": "server" + }, + "hostedAt": { + "message": "hosted at" } } diff --git a/apps/browser/src/_locales/zh_CN/messages.json b/apps/browser/src/_locales/zh_CN/messages.json index 6f3d57c77e7..8db5c7fb35c 100644 --- a/apps/browser/src/_locales/zh_CN/messages.json +++ b/apps/browser/src/_locales/zh_CN/messages.json @@ -365,6 +365,9 @@ "unlockMethodNeededToChangeTimeoutActionDesc": { "message": "设置一个解锁方式以更改您的密码库超时动作。" }, + "unlockMethodNeeded": { + "message": "在设置中设置解锁方法" + }, "rateExtension": { "message": "为本扩展打分" }, @@ -405,6 +408,9 @@ "lockNow": { "message": "立即锁定" }, + "lockAll": { + "message": "全部锁定" + }, "immediately": { "message": "立即" }, @@ -1027,6 +1033,12 @@ "showAutoFillMenuOnFormFieldsDescAlt": { "message": "适用于所有已登录的账户。" }, + "turnOffBrowserBuiltInPasswordManagerSettings": { + "message": "Turn off your browser’s built in password manager settings to avoid conflicts." + }, + "turnOffBrowserBuiltInPasswordManagerSettingsLink": { + "message": "编辑浏览器设置。" + }, "autofillOverlayVisibilityOff": { "message": "关闭", "description": "Overlay setting select option for disabling autofill overlay" @@ -1131,6 +1143,9 @@ "faviconDesc": { "message": "在每个登录项目旁边显示一个可识别的图像。" }, + "faviconDescAlt": { + "message": "在每个登录的旁边显示一个可识别的图像。适用于所有已登录的账户。" + }, "enableBadgeCounter": { "message": "显示角标计数器" }, @@ -2538,36 +2553,12 @@ "message": "Bitwarden 自动填充菜单可用。按向下箭头键进行选择。", "description": "Screen reader text for announcing when the overlay opens on the page" }, - "overrideBrowserAutofillTitle": { - "message": "覆盖浏览器自动填充吗?", - "description": "Dialog title facilitating the ability to override a chrome browser's default autofill behavior" - }, - "overrideBrowserAutofillDescription": { - "message": "关闭此设置可能会导致 Bitwarden 自动填充菜单与浏览器菜单发生冲突。", - "description": "Dialog message facilitating the ability to override a chrome browser's default autofill behavior" - }, - "overrideBrowserAutofillPrivacyRequiredDescription": { - "message": "关闭此设置可能会导致 Bitwarden 自动填充菜单与浏览器菜单发生冲突。打开此选项将重新启动 Bitwarden 扩展。", - "description": "Dialog message facilitating the ability to override a chrome browser's default autofill behavior" - }, "turnOn": { "message": "开启" }, "ignore": { "message": "忽略" }, - "overrideBrowserAutoFillSettings": { - "message": "覆盖浏览器的自动填充设置", - "description": "Label for the setting that allows overriding the default browser autofill settings" - }, - "extensionPrivacyPermissionNotGrantedTitle": { - "message": "无法覆盖浏览器自动填充", - "description": "Title for the dialog that appears when the user has not granted the extension permission to set privacy settings" - }, - "extensionPrivacyPermissionNotGrantedDescription": { - "message": "Bitwarden 必须拥有访问扩展隐私的权限,才能覆盖浏览器的自动填充设置。", - "description": "Description for the dialog that appears when the user has not granted the extension permission to set privacy settings" - }, "importData": { "message": "导入数据", "description": "Used for the header of the import dialog, the import button and within the file-password-prompt" @@ -2679,6 +2670,9 @@ "confirmFilePassword": { "message": "确认文件密码" }, + "typePasskey": { + "message": "通行密钥" + }, "passkeyNotCopied": { "message": "通行密钥不会被复制" }, @@ -2793,5 +2787,35 @@ }, "lastPassYubikeyDesc": { "message": "将与您的 LastPass 账户关联的 YubiKey 插入计算机的 USB 端口,然后触摸其按钮。" + }, + "switchAccount": { + "message": "切换账户" + }, + "switchAccounts": { + "message": "切换账户" + }, + "switchToAccount": { + "message": "切换到账户" + }, + "activeAccount": { + "message": "活动账户" + }, + "accountLimitReached": { + "message": "已达到账户上限。请注销一个账户后再添加其他账户。" + }, + "active": { + "message": "已生效" + }, + "locked": { + "message": "已锁定" + }, + "unlocked": { + "message": "已解锁" + }, + "server": { + "message": "服务器" + }, + "hostedAt": { + "message": "托管于" } } diff --git a/apps/browser/src/_locales/zh_TW/messages.json b/apps/browser/src/_locales/zh_TW/messages.json index 492d6aec22a..51c319611b9 100644 --- a/apps/browser/src/_locales/zh_TW/messages.json +++ b/apps/browser/src/_locales/zh_TW/messages.json @@ -365,6 +365,9 @@ "unlockMethodNeededToChangeTimeoutActionDesc": { "message": "設定一個解鎖方式來變更您的密碼庫逾時動作。" }, + "unlockMethodNeeded": { + "message": "Set up an unlock method in Settings" + }, "rateExtension": { "message": "為本套件評分" }, @@ -405,6 +408,9 @@ "lockNow": { "message": "立即鎖定" }, + "lockAll": { + "message": "Lock all" + }, "immediately": { "message": "立即" }, @@ -1027,6 +1033,12 @@ "showAutoFillMenuOnFormFieldsDescAlt": { "message": "適用於所有已登入的帳戶。" }, + "turnOffBrowserBuiltInPasswordManagerSettings": { + "message": "Turn off your browser’s built in password manager settings to avoid conflicts." + }, + "turnOffBrowserBuiltInPasswordManagerSettingsLink": { + "message": "Edit browser settings." + }, "autofillOverlayVisibilityOff": { "message": "關閉", "description": "Overlay setting select option for disabling autofill overlay" @@ -1131,6 +1143,9 @@ "faviconDesc": { "message": "在每個登入資料旁顯示一個可辨識的圖片。" }, + "faviconDescAlt": { + "message": "Show a recognizable image next to each login. Applies to all logged in accounts." + }, "enableBadgeCounter": { "message": "顯示圖示計數器" }, @@ -2538,36 +2553,12 @@ "message": "Bitwarden 自動填入選單可用。按下箭頭鍵來選擇。", "description": "Screen reader text for announcing when the overlay opens on the page" }, - "overrideBrowserAutofillTitle": { - "message": "覆蓋瀏覽器的自動填入?", - "description": "Dialog title facilitating the ability to override a chrome browser's default autofill behavior" - }, - "overrideBrowserAutofillDescription": { - "message": "關閉此設定可能會導致 Bitwarden 自動填入選單與您的瀏覽器產生衝突。", - "description": "Dialog message facilitating the ability to override a chrome browser's default autofill behavior" - }, - "overrideBrowserAutofillPrivacyRequiredDescription": { - "message": "關閉此設定可能會導致 Bitwarden 自動填入選單與您的瀏覽器產生衝突。開啟此選項將會重新啟動 Bitwarden 擴充功能。", - "description": "Dialog message facilitating the ability to override a chrome browser's default autofill behavior" - }, "turnOn": { "message": "開啟" }, "ignore": { "message": "忽略" }, - "overrideBrowserAutoFillSettings": { - "message": "覆蓋瀏覽器的自動填入設定", - "description": "Label for the setting that allows overriding the default browser autofill settings" - }, - "extensionPrivacyPermissionNotGrantedTitle": { - "message": "無法覆蓋瀏覽器的自動填入", - "description": "Title for the dialog that appears when the user has not granted the extension permission to set privacy settings" - }, - "extensionPrivacyPermissionNotGrantedDescription": { - "message": "Bitwarden 必須取得擴充功能的隱私權限才能覆蓋瀏覽器的自動填入設定。", - "description": "Description for the dialog that appears when the user has not granted the extension permission to set privacy settings" - }, "importData": { "message": "匯入資料", "description": "Used for the header of the import dialog, the import button and within the file-password-prompt" @@ -2679,6 +2670,9 @@ "confirmFilePassword": { "message": "確認檔案密碼" }, + "typePasskey": { + "message": "Passkey" + }, "passkeyNotCopied": { "message": "密碼金鑰不會被複製" }, @@ -2793,5 +2787,35 @@ }, "lastPassYubikeyDesc": { "message": "將與您的 LastPass 帳戶關聯的 YubiKey 插入電腦的 USB 連接埠,然後觸摸其按鈕。" + }, + "switchAccount": { + "message": "Switch account" + }, + "switchAccounts": { + "message": "Switch accounts" + }, + "switchToAccount": { + "message": "Switch to account" + }, + "activeAccount": { + "message": "Active account" + }, + "accountLimitReached": { + "message": "Account limit reached. Log out of an account to add another." + }, + "active": { + "message": "active" + }, + "locked": { + "message": "locked" + }, + "unlocked": { + "message": "unlocked" + }, + "server": { + "message": "server" + }, + "hostedAt": { + "message": "hosted at" } } diff --git a/apps/browser/src/admin-console/background/service-factories/organization-service.factory.ts b/apps/browser/src/admin-console/background/service-factories/organization-service.factory.ts index a050dc22ecc..0b9dc08ad2c 100644 --- a/apps/browser/src/admin-console/background/service-factories/organization-service.factory.ts +++ b/apps/browser/src/admin-console/background/service-factories/organization-service.factory.ts @@ -18,12 +18,12 @@ export type OrganizationServiceInitOptions = OrganizationServiceFactoryOptions & export function organizationServiceFactory( cache: { organizationService?: AbstractOrganizationService } & CachedServices, - opts: OrganizationServiceInitOptions + opts: OrganizationServiceInitOptions, ): Promise { return factory( cache, "organizationService", opts, - async () => new BrowserOrganizationService(await stateServiceFactory(cache, opts)) + async () => new BrowserOrganizationService(await stateServiceFactory(cache, opts)), ); } diff --git a/apps/browser/src/admin-console/background/service-factories/policy-service.factory.ts b/apps/browser/src/admin-console/background/service-factories/policy-service.factory.ts index 89f4a667f8d..6d4e8995942 100644 --- a/apps/browser/src/admin-console/background/service-factories/policy-service.factory.ts +++ b/apps/browser/src/admin-console/background/service-factories/policy-service.factory.ts @@ -24,7 +24,7 @@ export type PolicyServiceInitOptions = PolicyServiceFactoryOptions & export function policyServiceFactory( cache: { policyService?: AbstractPolicyService } & CachedServices, - opts: PolicyServiceInitOptions + opts: PolicyServiceInitOptions, ): Promise { return factory( cache, @@ -33,7 +33,7 @@ export function policyServiceFactory( async () => new BrowserPolicyService( await stateServiceFactory(cache, opts), - await organizationServiceFactory(cache, opts) - ) + await organizationServiceFactory(cache, opts), + ), ); } diff --git a/apps/browser/src/admin-console/services/browser-policy.service.ts b/apps/browser/src/admin-console/services/browser-policy.service.ts index 74aa0f546af..0fc924fc243 100644 --- a/apps/browser/src/admin-console/services/browser-policy.service.ts +++ b/apps/browser/src/admin-console/services/browser-policy.service.ts @@ -38,7 +38,7 @@ export class BrowserPolicyService extends PolicyService { if (activated === undefined) { this.stateService.setActivateAutoFillOnPageLoadFromPolicy(!autofillEnabled); } - }) + }), ); } } diff --git a/apps/browser/src/auth/background/service-factories/account-service.factory.ts b/apps/browser/src/auth/background/service-factories/account-service.factory.ts index 3a180df1564..9b7ad05ec49 100644 --- a/apps/browser/src/auth/background/service-factories/account-service.factory.ts +++ b/apps/browser/src/auth/background/service-factories/account-service.factory.ts @@ -28,7 +28,7 @@ export type AccountServiceInitOptions = AccountServiceFactoryOptions & export function accountServiceFactory( cache: { accountService?: AccountService } & CachedServices, - opts: AccountServiceInitOptions + opts: AccountServiceInitOptions, ): Promise { return factory( cache, @@ -38,7 +38,7 @@ export function accountServiceFactory( new AccountServiceImplementation( await messagingServiceFactory(cache, opts), await logServiceFactory(cache, opts), - await globalStateProviderFactory(cache, opts) - ) + await globalStateProviderFactory(cache, opts), + ), ); } diff --git a/apps/browser/src/auth/background/service-factories/auth-request-crypto-service.factory.ts b/apps/browser/src/auth/background/service-factories/auth-request-crypto-service.factory.ts index e1757f98129..ce6c0d78f03 100644 --- a/apps/browser/src/auth/background/service-factories/auth-request-crypto-service.factory.ts +++ b/apps/browser/src/auth/background/service-factories/auth-request-crypto-service.factory.ts @@ -18,12 +18,12 @@ export type AuthRequestCryptoServiceInitOptions = AuthRequestCryptoServiceFactor export function authRequestCryptoServiceFactory( cache: { authRequestCryptoService?: AuthRequestCryptoServiceAbstraction } & CachedServices, - opts: AuthRequestCryptoServiceInitOptions + opts: AuthRequestCryptoServiceInitOptions, ): Promise { return factory( cache, "authRequestCryptoService", opts, - async () => new AuthRequestCryptoServiceImplementation(await cryptoServiceFactory(cache, opts)) + async () => new AuthRequestCryptoServiceImplementation(await cryptoServiceFactory(cache, opts)), ); } diff --git a/apps/browser/src/auth/background/service-factories/auth-service.factory.ts b/apps/browser/src/auth/background/service-factories/auth-service.factory.ts index 6aaeb476369..31d338fecc7 100644 --- a/apps/browser/src/auth/background/service-factories/auth-service.factory.ts +++ b/apps/browser/src/auth/background/service-factories/auth-service.factory.ts @@ -89,7 +89,7 @@ export type AuthServiceInitOptions = AuthServiceFactoyOptions & export function authServiceFactory( cache: { authService?: AbstractAuthService } & CachedServices, - opts: AuthServiceInitOptions + opts: AuthServiceInitOptions, ): Promise { return factory( cache, @@ -113,7 +113,7 @@ export function authServiceFactory( await passwordStrengthServiceFactory(cache, opts), await policyServiceFactory(cache, opts), await deviceTrustCryptoServiceFactory(cache, opts), - await authRequestCryptoServiceFactory(cache, opts) - ) + await authRequestCryptoServiceFactory(cache, opts), + ), ); } diff --git a/apps/browser/src/auth/background/service-factories/device-trust-crypto-service.factory.ts b/apps/browser/src/auth/background/service-factories/device-trust-crypto-service.factory.ts index 430d50fea75..8458c40b9bb 100644 --- a/apps/browser/src/auth/background/service-factories/device-trust-crypto-service.factory.ts +++ b/apps/browser/src/auth/background/service-factories/device-trust-crypto-service.factory.ts @@ -53,7 +53,7 @@ export type DeviceTrustCryptoServiceInitOptions = DeviceTrustCryptoServiceFactor export function deviceTrustCryptoServiceFactory( cache: { deviceTrustCryptoService?: DeviceTrustCryptoServiceAbstraction } & CachedServices, - opts: DeviceTrustCryptoServiceInitOptions + opts: DeviceTrustCryptoServiceInitOptions, ): Promise { return factory( cache, @@ -68,7 +68,7 @@ export function deviceTrustCryptoServiceFactory( await appIdServiceFactory(cache, opts), await devicesApiServiceFactory(cache, opts), await i18nServiceFactory(cache, opts), - await platformUtilsServiceFactory(cache, opts) - ) + await platformUtilsServiceFactory(cache, opts), + ), ); } diff --git a/apps/browser/src/auth/background/service-factories/key-connector-service.factory.ts b/apps/browser/src/auth/background/service-factories/key-connector-service.factory.ts index 25eb85e5568..625a14e30ea 100644 --- a/apps/browser/src/auth/background/service-factories/key-connector-service.factory.ts +++ b/apps/browser/src/auth/background/service-factories/key-connector-service.factory.ts @@ -50,7 +50,7 @@ export type KeyConnectorServiceInitOptions = KeyConnectorServiceFactoryOptions & export function keyConnectorServiceFactory( cache: { keyConnectorService?: AbstractKeyConnectorService } & CachedServices, - opts: KeyConnectorServiceInitOptions + opts: KeyConnectorServiceInitOptions, ): Promise { return factory( cache, @@ -65,7 +65,7 @@ export function keyConnectorServiceFactory( await logServiceFactory(cache, opts), await organizationServiceFactory(cache, opts), await cryptoFunctionServiceFactory(cache, opts), - opts.keyConnectorServiceOptions.logoutCallback - ) + opts.keyConnectorServiceOptions.logoutCallback, + ), ); } diff --git a/apps/browser/src/auth/background/service-factories/token-service.factory.ts b/apps/browser/src/auth/background/service-factories/token-service.factory.ts index 389f8d1541a..476b8e2d785 100644 --- a/apps/browser/src/auth/background/service-factories/token-service.factory.ts +++ b/apps/browser/src/auth/background/service-factories/token-service.factory.ts @@ -17,12 +17,12 @@ export type TokenServiceInitOptions = TokenServiceFactoryOptions & StateServiceI export function tokenServiceFactory( cache: { tokenService?: AbstractTokenService } & CachedServices, - opts: TokenServiceInitOptions + opts: TokenServiceInitOptions, ): Promise { return factory( cache, "tokenService", opts, - async () => new TokenService(await stateServiceFactory(cache, opts)) + async () => new TokenService(await stateServiceFactory(cache, opts)), ); } diff --git a/apps/browser/src/auth/background/service-factories/two-factor-service.factory.ts b/apps/browser/src/auth/background/service-factories/two-factor-service.factory.ts index 040a5edfb4a..1d79bbbaf1d 100644 --- a/apps/browser/src/auth/background/service-factories/two-factor-service.factory.ts +++ b/apps/browser/src/auth/background/service-factories/two-factor-service.factory.ts @@ -23,7 +23,7 @@ export type TwoFactorServiceInitOptions = TwoFactorServiceFactoryOptions & export async function twoFactorServiceFactory( cache: { twoFactorService?: AbstractTwoFactorService } & CachedServices, - opts: TwoFactorServiceInitOptions + opts: TwoFactorServiceInitOptions, ): Promise { const service = await factory( cache, @@ -32,8 +32,8 @@ export async function twoFactorServiceFactory( async () => new TwoFactorService( await i18nServiceFactory(cache, opts), - await platformUtilsServiceFactory(cache, opts) - ) + await platformUtilsServiceFactory(cache, opts), + ), ); service.init(); return service; diff --git a/apps/browser/src/auth/background/service-factories/user-verification-api-service.factory.ts b/apps/browser/src/auth/background/service-factories/user-verification-api-service.factory.ts index 01bfb0f13cb..f8d43877632 100644 --- a/apps/browser/src/auth/background/service-factories/user-verification-api-service.factory.ts +++ b/apps/browser/src/auth/background/service-factories/user-verification-api-service.factory.ts @@ -18,12 +18,12 @@ export type UserVerificationApiServiceInitOptions = UserVerificationApiServiceFa export function userVerificationApiServiceFactory( cache: { userVerificationApiService?: UserVerificationApiServiceAbstraction } & CachedServices, - opts: UserVerificationApiServiceInitOptions + opts: UserVerificationApiServiceInitOptions, ): Promise { return factory( cache, "userVerificationApiService", opts, - async () => new UserVerificationApiService(await apiServiceFactory(cache, opts)) + async () => new UserVerificationApiService(await apiServiceFactory(cache, opts)), ); } diff --git a/apps/browser/src/auth/background/service-factories/user-verification-service.factory.ts b/apps/browser/src/auth/background/service-factories/user-verification-service.factory.ts index 79d327c9485..d0cdfff9932 100644 --- a/apps/browser/src/auth/background/service-factories/user-verification-service.factory.ts +++ b/apps/browser/src/auth/background/service-factories/user-verification-service.factory.ts @@ -34,7 +34,7 @@ export type UserVerificationServiceInitOptions = UserVerificationServiceFactoryO export function userVerificationServiceFactory( cache: { userVerificationService?: AbstractUserVerificationService } & CachedServices, - opts: UserVerificationServiceInitOptions + opts: UserVerificationServiceInitOptions, ): Promise { return factory( cache, @@ -45,7 +45,7 @@ export function userVerificationServiceFactory( await stateServiceFactory(cache, opts), await cryptoServiceFactory(cache, opts), await i18nServiceFactory(cache, opts), - await userVerificationApiServiceFactory(cache, opts) - ) + await userVerificationApiServiceFactory(cache, opts), + ), ); } diff --git a/apps/browser/src/auth/guards/fido2-auth.guard.ts b/apps/browser/src/auth/guards/fido2-auth.guard.ts index 7ff0060663d..f6b560c71d6 100644 --- a/apps/browser/src/auth/guards/fido2-auth.guard.ts +++ b/apps/browser/src/auth/guards/fido2-auth.guard.ts @@ -17,7 +17,7 @@ import { BrowserRouterService } from "../../platform/popup/services/browser-rout */ export const fido2AuthGuard: CanActivateFn = async ( route: ActivatedRouteSnapshot, - state: RouterStateSnapshot + state: RouterStateSnapshot, ) => { const routerService = inject(BrowserRouterService); const authService = inject(AuthService); diff --git a/apps/browser/src/auth/popup/account-switching/account-switcher.component.html b/apps/browser/src/auth/popup/account-switching/account-switcher.component.html index bde9c3f6a02..f7421870143 100644 --- a/apps/browser/src/auth/popup/account-switching/account-switcher.component.html +++ b/apps/browser/src/auth/popup/account-switching/account-switcher.component.html @@ -1,7 +1,70 @@ -
-
- + +
+
-
+
{{ "switchAccounts" | i18n }}
+ + +
+ +
+
+
+
+
    +
  • + +
  • +
+ +

+ {{ "accountLimitReached" | i18n }} +

+
+ +
+
{{ "options" | i18n }}
+
+ + + +
+
+
+
diff --git a/apps/browser/src/auth/popup/account-switching/account-switcher.component.ts b/apps/browser/src/auth/popup/account-switching/account-switcher.component.ts index 8d4777c30d8..d7661fa9d44 100644 --- a/apps/browser/src/auth/popup/account-switching/account-switcher.component.ts +++ b/apps/browser/src/auth/popup/account-switching/account-switcher.component.ts @@ -1,20 +1,113 @@ -import { Component } from "@angular/core"; +import { Location } from "@angular/common"; +import { Component, OnDestroy, OnInit } from "@angular/core"; import { Router } from "@angular/router"; +import { Subject, firstValueFrom, map, switchMap, takeUntil } from "rxjs"; -import { AccountSwitcherService } from "../services/account-switcher.service"; +import { VaultTimeoutSettingsService } from "@bitwarden/common/abstractions/vault-timeout/vault-timeout-settings.service"; +import { VaultTimeoutService } from "@bitwarden/common/abstractions/vault-timeout/vault-timeout.service"; +import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; +import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status"; +import { VaultTimeoutAction } from "@bitwarden/common/enums/vault-timeout-action.enum"; +import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service"; +import { DialogService } from "@bitwarden/components"; + +import { AccountSwitcherService } from "./services/account-switcher.service"; @Component({ templateUrl: "account-switcher.component.html", }) -export class AccountSwitcherComponent { - constructor(private accountSwitcherService: AccountSwitcherService, private router: Router) {} +export class AccountSwitcherComponent implements OnInit, OnDestroy { + readonly lockedStatus = AuthenticationStatus.Locked; + private destroy$ = new Subject(); - get accountOptions$() { - return this.accountSwitcherService.accountOptions$; + loading = false; + activeUserCanLock = false; + + constructor( + private accountSwitcherService: AccountSwitcherService, + private accountService: AccountService, + private vaultTimeoutService: VaultTimeoutService, + private messagingService: MessagingService, + private dialogService: DialogService, + private location: Location, + private router: Router, + private vaultTimeoutSettingsService: VaultTimeoutSettingsService, + ) {} + + get accountLimit() { + return this.accountSwitcherService.ACCOUNT_LIMIT; } - async selectAccount(id: string) { - await this.accountSwitcherService.selectAccount(id); - this.router.navigate(["/home"]); + get specialAddAccountId() { + return this.accountSwitcherService.SPECIAL_ADD_ACCOUNT_ID; + } + + get availableAccounts$() { + return this.accountSwitcherService.availableAccounts$; + } + + get currentAccount$() { + return this.accountService.activeAccount$; + } + + async ngOnInit() { + const availableVaultTimeoutActions = await firstValueFrom( + this.vaultTimeoutSettingsService.availableVaultTimeoutActions$(), + ); + this.activeUserCanLock = availableVaultTimeoutActions.includes(VaultTimeoutAction.Lock); + } + + back() { + this.location.back(); + } + + async lock(userId?: string) { + this.loading = true; + await this.vaultTimeoutService.lock(userId ? userId : null); + this.router.navigate(["lock"]); + } + + async lockAll() { + this.loading = true; + this.availableAccounts$ + .pipe( + map((accounts) => + accounts + .filter((account) => account.id !== this.specialAddAccountId) + .sort((a, b) => (a.isActive ? -1 : b.isActive ? 1 : 0)) // Log out of the active account first + .map((account) => account.id), + ), + switchMap(async (accountIds) => { + if (accountIds.length === 0) { + return; + } + + // Must lock active (first) account first, then order doesn't matter + await this.vaultTimeoutService.lock(accountIds.shift()); + await Promise.all(accountIds.map((id) => this.vaultTimeoutService.lock(id))); + }), + takeUntil(this.destroy$), + ) + .subscribe(() => this.router.navigate(["lock"])); + } + + async logOut() { + this.loading = true; + const confirmed = await this.dialogService.openSimpleDialog({ + title: { key: "logOut" }, + content: { key: "logOutConfirmation" }, + type: "info", + }); + + if (confirmed) { + this.messagingService.send("logout"); + } + + this.router.navigate(["account-switcher"]); + } + + ngOnDestroy() { + this.destroy$.next(); + this.destroy$.complete(); } } diff --git a/apps/browser/src/auth/popup/account-switching/account.component.html b/apps/browser/src/auth/popup/account-switching/account.component.html new file mode 100644 index 00000000000..52edeabc7b5 --- /dev/null +++ b/apps/browser/src/auth/popup/account-switching/account.component.html @@ -0,0 +1,49 @@ + + + diff --git a/apps/browser/src/auth/popup/account-switching/account.component.ts b/apps/browser/src/auth/popup/account-switching/account.component.ts new file mode 100644 index 00000000000..2455aee1b86 --- /dev/null +++ b/apps/browser/src/auth/popup/account-switching/account.component.ts @@ -0,0 +1,55 @@ +import { CommonModule, Location } from "@angular/common"; +import { Component, EventEmitter, Input, Output } from "@angular/core"; +import { Router } from "@angular/router"; + +import { JslibModule } from "@bitwarden/angular/jslib.module"; +import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status"; +import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; +import { AvatarModule } from "@bitwarden/components"; + +import { AccountSwitcherService, AvailableAccount } from "./services/account-switcher.service"; + +@Component({ + standalone: true, + selector: "auth-account", + templateUrl: "account.component.html", + imports: [CommonModule, JslibModule, AvatarModule], +}) +export class AccountComponent { + @Input() account: AvailableAccount; + @Output() loading = new EventEmitter(); + + constructor( + private accountSwitcherService: AccountSwitcherService, + private router: Router, + private location: Location, + private i18nService: I18nService, + ) {} + + get specialAccountAddId() { + return this.accountSwitcherService.SPECIAL_ADD_ACCOUNT_ID; + } + + async selectAccount(id: string) { + this.loading.emit(true); + await this.accountSwitcherService.selectAccount(id); + + if (id === this.specialAccountAddId) { + this.router.navigate(["home"]); + } else { + this.location.back(); + } + } + + get status() { + if (this.account.isActive && this.account.status !== AuthenticationStatus.Locked) { + return { text: this.i18nService.t("active"), icon: "bwi-check-circle" }; + } + + if (this.account.status === AuthenticationStatus.Unlocked) { + return { text: this.i18nService.t("unlocked"), icon: "bwi-unlock" }; + } + + return { text: this.i18nService.t("locked"), icon: "bwi-lock" }; + } +} diff --git a/apps/browser/src/auth/popup/account-switching/current-account.component.html b/apps/browser/src/auth/popup/account-switching/current-account.component.html index bb482347e72..777d3af3047 100644 --- a/apps/browser/src/auth/popup/account-switching/current-account.component.html +++ b/apps/browser/src/auth/popup/account-switching/current-account.component.html @@ -1,5 +1,35 @@ -
-
- -
+
+ + + +
diff --git a/apps/browser/src/auth/popup/account-switching/current-account.component.ts b/apps/browser/src/auth/popup/account-switching/current-account.component.ts index cf50ab2798d..df6cb322507 100644 --- a/apps/browser/src/auth/popup/account-switching/current-account.component.ts +++ b/apps/browser/src/auth/popup/account-switching/current-account.component.ts @@ -1,20 +1,30 @@ +import { Location } from "@angular/common"; import { Component } from "@angular/core"; -import { Router } from "@angular/router"; +import { ActivatedRoute, Router } from "@angular/router"; -import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; +import { CurrentAccountService } from "./services/current-account.service"; @Component({ selector: "app-current-account", templateUrl: "current-account.component.html", }) export class CurrentAccountComponent { - constructor(private accountService: AccountService, private router: Router) {} + constructor( + private currentAccountService: CurrentAccountService, + private router: Router, + private location: Location, + private route: ActivatedRoute, + ) {} get currentAccount$() { - return this.accountService.activeAccount$; + return this.currentAccountService.currentAccount$; } - currentAccountClicked() { - this.router.navigate(["/account-switcher"]); + async currentAccountClicked() { + if (this.route.snapshot.data.state.includes("account-switcher")) { + this.location.back(); + } else { + this.router.navigate(["/account-switcher"]); + } } } diff --git a/apps/browser/src/auth/popup/services/account-switcher.service.spec.ts b/apps/browser/src/auth/popup/account-switching/services/account-switcher.service.spec.ts similarity index 61% rename from apps/browser/src/auth/popup/services/account-switcher.service.spec.ts rename to apps/browser/src/auth/popup/account-switching/services/account-switcher.service.spec.ts index d60166e9b40..9845fac1dad 100644 --- a/apps/browser/src/auth/popup/services/account-switcher.service.spec.ts +++ b/apps/browser/src/auth/popup/account-switching/services/account-switcher.service.spec.ts @@ -3,6 +3,8 @@ import { BehaviorSubject, firstValueFrom, timeout } from "rxjs"; import { AccountInfo, AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status"; +import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service"; +import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service"; import { StateService } from "@bitwarden/common/platform/abstractions/state.service"; import { UserId } from "@bitwarden/common/types/guid"; @@ -16,6 +18,8 @@ describe("AccountSwitcherService", () => { const accountService = mock(); const stateService = mock(); const messagingService = mock(); + const environmentService = mock(); + const logService = mock(); let accountSwitcherService: AccountSwitcherService; @@ -26,11 +30,13 @@ describe("AccountSwitcherService", () => { accountSwitcherService = new AccountSwitcherService( accountService, stateService, - messagingService + messagingService, + environmentService, + logService, ); }); - describe("accountOptions$", () => { + describe("availableAccounts$", () => { it("should return all accounts and an add account option when accounts are less than 5", async () => { const user1AccountInfo: AccountInfo = { name: "Test User 1", @@ -45,14 +51,14 @@ describe("AccountSwitcherService", () => { activeAccountSubject.next(Object.assign(user1AccountInfo, { id: "1" as UserId })); const accounts = await firstValueFrom( - accountSwitcherService.accountOptions$.pipe(timeout(20)) + accountSwitcherService.availableAccounts$.pipe(timeout(20)), ); expect(accounts).toHaveLength(2); expect(accounts[0].id).toBe("1"); - expect(accounts[0].isSelected).toBeTruthy(); + expect(accounts[0].isActive).toBeTruthy(); expect(accounts[1].id).toBe("addAccount"); - expect(accounts[1].isSelected).toBeFalsy(); + expect(accounts[1].isActive).toBeFalsy(); }); it.each([5, 6])( @@ -68,39 +74,70 @@ describe("AccountSwitcherService", () => { } accountsSubject.next(seedAccounts); activeAccountSubject.next( - Object.assign(seedAccounts["1" as UserId], { id: "1" as UserId }) + Object.assign(seedAccounts["1" as UserId], { id: "1" as UserId }), ); - const accounts = await firstValueFrom(accountSwitcherService.accountOptions$); + const accounts = await firstValueFrom(accountSwitcherService.availableAccounts$); expect(accounts).toHaveLength(numberOfAccounts); accounts.forEach((account) => { expect(account.id).not.toBe("addAccount"); }); - } + }, ); }); describe("selectAccount", () => { it("initiates an add account logic when add account is selected", async () => { - await accountSwitcherService.selectAccount("addAccount"); + let listener: ( + message: { command: string; userId: string }, + sender: unknown, + sendResponse: unknown, + ) => void = null; + jest.spyOn(chrome.runtime.onMessage, "addListener").mockImplementation((addedListener) => { + listener = addedListener; + }); - expect(stateService.setActiveUser).toBeCalledWith(null); - expect(stateService.setRememberedEmail).toBeCalledWith(null); + const removeListenerSpy = jest.spyOn(chrome.runtime.onMessage, "removeListener"); - expect(accountService.switchAccount).not.toBeCalled(); + const selectAccountPromise = accountSwitcherService.selectAccount("addAccount"); + + expect(listener).not.toBeNull(); + listener({ command: "switchAccountFinish", userId: null }, undefined, undefined); + + await selectAccountPromise; + + expect(accountService.switchAccount).toBeCalledWith(null); + + expect(removeListenerSpy).toBeCalledTimes(1); }); it("initiates an account switch with an account id", async () => { - await accountSwitcherService.selectAccount("1"); + let listener: ( + message: { command: string; userId: string }, + sender: unknown, + sendResponse: unknown, + ) => void; + jest.spyOn(chrome.runtime.onMessage, "addListener").mockImplementation((addedListener) => { + listener = addedListener; + }); + + const removeListenerSpy = jest.spyOn(chrome.runtime.onMessage, "removeListener"); + + const selectAccountPromise = accountSwitcherService.selectAccount("1"); + + listener({ command: "switchAccountFinish", userId: "1" }, undefined, undefined); + + await selectAccountPromise; expect(accountService.switchAccount).toBeCalledWith("1"); expect(messagingService.send).toBeCalledWith( "switchAccount", matches((payload) => { return payload.userId === "1"; - }) + }), ); + expect(removeListenerSpy).toBeCalledTimes(1); }); }); }); diff --git a/apps/browser/src/auth/popup/account-switching/services/account-switcher.service.ts b/apps/browser/src/auth/popup/account-switching/services/account-switcher.service.ts new file mode 100644 index 00000000000..19ede98e2c3 --- /dev/null +++ b/apps/browser/src/auth/popup/account-switching/services/account-switcher.service.ts @@ -0,0 +1,139 @@ +import { Injectable } from "@angular/core"; +import { + Observable, + combineLatest, + filter, + firstValueFrom, + map, + switchMap, + throwError, + timeout, +} from "rxjs"; + +import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; +import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status"; +import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service"; +import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; +import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service"; +import { StateService } from "@bitwarden/common/platform/abstractions/state.service"; +import { UserId } from "@bitwarden/common/types/guid"; + +import { fromChromeEvent } from "../../../../platform/browser/from-chrome-event"; + +export type AvailableAccount = { + name: string; + email?: string; + id: string; + isActive: boolean; + server?: string; + status?: AuthenticationStatus; + avatarColor?: string; +}; + +@Injectable({ + providedIn: "root", +}) +export class AccountSwitcherService { + static incompleteAccountSwitchError = "Account switch did not complete."; + + ACCOUNT_LIMIT = 5; + SPECIAL_ADD_ACCOUNT_ID = "addAccount"; + availableAccounts$: Observable; + + switchAccountFinished$: Observable; + + constructor( + private accountService: AccountService, + private stateService: StateService, + private messagingService: MessagingService, + private environmentService: EnvironmentService, + private logService: LogService, + ) { + this.availableAccounts$ = combineLatest([ + this.accountService.accounts$, + this.accountService.activeAccount$, + ]).pipe( + switchMap(async ([accounts, activeAccount]) => { + const accountEntries = Object.entries(accounts).filter( + ([_, account]) => account.status !== AuthenticationStatus.LoggedOut, + ); + // Accounts shouldn't ever be more than ACCOUNT_LIMIT but just in case do a greater than + const hasMaxAccounts = accountEntries.length >= this.ACCOUNT_LIMIT; + const options: AvailableAccount[] = await Promise.all( + accountEntries.map(async ([id, account]) => { + return { + name: account.name ?? account.email, + email: account.email, + id: id, + server: await this.environmentService.getHost(id), + status: account.status, + isActive: id === activeAccount?.id, + avatarColor: await this.stateService.getAvatarColor({ userId: id }), + }; + }), + ); + + if (!hasMaxAccounts) { + options.push({ + name: "Add account", + id: this.SPECIAL_ADD_ACCOUNT_ID, + isActive: activeAccount?.id == null, + }); + } + + return options; + }), + ); + + // Create a reusable observable that listens to the the switchAccountFinish message and returns the userId from the message + this.switchAccountFinished$ = fromChromeEvent<[message: { command: string; userId: string }]>( + chrome.runtime.onMessage, + ).pipe( + filter(([message]) => message.command === "switchAccountFinish"), + map(([message]) => message.userId), + ); + } + + get specialAccountAddId() { + return this.SPECIAL_ADD_ACCOUNT_ID; + } + + async selectAccount(id: string) { + if (id === this.SPECIAL_ADD_ACCOUNT_ID) { + id = null; + } + + // Creates a subscription to the switchAccountFinished observable but further + // filters it to only care about the current userId. + const switchAccountFinishedPromise = firstValueFrom( + this.switchAccountFinished$.pipe( + filter((userId) => userId === id), + timeout({ + // Much longer than account switching is expected to take for normal accounts + // but the account switching process includes a possible full sync so we need to account + // for very large accounts and want to still have a timeout + // to avoid a promise that might never resolve/reject + first: 60_000, + with: () => + throwError(() => new Error(AccountSwitcherService.incompleteAccountSwitchError)), + }), + ), + ); + + // Initiate the actions required to make account switching happen + await this.accountService.switchAccount(id as UserId); + this.messagingService.send("switchAccount", { userId: id }); // This message should cause switchAccountFinish to be sent + + // Wait until we recieve the switchAccountFinished message + await switchAccountFinishedPromise.catch((err) => { + if ( + err instanceof Error && + err.message === AccountSwitcherService.incompleteAccountSwitchError + ) { + this.logService.warning("message 'switchAccount' never responded."); + return; + } + throw err; + }); + } +} diff --git a/apps/browser/src/auth/popup/account-switching/services/current-account.service.ts b/apps/browser/src/auth/popup/account-switching/services/current-account.service.ts new file mode 100644 index 00000000000..21fc3bdac43 --- /dev/null +++ b/apps/browser/src/auth/popup/account-switching/services/current-account.service.ts @@ -0,0 +1,44 @@ +import { Injectable } from "@angular/core"; +import { Observable, switchMap } from "rxjs"; + +import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; +import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status"; +import { StateService } from "@bitwarden/common/platform/abstractions/state.service"; +import { UserId } from "@bitwarden/common/types/guid"; + +export type CurrentAccount = { + id: UserId; + name: string | undefined; + email: string; + status: AuthenticationStatus; + avatarColor: string; +}; + +@Injectable({ + providedIn: "root", +}) +export class CurrentAccountService { + currentAccount$: Observable; + + constructor( + private accountService: AccountService, + private stateService: StateService, + ) { + this.currentAccount$ = this.accountService.activeAccount$.pipe( + switchMap(async (account) => { + if (account == null) { + return null; + } + const currentAccount: CurrentAccount = { + id: account.id, + name: account.name || account.email, + email: account.email, + status: account.status, + avatarColor: await this.stateService.getAvatarColor({ userId: account.id }), + }; + + return currentAccount; + }), + ); + } +} diff --git a/apps/browser/src/auth/popup/environment.component.ts b/apps/browser/src/auth/popup/environment.component.ts index a5cbfe7c34b..6246aaf5db1 100644 --- a/apps/browser/src/auth/popup/environment.component.ts +++ b/apps/browser/src/auth/popup/environment.component.ts @@ -20,7 +20,7 @@ export class EnvironmentComponent extends BaseEnvironmentComponent implements On public environmentService: BrowserEnvironmentService, i18nService: I18nService, private router: Router, - modalService: ModalService + modalService: ModalService, ) { super(platformUtilsService, environmentService, i18nService, modalService); this.showCustom = true; diff --git a/apps/browser/src/auth/popup/hint.component.ts b/apps/browser/src/auth/popup/hint.component.ts index a743dc7da24..6de2c35301f 100644 --- a/apps/browser/src/auth/popup/hint.component.ts +++ b/apps/browser/src/auth/popup/hint.component.ts @@ -20,7 +20,7 @@ export class HintComponent extends BaseHintComponent { apiService: ApiService, logService: LogService, private route: ActivatedRoute, - loginService: LoginService + loginService: LoginService, ) { super(router, i18nService, apiService, platformUtilsService, logService, loginService); diff --git a/apps/browser/src/auth/popup/home.component.html b/apps/browser/src/auth/popup/home.component.html index 42fe3106ffb..f70a4c6d030 100644 --- a/apps/browser/src/auth/popup/home.component.html +++ b/apps/browser/src/auth/popup/home.component.html @@ -1,4 +1,4 @@ - +
diff --git a/apps/browser/src/auth/popup/lock.component.ts b/apps/browser/src/auth/popup/lock.component.ts index 4a0c752a5d2..0379886bc24 100644 --- a/apps/browser/src/auth/popup/lock.component.ts +++ b/apps/browser/src/auth/popup/lock.component.ts @@ -56,7 +56,7 @@ export class LockComponent extends BaseLockComponent { dialogService: DialogService, deviceTrustCryptoService: DeviceTrustCryptoServiceAbstraction, userVerificationService: UserVerificationService, - private routerService: BrowserRouterService + private routerService: BrowserRouterService, ) { super( router, @@ -76,7 +76,7 @@ export class LockComponent extends BaseLockComponent { passwordStrengthService, dialogService, deviceTrustCryptoService, - userVerificationService + userVerificationService, ); this.successRoute = "/tabs/current"; this.isInitialLockScreen = (window as any).previousPopupUrl == null; diff --git a/apps/browser/src/auth/popup/login-via-auth-request.component.ts b/apps/browser/src/auth/popup/login-via-auth-request.component.ts index b041f3fdd62..b5ff436f14a 100644 --- a/apps/browser/src/auth/popup/login-via-auth-request.component.ts +++ b/apps/browser/src/auth/popup/login-via-auth-request.component.ts @@ -48,7 +48,7 @@ export class LoginViaAuthRequestComponent syncService: SyncService, deviceTrustCryptoService: DeviceTrustCryptoServiceAbstraction, authReqCryptoService: AuthRequestCryptoServiceAbstraction, - private location: Location + private location: Location, ) { super( router, @@ -67,7 +67,7 @@ export class LoginViaAuthRequestComponent stateService, loginService, deviceTrustCryptoService, - authReqCryptoService + authReqCryptoService, ); super.onSuccessfulLogin = async () => { await syncService.fullSync(true); diff --git a/apps/browser/src/auth/popup/login.component.ts b/apps/browser/src/auth/popup/login.component.ts index b930f2747de..0d191c3bc49 100644 --- a/apps/browser/src/auth/popup/login.component.ts +++ b/apps/browser/src/auth/popup/login.component.ts @@ -45,7 +45,7 @@ export class LoginComponent extends BaseLoginComponent { formValidationErrorService: FormValidationErrorsService, route: ActivatedRoute, loginService: LoginService, - webAuthnLoginService: WebAuthnLoginServiceAbstraction + webAuthnLoginService: WebAuthnLoginServiceAbstraction, ) { super( devicesApiService, @@ -64,7 +64,7 @@ export class LoginComponent extends BaseLoginComponent { formValidationErrorService, route, loginService, - webAuthnLoginService + webAuthnLoginService, ); super.onSuccessfulLogin = async () => { await syncService.fullSync(true); @@ -123,7 +123,7 @@ export class LoginComponent extends BaseLoginComponent { "&codeChallenge=" + codeChallenge + "&email=" + - encodeURIComponent(this.formGroup.controls.email.value) + encodeURIComponent(this.formGroup.controls.email.value), ); } } diff --git a/apps/browser/src/auth/popup/register.component.ts b/apps/browser/src/auth/popup/register.component.ts index 837fcb2160f..4752c885728 100644 --- a/apps/browser/src/auth/popup/register.component.ts +++ b/apps/browser/src/auth/popup/register.component.ts @@ -38,7 +38,7 @@ export class RegisterComponent extends BaseRegisterComponent { environmentService: EnvironmentService, logService: LogService, auditService: AuditService, - dialogService: DialogService + dialogService: DialogService, ) { super( formValidationErrorService, @@ -54,7 +54,7 @@ export class RegisterComponent extends BaseRegisterComponent { environmentService, logService, auditService, - dialogService + dialogService, ); } } diff --git a/apps/browser/src/auth/popup/remove-password.component.html b/apps/browser/src/auth/popup/remove-password.component.html index 8024023976f..793bcff3e09 100644 --- a/apps/browser/src/auth/popup/remove-password.component.html +++ b/apps/browser/src/auth/popup/remove-password.component.html @@ -10,7 +10,7 @@
-

{{ "convertOrganizationEncryptionDesc" | i18n : organization.name }}

+

{{ "convertOrganizationEncryptionDesc" | i18n: organization.name }}

diff --git a/apps/browser/src/auth/popup/two-factor.component.ts b/apps/browser/src/auth/popup/two-factor.component.ts index a78f3d3de58..fde6f4ffc99 100644 --- a/apps/browser/src/auth/popup/two-factor.component.ts +++ b/apps/browser/src/auth/popup/two-factor.component.ts @@ -51,7 +51,7 @@ export class TwoFactorComponent extends BaseTwoFactorComponent { loginService: LoginService, configService: ConfigServiceAbstraction, private dialogService: DialogService, - @Inject(WINDOW) protected win: Window + @Inject(WINDOW) protected win: Window, ) { super( authService, @@ -67,7 +67,7 @@ export class TwoFactorComponent extends BaseTwoFactorComponent { twoFactorService, appIdService, loginService, - configService + configService, ); super.onSuccessfulLogin = async () => { syncService.fullSync(true); diff --git a/apps/browser/src/auth/popup/utils/auth-popout-window.spec.ts b/apps/browser/src/auth/popup/utils/auth-popout-window.spec.ts index e682181454e..673e9d5c335 100644 --- a/apps/browser/src/auth/popup/utils/auth-popout-window.spec.ts +++ b/apps/browser/src/auth/popup/utils/auth-popout-window.spec.ts @@ -103,7 +103,7 @@ describe("AuthPopoutWindow", () => { expect(openPopoutSpy).toHaveBeenCalledWith( "popup/index.html#/2fa;webAuthnResponse=data;remember=remember", - { singleActionKey: AuthPopoutType.twoFactorAuth } + { singleActionKey: AuthPopoutType.twoFactorAuth }, ); }); }); diff --git a/apps/browser/src/auth/popup/utils/auth-popout-window.ts b/apps/browser/src/auth/popup/utils/auth-popout-window.ts index d436aeb73eb..d01f9ad25e8 100644 --- a/apps/browser/src/auth/popup/utils/auth-popout-window.ts +++ b/apps/browser/src/auth/popup/utils/auth-popout-window.ts @@ -47,7 +47,7 @@ async function closeUnlockPopout() { async function openSsoAuthResultPopout(resultData: { code: string; state: string }) { const { code, state } = resultData; const authResultUrl = `popup/index.html#/sso?code=${encodeURIComponent( - code + code, )}&state=${encodeURIComponent(state)}`; await BrowserPopupUtils.openPopout(authResultUrl, { diff --git a/apps/browser/src/autofill/background/abstractions/overlay.background.ts b/apps/browser/src/autofill/background/abstractions/overlay.background.ts index 58aaa425ac3..3c2b70bb475 100644 --- a/apps/browser/src/autofill/background/abstractions/overlay.background.ts +++ b/apps/browser/src/autofill/background/abstractions/overlay.background.ts @@ -96,6 +96,7 @@ type OverlayButtonPortMessageHandlers = { [key: string]: CallableFunction; overlayButtonClicked: ({ port }: PortConnectionParam) => void; closeAutofillOverlay: ({ port }: PortConnectionParam) => void; + forceCloseAutofillOverlay: ({ port }: PortConnectionParam) => void; overlayPageBlurred: () => void; redirectOverlayFocusOut: ({ message, port }: PortOnMessageHandlerParams) => void; }; @@ -103,6 +104,7 @@ type OverlayButtonPortMessageHandlers = { type OverlayListPortMessageHandlers = { [key: string]: CallableFunction; checkAutofillOverlayButtonFocused: () => void; + forceCloseAutofillOverlay: ({ port }: PortConnectionParam) => void; overlayPageBlurred: () => void; unlockVault: ({ port }: PortConnectionParam) => void; fillSelectedListItem: ({ message, port }: PortOnMessageHandlerParams) => void; diff --git a/apps/browser/src/autofill/background/context-menus.background.ts b/apps/browser/src/autofill/background/context-menus.background.ts index add392f3293..af43c2500df 100644 --- a/apps/browser/src/autofill/background/context-menus.background.ts +++ b/apps/browser/src/autofill/background/context-menus.background.ts @@ -1,6 +1,6 @@ -import LockedVaultPendingNotificationsItem from "../../background/models/lockedVaultPendingNotificationsItem"; import { BrowserApi } from "../../platform/browser/browser-api"; import { ContextMenuClickedHandler } from "../browser/context-menu-clicked-handler"; +import LockedVaultPendingNotificationsItem from "../notification/models/locked-vault-pending-notifications-item"; export default class ContextMenusBackground { private contextMenus: typeof chrome.contextMenus; @@ -15,14 +15,14 @@ export default class ContextMenusBackground { } this.contextMenus.onClicked.addListener((info, tab) => - this.contextMenuClickedHandler.run(info, tab) + this.contextMenuClickedHandler.run(info, tab), ); BrowserApi.messageListener( "contextmenus.background", ( msg: { command: string; data: LockedVaultPendingNotificationsItem }, - sender: chrome.runtime.MessageSender + sender: chrome.runtime.MessageSender, ) => { if (msg.command === "unlockCompleted" && msg.data.target === "contextmenus.background") { this.contextMenuClickedHandler @@ -31,7 +31,7 @@ export default class ContextMenusBackground { BrowserApi.tabSendMessageData(sender.tab, "closeNotificationBar"); }); } - } + }, ); } } diff --git a/apps/browser/src/autofill/background/notification.background.spec.ts b/apps/browser/src/autofill/background/notification.background.spec.ts index 6a0a4a7345a..1a898666794 100644 --- a/apps/browser/src/autofill/background/notification.background.spec.ts +++ b/apps/browser/src/autofill/background/notification.background.spec.ts @@ -30,7 +30,7 @@ describe("NotificationBackground", () => { policyService, folderService, stateService, - environmentService + environmentService, ); }); diff --git a/apps/browser/src/autofill/background/notification.background.ts b/apps/browser/src/autofill/background/notification.background.ts index b4768e10e78..5f6522f59f7 100644 --- a/apps/browser/src/autofill/background/notification.background.ts +++ b/apps/browser/src/autofill/background/notification.background.ts @@ -13,17 +13,17 @@ import { CipherType } from "@bitwarden/common/vault/enums"; import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; import { openUnlockPopout } from "../../auth/popup/utils/auth-popout-window"; -import AddUnlockVaultQueueMessage from "../../background/models/add-unlock-vault-queue-message"; -import AddChangePasswordQueueMessage from "../../background/models/addChangePasswordQueueMessage"; -import AddLoginQueueMessage from "../../background/models/addLoginQueueMessage"; -import AddLoginRuntimeMessage from "../../background/models/addLoginRuntimeMessage"; -import ChangePasswordRuntimeMessage from "../../background/models/changePasswordRuntimeMessage"; -import LockedVaultPendingNotificationsItem from "../../background/models/lockedVaultPendingNotificationsItem"; -import { NotificationQueueMessageType } from "../../background/models/notificationQueueMessageType"; import { BrowserApi } from "../../platform/browser/browser-api"; import { BrowserStateService } from "../../platform/services/abstractions/browser-state.service"; import { openAddEditVaultItemPopout } from "../../vault/popup/utils/vault-popout-window"; import { NOTIFICATION_BAR_LIFESPAN_MS } from "../constants"; +import AddChangePasswordQueueMessage from "../notification/models/add-change-password-queue-message"; +import AddLoginQueueMessage from "../notification/models/add-login-queue-message"; +import AddLoginRuntimeMessage from "../notification/models/add-login-runtime-message"; +import AddUnlockVaultQueueMessage from "../notification/models/add-unlock-vault-queue-message"; +import ChangePasswordRuntimeMessage from "../notification/models/change-password-runtime-message"; +import LockedVaultPendingNotificationsItem from "../notification/models/locked-vault-pending-notifications-item"; +import { NotificationQueueMessageType } from "../notification/models/notification-queue-message-type"; import { AutofillService } from "../services/abstractions/autofill.service"; export default class NotificationBackground { @@ -40,7 +40,7 @@ export default class NotificationBackground { private policyService: PolicyService, private folderService: FolderService, private stateService: BrowserStateService, - private environmentService: EnvironmentService + private environmentService: EnvironmentService, ) {} async init() { @@ -52,7 +52,7 @@ export default class NotificationBackground { "notification.background", (msg: any, sender: chrome.runtime.MessageSender) => { this.processMessage(msg, sender); - } + }, ); this.cleanupNotificationQueue(); @@ -97,7 +97,7 @@ export default class NotificationBackground { await BrowserApi.tabSendMessageData( sender.tab, "addToLockedVaultPendingNotifications", - retryMessage + retryMessage, ); await openUnlockPopout(sender.tab); return; @@ -259,7 +259,7 @@ export default class NotificationBackground { const ciphers = await this.cipherService.getAllDecryptedForUrl(loginInfo.url); const usernameMatches = ciphers.filter( - (c) => c.login.username != null && c.login.username.toLowerCase() === normalizedUsername + (c) => c.login.username != null && c.login.username.toLowerCase() === normalizedUsername, ); if (usernameMatches.length === 0) { if (disabledAddLogin) { @@ -284,7 +284,7 @@ export default class NotificationBackground { loginDomain: string, loginInfo: AddLoginRuntimeMessage, tab: chrome.tabs.Tab, - isVaultLocked = false + isVaultLocked = false, ) { // remove any old messages for this tab this.removeTabFromNotificationQueue(tab); @@ -317,7 +317,7 @@ export default class NotificationBackground { const ciphers = await this.cipherService.getAllDecryptedForUrl(changeData.url); if (changeData.currentPassword != null) { const passwordMatches = ciphers.filter( - (c) => c.login.password === changeData.currentPassword + (c) => c.login.password === changeData.currentPassword, ); if (passwordMatches.length === 1) { id = passwordMatches[0].id; @@ -339,7 +339,7 @@ export default class NotificationBackground { */ private async unlockVault( message: { data?: { skipNotification?: boolean } }, - tab: chrome.tabs.Tab + tab: chrome.tabs.Tab, ) { if (message.data?.skipNotification) { return; @@ -363,7 +363,7 @@ export default class NotificationBackground { loginDomain: string, newPassword: string, tab: chrome.tabs.Tab, - isVaultLocked = false + isVaultLocked = false, ) { // remove any old messages for this tab this.removeTabFromNotificationQueue(tab); @@ -421,7 +421,7 @@ export default class NotificationBackground { const allCiphers = await this.cipherService.getAllDecryptedForUrl(queueMessage.uri); const existingCipher = allCiphers.find( (c) => - c.login.username != null && c.login.username.toLowerCase() === queueMessage.username + c.login.username != null && c.login.username.toLowerCase() === queueMessage.username, ); if (existingCipher != null) { @@ -449,7 +449,7 @@ export default class NotificationBackground { cipherView: CipherView, newPassword: string, edit: boolean, - tab: chrome.tabs.Tab + tab: chrome.tabs.Tab, ) { cipherView.login.password = newPassword; @@ -525,13 +525,13 @@ export default class NotificationBackground { private async removeIndividualVault(): Promise { return await firstValueFrom( - this.policyService.policyAppliesToActiveUser$(PolicyType.PersonalOwnership) + this.policyService.policyAppliesToActiveUser$(PolicyType.PersonalOwnership), ); } private async handleUnlockCompleted( messageData: LockedVaultPendingNotificationsItem, - sender: chrome.runtime.MessageSender + sender: chrome.runtime.MessageSender, ): Promise { if (messageData.commandToRetry.msg.command === "autofill_login") { await BrowserApi.tabSendMessageData(sender.tab, "closeNotificationBar"); @@ -543,7 +543,7 @@ export default class NotificationBackground { await this.processMessage( messageData.commandToRetry.msg.command, - messageData.commandToRetry.sender + messageData.commandToRetry.sender, ); } } diff --git a/apps/browser/src/autofill/background/overlay.background.spec.ts b/apps/browser/src/autofill/background/overlay.background.spec.ts index bc9c2374d56..92f0cc1380b 100644 --- a/apps/browser/src/autofill/background/overlay.background.spec.ts +++ b/apps/browser/src/autofill/background/overlay.background.spec.ts @@ -70,7 +70,7 @@ describe("OverlayBackground", () => { environmentService, settingsService, stateService, - i18nService + i18nService, ); overlayBackground.init(); }); @@ -163,7 +163,7 @@ describe("OverlayBackground", () => { new Map([ ["overlay-cipher-0", cipher2], ["overlay-cipher-1", cipher1], - ]) + ]), ); expect(overlayBackground["getOverlayCipherData"]).toHaveBeenCalled(); }); @@ -219,7 +219,7 @@ describe("OverlayBackground", () => { expect(BrowserApi.tabSendMessageData).toHaveBeenCalledWith( tab, "updateIsOverlayCiphersPopulated", - { isOverlayCiphersPopulated: true } + { isOverlayCiphersPopulated: true }, ); }); }); @@ -489,7 +489,7 @@ describe("OverlayBackground", () => { const returnValue = overlayBackground["handleExtensionMessage"]( message, sender, - sendResponse + sendResponse, ); expect(returnValue).toBe(undefined); @@ -507,7 +507,7 @@ describe("OverlayBackground", () => { const returnValue = overlayBackground["handleExtensionMessage"]( message, sender, - sendResponse + sendResponse, ); expect(returnValue).toBe(undefined); @@ -526,7 +526,7 @@ describe("OverlayBackground", () => { const returnValue = overlayBackground["handleExtensionMessage"]( message, sender, - sendResponse + sendResponse, ); expect(returnValue).toBe(true); @@ -555,7 +555,7 @@ describe("OverlayBackground", () => { isFocusingFieldElement: false, isOpeningFullOverlay: false, authStatus: AuthenticationStatus.Unlocked, - } + }, ); }); }); @@ -614,7 +614,7 @@ describe("OverlayBackground", () => { password: "password", }, }, - sender + sender, ); await flushPromises(); @@ -635,7 +635,7 @@ describe("OverlayBackground", () => { await flushPromises(); expect(overlayBackground["overlayVisibility"]).toBe( - AutofillOverlayVisibility.OnFieldFocus + AutofillOverlayVisibility.OnFieldFocus, ); }); @@ -645,7 +645,7 @@ describe("OverlayBackground", () => { sendExtensionRuntimeMessage( { command: "getAutofillOverlayVisibility" }, undefined, - sendMessageSpy + sendMessageSpy, ); await flushPromises(); @@ -851,7 +851,7 @@ describe("OverlayBackground", () => { it("stores the page details provided by the message by the tab id of the sender", () => { sendExtensionRuntimeMessage( { command: "collectPageDetailsResponse", details: pageDetails1 }, - sender + sender, ); expect(overlayBackground["pageDetailsForTab"][sender.tab.id]).toStrictEqual([ @@ -870,7 +870,7 @@ describe("OverlayBackground", () => { sendExtensionRuntimeMessage( { command: "collectPageDetailsResponse", details: pageDetails2 }, - sender + sender, ); expect(overlayBackground["pageDetailsForTab"][sender.tab.id]).toStrictEqual([ @@ -930,7 +930,7 @@ describe("OverlayBackground", () => { isFocusingFieldElement: true, isOpeningFullOverlay: false, authStatus: AuthenticationStatus.Unlocked, - } + }, ); }); }); @@ -1056,13 +1056,29 @@ describe("OverlayBackground", () => { describe("closeAutofillOverlay", () => { it("sends a `closeOverlay` message to the sender tab", () => { - jest.spyOn(BrowserApi, "tabSendMessage"); + jest.spyOn(BrowserApi, "tabSendMessageData"); sendPortMessage(buttonPortSpy, { command: "closeAutofillOverlay" }); - expect(BrowserApi.tabSendMessage).toHaveBeenCalledWith(buttonPortSpy.sender.tab, { - command: "closeAutofillOverlay", - }); + expect(BrowserApi.tabSendMessageData).toHaveBeenCalledWith( + buttonPortSpy.sender.tab, + "closeAutofillOverlay", + { forceCloseOverlay: false }, + ); + }); + }); + + describe("forceCloseAutofillOverlay", () => { + it("sends a `closeOverlay` message to the sender tab with a `forceCloseOverlay` flag of `true` set", () => { + jest.spyOn(BrowserApi, "tabSendMessageData"); + + sendPortMessage(buttonPortSpy, { command: "forceCloseAutofillOverlay" }); + + expect(BrowserApi.tabSendMessageData).toHaveBeenCalledWith( + buttonPortSpy.sender.tab, + "closeAutofillOverlay", + { forceCloseOverlay: true }, + ); }); }); @@ -1096,7 +1112,7 @@ describe("OverlayBackground", () => { expect(BrowserApi.tabSendMessageData).toHaveBeenCalledWith( buttonPortSpy.sender.tab, "redirectOverlayFocusOut", - { direction: RedirectFocusDirection.Next } + { direction: RedirectFocusDirection.Next }, ); }); }); @@ -1113,6 +1129,20 @@ describe("OverlayBackground", () => { }); }); + describe("forceCloseAutofillOverlay", () => { + it("sends a `closeOverlay` message to the sender tab with a `forceCloseOverlay` flag of `true` set", () => { + jest.spyOn(BrowserApi, "tabSendMessageData"); + + sendPortMessage(listPortSpy, { command: "forceCloseAutofillOverlay" }); + + expect(BrowserApi.tabSendMessageData).toHaveBeenCalledWith( + listPortSpy.sender.tab, + "closeAutofillOverlay", + { forceCloseOverlay: true }, + ); + }); + }); + describe("overlayPageBlurred", () => { it("checks on the focus state of the overlay button", () => { jest.spyOn(overlayBackground as any, "checkOverlayButtonFocused").mockImplementation(); @@ -1142,11 +1172,11 @@ describe("OverlayBackground", () => { sender: listPortSpy.sender, }, target: "overlay.background", - } + }, ); expect(overlayBackground["openUnlockPopout"]).toHaveBeenCalledWith( listPortSpy.sender.tab, - true + true, ); }); }); @@ -1160,7 +1190,7 @@ describe("OverlayBackground", () => { getLoginCiphersSpy = jest.spyOn(overlayBackground["overlayLoginCiphers"], "get"); isPasswordRepromptRequiredSpy = jest.spyOn( overlayBackground["autofillService"], - "isPasswordRepromptRequired" + "isPasswordRepromptRequired", ); doAutoFillSpy = jest.spyOn(overlayBackground["autofillService"], "doAutoFill"); }); @@ -1192,7 +1222,7 @@ describe("OverlayBackground", () => { expect(getLoginCiphersSpy).toHaveBeenCalled(); expect(isPasswordRepromptRequiredSpy).toHaveBeenCalledWith( cipher, - listPortSpy.sender.tab + listPortSpy.sender.tab, ); expect(doAutoFillSpy).not.toHaveBeenCalled(); }); @@ -1216,7 +1246,7 @@ describe("OverlayBackground", () => { expect(isPasswordRepromptRequiredSpy).toHaveBeenCalledWith( cipher2, - listPortSpy.sender.tab + listPortSpy.sender.tab, ); expect(doAutoFillSpy).toHaveBeenCalledWith({ tab: listPortSpy.sender.tab, @@ -1230,7 +1260,7 @@ describe("OverlayBackground", () => { ["overlay-cipher-2", cipher2], ["overlay-cipher-1", cipher1], ["overlay-cipher-3", cipher3], - ]).entries() + ]).entries(), ); }); }); @@ -1289,7 +1319,7 @@ describe("OverlayBackground", () => { { cipherId: cipher.id, action: SHOW_AUTOFILL_BUTTON, - } + }, ); }); }); @@ -1302,7 +1332,7 @@ describe("OverlayBackground", () => { }; const redirectOverlayFocusOutSpy = jest.spyOn( overlayBackground as any, - "redirectOverlayFocusOut" + "redirectOverlayFocusOut", ); sendPortMessage(listPortSpy, message); diff --git a/apps/browser/src/autofill/background/overlay.background.ts b/apps/browser/src/autofill/background/overlay.background.ts index 38554ee8aca..3d6f00ec10f 100644 --- a/apps/browser/src/autofill/background/overlay.background.ts +++ b/apps/browser/src/autofill/background/overlay.background.ts @@ -14,13 +14,13 @@ import { LoginUriView } from "@bitwarden/common/vault/models/view/login-uri.view import { LoginView } from "@bitwarden/common/vault/models/view/login.view"; import { openUnlockPopout } from "../../auth/popup/utils/auth-popout-window"; -import LockedVaultPendingNotificationsItem from "../../background/models/lockedVaultPendingNotificationsItem"; import { BrowserApi } from "../../platform/browser/browser-api"; import { openViewVaultItemPopout, openAddEditVaultItemPopout, } from "../../vault/popup/utils/vault-popout-window"; import { SHOW_AUTOFILL_BUTTON } from "../constants"; +import LockedVaultPendingNotificationsItem from "../notification/models/locked-vault-pending-notifications-item"; import { AutofillService, PageDetail } from "../services/abstractions/autofill.service"; import { AutofillOverlayElement, AutofillOverlayPort } from "../utils/autofill-overlay.enum"; @@ -68,11 +68,13 @@ class OverlayBackground implements OverlayBackgroundInterface { private readonly overlayButtonPortMessageHandlers: OverlayButtonPortMessageHandlers = { overlayButtonClicked: ({ port }) => this.handleOverlayButtonClicked(port), closeAutofillOverlay: ({ port }) => this.closeOverlay(port), + forceCloseAutofillOverlay: ({ port }) => this.closeOverlay(port, true), overlayPageBlurred: () => this.checkOverlayListFocused(), redirectOverlayFocusOut: ({ message, port }) => this.redirectOverlayFocusOut(message, port), }; private readonly overlayListPortMessageHandlers: OverlayListPortMessageHandlers = { checkAutofillOverlayButtonFocused: () => this.checkOverlayButtonFocused(), + forceCloseAutofillOverlay: ({ port }) => this.closeOverlay(port, true), overlayPageBlurred: () => this.checkOverlayButtonFocused(), unlockVault: ({ port }) => this.unlockVault(port), fillSelectedListItem: ({ message, port }) => this.fillSelectedOverlayListItem(message, port), @@ -88,7 +90,7 @@ class OverlayBackground implements OverlayBackgroundInterface { private environmentService: EnvironmentService, private settingsService: SettingsService, private stateService: StateService, - private i18nService: I18nService + private i18nService: I18nService, ) { this.iconsServerUrl = this.environmentService.getIconsUrl(); } @@ -130,7 +132,7 @@ class OverlayBackground implements OverlayBackgroundInterface { this.overlayLoginCiphers = new Map(); const ciphersViews = (await this.cipherService.getAllDecryptedForUrl(currentTab.url)).sort( - (a, b) => this.cipherService.sortCiphersByLastUsedThenName(a, b) + (a, b) => this.cipherService.sortCiphersByLastUsedThenName(a, b), ); for (let cipherIndex = 0; cipherIndex < ciphersViews.length; cipherIndex++) { this.overlayLoginCiphers.set(`overlay-cipher-${cipherIndex}`, ciphersViews[cipherIndex]); @@ -189,7 +191,7 @@ class OverlayBackground implements OverlayBackgroundInterface { */ private storePageDetails( message: OverlayBackgroundExtensionMessage, - sender: chrome.runtime.MessageSender + sender: chrome.runtime.MessageSender, ) { const pageDetails = { frameId: sender.frameId, @@ -214,7 +216,7 @@ class OverlayBackground implements OverlayBackgroundInterface { */ private async fillSelectedOverlayListItem( { overlayCipherId }: OverlayPortMessage, - { sender }: chrome.runtime.Port + { sender }: chrome.runtime.Port, ) { if (!overlayCipherId) { return; @@ -268,9 +270,10 @@ class OverlayBackground implements OverlayBackgroundInterface { * Sends a message to the sender tab to close the autofill overlay. * * @param sender - The sender of the port message + * @param forceCloseOverlay - Identifies whether the overlay should be force closed */ - private closeOverlay({ sender }: chrome.runtime.Port) { - BrowserApi.tabSendMessage(sender.tab, { command: "closeAutofillOverlay" }); + private closeOverlay({ sender }: chrome.runtime.Port, forceCloseOverlay = false) { + BrowserApi.tabSendMessageData(sender.tab, "closeAutofillOverlay", { forceCloseOverlay }); } /** @@ -528,7 +531,7 @@ class OverlayBackground implements OverlayBackgroundInterface { await BrowserApi.tabSendMessageData( sender.tab, "addToLockedVaultPendingNotifications", - retryMessage + retryMessage, ); await this.openUnlockPopout(sender.tab, true); } @@ -541,7 +544,7 @@ class OverlayBackground implements OverlayBackgroundInterface { */ private async viewSelectedCipher( { overlayCipherId }: OverlayPortMessage, - { sender }: chrome.runtime.Port + { sender }: chrome.runtime.Port, ) { const cipher = this.overlayLoginCiphers.get(overlayCipherId); if (!cipher) { @@ -609,7 +612,7 @@ class OverlayBackground implements OverlayBackgroundInterface { */ private redirectOverlayFocusOut( { direction }: OverlayPortMessage, - { sender }: chrome.runtime.Port + { sender }: chrome.runtime.Port, ) { if (!direction) { return; @@ -637,7 +640,7 @@ class OverlayBackground implements OverlayBackgroundInterface { */ private async addNewVaultItem( { login }: OverlayAddNewItemMessage, - sender: chrome.runtime.MessageSender + sender: chrome.runtime.MessageSender, ) { if (!login) { return; @@ -683,7 +686,7 @@ class OverlayBackground implements OverlayBackgroundInterface { private handleExtensionMessage = ( message: OverlayBackgroundExtensionMessage, sender: chrome.runtime.MessageSender, - sendResponse: (response?: any) => void + sendResponse: (response?: any) => void, ) => { const handler: CallableFunction | undefined = this.extensionMessageHandlers[message?.command]; if (!handler) { @@ -742,7 +745,7 @@ class OverlayBackground implements OverlayBackgroundInterface { */ private handleOverlayElementPortMessage = ( message: OverlayBackgroundExtensionMessage, - port: chrome.runtime.Port + port: chrome.runtime.Port, ) => { const command = message?.command; let handler: CallableFunction | undefined; diff --git a/apps/browser/src/autofill/background/service_factories/autofill-service.factory.ts b/apps/browser/src/autofill/background/service_factories/autofill-service.factory.ts index 4050d000817..acd9be2a8ea 100644 --- a/apps/browser/src/autofill/background/service_factories/autofill-service.factory.ts +++ b/apps/browser/src/autofill/background/service_factories/autofill-service.factory.ts @@ -47,7 +47,7 @@ export type AutoFillServiceInitOptions = AutoFillServiceOptions & export function autofillServiceFactory( cache: { autofillService?: AbstractAutoFillService } & CachedServices, - opts: AutoFillServiceInitOptions + opts: AutoFillServiceInitOptions, ): Promise { return factory( cache, @@ -61,7 +61,7 @@ export function autofillServiceFactory( await eventCollectionServiceFactory(cache, opts), await logServiceFactory(cache, opts), await settingsServiceFactory(cache, opts), - await userVerificationServiceFactory(cache, opts) - ) + await userVerificationServiceFactory(cache, opts), + ), ); } diff --git a/apps/browser/src/autofill/background/tabs.background.spec.ts b/apps/browser/src/autofill/background/tabs.background.spec.ts index e320f22248e..b3de1e96ce3 100644 --- a/apps/browser/src/autofill/background/tabs.background.spec.ts +++ b/apps/browser/src/autofill/background/tabs.background.spec.ts @@ -36,13 +36,13 @@ describe("TabsBackground", () => { it("sets up a window on focusChanged listener", () => { const handleWindowOnFocusChangedSpy = jest.spyOn( tabsBackgorund as any, - "handleWindowOnFocusChanged" + "handleWindowOnFocusChanged", ); tabsBackgorund.init(); expect(chrome.windows.onFocusChanged.addListener).toHaveBeenCalledWith( - handleWindowOnFocusChangedSpy + handleWindowOnFocusChangedSpy, ); }); }); diff --git a/apps/browser/src/autofill/background/tabs.background.ts b/apps/browser/src/autofill/background/tabs.background.ts index c47ef76f665..b095f99ce20 100644 --- a/apps/browser/src/autofill/background/tabs.background.ts +++ b/apps/browser/src/autofill/background/tabs.background.ts @@ -7,7 +7,7 @@ export default class TabsBackground { constructor( private main: MainBackground, private notificationBackground: NotificationBackground, - private overlayBackground: OverlayBackground + private overlayBackground: OverlayBackground, ) {} private focusedWindowId: number; @@ -74,7 +74,7 @@ export default class TabsBackground { private handleTabOnUpdated = async ( tabId: number, changeInfo: chrome.tabs.TabChangeInfo, - tab: chrome.tabs.Tab + tab: chrome.tabs.Tab, ) => { const removePageDetailsStatus = new Set(["loading", "unloaded"]); if (removePageDetailsStatus.has(changeInfo.status)) { diff --git a/apps/browser/src/background/webRequest.background.ts b/apps/browser/src/autofill/background/web-request.background.ts similarity index 93% rename from apps/browser/src/background/webRequest.background.ts rename to apps/browser/src/autofill/background/web-request.background.ts index a4ad0d37dc0..7bb3e679c79 100644 --- a/apps/browser/src/background/webRequest.background.ts +++ b/apps/browser/src/autofill/background/web-request.background.ts @@ -4,7 +4,7 @@ import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/pl import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; import { UriMatchType } from "@bitwarden/common/vault/enums"; -import { BrowserApi } from "../platform/browser/browser-api"; +import { BrowserApi } from "../../platform/browser/browser-api"; export default class WebRequestBackground { private pendingAuthRequests: any[] = []; @@ -14,7 +14,7 @@ export default class WebRequestBackground { constructor( platformUtilsService: PlatformUtilsService, private cipherService: CipherService, - private authService: AuthService + private authService: AuthService, ) { if (BrowserApi.manifestVersion === 2) { this.webRequest = (window as any).chrome.webRequest; @@ -48,7 +48,7 @@ export default class WebRequestBackground { } }, { urls: ["http://*/*", "https://*/*"] }, - [this.isFirefox ? "blocking" : "asyncBlocking"] + [this.isFirefox ? "blocking" : "asyncBlocking"], ); this.webRequest.onCompleted.addListener((details: any) => this.completeAuthRequest(details), { @@ -58,7 +58,7 @@ export default class WebRequestBackground { (details: any) => this.completeAuthRequest(details), { urls: ["http://*/*"], - } + }, ); } @@ -73,7 +73,7 @@ export default class WebRequestBackground { const ciphers = await this.cipherService.getAllDecryptedForUrl( domain, null, - UriMatchType.Host + UriMatchType.Host, ); if (ciphers == null || ciphers.length !== 1) { error(); diff --git a/apps/browser/src/autofill/browser/cipher-context-menu-handler.spec.ts b/apps/browser/src/autofill/browser/cipher-context-menu-handler.spec.ts index 70820f23ce4..ceec27a34e3 100644 --- a/apps/browser/src/autofill/browser/cipher-context-menu-handler.spec.ts +++ b/apps/browser/src/autofill/browser/cipher-context-menu-handler.spec.ts @@ -120,19 +120,19 @@ describe("CipherContextMenuHandler", () => { expect(mainContextMenuHandler.loadOptions).toHaveBeenCalledWith( "Test Cipher (Test Username)", "5", - loginCipher + loginCipher, ); expect(mainContextMenuHandler.loadOptions).toHaveBeenCalledWith( "Test Reprompt Cipher (Test Username)", "6", - repromptLoginCipher + repromptLoginCipher, ); expect(mainContextMenuHandler.loadOptions).toHaveBeenCalledWith( "Test Card Cipher", "7", - cardCipher + cardCipher, ); }); }); diff --git a/apps/browser/src/autofill/browser/cipher-context-menu-handler.ts b/apps/browser/src/autofill/browser/cipher-context-menu-handler.ts index ae2f22e244c..cfb966f87ec 100644 --- a/apps/browser/src/autofill/browser/cipher-context-menu-handler.ts +++ b/apps/browser/src/autofill/browser/cipher-context-menu-handler.ts @@ -38,7 +38,7 @@ export class CipherContextMenuHandler { constructor( private mainContextMenuHandler: MainContextMenuHandler, private authService: AuthService, - private cipherService: CipherService + private cipherService: CipherService, ) {} static async create(cachedServices: CachedServices) { @@ -74,7 +74,7 @@ export class CipherContextMenuHandler { return new CipherContextMenuHandler( await MainContextMenuHandler.mv3Create(cachedServices), await authServiceFactory(cachedServices, serviceOptions), - await cipherServiceFactory(cachedServices, serviceOptions) + await cipherServiceFactory(cachedServices, serviceOptions), ); } @@ -86,7 +86,7 @@ export class CipherContextMenuHandler { static async tabsOnActivatedListener( activeInfo: chrome.tabs.TabActiveInfo, - serviceCache: CachedServices + serviceCache: CachedServices, ) { const cipherContextMenuHandler = await CipherContextMenuHandler.create(serviceCache); const tab = await BrowserApi.getTab(activeInfo.tabId); @@ -96,7 +96,7 @@ export class CipherContextMenuHandler { static async tabsOnReplacedListener( addedTabId: number, removedTabId: number, - serviceCache: CachedServices + serviceCache: CachedServices, ) { const cipherContextMenuHandler = await CipherContextMenuHandler.create(serviceCache); const tab = await BrowserApi.getTab(addedTabId); @@ -107,7 +107,7 @@ export class CipherContextMenuHandler { tabId: number, changeInfo: chrome.tabs.TabChangeInfo, tab: chrome.tabs.Tab, - serviceCache: CachedServices + serviceCache: CachedServices, ) { if (changeInfo.status !== "complete") { return; @@ -119,7 +119,7 @@ export class CipherContextMenuHandler { static async messageListener( message: { command: string }, sender: chrome.runtime.MessageSender, - cachedServices: CachedServices + cachedServices: CachedServices, ) { if (!CipherContextMenuHandler.shouldListen(message)) { return; @@ -183,7 +183,7 @@ export class CipherContextMenuHandler { [CipherType.Login]: [], [CipherType.Card]: [], [CipherType.Identity]: [], - } + }, ); if (groupedCiphers[CipherType.Login].length === 0) { diff --git a/apps/browser/src/autofill/browser/context-menu-clicked-handler.spec.ts b/apps/browser/src/autofill/browser/context-menu-clicked-handler.spec.ts index ae5436f71a2..f2312a0d665 100644 --- a/apps/browser/src/autofill/browser/context-menu-clicked-handler.spec.ts +++ b/apps/browser/src/autofill/browser/context-menu-clicked-handler.spec.ts @@ -31,7 +31,7 @@ import { describe("ContextMenuClickedHandler", () => { const createData = ( menuItemId: chrome.contextMenus.OnClickData["menuItemId"], - parentMenuItemId?: chrome.contextMenus.OnClickData["parentMenuItemId"] + parentMenuItemId?: chrome.contextMenus.OnClickData["parentMenuItemId"], ): chrome.contextMenus.OnClickData => { return { menuItemId: menuItemId, @@ -52,7 +52,7 @@ describe("ContextMenuClickedHandler", () => { new Cipher({ id: id ?? "1", type: CipherType.Login, - } as any) + } as any), ); cipherView.login.username = username ?? "USERNAME"; @@ -92,7 +92,7 @@ describe("ContextMenuClickedHandler", () => { stateService, totpService, eventCollectionService, - userVerificationService + userVerificationService, ); }); diff --git a/apps/browser/src/autofill/browser/context-menu-clicked-handler.ts b/apps/browser/src/autofill/browser/context-menu-clicked-handler.ts index 19643eb5e5f..0de2009bbe1 100644 --- a/apps/browser/src/autofill/browser/context-menu-clicked-handler.ts +++ b/apps/browser/src/autofill/browser/context-menu-clicked-handler.ts @@ -18,7 +18,6 @@ import { } from "../../auth/background/service-factories/auth-service.factory"; import { userVerificationServiceFactory } from "../../auth/background/service-factories/user-verification-service.factory"; import { openUnlockPopout } from "../../auth/popup/utils/auth-popout-window"; -import LockedVaultPendingNotificationsItem from "../../background/models/lockedVaultPendingNotificationsItem"; import { eventCollectionServiceFactory } from "../../background/service-factories/event-collection-service.factory"; import { Account } from "../../models/account"; import { CachedServices } from "../../platform/background/service-factories/factory-options"; @@ -51,6 +50,7 @@ import { GENERATE_PASSWORD_ID, NOOP_COMMAND_SUFFIX, } from "../constants"; +import LockedVaultPendingNotificationsItem from "../notification/models/locked-vault-pending-notifications-item"; import { AutofillCipherTypeId } from "../types"; export type CopyToClipboardOptions = { text: string; tab: chrome.tabs.Tab }; @@ -72,7 +72,7 @@ export class ContextMenuClickedHandler { private stateService: StateService, private totpService: TotpService, private eventCollectionService: EventCollectionService, - private userVerificationService: UserVerificationService + private userVerificationService: UserVerificationService, ) {} static async mv3Create(cachedServices: CachedServices) { @@ -108,11 +108,11 @@ export class ContextMenuClickedHandler { const generatePasswordToClipboardCommand = new GeneratePasswordToClipboardCommand( await passwordGenerationServiceFactory(cachedServices, serviceOptions), - await stateServiceFactory(cachedServices, serviceOptions) + await stateServiceFactory(cachedServices, serviceOptions), ); const autofillCommand = new AutofillTabCommand( - await autofillServiceFactory(cachedServices, serviceOptions) + await autofillServiceFactory(cachedServices, serviceOptions), ); return new ContextMenuClickedHandler( @@ -124,14 +124,14 @@ export class ContextMenuClickedHandler { await stateServiceFactory(cachedServices, serviceOptions), await totpServiceFactory(cachedServices, serviceOptions), await eventCollectionServiceFactory(cachedServices, serviceOptions), - await userVerificationServiceFactory(cachedServices, serviceOptions) + await userVerificationServiceFactory(cachedServices, serviceOptions), ); } static async onClickedListener( info: chrome.contextMenus.OnClickData, tab?: chrome.tabs.Tab, - cachedServices: CachedServices = {} + cachedServices: CachedServices = {}, ) { const contextMenuClickedHandler = await ContextMenuClickedHandler.mv3Create(cachedServices); await contextMenuClickedHandler.run(info, tab); @@ -140,7 +140,7 @@ export class ContextMenuClickedHandler { static async messageListener( message: { command: string; data: LockedVaultPendingNotificationsItem }, sender: chrome.runtime.MessageSender, - cachedServices: CachedServices + cachedServices: CachedServices, ) { if ( message.command !== "unlockCompleted" || @@ -152,7 +152,7 @@ export class ContextMenuClickedHandler { const contextMenuClickedHandler = await ContextMenuClickedHandler.mv3Create(cachedServices); await contextMenuClickedHandler.run( message.data.commandToRetry.msg.data, - message.data.commandToRetry.sender.tab + message.data.commandToRetry.sender.tab, ); } @@ -189,7 +189,7 @@ export class ContextMenuClickedHandler { await BrowserApi.tabSendMessageData( tab, "addToLockedVaultPendingNotifications", - retryMessage + retryMessage, ); await openUnlockPopout(tab); @@ -201,7 +201,7 @@ export class ContextMenuClickedHandler { const menuItemId = (info.menuItemId as string).split("_")[1]; // We create all the ids, we can guarantee they are strings let cipher: CipherView | undefined; const isCreateCipherAction = [CREATE_LOGIN_ID, CREATE_IDENTITY_ID, CREATE_CARD_ID].includes( - menuItemId as string + menuItemId as string, ); if (isCreateCipherAction) { @@ -211,15 +211,15 @@ export class ContextMenuClickedHandler { info.parentMenuItemId === AUTOFILL_IDENTITY_ID ? [CipherType.Identity] : info.parentMenuItemId === AUTOFILL_CARD_ID - ? [CipherType.Card] - : []; + ? [CipherType.Card] + : []; // This NOOP item has come through which is generally only for no access state but since we got here // we are actually unlocked we will do our best to find a good match of an item to autofill this is useful // in scenarios like unlock on autofill const ciphers = await this.cipherService.getAllDecryptedForUrl( tab.url, - additionalCiphersToGet + additionalCiphersToGet, ); cipher = ciphers[0]; @@ -314,10 +314,10 @@ export class ContextMenuClickedHandler { return menuItemId === CREATE_IDENTITY_ID ? CipherType.Identity : menuItemId === CREATE_CARD_ID - ? CipherType.Card - : menuItemId === CREATE_LOGIN_ID - ? CipherType.Login - : null; + ? CipherType.Card + : menuItemId === CREATE_LOGIN_ID + ? CipherType.Login + : null; } private async getIdentifier(tab: chrome.tabs.Tab, info: chrome.contextMenus.OnClickData) { @@ -333,7 +333,7 @@ export class ContextMenuClickedHandler { } resolve(identifier); - } + }, ); }); } diff --git a/apps/browser/src/autofill/browser/main-context-menu-handler.spec.ts b/apps/browser/src/autofill/browser/main-context-menu-handler.spec.ts index aa5f43fbc94..8617d657e83 100644 --- a/apps/browser/src/autofill/browser/main-context-menu-handler.spec.ts +++ b/apps/browser/src/autofill/browser/main-context-menu-handler.spec.ts @@ -88,7 +88,7 @@ describe("context-menu", () => { id: id ?? "1", type: CipherType.Login, viewPassword: viewPassword ?? true, - } as any) + } as any), ); cipherView.login.username = username ?? "USERNAME"; cipherView.login.password = password ?? "PASSWORD"; @@ -113,7 +113,7 @@ describe("context-menu", () => { username: "", totp: "", viewPassword: false, - }) + }), ); expect(createSpy).toHaveBeenCalledTimes(1); diff --git a/apps/browser/src/autofill/browser/main-context-menu-handler.ts b/apps/browser/src/autofill/browser/main-context-menu-handler.ts index 1e90e90d0d1..f1686452ac4 100644 --- a/apps/browser/src/autofill/browser/main-context-menu-handler.ts +++ b/apps/browser/src/autofill/browser/main-context-menu-handler.ts @@ -46,7 +46,7 @@ export class MainContextMenuHandler { constructor( private stateService: BrowserStateService, private i18nService: I18nService, - private logService: LogService + private logService: LogService, ) { if (chrome.contextMenus) { this.create = (options) => { @@ -89,7 +89,7 @@ export class MainContextMenuHandler { return new MainContextMenuHandler( await stateServiceFactory(cachedServices, serviceOptions), await i18nServiceFactory(cachedServices, serviceOptions), - await logServiceFactory(cachedServices, serviceOptions) + await logServiceFactory(cachedServices, serviceOptions), ); } @@ -274,7 +274,7 @@ export class MainContextMenuHandler { const authed = await this.stateService.getIsAuthenticated(); await this.loadOptions( this.i18nService.t(authed ? "unlockVaultMenu" : "loginToVaultMenu"), - NOOP_COMMAND_SUFFIX + NOOP_COMMAND_SUFFIX, ); } } diff --git a/apps/browser/src/autofill/clipboard/generate-password-to-clipboard-command.ts b/apps/browser/src/autofill/clipboard/generate-password-to-clipboard-command.ts index 62110166658..5094f6701ab 100644 --- a/apps/browser/src/autofill/clipboard/generate-password-to-clipboard-command.ts +++ b/apps/browser/src/autofill/clipboard/generate-password-to-clipboard-command.ts @@ -9,7 +9,7 @@ import { copyToClipboard } from "./copy-to-clipboard-command"; export class GeneratePasswordToClipboardCommand { constructor( private passwordGenerationService: PasswordGenerationServiceAbstraction, - private stateService: BrowserStateService + private stateService: BrowserStateService, ) {} async generatePasswordToClipboard(tab: chrome.tabs.Tab) { diff --git a/apps/browser/src/autofill/commands/autofill-tab-command.ts b/apps/browser/src/autofill/commands/autofill-tab-command.ts index b51edd929ee..16ce40ff3d7 100644 --- a/apps/browser/src/autofill/commands/autofill-tab-command.ts +++ b/apps/browser/src/autofill/commands/autofill-tab-command.ts @@ -21,7 +21,7 @@ export class AutofillTabCommand { }, ], tab, - true + true, ); } @@ -64,7 +64,7 @@ export class AutofillTabCommand { } resolve(response); - } + }, ); }); } diff --git a/apps/browser/src/autofill/content/abstractions/autofill-init.ts b/apps/browser/src/autofill/content/abstractions/autofill-init.ts index e15cac15331..139099a4d58 100644 --- a/apps/browser/src/autofill/content/abstractions/autofill-init.ts +++ b/apps/browser/src/autofill/content/abstractions/autofill-init.ts @@ -16,6 +16,7 @@ type AutofillExtensionMessage = { isOverlayCiphersPopulated?: boolean; direction?: "previous" | "next"; isOpeningFullOverlay?: boolean; + forceCloseOverlay?: boolean; }; }; @@ -27,7 +28,7 @@ type AutofillExtensionMessageHandlers = { collectPageDetailsImmediately: ({ message }: AutofillExtensionMessageParam) => void; fillForm: ({ message }: AutofillExtensionMessageParam) => void; openAutofillOverlay: ({ message }: AutofillExtensionMessageParam) => void; - closeAutofillOverlay: () => void; + closeAutofillOverlay: ({ message }: AutofillExtensionMessageParam) => void; addNewVaultItemFromOverlay: () => void; redirectOverlayFocusOut: ({ message }: AutofillExtensionMessageParam) => void; updateIsOverlayCiphersPopulated: ({ message }: AutofillExtensionMessageParam) => void; diff --git a/apps/browser/src/autofill/content/autofill-init.spec.ts b/apps/browser/src/autofill/content/autofill-init.spec.ts index 93614970ec5..1524fdce100 100644 --- a/apps/browser/src/autofill/content/autofill-init.spec.ts +++ b/apps/browser/src/autofill/content/autofill-init.spec.ts @@ -41,7 +41,7 @@ describe("AutofillInit", () => { autofillInit["setupExtensionMessageListeners"](); expect(chrome.runtime.onMessage.addListener).toHaveBeenCalledWith( - autofillInit["handleExtensionMessage"] + autofillInit["handleExtensionMessage"], ); }); }); @@ -157,7 +157,7 @@ describe("AutofillInit", () => { sendExtensionRuntimeMessage( { command: "collectPageDetailsImmediately" }, sender, - sendResponse + sendResponse, ); await flushPromises(); @@ -186,7 +186,7 @@ describe("AutofillInit", () => { await flushPromises(); expect(autofillInit["insertAutofillContentService"].fillForm).not.toHaveBeenCalledWith( - fillScript + fillScript, ); }); @@ -199,7 +199,7 @@ describe("AutofillInit", () => { await flushPromises(); expect(autofillInit["insertAutofillContentService"].fillForm).toHaveBeenCalledWith( - fillScript + fillScript, ); }); @@ -220,11 +220,11 @@ describe("AutofillInit", () => { expect(autofillInit["updateOverlayIsCurrentlyFilling"]).toHaveBeenNthCalledWith(1, true); expect(autofillInit["insertAutofillContentService"].fillForm).toHaveBeenCalledWith( - fillScript + fillScript, ); expect(autofillInit["updateOverlayIsCurrentlyFilling"]).toHaveBeenNthCalledWith(2, false); expect( - autofillInit["autofillOverlayContentService"].focusMostRecentOverlayField + autofillInit["autofillOverlayContentService"].focusMostRecentOverlayField, ).toHaveBeenCalled(); }); @@ -247,14 +247,14 @@ describe("AutofillInit", () => { expect(newAutofillInit["updateOverlayIsCurrentlyFilling"]).toHaveBeenNthCalledWith( 1, - true + true, ); expect(newAutofillInit["insertAutofillContentService"].fillForm).toHaveBeenCalledWith( - fillScript + fillScript, ); expect(newAutofillInit["updateOverlayIsCurrentlyFilling"]).not.toHaveBeenNthCalledWith( 2, - false + false, ); }); }); @@ -282,7 +282,7 @@ describe("AutofillInit", () => { sendExtensionRuntimeMessage(message); expect( - autofillInit["autofillOverlayContentService"].openAutofillOverlay + autofillInit["autofillOverlayContentService"].openAutofillOverlay, ).toHaveBeenCalledWith({ isFocusingFieldElement: message.data.isFocusingFieldElement, isOpeningFullOverlay: message.data.isOpeningFullOverlay, @@ -297,16 +297,40 @@ describe("AutofillInit", () => { autofillInit["autofillOverlayContentService"].isCurrentlyFilling = false; }); + it("skips attempting to remove the overlay if the autofillOverlayContentService is not present", () => { + const newAutofillInit = new AutofillInit(undefined); + newAutofillInit.init(); + jest.spyOn(newAutofillInit as any, "removeAutofillOverlay"); + + sendExtensionRuntimeMessage({ + command: "closeAutofillOverlay", + data: { forceCloseOverlay: false }, + }); + + expect(newAutofillInit["autofillOverlayContentService"]).toBe(undefined); + }); + + it("removes the autofill overlay if the message flags a forced closure", () => { + sendExtensionRuntimeMessage({ + command: "closeAutofillOverlay", + data: { forceCloseOverlay: true }, + }); + + expect( + autofillInit["autofillOverlayContentService"].removeAutofillOverlay, + ).toHaveBeenCalled(); + }); + it("ignores the message if a field is currently focused", () => { autofillInit["autofillOverlayContentService"].isFieldCurrentlyFocused = true; sendExtensionRuntimeMessage({ command: "closeAutofillOverlay" }); expect( - autofillInit["autofillOverlayContentService"].removeAutofillOverlayList + autofillInit["autofillOverlayContentService"].removeAutofillOverlayList, ).not.toHaveBeenCalled(); expect( - autofillInit["autofillOverlayContentService"].removeAutofillOverlay + autofillInit["autofillOverlayContentService"].removeAutofillOverlay, ).not.toHaveBeenCalled(); }); @@ -316,10 +340,10 @@ describe("AutofillInit", () => { sendExtensionRuntimeMessage({ command: "closeAutofillOverlay" }); expect( - autofillInit["autofillOverlayContentService"].removeAutofillOverlayList + autofillInit["autofillOverlayContentService"].removeAutofillOverlayList, ).toHaveBeenCalled(); expect( - autofillInit["autofillOverlayContentService"].removeAutofillOverlay + autofillInit["autofillOverlayContentService"].removeAutofillOverlay, ).not.toHaveBeenCalled(); }); @@ -327,10 +351,10 @@ describe("AutofillInit", () => { sendExtensionRuntimeMessage({ command: "closeAutofillOverlay" }); expect( - autofillInit["autofillOverlayContentService"].removeAutofillOverlayList + autofillInit["autofillOverlayContentService"].removeAutofillOverlayList, ).not.toHaveBeenCalled(); expect( - autofillInit["autofillOverlayContentService"].removeAutofillOverlay + autofillInit["autofillOverlayContentService"].removeAutofillOverlay, ).toHaveBeenCalled(); }); }); @@ -373,7 +397,7 @@ describe("AutofillInit", () => { sendExtensionRuntimeMessage(message); expect( - autofillInit["autofillOverlayContentService"].redirectOverlayFocusOut + autofillInit["autofillOverlayContentService"].redirectOverlayFocusOut, ).toHaveBeenCalledWith(message.data.direction); }); }); @@ -399,7 +423,7 @@ describe("AutofillInit", () => { sendExtensionRuntimeMessage(message); expect(autofillInit["autofillOverlayContentService"].isOverlayCiphersPopulated).toEqual( - message.data.isOverlayCiphersPopulated + message.data.isOverlayCiphersPopulated, ); }); }); @@ -423,7 +447,7 @@ describe("AutofillInit", () => { sendExtensionRuntimeMessage({ command: "bgUnlockPopoutOpened" }); expect( - autofillInit["autofillOverlayContentService"].blurMostRecentOverlayField + autofillInit["autofillOverlayContentService"].blurMostRecentOverlayField, ).toHaveBeenCalled(); expect(autofillInit["removeAutofillOverlay"]).toHaveBeenCalled(); }); @@ -448,7 +472,7 @@ describe("AutofillInit", () => { sendExtensionRuntimeMessage({ command: "bgVaultItemRepromptPopoutOpened" }); expect( - autofillInit["autofillOverlayContentService"].blurMostRecentOverlayField + autofillInit["autofillOverlayContentService"].blurMostRecentOverlayField, ).toHaveBeenCalled(); expect(autofillInit["removeAutofillOverlay"]).toHaveBeenCalled(); }); diff --git a/apps/browser/src/autofill/content/autofill-init.ts b/apps/browser/src/autofill/content/autofill-init.ts index 0bd04edee4b..9b23305377c 100644 --- a/apps/browser/src/autofill/content/autofill-init.ts +++ b/apps/browser/src/autofill/content/autofill-init.ts @@ -20,7 +20,7 @@ class AutofillInit implements AutofillInitInterface { collectPageDetailsImmediately: ({ message }) => this.collectPageDetails(message, true), fillForm: ({ message }) => this.fillForm(message), openAutofillOverlay: ({ message }) => this.openAutofillOverlay(message), - closeAutofillOverlay: () => this.removeAutofillOverlay(), + closeAutofillOverlay: ({ message }) => this.removeAutofillOverlay(message), addNewVaultItemFromOverlay: () => this.addNewVaultItemFromOverlay(), redirectOverlayFocusOut: ({ message }) => this.redirectOverlayFocusOut(message), updateIsOverlayCiphersPopulated: ({ message }) => this.updateIsOverlayCiphersPopulated(message), @@ -39,11 +39,11 @@ class AutofillInit implements AutofillInitInterface { this.domElementVisibilityService = new DomElementVisibilityService(); this.collectAutofillContentService = new CollectAutofillContentService( this.domElementVisibilityService, - this.autofillOverlayContentService + this.autofillOverlayContentService, ); this.insertAutofillContentService = new InsertAutofillContentService( this.domElementVisibilityService, - this.collectAutofillContentService + this.collectAutofillContentService, ); } @@ -69,7 +69,7 @@ class AutofillInit implements AutofillInitInterface { */ private async collectPageDetails( message: AutofillExtensionMessage, - sendDetailsInResponse = false + sendDetailsInResponse = false, ): Promise { const pageDetails: AutofillPageDetails = await this.collectAutofillContentService.getPageDetails(); @@ -153,7 +153,12 @@ class AutofillInit implements AutofillInitInterface { * If the autofill is currently filling, only the overlay list will be * removed. */ - private removeAutofillOverlay() { + private removeAutofillOverlay(message?: AutofillExtensionMessage) { + if (message?.data?.forceCloseOverlay) { + this.autofillOverlayContentService?.removeAutofillOverlay(); + return; + } + if ( !this.autofillOverlayContentService || this.autofillOverlayContentService.isFieldCurrentlyFocused @@ -205,7 +210,7 @@ class AutofillInit implements AutofillInitInterface { } this.autofillOverlayContentService.isOverlayCiphersPopulated = Boolean( - data?.isOverlayCiphersPopulated + data?.isOverlayCiphersPopulated, ); } @@ -226,7 +231,7 @@ class AutofillInit implements AutofillInitInterface { private handleExtensionMessage = ( message: AutofillExtensionMessage, sender: chrome.runtime.MessageSender, - sendResponse: (response?: any) => void + sendResponse: (response?: any) => void, ): boolean => { const command: string = message.command; const handler: CallableFunction | undefined = this.extensionMessageHandlers[command]; diff --git a/apps/browser/src/autofill/content/message_handler.ts b/apps/browser/src/autofill/content/message_handler.ts index c2c5c4d3196..9bf48e3b17d 100644 --- a/apps/browser/src/autofill/content/message_handler.ts +++ b/apps/browser/src/autofill/content/message_handler.ts @@ -24,7 +24,7 @@ window.addEventListener( }); } }, - false + false, ); const forwardCommands = [ diff --git a/apps/browser/src/autofill/content/misc-utils.ts b/apps/browser/src/autofill/content/misc-utils.ts index b34a27a30a5..7fe884f1d51 100644 --- a/apps/browser/src/autofill/content/misc-utils.ts +++ b/apps/browser/src/autofill/content/misc-utils.ts @@ -7,7 +7,7 @@ async function copyText(text: string) { async function onMessageListener( msg: TabMessage, sender: chrome.runtime.MessageSender, - responseCallback: (response: unknown) => void + responseCallback: (response: unknown) => void, ) { switch (msg.command) { case "copyText": diff --git a/apps/browser/src/autofill/content/notification-bar.ts b/apps/browser/src/autofill/content/notification-bar.ts index 223d6ab1ddf..92e8f599385 100644 --- a/apps/browser/src/autofill/content/notification-bar.ts +++ b/apps/browser/src/autofill/content/notification-bar.ts @@ -1,7 +1,7 @@ -import AddLoginRuntimeMessage from "../../background/models/addLoginRuntimeMessage"; -import ChangePasswordRuntimeMessage from "../../background/models/changePasswordRuntimeMessage"; import AutofillField from "../models/autofill-field"; import { WatchedForm } from "../models/watched-form"; +import AddLoginRuntimeMessage from "../notification/models/add-login-runtime-message"; +import ChangePasswordRuntimeMessage from "../notification/models/change-password-runtime-message"; import { FormData } from "../services/abstractions/autofill.service"; import { GlobalSettings, UserSettings } from "../types"; @@ -445,7 +445,7 @@ async function loadNotificationBar() { // know what type of form we are watching const submitButton = getSubmitButton( form, - unionSets(logInButtonNames, changePasswordButtonNames) + unionSets(logInButtonNames, changePasswordButtonNames), ); if (submitButton != null) { @@ -475,7 +475,7 @@ async function loadNotificationBar() { watchedForm.formEl, watchedForm.data.password, inputs, - true // Only do fallback if we have expect to find a single password field + true, // Only do fallback if we have expect to find a single password field ); } else if (watchedForm.data.passwords != null) { // if we didn't find a username field, try to locate multiple password fields @@ -499,7 +499,7 @@ async function loadNotificationBar() { form: HTMLFormElement, passwordData: AutofillField, inputs: HTMLInputElement[], - doLastFallback: boolean + doLastFallback: boolean, ): HTMLInputElement { let el = locateField(form, passwordData, inputs); if (el != null && el.type !== "password") { @@ -521,7 +521,7 @@ async function loadNotificationBar() { function locateField( form: HTMLFormElement, fieldData: AutofillField, - inputs: HTMLInputElement[] + inputs: HTMLInputElement[], ): HTMLInputElement | null { // If we have no field data, we cannot locate the field if (fieldData == null) { @@ -667,7 +667,7 @@ async function loadNotificationBar() { // Check if the submit button contains any of the change password button names as a safeguard const buttonText = getButtonText(getSubmitButton(form, changePasswordButtonNames)); const matches = Array.from(changePasswordButtonContainsNames).filter( - (n) => buttonText.indexOf(n) > -1 + (n) => buttonText.indexOf(n) > -1, ); if (matches.length > 0) { @@ -744,8 +744,8 @@ async function loadNotificationBar() { if (submitButton == null) { const possibleSubmitButtons = Array.from( wrappingEl.querySelectorAll( - 'a, span, button[type="button"], ' + 'input[type="button"], button:not([type])' - ) + 'a, span, button[type="button"], ' + 'input[type="button"], button:not([type])', + ), ) as HTMLElement[]; let typelessButton: HTMLElement = null; // Loop through all possible submit buttons and find the first one that matches a submit button name diff --git a/apps/browser/src/autofill/jest/autofill-mocks.ts b/apps/browser/src/autofill/jest/autofill-mocks.ts index 6d8c727dd51..de84cef2494 100644 --- a/apps/browser/src/autofill/jest/autofill-mocks.ts +++ b/apps/browser/src/autofill/jest/autofill-mocks.ts @@ -106,7 +106,7 @@ function createGenerateFillScriptOptionsMock(customFields = {}): GenerateFillScr function createAutofillScriptMock( customFields = {}, - scriptTypes?: Record + scriptTypes?: Record, ): AutofillScript { let script: FillScript[] = [ ["click_on_opid", "default-field"], @@ -152,7 +152,7 @@ const overlayPagesTranslations = { addNewVaultItem: "addNewVaultItem", }; function createInitAutofillOverlayButtonMessageMock( - customFields = {} + customFields = {}, ): InitAutofillOverlayButtonMessage { return { command: "initAutofillOverlayButton", @@ -181,7 +181,7 @@ function createAutofillOverlayCipherDataMock(index: number, customFields = {}): } function createInitAutofillOverlayListMessageMock( - customFields = {} + customFields = {}, ): InitAutofillOverlayListMessage { return { command: "initAutofillOverlayList", diff --git a/apps/browser/src/autofill/jest/testing-utils.ts b/apps/browser/src/autofill/jest/testing-utils.ts index c3fe951a8ee..e0972e3d90d 100644 --- a/apps/browser/src/autofill/jest/testing-utils.ts +++ b/apps/browser/src/autofill/jest/testing-utils.ts @@ -18,7 +18,7 @@ function postWindowMessage(data: any, origin = "https://localhost/") { function sendExtensionRuntimeMessage( message: any, sender?: chrome.runtime.MessageSender, - sendResponse?: CallableFunction + sendResponse?: CallableFunction, ) { (chrome.runtime.onMessage.addListener as unknown as jest.SpyInstance).mock.calls.forEach( (call) => { @@ -26,9 +26,9 @@ function sendExtensionRuntimeMessage( callback( message || {}, sender || mock(), - sendResponse || jest.fn() + sendResponse || jest.fn(), ); - } + }, ); } @@ -51,7 +51,7 @@ function triggerWindowOnFocusedChangedEvent(windowId: number) { (call) => { const callback = call[0]; callback(windowId); - } + }, ); } @@ -60,7 +60,7 @@ function triggerTabOnActivatedEvent(activeInfo: chrome.tabs.TabActiveInfo) { (call) => { const callback = call[0]; callback(activeInfo); - } + }, ); } @@ -74,7 +74,7 @@ function triggerTabOnReplacedEvent(addedTabId: number, removedTabId: number) { function triggerTabOnUpdatedEvent( tabId: number, changeInfo: chrome.tabs.TabChangeInfo, - tab: chrome.tabs.Tab + tab: chrome.tabs.Tab, ) { (chrome.tabs.onUpdated.addListener as unknown as jest.SpyInstance).mock.calls.forEach((call) => { const callback = call[0]; diff --git a/apps/browser/src/autofill/notification/bar.html b/apps/browser/src/autofill/notification/bar.html index a6be58de70a..0b7e4c8e0ec 100644 --- a/apps/browser/src/autofill/notification/bar.html +++ b/apps/browser/src/autofill/notification/bar.html @@ -1,4 +1,4 @@ - + Bitwarden diff --git a/apps/browser/src/background/models/addChangePasswordQueueMessage.ts b/apps/browser/src/autofill/notification/models/add-change-password-queue-message.ts similarity index 55% rename from apps/browser/src/background/models/addChangePasswordQueueMessage.ts rename to apps/browser/src/autofill/notification/models/add-change-password-queue-message.ts index 16491032e9b..f1d5bebdc9e 100644 --- a/apps/browser/src/background/models/addChangePasswordQueueMessage.ts +++ b/apps/browser/src/autofill/notification/models/add-change-password-queue-message.ts @@ -1,5 +1,5 @@ -import NotificationQueueMessage from "./notificationQueueMessage"; -import { NotificationQueueMessageType } from "./notificationQueueMessageType"; +import NotificationQueueMessage from "./notification-queue-message"; +import { NotificationQueueMessageType } from "./notification-queue-message-type"; export default class AddChangePasswordQueueMessage extends NotificationQueueMessage { type: NotificationQueueMessageType.ChangePassword; diff --git a/apps/browser/src/background/models/addLoginQueueMessage.ts b/apps/browser/src/autofill/notification/models/add-login-queue-message.ts similarity index 88% rename from apps/browser/src/background/models/addLoginQueueMessage.ts rename to apps/browser/src/autofill/notification/models/add-login-queue-message.ts index 1f727f40a5c..0d4a23f76fc 100644 --- a/apps/browser/src/background/models/addLoginQueueMessage.ts +++ b/apps/browser/src/autofill/notification/models/add-login-queue-message.ts @@ -4,8 +4,8 @@ import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; import { LoginUriView } from "@bitwarden/common/vault/models/view/login-uri.view"; import { LoginView } from "@bitwarden/common/vault/models/view/login.view"; -import NotificationQueueMessage from "./notificationQueueMessage"; -import { NotificationQueueMessageType } from "./notificationQueueMessageType"; +import NotificationQueueMessage from "./notification-queue-message"; +import { NotificationQueueMessageType } from "./notification-queue-message-type"; export default class AddLoginQueueMessage extends NotificationQueueMessage { type: NotificationQueueMessageType.AddLogin; diff --git a/apps/browser/src/background/models/addLoginRuntimeMessage.ts b/apps/browser/src/autofill/notification/models/add-login-runtime-message.ts similarity index 100% rename from apps/browser/src/background/models/addLoginRuntimeMessage.ts rename to apps/browser/src/autofill/notification/models/add-login-runtime-message.ts diff --git a/apps/browser/src/autofill/notification/models/add-unlock-vault-queue-message.ts b/apps/browser/src/autofill/notification/models/add-unlock-vault-queue-message.ts new file mode 100644 index 00000000000..602e9f6c444 --- /dev/null +++ b/apps/browser/src/autofill/notification/models/add-unlock-vault-queue-message.ts @@ -0,0 +1,6 @@ +import NotificationQueueMessage from "./notification-queue-message"; +import { NotificationQueueMessageType } from "./notification-queue-message-type"; + +export default class AddUnlockVaultQueueMessage extends NotificationQueueMessage { + type: NotificationQueueMessageType.UnlockVault; +} diff --git a/apps/browser/src/background/models/changePasswordRuntimeMessage.ts b/apps/browser/src/autofill/notification/models/change-password-runtime-message.ts similarity index 100% rename from apps/browser/src/background/models/changePasswordRuntimeMessage.ts rename to apps/browser/src/autofill/notification/models/change-password-runtime-message.ts diff --git a/apps/browser/src/background/models/lockedVaultPendingNotificationsItem.ts b/apps/browser/src/autofill/notification/models/locked-vault-pending-notifications-item.ts similarity index 100% rename from apps/browser/src/background/models/lockedVaultPendingNotificationsItem.ts rename to apps/browser/src/autofill/notification/models/locked-vault-pending-notifications-item.ts diff --git a/apps/browser/src/background/models/notificationQueueMessageType.ts b/apps/browser/src/autofill/notification/models/notification-queue-message-type.ts similarity index 100% rename from apps/browser/src/background/models/notificationQueueMessageType.ts rename to apps/browser/src/autofill/notification/models/notification-queue-message-type.ts diff --git a/apps/browser/src/background/models/notificationQueueMessage.ts b/apps/browser/src/autofill/notification/models/notification-queue-message.ts similarity index 68% rename from apps/browser/src/background/models/notificationQueueMessage.ts rename to apps/browser/src/autofill/notification/models/notification-queue-message.ts index 3c6a582ee54..b5e5c3b2e47 100644 --- a/apps/browser/src/background/models/notificationQueueMessage.ts +++ b/apps/browser/src/autofill/notification/models/notification-queue-message.ts @@ -1,4 +1,4 @@ -import { NotificationQueueMessageType } from "./notificationQueueMessageType"; +import { NotificationQueueMessageType } from "./notification-queue-message-type"; export default class NotificationQueueMessage { type: NotificationQueueMessageType; diff --git a/apps/browser/src/autofill/overlay/iframe-content/autofill-overlay-button-iframe.ts b/apps/browser/src/autofill/overlay/iframe-content/autofill-overlay-button-iframe.ts index 125a53c01de..813d8054704 100644 --- a/apps/browser/src/autofill/overlay/iframe-content/autofill-overlay-button-iframe.ts +++ b/apps/browser/src/autofill/overlay/iframe-content/autofill-overlay-button-iframe.ts @@ -12,7 +12,7 @@ class AutofillOverlayButtonIframe extends AutofillOverlayIframeElement { border: "none", }, chrome.i18n.getMessage("bitwardenOverlayButton"), - chrome.i18n.getMessage("bitwardenOverlayMenuAvailable") + chrome.i18n.getMessage("bitwardenOverlayMenuAvailable"), ); } } diff --git a/apps/browser/src/autofill/overlay/iframe-content/autofill-overlay-iframe-element.ts b/apps/browser/src/autofill/overlay/iframe-content/autofill-overlay-iframe-element.ts index 7f84c07870d..209834410f9 100644 --- a/apps/browser/src/autofill/overlay/iframe-content/autofill-overlay-iframe-element.ts +++ b/apps/browser/src/autofill/overlay/iframe-content/autofill-overlay-iframe-element.ts @@ -6,7 +6,7 @@ class AutofillOverlayIframeElement extends HTMLElement { portName: string, initStyles: Partial, iframeTitle: string, - ariaAlert?: string + ariaAlert?: string, ) { super(); @@ -14,7 +14,7 @@ class AutofillOverlayIframeElement extends HTMLElement { const autofillOverlayIframeService = new AutofillOverlayIframeService( iframePath, portName, - shadow + shadow, ); autofillOverlayIframeService.initOverlayIframe(initStyles, iframeTitle, ariaAlert); } diff --git a/apps/browser/src/autofill/overlay/iframe-content/autofill-overlay-iframe.service.spec.ts b/apps/browser/src/autofill/overlay/iframe-content/autofill-overlay-iframe.service.spec.ts index 432f1a548ab..26dc50ecd09 100644 --- a/apps/browser/src/autofill/overlay/iframe-content/autofill-overlay-iframe.service.spec.ts +++ b/apps/browser/src/autofill/overlay/iframe-content/autofill-overlay-iframe.service.spec.ts @@ -23,17 +23,17 @@ describe("AutofillOverlayIframeService", () => { autofillOverlayIframeService = new AutofillOverlayIframeService( iframePath, AutofillOverlayPort.Button, - shadow + shadow, ); shadowAppendSpy = jest.spyOn(shadow, "appendChild"); handlePortDisconnectSpy = jest.spyOn( autofillOverlayIframeService as any, - "handlePortDisconnect" + "handlePortDisconnect", ); handlePortMessageSpy = jest.spyOn(autofillOverlayIframeService as any, "handlePortMessage"); handleWindowMessageSpy = jest.spyOn(autofillOverlayIframeService as any, "handleWindowMessage"); chrome.runtime.connect = jest.fn((connectInfo: chrome.runtime.ConnectInfo) => - createPortSpyMock(connectInfo.name) + createPortSpyMock(connectInfo.name), ) as unknown as typeof chrome.runtime.connect; }); @@ -54,7 +54,7 @@ describe("AutofillOverlayIframeService", () => { autofillOverlayIframeService.initOverlayIframe({}, "title"); expect(autofillOverlayIframeService["shadow"].appendChild).toBeCalledWith( - autofillOverlayIframeService["iframe"] + autofillOverlayIframeService["iframe"], ); }); @@ -147,7 +147,7 @@ describe("AutofillOverlayIframeService", () => { expect(globalThis.removeEventListener).toBeCalledWith( EVENTS.MESSAGE, - handleWindowMessageSpy + handleWindowMessageSpy, ); }); @@ -186,7 +186,7 @@ describe("AutofillOverlayIframeService", () => { expect(autofillOverlayIframeService["iframe"].contentWindow.postMessage).toBeCalledWith( message, - "*" + "*", ); }); @@ -204,7 +204,7 @@ describe("AutofillOverlayIframeService", () => { beforeEach(() => { updateElementStylesSpy = jest.spyOn( autofillOverlayIframeService as any, - "updateElementStyles" + "updateElementStyles", ); }); @@ -219,7 +219,7 @@ describe("AutofillOverlayIframeService", () => { expect(updateElementStylesSpy).not.toBeCalled(); expect(autofillOverlayIframeService["iframe"].contentWindow.postMessage).toBeCalledWith( message, - "*" + "*", ); }); @@ -344,7 +344,7 @@ describe("AutofillOverlayIframeService", () => { new MessageEvent("message", { data: {}, source: window, - }) + }), ); expect(portSpy.postMessage).not.toBeCalled(); @@ -356,7 +356,7 @@ describe("AutofillOverlayIframeService", () => { data: {}, source: autofillOverlayIframeService["iframe"].contentWindow, origin: "https://www.google.com", - }) + }), ); expect(portSpy.postMessage).not.toBeCalled(); @@ -368,7 +368,7 @@ describe("AutofillOverlayIframeService", () => { data: { command: "not-a-handled-command" }, source: autofillOverlayIframeService["iframe"].contentWindow, origin: "chrome-extension://id", - }) + }), ); expect(portSpy.postMessage).toBeCalledWith({ command: "not-a-handled-command" }); @@ -380,7 +380,7 @@ describe("AutofillOverlayIframeService", () => { data: { command: "updateAutofillOverlayListHeight", styles: { height: "300px" } }, source: autofillOverlayIframeService["iframe"].contentWindow, origin: "chrome-extension://id", - }) + }), ); expect(autofillOverlayIframeService["iframe"].style.height).toBe("300px"); @@ -392,6 +392,23 @@ describe("AutofillOverlayIframeService", () => { beforeEach(() => { autofillOverlayIframeService.initOverlayIframe({ height: "0px" }, "title", "ariaAlert"); autofillOverlayIframeService["iframe"].dispatchEvent(new Event(EVENTS.LOAD)); + portSpy = autofillOverlayIframeService["port"]; + }); + + it("skips handling found mutations if excessive mutations are triggering", async () => { + jest.useFakeTimers(); + jest + .spyOn( + autofillOverlayIframeService as any, + "isTriggeringExcessiveMutationObserverIterations", + ) + .mockReturnValue(true); + jest.spyOn(autofillOverlayIframeService as any, "updateElementStyles"); + + autofillOverlayIframeService["iframe"].style.visibility = "hidden"; + await flushPromises(); + + expect(autofillOverlayIframeService["updateElementStyles"]).not.toBeCalled(); }); it("reverts any styles changes made directly to the iframe", async () => { @@ -402,5 +419,47 @@ describe("AutofillOverlayIframeService", () => { expect(autofillOverlayIframeService["iframe"].style.visibility).toBe("visible"); }); + + it("force closes the autofill overlay if more than 9 foreign mutations are triggered", async () => { + jest.useFakeTimers(); + autofillOverlayIframeService["foreignMutationsCount"] = 10; + + autofillOverlayIframeService["iframe"].src = "http://malicious-site.com"; + await flushPromises(); + + expect(portSpy.postMessage).toBeCalledWith({ command: "forceCloseAutofillOverlay" }); + }); + + it("force closes the autofill overlay if excessive mutations are being triggered", async () => { + jest.useFakeTimers(); + autofillOverlayIframeService["mutationObserverIterations"] = 20; + + autofillOverlayIframeService["iframe"].src = "http://malicious-site.com"; + await flushPromises(); + + expect(portSpy.postMessage).toBeCalledWith({ command: "forceCloseAutofillOverlay" }); + }); + + it("resets the excessive mutations and foreign mutation counters", async () => { + jest.useFakeTimers(); + autofillOverlayIframeService["foreignMutationsCount"] = 9; + autofillOverlayIframeService["mutationObserverIterations"] = 19; + + autofillOverlayIframeService["iframe"].src = "http://malicious-site.com"; + jest.advanceTimersByTime(2001); + await flushPromises(); + + expect(autofillOverlayIframeService["foreignMutationsCount"]).toBe(0); + expect(autofillOverlayIframeService["mutationObserverIterations"]).toBe(0); + }); + + it("resets any mutated default attributes for the iframe", async () => { + jest.useFakeTimers(); + + autofillOverlayIframeService["iframe"].title = "some-other-title"; + await flushPromises(); + + expect(autofillOverlayIframeService["iframe"].title).toBe("title"); + }); }); }); diff --git a/apps/browser/src/autofill/overlay/iframe-content/autofill-overlay-iframe.service.ts b/apps/browser/src/autofill/overlay/iframe-content/autofill-overlay-iframe.service.ts index 2ec2bc0aa3b..20f5aa830fc 100644 --- a/apps/browser/src/autofill/overlay/iframe-content/autofill-overlay-iframe.service.ts +++ b/apps/browser/src/autofill/overlay/iframe-content/autofill-overlay-iframe.service.ts @@ -30,6 +30,16 @@ class AutofillOverlayIframeService implements AutofillOverlayIframeServiceInterf colorScheme: "normal", opacity: "0", }; + private defaultIframeAttributes: Record = { + src: "", + title: "", + sandbox: "allow-scripts", + allowtransparency: "true", + tabIndex: "-1", + }; + private foreignMutationsCount = 0; + private mutationObserverIterations = 0; + private mutationObserverIterationsResetTimeout: NodeJS.Timeout; private readonly windowMessageHandlers: AutofillOverlayIframeWindowMessageHandlers = { updateAutofillOverlayListHeight: (message) => this.updateElementStyles(this.iframe, message.styles), @@ -40,7 +50,11 @@ class AutofillOverlayIframeService implements AutofillOverlayIframeServiceInterf updateOverlayHidden: ({ message }) => this.updateElementStyles(this.iframe, message.styles), }; - constructor(private iframePath: string, private portName: string, private shadow: ShadowRoot) { + constructor( + private iframePath: string, + private portName: string, + private shadow: ShadowRoot, + ) { this.extensionOriginsSet = new Set([ chrome.runtime.getURL("").slice(0, -1).toLowerCase(), // Remove the trailing slash and normalize the extension url to lowercase "null", @@ -65,15 +79,16 @@ class AutofillOverlayIframeService implements AutofillOverlayIframeServiceInterf initOverlayIframe( initStyles: Partial, iframeTitle: string, - ariaAlert?: string + ariaAlert?: string, ) { + this.defaultIframeAttributes.src = chrome.runtime.getURL(this.iframePath); + this.defaultIframeAttributes.title = iframeTitle; + this.iframe = globalThis.document.createElement("iframe"); - this.iframe.src = chrome.runtime.getURL(this.iframePath); this.updateElementStyles(this.iframe, { ...this.iframeStyles, ...initStyles }); - this.iframe.tabIndex = -1; - this.iframe.setAttribute("title", iframeTitle); - this.iframe.setAttribute("sandbox", "allow-scripts"); - this.iframe.setAttribute("allowtransparency", "true"); + for (const [attribute, value] of Object.entries(this.defaultIframeAttributes)) { + this.iframe.setAttribute(attribute, value); + } this.iframe.addEventListener(EVENTS.LOAD, this.setupPortMessageListener); if (ariaAlert) { @@ -167,7 +182,7 @@ class AutofillOverlayIframeService implements AutofillOverlayIframeServiceInterf */ private handlePortMessage = ( message: AutofillOverlayIframeExtensionMessage, - port: chrome.runtime.Port + port: chrome.runtime.Port, ) => { if (port.name !== this.portName) { return; @@ -286,9 +301,20 @@ class AutofillOverlayIframeService implements AutofillOverlayIframeServiceInterf * @param mutations - The mutations to the iframe element */ private handleMutations = (mutations: MutationRecord[]) => { + if (this.isTriggeringExcessiveMutationObserverIterations()) { + return; + } + for (let index = 0; index < mutations.length; index++) { const mutation = mutations[index]; - if (mutation.type !== "attributes" || mutation.attributeName !== "style") { + if (mutation.type !== "attributes") { + continue; + } + + const element = mutation.target as HTMLElement; + if (mutation.attributeName !== "style") { + this.handleElementAttributeMutation(element); + continue; } @@ -297,6 +323,41 @@ class AutofillOverlayIframeService implements AutofillOverlayIframeServiceInterf } }; + /** + * Handles mutations to the iframe element's attributes. This ensures that + * the iframe element's attributes are not modified by a third party source. + * + * @param element - The element to handle attribute mutations for + */ + private handleElementAttributeMutation(element: HTMLElement) { + const attributes = Array.from(element.attributes); + for (let attributeIndex = 0; attributeIndex < attributes.length; attributeIndex++) { + const attribute = attributes[attributeIndex]; + if (attribute.name === "style") { + continue; + } + + if (this.foreignMutationsCount >= 10) { + this.port?.postMessage({ command: "forceCloseAutofillOverlay" }); + break; + } + + const defaultIframeAttribute = this.defaultIframeAttributes[attribute.name]; + if (!defaultIframeAttribute) { + this.iframe.removeAttribute(attribute.name); + this.foreignMutationsCount++; + continue; + } + + if (attribute.value === defaultIframeAttribute) { + continue; + } + + this.iframe.setAttribute(attribute.name, defaultIframeAttribute); + this.foreignMutationsCount++; + } + } + /** * Observes the iframe element for mutations to its style attribute. */ @@ -310,6 +371,35 @@ class AutofillOverlayIframeService implements AutofillOverlayIframeServiceInterf private unobserveIframe() { this.iframeMutationObserver.disconnect(); } + + /** + * Identifies if the mutation observer is triggering excessive iterations. + * Will remove the autofill overlay if any set mutation observer is + * triggering excessive iterations. + */ + private isTriggeringExcessiveMutationObserverIterations() { + const resetCounters = () => { + this.mutationObserverIterations = 0; + this.foreignMutationsCount = 0; + }; + + if (this.mutationObserverIterationsResetTimeout) { + clearTimeout(this.mutationObserverIterationsResetTimeout); + } + + this.mutationObserverIterations++; + this.mutationObserverIterationsResetTimeout = setTimeout(() => resetCounters(), 2000); + + if (this.mutationObserverIterations > 20) { + clearTimeout(this.mutationObserverIterationsResetTimeout); + resetCounters(); + this.port?.postMessage({ command: "forceCloseAutofillOverlay" }); + + return true; + } + + return false; + } } export default AutofillOverlayIframeService; diff --git a/apps/browser/src/autofill/overlay/iframe-content/autofill-overlay-list-iframe.ts b/apps/browser/src/autofill/overlay/iframe-content/autofill-overlay-list-iframe.ts index 26a0ed2765e..b60b618e4e7 100644 --- a/apps/browser/src/autofill/overlay/iframe-content/autofill-overlay-list-iframe.ts +++ b/apps/browser/src/autofill/overlay/iframe-content/autofill-overlay-list-iframe.ts @@ -17,7 +17,7 @@ class AutofillOverlayListIframe extends AutofillOverlayIframeElement { borderStyle: "solid", borderColor: "rgb(206, 212, 220)", }, - chrome.i18n.getMessage("bitwardenVault") + chrome.i18n.getMessage("bitwardenVault"), ); } } diff --git a/apps/browser/src/autofill/overlay/pages/button/autofill-overlay-button.spec.ts b/apps/browser/src/autofill/overlay/pages/button/autofill-overlay-button.spec.ts index a4d0b336225..c4927f4a971 100644 --- a/apps/browser/src/autofill/overlay/pages/button/autofill-overlay-button.spec.ts +++ b/apps/browser/src/autofill/overlay/pages/button/autofill-overlay-button.spec.ts @@ -25,12 +25,12 @@ describe("AutofillOverlayButton", () => { describe("initAutofillOverlayButton", () => { it("creates the button element with the locked icon when the user's auth status is not Unlocked", () => { postWindowMessage( - createInitAutofillOverlayButtonMessageMock({ authStatus: AuthenticationStatus.Locked }) + createInitAutofillOverlayButtonMessageMock({ authStatus: AuthenticationStatus.Locked }), ); expect(autofillOverlayButton["buttonElement"]).toMatchSnapshot(); expect(autofillOverlayButton["buttonElement"].querySelector("svg")).toBe( - autofillOverlayButton["logoLockedIconElement"] + autofillOverlayButton["logoLockedIconElement"], ); }); @@ -39,7 +39,7 @@ describe("AutofillOverlayButton", () => { expect(autofillOverlayButton["buttonElement"]).toMatchSnapshot(); expect(autofillOverlayButton["buttonElement"].querySelector("svg")).toBe( - autofillOverlayButton["logoIconElement"] + autofillOverlayButton["logoIconElement"], ); }); @@ -49,7 +49,7 @@ describe("AutofillOverlayButton", () => { expect(globalThis.parent.postMessage).toHaveBeenCalledWith( { command: "overlayButtonClicked" }, - "https://localhost/" + "https://localhost/", ); }); }); @@ -74,7 +74,7 @@ describe("AutofillOverlayButton", () => { expect(globalThis.parent.postMessage).toHaveBeenCalledWith( { command: "closeAutofillOverlay" }, - "https://localhost/" + "https://localhost/", ); }); diff --git a/apps/browser/src/autofill/overlay/pages/button/autofill-overlay-button.ts b/apps/browser/src/autofill/overlay/pages/button/autofill-overlay-button.ts index 7b3acdac9e5..94c0772fd2b 100644 --- a/apps/browser/src/autofill/overlay/pages/button/autofill-overlay-button.ts +++ b/apps/browser/src/autofill/overlay/pages/button/autofill-overlay-button.ts @@ -58,7 +58,7 @@ class AutofillOverlayButton extends AutofillOverlayPageElement { this.buttonElement.classList.add("overlay-button"); this.buttonElement.setAttribute( "aria-label", - this.getTranslation("toggleBitwardenVaultOverlay") + this.getTranslation("toggleBitwardenVaultOverlay"), ); this.buttonElement.addEventListener(EVENTS.CLICK, this.handleButtonElementClick); diff --git a/apps/browser/src/autofill/overlay/pages/button/button.html b/apps/browser/src/autofill/overlay/pages/button/button.html index de553e008d9..6cbd1332743 100644 --- a/apps/browser/src/autofill/overlay/pages/button/button.html +++ b/apps/browser/src/autofill/overlay/pages/button/button.html @@ -1,4 +1,4 @@ - + Bitwarden overlay button diff --git a/apps/browser/src/autofill/overlay/pages/list/autofill-overlay-list.spec.ts b/apps/browser/src/autofill/overlay/pages/list/autofill-overlay-list.spec.ts index 358b003401d..f26dc2bab0f 100644 --- a/apps/browser/src/autofill/overlay/pages/list/autofill-overlay-list.spec.ts +++ b/apps/browser/src/autofill/overlay/pages/list/autofill-overlay-list.spec.ts @@ -36,7 +36,7 @@ describe("AutofillOverlayList", () => { createInitAutofillOverlayListMessageMock({ authStatus: AuthenticationStatus.Locked, cipherList: [], - }) + }), ); }); @@ -52,7 +52,7 @@ describe("AutofillOverlayList", () => { expect(globalThis.parent.postMessage).toHaveBeenCalledWith( { command: "unlockVault" }, - "https://localhost/" + "https://localhost/", ); }); }); @@ -63,7 +63,7 @@ describe("AutofillOverlayList", () => { createInitAutofillOverlayListMessageMock({ authStatus: AuthenticationStatus.Unlocked, ciphers: [], - }) + }), ); }); @@ -79,7 +79,7 @@ describe("AutofillOverlayList", () => { expect(globalThis.parent.postMessage).toHaveBeenCalledWith( { command: "addNewVaultItem" }, - "https://localhost/" + "https://localhost/", ); }); }); @@ -113,7 +113,7 @@ describe("AutofillOverlayList", () => { autofillOverlayList["cipherListScrollDebounceTimeout"] = setTimeout(jest.fn, 0); const handleDebouncedScrollEventSpy = jest.spyOn( autofillOverlayList as any, - "handleDebouncedScrollEvent" + "handleDebouncedScrollEvent", ); autofillOverlayList["handleCiphersListScrollEvent"](); @@ -139,7 +139,7 @@ describe("AutofillOverlayList", () => { expect(globalThis.parent.postMessage).toHaveBeenCalledWith( { command: "fillSelectedListItem", overlayCipherId: "1" }, - "https://localhost/" + "https://localhost/", ); }); @@ -227,7 +227,7 @@ describe("AutofillOverlayList", () => { expect(globalThis.parent.postMessage).toHaveBeenCalledWith( { command: "viewSelectedCipher", overlayCipherId: "1" }, - "https://localhost/" + "https://localhost/", ); }); @@ -298,7 +298,7 @@ describe("AutofillOverlayList", () => { expect(globalThis.parent.postMessage).toHaveBeenCalledWith( { command: "checkAutofillOverlayButtonFocused" }, - "https://localhost/" + "https://localhost/", ); }); @@ -317,7 +317,7 @@ describe("AutofillOverlayList", () => { createInitAutofillOverlayListMessageMock({ authStatus: AuthenticationStatus.Locked, cipherList: [], - }) + }), ); const unlockButton = autofillOverlayList["overlayListContainer"].querySelector("#unlock-button"); @@ -382,7 +382,7 @@ describe("AutofillOverlayList", () => { expect(globalThis.parent.postMessage).toHaveBeenCalledWith( { command: "updateAutofillOverlayListHeight", styles: { height: "300px" } }, - "https://localhost/" + "https://localhost/", ); }); }); diff --git a/apps/browser/src/autofill/overlay/pages/list/autofill-overlay-list.ts b/apps/browser/src/autofill/overlay/pages/list/autofill-overlay-list.ts index 77ddd56b34d..053fddb9c13 100644 --- a/apps/browser/src/autofill/overlay/pages/list/autofill-overlay-list.ts +++ b/apps/browser/src/autofill/overlay/pages/list/autofill-overlay-list.ts @@ -89,7 +89,7 @@ class AutofillOverlayList extends AutofillOverlayPageElement { unlockButtonElement.textContent = this.getTranslation("unlockAccount"); unlockButtonElement.setAttribute( "aria-label", - `${this.getTranslation("unlockAccount")}, ${this.getTranslation("opensInANewWindow")}` + `${this.getTranslation("unlockAccount")}, ${this.getTranslation("opensInANewWindow")}`, ); unlockButtonElement.prepend(buildSvgDomElement(lockIcon)); unlockButtonElement.addEventListener(EVENTS.CLICK, this.handleUnlockButtonClick); @@ -118,7 +118,9 @@ class AutofillOverlayList extends AutofillOverlayPageElement { private updateListItems(ciphers: OverlayCipherData[]) { this.ciphers = ciphers; this.currentCipherIndex = 0; - this.overlayListContainer.innerHTML = ""; + if (this.overlayListContainer) { + this.overlayListContainer.innerHTML = ""; + } if (!ciphers?.length) { this.buildNoResultsOverlayList(); @@ -151,7 +153,7 @@ class AutofillOverlayList extends AutofillOverlayPageElement { newItemButton.textContent = this.getTranslation("newItem"); newItemButton.setAttribute( "aria-label", - `${this.getTranslation("addNewVaultItem")}, ${this.getTranslation("opensInANewWindow")}` + `${this.getTranslation("addNewVaultItem")}, ${this.getTranslation("opensInANewWindow")}`, ); newItemButton.prepend(buildSvgDomElement(plusIcon)); newItemButton.addEventListener(EVENTS.CLICK, this.handeNewItemButtonClick); @@ -177,7 +179,7 @@ class AutofillOverlayList extends AutofillOverlayPageElement { private loadPageOfCiphers() { const lastIndex = Math.min( this.currentCipherIndex + this.showCiphersPerPage, - this.ciphers.length + this.ciphers.length, ); for (let cipherIndex = this.currentCipherIndex; cipherIndex < lastIndex; cipherIndex++) { this.ciphersList.appendChild(this.buildOverlayActionsListItem(this.ciphers[cipherIndex])); @@ -253,11 +255,11 @@ class AutofillOverlayList extends AutofillOverlayPageElement { fillCipherElement.classList.add("fill-cipher-button"); fillCipherElement.setAttribute( "aria-label", - `${this.getTranslation("fillCredentialsFor")} ${cipher.name}` + `${this.getTranslation("fillCredentialsFor")} ${cipher.name}`, ); fillCipherElement.setAttribute( "aria-description", - `${this.getTranslation("partialUsername")}, ${cipher.login.username}` + `${this.getTranslation("partialUsername")}, ${cipher.login.username}`, ); fillCipherElement.append(cipherIcon, cipherDetailsElement); fillCipherElement.addEventListener(EVENTS.CLICK, this.handleFillCipherClickEvent(cipher)); @@ -279,7 +281,7 @@ class AutofillOverlayList extends AutofillOverlayPageElement { command: "fillSelectedListItem", overlayCipherId: cipher.id, }), - `${cipher.id}-fill-cipher-button-click-handler` + `${cipher.id}-fill-cipher-button-click-handler`, ); }; @@ -323,7 +325,7 @@ class AutofillOverlayList extends AutofillOverlayPageElement { viewCipherElement.classList.add("view-cipher-button"); viewCipherElement.setAttribute( "aria-label", - `${this.getTranslation("view")} ${cipher.name}, ${this.getTranslation("opensInANewWindow")}` + `${this.getTranslation("view")} ${cipher.name}, ${this.getTranslation("opensInANewWindow")}`, ); viewCipherElement.append(buildSvgDomElement(viewCipherIcon)); viewCipherElement.addEventListener(EVENTS.CLICK, this.handleViewCipherClickEvent(cipher)); @@ -341,7 +343,7 @@ class AutofillOverlayList extends AutofillOverlayPageElement { private handleViewCipherClickEvent = (cipher: OverlayCipherData) => { return this.useEventHandlersMemo( () => this.postMessageToParent({ command: "viewSelectedCipher", overlayCipherId: cipher.id }), - `${cipher.id}-view-cipher-button-click-handler` + `${cipher.id}-view-cipher-button-click-handler`, ); }; @@ -485,7 +487,7 @@ class AutofillOverlayList extends AutofillOverlayPageElement { */ private focusOverlayList() { const unlockButtonElement = this.overlayListContainer.querySelector( - "#unlock-button" + "#unlock-button", ) as HTMLElement; if (unlockButtonElement) { unlockButtonElement.focus(); @@ -493,7 +495,7 @@ class AutofillOverlayList extends AutofillOverlayPageElement { } const newItemButtonElement = this.overlayListContainer.querySelector( - "#new-item-button" + "#new-item-button", ) as HTMLElement; if (newItemButtonElement) { newItemButtonElement.focus(); @@ -501,7 +503,7 @@ class AutofillOverlayList extends AutofillOverlayPageElement { } const firstCipherElement = this.overlayListContainer.querySelector( - ".fill-cipher-button" + ".fill-cipher-button", ) as HTMLElement; firstCipherElement?.focus(); } diff --git a/apps/browser/src/autofill/overlay/pages/list/list.html b/apps/browser/src/autofill/overlay/pages/list/list.html index 5678a3e4f38..8e63de67374 100644 --- a/apps/browser/src/autofill/overlay/pages/list/list.html +++ b/apps/browser/src/autofill/overlay/pages/list/list.html @@ -1,4 +1,4 @@ - + Bitwarden vault diff --git a/apps/browser/src/autofill/overlay/pages/shared/autofill-overlay-page-element.spec.ts b/apps/browser/src/autofill/overlay/pages/shared/autofill-overlay-page-element.spec.ts index 1c0587ce861..c70892cfcad 100644 --- a/apps/browser/src/autofill/overlay/pages/shared/autofill-overlay-page-element.spec.ts +++ b/apps/browser/src/autofill/overlay/pages/shared/autofill-overlay-page-element.spec.ts @@ -35,12 +35,12 @@ describe("AutofillOverlayPageElement", () => { const linkElement = autofillOverlayPageElement["initOverlayPage"]( "button", "https://jest-testing-website.com", - translations + translations, ); expect(globalThis.document.documentElement.setAttribute).toHaveBeenCalledWith( "lang", - translations.locale + translations.locale, ); expect(globalThis.document.head.title).toEqual(translations.buttonPageTitle); expect(globalThis.document.createElement).toHaveBeenCalledWith("link"); @@ -62,7 +62,7 @@ describe("AutofillOverlayPageElement", () => { expect(globalThis.parent.postMessage).toHaveBeenCalledWith( { command: "test" }, - "https://jest-testing-website.com" + "https://jest-testing-website.com", ); }); }); @@ -79,25 +79,25 @@ describe("AutofillOverlayPageElement", () => { it("sets up global event listeners", () => { const handleWindowMessageSpy = jest.spyOn( autofillOverlayPageElement as any, - "handleWindowMessage" + "handleWindowMessage", ); const handleWindowBlurEventSpy = jest.spyOn( autofillOverlayPageElement as any, - "handleWindowBlurEvent" + "handleWindowBlurEvent", ); const handleDocumentKeyDownEventSpy = jest.spyOn( autofillOverlayPageElement as any, - "handleDocumentKeyDownEvent" + "handleDocumentKeyDownEvent", ); autofillOverlayPageElement["setupGlobalListeners"]( - mock() + mock(), ); expect(globalThis.addEventListener).toHaveBeenCalledWith("message", handleWindowMessageSpy); expect(globalThis.addEventListener).toHaveBeenCalledWith("blur", handleWindowBlurEventSpy); expect(globalThis.document.addEventListener).toHaveBeenCalledWith( "keydown", - handleDocumentKeyDownEventSpy + handleDocumentKeyDownEventSpy, ); }); @@ -106,18 +106,18 @@ describe("AutofillOverlayPageElement", () => { autofillOverlayPageElement["setupGlobalListeners"]( mock({ initAutofillOverlayButton: initAutofillOverlayButtonSpy, - }) + }), ); globalThis.dispatchEvent( new MessageEvent("message", { data: { command: "initAutofillOverlayButton" }, origin: "https://jest-testing-website.com", - }) + }), ); expect(autofillOverlayPageElement["messageOrigin"]).toEqual( - "https://jest-testing-website.com" + "https://jest-testing-website.com", ); }); @@ -126,7 +126,7 @@ describe("AutofillOverlayPageElement", () => { autofillOverlayPageElement["setupGlobalListeners"]( mock({ initAutofillOverlayButton: initAutofillOverlayButtonSpy, - }) + }), ); const data = { command: "initAutofillOverlayButton" }; @@ -140,7 +140,7 @@ describe("AutofillOverlayPageElement", () => { autofillOverlayPageElement["setupGlobalListeners"]( mock({ initAutofillOverlayButton: initAutofillOverlayButtonSpy, - }) + }), ); globalThis.dispatchEvent(new MessageEvent("message", { data: { command: "test" } })); @@ -151,20 +151,20 @@ describe("AutofillOverlayPageElement", () => { it("posts a message to the parent when the window is blurred", () => { autofillOverlayPageElement["messageOrigin"] = "https://jest-testing-website.com"; autofillOverlayPageElement["setupGlobalListeners"]( - mock() + mock(), ); globalThis.dispatchEvent(new Event("blur")); expect(globalThis.parent.postMessage).toHaveBeenCalledWith( { command: "overlayPageBlurred" }, - "https://jest-testing-website.com" + "https://jest-testing-website.com", ); }); it("skips redirecting keyboard focus when a KeyDown event triggers and the key is not a `Tab` or `Escape` key", () => { autofillOverlayPageElement["setupGlobalListeners"]( - mock() + mock(), ); globalThis.document.dispatchEvent(new KeyboardEvent("keydown", { code: "test" })); @@ -175,44 +175,44 @@ describe("AutofillOverlayPageElement", () => { it("redirects the overlay focus out to the previous element on KeyDown of the `Tab+Shift` keys", () => { autofillOverlayPageElement["messageOrigin"] = "https://jest-testing-website.com"; autofillOverlayPageElement["setupGlobalListeners"]( - mock() + mock(), ); globalThis.document.dispatchEvent( - new KeyboardEvent("keydown", { code: "Tab", shiftKey: true }) + new KeyboardEvent("keydown", { code: "Tab", shiftKey: true }), ); expect(globalThis.parent.postMessage).toHaveBeenCalledWith( { command: "redirectOverlayFocusOut", direction: "previous" }, - "https://jest-testing-website.com" + "https://jest-testing-website.com", ); }); it("redirects the overlay focus out to the next element on KeyDown of the `Tab` key", () => { autofillOverlayPageElement["messageOrigin"] = "https://jest-testing-website.com"; autofillOverlayPageElement["setupGlobalListeners"]( - mock() + mock(), ); globalThis.document.dispatchEvent(new KeyboardEvent("keydown", { code: "Tab" })); expect(globalThis.parent.postMessage).toHaveBeenCalledWith( { command: "redirectOverlayFocusOut", direction: "next" }, - "https://jest-testing-website.com" + "https://jest-testing-website.com", ); }); it("redirects the overlay focus out to the current element on KeyDown of the `Escape` key", () => { autofillOverlayPageElement["messageOrigin"] = "https://jest-testing-website.com"; autofillOverlayPageElement["setupGlobalListeners"]( - mock() + mock(), ); globalThis.document.dispatchEvent(new KeyboardEvent("keydown", { code: "Escape" })); expect(globalThis.parent.postMessage).toHaveBeenCalledWith( { command: "redirectOverlayFocusOut", direction: "current" }, - "https://jest-testing-website.com" + "https://jest-testing-website.com", ); }); }); diff --git a/apps/browser/src/autofill/overlay/pages/shared/autofill-overlay-page-element.ts b/apps/browser/src/autofill/overlay/pages/shared/autofill-overlay-page-element.ts index 692803a7529..cca96555b2a 100644 --- a/apps/browser/src/autofill/overlay/pages/shared/autofill-overlay-page-element.ts +++ b/apps/browser/src/autofill/overlay/pages/shared/autofill-overlay-page-element.ts @@ -28,7 +28,7 @@ class AutofillOverlayPageElement extends HTMLElement { protected initOverlayPage( elementName: "button" | "list", styleSheetUrl: string, - translations: Record + translations: Record, ): HTMLLinkElement { this.translations = translations; globalThis.document.documentElement.setAttribute("lang", this.getTranslation("locale")); @@ -127,7 +127,7 @@ class AutofillOverlayPageElement extends HTMLElement { if (event.code === "Tab") { this.redirectOverlayFocusOutMessage( - event.shiftKey ? RedirectFocusDirection.Previous : RedirectFocusDirection.Next + event.shiftKey ? RedirectFocusDirection.Previous : RedirectFocusDirection.Next, ); return; } diff --git a/apps/browser/src/popup/settings/autofill.component.html b/apps/browser/src/autofill/popup/settings/autofill.component.html similarity index 89% rename from apps/browser/src/popup/settings/autofill.component.html rename to apps/browser/src/autofill/popup/settings/autofill.component.html index 9204add4458..d675615ac8d 100644 --- a/apps/browser/src/popup/settings/autofill.component.html +++ b/apps/browser/src/autofill/popup/settings/autofill.component.html @@ -45,22 +45,20 @@ +
-
-
-
- - -
-
-
diff --git a/apps/browser/src/popup/settings/autofill.component.ts b/apps/browser/src/autofill/popup/settings/autofill.component.ts similarity index 63% rename from apps/browser/src/popup/settings/autofill.component.ts rename to apps/browser/src/autofill/popup/settings/autofill.component.ts index fb544d7526a..d9038b0eb27 100644 --- a/apps/browser/src/popup/settings/autofill.component.ts +++ b/apps/browser/src/autofill/popup/settings/autofill.component.ts @@ -7,22 +7,20 @@ import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.servic import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { StateService } from "@bitwarden/common/platform/abstractions/state.service"; import { UriMatchType } from "@bitwarden/common/vault/enums"; -import { DialogService } from "@bitwarden/components"; -import { AutofillOverlayVisibility } from "../../autofill/utils/autofill-overlay.enum"; -import { BrowserApi } from "../../platform/browser/browser-api"; -import { flagEnabled } from "../../platform/flags"; +import { BrowserApi } from "../../../platform/browser/browser-api"; +import { flagEnabled } from "../../../platform/flags"; +import { AutofillOverlayVisibility } from "../../utils/autofill-overlay.enum"; @Component({ selector: "app-autofill", templateUrl: "autofill.component.html", }) export class AutofillComponent implements OnInit { - protected canOverrideBrowserAutofillSetting = false; - protected defaultBrowserAutofillDisabled = false; protected isAutoFillOverlayFlagEnabled = false; protected autoFillOverlayVisibility: number; protected autoFillOverlayVisibilityOptions: any[]; + protected disablePasswordManagerLink: string; enableAutoFillOnPageLoad = false; autoFillOnPageLoadDefault = false; autoFillOnPageLoadOptions: any[]; @@ -37,7 +35,6 @@ export class AutofillComponent implements OnInit { private platformUtilsService: PlatformUtilsService, private configService: ConfigServiceAbstraction, private settingsService: SettingsService, - private dialogService: DialogService ) { this.autoFillOverlayVisibilityOptions = [ { @@ -67,15 +64,12 @@ export class AutofillComponent implements OnInit { ]; this.accountSwitcherEnabled = flagEnabled("accountSwitching"); + this.disablePasswordManagerLink = this.getDisablePasswordManagerLink(); } async ngOnInit() { - this.canOverrideBrowserAutofillSetting = this.platformUtilsService.isChrome(); - - this.defaultBrowserAutofillDisabled = await this.browserAutofillSettingCurrentlyOverridden(); - this.isAutoFillOverlayFlagEnabled = await this.configService.getFeatureFlag( - FeatureFlag.AutofillOverlay + FeatureFlag.AutofillOverlay, ); this.autoFillOverlayVisibility = (await this.settingsService.getAutoFillOverlayVisibility()) || AutofillOverlayVisibility.Off; @@ -91,54 +85,8 @@ export class AutofillComponent implements OnInit { await this.setAutofillKeyboardHelperText(command); } - async updateDefaultBrowserAutofillDisabled() { - const privacyPermissionGranted = await this.privacyPermissionGranted(); - if (!this.defaultBrowserAutofillDisabled && !privacyPermissionGranted) { - return; - } - - if ( - !privacyPermissionGranted && - !(await BrowserApi.requestPermission({ permissions: ["privacy"] })) - ) { - await this.dialogService.openSimpleDialog({ - title: { key: "extensionPrivacyPermissionNotGrantedTitle" }, - content: { key: "extensionPrivacyPermissionNotGrantedDescription" }, - acceptButtonText: { key: "ok" }, - cancelButtonText: null, - type: "warning", - }); - this.defaultBrowserAutofillDisabled = false; - - return; - } - - await BrowserApi.updateDefaultBrowserAutofillSettings(!this.defaultBrowserAutofillDisabled); - } - async updateAutoFillOverlayVisibility() { await this.settingsService.setAutoFillOverlayVisibility(this.autoFillOverlayVisibility); - - if ( - this.autoFillOverlayVisibility === AutofillOverlayVisibility.Off || - !this.canOverrideBrowserAutofillSetting || - (await this.browserAutofillSettingCurrentlyOverridden()) - ) { - return; - } - - const permissionGranted = await this.privacyPermissionGranted(); - const contentKey = permissionGranted - ? "overrideBrowserAutofillDescription" - : "overrideBrowserAutofillPrivacyRequiredDescription"; - await this.dialogService.openSimpleDialog({ - title: { key: "overrideBrowserAutofillTitle" }, - content: { key: contentKey }, - acceptButtonText: { key: "turnOn" }, - acceptAction: async () => await this.handleOverrideDialogAccept(), - cancelButtonText: { key: "ignore" }, - type: "info", - }); } async updateAutoFillOnPageLoad() { @@ -175,24 +123,25 @@ export class AutofillComponent implements OnInit { } } - private handleOverrideDialogAccept = async () => { - this.defaultBrowserAutofillDisabled = true; - await this.updateDefaultBrowserAutofillDisabled(); - }; - - async browserAutofillSettingCurrentlyOverridden() { - if (!this.canOverrideBrowserAutofillSetting) { - return false; + private getDisablePasswordManagerLink(): string { + if (this.platformUtilsService.isChrome()) { + return "chrome://settings/autofill"; + } + if (this.platformUtilsService.isOpera()) { + return "opera://settings/autofill"; + } + if (this.platformUtilsService.isEdge()) { + return "edge://settings/passwords"; + } + if (this.platformUtilsService.isVivaldi()) { + return "vivaldi://settings/autofill"; } - if (!(await this.privacyPermissionGranted())) { - return false; - } - - return await BrowserApi.browserAutofillSettingsOverridden(); + return "https://bitwarden.com/help/disable-browser-autofill/"; } - async privacyPermissionGranted(): Promise { - return await BrowserApi.permissionsGranted(["privacy"]); + protected openDisablePasswordManagerLink(event: Event) { + event.preventDefault(); + BrowserApi.createNewTab(this.disablePasswordManagerLink); } } diff --git a/apps/browser/src/autofill/services/abstractions/autofill-overlay-content.service.ts b/apps/browser/src/autofill/services/abstractions/autofill-overlay-content.service.ts index 1cfde6e6274..ac7d55a54d4 100644 --- a/apps/browser/src/autofill/services/abstractions/autofill-overlay-content.service.ts +++ b/apps/browser/src/autofill/services/abstractions/autofill-overlay-content.service.ts @@ -17,7 +17,7 @@ interface AutofillOverlayContentService { init(): void; setupAutofillOverlayListenerOnField( autofillFieldElement: ElementWithOpId, - autofillFieldData: AutofillField + autofillFieldData: AutofillField, ): Promise; openAutofillOverlay(options: OpenAutofillOverlayOptions): void; removeAutofillOverlay(): void; diff --git a/apps/browser/src/autofill/services/abstractions/autofill.service.ts b/apps/browser/src/autofill/services/abstractions/autofill.service.ts index 3e8472f2c1a..a0959db72cb 100644 --- a/apps/browser/src/autofill/services/abstractions/autofill.service.ts +++ b/apps/browser/src/autofill/services/abstractions/autofill.service.ts @@ -47,19 +47,19 @@ export abstract class AutofillService { injectAutofillScripts: ( sender: chrome.runtime.MessageSender, autofillV2?: boolean, - autofillOverlay?: boolean + autofillOverlay?: boolean, ) => Promise; getFormsWithPasswordFields: (pageDetails: AutofillPageDetails) => FormData[]; doAutoFill: (options: AutoFillOptions) => Promise; doAutoFillOnTab: ( pageDetails: PageDetail[], tab: chrome.tabs.Tab, - fromCommand: boolean + fromCommand: boolean, ) => Promise; doAutoFillActiveTab: ( pageDetails: PageDetail[], fromCommand: boolean, - cipherType?: CipherType + cipherType?: CipherType, ) => Promise; isPasswordRepromptRequired: (cipher: CipherView, tab: chrome.tabs.Tab) => Promise; } diff --git a/apps/browser/src/autofill/services/abstractions/collect-autofill-content.service.ts b/apps/browser/src/autofill/services/abstractions/collect-autofill-content.service.ts index e4a409eb599..78befa7bc61 100644 --- a/apps/browser/src/autofill/services/abstractions/collect-autofill-content.service.ts +++ b/apps/browser/src/autofill/services/abstractions/collect-autofill-content.service.ts @@ -20,7 +20,7 @@ interface CollectAutofillContentService { queryAllTreeWalkerNodes( rootNode: Node, filterCallback: CallableFunction, - isObservingShadowRoot?: boolean + isObservingShadowRoot?: boolean, ): Node[]; } diff --git a/apps/browser/src/autofill/services/autofill-overlay-content.service.spec.ts b/apps/browser/src/autofill/services/autofill-overlay-content.service.spec.ts index 12b9f19e030..7753a4b2672 100644 --- a/apps/browser/src/autofill/services/autofill-overlay-content.service.spec.ts +++ b/apps/browser/src/autofill/services/autofill-overlay-content.service.spec.ts @@ -58,11 +58,11 @@ describe("AutofillOverlayContentService", () => { jest.spyOn(window, "addEventListener"); setupGlobalEventListenersSpy = jest.spyOn( autofillOverlayContentService as any, - "setupGlobalEventListeners" + "setupGlobalEventListeners", ); setupMutationObserverSpy = jest.spyOn( autofillOverlayContentService as any, - "setupMutationObserver" + "setupMutationObserver", ); }); @@ -76,7 +76,7 @@ describe("AutofillOverlayContentService", () => { expect(document.addEventListener).toHaveBeenCalledWith( "DOMContentLoaded", - setupGlobalEventListenersSpy + setupGlobalEventListenersSpy, ); expect(setupGlobalEventListenersSpy).not.toHaveBeenCalled(); }); @@ -84,21 +84,21 @@ describe("AutofillOverlayContentService", () => { it("sets up a visibility change listener for the DOM", () => { const handleVisibilityChangeEventSpy = jest.spyOn( autofillOverlayContentService as any, - "handleVisibilityChangeEvent" + "handleVisibilityChangeEvent", ); autofillOverlayContentService.init(); expect(document.addEventListener).toHaveBeenCalledWith( "visibilitychange", - handleVisibilityChangeEventSpy + handleVisibilityChangeEventSpy, ); }); it("sets up a focus out listener for the window", () => { const handleFormFieldBlurEventSpy = jest.spyOn( autofillOverlayContentService as any, - "handleFormFieldBlurEvent" + "handleFormFieldBlurEvent", ); autofillOverlayContentService.init(); @@ -112,15 +112,15 @@ describe("AutofillOverlayContentService", () => { .mockImplementation(() => mock({ observe: jest.fn() })); const handleOverlayElementMutationObserverUpdateSpy = jest.spyOn( autofillOverlayContentService as any, - "handleOverlayElementMutationObserverUpdate" + "handleOverlayElementMutationObserverUpdate", ); const handleBodyElementMutationObserverUpdateSpy = jest.spyOn( autofillOverlayContentService as any, - "handleBodyElementMutationObserverUpdate" + "handleBodyElementMutationObserverUpdate", ); const handleDocumentElementMutationObserverUpdateSpy = jest.spyOn( autofillOverlayContentService as any, - "handleDocumentElementMutationObserverUpdate" + "handleDocumentElementMutationObserverUpdate", ); autofillOverlayContentService.init(); @@ -128,15 +128,15 @@ describe("AutofillOverlayContentService", () => { expect(setupMutationObserverSpy).toHaveBeenCalledTimes(1); expect(globalThis.MutationObserver).toHaveBeenNthCalledWith( 1, - handleOverlayElementMutationObserverUpdateSpy + handleOverlayElementMutationObserverUpdateSpy, ); expect(globalThis.MutationObserver).toHaveBeenNthCalledWith( 2, - handleBodyElementMutationObserverUpdateSpy + handleBodyElementMutationObserverUpdateSpy, ); expect(globalThis.MutationObserver).toHaveBeenNthCalledWith( 3, - handleDocumentElementMutationObserverUpdateSpy + handleDocumentElementMutationObserverUpdateSpy, ); }); }); @@ -154,7 +154,7 @@ describe("AutofillOverlayContentService", () => { `; autofillFieldElement = document.getElementById( - "username-field" + "username-field", ) as ElementWithOpId; autofillFieldElement.opid = "op-1"; jest.spyOn(autofillFieldElement, "addEventListener"); @@ -176,7 +176,7 @@ describe("AutofillOverlayContentService", () => { autofillOverlayContentService.setupAutofillOverlayListenerOnField( autofillFieldElement, - autofillFieldData + autofillFieldData, ); expect(autofillFieldElement.addEventListener).not.toHaveBeenCalled(); @@ -187,7 +187,7 @@ describe("AutofillOverlayContentService", () => { autofillOverlayContentService.setupAutofillOverlayListenerOnField( autofillFieldElement, - autofillFieldData + autofillFieldData, ); expect(autofillFieldElement.addEventListener).not.toHaveBeenCalled(); @@ -198,7 +198,7 @@ describe("AutofillOverlayContentService", () => { autofillOverlayContentService.setupAutofillOverlayListenerOnField( autofillFieldElement, - autofillFieldData + autofillFieldData, ); expect(autofillFieldElement.addEventListener).not.toHaveBeenCalled(); @@ -210,7 +210,7 @@ describe("AutofillOverlayContentService", () => { autofillOverlayContentService.setupAutofillOverlayListenerOnField( autofillFieldElement, - autofillFieldData + autofillFieldData, ); expect(autofillFieldElement.addEventListener).not.toHaveBeenCalled(); @@ -222,7 +222,7 @@ describe("AutofillOverlayContentService", () => { autofillOverlayContentService.setupAutofillOverlayListenerOnField( autofillFieldElement, - autofillFieldData + autofillFieldData, ); expect(autofillFieldElement.addEventListener).not.toHaveBeenCalled(); @@ -233,7 +233,7 @@ describe("AutofillOverlayContentService", () => { autofillOverlayContentService.setupAutofillOverlayListenerOnField( autofillFieldElement, - autofillFieldData + autofillFieldData, ); expect(autofillFieldElement.addEventListener).not.toHaveBeenCalled(); @@ -244,7 +244,7 @@ describe("AutofillOverlayContentService", () => { autofillOverlayContentService.setupAutofillOverlayListenerOnField( autofillFieldElement, - autofillFieldData + autofillFieldData, ); expect(autofillFieldElement.addEventListener).not.toHaveBeenCalled(); @@ -258,12 +258,12 @@ describe("AutofillOverlayContentService", () => { await autofillOverlayContentService.setupAutofillOverlayListenerOnField( autofillFieldElement, - autofillFieldData + autofillFieldData, ); expect(sendExtensionMessageSpy).toHaveBeenCalledWith("getAutofillOverlayVisibility"); expect(autofillOverlayContentService["autofillOverlayVisibility"]).toEqual( - AutofillOverlayVisibility.OnFieldFocus + AutofillOverlayVisibility.OnFieldFocus, ); }); @@ -273,11 +273,11 @@ describe("AutofillOverlayContentService", () => { await autofillOverlayContentService.setupAutofillOverlayListenerOnField( autofillFieldElement, - autofillFieldData + autofillFieldData, ); expect(autofillOverlayContentService["autofillOverlayVisibility"]).toEqual( - AutofillOverlayVisibility.OnFieldFocus + AutofillOverlayVisibility.OnFieldFocus, ); }); }); @@ -296,23 +296,23 @@ describe("AutofillOverlayContentService", () => { await autofillOverlayContentService.setupAutofillOverlayListenerOnField( autofillFieldElement, - autofillFieldData + autofillFieldData, ); expect(autofillFieldElement.removeEventListener).toHaveBeenNthCalledWith( 1, "input", - inputHandler + inputHandler, ); expect(autofillFieldElement.removeEventListener).toHaveBeenNthCalledWith( 2, "click", - clickHandler + clickHandler, ); expect(autofillFieldElement.removeEventListener).toHaveBeenNthCalledWith( 3, "focus", - focusHandler + focusHandler, ); }); @@ -320,7 +320,7 @@ describe("AutofillOverlayContentService", () => { beforeEach(async () => { await autofillOverlayContentService.setupAutofillOverlayListenerOnField( autofillFieldElement, - autofillFieldData + autofillFieldData, ); }); @@ -343,7 +343,7 @@ describe("AutofillOverlayContentService", () => { beforeEach(async () => { await autofillOverlayContentService.setupAutofillOverlayListenerOnField( autofillFieldElement, - autofillFieldData + autofillFieldData, ); jest.spyOn(globalThis.customElements, "define").mockImplementation(); }); @@ -359,7 +359,7 @@ describe("AutofillOverlayContentService", () => { it("repositions the overlay if autofill is not currently filling when the `Enter` key is pressed", () => { const handleOverlayRepositionEventSpy = jest.spyOn( autofillOverlayContentService as any, - "handleOverlayRepositionEvent" + "handleOverlayRepositionEvent", ); autofillOverlayContentService["isCurrentlyFilling"] = false; @@ -371,7 +371,7 @@ describe("AutofillOverlayContentService", () => { it("skips repositioning the overlay if autofill is currently filling when the `Enter` key is pressed", () => { const handleOverlayRepositionEventSpy = jest.spyOn( autofillOverlayContentService as any, - "handleOverlayRepositionEvent" + "handleOverlayRepositionEvent", ); autofillOverlayContentService["isCurrentlyFilling"] = true; @@ -384,11 +384,11 @@ describe("AutofillOverlayContentService", () => { jest.useFakeTimers(); const updateMostRecentlyFocusedFieldSpy = jest.spyOn( autofillOverlayContentService as any, - "updateMostRecentlyFocusedField" + "updateMostRecentlyFocusedField", ); const openAutofillOverlaySpy = jest.spyOn( autofillOverlayContentService as any, - "openAutofillOverlay" + "openAutofillOverlay", ); autofillOverlayContentService["isOverlayListVisible"] = false; @@ -420,13 +420,13 @@ describe("AutofillOverlayContentService", () => { it("ignores span elements that trigger the listener", async () => { const spanAutofillFieldElement = document.createElement( - "span" + "span", ) as ElementWithOpId; jest.spyOn(autofillOverlayContentService as any, "storeModifiedFormElement"); await autofillOverlayContentService.setupAutofillOverlayListenerOnField( spanAutofillFieldElement, - autofillFieldData + autofillFieldData, ); spanAutofillFieldElement.dispatchEvent(new Event("input")); @@ -437,28 +437,28 @@ describe("AutofillOverlayContentService", () => { it("stores the field as a user filled field if the form field data indicates that it is for a username", async () => { await autofillOverlayContentService.setupAutofillOverlayListenerOnField( autofillFieldElement, - autofillFieldData + autofillFieldData, ); autofillFieldElement.dispatchEvent(new Event("input")); expect(autofillOverlayContentService["userFilledFields"].username).toEqual( - autofillFieldElement + autofillFieldElement, ); }); it("stores the field as a user filled field if the form field is of type password", async () => { const passwordFieldElement = document.getElementById( - "password-field" + "password-field", ) as ElementWithOpId; await autofillOverlayContentService.setupAutofillOverlayListenerOnField( passwordFieldElement, - autofillFieldData + autofillFieldData, ); passwordFieldElement.dispatchEvent(new Event("input")); expect(autofillOverlayContentService["userFilledFields"].password).toEqual( - passwordFieldElement + passwordFieldElement, ); }); @@ -466,13 +466,13 @@ describe("AutofillOverlayContentService", () => { jest.spyOn(autofillOverlayContentService as any, "isUserAuthed").mockReturnValue(false); const removeAutofillOverlayListSpy = jest.spyOn( autofillOverlayContentService as any, - "removeAutofillOverlayList" + "removeAutofillOverlayList", ); (autofillFieldElement as HTMLInputElement).value = "test"; await autofillOverlayContentService.setupAutofillOverlayListenerOnField( autofillFieldElement, - autofillFieldData + autofillFieldData, ); autofillFieldElement.dispatchEvent(new Event("input")); @@ -484,13 +484,13 @@ describe("AutofillOverlayContentService", () => { autofillOverlayContentService["isOverlayCiphersPopulated"] = true; const removeAutofillOverlayListSpy = jest.spyOn( autofillOverlayContentService as any, - "removeAutofillOverlayList" + "removeAutofillOverlayList", ); (autofillFieldElement as HTMLInputElement).value = "test"; await autofillOverlayContentService.setupAutofillOverlayListenerOnField( autofillFieldElement, - autofillFieldData + autofillFieldData, ); autofillFieldElement.dispatchEvent(new Event("input")); @@ -503,7 +503,7 @@ describe("AutofillOverlayContentService", () => { await autofillOverlayContentService.setupAutofillOverlayListenerOnField( autofillFieldElement, - autofillFieldData + autofillFieldData, ); autofillFieldElement.dispatchEvent(new Event("input")); @@ -517,7 +517,7 @@ describe("AutofillOverlayContentService", () => { await autofillOverlayContentService.setupAutofillOverlayListenerOnField( autofillFieldElement, - autofillFieldData + autofillFieldData, ); autofillFieldElement.dispatchEvent(new Event("input")); @@ -532,7 +532,7 @@ describe("AutofillOverlayContentService", () => { await autofillOverlayContentService.setupAutofillOverlayListenerOnField( autofillFieldElement, - autofillFieldData + autofillFieldData, ); autofillFieldElement.dispatchEvent(new Event("input")); @@ -549,7 +549,7 @@ describe("AutofillOverlayContentService", () => { autofillOverlayContentService["isOverlayListVisible"] = false; await autofillOverlayContentService.setupAutofillOverlayListenerOnField( autofillFieldElement, - autofillFieldData + autofillFieldData, ); }); @@ -565,7 +565,7 @@ describe("AutofillOverlayContentService", () => { autofillFieldElement.dispatchEvent(new Event("click")); expect( - autofillOverlayContentService["triggerFormFieldFocusedAction"] + autofillOverlayContentService["triggerFormFieldFocusedAction"], ).not.toHaveBeenCalled(); }); @@ -575,7 +575,7 @@ describe("AutofillOverlayContentService", () => { autofillFieldElement.dispatchEvent(new Event("click")); expect( - autofillOverlayContentService["triggerFormFieldFocusedAction"] + autofillOverlayContentService["triggerFormFieldFocusedAction"], ).not.toHaveBeenCalled(); }); }); @@ -587,7 +587,7 @@ describe("AutofillOverlayContentService", () => { jest.spyOn(globalThis.customElements, "define").mockImplementation(); updateMostRecentlyFocusedFieldSpy = jest.spyOn( autofillOverlayContentService as any, - "updateMostRecentlyFocusedField" + "updateMostRecentlyFocusedField", ); autofillOverlayContentService["isCurrentlyFilling"] = false; }); @@ -599,7 +599,7 @@ describe("AutofillOverlayContentService", () => { AutofillOverlayVisibility.OnFieldFocus; await autofillOverlayContentService.setupAutofillOverlayListenerOnField( autofillFieldElement, - autofillFieldData + autofillFieldData, ); autofillFieldElement.dispatchEvent(new Event("focus")); @@ -610,14 +610,14 @@ describe("AutofillOverlayContentService", () => { it("updates the most recently focused field", async () => { await autofillOverlayContentService.setupAutofillOverlayListenerOnField( autofillFieldElement, - autofillFieldData + autofillFieldData, ); autofillFieldElement.dispatchEvent(new Event("focus")); expect(updateMostRecentlyFocusedFieldSpy).toHaveBeenCalledWith(autofillFieldElement); expect(autofillOverlayContentService["mostRecentlyFocusedField"]).toEqual( - autofillFieldElement + autofillFieldElement, ); }); @@ -627,7 +627,7 @@ describe("AutofillOverlayContentService", () => { AutofillOverlayVisibility.OnButtonClick; await autofillOverlayContentService.setupAutofillOverlayListenerOnField( autofillFieldElement, - autofillFieldData + autofillFieldData, ); autofillFieldElement.dispatchEvent(new Event("focus")); @@ -641,12 +641,12 @@ describe("AutofillOverlayContentService", () => { it("removes the overlay list if the form element has a value and the focused field is newly focused", async () => { autofillOverlayContentService["overlayListElement"] = document.createElement("div"); autofillOverlayContentService["mostRecentlyFocusedField"] = document.createElement( - "input" + "input", ) as ElementWithOpId; (autofillFieldElement as HTMLInputElement).value = "test"; await autofillOverlayContentService.setupAutofillOverlayListenerOnField( autofillFieldElement, - autofillFieldData + autofillFieldData, ); autofillFieldElement.dispatchEvent(new Event("focus")); @@ -664,7 +664,7 @@ describe("AutofillOverlayContentService", () => { AutofillOverlayVisibility.OnFieldFocus; await autofillOverlayContentService.setupAutofillOverlayListenerOnField( autofillFieldElement, - autofillFieldData + autofillFieldData, ); autofillFieldElement.dispatchEvent(new Event("focus")); @@ -681,7 +681,7 @@ describe("AutofillOverlayContentService", () => { jest.spyOn(autofillOverlayContentService as any, "isUserAuthed").mockReturnValue(true); await autofillOverlayContentService.setupAutofillOverlayListenerOnField( autofillFieldElement, - autofillFieldData + autofillFieldData, ); autofillFieldElement.dispatchEvent(new Event("focus")); @@ -697,7 +697,7 @@ describe("AutofillOverlayContentService", () => { autofillOverlayContentService["isOverlayCiphersPopulated"] = true; await autofillOverlayContentService.setupAutofillOverlayListenerOnField( autofillFieldElement, - autofillFieldData + autofillFieldData, ); autofillFieldElement.dispatchEvent(new Event("focus")); @@ -719,12 +719,12 @@ describe("AutofillOverlayContentService", () => { await autofillOverlayContentService.setupAutofillOverlayListenerOnField( autofillFieldElement, - autofillFieldData + autofillFieldData, ); expect(sendExtensionMessageSpy).toHaveBeenCalledWith("openAutofillOverlay"); expect(autofillOverlayContentService["mostRecentlyFocusedField"]).toEqual( - autofillFieldElement + autofillFieldElement, ); }); @@ -733,11 +733,11 @@ describe("AutofillOverlayContentService", () => { await autofillOverlayContentService.setupAutofillOverlayListenerOnField( autofillFieldElement, - autofillFieldData + autofillFieldData, ); expect(autofillOverlayContentService["mostRecentlyFocusedField"]).toEqual( - autofillFieldElement + autofillFieldElement, ); }); }); @@ -754,7 +754,7 @@ describe("AutofillOverlayContentService", () => { `; autofillFieldElement = document.getElementById( - "username-field" + "username-field", ) as ElementWithOpId; autofillFieldElement.opid = "op-1"; autofillOverlayContentService["mostRecentlyFocusedField"] = autofillFieldElement; @@ -776,7 +776,7 @@ describe("AutofillOverlayContentService", () => { }); const focusMostRecentOverlayFieldSpy = jest.spyOn( autofillOverlayContentService as any, - "focusMostRecentOverlayField" + "focusMostRecentOverlayField", ); autofillOverlayContentService["openAutofillOverlay"]({ isFocusingFieldElement: true }); @@ -792,7 +792,7 @@ describe("AutofillOverlayContentService", () => { }); const focusMostRecentOverlayFieldSpy = jest.spyOn( autofillOverlayContentService as any, - "focusMostRecentOverlayField" + "focusMostRecentOverlayField", ); autofillOverlayContentService["openAutofillOverlay"]({ isFocusingFieldElement: true }); @@ -866,7 +866,7 @@ describe("AutofillOverlayContentService", () => { describe("focusMostRecentOverlayField", () => { it("focuses the most recently focused overlay field", () => { const mostRecentlyFocusedField = document.createElement( - "input" + "input", ) as ElementWithOpId; autofillOverlayContentService["mostRecentlyFocusedField"] = mostRecentlyFocusedField; jest.spyOn(mostRecentlyFocusedField, "focus"); @@ -880,7 +880,7 @@ describe("AutofillOverlayContentService", () => { describe("blurMostRecentOverlayField", () => { it("removes focus from the most recently focused overlay field", () => { const mostRecentlyFocusedField = document.createElement( - "input" + "input", ) as ElementWithOpId; autofillOverlayContentService["mostRecentlyFocusedField"] = mostRecentlyFocusedField; jest.spyOn(mostRecentlyFocusedField, "blur"); @@ -906,7 +906,7 @@ describe("AutofillOverlayContentService", () => { beforeEach(() => { document.body.innerHTML = `
`; autofillOverlayContentService["overlayButtonElement"] = document.querySelector( - ".overlay-button" + ".overlay-button", ) as HTMLElement; }); @@ -933,7 +933,7 @@ describe("AutofillOverlayContentService", () => { jest.spyOn(globalThis, "removeEventListener"); const handleOverlayRepositionEventSpy = jest.spyOn( autofillOverlayContentService as any, - "handleOverlayRepositionEvent" + "handleOverlayRepositionEvent", ); autofillOverlayContentService.removeAutofillOverlay(); @@ -943,11 +943,11 @@ describe("AutofillOverlayContentService", () => { handleOverlayRepositionEventSpy, { capture: true, - } + }, ); expect(globalThis.removeEventListener).toHaveBeenCalledWith( EVENTS.RESIZE, - handleOverlayRepositionEventSpy + handleOverlayRepositionEventSpy, ); }); }); @@ -956,7 +956,7 @@ describe("AutofillOverlayContentService", () => { beforeEach(() => { document.body.innerHTML = `
`; autofillOverlayContentService["overlayListElement"] = document.querySelector( - ".overlay-list" + ".overlay-list", ) as HTMLElement; }); @@ -1011,10 +1011,10 @@ describe("AutofillOverlayContentService", () => { `; const usernameField = document.getElementById( - "username-field" + "username-field", ) as ElementWithOpId; const passwordField = document.getElementById( - "password-field" + "password-field", ) as ElementWithOpId; usernameField.value = "test-username"; passwordField.value = "test-password"; @@ -1054,11 +1054,11 @@ describe("AutofillOverlayContentService", () => {
`; autofillFieldElement = document.getElementById( - "username-field" + "username-field", ) as ElementWithOpId; autofillFieldElement.opid = "op-1"; previousFocusableElement = document.querySelector( - ".previous-focusable-element" + ".previous-focusable-element", ) as HTMLElement; nextFocusableElement = document.querySelector(".next-focusable-element") as HTMLElement; autofillFieldFocusSpy = jest.spyOn(autofillFieldElement, "focus"); @@ -1099,7 +1099,7 @@ describe("AutofillOverlayContentService", () => { jest.useFakeTimers(); const removeAutofillOverlaySpy = jest.spyOn( autofillOverlayContentService as any, - "removeAutofillOverlay" + "removeAutofillOverlay", ); autofillOverlayContentService.redirectOverlayFocusOut(RedirectFocusDirection.Current); @@ -1149,7 +1149,7 @@ describe("AutofillOverlayContentService", () => { `; const usernameField = document.getElementById( - "username-field" + "username-field", ) as ElementWithOpId; autofillOverlayContentService["mostRecentlyFocusedField"] = usernameField; autofillOverlayContentService["setOverlayRepositionEventListeners"](); @@ -1196,7 +1196,7 @@ describe("AutofillOverlayContentService", () => { .mockReturnValue(false); const removeAutofillOverlaySpy = jest.spyOn( autofillOverlayContentService as any, - "removeAutofillOverlay" + "removeAutofillOverlay", ); autofillOverlayContentService["mostRecentlyFocusedField"] = undefined; @@ -1225,7 +1225,7 @@ describe("AutofillOverlayContentService", () => { }); const clearUserInteractionEventTimeoutSpy = jest.spyOn( autofillOverlayContentService as any, - "clearUserInteractionEventTimeout" + "clearUserInteractionEventTimeout", ); globalThis.dispatchEvent(new Event(EVENTS.SCROLL)); @@ -1255,7 +1255,7 @@ describe("AutofillOverlayContentService", () => { }); const removeAutofillOverlaySpy = jest.spyOn( autofillOverlayContentService as any, - "removeAutofillOverlay" + "removeAutofillOverlay", ); globalThis.dispatchEvent(new Event(EVENTS.SCROLL)); @@ -1277,7 +1277,7 @@ describe("AutofillOverlayContentService", () => { `; usernameField = document.getElementById( - "username-field" + "username-field", ) as ElementWithOpId; usernameField.style.setProperty("display", "block", "important"); jest.spyOn(usernameField, "removeAttribute"); @@ -1285,7 +1285,7 @@ describe("AutofillOverlayContentService", () => { jest .spyOn( autofillOverlayContentService as any, - "isTriggeringExcessiveMutationObserverIterations" + "isTriggeringExcessiveMutationObserverIterations", ) .mockReturnValue(false); }); @@ -1294,7 +1294,7 @@ describe("AutofillOverlayContentService", () => { jest .spyOn( autofillOverlayContentService as any, - "isTriggeringExcessiveMutationObserverIterations" + "isTriggeringExcessiveMutationObserverIterations", ) .mockReturnValue(true); @@ -1344,7 +1344,7 @@ describe("AutofillOverlayContentService", () => { expect(usernameField.style.setProperty).toHaveBeenCalledWith( "position", "fixed", - "important" + "important", ); expect(usernameField.style.setProperty).toHaveBeenCalledWith("display", "block", "important"); }); @@ -1368,7 +1368,7 @@ describe("AutofillOverlayContentService", () => { jest .spyOn( autofillOverlayContentService as any, - "isTriggeringExcessiveMutationObserverIterations" + "isTriggeringExcessiveMutationObserverIterations", ) .mockReturnValue(false); }); @@ -1386,7 +1386,7 @@ describe("AutofillOverlayContentService", () => { jest .spyOn( autofillOverlayContentService as any, - "isTriggeringExcessiveMutationObserverIterations" + "isTriggeringExcessiveMutationObserverIterations", ) .mockReturnValue(true); @@ -1418,7 +1418,7 @@ describe("AutofillOverlayContentService", () => { expect(globalThis.document.body.insertBefore).toHaveBeenCalledWith( overlayButtonElement, - overlayListElement + overlayListElement, ); }); @@ -1429,7 +1429,7 @@ describe("AutofillOverlayContentService", () => { expect(globalThis.document.body.insertBefore).toHaveBeenCalledWith( overlayButtonElement, - overlayListElement + overlayListElement, ); }); @@ -1441,7 +1441,7 @@ describe("AutofillOverlayContentService", () => { expect(globalThis.document.body.insertBefore).toHaveBeenCalledWith( injectedElement, - overlayButtonElement + overlayButtonElement, ); }); }); @@ -1465,7 +1465,7 @@ describe("AutofillOverlayContentService", () => { jest .spyOn( autofillOverlayContentService as any, - "isTriggeringExcessiveMutationObserverIterations" + "isTriggeringExcessiveMutationObserverIterations", ) .mockReturnValue(false); }); @@ -1485,7 +1485,7 @@ describe("AutofillOverlayContentService", () => { jest .spyOn( autofillOverlayContentService as any, - "isTriggeringExcessiveMutationObserverIterations" + "isTriggeringExcessiveMutationObserverIterations", ) .mockReturnValue(true); @@ -1551,7 +1551,7 @@ describe("AutofillOverlayContentService", () => { const clearTimeoutSpy = jest.spyOn(globalThis, "clearTimeout"); autofillOverlayContentService["mutationObserverIterationsResetTimeout"] = setTimeout( jest.fn(), - 123 + 123, ); autofillOverlayContentService["isTriggeringExcessiveMutationObserverIterations"](); @@ -1573,11 +1573,11 @@ describe("AutofillOverlayContentService", () => { autofillOverlayContentService["mutationObserverIterations"] = 101; const blurMostRecentOverlayFieldSpy = jest.spyOn( autofillOverlayContentService as any, - "blurMostRecentOverlayField" + "blurMostRecentOverlayField", ); const removeAutofillOverlaySpy = jest.spyOn( autofillOverlayContentService as any, - "removeAutofillOverlay" + "removeAutofillOverlay", ); autofillOverlayContentService["isTriggeringExcessiveMutationObserverIterations"](); diff --git a/apps/browser/src/autofill/services/autofill-overlay-content.service.ts b/apps/browser/src/autofill/services/autofill-overlay-content.service.ts index fc1136a368b..9e5acae887c 100644 --- a/apps/browser/src/autofill/services/autofill-overlay-content.service.ts +++ b/apps/browser/src/autofill/services/autofill-overlay-content.service.ts @@ -80,7 +80,7 @@ class AutofillOverlayContentService implements AutofillOverlayContentServiceInte */ async setupAutofillOverlayListenerOnField( formFieldElement: ElementWithOpId, - autofillFieldData: AutofillField + autofillFieldData: AutofillField, ) { if (this.isIgnoredField(autofillFieldData)) { return; @@ -241,7 +241,7 @@ class AutofillOverlayContentService implements AutofillOverlayContentServiceInte } const focusedElementIndex = this.focusableElements.findIndex( - (element) => element === this.mostRecentlyFocusedField + (element) => element === this.mostRecentlyFocusedField, ); const indexOffset = direction === RedirectFocusDirection.Previous ? -1 : 1; @@ -263,15 +263,15 @@ class AutofillOverlayContentService implements AutofillOverlayContentServiceInte formFieldElement.addEventListener(EVENTS.KEYUP, this.handleFormFieldKeyupEvent); formFieldElement.addEventListener( EVENTS.INPUT, - this.handleFormFieldInputEvent(formFieldElement) + this.handleFormFieldInputEvent(formFieldElement), ); formFieldElement.addEventListener( EVENTS.CLICK, - this.handleFormFieldClickEvent(formFieldElement) + this.handleFormFieldClickEvent(formFieldElement), ); formFieldElement.addEventListener( EVENTS.FOCUS, - this.handleFormFieldFocusEvent(formFieldElement) + this.handleFormFieldFocusEvent(formFieldElement), ); } @@ -314,7 +314,7 @@ class AutofillOverlayContentService implements AutofillOverlayContentServiceInte */ private getFormFieldHandlerMemoIndex( formFieldElement: ElementWithOpId, - event: string + event: string, ) { return `${formFieldElement.opid}-${formFieldElement.id}-${event}-handler`; } @@ -381,7 +381,7 @@ class AutofillOverlayContentService implements AutofillOverlayContentServiceInte private handleFormFieldInputEvent = (formFieldElement: ElementWithOpId) => { return this.useEventHandlersMemo( () => this.triggerFormFieldInput(formFieldElement), - this.getFormFieldHandlerMemoIndex(formFieldElement, EVENTS.INPUT) + this.getFormFieldHandlerMemoIndex(formFieldElement, EVENTS.INPUT), ); }; @@ -436,7 +436,7 @@ class AutofillOverlayContentService implements AutofillOverlayContentServiceInte private handleFormFieldClickEvent = (formFieldElement: ElementWithOpId) => { return this.useEventHandlersMemo( () => this.triggerFormFieldClickedAction(formFieldElement), - this.getFormFieldHandlerMemoIndex(formFieldElement, EVENTS.CLICK) + this.getFormFieldHandlerMemoIndex(formFieldElement, EVENTS.CLICK), ); }; @@ -462,7 +462,7 @@ class AutofillOverlayContentService implements AutofillOverlayContentServiceInte private handleFormFieldFocusEvent = (formFieldElement: ElementWithOpId) => { return this.useEventHandlersMemo( () => this.triggerFormFieldFocusedAction(formFieldElement), - this.getFormFieldHandlerMemoIndex(formFieldElement, EVENTS.FOCUS) + this.getFormFieldHandlerMemoIndex(formFieldElement, EVENTS.FOCUS), ); }; @@ -637,13 +637,12 @@ class AutofillOverlayContentService implements AutofillOverlayContentServiceInte * @param formFieldElement - The form field element that triggered the focus event. */ private async updateMostRecentlyFocusedField( - formFieldElement: ElementWithOpId + formFieldElement: ElementWithOpId, ) { this.mostRecentlyFocusedField = formFieldElement; const { paddingRight, paddingLeft } = globalThis.getComputedStyle(formFieldElement); - const { width, height, top, left } = await this.getMostRecentlyFocusedFieldRects( - formFieldElement - ); + const { width, height, top, left } = + await this.getMostRecentlyFocusedFieldRects(formFieldElement); this.focusedFieldData = { focusedFieldStyles: { paddingRight, paddingLeft }, focusedFieldRects: { width, height, top, left }, @@ -664,11 +663,10 @@ class AutofillOverlayContentService implements AutofillOverlayContentServiceInte * @param formFieldElement - The form field element that triggered the focus event. */ private async getMostRecentlyFocusedFieldRects( - formFieldElement: ElementWithOpId + formFieldElement: ElementWithOpId, ) { - const focusedFieldRects = await this.getBoundingClientRectFromIntersectionObserver( - formFieldElement - ); + const focusedFieldRects = + await this.getBoundingClientRectFromIntersectionObserver(formFieldElement); if (focusedFieldRects) { return focusedFieldRects; } @@ -682,7 +680,7 @@ class AutofillOverlayContentService implements AutofillOverlayContentServiceInte * @param formFieldElement - The form field element that triggered the focus event. */ private async getBoundingClientRectFromIntersectionObserver( - formFieldElement: ElementWithOpId + formFieldElement: ElementWithOpId, ): Promise { if (!("IntersectionObserver" in window) && !("IntersectionObserverEntry" in window)) { return null; @@ -703,7 +701,7 @@ class AutofillOverlayContentService implements AutofillOverlayContentServiceInte root: globalThis.document.body, rootMargin: "0px", threshold: 0.9999, // Safari doesn't seem to function properly with a threshold of 1 - } + }, ); intersectionObserver.observe(formFieldElement); }); @@ -896,15 +894,15 @@ class AutofillOverlayContentService implements AutofillOverlayContentServiceInte */ private setupMutationObserver = () => { this.overlayElementsMutationObserver = new MutationObserver( - this.handleOverlayElementMutationObserverUpdate + this.handleOverlayElementMutationObserverUpdate, ); this.bodyElementMutationObserver = new MutationObserver( - this.handleBodyElementMutationObserverUpdate + this.handleBodyElementMutationObserverUpdate, ); const documentElementMutationObserver = new MutationObserver( - this.handleDocumentElementMutationObserverUpdate + this.handleDocumentElementMutationObserverUpdate, ); documentElementMutationObserver.observe(globalThis.document.documentElement, { childList: true, @@ -1095,7 +1093,7 @@ class AutofillOverlayContentService implements AutofillOverlayContentServiceInte this.mutationObserverIterations++; this.mutationObserverIterationsResetTimeout = setTimeout( () => (this.mutationObserverIterations = 0), - 2000 + 2000, ); if (this.mutationObserverIterations > 100) { diff --git a/apps/browser/src/autofill/services/autofill.service.spec.ts b/apps/browser/src/autofill/services/autofill.service.spec.ts index c4bea144d2b..aa9232c791f 100644 --- a/apps/browser/src/autofill/services/autofill.service.spec.ts +++ b/apps/browser/src/autofill/services/autofill.service.spec.ts @@ -63,7 +63,7 @@ describe("AutofillService", () => { eventCollectionService, logService, settingsService, - userVerificationService + userVerificationService, ); }); @@ -178,7 +178,7 @@ describe("AutofillService", () => { true, true, false, - true + true, ); expect(formData).toStrictEqual([]); }); @@ -280,7 +280,7 @@ describe("AutofillService", () => { passwordInputField, true, true, - false + false, ); expect(formData).toStrictEqual([ { @@ -415,7 +415,7 @@ describe("AutofillService", () => { cipher: autofillOptions.cipher, tabUrl: autofillOptions.tab.url, defaultUriMatch: 0, - } + }, ); expect(autofillService["generateLoginFillScript"]).toHaveBeenCalled(); expect(logService.info).not.toHaveBeenCalled(); @@ -448,11 +448,11 @@ describe("AutofillService", () => { { frameId: currentAutofillPageDetails.frameId, }, - expect.any(Function) + expect.any(Function), ); expect(eventCollectionService.collect).toHaveBeenCalledWith( EventType.Cipher_ClientAutofilled, - autofillOptions.cipher.id + autofillOptions.cipher.id, ); expect(autofillResult).toBeNull(); }); @@ -479,7 +479,7 @@ describe("AutofillService", () => { expect(chrome.tabs.sendMessage).toHaveBeenCalled(); expect(eventCollectionService.collect).toHaveBeenCalledWith( EventType.Cipher_ClientAutofilled, - autofillOptions.cipher.id + autofillOptions.cipher.id, ); }); @@ -507,7 +507,7 @@ describe("AutofillService", () => { expect(chrome.tabs.sendMessage).toHaveBeenCalled(); expect(eventCollectionService.collect).toHaveBeenCalledWith( EventType.Cipher_ClientAutofilled, - autofillOptions.cipher.id + autofillOptions.cipher.id, ); }); @@ -521,7 +521,7 @@ describe("AutofillService", () => { triggerTestFailure(); } catch (error) { expect(logService.info).toHaveBeenCalledWith( - "Auto-fill on page load was blocked due to an untrusted iframe." + "Auto-fill on page load was blocked due to an untrusted iframe.", ); expect(error.message).toBe(didNotAutofillError); } @@ -535,7 +535,7 @@ describe("AutofillService", () => { await autofillService.doAutoFill(autofillOptions); expect(logService.info).not.toHaveBeenCalledWith( - "Auto-fill on page load was blocked due to an untrusted iframe." + "Auto-fill on page load was blocked due to an untrusted iframe.", ); }); @@ -891,7 +891,7 @@ describe("AutofillService", () => { const result = await autofillService.doAutoFillActiveTab( pageDetails, fromCommand, - CipherType.Login + CipherType.Login, ); expect(autofillService["getActiveTab"]).toHaveBeenCalled(); @@ -981,7 +981,7 @@ describe("AutofillService", () => { await autofillService.doAutoFillActiveTab( identityFormPageDetails, false, - CipherType.Identity + CipherType.Identity, ); expect(autofillService["cipherService"].getAllDecryptedForUrl).toHaveBeenCalled(); @@ -1066,7 +1066,7 @@ describe("AutofillService", () => { it("returns null if the page details are not provided", async () => { const value = await autofillService["generateFillScript"]( undefined, - generateFillScriptOptions + generateFillScriptOptions, ); expect(value).toBeNull(); @@ -1077,7 +1077,7 @@ describe("AutofillService", () => { const value = await autofillService["generateFillScript"]( pageDetail, - generateFillScriptOptions + generateFillScriptOptions, ); expect(value).toBeNull(); @@ -1101,7 +1101,7 @@ describe("AutofillService", () => { expect(AutofillService.fillByOpid).not.toHaveBeenCalledWith( expect.anything(), duplicateUsernameField, - duplicateUsernameField.value + duplicateUsernameField.value, ); }); @@ -1114,7 +1114,7 @@ describe("AutofillService", () => { expect(AutofillService.fillByOpid).not.toHaveBeenCalledWith( expect.anything(), defaultUsernameField, - defaultUsernameField.value + defaultUsernameField.value, ); }); @@ -1129,7 +1129,7 @@ describe("AutofillService", () => { 1, expect.anything(), defaultUsernameField, - defaultUsernameField.value + defaultUsernameField.value, ); }); @@ -1142,7 +1142,7 @@ describe("AutofillService", () => { expect(AutofillService.fillByOpid).not.toHaveBeenCalledWith( expect.anything(), defaultUsernameField, - defaultUsernameField.value + defaultUsernameField.value, ); }); @@ -1159,13 +1159,13 @@ describe("AutofillService", () => { 1, expect.anything(), defaultUsernameField, - defaultUsernameField.value + defaultUsernameField.value, ); expect(AutofillService.fillByOpid).toHaveBeenNthCalledWith( 2, expect.anything(), defaultPasswordField, - defaultPasswordField.value + defaultPasswordField.value, ); }); @@ -1183,19 +1183,19 @@ describe("AutofillService", () => { expect(generateFillScriptOptions.cipher.linkedFieldValue).toHaveBeenCalledTimes(1); expect(generateFillScriptOptions.cipher.linkedFieldValue).toHaveBeenCalledWith( - fieldLinkedId + fieldLinkedId, ); expect(AutofillService.fillByOpid).toHaveBeenNthCalledWith( 1, expect.anything(), defaultUsernameField, - linkedFieldValue + linkedFieldValue, ); expect(AutofillService.fillByOpid).toHaveBeenNthCalledWith( 2, expect.anything(), defaultPasswordField, - defaultPasswordField.value + defaultPasswordField.value, ); }); @@ -1212,7 +1212,7 @@ describe("AutofillService", () => { 1, expect.anything(), defaultUsernameField, - defaultUsernameFieldView.value + defaultUsernameFieldView.value, ); }); @@ -1227,7 +1227,7 @@ describe("AutofillService", () => { 1, expect.anything(), defaultUsernameField, - "false" + "false", ); }); }); @@ -1235,7 +1235,7 @@ describe("AutofillService", () => { it("returns a fill script generated for a login autofill", async () => { const fillScriptMock = createAutofillScriptMock( {}, - { "username-field": "username-value", "password-value": "password-value" } + { "username-field": "username-value", "password-value": "password-value" }, ); generateFillScriptOptions.cipher.type = CipherType.Login; jest @@ -1244,7 +1244,7 @@ describe("AutofillService", () => { const value = await autofillService["generateFillScript"]( pageDetail, - generateFillScriptOptions + generateFillScriptOptions, ); expect(autofillService["generateLoginFillScript"]).toHaveBeenCalledWith( @@ -1266,7 +1266,7 @@ describe("AutofillService", () => { "password-field": defaultPasswordField, "username-field": defaultUsernameField, }, - generateFillScriptOptions + generateFillScriptOptions, ); expect(value).toBe(fillScriptMock); }); @@ -1274,7 +1274,7 @@ describe("AutofillService", () => { it("returns a fill script generated for a card autofill", async () => { const fillScriptMock = createAutofillScriptMock( {}, - { "first-name-field": "first-name-value", "last-name-value": "last-name-value" } + { "first-name-field": "first-name-value", "last-name-value": "last-name-value" }, ); generateFillScriptOptions.cipher.type = CipherType.Card; jest @@ -1283,7 +1283,7 @@ describe("AutofillService", () => { const value = await autofillService["generateFillScript"]( pageDetail, - generateFillScriptOptions + generateFillScriptOptions, ); expect(autofillService["generateCardFillScript"]).toHaveBeenCalledWith( @@ -1305,7 +1305,7 @@ describe("AutofillService", () => { "password-field": defaultPasswordField, "username-field": defaultUsernameField, }, - generateFillScriptOptions + generateFillScriptOptions, ); expect(value).toBe(fillScriptMock); }); @@ -1313,7 +1313,7 @@ describe("AutofillService", () => { it("returns a fill script generated for an identity autofill", async () => { const fillScriptMock = createAutofillScriptMock( {}, - { "first-name-field": "first-name-value", "last-name-value": "last-name-value" } + { "first-name-field": "first-name-value", "last-name-value": "last-name-value" }, ); generateFillScriptOptions.cipher.type = CipherType.Identity; jest @@ -1322,7 +1322,7 @@ describe("AutofillService", () => { const value = await autofillService["generateFillScript"]( pageDetail, - generateFillScriptOptions + generateFillScriptOptions, ); expect(autofillService["generateIdentityFillScript"]).toHaveBeenCalledWith( @@ -1344,7 +1344,7 @@ describe("AutofillService", () => { "password-field": defaultPasswordField, "username-field": defaultUsernameField, }, - generateFillScriptOptions + generateFillScriptOptions, ); expect(value).toBe(fillScriptMock); }); @@ -1354,7 +1354,7 @@ describe("AutofillService", () => { const value = await autofillService["generateFillScript"]( pageDetail, - generateFillScriptOptions + generateFillScriptOptions, ); expect(value).toBeNull(); @@ -1412,7 +1412,7 @@ describe("AutofillService", () => { fillScript, pageDetails, filledFields, - options + options, ); expect(autofillService["inUntrustedIframe"]).not.toHaveBeenCalled(); @@ -1432,7 +1432,7 @@ describe("AutofillService", () => { fillScript, pageDetails, filledFields, - options + options, ); expect(value.savedUrls).toStrictEqual([]); @@ -1451,7 +1451,7 @@ describe("AutofillService", () => { fillScript, pageDetails, filledFields, - options + options, ); expect(value.savedUrls).toStrictEqual([ @@ -1475,7 +1475,7 @@ describe("AutofillService", () => { fillScript, pageDetails, filledFields, - options + options, ); expect(value.savedUrls).toStrictEqual([defaultLoginUriView.uri, secondUriView.uri]); @@ -1542,7 +1542,7 @@ describe("AutofillService", () => { fillScript, pageDetails, filledFields, - options + options, ); expect(AutofillService.loadPasswordFields).toHaveBeenCalledTimes(2); @@ -1552,7 +1552,7 @@ describe("AutofillService", () => { false, false, options.onlyEmptyFields, - options.fillNewPassword + options.fillNewPassword, ); expect(AutofillService.loadPasswordFields).toHaveBeenNthCalledWith( 2, @@ -1560,7 +1560,7 @@ describe("AutofillService", () => { true, true, options.onlyEmptyFields, - options.fillNewPassword + options.fillNewPassword, ); }); @@ -1579,7 +1579,7 @@ describe("AutofillService", () => { fillScript, pageDetails, filledFields, - options + options, ); expect(autofillService["findUsernameField"]).toHaveBeenCalledTimes(2); @@ -1589,7 +1589,7 @@ describe("AutofillService", () => { passwordField, false, false, - false + false, ); expect(autofillService["findUsernameField"]).toHaveBeenNthCalledWith( 2, @@ -1597,7 +1597,7 @@ describe("AutofillService", () => { passwordField, true, true, - false + false, ); }); @@ -1608,7 +1608,7 @@ describe("AutofillService", () => { fillScript, pageDetails, filledFields, - options + options, ); expect(autofillService["findUsernameField"]).toHaveBeenCalledTimes(1); @@ -1618,7 +1618,7 @@ describe("AutofillService", () => { passwordField, false, false, - false + false, ); expect(autofillService["findUsernameField"]).not.toHaveBeenNthCalledWith( 2, @@ -1626,7 +1626,7 @@ describe("AutofillService", () => { passwordField, true, true, - false + false, ); }); @@ -1636,7 +1636,7 @@ describe("AutofillService", () => { fillScript, pageDetails, filledFields, - options + options, ); expect(autofillService["findTotpField"]).toHaveBeenCalledTimes(2); @@ -1646,7 +1646,7 @@ describe("AutofillService", () => { passwordField, false, false, - false + false, ); expect(autofillService["findTotpField"]).toHaveBeenNthCalledWith( 2, @@ -1654,7 +1654,7 @@ describe("AutofillService", () => { passwordField, true, true, - false + false, ); }); @@ -1666,7 +1666,7 @@ describe("AutofillService", () => { fillScript, pageDetails, filledFields, - options + options, ); expect(autofillService["findTotpField"]).toHaveBeenCalledTimes(1); @@ -1676,7 +1676,7 @@ describe("AutofillService", () => { passwordField, false, false, - false + false, ); expect(autofillService["findTotpField"]).not.toHaveBeenNthCalledWith( 2, @@ -1684,7 +1684,7 @@ describe("AutofillService", () => { passwordField, true, true, - false + false, ); }); @@ -1695,7 +1695,7 @@ describe("AutofillService", () => { fillScript, pageDetails, filledFields, - options + options, ); expect(autofillService["findTotpField"]).not.toHaveBeenCalled(); @@ -1714,7 +1714,7 @@ describe("AutofillService", () => { fillScript, pageDetails, filledFields, - options + options, ); expect(autofillService["findUsernameField"]).toHaveBeenCalledTimes(1); @@ -1723,7 +1723,7 @@ describe("AutofillService", () => { passwordField, false, false, - true + true, ); }); @@ -1735,7 +1735,7 @@ describe("AutofillService", () => { fillScript, pageDetails, filledFields, - options + options, ); expect(autofillService["findUsernameField"]).toHaveBeenCalledTimes(2); @@ -1745,7 +1745,7 @@ describe("AutofillService", () => { passwordField, false, false, - true + true, ); expect(autofillService["findUsernameField"]).toHaveBeenNthCalledWith( 2, @@ -1753,7 +1753,7 @@ describe("AutofillService", () => { passwordField, true, true, - true + true, ); }); @@ -1766,7 +1766,7 @@ describe("AutofillService", () => { fillScript, pageDetails, filledFields, - options + options, ); expect(autofillService["findUsernameField"]).toHaveBeenCalledTimes(1); @@ -1776,7 +1776,7 @@ describe("AutofillService", () => { passwordField, false, false, - true + true, ); expect(autofillService["findUsernameField"]).not.toHaveBeenNthCalledWith( 2, @@ -1784,7 +1784,7 @@ describe("AutofillService", () => { passwordField, true, true, - true + true, ); }); @@ -1795,7 +1795,7 @@ describe("AutofillService", () => { fillScript, pageDetails, filledFields, - options + options, ); expect(autofillService["findTotpField"]).toHaveBeenCalledTimes(1); @@ -1804,7 +1804,7 @@ describe("AutofillService", () => { passwordField, false, false, - true + true, ); }); @@ -1818,7 +1818,7 @@ describe("AutofillService", () => { fillScript, pageDetails, filledFields, - options + options, ); expect(autofillService["findTotpField"]).toHaveBeenCalledTimes(2); @@ -1828,7 +1828,7 @@ describe("AutofillService", () => { passwordField, false, false, - true + true, ); expect(autofillService["findTotpField"]).toHaveBeenNthCalledWith( 2, @@ -1836,7 +1836,7 @@ describe("AutofillService", () => { passwordField, true, true, - true + true, ); }); }); @@ -1913,40 +1913,40 @@ describe("AutofillService", () => { fillScript, pageDetails, filledFields, - options + options, ); expect(AutofillService.fieldIsFuzzyMatch).toHaveBeenCalledTimes(4); expect(AutofillService.fieldIsFuzzyMatch).toHaveBeenNthCalledWith( 1, usernameField, - AutoFillConstants.UsernameFieldNames + AutoFillConstants.UsernameFieldNames, ); expect(AutofillService.fieldIsFuzzyMatch).toHaveBeenNthCalledWith( 2, emailField, - AutoFillConstants.UsernameFieldNames + AutoFillConstants.UsernameFieldNames, ); expect(AutofillService.fieldIsFuzzyMatch).toHaveBeenNthCalledWith( 3, telephoneField, - AutoFillConstants.UsernameFieldNames + AutoFillConstants.UsernameFieldNames, ); expect(AutofillService.fieldIsFuzzyMatch).toHaveBeenNthCalledWith( 4, totpField, - AutoFillConstants.UsernameFieldNames + AutoFillConstants.UsernameFieldNames, ); expect(AutofillService.fieldIsFuzzyMatch).not.toHaveBeenNthCalledWith( 5, nonViewableField, - AutoFillConstants.UsernameFieldNames + AutoFillConstants.UsernameFieldNames, ); expect(AutofillService.fillByOpid).toHaveBeenCalledTimes(1); expect(AutofillService.fillByOpid).toHaveBeenCalledWith( fillScript, usernameField, - options.cipher.login.username + options.cipher.login.username, ); }); @@ -1957,12 +1957,12 @@ describe("AutofillService", () => { fillScript, pageDetails, filledFields, - options + options, ); expect(AutofillService.fieldIsFuzzyMatch).not.toHaveBeenCalledWith( expect.anything(), - AutoFillConstants.UsernameFieldNames + AutoFillConstants.UsernameFieldNames, ); }); @@ -1973,12 +1973,12 @@ describe("AutofillService", () => { fillScript, pageDetails, filledFields, - options + options, ); expect(AutofillService.fieldIsFuzzyMatch).toHaveBeenCalledWith( expect.anything(), - AutoFillConstants.TotpFieldNames + AutoFillConstants.TotpFieldNames, ); }); @@ -1989,12 +1989,12 @@ describe("AutofillService", () => { fillScript, pageDetails, filledFields, - options + options, ); expect(AutofillService.fieldIsFuzzyMatch).not.toHaveBeenCalledWith( expect.anything(), - AutoFillConstants.TotpFieldNames + AutoFillConstants.TotpFieldNames, ); }); }); @@ -2006,7 +2006,7 @@ describe("AutofillService", () => { fillScript, pageDetails, filledFields, - options + options, ); expect(value.untrustedIframe).toBe(true); @@ -2024,7 +2024,7 @@ describe("AutofillService", () => { fillScript, pageDetails, filledFields, - options + options, ); expect(autofillService["inUntrustedIframe"]).toHaveBeenCalledWith(pageDetails.url, options); @@ -2033,14 +2033,14 @@ describe("AutofillService", () => { false, false, options.onlyEmptyFields, - options.fillNewPassword + options.fillNewPassword, ); expect(autofillService["findUsernameField"]).toHaveBeenCalledWith( pageDetails, passwordField, false, false, - false + false, ); expect(AutofillService.fieldIsFuzzyMatch).not.toHaveBeenCalled(); expect(AutofillService.fillByOpid).toHaveBeenCalledTimes(2); @@ -2048,17 +2048,17 @@ describe("AutofillService", () => { 1, fillScript, usernameField, - options.cipher.login.username + options.cipher.login.username, ); expect(AutofillService.fillByOpid).toHaveBeenNthCalledWith( 2, fillScript, passwordField, - options.cipher.login.password + options.cipher.login.password, ); expect(AutofillService.setFillScriptForFocus).toHaveBeenCalledWith( filledFields, - fillScript + fillScript, ); expect(value).toStrictEqual({ autosubmit: null, @@ -2138,7 +2138,7 @@ describe("AutofillService", () => { fillScript, pageDetails, filledFields, - options + options, ); expect(value).toBeNull(); @@ -2171,7 +2171,7 @@ describe("AutofillService", () => { fillScript, pageDetails, filledFields, - options + options, ); expect(AutofillService.forCustomFieldsOnly).toHaveBeenCalledWith(spanField); @@ -2196,13 +2196,13 @@ describe("AutofillService", () => { fillScript, pageDetails, filledFields, - options + options, ); expect(AutofillService.forCustomFieldsOnly).toHaveBeenCalledWith(invalidField); expect(autofillService["isExcludedType"]).toHaveBeenCalledWith( invalidField.type, - AutoFillConstants.ExcludedAutofillTypes + AutoFillConstants.ExcludedAutofillTypes, ); expect(value).toStrictEqual(unmodifiedFillScriptValues); }); @@ -2225,7 +2225,7 @@ describe("AutofillService", () => { fillScript, pageDetails, filledFields, - options + options, ); expect(AutofillService.forCustomFieldsOnly).toHaveBeenCalledWith(notViewableField); @@ -2328,7 +2328,7 @@ describe("AutofillService", () => { fillScript, pageDetails, filledFields, - options + options, ); expect(AutofillService.forCustomFieldsOnly).toHaveBeenCalledTimes(6); @@ -2413,7 +2413,7 @@ describe("AutofillService", () => { fillScript, pageDetails, filledFields, - options + options, ); expect(value.script[2]).toStrictEqual(["fill_by_opid", expMonthField.opid, testValue]); @@ -2428,7 +2428,7 @@ describe("AutofillService", () => { fillScript, pageDetails, filledFields, - options + options, ); expect(value.script[2]).toStrictEqual(["fill_by_opid", expMonthField.opid, testValue]); @@ -2443,7 +2443,7 @@ describe("AutofillService", () => { fillScript, pageDetails, filledFields, - options + options, ); expect(value.script[2]).toStrictEqual(["fill_by_opid", expMonthField.opid, testValue]); @@ -2459,7 +2459,7 @@ describe("AutofillService", () => { fillScript, pageDetails, filledFields, - options + options, ); expect(value.script[2]).toStrictEqual(["fill_by_opid", expMonthField.opid, "05"]); @@ -2499,7 +2499,7 @@ describe("AutofillService", () => { fillScript, pageDetails, filledFields, - options + options, ); expect(value.script[2]).toStrictEqual(["fill_by_opid", expYearField.opid, someTestValue]); @@ -2510,7 +2510,7 @@ describe("AutofillService", () => { fillScript, pageDetails, filledFields, - options + options, ); expect(value.script[2]).toStrictEqual(["fill_by_opid", expYearField.opid, someTestValue]); @@ -2525,7 +2525,7 @@ describe("AutofillService", () => { fillScript, pageDetails, filledFields, - options + options, ); expect(value.script[2]).toStrictEqual(["fill_by_opid", expYearField.opid, yearValue]); @@ -2541,7 +2541,7 @@ describe("AutofillService", () => { fillScript, pageDetails, filledFields, - options + options, ); expect(value.script[2]).toStrictEqual([ @@ -2562,7 +2562,7 @@ describe("AutofillService", () => { fillScript, pageDetails, filledFields, - options + options, ); expect(value.script[2]).toStrictEqual([ @@ -2583,7 +2583,7 @@ describe("AutofillService", () => { fillScript, pageDetails, filledFields, - options + options, ); expect(value.script[2]).toStrictEqual(["fill_by_opid", expYearField.opid, yearValue]); @@ -2637,7 +2637,7 @@ describe("AutofillService", () => { fillScript, pageDetails, filledFields, - options + options, ); expect(value.script[2]).toStrictEqual(["fill_by_opid", "expirationDate", dateFormat[1]]); @@ -2649,7 +2649,7 @@ describe("AutofillService", () => { fillScript, pageDetails, filledFields, - options + options, ); expect(value.script[2]).toStrictEqual(["fill_by_opid", "expirationDate", "2024-05"]); @@ -2686,7 +2686,7 @@ describe("AutofillService", () => { expect(generateFillScriptOptions.cipher.login.matchesUri).toHaveBeenCalledWith( pageUrl, equivalentDomains, - generateFillScriptOptions.defaultUriMatch + generateFillScriptOptions.defaultUriMatch, ); expect(result).toBe(false); }); @@ -2705,7 +2705,7 @@ describe("AutofillService", () => { expect(generateFillScriptOptions.cipher.login.matchesUri).toHaveBeenCalledWith( pageUrl, equivalentDomains, - generateFillScriptOptions.defaultUriMatch + generateFillScriptOptions.defaultUriMatch, ); expect(result).toBe(true); }); @@ -2765,7 +2765,7 @@ describe("AutofillService", () => { fillScript, pageDetails, filledFields, - options + options, ); expect(value).toBeNull(); @@ -2795,7 +2795,7 @@ describe("AutofillService", () => { fillScript, pageDetails, filledFields, - options + options, ); expect(AutofillService.forCustomFieldsOnly).toHaveBeenCalledWith(customField); @@ -2812,13 +2812,13 @@ describe("AutofillService", () => { fillScript, pageDetails, filledFields, - options + options, ); expect(AutofillService.forCustomFieldsOnly).toHaveBeenCalledWith(excludedField); expect(autofillService["isExcludedType"]).toHaveBeenCalledWith( excludedField.type, - AutoFillConstants.ExcludedAutofillTypes + AutoFillConstants.ExcludedAutofillTypes, ); expect(AutofillService["isFieldMatch"]).not.toHaveBeenCalled(); expect(value.script).toStrictEqual([]); @@ -2832,7 +2832,7 @@ describe("AutofillService", () => { fillScript, pageDetails, filledFields, - options + options, ); expect(AutofillService.forCustomFieldsOnly).toHaveBeenCalledWith(viewableField); @@ -2852,19 +2852,19 @@ describe("AutofillService", () => { fillScript, pageDetails, filledFields, - options + options, ); expect(AutofillService["isFieldMatch"]).toHaveBeenCalledWith( fullNameField.htmlName, IdentityAutoFillConstants.FullNameFieldNames, - IdentityAutoFillConstants.FullNameFieldNameValues + IdentityAutoFillConstants.FullNameFieldNameValues, ); expect(autofillService["makeScriptActionWithValue"]).toHaveBeenCalledWith( fillScript, `${firstName} ${middleName} ${lastName}`, fullNameField, - filledFields + filledFields, ); expect(value.script[2]).toStrictEqual([ "fill_by_opid", @@ -2884,19 +2884,19 @@ describe("AutofillService", () => { fillScript, pageDetails, filledFields, - options + options, ); expect(AutofillService["isFieldMatch"]).toHaveBeenCalledWith( fullNameField.htmlName, IdentityAutoFillConstants.FullNameFieldNames, - IdentityAutoFillConstants.FullNameFieldNameValues + IdentityAutoFillConstants.FullNameFieldNameValues, ); expect(autofillService["makeScriptActionWithValue"]).toHaveBeenCalledWith( fillScript, lastName, fullNameField, - filledFields + filledFields, ); expect(value.script[2]).toStrictEqual(["fill_by_opid", fullNameField.opid, lastName]); }); @@ -2920,41 +2920,41 @@ describe("AutofillService", () => { fillScript, pageDetails, filledFields, - options + options, ); expect(AutofillService["isFieldMatch"]).toHaveBeenCalledWith( firstNameField.htmlName, - IdentityAutoFillConstants.FirstnameFieldNames + IdentityAutoFillConstants.FirstnameFieldNames, ); expect(AutofillService["isFieldMatch"]).toHaveBeenCalledWith( middleNameField.htmlName, - IdentityAutoFillConstants.MiddlenameFieldNames + IdentityAutoFillConstants.MiddlenameFieldNames, ); expect(AutofillService["isFieldMatch"]).toHaveBeenCalledWith( lastNameField.htmlName, - IdentityAutoFillConstants.LastnameFieldNames + IdentityAutoFillConstants.LastnameFieldNames, ); expect(autofillService["makeScriptAction"]).toHaveBeenCalledWith( fillScript, options.cipher.identity, expect.anything(), filledFields, - firstNameField.opid + firstNameField.opid, ); expect(autofillService["makeScriptAction"]).toHaveBeenCalledWith( fillScript, options.cipher.identity, expect.anything(), filledFields, - middleNameField.opid + middleNameField.opid, ); expect(autofillService["makeScriptAction"]).toHaveBeenCalledWith( fillScript, options.cipher.identity, expect.anything(), filledFields, - lastNameField.opid + lastNameField.opid, ); expect(value.script[2]).toStrictEqual(["fill_by_opid", firstNameField.opid, firstName]); expect(value.script[5]).toStrictEqual(["fill_by_opid", middleNameField.opid, middleName]); @@ -2974,30 +2974,30 @@ describe("AutofillService", () => { fillScript, pageDetails, filledFields, - options + options, ); expect(AutofillService["isFieldMatch"]).toHaveBeenCalledWith( titleField.htmlName, - IdentityAutoFillConstants.TitleFieldNames + IdentityAutoFillConstants.TitleFieldNames, ); expect(AutofillService["isFieldMatch"]).toHaveBeenCalledWith( emailField.htmlName, - IdentityAutoFillConstants.EmailFieldNames + IdentityAutoFillConstants.EmailFieldNames, ); expect(autofillService["makeScriptAction"]).toHaveBeenCalledWith( fillScript, options.cipher.identity, expect.anything(), filledFields, - titleField.opid + titleField.opid, ); expect(autofillService["makeScriptAction"]).toHaveBeenCalledWith( fillScript, options.cipher.identity, expect.anything(), filledFields, - emailField.opid + emailField.opid, ); expect(value.script[2]).toStrictEqual(["fill_by_opid", titleField.opid, title]); expect(value.script[5]).toStrictEqual(["fill_by_opid", emailField.opid, email]); @@ -3020,19 +3020,19 @@ describe("AutofillService", () => { fillScript, pageDetails, filledFields, - options + options, ); expect(AutofillService["isFieldMatch"]).toHaveBeenCalledWith( fullAddressField.htmlName, IdentityAutoFillConstants.AddressFieldNames, - IdentityAutoFillConstants.AddressFieldNameValues + IdentityAutoFillConstants.AddressFieldNameValues, ); expect(autofillService["makeScriptActionWithValue"]).toHaveBeenCalledWith( fillScript, `${address1}, ${address2}, ${address3}`, fullAddressField, - filledFields + filledFields, ); expect(value.script[2]).toStrictEqual([ "fill_by_opid", @@ -3092,48 +3092,48 @@ describe("AutofillService", () => { fillScript, pageDetails, filledFields, - options + options, ); expect(AutofillService["isFieldMatch"]).toHaveBeenCalledWith( address1Field.htmlName, - IdentityAutoFillConstants.Address1FieldNames + IdentityAutoFillConstants.Address1FieldNames, ); expect(AutofillService["isFieldMatch"]).toHaveBeenCalledWith( address2Field.htmlName, - IdentityAutoFillConstants.Address2FieldNames + IdentityAutoFillConstants.Address2FieldNames, ); expect(AutofillService["isFieldMatch"]).toHaveBeenCalledWith( address3Field.htmlName, - IdentityAutoFillConstants.Address3FieldNames + IdentityAutoFillConstants.Address3FieldNames, ); expect(AutofillService["isFieldMatch"]).toHaveBeenCalledWith( postalCodeField.htmlName, - IdentityAutoFillConstants.PostalCodeFieldNames + IdentityAutoFillConstants.PostalCodeFieldNames, ); expect(AutofillService["isFieldMatch"]).toHaveBeenCalledWith( cityField.htmlName, - IdentityAutoFillConstants.CityFieldNames + IdentityAutoFillConstants.CityFieldNames, ); expect(AutofillService["isFieldMatch"]).toHaveBeenCalledWith( stateField.htmlName, - IdentityAutoFillConstants.StateFieldNames + IdentityAutoFillConstants.StateFieldNames, ); expect(AutofillService["isFieldMatch"]).toHaveBeenCalledWith( countryField.htmlName, - IdentityAutoFillConstants.CountryFieldNames + IdentityAutoFillConstants.CountryFieldNames, ); expect(AutofillService["isFieldMatch"]).toHaveBeenCalledWith( phoneField.htmlName, - IdentityAutoFillConstants.PhoneFieldNames + IdentityAutoFillConstants.PhoneFieldNames, ); expect(AutofillService["isFieldMatch"]).toHaveBeenCalledWith( usernameField.htmlName, - IdentityAutoFillConstants.UserNameFieldNames + IdentityAutoFillConstants.UserNameFieldNames, ); expect(AutofillService["isFieldMatch"]).toHaveBeenCalledWith( companyField.htmlName, - IdentityAutoFillConstants.CompanyFieldNames + IdentityAutoFillConstants.CompanyFieldNames, ); expect(autofillService["makeScriptAction"]).toHaveBeenCalled(); expect(value.script[2]).toStrictEqual(["fill_by_opid", address1Field.opid, address1]); @@ -3158,14 +3158,14 @@ describe("AutofillService", () => { fillScript, pageDetails, filledFields, - options + options, ); expect(autofillService["makeScriptActionWithValue"]).toHaveBeenCalledWith( fillScript, "CA", expect.anything(), - expect.anything() + expect.anything(), ); expect(value.script[2]).toStrictEqual(["fill_by_opid", stateField.opid, "CA"]); }); @@ -3180,14 +3180,14 @@ describe("AutofillService", () => { fillScript, pageDetails, filledFields, - options + options, ); expect(autofillService["makeScriptActionWithValue"]).toHaveBeenCalledWith( fillScript, "ON", expect.anything(), - expect.anything() + expect.anything(), ); expect(value.script[2]).toStrictEqual(["fill_by_opid", stateField.opid, "ON"]); }); @@ -3202,14 +3202,14 @@ describe("AutofillService", () => { fillScript, pageDetails, filledFields, - options + options, ); expect(autofillService["makeScriptActionWithValue"]).toHaveBeenCalledWith( fillScript, "SO", expect.anything(), - expect.anything() + expect.anything(), ); expect(value.script[2]).toStrictEqual(["fill_by_opid", countryField.opid, "SO"]); }); @@ -3220,7 +3220,7 @@ describe("AutofillService", () => { it("returns true if the passed type is within the excluded type list", () => { const value = autofillService["isExcludedType"]( "hidden", - AutoFillConstants.ExcludedAutofillTypes + AutoFillConstants.ExcludedAutofillTypes, ); expect(value).toBe(true); @@ -3229,7 +3229,7 @@ describe("AutofillService", () => { it("returns true if the passed type is within the excluded type list", () => { const value = autofillService["isExcludedType"]( "text", - AutoFillConstants.ExcludedAutofillTypes + AutoFillConstants.ExcludedAutofillTypes, ); expect(value).toBe(false); @@ -3254,7 +3254,7 @@ describe("AutofillService", () => { const value = AutofillService["isFieldMatch"]( passedAttribute, passedOptions, - containsOptions + containsOptions, ); expect(value).toBe(true); @@ -3268,7 +3268,7 @@ describe("AutofillService", () => { const value = AutofillService["isFieldMatch"]( passedAttribute, passedOptions, - containsOptions + containsOptions, ); expect(value).toBe(false); @@ -3301,14 +3301,14 @@ describe("AutofillService", () => { options.cipher.login, fillFields, filledFields, - dataProp + dataProp, ); expect(autofillService["makeScriptActionWithValue"]).toHaveBeenCalledWith( fillScript, mockLoginView[dataProp], fillFields[dataProp], - filledFields + filledFields, ); }); @@ -3322,14 +3322,14 @@ describe("AutofillService", () => { fillFields, filledFields, dataProp, - fieldProp + fieldProp, ); expect(autofillService["makeScriptActionWithValue"]).toHaveBeenCalledWith( fillScript, mockLoginView[dataProp], fillFields[fieldProp], - filledFields + filledFields, ); }); }); @@ -3361,7 +3361,7 @@ describe("AutofillService", () => { fillScript, dataValue, fillFields["username-field"], - filledFields + filledFields, ); expect(AutofillService.hasValue).toHaveBeenCalledWith(dataValue); @@ -3384,14 +3384,14 @@ describe("AutofillService", () => { fillScript, dataValue, fillFields["username-field"], - filledFields + filledFields, ); expect(AutofillService.hasValue).toHaveBeenCalledWith(dataValue); expect(AutofillService.fillByOpid).toHaveBeenCalledWith( fillScript, fillFields["username-field"], - dataValue + dataValue, ); }); @@ -3411,7 +3411,7 @@ describe("AutofillService", () => { fillScript, dataValue, selectField, - filledFields + filledFields, ); expect(AutofillService.hasValue).toHaveBeenCalledWith(dataValue); @@ -3433,14 +3433,14 @@ describe("AutofillService", () => { fillScript, dataValue, selectField, - filledFields + filledFields, ); expect(AutofillService.hasValue).toHaveBeenCalledWith(dataValue); expect(AutofillService.fillByOpid).toHaveBeenCalledWith( fillScript, selectField, - "Some Other Username Value" + "Some Other Username Value", ); }); }); @@ -3681,7 +3681,7 @@ describe("AutofillService", () => { passwordField, false, false, - false + false, ); expect(result).toBe(null); @@ -3695,7 +3695,7 @@ describe("AutofillService", () => { passwordField, false, false, - false + false, ); expect(result).toBe(null); @@ -3712,7 +3712,7 @@ describe("AutofillService", () => { passwordField, false, false, - false + false, ); expect(result).toBe(null); @@ -3724,7 +3724,7 @@ describe("AutofillService", () => { passwordField, false, true, - false + false, ); expect(result).toBe(usernameField); @@ -3743,7 +3743,7 @@ describe("AutofillService", () => { passwordField, false, false, - false + false, ); expect(result).toBe(null); @@ -3755,7 +3755,7 @@ describe("AutofillService", () => { passwordField, false, false, - true + true, ); expect(result).toBe(usernameField); @@ -3774,7 +3774,7 @@ describe("AutofillService", () => { passwordField, false, false, - false + false, ); expect(result).toBe(null); @@ -3786,7 +3786,7 @@ describe("AutofillService", () => { passwordField, true, false, - false + false, ); expect(result).toBe(usernameField); @@ -3801,7 +3801,7 @@ describe("AutofillService", () => { passwordField, false, false, - false + false, ); expect(result).toBe(null); @@ -3829,14 +3829,14 @@ describe("AutofillService", () => { passwordField, false, false, - false + false, ); expect(result).toBe(usernameField2); expect(autofillService["findMatchingFieldIndex"]).toHaveBeenCalledTimes(2); expect(autofillService["findMatchingFieldIndex"]).not.toHaveBeenCalledWith( usernameField3, - AutoFillConstants.UsernameFieldNames + AutoFillConstants.UsernameFieldNames, ); }); }); @@ -3885,7 +3885,7 @@ describe("AutofillService", () => { passwordField, false, false, - false + false, ); expect(result).toBe(null); @@ -3902,7 +3902,7 @@ describe("AutofillService", () => { passwordField, false, false, - false + false, ); expect(result).toBe(null); @@ -3914,7 +3914,7 @@ describe("AutofillService", () => { passwordField, false, true, - false + false, ); expect(result).toBe(totpField); @@ -3932,7 +3932,7 @@ describe("AutofillService", () => { passwordField, false, false, - false + false, ); expect(result).toBe(null); @@ -3944,7 +3944,7 @@ describe("AutofillService", () => { passwordField, false, false, - true + true, ); expect(result).toBe(totpField); @@ -3963,7 +3963,7 @@ describe("AutofillService", () => { passwordField, false, false, - false + false, ); expect(result).toBe(null); @@ -3975,7 +3975,7 @@ describe("AutofillService", () => { passwordField, true, false, - false + false, ); expect(result).toBe(totpField); @@ -3990,7 +3990,7 @@ describe("AutofillService", () => { passwordField, false, false, - false + false, ); expect(result).toBe(null); @@ -4005,7 +4005,7 @@ describe("AutofillService", () => { passwordField, false, false, - false + false, ); expect(result).toBe(totpField); @@ -4039,7 +4039,7 @@ describe("AutofillService", () => { expect(autofillService["fieldPropertyIsMatch"]).toHaveBeenCalledWith( field, attribute[0], - value + value, ); expect(result).toBe(0); }); @@ -4075,7 +4075,7 @@ describe("AutofillService", () => { field, "htmlID", "id=username", - "id" + "id", ); expect(result).toBe(true); @@ -4088,7 +4088,7 @@ describe("AutofillService", () => { field, "htmlID", "id=some-othername", - "id" + "id", ); expect(result).toBe(false); @@ -4164,7 +4164,7 @@ describe("AutofillService", () => { const result = autofillService["fieldPropertyIsMatch"]( field, "htmlID", - "csv=some-value,some-other-value,some-third-value" + "csv=some-value,some-other-value,some-third-value", ); expect(result).toBe(false); @@ -4176,7 +4176,7 @@ describe("AutofillService", () => { const result = autofillService["fieldPropertyIsMatch"]( field, "htmlID", - "csv=some-value,some-other-value,some-third-value" + "csv=some-value,some-other-value,some-third-value", ); expect(result).toBe(true); @@ -4223,7 +4223,7 @@ describe("AutofillService", () => { expect(AutofillService.hasValue).toHaveBeenCalled(); expect(AutofillService["fuzzyMatch"]).toHaveBeenCalledWith( ["some-value"], - "some-false-value" + "some-false-value", ); expect(result).toBe(false); @@ -4280,7 +4280,7 @@ describe("AutofillService", () => { it("returns true if the passed value is within the options array", () => { const result = AutofillService["fuzzyMatch"]( ["some-other-value", "some-value"], - "some-value" + "some-value", ); expect(result).toBe(true); diff --git a/apps/browser/src/autofill/services/autofill.service.ts b/apps/browser/src/autofill/services/autofill.service.ts index b2672e86762..a1ef5a47a1c 100644 --- a/apps/browser/src/autofill/services/autofill.service.ts +++ b/apps/browser/src/autofill/services/autofill.service.ts @@ -43,7 +43,7 @@ export default class AutofillService implements AutofillServiceInterface { private eventCollectionService: EventCollectionService, private logService: LogService, private settingsService: SettingsService, - private userVerificationService: UserVerificationService + private userVerificationService: UserVerificationService, ) {} /** @@ -59,7 +59,7 @@ export default class AutofillService implements AutofillServiceInterface { async injectAutofillScripts( sender: chrome.runtime.MessageSender, autofillV2 = false, - autofillOverlay = false + autofillOverlay = false, ) { let mainAutofillScript = "autofill.js"; @@ -226,7 +226,7 @@ export default class AutofillService implements AutofillServiceInterface { url: tab.url, pageDetailsUrl: pd.details.url, }, - { frameId: pd.frameId } + { frameId: pd.frameId }, ); // Skip getting the TOTP code for clipboard in these cases @@ -245,7 +245,7 @@ export default class AutofillService implements AutofillServiceInterface { } return null; }); - }) + }), ); if (didAutofill) { @@ -270,7 +270,7 @@ export default class AutofillService implements AutofillServiceInterface { async doAutoFillOnTab( pageDetails: PageDetail[], tab: chrome.tabs.Tab, - fromCommand: boolean + fromCommand: boolean, ): Promise { let cipher: CipherView; if (fromCommand) { @@ -347,7 +347,7 @@ export default class AutofillService implements AutofillServiceInterface { async doAutoFillActiveTab( pageDetails: PageDetail[], fromCommand: boolean, - cipherType?: CipherType + cipherType?: CipherType, ): Promise { if (!pageDetails[0]?.details?.fields?.length) { return null; @@ -410,7 +410,7 @@ export default class AutofillService implements AutofillServiceInterface { */ private async generateFillScript( pageDetails: AutofillPageDetails, - options: GenerateFillScriptOptions + options: GenerateFillScriptOptions, ): Promise { if (!pageDetails || !options.cipher) { return null; @@ -465,7 +465,7 @@ export default class AutofillService implements AutofillServiceInterface { fillScript, pageDetails, filledFields, - options + options, ); break; case CipherType.Card: @@ -476,7 +476,7 @@ export default class AutofillService implements AutofillServiceInterface { fillScript, pageDetails, filledFields, - options + options, ); break; default: @@ -499,7 +499,7 @@ export default class AutofillService implements AutofillServiceInterface { fillScript: AutofillScript, pageDetails: AutofillPageDetails, filledFields: { [id: string]: AutofillField }, - options: GenerateFillScriptOptions + options: GenerateFillScriptOptions, ): Promise { if (!options.cipher.login) { return null; @@ -522,7 +522,7 @@ export default class AutofillService implements AutofillServiceInterface { false, false, options.onlyEmptyFields, - options.fillNewPassword + options.fillNewPassword, ); if (!passwordFields.length && !options.onlyVisibleFields) { // not able to find any viewable password fields. maybe there are some "hidden" ones? @@ -531,7 +531,7 @@ export default class AutofillService implements AutofillServiceInterface { true, true, options.onlyEmptyFields, - options.fillNewPassword + options.fillNewPassword, ); } @@ -661,7 +661,7 @@ export default class AutofillService implements AutofillServiceInterface { filledFields[t.opid] = t; const totpValue = await this.totpService.getCode(login.totp); AutofillService.fillByOpid(fillScript, t, totpValue); - }) + }), ); } @@ -682,7 +682,7 @@ export default class AutofillService implements AutofillServiceInterface { fillScript: AutofillScript, pageDetails: AutofillPageDetails, filledFields: { [id: string]: AutofillField }, - options: GenerateFillScriptOptions + options: GenerateFillScriptOptions, ): AutofillScript | null { if (!options.cipher.card) { return null; @@ -713,7 +713,7 @@ export default class AutofillService implements AutofillServiceInterface { AutofillService.isFieldMatch( f[attr], CreditCardAutoFillConstants.CardHolderFieldNames, - CreditCardAutoFillConstants.CardHolderFieldNameValues + CreditCardAutoFillConstants.CardHolderFieldNameValues, ) ) { fillFields.cardholderName = f; @@ -723,7 +723,7 @@ export default class AutofillService implements AutofillServiceInterface { AutofillService.isFieldMatch( f[attr], CreditCardAutoFillConstants.CardNumberFieldNames, - CreditCardAutoFillConstants.CardNumberFieldNameValues + CreditCardAutoFillConstants.CardNumberFieldNameValues, ) ) { fillFields.number = f; @@ -733,7 +733,7 @@ export default class AutofillService implements AutofillServiceInterface { AutofillService.isFieldMatch( f[attr], CreditCardAutoFillConstants.CardExpiryFieldNames, - CreditCardAutoFillConstants.CardExpiryFieldNameValues + CreditCardAutoFillConstants.CardExpiryFieldNameValues, ) ) { fillFields.exp = f; @@ -879,7 +879,7 @@ export default class AutofillService implements AutofillServiceInterface { fillFields.exp, CreditCardAutoFillConstants.MonthAbbr[i] + "/" + - CreditCardAutoFillConstants.YearAbbrLong[i] + CreditCardAutoFillConstants.YearAbbrLong[i], ) ) { exp = fullMonth + "/" + fullYear; @@ -888,7 +888,7 @@ export default class AutofillService implements AutofillServiceInterface { fillFields.exp, CreditCardAutoFillConstants.MonthAbbr[i] + "/" + - CreditCardAutoFillConstants.YearAbbrShort[i] + CreditCardAutoFillConstants.YearAbbrShort[i], ) && partYear != null ) { @@ -898,7 +898,7 @@ export default class AutofillService implements AutofillServiceInterface { fillFields.exp, CreditCardAutoFillConstants.YearAbbrLong[i] + "/" + - CreditCardAutoFillConstants.MonthAbbr[i] + CreditCardAutoFillConstants.MonthAbbr[i], ) ) { exp = fullYear + "/" + fullMonth; @@ -907,7 +907,7 @@ export default class AutofillService implements AutofillServiceInterface { fillFields.exp, CreditCardAutoFillConstants.YearAbbrShort[i] + "/" + - CreditCardAutoFillConstants.MonthAbbr[i] + CreditCardAutoFillConstants.MonthAbbr[i], ) && partYear != null ) { @@ -917,7 +917,7 @@ export default class AutofillService implements AutofillServiceInterface { fillFields.exp, CreditCardAutoFillConstants.MonthAbbr[i] + "-" + - CreditCardAutoFillConstants.YearAbbrLong[i] + CreditCardAutoFillConstants.YearAbbrLong[i], ) ) { exp = fullMonth + "-" + fullYear; @@ -926,7 +926,7 @@ export default class AutofillService implements AutofillServiceInterface { fillFields.exp, CreditCardAutoFillConstants.MonthAbbr[i] + "-" + - CreditCardAutoFillConstants.YearAbbrShort[i] + CreditCardAutoFillConstants.YearAbbrShort[i], ) && partYear != null ) { @@ -936,7 +936,7 @@ export default class AutofillService implements AutofillServiceInterface { fillFields.exp, CreditCardAutoFillConstants.YearAbbrLong[i] + "-" + - CreditCardAutoFillConstants.MonthAbbr[i] + CreditCardAutoFillConstants.MonthAbbr[i], ) ) { exp = fullYear + "-" + fullMonth; @@ -945,7 +945,7 @@ export default class AutofillService implements AutofillServiceInterface { fillFields.exp, CreditCardAutoFillConstants.YearAbbrShort[i] + "-" + - CreditCardAutoFillConstants.MonthAbbr[i] + CreditCardAutoFillConstants.MonthAbbr[i], ) && partYear != null ) { @@ -953,14 +953,14 @@ export default class AutofillService implements AutofillServiceInterface { } else if ( this.fieldAttrsContain( fillFields.exp, - CreditCardAutoFillConstants.YearAbbrLong[i] + CreditCardAutoFillConstants.MonthAbbr[i] + CreditCardAutoFillConstants.YearAbbrLong[i] + CreditCardAutoFillConstants.MonthAbbr[i], ) ) { exp = fullYear + fullMonth; } else if ( this.fieldAttrsContain( fillFields.exp, - CreditCardAutoFillConstants.YearAbbrShort[i] + CreditCardAutoFillConstants.MonthAbbr[i] + CreditCardAutoFillConstants.YearAbbrShort[i] + CreditCardAutoFillConstants.MonthAbbr[i], ) && partYear != null ) { @@ -968,14 +968,14 @@ export default class AutofillService implements AutofillServiceInterface { } else if ( this.fieldAttrsContain( fillFields.exp, - CreditCardAutoFillConstants.MonthAbbr[i] + CreditCardAutoFillConstants.YearAbbrLong[i] + CreditCardAutoFillConstants.MonthAbbr[i] + CreditCardAutoFillConstants.YearAbbrLong[i], ) ) { exp = fullMonth + fullYear; } else if ( this.fieldAttrsContain( fillFields.exp, - CreditCardAutoFillConstants.MonthAbbr[i] + CreditCardAutoFillConstants.YearAbbrShort[i] + CreditCardAutoFillConstants.MonthAbbr[i] + CreditCardAutoFillConstants.YearAbbrShort[i], ) && partYear != null ) { @@ -1018,7 +1018,7 @@ export default class AutofillService implements AutofillServiceInterface { const matchesUri = options.cipher.login.matchesUri( pageUrl, equivalentDomains, - options.defaultUriMatch + options.defaultUriMatch, ); return !matchesUri; } @@ -1064,7 +1064,7 @@ export default class AutofillService implements AutofillServiceInterface { fillScript: AutofillScript, pageDetails: AutofillPageDetails, filledFields: { [id: string]: AutofillField }, - options: GenerateFillScriptOptions + options: GenerateFillScriptOptions, ): AutofillScript { if (!options.cipher.identity) { return null; @@ -1095,7 +1095,7 @@ export default class AutofillService implements AutofillServiceInterface { AutofillService.isFieldMatch( f[attr], IdentityAutoFillConstants.FullNameFieldNames, - IdentityAutoFillConstants.FullNameFieldNameValues + IdentityAutoFillConstants.FullNameFieldNameValues, ) ) { fillFields.name = f; @@ -1135,7 +1135,7 @@ export default class AutofillService implements AutofillServiceInterface { AutofillService.isFieldMatch( f[attr], IdentityAutoFillConstants.AddressFieldNames, - IdentityAutoFillConstants.AddressFieldNameValues + IdentityAutoFillConstants.AddressFieldNameValues, ) ) { fillFields.address = f; @@ -1320,7 +1320,7 @@ export default class AutofillService implements AutofillServiceInterface { private static isFieldMatch( value: string, options: string[], - containsOptions?: string[] + containsOptions?: string[], ): boolean { value = value .trim() @@ -1355,14 +1355,14 @@ export default class AutofillService implements AutofillServiceInterface { fillFields: { [id: string]: AutofillField }, filledFields: { [id: string]: AutofillField }, dataProp: string, - fieldProp?: string + fieldProp?: string, ) { fieldProp = fieldProp || dataProp; this.makeScriptActionWithValue( fillScript, cipherData[dataProp], fillFields[fieldProp], - filledFields + filledFields, ); } @@ -1381,7 +1381,7 @@ export default class AutofillService implements AutofillServiceInterface { fillScript: AutofillScript, dataValue: any, field: AutofillField, - filledFields: { [id: string]: AutofillField } + filledFields: { [id: string]: AutofillField }, ) { let doFill = false; if (AutofillService.hasValue(dataValue) && field) { @@ -1431,7 +1431,7 @@ export default class AutofillService implements AutofillServiceInterface { canBeHidden: boolean, canBeReadOnly: boolean, mustBeEmpty: boolean, - fillNewPassword: boolean + fillNewPassword: boolean, ) { const arr: AutofillField[] = []; pageDetails.fields.forEach((f) => { @@ -1503,7 +1503,7 @@ export default class AutofillService implements AutofillServiceInterface { passwordField: AutofillField, canBeHidden: boolean, canBeReadOnly: boolean, - withoutForm: boolean + withoutForm: boolean, ): AutofillField | null { let usernameField: AutofillField = null; for (let i = 0; i < pageDetails.fields.length; i++) { @@ -1551,7 +1551,7 @@ export default class AutofillService implements AutofillServiceInterface { passwordField: AutofillField, canBeHidden: boolean, canBeReadOnly: boolean, - withoutForm: boolean + withoutForm: boolean, ): AutofillField | null { let totpField: AutofillField = null; for (let i = 0; i < pageDetails.fields.length; i++) { @@ -1659,7 +1659,7 @@ export default class AutofillService implements AutofillServiceInterface { property: string, name: string, prefix: string, - separator = "=" + separator = "=", ): boolean { if (name.indexOf(prefix + separator) === 0) { const sepIndex = name.indexOf(separator); @@ -1806,7 +1806,7 @@ export default class AutofillService implements AutofillServiceInterface { */ static setFillScriptForFocus( filledFields: { [id: string]: AutofillField }, - fillScript: AutofillScript + fillScript: AutofillScript, ): AutofillScript { let lastField: AutofillField = null; let lastPasswordField: AutofillField = null; diff --git a/apps/browser/src/autofill/services/collect-autofill-content.service.spec.ts b/apps/browser/src/autofill/services/collect-autofill-content.service.spec.ts index b58f31507ba..e300b65acc2 100644 --- a/apps/browser/src/autofill/services/collect-autofill-content.service.spec.ts +++ b/apps/browser/src/autofill/services/collect-autofill-content.service.spec.ts @@ -31,7 +31,7 @@ describe("CollectAutofillContentService", () => { document.body.innerHTML = mockLoginForm; collectAutofillContentService = new CollectAutofillContentService( domElementVisibilityService, - autofillOverlayContentService + autofillOverlayContentService, ); }); @@ -68,7 +68,7 @@ describe("CollectAutofillContentService", () => { expect(collectAutofillContentService["getFormattedPageDetails"]).toHaveBeenCalledWith({}, []); expect( - collectAutofillContentService["queryAutofillFormAndFieldElements"] + collectAutofillContentService["queryAutofillFormAndFieldElements"], ).not.toHaveBeenCalled(); expect(collectAutofillContentService["buildAutofillFormsData"]).not.toHaveBeenCalled(); expect(collectAutofillContentService["buildAutofillFieldsData"]).not.toHaveBeenCalled(); @@ -102,7 +102,7 @@ describe("CollectAutofillContentService", () => { htmlMethod: formMethod, }; const fieldElement = document.getElementById( - usernameFieldId + usernameFieldId, ) as ElementWithOpId; const autofillField: AutofillField = { opid: "__0", @@ -156,7 +156,7 @@ describe("CollectAutofillContentService", () => { expect(collectAutofillContentService["getFormattedAutofillFormsData"]).toHaveBeenCalled(); expect(collectAutofillContentService["getFormattedAutofillFieldsData"]).toHaveBeenCalled(); expect( - collectAutofillContentService["queryAutofillFormAndFieldElements"] + collectAutofillContentService["queryAutofillFormAndFieldElements"], ).not.toHaveBeenCalled(); expect(collectAutofillContentService["buildAutofillFormsData"]).not.toHaveBeenCalled(); expect(collectAutofillContentService["buildAutofillFieldsData"]).not.toHaveBeenCalled(); @@ -294,7 +294,7 @@ describe("CollectAutofillContentService", () => { it("returns the element with the opid property value matching the passed value", () => { const textInput = document.querySelector('input[type="text"]') as FormElementWithAttribute; const passwordInput = document.querySelector( - 'input[type="password"]' + 'input[type="password"]', ) as FormElementWithAttribute; textInput.opid = "__0"; passwordInput.opid = "__1"; @@ -311,7 +311,7 @@ describe("CollectAutofillContentService", () => { it("returns the first of the element with an `opid` value matching the passed value and emits a console warning if multiple fields contain the same `opid`", () => { const textInput = document.querySelector('input[type="text"]') as FormElementWithAttribute; const passwordInput = document.querySelector( - 'input[type="password"]' + 'input[type="password"]', ) as FormElementWithAttribute; jest.spyOn(console, "warn").mockImplementationOnce(jest.fn()); textInput.opid = "__1"; @@ -330,7 +330,7 @@ describe("CollectAutofillContentService", () => { it("returns the element at the index position (parsed from passed opid) of all AutofillField elements when the passed opid value cannot be found", () => { const textInput = document.querySelector('input[type="text"]') as FormElementWithAttribute; const passwordInput = document.querySelector( - 'input[type="password"]' + 'input[type="password"]', ) as FormElementWithAttribute; textInput.opid = undefined; passwordInput.opid = "__1"; @@ -387,7 +387,7 @@ describe("CollectAutofillContentService", () => { jest.spyOn(collectAutofillContentService as any, "getFormActionAttribute"); const autofillFormsData = collectAutofillContentService["buildAutofillFormsData"]( - formElements as Node[] + formElements as Node[], ); expect(collectAutofillContentService["getFormActionAttribute"]).not.toHaveBeenCalled(); @@ -452,13 +452,13 @@ describe("CollectAutofillContentService", () => { const { formFieldElements } = collectAutofillContentService["queryAutofillFormAndFieldElements"](); const autofillFieldsPromise = collectAutofillContentService["buildAutofillFieldsData"]( - formFieldElements as FormFieldElement[] + formFieldElements as FormFieldElement[], ); const autofillFieldsData = await Promise.resolve(autofillFieldsPromise); expect(collectAutofillContentService["getAutofillFieldElements"]).toHaveBeenCalledWith( 100, - formFieldElements + formFieldElements, ); expect(collectAutofillContentService["buildAutofillFieldItem"]).toHaveBeenCalledTimes(2); expect(autofillFieldsPromise).toBeInstanceOf(Promise); @@ -595,12 +595,12 @@ describe("CollectAutofillContentService", () => { expect(collectAutofillContentService["getPropertyOrAttribute"]).toHaveBeenNthCalledWith( 1, spanElement, - "type" + "type", ); expect(collectAutofillContentService["getPropertyOrAttribute"]).toHaveBeenNthCalledWith( 2, textAreaInput, - "type" + "type", ); expect(formElements).toEqual([spanElement, textAreaInput]); }); @@ -799,7 +799,7 @@ describe("CollectAutofillContentService", () => { viewable: true, }; const usernameInput = document.getElementById( - usernameField.id + usernameField.id, ) as ElementWithOpId; usernameInput.opid = "__0"; collectAutofillContentService["autofillFieldElements"].set(usernameInput, existingFieldData); @@ -812,12 +812,12 @@ describe("CollectAutofillContentService", () => { const autofillFieldItem = await collectAutofillContentService["buildAutofillFieldItem"]( usernameInput, - 0 + 0, ); expect(collectAutofillContentService["getAutofillFieldMaxLength"]).not.toHaveBeenCalled(); expect( - collectAutofillContentService["domElementVisibilityService"].isFormFieldViewable + collectAutofillContentService["domElementVisibilityService"].isFormFieldViewable, ).not.toHaveBeenCalled(); expect(collectAutofillContentService["getPropertyOrAttribute"]).not.toHaveBeenCalled(); expect(collectAutofillContentService["getElementValue"]).not.toHaveBeenCalled(); @@ -834,7 +834,7 @@ describe("CollectAutofillContentService", () => { Span Element `; const spanElement = document.getElementById( - spanElementId + spanElementId, ) as ElementWithOpId; jest.spyOn(collectAutofillContentService as any, "getAutofillFieldMaxLength"); jest @@ -845,44 +845,44 @@ describe("CollectAutofillContentService", () => { const autofillFieldItem = await collectAutofillContentService["buildAutofillFieldItem"]( spanElement, - index + index, ); expect(collectAutofillContentService["getAutofillFieldMaxLength"]).toHaveBeenCalledWith( - spanElement + spanElement, ); expect( - collectAutofillContentService["domElementVisibilityService"].isFormFieldViewable + collectAutofillContentService["domElementVisibilityService"].isFormFieldViewable, ).toHaveBeenCalledWith(spanElement); expect(collectAutofillContentService["getPropertyOrAttribute"]).toHaveBeenNthCalledWith( 1, spanElement, - "id" + "id", ); expect(collectAutofillContentService["getPropertyOrAttribute"]).toHaveBeenNthCalledWith( 2, spanElement, - "name" + "name", ); expect(collectAutofillContentService["getPropertyOrAttribute"]).toHaveBeenNthCalledWith( 3, spanElement, - "class" + "class", ); expect(collectAutofillContentService["getPropertyOrAttribute"]).toHaveBeenNthCalledWith( 4, spanElement, - "tabindex" + "tabindex", ); expect(collectAutofillContentService["getPropertyOrAttribute"]).toHaveBeenNthCalledWith( 5, spanElement, - "title" + "title", ); expect(collectAutofillContentService["getPropertyOrAttribute"]).toHaveBeenNthCalledWith( 6, spanElement, - "tagName" + "tagName", ); expect(collectAutofillContentService["getElementValue"]).not.toHaveBeenCalled(); expect(autofillFieldItem).toEqual({ @@ -942,7 +942,7 @@ describe("CollectAutofillContentService", () => { const formElement = document.querySelector("form"); formElement.opid = "form-opid"; const usernameInput = document.getElementById( - usernameField.id + usernameField.id, ) as ElementWithOpId; jest.spyOn(collectAutofillContentService as any, "getAutofillFieldMaxLength"); jest @@ -953,7 +953,7 @@ describe("CollectAutofillContentService", () => { const autofillFieldItem = await collectAutofillContentService["buildAutofillFieldItem"]( usernameInput, - index + index, ); expect(autofillFieldItem).toEqual({ @@ -1027,7 +1027,7 @@ describe("CollectAutofillContentService", () => { const formElement = document.querySelector("form"); formElement.opid = "form-opid"; const hiddenInput = document.getElementById( - hiddenField.id + hiddenField.id, ) as ElementWithOpId; jest.spyOn(collectAutofillContentService as any, "getAutofillFieldMaxLength"); jest @@ -1038,7 +1038,7 @@ describe("CollectAutofillContentService", () => { const autofillFieldItem = await collectAutofillContentService["buildAutofillFieldItem"]( hiddenInput, - index + index, ); expect(autofillFieldItem).toEqual({ @@ -1086,7 +1086,7 @@ describe("CollectAutofillContentService", () => { const labelTag = collectAutofillContentService["createAutofillFieldLabelTag"](element); expect(collectAutofillContentService["createLabelElementsTag"]).toHaveBeenCalledWith( - new Set(element.labels) + new Set(element.labels), ); expect(document.querySelectorAll).not.toHaveBeenCalled(); expect(labelTag).toEqual("Username"); @@ -1104,7 +1104,7 @@ describe("CollectAutofillContentService", () => { expect(document.querySelectorAll).toHaveBeenCalledWith(`label[for="${element.id}"]`); expect(collectAutofillContentService["createLabelElementsTag"]).toHaveBeenCalledWith( - new Set([elementLabel]) + new Set([elementLabel]), ); expect(labelTag).toEqual("Country"); }); @@ -1122,7 +1122,7 @@ describe("CollectAutofillContentService", () => { expect(document.querySelectorAll).not.toHaveBeenCalledWith(`label[for="${element.id}"]`); expect(document.querySelectorAll).toHaveBeenCalledWith(`label[for="${element.name}"]`); expect(collectAutofillContentService["createLabelElementsTag"]).toHaveBeenCalledWith( - new Set([elementLabel]) + new Set([elementLabel]), ); expect(labelTag).toEqual("Country"); }); @@ -1139,10 +1139,10 @@ describe("CollectAutofillContentService", () => { const labelTag = collectAutofillContentService["createAutofillFieldLabelTag"](element); expect(document.querySelectorAll).toHaveBeenCalledWith( - `label[for="${element.id}"], label[for="${element.name}"]` + `label[for="${element.id}"], label[for="${element.name}"]`, ); expect(collectAutofillContentService["createLabelElementsTag"]).toHaveBeenCalledWith( - new Set([elementLabel]) + new Set([elementLabel]), ); expect(labelTag).toEqual("Country"); }); @@ -1158,7 +1158,7 @@ describe("CollectAutofillContentService", () => { const labelTag = collectAutofillContentService["createAutofillFieldLabelTag"](element); expect(collectAutofillContentService["createLabelElementsTag"]).toHaveBeenCalledWith( - new Set([elementLabel]) + new Set([elementLabel]), ); expect(labelTag).toEqual("Username"); }); @@ -1178,7 +1178,7 @@ describe("CollectAutofillContentService", () => { const labelTag = collectAutofillContentService["createAutofillFieldLabelTag"](element); expect(collectAutofillContentService["createLabelElementsTag"]).toHaveBeenCalledWith( - new Set([elementLabel]) + new Set([elementLabel]), ); expect(labelTag).toEqual("Username"); }); @@ -1275,10 +1275,10 @@ describe("CollectAutofillContentService", () => { const labelTag = collectAutofillContentService["createLabelElementsTag"](new Set(labels)); expect( - collectAutofillContentService["trimAndRemoveNonPrintableText"] + collectAutofillContentService["trimAndRemoveNonPrintableText"], ).toHaveBeenNthCalledWith(1, firstLabelText); expect( - collectAutofillContentService["trimAndRemoveNonPrintableText"] + collectAutofillContentService["trimAndRemoveNonPrintableText"], ).toHaveBeenNthCalledWith(2, secondLabelText); expect(labelTag).toEqual(`${firstLabelText}${secondLabelText}`); }); @@ -1406,7 +1406,7 @@ describe("CollectAutofillContentService", () => { `; const targetTableCellInput = document.querySelector( - 'input[name="password"]' + 'input[name="password"]', ) as HTMLInputElement; const targetTableCellLabel = @@ -1433,7 +1433,7 @@ describe("CollectAutofillContentService", () => { `; const targetTableCellInput = document.querySelector( - 'input[name="auth-code"]' + 'input[name="auth-code"]', ) as HTMLInputElement; const targetTableCellLabel = @@ -1455,7 +1455,7 @@ describe("CollectAutofillContentService", () => { `; const targetTableCellInput = document.querySelector( - 'input[name="password"]' + 'input[name="password"]', ) as HTMLInputElement; const targetTableCellLabel = @@ -1482,7 +1482,7 @@ describe("CollectAutofillContentService", () => { `; const targetTableCellInput = document.querySelector( - 'input[name="password"]' + 'input[name="password"]', ) as HTMLInputElement; const targetTableCellLabel = @@ -1508,7 +1508,7 @@ describe("CollectAutofillContentService", () => { `; const targetTableCellInput = document.querySelector( - 'input[name="auth-code"]' + 'input[name="auth-code"]', ) as HTMLInputElement; const targetTableCellLabel = @@ -1573,7 +1573,7 @@ describe("CollectAutofillContentService", () => { const element = document.querySelector("#username-id"); const textNode = element.previousSibling; const parsedTextContent = collectAutofillContentService["trimAndRemoveNonPrintableText"]( - textNode.nodeValue + textNode.nodeValue, ); jest.spyOn(collectAutofillContentService as any, "trimAndRemoveNonPrintableText"); @@ -1581,7 +1581,7 @@ describe("CollectAutofillContentService", () => { expect(textNode.nodeType).toEqual(Node.TEXT_NODE); expect(collectAutofillContentService["trimAndRemoveNonPrintableText"]).toHaveBeenCalledWith( - textNode.nodeValue + textNode.nodeValue, ); expect(textContent).toEqual(parsedTextContent); }); @@ -1600,7 +1600,7 @@ describe("CollectAutofillContentService", () => { expect(element.nodeType).toEqual(Node.ELEMENT_NODE); expect(collectAutofillContentService["trimAndRemoveNonPrintableText"]).toHaveBeenCalledWith( - element.textContent + element.textContent, ); expect(textContent).toEqual(element.textContent); }); @@ -1735,20 +1735,20 @@ describe("CollectAutofillContentService", () => { const textInputValue = collectAutofillContentService["getPropertyOrAttribute"]( textInput, - "value" + "value", ); const textInputId = collectAutofillContentService["getPropertyOrAttribute"](textInput, "id"); const textInputBaseURI = collectAutofillContentService["getPropertyOrAttribute"]( textInput, - "baseURI" + "baseURI", ); const textInputAutofocus = collectAutofillContentService["getPropertyOrAttribute"]( textInput, - "autofocus" + "autofocus", ); const checkboxInputChecked = collectAutofillContentService["getPropertyOrAttribute"]( checkboxInput, - "checked" + "checked", ); expect(textInput.getAttribute).not.toHaveBeenCalled(); @@ -1767,7 +1767,7 @@ describe("CollectAutofillContentService", () => { const textInputUniqueAttribute = collectAutofillContentService["getPropertyOrAttribute"]( textInput, - "data-unique-attribute" + "data-unique-attribute", ); expect(textInputUniqueAttribute).toEqual("unique-value"); @@ -1780,7 +1780,7 @@ describe("CollectAutofillContentService", () => { const textInputNonExistentAttribute = collectAutofillContentService["getPropertyOrAttribute"]( textInput, - "non-existent-attribute" + "non-existent-attribute", ); expect(textInputNonExistentAttribute).toEqual(null); @@ -1841,14 +1841,14 @@ describe("CollectAutofillContentService", () => { `; const longValueHiddenInput = document.querySelector( - "#long-value-hidden-input" + "#long-value-hidden-input", ) as HTMLInputElement; const longHiddenValue = collectAutofillContentService["getElementValue"](longValueHiddenInput); expect(longHiddenValue).toEqual( - "’Twas brillig, and the slithy toves | Did gyre and gimble in the wabe: | All mimsy were the borogoves, | And the mome raths outgrabe. | “Beware the Jabberwock, my son! | The jaws that bite, the claws that catch! | Beware the Jubjub bird, and shun | The f...SNIPPED" + "’Twas brillig, and the slithy toves | Did gyre and gimble in the wabe: | All mimsy were the borogoves, | And the mome raths outgrabe. | “Beware the Jabberwock, my son! | The jaws that bite, the claws that catch! | Beware the Jubjub bird, and shun | The f...SNIPPED", ); }); }); @@ -1866,7 +1866,7 @@ describe("CollectAutofillContentService", () => { `; const selectWithOptions = document.querySelector("#select-with-options") as HTMLSelectElement; const selectWithoutOptions = document.querySelector( - "#select-without-options" + "#select-without-options", ) as HTMLSelectElement; const selectWithOptionsOptions = @@ -1925,7 +1925,7 @@ describe("CollectAutofillContentService", () => { document.body, [], callbackFilter, - true + true, ); expect(collectAutofillContentService["buildTreeWalkerNodesQueryResults"]).toBeCalledTimes(2); @@ -1946,7 +1946,7 @@ describe("CollectAutofillContentService", () => { document.body, [], callbackFilter, - false + false, ); expect(collectAutofillContentService["mutationObserver"].observe).not.toBeCalled(); @@ -1993,10 +1993,10 @@ describe("CollectAutofillContentService", () => { expect(collectAutofillContentService["noFieldsFound"]).toEqual(false); expect(collectAutofillContentService["isAutofillElementNodeMutated"]).toBeCalledWith( removedNodes, - true + true, ); expect(collectAutofillContentService["isAutofillElementNodeMutated"]).toBeCalledWith( - addedNodes + addedNodes, ); }); @@ -2048,9 +2048,86 @@ describe("CollectAutofillContentService", () => { expect(collectAutofillContentService["handleWindowLocationMutation"]).toBeCalled(); expect(collectAutofillContentService["isAutofillElementNodeMutated"]).not.toBeCalled(); expect( - collectAutofillContentService["handleAutofillElementAttributeMutation"] + collectAutofillContentService["handleAutofillElementAttributeMutation"], ).not.toBeCalled(); }); + + it("will setup the overlay listeners on mutated elements", async () => { + jest.useFakeTimers(); + const form = document.createElement("form"); + document.body.appendChild(form); + const addedNodes = document.querySelectorAll("form"); + const removedNodes = document.querySelectorAll("li"); + const mutationRecord: MutationRecord = { + type: "childList", + addedNodes: addedNodes, + attributeName: null, + attributeNamespace: null, + nextSibling: null, + oldValue: null, + previousSibling: null, + removedNodes: removedNodes, + target: document.body, + }; + collectAutofillContentService["domRecentlyMutated"] = false; + collectAutofillContentService["noFieldsFound"] = true; + collectAutofillContentService["currentLocationHref"] = window.location.href; + jest.spyOn(collectAutofillContentService as any, "setupOverlayListenersOnMutatedElements"); + + collectAutofillContentService["handleMutationObserverMutation"]([mutationRecord]); + jest.runAllTimers(); + + expect(collectAutofillContentService["setupOverlayListenersOnMutatedElements"]).toBeCalled(); + }); + }); + + describe("setupOverlayListenersOnMutatedElements", () => { + it("skips building the autofill field item if the node is not a form field element", () => { + const divElement = document.createElement("div"); + const nodes = [divElement]; + jest.spyOn(collectAutofillContentService as any, "buildAutofillFieldItem"); + + collectAutofillContentService["setupOverlayListenersOnMutatedElements"](nodes); + + expect(collectAutofillContentService["buildAutofillFieldItem"]).not.toBeCalled(); + }); + + it("skips building the autofill field item if the node is already a field element", () => { + const inputElement = document.createElement("input") as ElementWithOpId; + inputElement.setAttribute("type", "password"); + const nodes = [inputElement]; + collectAutofillContentService["autofillFieldElements"].set(inputElement, { + opid: "1234", + } as AutofillField); + jest.spyOn(collectAutofillContentService as any, "buildAutofillFieldItem"); + + collectAutofillContentService["setupOverlayListenersOnMutatedElements"](nodes); + + expect(collectAutofillContentService["buildAutofillFieldItem"]).not.toBeCalled(); + }); + + it("builds the autofill field item to ensure the overlay listeners are set", () => { + document.body.innerHTML = ` +
+ + +
+ `; + + const inputElement = document.getElementById( + "username-id", + ) as ElementWithOpId; + inputElement.setAttribute("type", "password"); + const nodes = [inputElement]; + jest.spyOn(collectAutofillContentService as any, "buildAutofillFieldItem"); + + collectAutofillContentService["setupOverlayListenersOnMutatedElements"](nodes); + + expect(collectAutofillContentService["buildAutofillFieldItem"]).toBeCalledWith( + inputElement, + -1, + ); + }); }); describe("deleteCachedAutofillElement", () => { @@ -2170,7 +2247,7 @@ describe("CollectAutofillContentService", () => { expect(collectAutofillContentService["updateAutofillFormElementData"]).toBeCalledWith( mutationRecord.attributeName, mutationRecord.target, - autofillForm + autofillForm, ); }); @@ -2217,7 +2294,7 @@ describe("CollectAutofillContentService", () => { expect(collectAutofillContentService["updateAutofillFieldElementData"]).toBeCalledWith( mutationRecord.attributeName, mutationRecord.target, - autofillField + autofillField, ); }); }); @@ -2240,12 +2317,12 @@ describe("CollectAutofillContentService", () => { collectAutofillContentService["updateAutofillFormElementData"]( attribute, formElement, - autofillForm + autofillForm, ); expect(collectAutofillContentService["autofillFormElements"].set).toBeCalledWith( formElement, - autofillForm + autofillForm, ); }); }); @@ -2256,7 +2333,7 @@ describe("CollectAutofillContentService", () => { collectAutofillContentService["updateAutofillFormElementData"]( "aria-label", formElement, - autofillForm + autofillForm, ); expect(collectAutofillContentService["autofillFormElements"].set).not.toBeCalled(); @@ -2305,12 +2382,12 @@ describe("CollectAutofillContentService", () => { await collectAutofillContentService["updateAutofillFieldElementData"]( attribute, fieldElement, - autofillField + autofillField, ); expect(collectAutofillContentService["autofillFieldElements"].set).toBeCalledWith( fieldElement, - autofillField + autofillField, ); }); }); @@ -2318,7 +2395,7 @@ describe("CollectAutofillContentService", () => { it("will check the dom element's visibility if the `style` or `class` attribute has updated ", async () => { jest.spyOn( collectAutofillContentService["domElementVisibilityService"], - "isFormFieldViewable" + "isFormFieldViewable", ); const attributes = ["class", "style"]; @@ -2326,11 +2403,11 @@ describe("CollectAutofillContentService", () => { await collectAutofillContentService["updateAutofillFieldElementData"]( attribute, fieldElement, - autofillField + autofillField, ); expect( - collectAutofillContentService["domElementVisibilityService"].isFormFieldViewable + collectAutofillContentService["domElementVisibilityService"].isFormFieldViewable, ).toBeCalledWith(fieldElement); } }); @@ -2341,7 +2418,7 @@ describe("CollectAutofillContentService", () => { await collectAutofillContentService["updateAutofillFieldElementData"]( "random-attribute", fieldElement, - autofillField + autofillField, ); expect(collectAutofillContentService["autofillFieldElements"].set).not.toBeCalled(); diff --git a/apps/browser/src/autofill/services/collect-autofill-content.service.ts b/apps/browser/src/autofill/services/collect-autofill-content.service.ts index 20f1c2b5921..d675a37921d 100644 --- a/apps/browser/src/autofill/services/collect-autofill-content.service.ts +++ b/apps/browser/src/autofill/services/collect-autofill-content.service.ts @@ -31,7 +31,7 @@ class CollectAutofillContentService implements CollectAutofillContentServiceInte constructor( domElementVisibilityService: DomElementVisibilityService, - autofillOverlayContentService?: AutofillOverlayContentService + autofillOverlayContentService?: AutofillOverlayContentService, ) { this.domElementVisibilityService = domElementVisibilityService; this.autofillOverlayContentService = autofillOverlayContentService; @@ -56,7 +56,7 @@ class CollectAutofillContentService implements CollectAutofillContentServiceInte if (!this.domRecentlyMutated && this.autofillFieldElements.size) { return this.getFormattedPageDetails( this.getFormattedAutofillFormsData(), - this.getFormattedAutofillFieldsData() + this.getFormattedAutofillFieldsData(), ); } @@ -64,7 +64,7 @@ class CollectAutofillContentService implements CollectAutofillContentServiceInte const autofillFormsData: Record = this.buildAutofillFormsData(formElements); const autofillFieldsData: AutofillField[] = await this.buildAutofillFieldsData( - formFieldElements as FormFieldElement[] + formFieldElements as FormFieldElement[], ); this.sortAutofillFieldElementsMap(); @@ -89,7 +89,7 @@ class CollectAutofillContentService implements CollectAutofillContentServiceInte ? cachedFormFieldElements : this.getAutofillFieldElements(); const fieldElementsWithOpid = formFieldElements.filter( - (fieldElement) => (fieldElement as ElementWithOpId).opid === opid + (fieldElement) => (fieldElement as ElementWithOpId).opid === opid, ) as ElementWithOpId[]; if (!fieldElementsWithOpid.length) { @@ -117,7 +117,7 @@ class CollectAutofillContentService implements CollectAutofillContentServiceInte queryAllTreeWalkerNodes( rootNode: Node, filterCallback: CallableFunction, - isObservingShadowRoot = true + isObservingShadowRoot = true, ): Node[] { const treeWalkerQueryResults: Node[] = []; @@ -125,7 +125,7 @@ class CollectAutofillContentService implements CollectAutofillContentServiceInte rootNode, treeWalkerQueryResults, filterCallback, - isObservingShadowRoot + isObservingShadowRoot, ); return treeWalkerQueryResults; @@ -141,7 +141,7 @@ class CollectAutofillContentService implements CollectAutofillContentServiceInte } this.autofillFieldElements = new Map( - [...this.autofillFieldElements].sort((a, b) => a[1].elementNumber - b[1].elementNumber) + [...this.autofillFieldElements].sort((a, b) => a[1].elementNumber - b[1].elementNumber), ); } @@ -154,7 +154,7 @@ class CollectAutofillContentService implements CollectAutofillContentServiceInte */ private getFormattedPageDetails( autofillFormsData: Record, - autofillFieldsData: AutofillField[] + autofillFieldsData: AutofillField[], ): AutofillPageDetails { return { title: document.title, @@ -231,7 +231,7 @@ class CollectAutofillContentService implements CollectAutofillContentServiceInte * @private */ private async buildAutofillFieldsData( - formFieldElements: FormFieldElement[] + formFieldElements: FormFieldElement[], ): Promise { const autofillFieldElements = this.getAutofillFieldElements(100, formFieldElements); const autofillFieldDataPromises = autofillFieldElements.map(this.buildAutofillFieldItem); @@ -250,12 +250,12 @@ class CollectAutofillContentService implements CollectAutofillContentServiceInte */ private getAutofillFieldElements( fieldsLimit?: number, - previouslyFoundFormFieldElements?: FormFieldElement[] + previouslyFoundFormFieldElements?: FormFieldElement[], ): FormFieldElement[] { const formFieldElements = previouslyFoundFormFieldElements || (this.queryAllTreeWalkerNodes(document.documentElement, (node: Node) => - this.isNodeFormFieldElement(node) + this.isNodeFormFieldElement(node), ) as FormFieldElement[]); if (!fieldsLimit || formFieldElements.length <= fieldsLimit) { @@ -298,12 +298,12 @@ class CollectAutofillContentService implements CollectAutofillContentServiceInte */ private buildAutofillFieldItem = async ( element: ElementWithOpId, - index: number + index: number, ): Promise => { element.opid = `__${index}`; const existingAutofillField = this.autofillFieldElements.get(element); - if (existingAutofillField) { + if (index >= 0 && existingAutofillField) { existingAutofillField.opid = element.opid; existingAutofillField.elementNumber = index; this.autofillFieldElements.set(element, existingAutofillField); @@ -325,10 +325,10 @@ class CollectAutofillContentService implements CollectAutofillContentServiceInte }; if (element instanceof HTMLSpanElement) { - this.autofillFieldElements.set(element, autofillFieldBase); + this.cacheAutofillFieldElement(index, element, autofillFieldBase); this.autofillOverlayContentService?.setupAutofillOverlayListenerOnField( element, - autofillFieldBase + autofillFieldBase, ); return autofillFieldBase; } @@ -366,11 +366,31 @@ class CollectAutofillContentService implements CollectAutofillContentServiceInte "data-stripe": this.getPropertyOrAttribute(element, "data-stripe"), }; - this.autofillFieldElements.set(element, autofillField); + this.cacheAutofillFieldElement(index, element, autofillField); this.autofillOverlayContentService?.setupAutofillOverlayListenerOnField(element, autofillField); return autofillField; }; + /** + * Caches the autofill field element and its data. + * Will not cache the element if the index is less than 0. + * + * @param index - The index of the autofill field element + * @param element - The autofill field element to cache + * @param autofillFieldData - The autofill field data to cache + */ + private cacheAutofillFieldElement( + index: number, + element: ElementWithOpId, + autofillFieldData: AutofillField, + ) { + if (index < 0) { + return; + } + + this.autofillFieldElements.set(element, autofillFieldData); + } + /** * Identifies the autocomplete attribute associated with an element and returns * the value of the attribute if it is not set to "off". @@ -397,7 +417,7 @@ class CollectAutofillContentService implements CollectAutofillContentServiceInte private getAttributeBoolean( element: ElementWithOpId, attributeName: string, - checkString = false + checkString = false, ): boolean { if (checkString) { return this.getPropertyOrAttribute(element, attributeName) === "true"; @@ -415,7 +435,7 @@ class CollectAutofillContentService implements CollectAutofillContentServiceInte */ private getAttributeLowerCase( element: ElementWithOpId, - attributeName: string + attributeName: string, ): string { return this.getPropertyOrAttribute(element, attributeName)?.toLowerCase(); } @@ -479,7 +499,7 @@ class CollectAutofillContentService implements CollectAutofillContentServiceInte * @private */ private queryElementLabels( - element: FillableFormFieldElement + element: FillableFormFieldElement, ): NodeListOf | null { let labelQuerySelectors = element.id ? `label[for="${element.id}"]` : ""; if (element.name) { @@ -494,7 +514,7 @@ class CollectAutofillContentService implements CollectAutofillContentServiceInte } return (element.getRootNode() as Document | ShadowRoot).querySelectorAll( - labelQuerySelectors.replace(/\n/g, "") + labelQuerySelectors.replace(/\n/g, ""), ); } @@ -645,7 +665,7 @@ class CollectAutofillContentService implements CollectAutofillContentServiceInte } return this.trimAndRemoveNonPrintableText( - element.textContent || (element as HTMLElement).innerText + element.textContent || (element as HTMLElement).innerText, ); } @@ -869,7 +889,7 @@ class CollectAutofillContentService implements CollectAutofillContentServiceInte rootNode: Node, treeWalkerQueryResults: Node[], filterCallback: CallableFunction, - isObservingShadowRoot: boolean + isObservingShadowRoot: boolean, ) { const treeWalker = document?.createTreeWalker(rootNode, NodeFilter.SHOW_ELEMENT); let currentNode = treeWalker?.currentNode; @@ -893,7 +913,7 @@ class CollectAutofillContentService implements CollectAutofillContentServiceInte nodeShadowRoot, treeWalkerQueryResults, filterCallback, - isObservingShadowRoot + isObservingShadowRoot, ); } @@ -987,7 +1007,7 @@ class CollectAutofillContentService implements CollectAutofillContentServiceInte } let isElementMutated = false; - const mutatedElements = []; + const mutatedElements: Node[] = []; for (let index = 0; index < nodes.length; index++) { const node = nodes[index]; if (!(node instanceof HTMLElement)) { @@ -1002,7 +1022,7 @@ class CollectAutofillContentService implements CollectAutofillContentServiceInte const childNodes = this.queryAllTreeWalkerNodes( node, - (node: Node) => node instanceof HTMLFormElement || this.isNodeFormFieldElement(node) + (node: Node) => node instanceof HTMLFormElement || this.isNodeFormFieldElement(node), ) as HTMLElement[]; if (childNodes.length) { isElementMutated = true; @@ -1010,17 +1030,31 @@ class CollectAutofillContentService implements CollectAutofillContentServiceInte } } + if (isRemovingNodes) { + for (let elementIndex = 0; elementIndex < mutatedElements.length; elementIndex++) { + const node = mutatedElements[elementIndex]; + this.deleteCachedAutofillElement( + node as ElementWithOpId | ElementWithOpId, + ); + } + } else if (this.autofillOverlayContentService) { + setTimeout(() => this.setupOverlayListenersOnMutatedElements(mutatedElements), 1000); + } + + return isElementMutated; + } + + /** + * Sets up the overlay listeners on the passed mutated elements. This ensures + * that the overlay can appear on elements that are injected into the DOM after + * the initial page load. + * + * @param mutatedElements - HTML elements that have been mutated + */ + private setupOverlayListenersOnMutatedElements(mutatedElements: Node[]) { for (let elementIndex = 0; elementIndex < mutatedElements.length; elementIndex++) { const node = mutatedElements[elementIndex]; - if (isRemovingNodes) { - this.deleteCachedAutofillElement( - node as ElementWithOpId | ElementWithOpId - ); - continue; - } - if ( - this.autofillOverlayContentService && this.isNodeFormFieldElement(node) && !this.autofillFieldElements.get(node as ElementWithOpId) ) { @@ -1029,8 +1063,6 @@ class CollectAutofillContentService implements CollectAutofillContentServiceInte this.buildAutofillFieldItem(node as ElementWithOpId, -1); } } - - return isElementMutated; } /** @@ -1040,7 +1072,7 @@ class CollectAutofillContentService implements CollectAutofillContentServiceInte * @private */ private deleteCachedAutofillElement( - element: ElementWithOpId | ElementWithOpId + element: ElementWithOpId | ElementWithOpId, ) { if (element instanceof HTMLFormElement && this.autofillFormElements.has(element)) { this.autofillFormElements.delete(element); @@ -1064,7 +1096,7 @@ class CollectAutofillContentService implements CollectAutofillContentServiceInte this.updateAutofillElementsAfterMutationTimeout = setTimeout( this.getPageDetails.bind(this), - this.updateAfterMutationTimeoutDelay + this.updateAfterMutationTimeoutDelay, ); } @@ -1081,21 +1113,21 @@ class CollectAutofillContentService implements CollectAutofillContentServiceInte const attributeName = mutation.attributeName?.toLowerCase(); const autofillForm = this.autofillFormElements.get( - targetElement as ElementWithOpId + targetElement as ElementWithOpId, ); if (autofillForm) { this.updateAutofillFormElementData( attributeName, targetElement as ElementWithOpId, - autofillForm + autofillForm, ); return; } const autofillField = this.autofillFieldElements.get( - targetElement as ElementWithOpId + targetElement as ElementWithOpId, ); if (!autofillField) { return; @@ -1104,7 +1136,7 @@ class CollectAutofillContentService implements CollectAutofillContentServiceInte this.updateAutofillFieldElementData( attributeName, targetElement as ElementWithOpId, - autofillField + autofillField, ); } @@ -1118,7 +1150,7 @@ class CollectAutofillContentService implements CollectAutofillContentServiceInte private updateAutofillFormElementData( attributeName: string, element: ElementWithOpId, - dataTarget: AutofillForm + dataTarget: AutofillForm, ) { const updateAttribute = (dataTargetKey: string) => { this.updateAutofillDataAttribute({ element, attributeName, dataTarget, dataTargetKey }); @@ -1149,7 +1181,7 @@ class CollectAutofillContentService implements CollectAutofillContentServiceInte private async updateAutofillFieldElementData( attributeName: string, element: ElementWithOpId, - dataTarget: AutofillField + dataTarget: AutofillField, ) { const updateAttribute = (dataTargetKey: string) => { this.updateAutofillDataAttribute({ element, attributeName, dataTarget, dataTargetKey }); diff --git a/apps/browser/src/autofill/services/dom-element-visibility.service.spec.ts b/apps/browser/src/autofill/services/dom-element-visibility.service.spec.ts index e17783b7a65..1fb1f969b53 100644 --- a/apps/browser/src/autofill/services/dom-element-visibility.service.spec.ts +++ b/apps/browser/src/autofill/services/dom-element-visibility.service.spec.ts @@ -47,19 +47,18 @@ describe("DomElementVisibilityService", () => { jest.spyOn(domElementVisibilityService, "isElementHiddenByCss"); jest.spyOn(domElementVisibilityService as any, "formFieldIsNotHiddenBehindAnotherElement"); - const isFormFieldViewable = await domElementVisibilityService.isFormFieldViewable( - usernameElement - ); + const isFormFieldViewable = + await domElementVisibilityService.isFormFieldViewable(usernameElement); expect(isFormFieldViewable).toEqual(false); expect(usernameElement.getBoundingClientRect).toHaveBeenCalled(); expect(domElementVisibilityService["isElementOutsideViewportBounds"]).toHaveBeenCalledWith( usernameElement, - usernameElement.getBoundingClientRect() + usernameElement.getBoundingClientRect(), ); expect(domElementVisibilityService["isElementHiddenByCss"]).not.toHaveBeenCalled(); expect( - domElementVisibilityService["formFieldIsNotHiddenBehindAnotherElement"] + domElementVisibilityService["formFieldIsNotHiddenBehindAnotherElement"], ).not.toHaveBeenCalled(); }); @@ -72,21 +71,20 @@ describe("DomElementVisibilityService", () => { jest.spyOn(domElementVisibilityService, "isElementHiddenByCss").mockReturnValueOnce(true); jest.spyOn(domElementVisibilityService as any, "formFieldIsNotHiddenBehindAnotherElement"); - const isFormFieldViewable = await domElementVisibilityService.isFormFieldViewable( - usernameElement - ); + const isFormFieldViewable = + await domElementVisibilityService.isFormFieldViewable(usernameElement); expect(isFormFieldViewable).toEqual(false); expect(usernameElement.getBoundingClientRect).toHaveBeenCalled(); expect(domElementVisibilityService["isElementOutsideViewportBounds"]).toHaveBeenCalledWith( usernameElement, - usernameElement.getBoundingClientRect() + usernameElement.getBoundingClientRect(), ); expect(domElementVisibilityService["isElementHiddenByCss"]).toHaveBeenCalledWith( - usernameElement + usernameElement, ); expect( - domElementVisibilityService["formFieldIsNotHiddenBehindAnotherElement"] + domElementVisibilityService["formFieldIsNotHiddenBehindAnotherElement"], ).not.toHaveBeenCalled(); }); @@ -101,21 +99,20 @@ describe("DomElementVisibilityService", () => { .spyOn(domElementVisibilityService as any, "formFieldIsNotHiddenBehindAnotherElement") .mockReturnValueOnce(false); - const isFormFieldViewable = await domElementVisibilityService.isFormFieldViewable( - usernameElement - ); + const isFormFieldViewable = + await domElementVisibilityService.isFormFieldViewable(usernameElement); expect(isFormFieldViewable).toEqual(false); expect(usernameElement.getBoundingClientRect).toHaveBeenCalled(); expect(domElementVisibilityService["isElementOutsideViewportBounds"]).toHaveBeenCalledWith( usernameElement, - usernameElement.getBoundingClientRect() + usernameElement.getBoundingClientRect(), ); expect(domElementVisibilityService["isElementHiddenByCss"]).toHaveBeenCalledWith( - usernameElement + usernameElement, ); expect( - domElementVisibilityService["formFieldIsNotHiddenBehindAnotherElement"] + domElementVisibilityService["formFieldIsNotHiddenBehindAnotherElement"], ).toHaveBeenCalledWith(usernameElement, usernameElement.getBoundingClientRect()); }); @@ -130,21 +127,20 @@ describe("DomElementVisibilityService", () => { .spyOn(domElementVisibilityService as any, "formFieldIsNotHiddenBehindAnotherElement") .mockReturnValueOnce(true); - const isFormFieldViewable = await domElementVisibilityService.isFormFieldViewable( - usernameElement - ); + const isFormFieldViewable = + await domElementVisibilityService.isFormFieldViewable(usernameElement); expect(isFormFieldViewable).toEqual(true); expect(usernameElement.getBoundingClientRect).toHaveBeenCalled(); expect(domElementVisibilityService["isElementOutsideViewportBounds"]).toHaveBeenCalledWith( usernameElement, - usernameElement.getBoundingClientRect() + usernameElement.getBoundingClientRect(), ); expect(domElementVisibilityService["isElementHiddenByCss"]).toHaveBeenCalledWith( - usernameElement + usernameElement, ); expect( - domElementVisibilityService["formFieldIsNotHiddenBehindAnotherElement"] + domElementVisibilityService["formFieldIsNotHiddenBehindAnotherElement"], ).toHaveBeenCalledWith(usernameElement, usernameElement.getBoundingClientRect()); }); }); @@ -186,12 +182,12 @@ describe("DomElementVisibilityService", () => { expect(isUsernameElementHidden).toEqual(true); expect(usernameElement.style.getPropertyValue).toHaveBeenCalled(); expect(usernameElement.ownerDocument.defaultView.getComputedStyle).toHaveBeenCalledWith( - usernameElement + usernameElement, ); expect(isPasswordElementHidden).toEqual(true); expect(passwordElement.style.getPropertyValue).toHaveBeenCalled(); expect(passwordElement.ownerDocument.defaultView.getComputedStyle).toHaveBeenCalledWith( - passwordElement + passwordElement, ); }); @@ -391,7 +387,7 @@ describe("DomElementVisibilityService", () => { expect(formFieldIsNotHiddenBehindAnotherElement).toEqual(true); expect(document.elementFromPoint).toHaveBeenCalledWith( mockBoundingRect.left + mockBoundingRect.width / 2, - mockBoundingRect.top + mockBoundingRect.height / 2 + mockBoundingRect.top + mockBoundingRect.height / 2, ); expect(usernameElement.getBoundingClientRect).not.toHaveBeenCalled(); }); diff --git a/apps/browser/src/autofill/services/dom-element-visibility.service.ts b/apps/browser/src/autofill/services/dom-element-visibility.service.ts index 2797ee0eb3d..acc5b120596 100644 --- a/apps/browser/src/autofill/services/dom-element-visibility.service.ts +++ b/apps/browser/src/autofill/services/dom-element-visibility.service.ts @@ -67,7 +67,7 @@ class DomElementVisibilityService implements domElementVisibilityServiceInterfac private getElementStyle(element: HTMLElement, styleProperty: string): string { if (!this.cachedComputedStyle) { this.cachedComputedStyle = (element.ownerDocument.defaultView || window).getComputedStyle( - element + element, ); } @@ -132,7 +132,7 @@ class DomElementVisibilityService implements domElementVisibilityServiceInterfac */ private isElementOutsideViewportBounds( targetElement: HTMLElement, - targetElementBoundingClientRect: DOMRectReadOnly | null = null + targetElementBoundingClientRect: DOMRectReadOnly | null = null, ): boolean { const documentElement = targetElement.ownerDocument.documentElement; const documentElementWidth = documentElement.scrollWidth; @@ -171,7 +171,7 @@ class DomElementVisibilityService implements domElementVisibilityServiceInterfac */ private formFieldIsNotHiddenBehindAnotherElement( targetElement: FormFieldElement, - targetElementBoundingClientRect: DOMRectReadOnly | null = null + targetElementBoundingClientRect: DOMRectReadOnly | null = null, ): boolean { const elementBoundingClientRect = targetElementBoundingClientRect || targetElement.getBoundingClientRect(); @@ -180,7 +180,7 @@ class DomElementVisibilityService implements domElementVisibilityServiceInterfac elementRootNode instanceof ShadowRoot ? elementRootNode : targetElement.ownerDocument; const elementAtCenterPoint = rootElement.elementFromPoint( elementBoundingClientRect.left + elementBoundingClientRect.width / 2, - elementBoundingClientRect.top + elementBoundingClientRect.height / 2 + elementBoundingClientRect.top + elementBoundingClientRect.height / 2, ); if (elementAtCenterPoint === targetElement) { diff --git a/apps/browser/src/autofill/services/insert-autofill-content.service.spec.ts b/apps/browser/src/autofill/services/insert-autofill-content.service.spec.ts index b6739db36a2..a3173204c97 100644 --- a/apps/browser/src/autofill/services/insert-autofill-content.service.spec.ts +++ b/apps/browser/src/autofill/services/insert-autofill-content.service.spec.ts @@ -41,8 +41,8 @@ const initEventCount = Object.freeze( ...eventCounts, [eventName]: 0, }), - {} - ) + {}, + ), ); let confirmSpy: jest.SpyInstance; @@ -68,7 +68,7 @@ describe("InsertAutofillContentService", () => { const autofillOverlayContentService = new AutofillOverlayContentService(); const collectAutofillContentService = new CollectAutofillContentService( domElementVisibilityService, - autofillOverlayContentService + autofillOverlayContentService, ); let insertAutofillContentService: InsertAutofillContentService; let fillScript: AutofillScript; @@ -79,7 +79,7 @@ describe("InsertAutofillContentService", () => { windowSpy = jest.spyOn(window, "window", "get"); insertAutofillContentService = new InsertAutofillContentService( domElementVisibilityService, - collectAutofillContentService + collectAutofillContentService, ); fillScript = { script: [ @@ -117,10 +117,10 @@ describe("InsertAutofillContentService", () => { expect(insertAutofillContentService["fillingWithinSandboxedIframe"]).not.toHaveBeenCalled(); expect( - insertAutofillContentService["userCancelledInsecureUrlAutofill"] + insertAutofillContentService["userCancelledInsecureUrlAutofill"], ).not.toHaveBeenCalled(); expect( - insertAutofillContentService["userCancelledUntrustedIframeAutofill"] + insertAutofillContentService["userCancelledUntrustedIframeAutofill"], ).not.toHaveBeenCalled(); expect(insertAutofillContentService["runFillScriptAction"]).not.toHaveBeenCalled(); }); @@ -137,10 +137,10 @@ describe("InsertAutofillContentService", () => { expect(insertAutofillContentService["fillingWithinSandboxedIframe"]).toHaveBeenCalled(); expect( - insertAutofillContentService["userCancelledInsecureUrlAutofill"] + insertAutofillContentService["userCancelledInsecureUrlAutofill"], ).not.toHaveBeenCalled(); expect( - insertAutofillContentService["userCancelledUntrustedIframeAutofill"] + insertAutofillContentService["userCancelledUntrustedIframeAutofill"], ).not.toHaveBeenCalled(); expect(insertAutofillContentService["runFillScriptAction"]).not.toHaveBeenCalled(); }); @@ -160,7 +160,7 @@ describe("InsertAutofillContentService", () => { expect(insertAutofillContentService["fillingWithinSandboxedIframe"]).toHaveBeenCalled(); expect(insertAutofillContentService["userCancelledInsecureUrlAutofill"]).toHaveBeenCalled(); expect( - insertAutofillContentService["userCancelledUntrustedIframeAutofill"] + insertAutofillContentService["userCancelledUntrustedIframeAutofill"], ).not.toHaveBeenCalled(); expect(insertAutofillContentService["runFillScriptAction"]).not.toHaveBeenCalled(); }); @@ -182,7 +182,7 @@ describe("InsertAutofillContentService", () => { expect(insertAutofillContentService["fillingWithinSandboxedIframe"]).toHaveBeenCalled(); expect(insertAutofillContentService["userCancelledInsecureUrlAutofill"]).toHaveBeenCalled(); expect( - insertAutofillContentService["userCancelledUntrustedIframeAutofill"] + insertAutofillContentService["userCancelledUntrustedIframeAutofill"], ).toHaveBeenCalled(); expect(insertAutofillContentService["runFillScriptAction"]).not.toHaveBeenCalled(); }); @@ -204,26 +204,26 @@ describe("InsertAutofillContentService", () => { expect(insertAutofillContentService["fillingWithinSandboxedIframe"]).toHaveBeenCalled(); expect(insertAutofillContentService["userCancelledInsecureUrlAutofill"]).toHaveBeenCalled(); expect( - insertAutofillContentService["userCancelledUntrustedIframeAutofill"] + insertAutofillContentService["userCancelledUntrustedIframeAutofill"], ).toHaveBeenCalled(); expect(insertAutofillContentService["runFillScriptAction"]).toHaveBeenCalledTimes(3); expect(insertAutofillContentService["runFillScriptAction"]).toHaveBeenNthCalledWith( 1, fillScript.script[0], 0, - fillScript.script + fillScript.script, ); expect(insertAutofillContentService["runFillScriptAction"]).toHaveBeenNthCalledWith( 2, fillScript.script[1], 1, - fillScript.script + fillScript.script, ); expect(insertAutofillContentService["runFillScriptAction"]).toHaveBeenNthCalledWith( 3, fillScript.script[2], 2, - fillScript.script + fillScript.script, ); }); }); @@ -432,7 +432,7 @@ describe("InsertAutofillContentService", () => { jest.advanceTimersByTime(20); expect( - insertAutofillContentService["autofillInsertActions"][action] + insertAutofillContentService["autofillInsertActions"][action], ).toHaveBeenCalledWith({ opid, value, @@ -451,18 +451,18 @@ describe("InsertAutofillContentService", () => { textInput.value = value; jest.spyOn( insertAutofillContentService["collectAutofillContentService"], - "getAutofillFieldElementByOpid" + "getAutofillFieldElementByOpid", ); jest.spyOn(insertAutofillContentService as any, "insertValueIntoField"); insertAutofillContentService["handleFillFieldByOpidAction"](opid, value); expect( - insertAutofillContentService["collectAutofillContentService"].getAutofillFieldElementByOpid + insertAutofillContentService["collectAutofillContentService"].getAutofillFieldElementByOpid, ).toHaveBeenCalledWith(opid); expect(insertAutofillContentService["insertValueIntoField"]).toHaveBeenCalledWith( textInput, - value + value, ); }); }); @@ -483,17 +483,17 @@ describe("InsertAutofillContentService", () => { textInput.addEventListener("click", clickEventHandler); jest.spyOn( insertAutofillContentService["collectAutofillContentService"], - "getAutofillFieldElementByOpid" + "getAutofillFieldElementByOpid", ); jest.spyOn(insertAutofillContentService as any, "triggerClickOnElement"); insertAutofillContentService["handleClickOnFieldByOpidAction"]("__1"); expect( - insertAutofillContentService["collectAutofillContentService"].getAutofillFieldElementByOpid + insertAutofillContentService["collectAutofillContentService"].getAutofillFieldElementByOpid, ).toBeCalledWith("__1"); expect((insertAutofillContentService as any)["triggerClickOnElement"]).toHaveBeenCalledWith( - textInput + textInput, ); expect(clickEventCount).toBe(expectedClickEventCount); @@ -544,20 +544,20 @@ describe("InsertAutofillContentService", () => { }); jest.spyOn( insertAutofillContentService["collectAutofillContentService"], - "getAutofillFieldElementByOpid" + "getAutofillFieldElementByOpid", ); jest.spyOn( insertAutofillContentService as any, - "simulateUserMouseClickAndFocusEventInteractions" + "simulateUserMouseClickAndFocusEventInteractions", ); insertAutofillContentService["handleFocusOnFieldByOpidAction"]("__0"); expect( - insertAutofillContentService["collectAutofillContentService"].getAutofillFieldElementByOpid + insertAutofillContentService["collectAutofillContentService"].getAutofillFieldElementByOpid, ).toBeCalledWith("__0"); expect( - insertAutofillContentService["simulateUserMouseClickAndFocusEventInteractions"] + insertAutofillContentService["simulateUserMouseClickAndFocusEventInteractions"], ).toHaveBeenCalledWith(targetInput, true); expect(elementEventCount).toEqual(expectedElementEventCount); }); @@ -572,7 +572,7 @@ describe("InsertAutofillContentService", () => { insertAutofillContentService["insertValueIntoField"](element, value); expect( - insertAutofillContentService["handleInsertValueAndTriggerSimulatedEvents"] + insertAutofillContentService["handleInsertValueAndTriggerSimulatedEvents"], ).not.toHaveBeenCalled(); }); @@ -584,7 +584,7 @@ describe("InsertAutofillContentService", () => { insertAutofillContentService["insertValueIntoField"](element, value); expect( - insertAutofillContentService["handleInsertValueAndTriggerSimulatedEvents"] + insertAutofillContentService["handleInsertValueAndTriggerSimulatedEvents"], ).not.toHaveBeenCalled(); }); @@ -598,7 +598,7 @@ describe("InsertAutofillContentService", () => { expect(element.innerText).toBe(value); expect( - insertAutofillContentService["handleInsertValueAndTriggerSimulatedEvents"] + insertAutofillContentService["handleInsertValueAndTriggerSimulatedEvents"], ).toHaveBeenCalledWith(element, expect.any(Function)); }); @@ -616,10 +616,10 @@ describe("InsertAutofillContentService", () => { expect(checkboxElement.checked).toBe(true); expect(radioElement.checked).toBe(true); expect( - insertAutofillContentService["handleInsertValueAndTriggerSimulatedEvents"] + insertAutofillContentService["handleInsertValueAndTriggerSimulatedEvents"], ).toHaveBeenCalledWith(checkboxElement, expect.any(Function)); expect( - insertAutofillContentService["handleInsertValueAndTriggerSimulatedEvents"] + insertAutofillContentService["handleInsertValueAndTriggerSimulatedEvents"], ).toHaveBeenCalledWith(radioElement, expect.any(Function)); checkboxElement.checked = false; @@ -641,14 +641,14 @@ describe("InsertAutofillContentService", () => { expect(textInputElement.value).toBe(value1); expect( - insertAutofillContentService["handleInsertValueAndTriggerSimulatedEvents"] + insertAutofillContentService["handleInsertValueAndTriggerSimulatedEvents"], ).toHaveBeenCalledWith(textInputElement, expect.any(Function)); insertAutofillContentService["insertValueIntoField"](textareaElement, value2); expect(textareaElement.value).toBe(value2); expect( - insertAutofillContentService["handleInsertValueAndTriggerSimulatedEvents"] + insertAutofillContentService["handleInsertValueAndTriggerSimulatedEvents"], ).toHaveBeenCalledWith(textareaElement, expect.any(Function)); }); }); @@ -661,23 +661,23 @@ describe("InsertAutofillContentService", () => { jest.spyOn(insertAutofillContentService as any, "triggerPostInsertEventsOnElement"); jest.spyOn(insertAutofillContentService as any, "triggerFillAnimationOnElement"); const valueChangeCallback = jest.fn( - () => ((element as FillableFormFieldElement).value = value) + () => ((element as FillableFormFieldElement).value = value), ); insertAutofillContentService["handleInsertValueAndTriggerSimulatedEvents"]( element, - valueChangeCallback + valueChangeCallback, ); expect(insertAutofillContentService["triggerPreInsertEventsOnElement"]).toHaveBeenCalledWith( - element + element, ); expect(valueChangeCallback).toHaveBeenCalled(); expect(insertAutofillContentService["triggerPostInsertEventsOnElement"]).toHaveBeenCalledWith( - element + element, ); expect(insertAutofillContentService["triggerFillAnimationOnElement"]).toHaveBeenCalledWith( - element + element, ); expect((element as FillableFormFieldElement).value).toBe(value); }); @@ -690,17 +690,17 @@ describe("InsertAutofillContentService", () => { const element = document.getElementById("username") as FillableFormFieldElement; jest.spyOn( insertAutofillContentService as any, - "simulateUserMouseClickAndFocusEventInteractions" + "simulateUserMouseClickAndFocusEventInteractions", ); jest.spyOn(insertAutofillContentService as any, "simulateUserKeyboardEventInteractions"); insertAutofillContentService["triggerPreInsertEventsOnElement"](element); expect( - insertAutofillContentService["simulateUserMouseClickAndFocusEventInteractions"] + insertAutofillContentService["simulateUserMouseClickAndFocusEventInteractions"], ).toHaveBeenCalledWith(element); expect( - insertAutofillContentService["simulateUserKeyboardEventInteractions"] + insertAutofillContentService["simulateUserKeyboardEventInteractions"], ).toHaveBeenCalledWith(element); expect(element.value).toBe(initialElementValue); }); @@ -718,10 +718,10 @@ describe("InsertAutofillContentService", () => { insertAutofillContentService["triggerPostInsertEventsOnElement"](element); expect( - insertAutofillContentService["simulateUserKeyboardEventInteractions"] + insertAutofillContentService["simulateUserKeyboardEventInteractions"], ).toHaveBeenCalledWith(element); expect(insertAutofillContentService["simulateInputElementChangedEvent"]).toHaveBeenCalledWith( - element + element, ); expect(element.blur).toHaveBeenCalled(); expect(element.value).toBe(elementValue); @@ -738,7 +738,7 @@ describe("InsertAutofillContentService", () => { it("the element is a non-hidden hidden input type", async () => { document.body.innerHTML = mockLoginForm + ''; const testElement = document.querySelector( - 'input[type="hidden"]' + 'input[type="hidden"]', ) as FillableFormFieldElement; jest.spyOn(testElement.classList, "add"); jest.spyOn(testElement.classList, "remove"); @@ -778,7 +778,7 @@ describe("InsertAutofillContentService", () => { it("the element has a `visibility: hidden;` CSS rule applied to it", () => { const testElement = document.querySelector( - 'input[type="password"]' + 'input[type="password"]', ) as FillableFormFieldElement; testElement.style.visibility = "hidden"; jest.spyOn(testElement.classList, "add"); @@ -793,7 +793,7 @@ describe("InsertAutofillContentService", () => { it("the element has a `display: none;` CSS rule applied to it", () => { const testElement = document.querySelector( - 'input[type="password"]' + 'input[type="password"]', ) as FillableFormFieldElement; testElement.style.display = "none"; jest.spyOn(testElement.classList, "add"); @@ -810,7 +810,7 @@ describe("InsertAutofillContentService", () => { document.body.innerHTML = mockLoginForm + '
'; const testElement = document.querySelector( - 'input[type="email"]' + 'input[type="email"]', ) as FillableFormFieldElement; jest.spyOn(testElement.classList, "add"); jest.spyOn(testElement.classList, "remove"); @@ -826,11 +826,11 @@ describe("InsertAutofillContentService", () => { describe("will trigger the animation when...", () => { it("the element is a non-hidden password field", () => { const testElement = document.querySelector( - 'input[type="password"]' + 'input[type="password"]', ) as FillableFormFieldElement; jest.spyOn( insertAutofillContentService["domElementVisibilityService"], - "isElementHiddenByCss" + "isElementHiddenByCss", ); jest.spyOn(testElement.classList, "add"); jest.spyOn(testElement.classList, "remove"); @@ -839,20 +839,20 @@ describe("InsertAutofillContentService", () => { jest.advanceTimersByTime(200); expect( - insertAutofillContentService["domElementVisibilityService"].isElementHiddenByCss + insertAutofillContentService["domElementVisibilityService"].isElementHiddenByCss, ).toHaveBeenCalledWith(testElement); expect(testElement.classList.add).toHaveBeenCalledWith( - "com-bitwarden-browser-animated-fill" + "com-bitwarden-browser-animated-fill", ); expect(testElement.classList.remove).toHaveBeenCalledWith( - "com-bitwarden-browser-animated-fill" + "com-bitwarden-browser-animated-fill", ); }); it("the element is a non-hidden email input", () => { document.body.innerHTML = mockLoginForm + ''; const testElement = document.querySelector( - 'input[type="email"]' + 'input[type="email"]', ) as FillableFormFieldElement; jest.spyOn(testElement.classList, "add"); jest.spyOn(testElement.classList, "remove"); @@ -861,17 +861,17 @@ describe("InsertAutofillContentService", () => { jest.advanceTimersByTime(200); expect(testElement.classList.add).toHaveBeenCalledWith( - "com-bitwarden-browser-animated-fill" + "com-bitwarden-browser-animated-fill", ); expect(testElement.classList.remove).toHaveBeenCalledWith( - "com-bitwarden-browser-animated-fill" + "com-bitwarden-browser-animated-fill", ); }); it("the element is a non-hidden text input", () => { document.body.innerHTML = mockLoginForm + ''; const testElement = document.querySelector( - 'input[type="text"]' + 'input[type="text"]', ) as FillableFormFieldElement; jest.spyOn(testElement.classList, "add"); jest.spyOn(testElement.classList, "remove"); @@ -880,17 +880,17 @@ describe("InsertAutofillContentService", () => { jest.advanceTimersByTime(200); expect(testElement.classList.add).toHaveBeenCalledWith( - "com-bitwarden-browser-animated-fill" + "com-bitwarden-browser-animated-fill", ); expect(testElement.classList.remove).toHaveBeenCalledWith( - "com-bitwarden-browser-animated-fill" + "com-bitwarden-browser-animated-fill", ); }); it("the element is a non-hidden number input", () => { document.body.innerHTML = mockLoginForm + ''; const testElement = document.querySelector( - 'input[type="number"]' + 'input[type="number"]', ) as FillableFormFieldElement; jest.spyOn(testElement.classList, "add"); jest.spyOn(testElement.classList, "remove"); @@ -899,10 +899,10 @@ describe("InsertAutofillContentService", () => { jest.advanceTimersByTime(200); expect(testElement.classList.add).toHaveBeenCalledWith( - "com-bitwarden-browser-animated-fill" + "com-bitwarden-browser-animated-fill", ); expect(testElement.classList.remove).toHaveBeenCalledWith( - "com-bitwarden-browser-animated-fill" + "com-bitwarden-browser-animated-fill", ); }); @@ -916,10 +916,10 @@ describe("InsertAutofillContentService", () => { jest.advanceTimersByTime(200); expect(testElement.classList.add).toHaveBeenCalledWith( - "com-bitwarden-browser-animated-fill" + "com-bitwarden-browser-animated-fill", ); expect(testElement.classList.remove).toHaveBeenCalledWith( - "com-bitwarden-browser-animated-fill" + "com-bitwarden-browser-animated-fill", ); }); @@ -933,10 +933,10 @@ describe("InsertAutofillContentService", () => { jest.advanceTimersByTime(200); expect(testElement.classList.add).toHaveBeenCalledWith( - "com-bitwarden-browser-animated-fill" + "com-bitwarden-browser-animated-fill", ); expect(testElement.classList.remove).toHaveBeenCalledWith( - "com-bitwarden-browser-animated-fill" + "com-bitwarden-browser-animated-fill", ); }); @@ -950,10 +950,10 @@ describe("InsertAutofillContentService", () => { jest.advanceTimersByTime(200); expect(testElement.classList.add).toHaveBeenCalledWith( - "com-bitwarden-browser-animated-fill" + "com-bitwarden-browser-animated-fill", ); expect(testElement.classList.remove).toHaveBeenCalledWith( - "com-bitwarden-browser-animated-fill" + "com-bitwarden-browser-animated-fill", ); }); }); @@ -1009,11 +1009,11 @@ describe("InsertAutofillContentService", () => { insertAutofillContentService["simulateUserMouseClickAndFocusEventInteractions"](inputElement); expect(insertAutofillContentService["triggerClickOnElement"]).toHaveBeenCalledWith( - inputElement + inputElement, ); expect(insertAutofillContentService["triggerFocusOnElement"]).toHaveBeenCalledWith( inputElement, - false + false, ); }); }); @@ -1027,7 +1027,7 @@ describe("InsertAutofillContentService", () => { [EVENTS.KEYDOWN, EVENTS.KEYPRESS, EVENTS.KEYUP].forEach((eventName) => { expect(inputElement.dispatchEvent).toHaveBeenCalledWith( - new KeyboardEvent(eventName, { bubbles: true }) + new KeyboardEvent(eventName, { bubbles: true }), ); }); }); @@ -1042,7 +1042,7 @@ describe("InsertAutofillContentService", () => { [EVENTS.INPUT, EVENTS.CHANGE].forEach((eventName) => { expect(inputElement.dispatchEvent).toHaveBeenCalledWith( - new Event(eventName, { bubbles: true }) + new Event(eventName, { bubbles: true }), ); }); }); diff --git a/apps/browser/src/autofill/services/insert-autofill-content.service.ts b/apps/browser/src/autofill/services/insert-autofill-content.service.ts index 11a2d7fc71e..3d95de0767c 100644 --- a/apps/browser/src/autofill/services/insert-autofill-content.service.ts +++ b/apps/browser/src/autofill/services/insert-autofill-content.service.ts @@ -21,7 +21,7 @@ class InsertAutofillContentService implements InsertAutofillContentServiceInterf */ constructor( domElementVisibilityService: DomElementVisibilityService, - collectAutofillContentService: CollectAutofillContentService + collectAutofillContentService: CollectAutofillContentService, ) { this.domElementVisibilityService = domElementVisibilityService; this.collectAutofillContentService = collectAutofillContentService; @@ -97,8 +97,8 @@ class InsertAutofillContentService implements InsertAutofillContentServiceInterf this.collectAutofillContentService.queryAllTreeWalkerNodes( document.documentElement, (node: Node) => node instanceof HTMLInputElement && node.type === "password", - false - )?.length + false, + )?.length, ); } @@ -139,7 +139,7 @@ class InsertAutofillContentService implements InsertAutofillContentServiceInterf */ private runFillScriptAction = ( [action, opid, value]: FillScript, - actionIndex: number + actionIndex: number, ): Promise => { if (!opid || !this.autofillInsertActions[action]) { return; @@ -150,7 +150,7 @@ class InsertAutofillContentService implements InsertAutofillContentServiceInterf setTimeout(() => { this.autofillInsertActions[action]({ opid, value }); resolve(); - }, delayActionsInMilliseconds * actionIndex) + }, delayActionsInMilliseconds * actionIndex), ); }; @@ -233,7 +233,7 @@ class InsertAutofillContentService implements InsertAutofillContentServiceInterf */ private handleInsertValueAndTriggerSimulatedEvents( element: FormFieldElement, - valueChangeCallback: CallableFunction + valueChangeCallback: CallableFunction, ): void { this.triggerPreInsertEventsOnElement(element); valueChangeCallback(); @@ -341,7 +341,7 @@ class InsertAutofillContentService implements InsertAutofillContentServiceInterf */ private simulateUserMouseClickAndFocusEventInteractions( element: FormFieldElement, - shouldResetValue = false + shouldResetValue = false, ): void { this.triggerClickOnElement(element); this.triggerFocusOnElement(element, shouldResetValue); diff --git a/apps/browser/src/autofill/utils/utils.spec.ts b/apps/browser/src/autofill/utils/utils.spec.ts index 43c3ef03ae5..1da83fef242 100644 --- a/apps/browser/src/autofill/utils/utils.spec.ts +++ b/apps/browser/src/autofill/utils/utils.spec.ts @@ -52,7 +52,7 @@ describe("setElementStyles", () => { const domParser = new DOMParser(); const testDivDOM = domParser.parseFromString( "
This is an unexciting div.
", - "text/html" + "text/html", ); const testDiv = testDivDOM.querySelector("div"); @@ -67,7 +67,7 @@ describe("setElementStyles", () => { const domParser = new DOMParser(); const testDivDOM = domParser.parseFromString( "
This is an unexciting div.
", - "text/html" + "text/html", ); const testDiv = testDivDOM.querySelector("div"); @@ -82,7 +82,7 @@ describe("setElementStyles", () => { const domParser = new DOMParser(); const testDivDOM = domParser.parseFromString( "
This is an unexciting div.
", - "text/html" + "text/html", ); const testDiv = testDivDOM.querySelector("div"); @@ -101,7 +101,7 @@ describe("setElementStyles", () => { const domParser = new DOMParser(); const testDivDOM = domParser.parseFromString( "
This is an unexciting div.
", - "text/html" + "text/html", ); const testDiv = testDivDOM.querySelector("div"); diff --git a/apps/browser/src/autofill/utils/utils.ts b/apps/browser/src/autofill/utils/utils.ts index 8f953fb01b9..73e133da32c 100644 --- a/apps/browser/src/autofill/utils/utils.ts +++ b/apps/browser/src/autofill/utils/utils.ts @@ -65,7 +65,7 @@ function buildSvgDomElement(svgString: string, ariaHidden = true): HTMLElement { */ async function sendExtensionMessage( command: string, - options: Record = {} + options: Record = {}, ): Promise { return new Promise((resolve) => { chrome.runtime.sendMessage(Object.assign({ command }, options), (response) => { @@ -88,7 +88,7 @@ async function sendExtensionMessage( function setElementStyles( element: HTMLElement, styles: Partial, - priority?: boolean + priority?: boolean, ) { if (!element || !styles || !Object.keys(styles).length) { return; @@ -98,7 +98,7 @@ function setElementStyles( element.style.setProperty( styleProperty.replace(/([a-z])([A-Z])/g, "$1-$2"), // Convert camelCase to kebab-case styles[styleProperty], - priority ? "important" : undefined + priority ? "important" : undefined, ); } } diff --git a/apps/browser/src/background/commands.background.ts b/apps/browser/src/background/commands.background.ts index 210119aa5f2..a37717baa03 100644 --- a/apps/browser/src/background/commands.background.ts +++ b/apps/browser/src/background/commands.background.ts @@ -5,10 +5,10 @@ import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/pl import { PasswordGenerationServiceAbstraction } from "@bitwarden/common/tools/generator/password"; import { openUnlockPopout } from "../auth/popup/utils/auth-popout-window"; +import LockedVaultPendingNotificationsItem from "../autofill/notification/models/locked-vault-pending-notifications-item"; import { BrowserApi } from "../platform/browser/browser-api"; import MainBackground from "./main.background"; -import LockedVaultPendingNotificationsItem from "./models/lockedVaultPendingNotificationsItem"; export default class CommandsBackground { private isSafari: boolean; @@ -19,7 +19,7 @@ export default class CommandsBackground { private passwordGenerationService: PasswordGenerationServiceAbstraction, private platformUtilsService: PlatformUtilsService, private vaultTimeoutService: VaultTimeoutService, - private authService: AuthService + private authService: AuthService, ) { this.isSafari = this.platformUtilsService.isSafari(); this.isVivaldi = this.platformUtilsService.isVivaldi(); @@ -85,7 +85,7 @@ export default class CommandsBackground { await BrowserApi.tabSendMessageData( tab, "addToLockedVaultPendingNotifications", - retryMessage + retryMessage, ); await openUnlockPopout(tab); diff --git a/apps/browser/src/background/idle.background.ts b/apps/browser/src/background/idle.background.ts index 7200301c795..ccc76883b11 100644 --- a/apps/browser/src/background/idle.background.ts +++ b/apps/browser/src/background/idle.background.ts @@ -14,7 +14,7 @@ export default class IdleBackground { constructor( private vaultTimeoutService: VaultTimeoutService, private stateService: BrowserStateService, - private notificationsService: NotificationsService + private notificationsService: NotificationsService, ) { this.idle = chrome.idle || (browser != null ? browser.idle : null); } diff --git a/apps/browser/src/background/main.background.ts b/apps/browser/src/background/main.background.ts index bb61bb64658..ec08fef4e22 100644 --- a/apps/browser/src/background/main.background.ts +++ b/apps/browser/src/background/main.background.ts @@ -24,6 +24,8 @@ import { TokenService as TokenServiceAbstraction } from "@bitwarden/common/auth/ import { TwoFactorService as TwoFactorServiceAbstraction } from "@bitwarden/common/auth/abstractions/two-factor.service"; import { UserVerificationApiServiceAbstraction } from "@bitwarden/common/auth/abstractions/user-verification/user-verification-api.service.abstraction"; import { UserVerificationService as UserVerificationServiceAbstraction } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction"; +import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status"; +import { ForceSetPasswordReason } from "@bitwarden/common/auth/models/domain/force-set-password-reason"; import { AccountServiceImplementation } from "@bitwarden/common/auth/services/account.service"; import { AuthRequestCryptoServiceImplementation } from "@bitwarden/common/auth/services/auth-request-crypto.service.implementation"; import { AuthService } from "@bitwarden/common/auth/services/auth.service"; @@ -87,6 +89,7 @@ import { import { SendApiService } from "@bitwarden/common/tools/send/services/send-api.service"; import { SendApiService as SendApiServiceAbstraction } from "@bitwarden/common/tools/send/services/send-api.service.abstraction"; import { InternalSendService as InternalSendServiceAbstraction } from "@bitwarden/common/tools/send/services/send.service.abstraction"; +import { UserId } from "@bitwarden/common/types/guid"; import { CipherService as CipherServiceAbstraction } from "@bitwarden/common/vault/abstractions/cipher.service"; import { CollectionService as CollectionServiceAbstraction } from "@bitwarden/common/vault/abstractions/collection.service"; import { Fido2AuthenticatorService as Fido2AuthenticatorServiceAbstraction } from "@bitwarden/common/vault/abstractions/fido2/fido2-authenticator.service.abstraction"; @@ -125,6 +128,7 @@ import ContextMenusBackground from "../autofill/background/context-menus.backgro import NotificationBackground from "../autofill/background/notification.background"; import OverlayBackground from "../autofill/background/overlay.background"; import TabsBackground from "../autofill/background/tabs.background"; +import WebRequestBackground from "../autofill/background/web-request.background"; import { CipherContextMenuHandler } from "../autofill/browser/cipher-context-menu-handler"; import { ContextMenuClickedHandler } from "../autofill/browser/context-menu-clicked-handler"; import { MainContextMenuHandler } from "../autofill/browser/main-context-menu-handler"; @@ -159,7 +163,6 @@ import CommandsBackground from "./commands.background"; import IdleBackground from "./idle.background"; import { NativeMessagingBackground } from "./nativeMessaging.background"; import RuntimeBackground from "./runtime.background"; -import WebRequestBackground from "./webRequest.background"; export default class MainBackground { messagingService: MessagingServiceAbstraction; @@ -281,17 +284,17 @@ export default class MainBackground { BrowserApi.manifestVersion === 3 ? new LocalBackedSessionStorageService( new EncryptServiceImplementation(this.cryptoFunctionService, this.logService, false), - new KeyGenerationService(this.cryptoFunctionService) + new KeyGenerationService(this.cryptoFunctionService), ) : new BackgroundMemoryStorageService(); this.globalStateProvider = new DefaultGlobalStateProvider( this.memoryStorageService as BackgroundMemoryStorageService, - this.storageService as BrowserLocalStorageService + this.storageService as BrowserLocalStorageService, ); this.accountService = new AccountServiceImplementation( this.messagingService, this.logService, - this.globalStateProvider + this.globalStateProvider, ); this.stateService = new BrowserStateService( this.storageService, @@ -299,7 +302,7 @@ export default class MainBackground { this.memoryStorageService, this.logService, new StateFactory(GlobalState, Account), - this.accountService + this.accountService, ); this.platformUtilsService = new BrowserPlatformUtilsService( this.messagingService, @@ -321,14 +324,14 @@ export default class MainBackground { return promise.then((result) => result.response === "unlocked"); } }, - window + window, ); this.i18nService = new BrowserI18nService(BrowserApi.getUILanguage(), this.stateService); this.encryptService = flagEnabled("multithreadDecryption") ? new MultithreadEncryptServiceImplementation( this.cryptoFunctionService, this.logService, - true + true, ) : new EncryptServiceImplementation(this.cryptoFunctionService, this.logService, true); this.cryptoService = new BrowserCryptoService( @@ -336,7 +339,7 @@ export default class MainBackground { this.encryptService, this.platformUtilsService, this.logService, - this.stateService + this.stateService, ); this.tokenService = new TokenService(this.stateService); this.appIdService = new AppIdService(this.storageService); @@ -346,20 +349,20 @@ export default class MainBackground { this.platformUtilsService, this.environmentService, this.appIdService, - (expired: boolean) => this.logout(expired) + (expired: boolean) => this.logout(expired), ); this.settingsService = new BrowserSettingsService(this.stateService); this.fileUploadService = new FileUploadService(this.logService); this.cipherFileUploadService = new CipherFileUploadService( this.apiService, - this.fileUploadService + this.fileUploadService, ); this.searchService = new SearchService(this.logService, this.i18nService); this.collectionService = new CollectionService( this.cryptoService, this.i18nService, - this.stateService + this.stateService, ); this.syncNotifierService = new SyncNotifierService(); this.organizationService = new BrowserOrganizationService(this.stateService); @@ -367,7 +370,7 @@ export default class MainBackground { this.policyApiService = new PolicyApiService( this.policyService, this.apiService, - this.stateService + this.stateService, ); this.keyConnectorService = new KeyConnectorService( this.stateService, @@ -377,7 +380,7 @@ export default class MainBackground { this.logService, this.organizationService, this.cryptoFunctionService, - logoutCallback + logoutCallback, ); this.passwordStrengthService = new PasswordStrengthService(); @@ -385,7 +388,7 @@ export default class MainBackground { this.passwordGenerationService = new PasswordGenerationService( this.cryptoService, this.policyService, - this.stateService + this.stateService, ); this.twoFactorService = new TwoFactorService(this.i18nService, this.platformUtilsService); @@ -409,7 +412,7 @@ export default class MainBackground { this.appIdService, this.devicesApiService, this.i18nService, - this.platformUtilsService + this.platformUtilsService, ); this.devicesService = new DevicesServiceImplementation(this.devicesApiService); @@ -433,7 +436,7 @@ export default class MainBackground { this.passwordStrengthService, this.policyService, this.deviceTrustCryptoService, - this.authRequestCryptoService + this.authRequestCryptoService, ); this.userVerificationApiService = new UserVerificationApiService(this.apiService); @@ -442,7 +445,7 @@ export default class MainBackground { this.stateService, this.cryptoService, this.i18nService, - this.userVerificationApiService + this.userVerificationApiService, ); this.configApiService = new ConfigApiService(this.apiService, this.authService); @@ -453,7 +456,7 @@ export default class MainBackground { this.authService, this.environmentService, this.logService, - true + true, ); this.cipherService = new CipherService( @@ -465,13 +468,13 @@ export default class MainBackground { this.stateService, this.encryptService, this.cipherFileUploadService, - this.configService + this.configService, ); this.folderService = new BrowserFolderService( this.cryptoService, this.i18nService, this.cipherService, - this.stateService + this.stateService, ); this.folderApiService = new FolderApiService(this.folderService, this.apiService); @@ -480,7 +483,7 @@ export default class MainBackground { this.tokenService, this.policyService, this.stateService, - this.userVerificationService + this.userVerificationService, ); this.vaultFilterService = new VaultFilterService( @@ -489,7 +492,7 @@ export default class MainBackground { this.folderService, this.cipherService, this.collectionService, - this.policyService + this.policyService, ); this.vaultTimeoutService = new VaultTimeoutService( @@ -504,19 +507,19 @@ export default class MainBackground { this.authService, this.vaultTimeoutSettingsService, lockedCallback, - logoutCallback + logoutCallback, ); this.containerService = new ContainerService(this.cryptoService, this.encryptService); this.sendService = new BrowserSendService( this.cryptoService, this.i18nService, this.cryptoFunctionService, - this.stateService + this.stateService, ); this.sendApiService = new SendApiService( this.apiService, this.fileUploadService, - this.sendService + this.sendService, ); this.providerService = new ProviderService(this.stateService); this.syncService = new SyncService( @@ -536,18 +539,19 @@ export default class MainBackground { this.folderApiService, this.organizationService, this.sendApiService, - logoutCallback + this.configService, + logoutCallback, ); this.eventUploadService = new EventUploadService( this.apiService, this.stateService, - this.logService + this.logService, ); this.eventCollectionService = new EventCollectionService( this.cipherService, this.stateService, this.organizationService, - this.eventUploadService + this.eventUploadService, ); this.totpService = new TotpService(this.cryptoFunctionService, this.logService); @@ -558,7 +562,7 @@ export default class MainBackground { this.eventCollectionService, this.logService, this.settingsService, - this.userVerificationService + this.userVerificationService, ); this.auditService = new AuditService(this.cryptoFunctionService, this.apiService); @@ -570,7 +574,7 @@ export default class MainBackground { this.importApiService, this.i18nService, this.collectionService, - this.cryptoService + this.cryptoService, ); this.exportService = new VaultExportService( @@ -579,7 +583,7 @@ export default class MainBackground { this.apiService, this.cryptoService, this.cryptoFunctionService, - this.stateService + this.stateService, ); this.notificationsService = new NotificationsService( this.logService, @@ -590,7 +594,7 @@ export default class MainBackground { logoutCallback, this.stateService, this.authService, - this.messagingService + this.messagingService, ); this.fido2UserInterfaceService = new BrowserFido2UserInterfaceService(this.authService); @@ -598,14 +602,14 @@ export default class MainBackground { this.cipherService, this.fido2UserInterfaceService, this.syncService, - this.logService + this.logService, ); this.fido2ClientService = new Fido2ClientService( this.fido2AuthenticatorService, this.configService, this.authService, this.stateService, - this.logService + this.logService, ); const systemUtilsServiceReloadCallback = () => { @@ -621,7 +625,8 @@ export default class MainBackground { this.messagingService, this.platformUtilsService, systemUtilsServiceReloadCallback, - this.stateService + this.stateService, + this.vaultTimeoutSettingsService, ); // Other fields @@ -639,7 +644,7 @@ export default class MainBackground { this.environmentService, this.messagingService, this.logService, - this.configService + this.configService, ); this.nativeMessagingBackground = new NativeMessagingBackground( this.cryptoService, @@ -651,14 +656,14 @@ export default class MainBackground { this.platformUtilsService, this.stateService, this.logService, - this.authService + this.authService, ); this.commandsBackground = new CommandsBackground( this, this.passwordGenerationService, this.platformUtilsService, this.vaultTimeoutService, - this.authService + this.authService, ); this.notificationBackground = new NotificationBackground( this.autofillService, @@ -667,7 +672,7 @@ export default class MainBackground { this.policyService, this.folderService, this.stateService, - this.environmentService + this.environmentService, ); this.overlayBackground = new OverlayBackground( this.cipherService, @@ -676,12 +681,12 @@ export default class MainBackground { this.environmentService, this.settingsService, this.stateService, - this.i18nService + this.i18nService, ); this.tabsBackground = new TabsBackground( this, this.notificationBackground, - this.overlayBackground + this.overlayBackground, ); if (!this.popupOnlyContext) { const contextMenuClickedHandler = new ContextMenuClickedHandler( @@ -709,7 +714,7 @@ export default class MainBackground { this.stateService, this.totpService, this.eventCollectionService, - this.userVerificationService + this.userVerificationService, ); this.contextMenusBackground = new ContextMenusBackground(contextMenuClickedHandler); @@ -718,18 +723,18 @@ export default class MainBackground { this.idleBackground = new IdleBackground( this.vaultTimeoutService, this.stateService, - this.notificationsService + this.notificationsService, ); this.webRequestBackground = new WebRequestBackground( this.platformUtilsService, this.cipherService, - this.authService + this.authService, ); this.usernameGenerationService = new UsernameGenerationService( this.cryptoService, this.stateService, - this.apiService + this.apiService, ); this.avatarUpdateService = new AvatarUpdateService(this.apiService, this.stateService); @@ -738,13 +743,13 @@ export default class MainBackground { this.mainContextMenuHandler = new MainContextMenuHandler( this.stateService, this.i18nService, - this.logService + this.logService, ); this.cipherContextMenuHandler = new CipherContextMenuHandler( this.mainContextMenuHandler, this.authService, - this.cipherService + this.cipherService, ); } } @@ -829,6 +834,43 @@ export default class MainBackground { } } + /** + * Switch accounts to indicated userId -- null is no active user + */ + async switchAccount(userId: UserId) { + try { + await this.stateService.setActiveUser(userId); + + if (userId == null) { + await this.stateService.setRememberedEmail(null); + await this.refreshBadge(); + await this.refreshMenu(); + return; + } + + const status = await this.authService.getAuthStatus(userId); + const forcePasswordReset = + (await this.stateService.getForceSetPasswordReason({ userId: userId })) != + ForceSetPasswordReason.None; + + await this.systemService.clearPendingClipboard(); + await this.notificationsService.updateConnection(false); + + if (status === AuthenticationStatus.Locked) { + this.messagingService.send("locked", { userId: userId }); + } else if (forcePasswordReset) { + this.messagingService.send("update-temp-password", { userId: userId }); + } else { + this.messagingService.send("unlocked", { userId: userId }); + await this.refreshBadge(); + await this.refreshMenu(); + await this.syncService.fullSync(false); + } + } finally { + this.messagingService.send("switchAccountFinish", { userId: userId }); + } + } + async logout(expired: boolean, userId?: string) { await this.eventUploadService.uploadEvents(userId); @@ -849,9 +891,16 @@ export default class MainBackground { //Needs to be checked before state is cleaned const needStorageReseed = await this.needsStorageReseed(); - await this.stateService.clean({ userId: userId }); + const currentUserId = await this.stateService.getUserId(); + const newActiveUser = await this.stateService.clean({ userId: userId }); - if (userId == null || userId === (await this.stateService.getUserId())) { + if (newActiveUser != null) { + // we have a new active user, do not continue tearing down application + await this.switchAccount(newActiveUser as UserId); + this.messagingService.send("switchAccountFinish"); + } + + if (userId == null || userId === currentUserId) { this.searchService.clearIndex(); this.messagingService.send("doneLoggingOut", { expired: expired, userId: userId }); } @@ -892,7 +941,7 @@ export default class MainBackground { tab: tab, sender: sender, }, - options + options, ); } diff --git a/apps/browser/src/background/models/add-unlock-vault-queue-message.ts b/apps/browser/src/background/models/add-unlock-vault-queue-message.ts deleted file mode 100644 index 9ddde271008..00000000000 --- a/apps/browser/src/background/models/add-unlock-vault-queue-message.ts +++ /dev/null @@ -1,6 +0,0 @@ -import NotificationQueueMessage from "./notificationQueueMessage"; -import { NotificationQueueMessageType } from "./notificationQueueMessageType"; - -export default class AddUnlockVaultQueueMessage extends NotificationQueueMessage { - type: NotificationQueueMessageType.UnlockVault; -} diff --git a/apps/browser/src/background/nativeMessaging.background.ts b/apps/browser/src/background/nativeMessaging.background.ts index f4eed16823a..308f40aeb4c 100644 --- a/apps/browser/src/background/nativeMessaging.background.ts +++ b/apps/browser/src/background/nativeMessaging.background.ts @@ -81,7 +81,7 @@ export class NativeMessagingBackground { private platformUtilsService: PlatformUtilsService, private stateService: StateService, private logService: LogService, - private authService: AuthService + private authService: AuthService, ) { this.stateService.setBiometricFingerprintValidated(false); @@ -136,7 +136,7 @@ export class NativeMessagingBackground { const decrypted = await this.cryptoFunctionService.rsaDecrypt( encrypted, this.privateKey, - EncryptionAlgorithm + EncryptionAlgorithm, ); if (this.validatingFingerprint) { @@ -278,7 +278,7 @@ export class NativeMessagingBackground { let message = rawMessage as ReceiveMessage; if (!this.platformUtilsService.isSafari()) { message = JSON.parse( - await this.cryptoService.decryptToUtf8(rawMessage as EncString, this.sharedSecret) + await this.cryptoService.decryptToUtf8(rawMessage as EncString, this.sharedSecret), ); } @@ -328,7 +328,7 @@ export class NativeMessagingBackground { try { if (message.userKeyB64) { const userKey = new SymmetricCryptoKey( - Utils.fromB64ToArray(message.userKeyB64) + Utils.fromB64ToArray(message.userKeyB64), ) as UserKey; await this.cryptoService.setUserKey(userKey); } else if (message.keyB64) { @@ -340,11 +340,11 @@ export class NativeMessagingBackground { throw new Error("No encrypted user key found"); } const masterKey = new SymmetricCryptoKey( - Utils.fromB64ToArray(message.keyB64) + Utils.fromB64ToArray(message.keyB64), ) as MasterKey; const userKey = await this.cryptoService.decryptUserKeyWithMasterKey( masterKey, - new EncString(encUserKey) + new EncString(encUserKey), ); await this.cryptoService.setMasterKey(masterKey); await this.cryptoService.setUserKey(userKey); @@ -424,7 +424,7 @@ export class NativeMessagingBackground { private async showFingerprintDialog() { const fingerprint = await this.cryptoService.getFingerprint( await this.stateService.getUserId(), - this.publicKey + this.publicKey, ); this.messagingService.send("showNativeMessagingFinterprintDialog", { diff --git a/apps/browser/src/background/runtime.background.ts b/apps/browser/src/background/runtime.background.ts index a5c101fa969..434fd078347 100644 --- a/apps/browser/src/background/runtime.background.ts +++ b/apps/browser/src/background/runtime.background.ts @@ -13,6 +13,7 @@ import { openSsoAuthResultPopout, openTwoFactorAuthPopout, } from "../auth/popup/utils/auth-popout-window"; +import LockedVaultPendingNotificationsItem from "../autofill/notification/models/locked-vault-pending-notifications-item"; import { AutofillService } from "../autofill/services/abstractions/autofill.service"; import { BrowserApi } from "../platform/browser/browser-api"; import { BrowserStateService } from "../platform/services/abstractions/browser-state.service"; @@ -21,7 +22,6 @@ import BrowserPlatformUtilsService from "../platform/services/browser-platform-u import { AbortManager } from "../vault/background/abort-manager"; import MainBackground from "./main.background"; -import LockedVaultPendingNotificationsItem from "./models/lockedVaultPendingNotificationsItem"; export default class RuntimeBackground { private autofillTimeout: any; @@ -41,7 +41,7 @@ export default class RuntimeBackground { private environmentService: BrowserEnvironmentService, private messagingService: MessagingService, private logService: LogService, - private configService: ConfigServiceAbstraction + private configService: ConfigServiceAbstraction, ) { // onInstalled listener must be wired up before anything else, so we do it in the ctor chrome.runtime.onInstalled.addListener((details: any) => { @@ -58,7 +58,7 @@ export default class RuntimeBackground { const backgroundMessageListener = ( msg: any, sender: chrome.runtime.MessageSender, - sendResponse: any + sendResponse: any, ) => { const messagesWithResponse = [ "checkFido2FeatureEnabled", @@ -69,7 +69,7 @@ export default class RuntimeBackground { if (messagesWithResponse.includes(msg.command)) { this.processMessage(msg, sender).then( (value) => sendResponse({ result: value }), - (error) => sendResponse({ error: { ...error, message: error.message } }) + (error) => sendResponse({ error: { ...error, message: error.message } }), ); return true; } @@ -106,7 +106,7 @@ export default class RuntimeBackground { await BrowserApi.tabSendMessageData( item.commandToRetry.sender.tab, "unlockCompleted", - item + item, ); } break; @@ -134,7 +134,7 @@ export default class RuntimeBackground { await this.autofillService.injectAutofillScripts( sender, await this.configService.getFeatureFlag(FeatureFlag.AutofillV2), - await this.configService.getFeatureFlag(FeatureFlag.AutofillOverlay) + await this.configService.getFeatureFlag(FeatureFlag.AutofillOverlay), ); break; case "bgCollectPageDetails": @@ -163,7 +163,7 @@ export default class RuntimeBackground { details: msg.details, }, ], - msg.sender === "autofill_cmd" + msg.sender === "autofill_cmd", ); if (totpCode != null) { this.platformUtilsService.copyToClipboard(totpCode, { window: window }); @@ -180,7 +180,7 @@ export default class RuntimeBackground { }, ], false, - CipherType.Card + CipherType.Card, ); break; } @@ -194,7 +194,7 @@ export default class RuntimeBackground { }, ], false, - CipherType.Identity + CipherType.Identity, ); break; } @@ -270,13 +270,13 @@ export default class RuntimeBackground { return await this.main.fido2ClientService.createCredential( msg.data, sender.tab, - abortController + abortController, ); } finally { await BrowserApi.focusTab(sender.tab.id); await BrowserApi.focusWindow(sender.tab.windowId); } - } + }, ); case "fido2GetCredentialRequest": return await this.abortManager.runWithAbortController( @@ -286,14 +286,17 @@ export default class RuntimeBackground { return await this.main.fido2ClientService.assertCredential( msg.data, sender.tab, - abortController + abortController, ); } finally { await BrowserApi.focusTab(sender.tab.id); await BrowserApi.focusWindow(sender.tab.windowId); } - } + }, ); + case "switchAccount": { + await this.main.switchAccount(msg.userId); + } } } diff --git a/apps/browser/src/background/service-factories/cipher-file-upload-service.factory.ts b/apps/browser/src/background/service-factories/cipher-file-upload-service.factory.ts index 7c83958927f..4127ff5acf4 100644 --- a/apps/browser/src/background/service-factories/cipher-file-upload-service.factory.ts +++ b/apps/browser/src/background/service-factories/cipher-file-upload-service.factory.ts @@ -23,7 +23,7 @@ export type CipherFileUploadServiceInitOptions = CipherFileUploadServiceFactoyOp export function cipherFileUploadServiceFactory( cache: { cipherFileUploadService?: CipherFileUploadServiceAbstraction } & CachedServices, - opts: CipherFileUploadServiceInitOptions + opts: CipherFileUploadServiceInitOptions, ): Promise { return factory( cache, @@ -32,7 +32,7 @@ export function cipherFileUploadServiceFactory( async () => new CipherFileUploadService( await apiServiceFactory(cache, opts), - await fileUploadServiceFactory(cache, opts) - ) + await fileUploadServiceFactory(cache, opts), + ), ); } diff --git a/apps/browser/src/background/service-factories/cipher-file-upload.service.factory.ts b/apps/browser/src/background/service-factories/cipher-file-upload.service.factory.ts index 7c83958927f..4127ff5acf4 100644 --- a/apps/browser/src/background/service-factories/cipher-file-upload.service.factory.ts +++ b/apps/browser/src/background/service-factories/cipher-file-upload.service.factory.ts @@ -23,7 +23,7 @@ export type CipherFileUploadServiceInitOptions = CipherFileUploadServiceFactoyOp export function cipherFileUploadServiceFactory( cache: { cipherFileUploadService?: CipherFileUploadServiceAbstraction } & CachedServices, - opts: CipherFileUploadServiceInitOptions + opts: CipherFileUploadServiceInitOptions, ): Promise { return factory( cache, @@ -32,7 +32,7 @@ export function cipherFileUploadServiceFactory( async () => new CipherFileUploadService( await apiServiceFactory(cache, opts), - await fileUploadServiceFactory(cache, opts) - ) + await fileUploadServiceFactory(cache, opts), + ), ); } diff --git a/apps/browser/src/background/service-factories/devices-api-service.factory.ts b/apps/browser/src/background/service-factories/devices-api-service.factory.ts index 8999b7c2c72..117b82777ae 100644 --- a/apps/browser/src/background/service-factories/devices-api-service.factory.ts +++ b/apps/browser/src/background/service-factories/devices-api-service.factory.ts @@ -17,12 +17,12 @@ export type DevicesApiServiceInitOptions = DevicesApiServiceFactoryOptions & Api export function devicesApiServiceFactory( cache: { devicesApiService?: DevicesApiServiceAbstraction } & CachedServices, - opts: DevicesApiServiceInitOptions + opts: DevicesApiServiceInitOptions, ): Promise { return factory( cache, "devicesApiService", opts, - async () => new DevicesApiServiceImplementation(await apiServiceFactory(cache, opts)) + async () => new DevicesApiServiceImplementation(await apiServiceFactory(cache, opts)), ); } diff --git a/apps/browser/src/background/service-factories/event-collection-service.factory.ts b/apps/browser/src/background/service-factories/event-collection-service.factory.ts index 5ed6c24ca4b..7ce77da045b 100644 --- a/apps/browser/src/background/service-factories/event-collection-service.factory.ts +++ b/apps/browser/src/background/service-factories/event-collection-service.factory.ts @@ -34,7 +34,7 @@ export type EventCollectionServiceInitOptions = EventCollectionServiceOptions & export function eventCollectionServiceFactory( cache: { eventCollectionService?: AbstractEventCollectionService } & CachedServices, - opts: EventCollectionServiceInitOptions + opts: EventCollectionServiceInitOptions, ): Promise { return factory( cache, @@ -45,7 +45,7 @@ export function eventCollectionServiceFactory( await cipherServiceFactory(cache, opts), await stateServiceFactory(cache, opts), await organizationServiceFactory(cache, opts), - await eventUploadServiceFactory(cache, opts) - ) + await eventUploadServiceFactory(cache, opts), + ), ); } diff --git a/apps/browser/src/background/service-factories/event-upload-service.factory.ts b/apps/browser/src/background/service-factories/event-upload-service.factory.ts index f9e72395a74..fcaec459c0d 100644 --- a/apps/browser/src/background/service-factories/event-upload-service.factory.ts +++ b/apps/browser/src/background/service-factories/event-upload-service.factory.ts @@ -28,7 +28,7 @@ export type EventUploadServiceInitOptions = EventUploadServiceOptions & export function eventUploadServiceFactory( cache: { eventUploadService?: AbstractEventUploadService } & CachedServices, - opts: EventUploadServiceInitOptions + opts: EventUploadServiceInitOptions, ): Promise { return factory( cache, @@ -38,7 +38,7 @@ export function eventUploadServiceFactory( new EventUploadService( await apiServiceFactory(cache, opts), await stateServiceFactory(cache, opts), - await logServiceFactory(cache, opts) - ) + await logServiceFactory(cache, opts), + ), ); } diff --git a/apps/browser/src/background/service-factories/password-generation-service.factory.ts b/apps/browser/src/background/service-factories/password-generation-service.factory.ts index d97e8ce98a6..ec7ca50ec46 100644 --- a/apps/browser/src/background/service-factories/password-generation-service.factory.ts +++ b/apps/browser/src/background/service-factories/password-generation-service.factory.ts @@ -30,7 +30,7 @@ export type PasswordGenerationServiceInitOptions = PasswordGenerationServiceFact export function passwordGenerationServiceFactory( cache: { passwordGenerationService?: PasswordGenerationServiceAbstraction } & CachedServices, - opts: PasswordGenerationServiceInitOptions + opts: PasswordGenerationServiceInitOptions, ): Promise { return factory( cache, @@ -40,7 +40,7 @@ export function passwordGenerationServiceFactory( new PasswordGenerationService( await cryptoServiceFactory(cache, opts), await policyServiceFactory(cache, opts), - await stateServiceFactory(cache, opts) - ) + await stateServiceFactory(cache, opts), + ), ); } diff --git a/apps/browser/src/background/service-factories/search-service.factory.ts b/apps/browser/src/background/service-factories/search-service.factory.ts index 6ff9691c524..38c7620b5aa 100644 --- a/apps/browser/src/background/service-factories/search-service.factory.ts +++ b/apps/browser/src/background/service-factories/search-service.factory.ts @@ -23,13 +23,16 @@ export type SearchServiceInitOptions = SearchServiceFactoryOptions & export function searchServiceFactory( cache: { searchService?: AbstractSearchService } & CachedServices, - opts: SearchServiceInitOptions + opts: SearchServiceInitOptions, ): Promise { return factory( cache, "searchService", opts, async () => - new SearchService(await logServiceFactory(cache, opts), await i18nServiceFactory(cache, opts)) + new SearchService( + await logServiceFactory(cache, opts), + await i18nServiceFactory(cache, opts), + ), ); } diff --git a/apps/browser/src/background/service-factories/send-service.factory.ts b/apps/browser/src/background/service-factories/send-service.factory.ts index bc0f83787fd..bb5ef1fa37f 100644 --- a/apps/browser/src/background/service-factories/send-service.factory.ts +++ b/apps/browser/src/background/service-factories/send-service.factory.ts @@ -29,7 +29,7 @@ export type SendServiceInitOptions = SendServiceFactoryOptions & export function sendServiceFactory( cache: { sendService?: InternalSendService } & CachedServices, - opts: SendServiceInitOptions + opts: SendServiceInitOptions, ): Promise { return factory( cache, @@ -40,7 +40,7 @@ export function sendServiceFactory( await cryptoServiceFactory(cache, opts), await i18nServiceFactory(cache, opts), await cryptoFunctionServiceFactory(cache, opts), - await stateServiceFactory(cache, opts) - ) + await stateServiceFactory(cache, opts), + ), ); } diff --git a/apps/browser/src/background/service-factories/settings-service.factory.ts b/apps/browser/src/background/service-factories/settings-service.factory.ts index 17e22a6678d..28e97de51fa 100644 --- a/apps/browser/src/background/service-factories/settings-service.factory.ts +++ b/apps/browser/src/background/service-factories/settings-service.factory.ts @@ -17,12 +17,12 @@ export type SettingsServiceInitOptions = SettingsServiceFactoryOptions & StateSe export function settingsServiceFactory( cache: { settingsService?: AbstractSettingsService } & CachedServices, - opts: SettingsServiceInitOptions + opts: SettingsServiceInitOptions, ): Promise { return factory( cache, "settingsService", opts, - async () => new BrowserSettingsService(await stateServiceFactory(cache, opts)) + async () => new BrowserSettingsService(await stateServiceFactory(cache, opts)), ); } diff --git a/apps/browser/src/background/service-factories/vault-timeout-service.factory.ts b/apps/browser/src/background/service-factories/vault-timeout-service.factory.ts index b019db3297d..7a34b6b4b14 100644 --- a/apps/browser/src/background/service-factories/vault-timeout-service.factory.ts +++ b/apps/browser/src/background/service-factories/vault-timeout-service.factory.ts @@ -66,7 +66,7 @@ export type VaultTimeoutServiceInitOptions = VaultTimeoutServiceFactoryOptions & export function vaultTimeoutServiceFactory( cache: { vaultTimeoutService?: AbstractVaultTimeoutService } & CachedServices, - opts: VaultTimeoutServiceInitOptions + opts: VaultTimeoutServiceInitOptions, ): Promise { return factory( cache, @@ -85,7 +85,7 @@ export function vaultTimeoutServiceFactory( await authServiceFactory(cache, opts), await vaultTimeoutSettingsServiceFactory(cache, opts), opts.vaultTimeoutServiceOptions.lockedCallback, - opts.vaultTimeoutServiceOptions.loggedOutCallback - ) + opts.vaultTimeoutServiceOptions.loggedOutCallback, + ), ); } diff --git a/apps/browser/src/background/service-factories/vault-timeout-settings-service.factory.ts b/apps/browser/src/background/service-factories/vault-timeout-settings-service.factory.ts index eda86c0a156..b2dfd96f5b0 100644 --- a/apps/browser/src/background/service-factories/vault-timeout-settings-service.factory.ts +++ b/apps/browser/src/background/service-factories/vault-timeout-settings-service.factory.ts @@ -38,7 +38,7 @@ export type VaultTimeoutSettingsServiceInitOptions = VaultTimeoutSettingsService export function vaultTimeoutSettingsServiceFactory( cache: { vaultTimeoutSettingsService?: AbstractVaultTimeoutSettingsService } & CachedServices, - opts: VaultTimeoutSettingsServiceInitOptions + opts: VaultTimeoutSettingsServiceInitOptions, ): Promise { return factory( cache, @@ -50,7 +50,7 @@ export function vaultTimeoutSettingsServiceFactory( await tokenServiceFactory(cache, opts), await policyServiceFactory(cache, opts), await stateServiceFactory(cache, opts), - await userVerificationServiceFactory(cache, opts) - ) + await userVerificationServiceFactory(cache, opts), + ), ); } diff --git a/apps/browser/src/browser/safariApp.ts b/apps/browser/src/browser/safariApp.ts index 683c9ef08a0..f96a560e3f1 100644 --- a/apps/browser/src/browser/safariApp.ts +++ b/apps/browser/src/browser/safariApp.ts @@ -19,7 +19,7 @@ export class SafariApp { }, (response: any) => { resolve(response); - } + }, ); }); } diff --git a/apps/browser/src/manifest.json b/apps/browser/src/manifest.json index d5bef7f950e..2bd032495c4 100644 --- a/apps/browser/src/manifest.json +++ b/apps/browser/src/manifest.json @@ -2,7 +2,7 @@ "manifest_version": 2, "name": "__MSG_extName__", "short_name": "__MSG_appName__", - "version": "2023.10.2", + "version": "2023.12.0", "description": "__MSG_extDesc__", "default_locale": "en", "author": "Bitwarden Inc.", @@ -59,7 +59,7 @@ "webRequest", "webRequestBlocking" ], - "optional_permissions": ["nativeMessaging", "privacy"], + "optional_permissions": ["nativeMessaging"], "content_security_policy": "script-src 'self' 'wasm-unsafe-eval'; object-src 'self'", "sandbox": { "pages": ["overlay/button.html", "overlay/list.html"], diff --git a/apps/browser/src/manifest.v3.json b/apps/browser/src/manifest.v3.json index 27b7a418260..dba40744e75 100644 --- a/apps/browser/src/manifest.v3.json +++ b/apps/browser/src/manifest.v3.json @@ -3,7 +3,7 @@ "minimum_chrome_version": "102.0", "name": "__MSG_extName__", "short_name": "__MSG_appName__", - "version": "2023.10.2", + "version": "2023.12.0", "description": "__MSG_extDesc__", "default_locale": "en", "author": "Bitwarden Inc.", @@ -69,7 +69,7 @@ "alarms", "scripting" ], - "optional_permissions": ["nativeMessaging", "privacy"], + "optional_permissions": ["nativeMessaging"], "host_permissions": ["http://*/*", "https://*/*"], "content_security_policy": { "extension_pages": "script-src 'self' 'wasm-unsafe-eval'; object-src 'self'", diff --git a/apps/browser/src/platform/background.ts b/apps/browser/src/platform/background.ts index f7913dade9f..148ec8280b6 100644 --- a/apps/browser/src/platform/background.ts +++ b/apps/browser/src/platform/background.ts @@ -28,7 +28,7 @@ if (BrowserApi.manifestVersion === 3) { "runtime.background", (message: { command: string }, sender, sendResponse) => { runtimeMessageListener(message, sender); - } + }, ); } else { const bitwardenMain = ((window as any).bitwardenMain = new MainBackground()); diff --git a/apps/browser/src/platform/background/service-factories/api-service.factory.ts b/apps/browser/src/platform/background/service-factories/api-service.factory.ts index bcde07fbb20..649fe1f7fe6 100644 --- a/apps/browser/src/platform/background/service-factories/api-service.factory.ts +++ b/apps/browser/src/platform/background/service-factories/api-service.factory.ts @@ -36,7 +36,7 @@ export type ApiServiceInitOptions = ApiServiceFactoryOptions & export function apiServiceFactory( cache: { apiService?: AbstractApiService } & CachedServices, - opts: ApiServiceInitOptions + opts: ApiServiceInitOptions, ): Promise { return factory( cache, @@ -49,7 +49,7 @@ export function apiServiceFactory( await environmentServiceFactory(cache, opts), await appIdServiceFactory(cache, opts), opts.apiServiceOptions.logoutCallback, - opts.apiServiceOptions.customUserAgent - ) + opts.apiServiceOptions.customUserAgent, + ), ); } diff --git a/apps/browser/src/platform/background/service-factories/app-id-service.factory.ts b/apps/browser/src/platform/background/service-factories/app-id-service.factory.ts index 30397d737ef..af5012798b8 100644 --- a/apps/browser/src/platform/background/service-factories/app-id-service.factory.ts +++ b/apps/browser/src/platform/background/service-factories/app-id-service.factory.ts @@ -12,12 +12,12 @@ export type AppIdServiceInitOptions = AppIdServiceFactoryOptions & DiskStorageOp export function appIdServiceFactory( cache: { appIdService?: AbstractAppIdService } & CachedServices, - opts: AppIdServiceInitOptions + opts: AppIdServiceInitOptions, ): Promise { return factory( cache, "appIdService", opts, - async () => new AppIdService(await diskStorageServiceFactory(cache, opts)) + async () => new AppIdService(await diskStorageServiceFactory(cache, opts)), ); } diff --git a/apps/browser/src/platform/background/service-factories/config-api.service.factory.ts b/apps/browser/src/platform/background/service-factories/config-api.service.factory.ts index e9e1b86488a..c0dbf1f475d 100644 --- a/apps/browser/src/platform/background/service-factories/config-api.service.factory.ts +++ b/apps/browser/src/platform/background/service-factories/config-api.service.factory.ts @@ -17,7 +17,7 @@ export type ConfigApiServiceInitOptions = ConfigApiServiceFactoyOptions & export function configApiServiceFactory( cache: { configApiService?: ConfigApiServiceAbstraction } & CachedServices, - opts: ConfigApiServiceInitOptions + opts: ConfigApiServiceInitOptions, ): Promise { return factory( cache, @@ -26,7 +26,7 @@ export function configApiServiceFactory( async () => new ConfigApiService( await apiServiceFactory(cache, opts), - await authServiceFactory(cache, opts) - ) + await authServiceFactory(cache, opts), + ), ); } diff --git a/apps/browser/src/platform/background/service-factories/config-service.factory.ts b/apps/browser/src/platform/background/service-factories/config-service.factory.ts index a5dc6016c65..9c8b485c2a7 100644 --- a/apps/browser/src/platform/background/service-factories/config-service.factory.ts +++ b/apps/browser/src/platform/background/service-factories/config-service.factory.ts @@ -30,7 +30,7 @@ export type ConfigServiceInitOptions = ConfigServiceFactoryOptions & export function configServiceFactory( cache: { configService?: ConfigServiceAbstraction } & CachedServices, - opts: ConfigServiceInitOptions + opts: ConfigServiceInitOptions, ): Promise { return factory( cache, @@ -43,7 +43,7 @@ export function configServiceFactory( await authServiceFactory(cache, opts), await environmentServiceFactory(cache, opts), await logServiceFactory(cache, opts), - opts.configServiceOptions?.subscribe ?? true - ) + opts.configServiceOptions?.subscribe ?? true, + ), ); } diff --git a/apps/browser/src/platform/background/service-factories/crypto-function-service.factory.ts b/apps/browser/src/platform/background/service-factories/crypto-function-service.factory.ts index bcfffb6bd08..b9a9cc66483 100644 --- a/apps/browser/src/platform/background/service-factories/crypto-function-service.factory.ts +++ b/apps/browser/src/platform/background/service-factories/crypto-function-service.factory.ts @@ -13,12 +13,12 @@ export type CryptoFunctionServiceInitOptions = CryptoFunctionServiceFactoryOptio export function cryptoFunctionServiceFactory( cache: { cryptoFunctionService?: CryptoFunctionService } & CachedServices, - opts: CryptoFunctionServiceFactoryOptions + opts: CryptoFunctionServiceFactoryOptions, ): Promise { return factory( cache, "cryptoFunctionService", opts, - () => new WebCryptoFunctionService(opts.cryptoFunctionServiceOptions.win) + () => new WebCryptoFunctionService(opts.cryptoFunctionServiceOptions.win), ); } diff --git a/apps/browser/src/platform/background/service-factories/crypto-service.factory.ts b/apps/browser/src/platform/background/service-factories/crypto-service.factory.ts index 7f66a4f6fe7..3922f3d435e 100644 --- a/apps/browser/src/platform/background/service-factories/crypto-service.factory.ts +++ b/apps/browser/src/platform/background/service-factories/crypto-service.factory.ts @@ -32,7 +32,7 @@ export type CryptoServiceInitOptions = CryptoServiceFactoryOptions & export function cryptoServiceFactory( cache: { cryptoService?: AbstractCryptoService } & CachedServices, - opts: CryptoServiceInitOptions + opts: CryptoServiceInitOptions, ): Promise { return factory( cache, @@ -44,7 +44,7 @@ export function cryptoServiceFactory( await encryptServiceFactory(cache, opts), await platformUtilsServiceFactory(cache, opts), await logServiceFactory(cache, opts), - await stateServiceFactory(cache, opts) - ) + await stateServiceFactory(cache, opts), + ), ); } diff --git a/apps/browser/src/platform/background/service-factories/encrypt-service.factory.ts b/apps/browser/src/platform/background/service-factories/encrypt-service.factory.ts index 75e8c1974e2..055e5235914 100644 --- a/apps/browser/src/platform/background/service-factories/encrypt-service.factory.ts +++ b/apps/browser/src/platform/background/service-factories/encrypt-service.factory.ts @@ -23,7 +23,7 @@ export type EncryptServiceInitOptions = EncryptServiceFactoryOptions & export function encryptServiceFactory( cache: { encryptService?: EncryptServiceImplementation } & CachedServices, - opts: EncryptServiceInitOptions + opts: EncryptServiceInitOptions, ): Promise { return factory( cache, @@ -33,7 +33,7 @@ export function encryptServiceFactory( new EncryptServiceImplementation( await cryptoFunctionServiceFactory(cache, opts), await logServiceFactory(cache, opts), - opts.encryptServiceOptions.logMacFailures - ) + opts.encryptServiceOptions.logMacFailures, + ), ); } diff --git a/apps/browser/src/platform/background/service-factories/environment-service.factory.ts b/apps/browser/src/platform/background/service-factories/environment-service.factory.ts index 5150c7bd4a0..4ea7dac225c 100644 --- a/apps/browser/src/platform/background/service-factories/environment-service.factory.ts +++ b/apps/browser/src/platform/background/service-factories/environment-service.factory.ts @@ -15,7 +15,7 @@ export type EnvironmentServiceInitOptions = EnvironmentServiceFactoryOptions & export function environmentServiceFactory( cache: { environmentService?: BrowserEnvironmentService } & CachedServices, - opts: EnvironmentServiceInitOptions + opts: EnvironmentServiceInitOptions, ): Promise { return factory( cache, @@ -24,7 +24,7 @@ export function environmentServiceFactory( async () => new BrowserEnvironmentService( await stateServiceFactory(cache, opts), - await logServiceFactory(cache, opts) - ) + await logServiceFactory(cache, opts), + ), ); } diff --git a/apps/browser/src/platform/background/service-factories/factory-options.ts b/apps/browser/src/platform/background/service-factories/factory-options.ts index 7dde09204ef..4219a67181e 100644 --- a/apps/browser/src/platform/background/service-factories/factory-options.ts +++ b/apps/browser/src/platform/background/service-factories/factory-options.ts @@ -9,12 +9,12 @@ export type FactoryOptions = { export async function factory< TCache extends CachedServices, TName extends keyof TCache, - TOpts extends FactoryOptions + TOpts extends FactoryOptions, >( cachedServices: TCache, name: TName, opts: TOpts, - factory: () => TCache[TName] | Promise + factory: () => TCache[TName] | Promise, ): Promise { let instance = cachedServices[name]; if (opts.alwaysInitializeNewService || !instance) { diff --git a/apps/browser/src/platform/background/service-factories/file-upload-service.factory.ts b/apps/browser/src/platform/background/service-factories/file-upload-service.factory.ts index d890a3b37a7..77b5703619b 100644 --- a/apps/browser/src/platform/background/service-factories/file-upload-service.factory.ts +++ b/apps/browser/src/platform/background/service-factories/file-upload-service.factory.ts @@ -15,12 +15,12 @@ export type FileUploadServiceInitOptions = FileUploadServiceFactoryOptions & Log export function fileUploadServiceFactory( cache: { fileUploadService?: FileUploadServiceAbstraction } & CachedServices, - opts: FileUploadServiceInitOptions + opts: FileUploadServiceInitOptions, ): Promise { return factory( cache, "fileUploadService", opts, - async () => new FileUploadService(await logServiceFactory(cache, opts)) + async () => new FileUploadService(await logServiceFactory(cache, opts)), ); } diff --git a/apps/browser/src/platform/background/service-factories/global-state-provider.factory.ts b/apps/browser/src/platform/background/service-factories/global-state-provider.factory.ts index d20b7a72ade..3615b2b060d 100644 --- a/apps/browser/src/platform/background/service-factories/global-state-provider.factory.ts +++ b/apps/browser/src/platform/background/service-factories/global-state-provider.factory.ts @@ -18,7 +18,7 @@ export type GlobalStateProviderInitOptions = GlobalStateProviderFactoryOptions & export async function globalStateProviderFactory( cache: { globalStateProvider?: GlobalStateProvider } & CachedServices, - opts: GlobalStateProviderInitOptions + opts: GlobalStateProviderInitOptions, ): Promise { return factory( cache, @@ -27,7 +27,7 @@ export async function globalStateProviderFactory( async () => new DefaultGlobalStateProvider( await observableMemoryStorageServiceFactory(cache, opts), - await observableDiskStorageServiceFactory(cache, opts) - ) + await observableDiskStorageServiceFactory(cache, opts), + ), ); } diff --git a/apps/browser/src/platform/background/service-factories/i18n-service.factory.ts b/apps/browser/src/platform/background/service-factories/i18n-service.factory.ts index 3dd7e1814ff..86ec82784b1 100644 --- a/apps/browser/src/platform/background/service-factories/i18n-service.factory.ts +++ b/apps/browser/src/platform/background/service-factories/i18n-service.factory.ts @@ -15,13 +15,13 @@ export type I18nServiceInitOptions = I18nServiceFactoryOptions; export async function i18nServiceFactory( cache: { i18nService?: AbstractI18nService } & CachedServices, - opts: I18nServiceInitOptions + opts: I18nServiceInitOptions, ): Promise { const service = await factory( cache, "i18nService", opts, - () => new I18nService(opts.i18nServiceOptions.systemLanguage) + () => new I18nService(opts.i18nServiceOptions.systemLanguage), ); if (!(service as BaseI18nService as any).inited) { await (service as BaseI18nService).init(); diff --git a/apps/browser/src/platform/background/service-factories/key-generation-service.factory.ts b/apps/browser/src/platform/background/service-factories/key-generation-service.factory.ts index 7dbcf3fa79e..d4f6d52738f 100644 --- a/apps/browser/src/platform/background/service-factories/key-generation-service.factory.ts +++ b/apps/browser/src/platform/background/service-factories/key-generation-service.factory.ts @@ -13,12 +13,12 @@ export type KeyGenerationServiceInitOptions = KeyGenerationServiceFactoryOptions export function keyGenerationServiceFactory( cache: { keyGenerationService?: KeyGenerationService } & CachedServices, - opts: KeyGenerationServiceInitOptions + opts: KeyGenerationServiceInitOptions, ): Promise { return factory( cache, "keyGenerationService", opts, - async () => new KeyGenerationService(await cryptoFunctionServiceFactory(cache, opts)) + async () => new KeyGenerationService(await cryptoFunctionServiceFactory(cache, opts)), ); } diff --git a/apps/browser/src/platform/background/service-factories/log-service.factory.ts b/apps/browser/src/platform/background/service-factories/log-service.factory.ts index 4dd5f7dd782..7e171ee3104 100644 --- a/apps/browser/src/platform/background/service-factories/log-service.factory.ts +++ b/apps/browser/src/platform/background/service-factories/log-service.factory.ts @@ -15,12 +15,12 @@ export type LogServiceInitOptions = LogServiceFactoryOptions; export function logServiceFactory( cache: { logService?: LogService } & CachedServices, - opts: LogServiceInitOptions + opts: LogServiceInitOptions, ): Promise { return factory( cache, "logService", opts, - () => new ConsoleLogService(opts.logServiceOptions.isDev, opts.logServiceOptions.filter) + () => new ConsoleLogService(opts.logServiceOptions.isDev, opts.logServiceOptions.filter), ); } diff --git a/apps/browser/src/platform/background/service-factories/messaging-service.factory.ts b/apps/browser/src/platform/background/service-factories/messaging-service.factory.ts index 0d0c797056e..46852712aa8 100644 --- a/apps/browser/src/platform/background/service-factories/messaging-service.factory.ts +++ b/apps/browser/src/platform/background/service-factories/messaging-service.factory.ts @@ -13,7 +13,7 @@ export type MessagingServiceInitOptions = MessagingServiceFactoryOptions; export function messagingServiceFactory( cache: { messagingService?: AbstractMessagingService } & CachedServices, - opts: MessagingServiceInitOptions + opts: MessagingServiceInitOptions, ): Promise { return factory(cache, "messagingService", opts, () => new BrowserMessagingService()); } diff --git a/apps/browser/src/platform/background/service-factories/platform-utils-service.factory.ts b/apps/browser/src/platform/background/service-factories/platform-utils-service.factory.ts index 5748c523f70..d3ac465e4c9 100644 --- a/apps/browser/src/platform/background/service-factories/platform-utils-service.factory.ts +++ b/apps/browser/src/platform/background/service-factories/platform-utils-service.factory.ts @@ -18,7 +18,7 @@ export type PlatformUtilsServiceInitOptions = PlatformUtilsServiceFactoryOptions export function platformUtilsServiceFactory( cache: { platformUtilsService?: PlatformUtilsService } & CachedServices, - opts: PlatformUtilsServiceInitOptions + opts: PlatformUtilsServiceInitOptions, ): Promise { return factory( cache, @@ -29,7 +29,7 @@ export function platformUtilsServiceFactory( await messagingServiceFactory(cache, opts), opts.platformUtilsServiceOptions.clipboardWriteCallback, opts.platformUtilsServiceOptions.biometricCallback, - opts.platformUtilsServiceOptions.win - ) + opts.platformUtilsServiceOptions.win, + ), ); } diff --git a/apps/browser/src/platform/background/service-factories/state-service.factory.ts b/apps/browser/src/platform/background/service-factories/state-service.factory.ts index 31a0316c09a..3fc5aedefb7 100644 --- a/apps/browser/src/platform/background/service-factories/state-service.factory.ts +++ b/apps/browser/src/platform/background/service-factories/state-service.factory.ts @@ -35,7 +35,7 @@ export type StateServiceInitOptions = StateServiceFactoryOptions & export async function stateServiceFactory( cache: { stateService?: BrowserStateService } & CachedServices, - opts: StateServiceInitOptions + opts: StateServiceInitOptions, ): Promise { const service = await factory( cache, @@ -49,8 +49,8 @@ export async function stateServiceFactory( await logServiceFactory(cache, opts), opts.stateServiceOptions.stateFactory, await accountServiceFactory(cache, opts), - opts.stateServiceOptions.useAccountCache - ) + opts.stateServiceOptions.useAccountCache, + ), ); service.init(); return service; diff --git a/apps/browser/src/platform/background/service-factories/storage-service.factory.ts b/apps/browser/src/platform/background/service-factories/storage-service.factory.ts index b83ec742782..226025c1f61 100644 --- a/apps/browser/src/platform/background/service-factories/storage-service.factory.ts +++ b/apps/browser/src/platform/background/service-factories/storage-service.factory.ts @@ -27,7 +27,7 @@ export type MemoryStorageServiceInitOptions = StorageServiceFactoryOptions & export function diskStorageServiceFactory( cache: { diskStorageService?: AbstractStorageService } & CachedServices, - opts: DiskStorageServiceInitOptions + opts: DiskStorageServiceInitOptions, ): Promise { return factory(cache, "diskStorageService", opts, () => new BrowserLocalStorageService()); } @@ -35,27 +35,27 @@ export function observableDiskStorageServiceFactory( cache: { diskStorageService?: AbstractStorageService & ObservableStorageService; } & CachedServices, - opts: DiskStorageServiceInitOptions + opts: DiskStorageServiceInitOptions, ): Promise { return factory(cache, "diskStorageService", opts, () => new BrowserLocalStorageService()); } export function secureStorageServiceFactory( cache: { secureStorageService?: AbstractStorageService } & CachedServices, - opts: SecureStorageServiceInitOptions + opts: SecureStorageServiceInitOptions, ): Promise { return factory(cache, "secureStorageService", opts, () => new BrowserLocalStorageService()); } export function memoryStorageServiceFactory( cache: { memoryStorageService?: AbstractMemoryStorageService } & CachedServices, - opts: MemoryStorageServiceInitOptions + opts: MemoryStorageServiceInitOptions, ): Promise { return factory(cache, "memoryStorageService", opts, async () => { if (BrowserApi.manifestVersion === 3) { return new LocalBackedSessionStorageService( await encryptServiceFactory(cache, opts), - await keyGenerationServiceFactory(cache, opts) + await keyGenerationServiceFactory(cache, opts), ); } return new MemoryStorageService(); @@ -66,7 +66,7 @@ export function observableMemoryStorageServiceFactory( cache: { memoryStorageService?: AbstractMemoryStorageService & ObservableStorageService; } & CachedServices, - opts: MemoryStorageServiceInitOptions + opts: MemoryStorageServiceInitOptions, ): Promise { return factory(cache, "memoryStorageService", opts, async () => { return new BackgroundMemoryStorageService(); diff --git a/apps/browser/src/platform/browser/browser-api.spec.ts b/apps/browser/src/platform/browser/browser-api.spec.ts index 057ae0a1010..887c1284a01 100644 --- a/apps/browser/src/platform/browser/browser-api.spec.ts +++ b/apps/browser/src/platform/browser/browser-api.spec.ts @@ -24,7 +24,7 @@ describe("BrowserApi", () => { expect(chrome.windows.get).toHaveBeenCalledWith( windowId, { populate: true }, - expect.anything() + expect.anything(), ); }); }); @@ -46,7 +46,7 @@ describe("BrowserApi", () => { expect(chrome.windows.get).toHaveBeenCalledWith( windowId, { populate: true }, - expect.anything() + expect.anything(), ); }); }); @@ -73,7 +73,7 @@ describe("BrowserApi", () => { expect(chrome.windows.update).toHaveBeenCalledWith( windowId, windowOptions, - expect.anything() + expect.anything(), ); }); }); @@ -87,7 +87,7 @@ describe("BrowserApi", () => { expect(chrome.windows.update).toHaveBeenCalledWith( windowId, { focused: true }, - expect.anything() + expect.anything(), ); }); }); @@ -98,7 +98,7 @@ describe("BrowserApi", () => { const injectDetails = mock(); jest.spyOn(BrowserApi, "manifestVersion", "get").mockReturnValue(2); (chrome.tabs.executeScript as jest.Mock).mockImplementation( - (tabId, injectDetails, callback) => callback(executeScriptResult) + (tabId, injectDetails, callback) => callback(executeScriptResult), ); const result = await BrowserApi.executeScriptInTab(tabId, injectDetails); @@ -106,7 +106,7 @@ describe("BrowserApi", () => { expect(chrome.tabs.executeScript).toHaveBeenCalledWith( tabId, injectDetails, - expect.any(Function) + expect.any(Function), ); expect(result).toEqual(executeScriptResult); }); diff --git a/apps/browser/src/platform/browser/browser-api.ts b/apps/browser/src/platform/browser/browser-api.ts index 19815b1cf53..aaab7e113ab 100644 --- a/apps/browser/src/platform/browser/browser-api.ts +++ b/apps/browser/src/platform/browser/browser-api.ts @@ -52,7 +52,7 @@ export class BrowserApi { return new Promise((resolve) => chrome.windows.create(options, (window) => { resolve(window); - }) + }), ); } @@ -73,12 +73,12 @@ export class BrowserApi { */ static async updateWindowProperties( windowId: number, - options: chrome.windows.UpdateInfo + options: chrome.windows.UpdateInfo, ): Promise { return new Promise((resolve) => chrome.windows.update(windowId, options, () => { resolve(); - }) + }), ); } @@ -110,7 +110,7 @@ export class BrowserApi { return new Promise((resolve) => chrome.tabs.get(tabId, (tab) => { resolve(tab); - }) + }), ); } @@ -147,7 +147,7 @@ export class BrowserApi { static tabSendMessageData( tab: chrome.tabs.Tab, command: string, - data: any = null + data: any = null, ): Promise { const obj: any = { command: command, @@ -163,7 +163,7 @@ export class BrowserApi { static async tabSendMessage( tab: chrome.tabs.Tab, obj: T, - options: chrome.tabs.MessageSendOptions = null + options: chrome.tabs.MessageSendOptions = null, ): Promise { if (!tab || !tab.id) { return; @@ -183,7 +183,7 @@ export class BrowserApi { tabId: number, message: TabMessage, options?: chrome.tabs.MessageSendOptions, - responseCallback?: (response: T) => void + responseCallback?: (response: T) => void, ) { chrome.tabs.sendMessage(tabId, message, options, responseCallback); } @@ -217,7 +217,7 @@ export class BrowserApi { static createNewTab(url: string, active = true): Promise { return new Promise((resolve) => - chrome.tabs.create({ url: url, active: active }, (tab) => resolve(tab)) + chrome.tabs.create({ url: url, active: active }, (tab) => resolve(tab)), ); } @@ -225,7 +225,7 @@ export class BrowserApi { // them when the popup gets unloaded, otherwise we cause a memory leak private static trackedChromeEventListeners: [ event: chrome.events.Event<(...args: unknown[]) => unknown>, - callback: (...args: unknown[]) => unknown + callback: (...args: unknown[]) => unknown, ][] = []; static messageListener( @@ -233,8 +233,8 @@ export class BrowserApi { callback: ( message: any, sender: chrome.runtime.MessageSender, - sendResponse: any - ) => boolean | void + sendResponse: any, + ) => boolean | void, ) { BrowserApi.addListener(chrome.runtime.onMessage, callback); } @@ -252,7 +252,7 @@ export class BrowserApi { } static storageChangeListener( - callback: Parameters[0] + callback: Parameters[0], ) { BrowserApi.addListener(chrome.storage.onChanged, callback); } @@ -268,7 +268,7 @@ export class BrowserApi { */ static addListener unknown>( event: chrome.events.Event, - callback: T + callback: T, ) { event.addListener(callback); @@ -285,7 +285,7 @@ export class BrowserApi { */ static removeListener unknown>( event: chrome.events.Event, - callback: T + callback: T, ) { event.removeListener(callback); @@ -315,6 +315,11 @@ export class BrowserApi { return chrome.runtime.sendMessage(message); } + static sendMessageWithResponse(subscriber: string, arg: any = {}) { + const message = Object.assign({}, { command: subscriber }, arg); + return new Promise((resolve) => chrome.runtime.sendMessage(message, resolve)); + } + static async focusTab(tabId: number) { chrome.tabs.update(tabId, { active: true, highlighted: true }); } @@ -381,7 +386,7 @@ export class BrowserApi { */ static async permissionsGranted(permissions: string[]): Promise { return new Promise((resolve) => - chrome.permissions.contains({ permissions }, (result) => resolve(result)) + chrome.permissions.contains({ permissions }, (result) => resolve(result)), ); } @@ -399,7 +404,7 @@ export class BrowserApi { } static getSidebarAction( - win: Window & typeof globalThis + win: Window & typeof globalThis, ): OperaSidebarAction | FirefoxSidebarAction | null { const deviceType = BrowserPlatformUtilsService.getDevice(win); if (deviceType !== DeviceType.FirefoxExtension && deviceType !== DeviceType.OperaExtension) { @@ -434,33 +439,4 @@ export class BrowserApi { }); }); } - - /** - * Identifies if the browser autofill settings are overridden by the extension. - */ - static async browserAutofillSettingsOverridden(): Promise { - const autofillAddressOverridden: boolean = await new Promise((resolve) => - chrome.privacy.services.autofillAddressEnabled.get({}, (details) => - resolve(details.levelOfControl === "controlled_by_this_extension" && !details.value) - ) - ); - - const autofillCreditCardOverridden: boolean = await new Promise((resolve) => - chrome.privacy.services.autofillCreditCardEnabled.get({}, (details) => - resolve(details.levelOfControl === "controlled_by_this_extension" && !details.value) - ) - ); - - return autofillAddressOverridden && autofillCreditCardOverridden; - } - - /** - * Updates the browser autofill settings to the given value. - * - * @param value - Determines whether to enable or disable the autofill settings. - */ - static async updateDefaultBrowserAutofillSettings(value: boolean) { - chrome.privacy.services.autofillAddressEnabled.set({ value }); - chrome.privacy.services.autofillCreditCardEnabled.set({ value }); - } } diff --git a/apps/browser/src/platform/browser/from-chrome-event.ts b/apps/browser/src/platform/browser/from-chrome-event.ts index 5217d8e7021..e45dcdcd082 100644 --- a/apps/browser/src/platform/browser/from-chrome-event.ts +++ b/apps/browser/src/platform/browser/from-chrome-event.ts @@ -21,7 +21,7 @@ import { BrowserApi } from "./browser-api"; * ``` */ export function fromChromeEvent( - event: chrome.events.Event<(...args: T) => void> + event: chrome.events.Event<(...args: T) => void>, ): Observable { return new Observable((subscriber) => { const handler = (...args: T) => { diff --git a/apps/browser/src/platform/browser/zoned-message-listener.service.ts b/apps/browser/src/platform/browser/zoned-message-listener.service.ts index 4f8d740d231..ce9f7e2021e 100644 --- a/apps/browser/src/platform/browser/zoned-message-listener.service.ts +++ b/apps/browser/src/platform/browser/zoned-message-listener.service.ts @@ -19,8 +19,8 @@ export class ZonedMessageListenerService { callback: ( message: any, sender: chrome.runtime.MessageSender, - sendResponse: any - ) => boolean | void + sendResponse: any, + ) => boolean | void, ) { BrowserApi.messageListener(name, (message, sender, sendResponse) => { return this.ngZone.run(() => callback(message, sender, sendResponse)); diff --git a/apps/browser/src/platform/decorators/dev-flag.decorator.ts b/apps/browser/src/platform/decorators/dev-flag.decorator.ts index 4a67d6239e6..a31cdbc9924 100644 --- a/apps/browser/src/platform/decorators/dev-flag.decorator.ts +++ b/apps/browser/src/platform/decorators/dev-flag.decorator.ts @@ -6,7 +6,7 @@ export function devFlag(flag: keyof DevFlags) { descriptor.value = function (...args: any[]) { if (!devFlagEnabled(flag)) { throw new Error( - `This method should not be called, it is protected by a disabled dev flag.` + `This method should not be called, it is protected by a disabled dev flag.`, ); } return originalMethod.apply(this, args); diff --git a/apps/browser/src/platform/decorators/session-sync-observable/browser-session.decorator.spec.ts b/apps/browser/src/platform/decorators/session-sync-observable/browser-session.decorator.spec.ts index 5ddc3f8e07e..4b0226d54e9 100644 --- a/apps/browser/src/platform/decorators/session-sync-observable/browser-session.decorator.spec.ts +++ b/apps/browser/src/platform/decorators/session-sync-observable/browser-session.decorator.spec.ts @@ -20,7 +20,7 @@ describe("browserSession decorator", () => { expect(() => { new TestClass(); }).toThrowError( - "Cannot decorate TestClass with browserSession, Browser's AbstractMemoryStorageService must be accessible through the observed classes parameters" + "Cannot decorate TestClass with browserSession, Browser's AbstractMemoryStorageService must be accessible through the observed classes parameters", ); }); diff --git a/apps/browser/src/platform/decorators/session-sync-observable/browser-session.decorator.ts b/apps/browser/src/platform/decorators/session-sync-observable/browser-session.decorator.ts index b325f6b9a56..47fb5d7e085 100644 --- a/apps/browser/src/platform/decorators/session-sync-observable/browser-session.decorator.ts +++ b/apps/browser/src/platform/decorators/session-sync-observable/browser-session.decorator.ts @@ -23,7 +23,7 @@ export function browserSession>(constructor: TCto // Require state service to be injected const storageService: AbstractMemoryStorageService = this.findStorageService( - [this as any].concat(args) + [this as any].concat(args), ); if (this.__syncedItemMetadata == null || !(this.__syncedItemMetadata instanceof Array)) { @@ -31,7 +31,7 @@ export function browserSession>(constructor: TCto } this.__sessionSyncers = this.__syncedItemMetadata.map((metadata) => - this.buildSyncer(metadata, storageService) + this.buildSyncer(metadata, storageService), ); } @@ -39,7 +39,7 @@ export function browserSession>(constructor: TCto const syncer = new SessionSyncer( (this as any)[metadata.propertyKey], storageSerice, - metadata + metadata, ); syncer.init(); return syncer; @@ -54,14 +54,15 @@ export function browserSession>(constructor: TCto const stateService = args.find( (arg) => - arg?.memoryStorageService != null && this.isMemoryStorageService(arg.memoryStorageService) + arg?.memoryStorageService != null && + this.isMemoryStorageService(arg.memoryStorageService), ); if (stateService) { return stateService.memoryStorageService; } throw new Error( - `Cannot decorate ${constructor.name} with browserSession, Browser's AbstractMemoryStorageService must be accessible through the observed classes parameters` + `Cannot decorate ${constructor.name} with browserSession, Browser's AbstractMemoryStorageService must be accessible through the observed classes parameters`, ); } diff --git a/apps/browser/src/platform/decorators/session-sync-observable/session-syncer.spec.ts b/apps/browser/src/platform/decorators/session-sync-observable/session-syncer.spec.ts index 96c5a4eea55..f163cf495a9 100644 --- a/apps/browser/src/platform/decorators/session-sync-observable/session-syncer.spec.ts +++ b/apps/browser/src/platform/decorators/session-sync-observable/session-syncer.spec.ts @@ -55,7 +55,7 @@ describe("session syncer", () => { sessionKey, initializeAs: "object", initializer: () => null, - }) + }), ).toBeDefined(); expect( new SessionSyncer(behaviorSubject, storageService, { @@ -63,7 +63,7 @@ describe("session syncer", () => { sessionKey, initializer: (s: any) => s, initializeAs: "object", - }) + }), ).toBeDefined(); }); it("should throw if neither ctor or initializer is provided", () => { diff --git a/apps/browser/src/platform/decorators/session-sync-observable/session-syncer.ts b/apps/browser/src/platform/decorators/session-sync-observable/session-syncer.ts index 120c4b8b58c..7f57e6431e2 100644 --- a/apps/browser/src/platform/decorators/session-sync-observable/session-syncer.ts +++ b/apps/browser/src/platform/decorators/session-sync-observable/session-syncer.ts @@ -17,7 +17,7 @@ export class SessionSyncer { constructor( private subject: Subject, private memoryStorageService: AbstractMemoryStorageService, - private metaData: SyncedItemMetadata + private metaData: SyncedItemMetadata, ) { if (!(subject instanceof Subject)) { throw new Error("subject must inherit from Subject"); @@ -66,7 +66,7 @@ export class SessionSyncer { return; } await this.updateSession(next); - }) + }), ) .subscribe(); } diff --git a/apps/browser/src/platform/globals.d.ts b/apps/browser/src/platform/globals.d.ts index 9c7be016b0b..9e53d898027 100644 --- a/apps/browser/src/platform/globals.d.ts +++ b/apps/browser/src/platform/globals.d.ts @@ -10,7 +10,7 @@ type OperaAddons = { installExtension: ( id: string, success_callback: () => void, - error_callback: (errorMessage: string) => void + error_callback: (errorMessage: string) => void, ) => void; }; @@ -49,7 +49,7 @@ type OperaSidebarAction = { path?: string | Record; tabId?: number; }, - callback?: () => void + callback?: () => void, ) => void; /** * @link https://dev.opera.com/extensions/sidebar-action-api/#method-setpanel @@ -84,7 +84,7 @@ type OperaSidebarAction = { */ getBadgeBackgroundColor: ( details: { tabId?: number }, - callback: (result: ColorArray) => void + callback: (result: ColorArray) => void, ) => void; /** * *Not supported on mac* diff --git a/apps/browser/src/platform/listeners/combine.ts b/apps/browser/src/platform/listeners/combine.ts index 91d2af7ba55..4737da2cbf7 100644 --- a/apps/browser/src/platform/listeners/combine.ts +++ b/apps/browser/src/platform/listeners/combine.ts @@ -4,7 +4,7 @@ type Listener = (...args: [...T, CachedServices]) => Promis export const combine = ( listeners: Listener[], - startingServices: CachedServices = {} + startingServices: CachedServices = {}, ) => { return async (...args: T) => { const cachedServices = { ...startingServices }; diff --git a/apps/browser/src/platform/listeners/on-command-listener.ts b/apps/browser/src/platform/listeners/on-command-listener.ts index 04f8b72dbfb..893136205a0 100644 --- a/apps/browser/src/platform/listeners/on-command-listener.ts +++ b/apps/browser/src/platform/listeners/on-command-listener.ts @@ -98,7 +98,7 @@ const doGeneratePasswordToClipboard = async (tab: chrome.tabs.Tab): Promise + serviceCache: Record, ) { await new UpdateBadge(self).run({ windowId, existingServices: serviceCache }); } static async tabsOnActivatedListener( activeInfo: chrome.tabs.TabActiveInfo, - serviceCache: Record + serviceCache: Record, ) { await new UpdateBadge(self).run({ tabId: activeInfo.tabId, @@ -63,7 +63,7 @@ export class UpdateBadge { static async tabsOnReplacedListener( addedTabId: number, removedTabId: number, - serviceCache: Record + serviceCache: Record, ) { await new UpdateBadge(self).run({ tabId: addedTabId, existingServices: serviceCache }); } @@ -72,7 +72,7 @@ export class UpdateBadge { tabId: number, changeInfo: chrome.tabs.TabChangeInfo, tab: chrome.tabs.Tab, - serviceCache: Record + serviceCache: Record, ) { await new UpdateBadge(self).run({ tabId, @@ -83,7 +83,7 @@ export class UpdateBadge { static async messageListener( message: { command: string; tabId: number }, - serviceCache: Record + serviceCache: Record, ) { if (!UpdateBadge.listenedToCommands.includes(message.command)) { return; @@ -229,7 +229,7 @@ export class UpdateBadge { if (this.isOperaSidebar(this.sidebarAction)) { await new Promise((resolve) => - (this.sidebarAction as OperaSidebarAction).setIcon(options, () => resolve()) + (this.sidebarAction as OperaSidebarAction).setIcon(options, () => resolve()), ); } else { await this.sidebarAction.setIcon(options); @@ -290,7 +290,7 @@ export class UpdateBadge { if (!self.bitwardenContainerService) { new ContainerService( serviceCache.cryptoService as CryptoService, - serviceCache.encryptService as EncryptService + serviceCache.encryptService as EncryptService, ).attachToGlobal(self); } @@ -300,7 +300,7 @@ export class UpdateBadge { } private isOperaSidebar( - action: OperaSidebarAction | FirefoxSidebarAction + action: OperaSidebarAction | FirefoxSidebarAction, ): action is OperaSidebarAction { return action != null && (action as OperaSidebarAction).setBadgeText != null; } diff --git a/apps/browser/src/platform/popup/browser-popup-utils.spec.ts b/apps/browser/src/platform/popup/browser-popup-utils.spec.ts index 7a9136ff9eb..d640a3bc23d 100644 --- a/apps/browser/src/platform/popup/browser-popup-utils.spec.ts +++ b/apps/browser/src/platform/popup/browser-popup-utils.spec.ts @@ -191,25 +191,18 @@ describe("BrowserPopupUtils", () => { }); }); - it("replaces any existing `uilocation=` query params within the passed extension url path to state the the uilocaiton is a popup", async () => { - const url = "popup/index.html#/tabs/vault?uilocation=sidebar"; + it("skips parsing the passed extension url path if the option to do that is set", () => { + const url = "popup/index.html?uilocation=popout#/tabs/vault"; jest.spyOn(BrowserPopupUtils as any, "isSingleActionPopoutOpen").mockResolvedValueOnce(false); + jest.spyOn(BrowserPopupUtils as any, "buildPopoutUrl"); - await BrowserPopupUtils.openPopout(url); + BrowserPopupUtils.openPopout(url); - expect(BrowserApi.createWindow).toHaveBeenCalledWith({ - type: "popup", - focused: true, - width: 380, - height: 630, - left: 85, - top: 190, - url: `chrome-extension://id/popup/index.html#/tabs/vault?uilocation=popout`, - }); + expect(BrowserPopupUtils["buildPopoutUrl"]).not.toHaveBeenCalled(); }); - it("appends the uilocation to the search params if an existing param is passed with the extension url path", async () => { - const url = "popup/index.html#/tabs/vault?existingParam=123"; + it("replaces any existing `uilocation=` query params within the passed extension url path to state the the uilocaiton is a popup", async () => { + const url = "popup/index.html?uilocation=sidebar#/tabs/vault"; jest.spyOn(BrowserPopupUtils as any, "isSingleActionPopoutOpen").mockResolvedValueOnce(false); await BrowserPopupUtils.openPopout(url); @@ -221,7 +214,7 @@ describe("BrowserPopupUtils", () => { height: 630, left: 85, top: 190, - url: `chrome-extension://id/${url}&uilocation=popout`, + url: `chrome-extension://id/popup/index.html?uilocation=popout#/tabs/vault`, }); }); @@ -383,7 +376,7 @@ describe("BrowserPopupUtils", () => { it("returns false if the popoutKey is not provided", async () => { await expect(BrowserPopupUtils["isSingleActionPopoutOpen"](undefined, {})).resolves.toBe( - false + false, ); }); @@ -391,7 +384,7 @@ describe("BrowserPopupUtils", () => { jest.spyOn(BrowserApi, "tabsQuery").mockResolvedValueOnce([]); await expect( - BrowserPopupUtils["isSingleActionPopoutOpen"]("123", windowOptions) + BrowserPopupUtils["isSingleActionPopoutOpen"]("123", windowOptions), ).resolves.toBe(false); }); @@ -412,7 +405,7 @@ describe("BrowserPopupUtils", () => { ]); await expect( - BrowserPopupUtils["isSingleActionPopoutOpen"]("789", windowOptions) + BrowserPopupUtils["isSingleActionPopoutOpen"]("789", windowOptions), ).resolves.toBe(false); }); @@ -433,7 +426,7 @@ describe("BrowserPopupUtils", () => { ]); await expect( - BrowserPopupUtils["isSingleActionPopoutOpen"]("123", windowOptions) + BrowserPopupUtils["isSingleActionPopoutOpen"]("123", windowOptions), ).resolves.toBe(true); expect(BrowserApi.updateWindowProperties).toHaveBeenCalledWith(2, { focused: true, diff --git a/apps/browser/src/platform/popup/browser-popup-utils.ts b/apps/browser/src/platform/popup/browser-popup-utils.ts index 3e687fa0815..8cc060969d4 100644 --- a/apps/browser/src/platform/popup/browser-popup-utils.ts +++ b/apps/browser/src/platform/popup/browser-popup-utils.ts @@ -67,7 +67,7 @@ class BrowserPopupUtils { options: ScrollOptions = { delay: 0, containerSelector: "main", - } + }, ) { const { delay, containerSelector } = options; return new Promise((resolve) => @@ -78,7 +78,7 @@ class BrowserPopupUtils { } resolve(); - }, delay) + }, delay), ); } @@ -109,7 +109,7 @@ class BrowserPopupUtils { singleActionKey?: string; forceCloseExistingWindows?: boolean; windowOptions?: Partial; - } = {} + } = {}, ) { const { senderWindowId, singleActionKey, forceCloseExistingWindows, windowOptions } = options; const defaultPopoutWindowOptions: chrome.windows.CreateData = { @@ -127,16 +127,14 @@ class BrowserPopupUtils { top: senderWindow.top + offsetTop, ...defaultPopoutWindowOptions, ...windowOptions, - url: chrome.runtime.getURL( - BrowserPopupUtils.buildPopoutUrlPath(extensionUrlPath, singleActionKey) - ), + url: BrowserPopupUtils.buildPopoutUrl(extensionUrlPath, singleActionKey), }; if ( (await BrowserPopupUtils.isSingleActionPopoutOpen( singleActionKey, popoutWindowOptions, - forceCloseExistingWindows + forceCloseExistingWindows, )) && !forceCloseExistingWindows ) { @@ -199,7 +197,7 @@ class BrowserPopupUtils { private static async isSingleActionPopoutOpen( popoutKey: string | undefined, windowInfo: chrome.windows.CreateData, - forceCloseExistingWindows = false + forceCloseExistingWindows = false, ) { if (!popoutKey) { return false; @@ -207,7 +205,7 @@ class BrowserPopupUtils { const extensionUrl = chrome.runtime.getURL("popup/index.html"); const popoutTabs = (await BrowserApi.tabsQuery({ url: `${extensionUrl}*` })).filter((tab) => - tab.url.includes(`singleActionPopout=${popoutKey}`) + tab.url.includes(`singleActionPopout=${popoutKey}`), ); if (popoutTabs.length === 0) { return false; @@ -240,7 +238,7 @@ class BrowserPopupUtils { private static urlContainsSearchParams( win: Window, searchParam: string, - searchValue: string + searchValue: string, ): boolean { return win.location.href.indexOf(`${searchParam}=${searchValue}`) > -1; } @@ -252,23 +250,15 @@ class BrowserPopupUtils { * @param extensionUrlPath - A relative path to the extension page. Example: "popup/index.html#/tabs/vault" * @param singleActionKey - The single action popout key used to identify the popout. */ - private static buildPopoutUrlPath(extensionUrlPath: string, singleActionKey: string) { - let formattedExtensionUrlPath = extensionUrlPath; - if (formattedExtensionUrlPath.includes("uilocation=")) { - formattedExtensionUrlPath = formattedExtensionUrlPath.replace( - /uilocation=[^&]*/g, - "uilocation=popout" - ); - } else { - formattedExtensionUrlPath += - (formattedExtensionUrlPath.includes("?") ? "&" : "?") + "uilocation=popout"; - } + private static buildPopoutUrl(extensionUrlPath: string, singleActionKey: string) { + const parsedUrl = new URL(chrome.runtime.getURL(extensionUrlPath)); + parsedUrl.searchParams.set("uilocation", "popout"); if (singleActionKey) { - formattedExtensionUrlPath += `&singleActionPopout=${singleActionKey}`; + parsedUrl.searchParams.set("singleActionPopout", singleActionKey); } - return formattedExtensionUrlPath; + return parsedUrl.toString(); } } diff --git a/apps/browser/src/platform/popup/header.component.ts b/apps/browser/src/platform/popup/header.component.ts index c32ee603458..a124c78f7b8 100644 --- a/apps/browser/src/platform/popup/header.component.ts +++ b/apps/browser/src/platform/popup/header.component.ts @@ -21,7 +21,7 @@ export class HeaderComponent { } return Object.values(accounts).some((a) => a.status !== AuthenticationStatus.LoggedOut); - }) + }), ); } } diff --git a/apps/browser/src/platform/services/abstractions/abstract-chrome-storage-api.service.ts b/apps/browser/src/platform/services/abstractions/abstract-chrome-storage-api.service.ts index 60ca9415e0e..22ce8d45649 100644 --- a/apps/browser/src/platform/services/abstractions/abstract-chrome-storage-api.service.ts +++ b/apps/browser/src/platform/services/abstractions/abstract-chrome-storage-api.service.ts @@ -32,7 +32,7 @@ export default abstract class AbstractChromeStorageService updateType: updateType, }; }); - }) + }), ); } diff --git a/apps/browser/src/platform/services/abstractions/browser-state.service.ts b/apps/browser/src/platform/services/abstractions/browser-state.service.ts index 30b1bb98635..88c2312762b 100644 --- a/apps/browser/src/platform/services/abstractions/browser-state.service.ts +++ b/apps/browser/src/platform/services/abstractions/browser-state.service.ts @@ -8,25 +8,25 @@ import { BrowserSendComponentState } from "../../../models/browserSendComponentS export abstract class BrowserStateService extends BaseStateServiceAbstraction { getBrowserGroupingComponentState: ( - options?: StorageOptions + options?: StorageOptions, ) => Promise; setBrowserGroupingComponentState: ( value: BrowserGroupingsComponentState, - options?: StorageOptions + options?: StorageOptions, ) => Promise; getBrowserVaultItemsComponentState: (options?: StorageOptions) => Promise; setBrowserVaultItemsComponentState: ( value: BrowserComponentState, - options?: StorageOptions + options?: StorageOptions, ) => Promise; getBrowserSendComponentState: (options?: StorageOptions) => Promise; setBrowserSendComponentState: ( value: BrowserSendComponentState, - options?: StorageOptions + options?: StorageOptions, ) => Promise; getBrowserSendTypeComponentState: (options?: StorageOptions) => Promise; setBrowserSendTypeComponentState: ( value: BrowserComponentState, - options?: StorageOptions + options?: StorageOptions, ) => Promise; } diff --git a/apps/browser/src/platform/services/browser-config.service.ts b/apps/browser/src/platform/services/browser-config.service.ts index 39d1fc565eb..557db4f33ce 100644 --- a/apps/browser/src/platform/services/browser-config.service.ts +++ b/apps/browser/src/platform/services/browser-config.service.ts @@ -21,7 +21,7 @@ export class BrowserConfigService extends ConfigService { authService: AuthService, environmentService: EnvironmentService, logService: LogService, - subscribe = false + subscribe = false, ) { super(stateService, configApiService, authService, environmentService, logService, subscribe); } diff --git a/apps/browser/src/platform/services/browser-crypto.service.ts b/apps/browser/src/platform/services/browser-crypto.service.ts index 0166fd439b0..54396206607 100644 --- a/apps/browser/src/platform/services/browser-crypto.service.ts +++ b/apps/browser/src/platform/services/browser-crypto.service.ts @@ -18,15 +18,18 @@ export class BrowserCryptoService extends CryptoService { * Browser doesn't store biometric keys, so we retrieve them from the desktop and return * if we successfully saved it into memory as the User Key */ - protected override async getKeyFromStorage(keySuffix: KeySuffixOptions): Promise { + protected override async getKeyFromStorage( + keySuffix: KeySuffixOptions, + userId?: string, + ): Promise { if (keySuffix === KeySuffixOptions.Biometric) { await this.platformUtilService.authenticateBiometric(); - const userKey = await this.stateService.getUserKey(); + const userKey = await this.stateService.getUserKey({ userId: userId }); if (userKey) { return new SymmetricCryptoKey(Utils.fromB64ToArray(userKey.keyB64)) as UserKey; } } - return await super.getKeyFromStorage(keySuffix); + return await super.getKeyFromStorage(keySuffix, userId); } } diff --git a/apps/browser/src/platform/services/browser-environment.service.ts b/apps/browser/src/platform/services/browser-environment.service.ts index 08a4d811d9d..db716baf4a3 100644 --- a/apps/browser/src/platform/services/browser-environment.service.ts +++ b/apps/browser/src/platform/services/browser-environment.service.ts @@ -6,7 +6,10 @@ import { GroupPolicyEnvironment } from "../../admin-console/types/group-policy-e import { devFlagEnabled, devFlagValue } from "../flags"; export class BrowserEnvironmentService extends EnvironmentService { - constructor(stateService: StateService, private logService: LogService) { + constructor( + stateService: StateService, + private logService: LogService, + ) { super(stateService); } diff --git a/apps/browser/src/platform/services/browser-file-download.service.ts b/apps/browser/src/platform/services/browser-file-download.service.ts index 1ade74367f5..ec1de5f3668 100644 --- a/apps/browser/src/platform/services/browser-file-download.service.ts +++ b/apps/browser/src/platform/services/browser-file-download.service.ts @@ -26,7 +26,7 @@ export class BrowserFileDownloadService implements FileDownloadService { blobOptions: request.blobOptions, fileName: request.fileName, }), - true + true, ); } else { const a = window.document.createElement("a"); diff --git a/apps/browser/src/platform/services/browser-i18n.service.ts b/apps/browser/src/platform/services/browser-i18n.service.ts index 03406c5b706..66821bc1582 100644 --- a/apps/browser/src/platform/services/browser-i18n.service.ts +++ b/apps/browser/src/platform/services/browser-i18n.service.ts @@ -11,7 +11,10 @@ export class BrowserI18nService extends I18nService { @sessionSync({ initializer: (s: string) => s }) protected _locale: ReplaySubject; - constructor(systemLanguage: string, private stateService: StateService) { + constructor( + systemLanguage: string, + private stateService: StateService, + ) { super(systemLanguage); } } diff --git a/apps/browser/src/platform/services/browser-platform-utils.service.ts b/apps/browser/src/platform/services/browser-platform-utils.service.ts index 018b1c623dc..a610afd733d 100644 --- a/apps/browser/src/platform/services/browser-platform-utils.service.ts +++ b/apps/browser/src/platform/services/browser-platform-utils.service.ts @@ -12,7 +12,7 @@ export default class BrowserPlatformUtilsService implements PlatformUtilsService private messagingService: MessagingService, private clipboardWriteCallback: (clipboardValue: string, clearMs: number) => void, private biometricCallback: () => Promise, - private win: Window & typeof globalThis + private win: Window & typeof globalThis, ) {} static getDevice(win: Window & typeof globalThis): DeviceType { @@ -197,7 +197,7 @@ export default class BrowserPlatformUtilsService implements PlatformUtilsService type: "error" | "success" | "warning" | "info", title: string, text: string | string[], - options?: any + options?: any, ): void { this.messagingService.send("showToast", { text: text, @@ -332,7 +332,7 @@ export default class BrowserPlatformUtilsService implements PlatformUtilsService autofillCommand = "Cmd+Shift+L"; } else if (this.isFirefox()) { autofillCommand = (await browser.commands.getAll()).find( - (c) => c.name === "autofill_login" + (c) => c.name === "autofill_login", ).shortcut; // Firefox is returning Ctrl instead of Cmd for the modifier key on macOS if // the command is the default one set on installation. @@ -345,8 +345,8 @@ export default class BrowserPlatformUtilsService implements PlatformUtilsService } else { await new Promise((resolve) => chrome.commands.getAll((c) => - resolve((autofillCommand = c.find((c) => c.name === "autofill_login").shortcut)) - ) + resolve((autofillCommand = c.find((c) => c.name === "autofill_login").shortcut)), + ), ); } return autofillCommand; diff --git a/apps/browser/src/platform/services/browser-state.service.spec.ts b/apps/browser/src/platform/services/browser-state.service.spec.ts index c63aae74036..08393ca0de3 100644 --- a/apps/browser/src/platform/services/browser-state.service.spec.ts +++ b/apps/browser/src/platform/services/browser-state.service.spec.ts @@ -66,7 +66,7 @@ describe("Browser State Service", () => { logService, stateFactory, accountService, - useAccountCache + useAccountCache, ); }); diff --git a/apps/browser/src/platform/services/browser-state.service.ts b/apps/browser/src/platform/services/browser-state.service.ts index ae5abb8a897..d5cba8fa9d3 100644 --- a/apps/browser/src/platform/services/browser-state.service.ts +++ b/apps/browser/src/platform/services/browser-state.service.ts @@ -44,7 +44,7 @@ export class BrowserStateService logService: LogService, stateFactory: StateFactory, accountService: AccountService, - useAccountCache = true + useAccountCache = true, ) { super( storageService, @@ -53,7 +53,7 @@ export class BrowserStateService logService, stateFactory, accountService, - useAccountCache + useAccountCache, ); // TODO: This is a hack to fix having a disk cache on both the popup and @@ -69,9 +69,31 @@ export class BrowserStateService } } }); + + BrowserApi.addListener( + chrome.runtime.onMessage, + (message: { command: string }, _, respond) => { + if (message.command === "initializeDiskCache") { + respond(JSON.stringify(this.accountDiskCache.value)); + } + }, + ); } } + override async initAccountState(): Promise { + if (this.isRecoveredSession && this.useAccountCache) { + // request cache initialization + + const response = await BrowserApi.sendMessageWithResponse("initializeDiskCache"); + this.accountDiskCache.next(JSON.parse(response)); + + return; + } + + await super.initAccountState(); + } + async addAccount(account: Account) { // Apply browser overrides to default account values account = new Account(account); @@ -88,7 +110,7 @@ export class BrowserStateService } async getBrowserGroupingComponentState( - options?: StorageOptions + options?: StorageOptions, ): Promise { return ( await this.getAccount(this.reconcileOptions(options, await this.defaultInMemoryOptions())) @@ -97,20 +119,20 @@ export class BrowserStateService async setBrowserGroupingComponentState( value: BrowserGroupingsComponentState, - options?: StorageOptions + options?: StorageOptions, ): Promise { const account = await this.getAccount( - this.reconcileOptions(options, await this.defaultInMemoryOptions()) + this.reconcileOptions(options, await this.defaultInMemoryOptions()), ); account.groupings = value; await this.saveAccount( account, - this.reconcileOptions(options, await this.defaultInMemoryOptions()) + this.reconcileOptions(options, await this.defaultInMemoryOptions()), ); } async getBrowserVaultItemsComponentState( - options?: StorageOptions + options?: StorageOptions, ): Promise { return ( await this.getAccount(this.reconcileOptions(options, await this.defaultInMemoryOptions())) @@ -119,15 +141,15 @@ export class BrowserStateService async setBrowserVaultItemsComponentState( value: BrowserComponentState, - options?: StorageOptions + options?: StorageOptions, ): Promise { const account = await this.getAccount( - this.reconcileOptions(options, await this.defaultInMemoryOptions()) + this.reconcileOptions(options, await this.defaultInMemoryOptions()), ); account.ciphers = value; await this.saveAccount( account, - this.reconcileOptions(options, await this.defaultInMemoryOptions()) + this.reconcileOptions(options, await this.defaultInMemoryOptions()), ); } @@ -139,15 +161,15 @@ export class BrowserStateService async setBrowserSendComponentState( value: BrowserSendComponentState, - options?: StorageOptions + options?: StorageOptions, ): Promise { const account = await this.getAccount( - this.reconcileOptions(options, await this.defaultInMemoryOptions()) + this.reconcileOptions(options, await this.defaultInMemoryOptions()), ); account.send = value; await this.saveAccount( account, - this.reconcileOptions(options, await this.defaultInMemoryOptions()) + this.reconcileOptions(options, await this.defaultInMemoryOptions()), ); } @@ -159,15 +181,15 @@ export class BrowserStateService async setBrowserSendTypeComponentState( value: BrowserComponentState, - options?: StorageOptions + options?: StorageOptions, ): Promise { const account = await this.getAccount( - this.reconcileOptions(options, await this.defaultInMemoryOptions()) + this.reconcileOptions(options, await this.defaultInMemoryOptions()), ); account.sendType = value; await this.saveAccount( account, - this.reconcileOptions(options, await this.defaultInMemoryOptions()) + this.reconcileOptions(options, await this.defaultInMemoryOptions()), ); } @@ -175,7 +197,7 @@ export class BrowserStateService // to delete the cache in the constructor above. protected override async saveAccountToDisk( account: Account, - options: StorageOptions + options: StorageOptions, ): Promise { const storageLocation = options.useSecureStorage ? this.secureStorageService diff --git a/apps/browser/src/platform/services/key-generation.service.ts b/apps/browser/src/platform/services/key-generation.service.ts index b2c76e1aee2..6b6f552c715 100644 --- a/apps/browser/src/platform/services/key-generation.service.ts +++ b/apps/browser/src/platform/services/key-generation.service.ts @@ -13,7 +13,7 @@ export class KeyGenerationService implements AbstractKeyGenerationService { "bitwarden-ephemeral", "ephemeral", 64, - "sha256" + "sha256", ); return new SymmetricCryptoKey(key); } diff --git a/apps/browser/src/platform/services/local-backed-session-storage.service.ts b/apps/browser/src/platform/services/local-backed-session-storage.service.ts index 8f6d519bcf1..d17a338f7fb 100644 --- a/apps/browser/src/platform/services/local-backed-session-storage.service.ts +++ b/apps/browser/src/platform/services/local-backed-session-storage.service.ts @@ -31,7 +31,7 @@ export class LocalBackedSessionStorageService extends AbstractMemoryStorageServi constructor( private encryptService: EncryptService, - private keyGenerationService: AbstractKeyGenerationService + private keyGenerationService: AbstractKeyGenerationService, ) { super(); this.updates$ = this.updatesSubject.asObservable(); diff --git a/apps/browser/src/platform/storage/background-memory-storage.service.ts b/apps/browser/src/platform/storage/background-memory-storage.service.ts index 14dadf225ee..9fb8cb71627 100644 --- a/apps/browser/src/platform/storage/background-memory-storage.service.ts +++ b/apps/browser/src/platform/storage/background-memory-storage.service.ts @@ -53,7 +53,7 @@ export class BackgroundMemoryStorageService extends MemoryStorageService { break; } case "save": - await this.save(message.key, JSON.parse(message.data as string) as unknown); + await this.save(message.key, JSON.parse((message.data as string) ?? null) as unknown); break; case "remove": await this.remove(message.key); diff --git a/apps/browser/src/platform/storage/foreground-memory-storage.service.ts b/apps/browser/src/platform/storage/foreground-memory-storage.service.ts index ea36c322082..1e5220002a8 100644 --- a/apps/browser/src/platform/storage/foreground-memory-storage.service.ts +++ b/apps/browser/src/platform/storage/foreground-memory-storage.service.ts @@ -29,14 +29,14 @@ export class ForegroundMemoryStorageService extends AbstractMemoryStorageService this._port = chrome.runtime.connect({ name: portName(chrome.storage.session) }); this._backgroundResponses$ = fromChromeEvent(this._port.onMessage).pipe( map(([message]) => message), - filter((message) => message.originator === "background") + filter((message) => message.originator === "background"), ); this._backgroundResponses$ .pipe( filter( - (message) => message.action === "subject_update" || message.action === "initialization" - ) + (message) => message.action === "subject_update" || message.action === "initialization", + ), ) .subscribe((message) => { switch (message.action) { @@ -71,15 +71,15 @@ export class ForegroundMemoryStorageService extends AbstractMemoryStorageService private async delegateToBackground( action: MemoryStoragePortMessage["action"], key: string, - data?: T + data?: T, ): Promise { const id = Utils.newGuid(); // listen for response before request const response = firstValueFrom( this._backgroundResponses$.pipe( filter((message) => message.id === id), - map((message) => JSON.parse(message.data as string) as T) - ) + map((message) => JSON.parse((message.data as string) ?? null) as T), + ), ); this.sendMessage({ diff --git a/apps/browser/src/platform/storage/memory-storage-service-interactions.spec.ts b/apps/browser/src/platform/storage/memory-storage-service-interactions.spec.ts index a09d733c6d3..0f604035247 100644 --- a/apps/browser/src/platform/storage/memory-storage-service-interactions.spec.ts +++ b/apps/browser/src/platform/storage/memory-storage-service-interactions.spec.ts @@ -1,3 +1,8 @@ +/** + * need to update test environment so structuredClone works appropriately + * @jest-environment ../../libs/shared/test.environment.ts + */ + import { trackEmissions } from "@bitwarden/common/../spec/utils"; import { BackgroundMemoryStorageService } from "./background-memory-storage.service"; @@ -29,7 +34,7 @@ describe("foreground background memory storage interaction", () => { const result = await foreground[action](key); expect(result).toEqual(value); - } + }, ); test("background should call save from foreground", async () => { diff --git a/apps/browser/src/popup/app-routing.animations.ts b/apps/browser/src/popup/app-routing.animations.ts index 42baf65c270..13403545fde 100644 --- a/apps/browser/src/popup/app-routing.animations.ts +++ b/apps/browser/src/popup/app-routing.animations.ts @@ -5,7 +5,7 @@ const queryShown = query( [style({ position: "fixed", width: "100%", height: "100%" })], { optional: true, - } + }, ); // ref: https://github.com/angular/angular/issues/15477 @@ -20,7 +20,7 @@ export function queryTranslate( axis: string, from: number, to: number, - zIndex = 1000 + zIndex = 1000, ) { return query( ":" + direction, @@ -32,7 +32,7 @@ export function queryTranslate( }), animate(speed + " ease-in-out", style({ transform: "translate" + axis + "(" + to + "%)" })), ], - { optional: true } + { optional: true }, ); } @@ -144,7 +144,7 @@ export const routerTransition = trigger("routerTransition", [ transition("view-cipher => edit-cipher, view-cipher => cipher-password-history", inSlideUp), transition( "edit-cipher => view-cipher, cipher-password-history => view-cipher, edit-cipher => tabs", - outSlideDown + outSlideDown, ), transition("view-cipher => clone-cipher", inSlideUp), @@ -161,11 +161,11 @@ export const routerTransition = trigger("routerTransition", [ transition( "add-cipher => generator, edit-cipher => generator, clone-cipher => generator", - inSlideUp + inSlideUp, ), transition( "generator => add-cipher, generator => edit-cipher, generator => clone-cipher", - outSlideDown + outSlideDown, ), transition("edit-cipher => attachments, edit-cipher => collections", inSlideLeft), @@ -214,4 +214,9 @@ export const routerTransition = trigger("routerTransition", [ transition("tabs => autofill", inSlideLeft), transition("autofill => tabs", outSlideRight), + + transition("* => account-switcher", inSlideUp), + transition("account-switcher => *", outSlideDown), + + transition("lock => *", outSlideDown), ]); diff --git a/apps/browser/src/popup/app-routing.module.ts b/apps/browser/src/popup/app-routing.module.ts index 33ac4558eca..f012dbe9c99 100644 --- a/apps/browser/src/popup/app-routing.module.ts +++ b/apps/browser/src/popup/app-routing.module.ts @@ -27,6 +27,7 @@ import { SsoComponent } from "../auth/popup/sso.component"; import { TwoFactorOptionsComponent } from "../auth/popup/two-factor-options.component"; import { TwoFactorComponent } from "../auth/popup/two-factor.component"; import { UpdateTempPasswordComponent } from "../auth/popup/update-temp-password.component"; +import { AutofillComponent } from "../autofill/popup/settings/autofill.component"; import { GeneratorComponent } from "../tools/popup/generator/generator.component"; import { PasswordGeneratorHistoryComponent } from "../tools/popup/generator/password-generator-history.component"; import { SendAddEditComponent } from "../tools/popup/send/send-add-edit.component"; @@ -46,8 +47,7 @@ import { VaultItemsComponent } from "../vault/popup/components/vault/vault-items import { ViewComponent } from "../vault/popup/components/vault/view.component"; import { FolderAddEditComponent } from "../vault/popup/settings/folder-add-edit.component"; -import { DebounceNavigationService } from "./services/debounceNavigationService"; -import { AutofillComponent } from "./settings/autofill.component"; +import { debounceNavigationGuard } from "./services/debounce-navigation.service"; import { ExcludedDomainsComponent } from "./settings/excluded-domains.component"; import { FoldersComponent } from "./settings/folders.component"; import { HelpAndFeedbackComponent } from "./settings/help-and-feedback.component"; @@ -183,14 +183,14 @@ const routes: Routes = [ { path: "add-cipher", component: AddEditComponent, - canActivate: [AuthGuard, DebounceNavigationService], + canActivate: [AuthGuard, debounceNavigationGuard()], data: { state: "add-cipher" }, runGuardsAndResolvers: "always", }, { path: "edit-cipher", component: AddEditComponent, - canActivate: [AuthGuard, DebounceNavigationService], + canActivate: [AuthGuard, debounceNavigationGuard()], data: { state: "edit-cipher" }, runGuardsAndResolvers: "always", }, @@ -366,7 +366,7 @@ const routes: Routes = [ { path: "account-switcher", component: AccountSwitcherComponent, - data: { state: "account-switcher" }, + data: { state: "account-switcher", doNotSaveUrl: true }, }, ]; diff --git a/apps/browser/src/popup/app.component.ts b/apps/browser/src/popup/app.component.ts index 9d91bef0ba3..22595f5a64e 100644 --- a/apps/browser/src/popup/app.component.ts +++ b/apps/browser/src/popup/app.component.ts @@ -52,7 +52,7 @@ export class AppComponent implements OnInit, OnDestroy { private sanitizer: DomSanitizer, private platformUtilsService: PlatformUtilsService, private dialogService: DialogService, - private browserMessagingApi: ZonedMessageListenerService + private browserMessagingApi: ZonedMessageListenerService, ) {} async ngOnInit() { @@ -70,7 +70,7 @@ export class AppComponent implements OnInit, OnDestroy { concatMap(async () => { await this.recordActivity(); }), - takeUntil(this.destroy$) + takeUntil(this.destroy$), ) .subscribe(); @@ -93,14 +93,15 @@ export class AppComponent implements OnInit, OnDestroy { }); } - if (this.activeUserId === null) { - this.router.navigate(["home"]); - } + this.router.navigate(["home"]); }); this.changeDetectorRef.detectChanges(); } else if (msg.command === "authBlocked") { this.router.navigate(["home"]); - } else if (msg.command === "locked" && msg.userId == null) { + } else if ( + msg.command === "locked" && + (msg.userId === null || msg.userId == this.activeUserId) + ) { this.router.navigate(["lock"]); } else if (msg.command === "showDialog") { this.showDialog(msg); @@ -117,12 +118,17 @@ export class AppComponent implements OnInit, OnDestroy { // Wait to make sure background has reloaded first. window.setTimeout( () => BrowserApi.reloadExtension(forceWindowReload ? window : null), - 2000 + 2000, ); } else if (msg.command === "reloadPopup") { this.router.navigate(["/"]); } else if (msg.command === "convertAccountToKeyConnector") { this.router.navigate(["/remove-password"]); + } else if (msg.command === "switchAccountFinish") { + // TODO: unset loading? + // this.loading = false; + } else if (msg.command == "update-temp-password") { + this.router.navigate(["/update-temp-password"]); } else { msg.webExtSender = sender; this.broadcasterService.send(msg); @@ -206,7 +212,7 @@ export class AppComponent implements OnInit, OnDestroy { } else { msg.text.forEach( (t: string) => - (message += "

" + this.sanitizer.sanitize(SecurityContext.HTML, t) + "

") + (message += "

" + this.sanitizer.sanitize(SecurityContext.HTML, t) + "

"), ); options.enableHtml = true; } diff --git a/apps/browser/src/popup/app.module.ts b/apps/browser/src/popup/app.module.ts index 3aff12b4e38..ae8fd394df9 100644 --- a/apps/browser/src/popup/app.module.ts +++ b/apps/browser/src/popup/app.module.ts @@ -18,6 +18,7 @@ import { ColorPasswordPipe } from "@bitwarden/angular/pipes/color-password.pipe" import { AvatarModule } from "@bitwarden/components"; import { AccountSwitcherComponent } from "../auth/popup/account-switching/account-switcher.component"; +import { AccountComponent } from "../auth/popup/account-switching/account.component"; import { CurrentAccountComponent } from "../auth/popup/account-switching/current-account.component"; import { SetPinComponent } from "../auth/popup/components/set-pin.component"; import { EnvironmentComponent } from "../auth/popup/environment.component"; @@ -34,6 +35,7 @@ import { SsoComponent } from "../auth/popup/sso.component"; import { TwoFactorOptionsComponent } from "../auth/popup/two-factor-options.component"; import { TwoFactorComponent } from "../auth/popup/two-factor.component"; import { UpdateTempPasswordComponent } from "../auth/popup/update-temp-password.component"; +import { AutofillComponent } from "../autofill/popup/settings/autofill.component"; import { HeaderComponent } from "../platform/popup/header.component"; import { FilePopoutCalloutComponent } from "../tools/popup/components/file-popout-callout.component"; import { GeneratorComponent } from "../tools/popup/generator/generator.component"; @@ -68,7 +70,6 @@ import { PopOutComponent } from "./components/pop-out.component"; import { PrivateModeWarningComponent } from "./components/private-mode-warning.component"; import { UserVerificationComponent } from "./components/user-verification.component"; import { ServicesModule } from "./services/services.module"; -import { AutofillComponent } from "./settings/autofill.component"; import { ExcludedDomainsComponent } from "./settings/excluded-domains.component"; import { FoldersComponent } from "./settings/folders.component"; import { HelpAndFeedbackComponent } from "./settings/help-and-feedback.component"; @@ -105,6 +106,7 @@ import "../platform/popup/locales"; DialogModule, FilePopoutCalloutComponent, AvatarModule, + AccountComponent, ], declarations: [ ActionButtonsComponent, diff --git a/apps/browser/src/popup/index.html b/apps/browser/src/popup/index.html index 17f50e0a0eb..e76a8c749fd 100644 --- a/apps/browser/src/popup/index.html +++ b/apps/browser/src/popup/index.html @@ -1,4 +1,4 @@ - + diff --git a/apps/browser/src/popup/scss/base.scss b/apps/browser/src/popup/scss/base.scss index 16cac3352a8..4ca486369b1 100644 --- a/apps/browser/src/popup/scss/base.scss +++ b/apps/browser/src/popup/scss/base.scss @@ -176,11 +176,10 @@ cdk-virtual-scroll-viewport::-webkit-scrollbar-thumb, } header:not(bit-callout header) { - max-height: 44px; + height: 44px; display: flex; &:not(.no-theme) { - min-height: 44px; border-bottom: 1px solid #000000; @include themify($themes) { @@ -231,7 +230,7 @@ header:not(bit-callout header) { } app-pop-out > button, - div > button, + div > button:not(app-current-account button):not(.home-acc-switcher-btn), div > a { border: none; padding: 0 10px; @@ -243,8 +242,8 @@ header:not(bit-callout header) { height: 100%; white-space: pre; - &:hover, - &:focus { + &:not(.home-acc-switcher-btn):hover, + &:not(.home-acc-switcher-btn):focus { @include themify($themes) { background-color: themed("headerBackgroundHoverColor"); color: themed("headerColor"); diff --git a/apps/browser/src/popup/scss/environment.scss b/apps/browser/src/popup/scss/environment.scss index f6adba86c2e..0fb8e0ddaba 100644 --- a/apps/browser/src/popup/scss/environment.scss +++ b/apps/browser/src/popup/scss/environment.scss @@ -65,7 +65,9 @@ html.browser_safari { } padding: 5px; width: 100%; - box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.14), 0 3px 1px -2px rgba(0, 0, 0, 0.12), + box-shadow: + 0 2px 2px 0 rgba(0, 0, 0, 0.14), + 0 3px 1px -2px rgba(0, 0, 0, 0.12), 0 1px 5px 0 rgba(0, 0, 0, 0.2); border-radius: $border-radius; } diff --git a/apps/browser/src/popup/services/debounceNavigationService.ts b/apps/browser/src/popup/services/debounce-navigation.service.ts similarity index 59% rename from apps/browser/src/popup/services/debounceNavigationService.ts rename to apps/browser/src/popup/services/debounce-navigation.service.ts index bf1e2e83ca5..b40738f0a80 100644 --- a/apps/browser/src/popup/services/debounceNavigationService.ts +++ b/apps/browser/src/popup/services/debounce-navigation.service.ts @@ -1,10 +1,24 @@ -import { Injectable, OnDestroy } from "@angular/core"; -import { CanActivate, NavigationEnd, NavigationStart, Router } from "@angular/router"; +import { inject, Injectable, OnDestroy } from "@angular/core"; +import { CanActivateFn, NavigationEnd, NavigationStart, Router } from "@angular/router"; import { Subscription } from "rxjs"; import { filter, pairwise } from "rxjs/operators"; +/** + * CanActivate guard that cancels duplicate navigation events, which can otherwise reinitialize some components + * unexpectedly. + * Specifically, this is used to avoid data loss when navigating from the password generator component back to the + * add/edit cipher component in browser. + * For more information, see https://github.com/bitwarden/clients/pull/1935 + */ +export function debounceNavigationGuard(): CanActivateFn { + return async () => { + const debounceNavigationService = inject(DebounceNavigationService); + return debounceNavigationService.canActivate(); + }; +} + @Injectable() -export class DebounceNavigationService implements CanActivate, OnDestroy { +export class DebounceNavigationService implements OnDestroy { navigationStartSub: Subscription; navigationSuccessSub: Subscription; @@ -16,11 +30,11 @@ export class DebounceNavigationService implements CanActivate, OnDestroy { this.navigationStartSub = this.router.events .pipe( filter((event) => event instanceof NavigationStart), - pairwise() + pairwise(), ) .subscribe( (events: [NavigationStart, NavigationStart]) => - ([this.lastNavigation, this.thisNavigation] = events) + ([this.lastNavigation, this.thisNavigation] = events), ); this.navigationSuccessSub = this.router.events diff --git a/apps/browser/src/popup/services/init.service.ts b/apps/browser/src/popup/services/init.service.ts index 78fbdfaa9f1..005664bdd20 100644 --- a/apps/browser/src/popup/services/init.service.ts +++ b/apps/browser/src/popup/services/init.service.ts @@ -17,7 +17,7 @@ export class InitService { private stateService: StateServiceAbstraction, private logService: LogServiceAbstraction, private themingService: AbstractThemingService, - private configService: ConfigService + private configService: ConfigService, ) {} init() { diff --git a/apps/browser/src/popup/services/popup-close-warning.service.ts b/apps/browser/src/popup/services/popup-close-warning.service.ts index 536def47d5d..01e91ecad70 100644 --- a/apps/browser/src/popup/services/popup-close-warning.service.ts +++ b/apps/browser/src/popup/services/popup-close-warning.service.ts @@ -26,7 +26,7 @@ export class PopupCloseWarningService { // Older methods with better support e.returnValue = confirmationMessage; return confirmationMessage; - } + }, ); } diff --git a/apps/browser/src/popup/services/popup-search.service.ts b/apps/browser/src/popup/services/popup-search.service.ts index 43fbed055e8..7eea1265a23 100644 --- a/apps/browser/src/popup/services/popup-search.service.ts +++ b/apps/browser/src/popup/services/popup-search.service.ts @@ -6,7 +6,7 @@ export class PopupSearchService extends SearchService { constructor( private mainSearchService: SearchService, consoleLogService: ConsoleLogService, - i18nService: I18nService + i18nService: I18nService, ) { super(consoleLogService, i18nService); } diff --git a/apps/browser/src/popup/services/services.module.ts b/apps/browser/src/popup/services/services.module.ts index 2cd0166f946..dc223488280 100644 --- a/apps/browser/src/popup/services/services.module.ts +++ b/apps/browser/src/popup/services/services.module.ts @@ -116,7 +116,7 @@ import { FilePopoutUtilsService } from "../../tools/popup/services/file-popout-u import { BrowserFolderService } from "../../vault/services/browser-folder.service"; import { VaultFilterService } from "../../vault/services/vault-filter.service"; -import { DebounceNavigationService } from "./debounceNavigationService"; +import { DebounceNavigationService } from "./debounce-navigation.service"; import { InitService } from "./init.service"; import { PopupCloseWarningService } from "./popup-close-warning.service"; import { PopupSearchService } from "./popup-search.service"; @@ -183,7 +183,7 @@ function getBgService(service: keyof MainBackground) { return new PopupSearchService( getBgService("searchService")(), logService, - i18nService + i18nService, ); }, deps: [LogServiceAbstraction, I18nServiceAbstraction], @@ -210,7 +210,7 @@ function getBgService(service: keyof MainBackground) { cryptoService: CryptoService, i18nService: I18nServiceAbstraction, cipherService: CipherService, - stateService: StateServiceAbstraction + stateService: StateServiceAbstraction, ) => { return new BrowserFolderService(cryptoService, i18nService, cipherService, stateService); }, @@ -293,7 +293,7 @@ function getBgService(service: keyof MainBackground) { provide: PolicyService, useFactory: ( stateService: StateServiceAbstraction, - organizationService: OrganizationService + organizationService: OrganizationService, ) => { return new BrowserPolicyService(stateService, organizationService); }, @@ -304,7 +304,7 @@ function getBgService(service: keyof MainBackground) { useFactory: ( policyService: InternalPolicyService, apiService: ApiService, - stateService: StateService + stateService: StateService, ) => { return new PolicyApiService(policyService, apiService, stateService); }, @@ -332,13 +332,13 @@ function getBgService(service: keyof MainBackground) { cryptoService: CryptoService, i18nService: I18nServiceAbstraction, cryptoFunctionService: CryptoFunctionService, - stateServiceAbstraction: StateServiceAbstraction + stateServiceAbstraction: StateServiceAbstraction, ) => { return new BrowserSendService( cryptoService, i18nService, cryptoFunctionService, - stateServiceAbstraction + stateServiceAbstraction, ); }, deps: [CryptoService, I18nServiceAbstraction, CryptoFunctionService, StateServiceAbstraction], @@ -352,7 +352,7 @@ function getBgService(service: keyof MainBackground) { useFactory: ( apiService: ApiService, fileUploadService: FileUploadService, - sendService: InternalSendServiceAbstraction + sendService: InternalSendServiceAbstraction, ) => { return new SendApiService(apiService, fileUploadService, sendService); }, @@ -430,7 +430,7 @@ function getBgService(service: keyof MainBackground) { stateService: StateServiceAbstraction, organizationService: OrganizationService, folderService: FolderService, - policyService: PolicyService + policyService: PolicyService, ) => { return new VaultFilterService( stateService, @@ -438,7 +438,7 @@ function getBgService(service: keyof MainBackground) { folderService, getBgService("cipherService")(), getBgService("collectionService")(), - policyService + policyService, ); }, deps: [StateServiceAbstraction, OrganizationService, FolderService, PolicyService], @@ -472,7 +472,7 @@ function getBgService(service: keyof MainBackground) { secureStorageService: AbstractStorageService, memoryStorageService: AbstractMemoryStorageService, logService: LogServiceAbstraction, - accountService: AccountServiceAbstraction + accountService: AccountServiceAbstraction, ) => { return new BrowserStateService( storageService, @@ -480,7 +480,7 @@ function getBgService(service: keyof MainBackground) { memoryStorageService, logService, new StateFactory(GlobalState, Account), - accountService + accountService, ); }, deps: [ @@ -514,14 +514,14 @@ function getBgService(service: keyof MainBackground) { provide: AbstractThemingService, useFactory: ( stateService: StateServiceAbstraction, - platformUtilsService: PlatformUtilsService + platformUtilsService: PlatformUtilsService, ) => { return new ThemingService( stateService, // Safari doesn't properly handle the (prefers-color-scheme) media query in the popup window, it always returns light. // In Safari we have to use the background page instead, which comes with limitations like not dynamically changing the extension theme when the system theme is changed. platformUtilsService.isSafari() ? getBgService("backgroundWindow")() : window, - document + document, ); }, deps: [StateServiceAbstraction, PlatformUtilsService], diff --git a/apps/browser/src/popup/settings/about.component.html b/apps/browser/src/popup/settings/about.component.html index b68f592492f..b4086178340 100644 --- a/apps/browser/src/popup/settings/about.component.html +++ b/apps/browser/src/popup/settings/about.component.html @@ -10,7 +10,7 @@

{{ "serverVersion" | i18n }}: {{ this.serverConfig?.version }} - ({{ "lastSeenOn" | i18n : (serverConfig.utcDate | date : "mediumDate") }}) + ({{ "lastSeenOn" | i18n: (serverConfig.utcDate | date: "mediumDate") }})

@@ -20,11 +20,11 @@ {{ "serverVersion" | i18n }} ({{ "thirdParty" | i18n }}): {{ this.serverConfig?.version }} - ({{ "lastSeenOn" | i18n : (serverConfig.utcDate | date : "mediumDate") }}) + ({{ "lastSeenOn" | i18n: (serverConfig.utcDate | date: "mediumDate") }})

- {{ "thirdPartyServerMessage" | i18n : serverConfig.server?.name }} + {{ "thirdPartyServerMessage" | i18n: serverConfig.server?.name }}
@@ -32,7 +32,7 @@ {{ "serverVersion" | i18n }} ({{ "selfHostedServer" | i18n }}): {{ this.serverConfig?.version }} - ({{ "lastSeenOn" | i18n : (serverConfig.utcDate | date : "mediumDate") }}) + ({{ "lastSeenOn" | i18n: (serverConfig.utcDate | date: "mediumDate") }})

diff --git a/apps/browser/src/popup/settings/about.component.ts b/apps/browser/src/popup/settings/about.component.ts index c0c9012f341..d6f6f000b61 100644 --- a/apps/browser/src/popup/settings/about.component.ts +++ b/apps/browser/src/popup/settings/about.component.ts @@ -24,6 +24,6 @@ export class AboutComponent { constructor( private configService: ConfigServiceAbstraction, - private environmentService: EnvironmentService + private environmentService: EnvironmentService, ) {} } diff --git a/apps/browser/src/popup/settings/excluded-domains.component.html b/apps/browser/src/popup/settings/excluded-domains.component.html index 51d6a20d5be..652eb4aef08 100644 --- a/apps/browser/src/popup/settings/excluded-domains.component.html +++ b/apps/browser/src/popup/settings/excluded-domains.component.html @@ -38,7 +38,7 @@
- + await this.loadCurrentUris(), - 500 + 500, ); break; default: @@ -96,7 +96,7 @@ export class ExcludedDomainsComponent implements OnInit, OnDestroy { this.platformUtilsService.showToast( "error", null, - this.i18nService.t("excludedDomainsInvalidDomain", domain.uri) + this.i18nService.t("excludedDomainsInvalidDomain", domain.uri), ); return; } @@ -116,7 +116,7 @@ export class ExcludedDomainsComponent implements OnInit, OnDestroy { getNewlyAddedDomains(domain: ExcludedDomain[]): ExcludedDomain[] { const result = this.excludedDomains.filter( (newDomain) => - !this.existingExcludedDomains.some((oldDomain) => newDomain.uri === oldDomain.uri) + !this.existingExcludedDomains.some((oldDomain) => newDomain.uri === oldDomain.uri), ); return result; } diff --git a/apps/browser/src/popup/settings/folders.component.ts b/apps/browser/src/popup/settings/folders.component.ts index cae5d6f5106..6e97fb332a2 100644 --- a/apps/browser/src/popup/settings/folders.component.ts +++ b/apps/browser/src/popup/settings/folders.component.ts @@ -12,7 +12,10 @@ import { FolderView } from "@bitwarden/common/vault/models/view/folder.view"; export class FoldersComponent { folders$: Observable; - constructor(private folderService: FolderService, private router: Router) { + constructor( + private folderService: FolderService, + private router: Router, + ) { this.folders$ = this.folderService.folderViews$.pipe( map((folders) => { if (folders.length > 0) { @@ -20,7 +23,7 @@ export class FoldersComponent { } return folders; - }) + }), ); } diff --git a/apps/browser/src/popup/settings/options.component.html b/apps/browser/src/popup/settings/options.component.html index fb8906463be..93c018233af 100644 --- a/apps/browser/src/popup/settings/options.component.html +++ b/apps/browser/src/popup/settings/options.component.html @@ -205,7 +205,9 @@ />
- +
diff --git a/apps/browser/src/popup/settings/options.component.ts b/apps/browser/src/popup/settings/options.component.ts index 4f943a857be..cac85405298 100644 --- a/apps/browser/src/popup/settings/options.component.ts +++ b/apps/browser/src/popup/settings/options.component.ts @@ -46,7 +46,7 @@ export class OptionsComponent implements OnInit { private totpService: TotpService, i18nService: I18nService, private themingService: AbstractThemingService, - private settingsService: SettingsService + private settingsService: SettingsService, ) { this.themeOptions = [ { name: i18nService.t("default"), value: ThemeType.System }, @@ -118,7 +118,7 @@ export class OptionsComponent implements OnInit { async updateChangedPasswordNotification() { await this.stateService.setDisableChangedPasswordNotification( - !this.enableChangedPasswordNotification + !this.enableChangedPasswordNotification, ); } diff --git a/apps/browser/src/popup/settings/premium.component.ts b/apps/browser/src/popup/settings/premium.component.ts index e57d53f3c40..cda93d5b68f 100644 --- a/apps/browser/src/popup/settings/premium.component.ts +++ b/apps/browser/src/popup/settings/premium.component.ts @@ -26,7 +26,7 @@ export class PremiumComponent extends BasePremiumComponent { private location: Location, private currencyPipe: CurrencyPipe, dialogService: DialogService, - environmentService: EnvironmentService + environmentService: EnvironmentService, ) { super( i18nService, @@ -35,7 +35,7 @@ export class PremiumComponent extends BasePremiumComponent { logService, stateService, dialogService, - environmentService + environmentService, ); // Support old price string. Can be removed in future once all translations are properly updated. diff --git a/apps/browser/src/popup/settings/settings.component.html b/apps/browser/src/popup/settings/settings.component.html index 0b752ac6ce6..f099528918b 100644 --- a/apps/browser/src/popup/settings/settings.component.html +++ b/apps/browser/src/popup/settings/settings.component.html @@ -1,4 +1,4 @@ -
+
@@ -6,7 +6,7 @@ {{ "settings" | i18n }}
-
+

{{ "manage" | i18n }}

@@ -52,14 +52,14 @@ {{ "vaultTimeoutPolicyWithActionInEffect" - | i18n : policy.timeout.hours : policy.timeout.minutes : (policy.action | i18n) + | i18n: policy.timeout.hours : policy.timeout.minutes : (policy.action | i18n) }} - {{ "vaultTimeoutPolicyInEffect" | i18n : policy.timeout.hours : policy.timeout.minutes }} + {{ "vaultTimeoutPolicyInEffect" | i18n: policy.timeout.hours : policy.timeout.minutes }} - {{ "vaultTimeoutActionPolicyInEffect" | i18n : (policy.action | i18n) }} + {{ "vaultTimeoutActionPolicyInEffect" | i18n: (policy.action | i18n) }} { await this.saveVaultTimeout(previousValue, newValue); }), - takeUntil(this.destroy$) + takeUntil(this.destroy$), ) .subscribe(); @@ -165,14 +165,14 @@ export class SettingsComponent implements OnInit { concatMap(async ([previousValue, newValue]) => { await this.saveVaultTimeoutAction(previousValue, newValue); }), - takeUntil(this.destroy$) + takeUntil(this.destroy$), ) .subscribe(); const initialValues = { vaultTimeout: timeout, vaultTimeoutAction: await firstValueFrom( - this.vaultTimeoutSettingsService.vaultTimeoutAction$() + this.vaultTimeoutSettingsService.vaultTimeoutAction$(), ), pin: pinStatus !== "DISABLED", biometric: await this.vaultTimeoutSettingsService.isBiometricLockSet(), @@ -189,7 +189,7 @@ export class SettingsComponent implements OnInit { await this.updatePin(value); this.refreshTimeoutSettings$.next(); }), - takeUntil(this.destroy$) + takeUntil(this.destroy$), ) .subscribe(); @@ -205,7 +205,7 @@ export class SettingsComponent implements OnInit { } this.refreshTimeoutSettings$.next(); }), - takeUntil(this.destroy$) + takeUntil(this.destroy$), ) .subscribe(); @@ -215,9 +215,9 @@ export class SettingsComponent implements OnInit { combineLatest([ this.vaultTimeoutSettingsService.availableVaultTimeoutActions$(), this.vaultTimeoutSettingsService.vaultTimeoutAction$(), - ]) + ]), ), - takeUntil(this.destroy$) + takeUntil(this.destroy$), ) .subscribe(([availableActions, action]) => { this.availableVaultTimeoutActions = availableActions; @@ -235,9 +235,9 @@ export class SettingsComponent implements OnInit { combineLatest([ this.vaultTimeoutSettingsService.availableVaultTimeoutActions$(), maximumVaultTimeoutPolicy, - ]) + ]), ), - takeUntil(this.destroy$) + takeUntil(this.destroy$), ) .subscribe(([availableActions, policy]) => { if (policy?.data?.action || availableActions.length <= 1) { @@ -268,14 +268,14 @@ export class SettingsComponent implements OnInit { this.platformUtilsService.showToast( "error", null, - this.i18nService.t("vaultTimeoutTooLarge") + this.i18nService.t("vaultTimeoutTooLarge"), ); return; } await this.vaultTimeoutSettingsService.setVaultTimeoutOptions( newValue, - await firstValueFrom(this.vaultTimeoutSettingsService.vaultTimeoutAction$()) + await firstValueFrom(this.vaultTimeoutSettingsService.vaultTimeoutAction$()), ); if (newValue == null) { this.messagingService.send("bgReseedStorage"); @@ -302,14 +302,14 @@ export class SettingsComponent implements OnInit { this.platformUtilsService.showToast( "error", null, - this.i18nService.t("vaultTimeoutTooLarge") + this.i18nService.t("vaultTimeoutTooLarge"), ); return; } await this.vaultTimeoutSettingsService.setVaultTimeoutOptions( this.form.value.vaultTimeout, - newValue + newValue, ); this.refreshTimeoutSettings$.next(); } @@ -386,7 +386,7 @@ export class SettingsComponent implements OnInit { this.platformUtilsService.showToast( "error", this.i18nService.t("errorEnableBiometricTitle"), - this.i18nService.t("errorEnableBiometricDesc") + this.i18nService.t("errorEnableBiometricDesc"), ); } }) @@ -416,7 +416,7 @@ export class SettingsComponent implements OnInit { async updateAutoBiometricsPrompt() { await this.stateService.setDisableAutoBiometricsPrompt( - !this.form.value.enableAutoBiometricsPrompt + !this.form.value.enableAutoBiometricsPrompt, ); } @@ -491,7 +491,7 @@ export class SettingsComponent implements OnInit { async fingerprint() { const fingerprint = await this.cryptoService.getFingerprint( - await this.stateService.getUserId() + await this.stateService.getUserId(), ); const dialogRef = FingerprintDialogComponent.open(this.dialogService, { diff --git a/apps/browser/src/popup/settings/sync.component.ts b/apps/browser/src/popup/settings/sync.component.ts index ffc20363f71..3fe4de9eb51 100644 --- a/apps/browser/src/popup/settings/sync.component.ts +++ b/apps/browser/src/popup/settings/sync.component.ts @@ -15,7 +15,7 @@ export class SyncComponent implements OnInit { constructor( private syncService: SyncService, private platformUtilsService: PlatformUtilsService, - private i18nService: I18nService + private i18nService: I18nService, ) {} async ngOnInit() { diff --git a/apps/browser/src/tools/background/service_factories/import-api-service.factory.ts b/apps/browser/src/tools/background/service_factories/import-api-service.factory.ts index 00954a0dc60..37d0b9000c2 100644 --- a/apps/browser/src/tools/background/service_factories/import-api-service.factory.ts +++ b/apps/browser/src/tools/background/service_factories/import-api-service.factory.ts @@ -15,12 +15,12 @@ type ServiceCache = { importApiService?: ImportApiServiceAbstraction } & CachedS export function importApiServiceFactory( cache: ServiceCache, - opts: ImportApiServiceInitOptions + opts: ImportApiServiceInitOptions, ): Promise { return factory( cache, "importApiService", opts, - async () => new ImportApiService(await apiServiceFactory(cache, opts)) + async () => new ImportApiService(await apiServiceFactory(cache, opts)), ); } diff --git a/apps/browser/src/tools/background/service_factories/import-service.factory.ts b/apps/browser/src/tools/background/service_factories/import-service.factory.ts index 7f5328f4d07..e800dbabaa1 100644 --- a/apps/browser/src/tools/background/service_factories/import-service.factory.ts +++ b/apps/browser/src/tools/background/service_factories/import-service.factory.ts @@ -42,7 +42,7 @@ export function importServiceFactory( cache: { importService?: ImportServiceAbstraction; } & CachedServices, - opts: ImportServiceInitOptions + opts: ImportServiceInitOptions, ): Promise { return factory( cache, @@ -55,7 +55,7 @@ export function importServiceFactory( await importApiServiceFactory(cache, opts), await i18nServiceFactory(cache, opts), await collectionServiceFactory(cache, opts), - await cryptoServiceFactory(cache, opts) - ) + await cryptoServiceFactory(cache, opts), + ), ); } diff --git a/apps/browser/src/tools/background/service_factories/password-generation-service.factory.ts b/apps/browser/src/tools/background/service_factories/password-generation-service.factory.ts index b5a56dc1184..4b4d80e76d4 100644 --- a/apps/browser/src/tools/background/service_factories/password-generation-service.factory.ts +++ b/apps/browser/src/tools/background/service_factories/password-generation-service.factory.ts @@ -30,7 +30,7 @@ export type PasswordGenerationServiceInitOptions = PasswordGenerationServiceFact export function passwordGenerationServiceFactory( cache: { passwordGenerationService?: PasswordGenerationServiceAbstraction } & CachedServices, - opts: PasswordGenerationServiceInitOptions + opts: PasswordGenerationServiceInitOptions, ): Promise { return factory( cache, @@ -40,7 +40,7 @@ export function passwordGenerationServiceFactory( new PasswordGenerationService( await cryptoServiceFactory(cache, opts), await policyServiceFactory(cache, opts), - await stateServiceFactory(cache, opts) - ) + await stateServiceFactory(cache, opts), + ), ); } diff --git a/apps/browser/src/tools/background/service_factories/password-strength-service.factory.ts b/apps/browser/src/tools/background/service_factories/password-strength-service.factory.ts index 3ebd4636fe3..c1e5b71ee32 100644 --- a/apps/browser/src/tools/background/service_factories/password-strength-service.factory.ts +++ b/apps/browser/src/tools/background/service_factories/password-strength-service.factory.ts @@ -17,7 +17,7 @@ export function passwordStrengthServiceFactory( cache: { passwordStrengthService?: PasswordStrengthServiceAbstraction; } & CachedServices, - opts: PasswordStrengthServiceInitOptions + opts: PasswordStrengthServiceInitOptions, ): Promise { return factory(cache, "passwordStrengthService", opts, async () => new PasswordStrengthService()); } diff --git a/apps/browser/src/tools/popup/generator/generator.component.html b/apps/browser/src/tools/popup/generator/generator.component.html index a236c765577..5f722b661ce 100644 --- a/apps/browser/src/tools/popup/generator/generator.component.html +++ b/apps/browser/src/tools/popup/generator/generator.component.html @@ -1,4 +1,4 @@ -
+
-
+
{{ "passwordGeneratorPolicyInEffect" | i18n }} diff --git a/apps/browser/src/tools/popup/generator/generator.component.ts b/apps/browser/src/tools/popup/generator/generator.component.ts index 03d7442e6eb..e4263d3caa2 100644 --- a/apps/browser/src/tools/popup/generator/generator.component.ts +++ b/apps/browser/src/tools/popup/generator/generator.component.ts @@ -28,7 +28,7 @@ export class GeneratorComponent extends BaseGeneratorComponent { stateService: StateService, route: ActivatedRoute, logService: LogService, - private location: Location + private location: Location, ) { super( passwordGenerationService, @@ -38,7 +38,7 @@ export class GeneratorComponent extends BaseGeneratorComponent { i18nService, logService, route, - window + window, ); } diff --git a/apps/browser/src/tools/popup/generator/password-generator-history.component.html b/apps/browser/src/tools/popup/generator/password-generator-history.component.html index 22e07810d07..8f4a246fc5e 100644 --- a/apps/browser/src/tools/popup/generator/password-generator-history.component.html +++ b/apps/browser/src/tools/popup/generator/password-generator-history.component.html @@ -25,7 +25,7 @@ [appCopyText]="h.password" [innerHTML]="h.password | colorPassword" >
- {{ h.date | date : "medium" }} + {{ h.date | date: "medium" }}
diff --git a/apps/browser/src/tools/popup/generator/password-generator-history.component.ts b/apps/browser/src/tools/popup/generator/password-generator-history.component.ts index fbe1ba10d33..8448077083e 100644 --- a/apps/browser/src/tools/popup/generator/password-generator-history.component.ts +++ b/apps/browser/src/tools/popup/generator/password-generator-history.component.ts @@ -15,7 +15,7 @@ export class PasswordGeneratorHistoryComponent extends BasePasswordGeneratorHist passwordGenerationService: PasswordGenerationServiceAbstraction, platformUtilsService: PlatformUtilsService, i18nService: I18nService, - private location: Location + private location: Location, ) { super(passwordGenerationService, platformUtilsService, i18nService, window); } diff --git a/apps/browser/src/tools/popup/send/components/send-list.component.html b/apps/browser/src/tools/popup/send/components/send-list.component.html index 3dc5af0106d..05c8e3e3754 100644 --- a/apps/browser/src/tools/popup/send/components/send-list.component.html +++ b/apps/browser/src/tools/popup/send/components/send-list.component.html @@ -57,7 +57,7 @@ {{ "pendingDeletion" | i18n }} - {{ s.deletionDate | date : "medium" }} + {{ s.deletionDate | date: "medium" }}
diff --git a/apps/browser/src/tools/popup/send/send-add-edit.component.ts b/apps/browser/src/tools/popup/send/send-add-edit.component.ts index 5ad664d871e..ef2b57992ac 100644 --- a/apps/browser/src/tools/popup/send/send-add-edit.component.ts +++ b/apps/browser/src/tools/popup/send/send-add-edit.component.ts @@ -48,7 +48,7 @@ export class SendAddEditComponent extends BaseAddEditComponent { sendApiService: SendApiService, dialogService: DialogService, formBuilder: FormBuilder, - private filePopoutUtilsService: FilePopoutUtilsService + private filePopoutUtilsService: FilePopoutUtilsService, ) { super( i18nService, @@ -62,7 +62,7 @@ export class SendAddEditComponent extends BaseAddEditComponent { stateService, sendApiService, dialogService, - formBuilder + formBuilder, ); } diff --git a/apps/browser/src/tools/popup/send/send-groupings.component.html b/apps/browser/src/tools/popup/send/send-groupings.component.html index e677895b409..edeabd6546a 100644 --- a/apps/browser/src/tools/popup/send/send-groupings.component.html +++ b/apps/browser/src/tools/popup/send/send-groupings.component.html @@ -1,9 +1,9 @@ -
+

{{ "send" | i18n }}

-
+
{{ "sendDisabledWarning" | i18n }} diff --git a/apps/browser/src/tools/popup/send/send-groupings.component.ts b/apps/browser/src/tools/popup/send/send-groupings.component.ts index 0fde93df8fe..40bb38d574f 100644 --- a/apps/browser/src/tools/popup/send/send-groupings.component.ts +++ b/apps/browser/src/tools/popup/send/send-groupings.component.ts @@ -50,7 +50,7 @@ export class SendGroupingsComponent extends BaseSendComponent { private broadcasterService: BroadcasterService, logService: LogService, sendApiService: SendApiService, - dialogService: DialogService + dialogService: DialogService, ) { super( sendService, @@ -62,7 +62,7 @@ export class SendGroupingsComponent extends BaseSendComponent { policyService, logService, sendApiService, - dialogService + dialogService, ); super.onSuccessfulLoad = async () => { this.calculateTypeCounts(); diff --git a/apps/browser/src/tools/popup/send/send-type.component.ts b/apps/browser/src/tools/popup/send/send-type.component.ts index 6e5e76a0e5d..f72be474f13 100644 --- a/apps/browser/src/tools/popup/send/send-type.component.ts +++ b/apps/browser/src/tools/popup/send/send-type.component.ts @@ -50,7 +50,7 @@ export class SendTypeComponent extends BaseSendComponent { private router: Router, logService: LogService, sendApiService: SendApiService, - dialogService: DialogService + dialogService: DialogService, ) { super( sendService, @@ -62,7 +62,7 @@ export class SendTypeComponent extends BaseSendComponent { policyService, logService, sendApiService, - dialogService + dialogService, ); super.onSuccessfulLoad = async () => { this.selectType(this.type); diff --git a/apps/browser/src/tools/popup/settings/export.component.ts b/apps/browser/src/tools/popup/settings/export.component.ts index d34b86a9e46..7c0a294ec5e 100644 --- a/apps/browser/src/tools/popup/settings/export.component.ts +++ b/apps/browser/src/tools/popup/settings/export.component.ts @@ -31,7 +31,7 @@ export class ExportComponent extends BaseExportComponent { userVerificationService: UserVerificationService, formBuilder: UntypedFormBuilder, fileDownloadService: FileDownloadService, - dialogService: DialogService + dialogService: DialogService, ) { super( cryptoService, @@ -45,7 +45,7 @@ export class ExportComponent extends BaseExportComponent { userVerificationService, formBuilder, fileDownloadService, - dialogService + dialogService, ); } diff --git a/apps/browser/src/vault/background/service_factories/cipher-service.factory.ts b/apps/browser/src/vault/background/service_factories/cipher-service.factory.ts index 46062ebc9cc..3c539586333 100644 --- a/apps/browser/src/vault/background/service_factories/cipher-service.factory.ts +++ b/apps/browser/src/vault/background/service_factories/cipher-service.factory.ts @@ -58,7 +58,7 @@ export type CipherServiceInitOptions = CipherServiceFactoryOptions & export function cipherServiceFactory( cache: { cipherService?: AbstractCipherService } & CachedServices, - opts: CipherServiceInitOptions + opts: CipherServiceInitOptions, ): Promise { return factory( cache, @@ -74,7 +74,7 @@ export function cipherServiceFactory( await stateServiceFactory(cache, opts), await encryptServiceFactory(cache, opts), await cipherFileUploadServiceFactory(cache, opts), - await configServiceFactory(cache, opts) - ) + await configServiceFactory(cache, opts), + ), ); } diff --git a/apps/browser/src/vault/background/service_factories/collection-service.factory.ts b/apps/browser/src/vault/background/service_factories/collection-service.factory.ts index 323eebe27d1..fd576ca12c3 100644 --- a/apps/browser/src/vault/background/service_factories/collection-service.factory.ts +++ b/apps/browser/src/vault/background/service_factories/collection-service.factory.ts @@ -28,7 +28,7 @@ export type CollectionServiceInitOptions = CollectionServiceFactoryOptions & export function collectionServiceFactory( cache: { collectionService?: AbstractCollectionService } & CachedServices, - opts: CollectionServiceInitOptions + opts: CollectionServiceInitOptions, ): Promise { return factory( cache, @@ -38,7 +38,7 @@ export function collectionServiceFactory( new CollectionService( await cryptoServiceFactory(cache, opts), await i18nServiceFactory(cache, opts), - await stateServiceFactory(cache, opts) - ) + await stateServiceFactory(cache, opts), + ), ); } diff --git a/apps/browser/src/vault/background/service_factories/folder-service.factory.ts b/apps/browser/src/vault/background/service_factories/folder-service.factory.ts index b33c79a012b..aeafbe5ef24 100644 --- a/apps/browser/src/vault/background/service_factories/folder-service.factory.ts +++ b/apps/browser/src/vault/background/service_factories/folder-service.factory.ts @@ -31,7 +31,7 @@ export type FolderServiceInitOptions = FolderServiceFactoryOptions & export function folderServiceFactory( cache: { folderService?: AbstractFolderService } & CachedServices, - opts: FolderServiceInitOptions + opts: FolderServiceInitOptions, ): Promise { return factory( cache, @@ -42,7 +42,7 @@ export function folderServiceFactory( await cryptoServiceFactory(cache, opts), await i18nServiceFactory(cache, opts), await cipherServiceFactory(cache, opts), - await stateServiceFactory(cache, opts) - ) + await stateServiceFactory(cache, opts), + ), ); } diff --git a/apps/browser/src/vault/background/service_factories/sync-notifier-service.factory.ts b/apps/browser/src/vault/background/service_factories/sync-notifier-service.factory.ts index 9e976b3bf75..715d0f2c860 100644 --- a/apps/browser/src/vault/background/service_factories/sync-notifier-service.factory.ts +++ b/apps/browser/src/vault/background/service_factories/sync-notifier-service.factory.ts @@ -13,9 +13,9 @@ export type SyncNotifierServiceInitOptions = SyncNotifierServiceFactoryOptions; export function syncNotifierServiceFactory( cache: { syncNotifierService?: AbstractSyncNotifierService } & CachedServices, - opts: SyncNotifierServiceInitOptions + opts: SyncNotifierServiceInitOptions, ): Promise { return factory(cache, "syncNotifierService", opts, () => - Promise.resolve(new SyncNotifierService()) + Promise.resolve(new SyncNotifierService()), ); } diff --git a/apps/browser/src/vault/background/service_factories/totp-service.factory.ts b/apps/browser/src/vault/background/service_factories/totp-service.factory.ts index 21e042370b7..c8f5a270b48 100644 --- a/apps/browser/src/vault/background/service_factories/totp-service.factory.ts +++ b/apps/browser/src/vault/background/service_factories/totp-service.factory.ts @@ -23,7 +23,7 @@ export type TotpServiceInitOptions = TotpServiceOptions & export function totpServiceFactory( cache: { totpService?: AbstractTotpService } & CachedServices, - opts: TotpServiceInitOptions + opts: TotpServiceInitOptions, ): Promise { return factory( cache, @@ -32,7 +32,7 @@ export function totpServiceFactory( async () => new TotpService( await cryptoFunctionServiceFactory(cache, opts), - await logServiceFactory(cache, opts) - ) + await logServiceFactory(cache, opts), + ), ); } diff --git a/apps/browser/src/vault/fido2/browser-fido2-user-interface.service.ts b/apps/browser/src/vault/fido2/browser-fido2-user-interface.service.ts index e03c430009d..c22c043fb92 100644 --- a/apps/browser/src/vault/fido2/browser-fido2-user-interface.service.ts +++ b/apps/browser/src/vault/fido2/browser-fido2-user-interface.service.ts @@ -46,7 +46,7 @@ export function fido2PopoutSessionData$() { sessionId: queryParams.sessionId as string, fallbackSupported: queryParams.fallbackSupported === "true", userVerification: queryParams.userVerification === "true", - })) + })), ); } @@ -121,13 +121,13 @@ export class BrowserFido2UserInterfaceService implements Fido2UserInterfaceServi async newSession( fallbackSupported: boolean, tab: chrome.tabs.Tab, - abortController?: AbortController + abortController?: AbortController, ): Promise { return await BrowserFido2UserInterfaceSession.create( this.authService, fallbackSupported, tab, - abortController + abortController, ); } } @@ -137,13 +137,13 @@ export class BrowserFido2UserInterfaceSession implements Fido2UserInterfaceSessi authService: AuthService, fallbackSupported: boolean, tab: chrome.tabs.Tab, - abortController?: AbortController + abortController?: AbortController, ): Promise { return new BrowserFido2UserInterfaceSession( authService, fallbackSupported, tab, - abortController + abortController, ); } @@ -170,7 +170,7 @@ export class BrowserFido2UserInterfaceSession implements Fido2UserInterfaceSessi private closed = false; private messages$ = (BrowserApi.messageListener$() as Observable).pipe( - filter((msg) => msg.sessionId === this.sessionId) + filter((msg) => msg.sessionId === this.sessionId), ); private connected$ = new BehaviorSubject(false); private windowClosed$: Observable; @@ -181,13 +181,13 @@ export class BrowserFido2UserInterfaceSession implements Fido2UserInterfaceSessi private readonly fallbackSupported: boolean, private readonly tab: chrome.tabs.Tab, readonly abortController = new AbortController(), - readonly sessionId = Utils.newGuid() + readonly sessionId = Utils.newGuid(), ) { this.messages$ .pipe( filter((msg) => msg.type === "ConnectResponse"), take(1), - takeUntil(this.destroy$) + takeUntil(this.destroy$), ) .subscribe(() => { this.connected$.next(true); @@ -209,7 +209,7 @@ export class BrowserFido2UserInterfaceSession implements Fido2UserInterfaceSessi .pipe( filter((msg) => msg.type === "AbortResponse"), take(1), - takeUntil(this.destroy$) + takeUntil(this.destroy$), ) .subscribe((msg) => { if (msg.type === "AbortResponse") { @@ -223,7 +223,7 @@ export class BrowserFido2UserInterfaceSession implements Fido2UserInterfaceSessi // and test that it doesn't break. Tracking Ticket: https://bitwarden.atlassian.net/browse/PM-4735 // eslint-disable-next-line no-restricted-syntax (handler: any) => chrome.windows.onRemoved.addListener(handler), - (handler: any) => chrome.windows.onRemoved.removeListener(handler) + (handler: any) => chrome.windows.onRemoved.removeListener(handler), ); BrowserFido2UserInterfaceSession.sendMessage({ @@ -318,14 +318,14 @@ export class BrowserFido2UserInterfaceSession implements Fido2UserInterfaceSessi } private async receive( - type: T + type: T, ): Promise { try { const response = await firstValueFrom( this.messages$.pipe( filter((msg) => msg.sessionId === this.sessionId && msg.type === type), - takeUntil(this.destroy$) - ) + takeUntil(this.destroy$), + ), ); return response as BrowserFido2Message & { type: T }; } catch (error) { @@ -345,9 +345,9 @@ export class BrowserFido2UserInterfaceSession implements Fido2UserInterfaceSessi merge( this.connected$.pipe(filter((connected) => connected === true)), fromEvent(this.abortController.signal, "abort").pipe( - switchMap(() => throwError(() => new SessionClosedError())) - ) - ) + switchMap(() => throwError(() => new SessionClosedError())), + ), + ), ); const popoutId = await openFido2Popout(this.tab, { @@ -360,7 +360,7 @@ export class BrowserFido2UserInterfaceSession implements Fido2UserInterfaceSessi filter((windowId) => { return popoutId === windowId; }), - takeUntil(this.destroy$) + takeUntil(this.destroy$), ) .subscribe(() => { this.close(); diff --git a/apps/browser/src/vault/fido2/content/content-script.ts b/apps/browser/src/vault/fido2/content/content-script.ts index 96af5a6e647..3fe3e814481 100644 --- a/apps/browser/src/vault/fido2/content/content-script.ts +++ b/apps/browser/src/vault/fido2/content/content-script.ts @@ -10,7 +10,7 @@ function isFido2FeatureEnabled(): Promise { return new Promise((resolve) => { chrome.runtime.sendMessage( { command: "checkFido2FeatureEnabled" }, - (response: { result?: boolean }) => resolve(response.result) + (response: { result?: boolean }) => resolve(response.result), ); }); } @@ -21,7 +21,7 @@ async function getFromLocalStorage(keys: string | string[]): Promise) { + const excludedDomains = activeUserSettings?.neverDomains; return excludedDomains && window.location.hostname in excludedDomains; } @@ -53,6 +57,10 @@ function isSameOriginWithAncestors() { } } +async function isLocationBitwardenVault(activeUserSettings: Record) { + return window.location.origin === activeUserSettings.serverConfig.environment.vault; +} + function initializeFido2ContentScript() { const s = document.createElement("script"); s.src = chrome.runtime.getURL("content/fido2/page-script.js"); @@ -92,7 +100,7 @@ function initializeFido2ContentScript() { type: MessageType.CredentialCreationResponse, result: response.result, }); - } + }, ); }); } @@ -120,10 +128,10 @@ function initializeFido2ContentScript() { type: MessageType.CredentialGetResponse, result: response.result, }); - } + }, ); }).finally(() => - abortController.signal.removeEventListener("abort", abortHandler) + abortController.signal.removeEventListener("abort", abortHandler), ) as Promise; } @@ -132,9 +140,21 @@ function initializeFido2ContentScript() { } async function run() { - if ((await hasActiveUser()) && (await isFido2FeatureEnabled()) && !(await isDomainExcluded())) { - initializeFido2ContentScript(); + if (!(await hasActiveUser())) { + return; } + + const activeUserSettings = await getActiveUserSettings(); + if ( + activeUserSettings == null || + !(await isFido2FeatureEnabled()) || + (await isDomainExcluded(activeUserSettings)) || + (await isLocationBitwardenVault(activeUserSettings)) + ) { + return; + } + + initializeFido2ContentScript(); } run(); diff --git a/apps/browser/src/vault/fido2/content/messaging/messenger.spec.ts b/apps/browser/src/vault/fido2/content/messaging/messenger.spec.ts index 505682d997d..02e0f944974 100644 --- a/apps/browser/src/vault/fido2/content/messaging/messenger.spec.ts +++ b/apps/browser/src/vault/fido2/content/messaging/messenger.spec.ts @@ -99,7 +99,7 @@ class TestChannelPair { class TestMessageHandler { readonly handler: ( message: TestMessage, - abortController?: AbortController + abortController?: AbortController, ) => Promise; private recievedMessages: { @@ -144,7 +144,7 @@ class MockMessagePort { postMessage(message: T, port?: MessagePort) { this.remotePort.onmessage( - new MessageEvent("message", { data: message, ports: port ? [port] : [] }) + new MessageEvent("message", { data: message, ports: port ? [port] : [] }), ); } diff --git a/apps/browser/src/vault/fido2/content/messaging/messenger.ts b/apps/browser/src/vault/fido2/content/messaging/messenger.ts index aeb835e2d5f..b69f6ac076f 100644 --- a/apps/browser/src/vault/fido2/content/messaging/messenger.ts +++ b/apps/browser/src/vault/fido2/content/messaging/messenger.ts @@ -13,7 +13,7 @@ export type Metadata = { SENDER: typeof SENDER }; export type MessageWithMetadata = Message & Metadata; type Handler = ( message: MessageWithMetadata, - abortController?: AbortController + abortController?: AbortController, ) => Promise; /** diff --git a/apps/browser/src/vault/fido2/content/page-script.ts b/apps/browser/src/vault/fido2/content/page-script.ts index 2071dbb1495..9a3a74bed1b 100644 --- a/apps/browser/src/vault/fido2/content/page-script.ts +++ b/apps/browser/src/vault/fido2/content/page-script.ts @@ -47,7 +47,7 @@ if (browserNativeWebauthnSupport) { const browserCredentials = { create: navigator.credentials.create.bind( - navigator.credentials + navigator.credentials, ) as typeof navigator.credentials.create, get: navigator.credentials.get.bind(navigator.credentials) as typeof navigator.credentials.get, }; @@ -55,16 +55,16 @@ const browserCredentials = { const messenger = ((window as any).messenger = Messenger.forDOMCommunication(window)); navigator.credentials.create = async ( options?: CredentialCreationOptions, - abortController?: AbortController + abortController?: AbortController, ): Promise => { if (!isWebauthnCall(options)) { return await browserCredentials.create(options); } const fallbackSupported = - (options?.publicKey?.authenticatorSelection.authenticatorAttachment === "platform" && + (options?.publicKey?.authenticatorSelection?.authenticatorAttachment === "platform" && browserNativeWebauthnPlatformAuthenticatorSupport) || - (options?.publicKey?.authenticatorSelection.authenticatorAttachment !== "platform" && + (options?.publicKey?.authenticatorSelection?.authenticatorAttachment !== "platform" && browserNativeWebauthnSupport); try { const response = await messenger.request( @@ -72,7 +72,7 @@ navigator.credentials.create = async ( type: MessageType.CredentialCreationRequest, data: WebauthnUtils.mapCredentialCreationOptions(options, fallbackSupported), }, - abortController + abortController, ); if (response.type !== MessageType.CredentialCreationResponse) { @@ -92,7 +92,7 @@ navigator.credentials.create = async ( navigator.credentials.get = async ( options?: CredentialRequestOptions, - abortController?: AbortController + abortController?: AbortController, ): Promise => { if (!isWebauthnCall(options)) { return await browserCredentials.get(options); @@ -110,7 +110,7 @@ navigator.credentials.get = async ( type: MessageType.CredentialGetRequest, data: WebauthnUtils.mapCredentialRequestOptions(options, fallbackSupported), }, - abortController + abortController, ); if (response.type !== MessageType.CredentialGetResponse) { @@ -161,9 +161,9 @@ async function waitForFocus(fallbackWait = 500, timeout = 5 * 60 * 1000) { timeoutId = window.setTimeout( () => reject( - new DOMException("The operation either timed out or was not allowed.", "AbortError") + new DOMException("The operation either timed out or was not allowed.", "AbortError"), ), - timeout + timeout, ); }); diff --git a/apps/browser/src/vault/fido2/webauthn-utils.ts b/apps/browser/src/vault/fido2/webauthn-utils.ts index 3227e5ef116..ec23303a6eb 100644 --- a/apps/browser/src/vault/fido2/webauthn-utils.ts +++ b/apps/browser/src/vault/fido2/webauthn-utils.ts @@ -12,7 +12,7 @@ import { export class WebauthnUtils { static mapCredentialCreationOptions( options: CredentialCreationOptions, - fallbackSupported: boolean + fallbackSupported: boolean, ): InsecureCreateCredentialParams { const keyOptions = options.publicKey; @@ -92,7 +92,7 @@ export class WebauthnUtils { static mapCredentialRequestOptions( options: CredentialRequestOptions, - fallbackSupported: boolean + fallbackSupported: boolean, ): InsecureAssertCredentialParams { const keyOptions = options.publicKey; diff --git a/apps/browser/src/vault/popup/components/action-buttons.component.ts b/apps/browser/src/vault/popup/components/action-buttons.component.ts index e9ed209bb27..5dbf4f35b8e 100644 --- a/apps/browser/src/vault/popup/components/action-buttons.component.ts +++ b/apps/browser/src/vault/popup/components/action-buttons.component.ts @@ -30,7 +30,7 @@ export class ActionButtonsComponent { private eventCollectionService: EventCollectionService, private totpService: TotpService, private stateService: StateService, - private passwordRepromptService: PasswordRepromptService + private passwordRepromptService: PasswordRepromptService, ) {} async ngOnInit() { @@ -64,7 +64,7 @@ export class ActionButtonsComponent { this.platformUtilsService.showToast( "info", null, - this.i18nService.t("valueCopied", this.i18nService.t(typeI18nKey)) + this.i18nService.t("valueCopied", this.i18nService.t(typeI18nKey)), ); if (typeI18nKey === "password") { diff --git a/apps/browser/src/vault/popup/components/fido2/fido2.component.ts b/apps/browser/src/vault/popup/components/fido2/fido2.component.ts index e0a81d760e3..9311b6f18ad 100644 --- a/apps/browser/src/vault/popup/components/fido2/fido2.component.ts +++ b/apps/browser/src/vault/popup/components/fido2/fido2.component.ts @@ -79,7 +79,7 @@ export class Fido2Component implements OnInit, OnDestroy { private searchService: SearchService, private logService: LogService, private dialogService: DialogService, - private browserMessagingApi: ZonedMessageListenerService + private browserMessagingApi: ZonedMessageListenerService, ) {} ngOnInit() { @@ -91,7 +91,7 @@ export class Fido2Component implements OnInit, OnDestroy { sessionId: queryParamMap.get("sessionId"), senderTabId: queryParamMap.get("senderTabId"), senderUrl: queryParamMap.get("senderUrl"), - })) + })), ); combineLatest([ @@ -125,7 +125,7 @@ export class Fido2Component implements OnInit, OnDestroy { return message; }), filter((message) => !!message), - takeUntil(this.destroy$) + takeUntil(this.destroy$), ) .subscribe((message) => { this.message$.next(message); @@ -139,10 +139,10 @@ export class Fido2Component implements OnInit, OnDestroy { const equivalentDomains = this.settingsService.getEquivalentDomains(this.url); this.ciphers = (await this.cipherService.getAllDecrypted()).filter( - (cipher) => cipher.type === CipherType.Login && !cipher.isDeleted + (cipher) => cipher.type === CipherType.Login && !cipher.isDeleted, ); this.displayedCiphers = this.ciphers.filter((cipher) => - cipher.login.matchesUri(this.url, equivalentDomains) + cipher.login.matchesUri(this.url, equivalentDomains), ); if (this.displayedCiphers.length > 0) { @@ -156,9 +156,9 @@ export class Fido2Component implements OnInit, OnDestroy { message.cipherIds.map(async (cipherId) => { const cipher = await this.cipherService.get(cipherId); return cipher.decrypt( - await this.cipherService.getKeyForCipherKeyDecryption(cipher) + await this.cipherService.getKeyForCipherKeyDecryption(cipher), ); - }) + }), ); this.displayedCiphers = [...this.ciphers]; if (this.displayedCiphers.length > 0) { @@ -172,9 +172,9 @@ export class Fido2Component implements OnInit, OnDestroy { message.existingCipherIds.map(async (cipherId) => { const cipher = await this.cipherService.get(cipherId); return cipher.decrypt( - await this.cipherService.getKeyForCipherKeyDecryption(cipher) + await this.cipherService.getKeyForCipherKeyDecryption(cipher), ); - }) + }), ); this.displayedCiphers = [...this.ciphers]; @@ -196,7 +196,7 @@ export class Fido2Component implements OnInit, OnDestroy { fallbackSupported: "fallbackSupported" in message && message.fallbackSupported, }; }), - takeUntil(this.destroy$) + takeUntil(this.destroy$), ); queryParams$.pipe(takeUntil(this.destroy$)).subscribe((queryParams) => { @@ -312,12 +312,12 @@ export class Fido2Component implements OnInit, OnDestroy { this.displayedCiphers = await this.searchService.searchCiphers( this.searchText, null, - this.ciphers + this.ciphers, ); } else { const equivalentDomains = this.settingsService.getEquivalentDomains(this.url); this.displayedCiphers = this.ciphers.filter((cipher) => - cipher.login.matchesUri(this.url, equivalentDomains) + cipher.login.matchesUri(this.url, equivalentDomains), ); } this.searchPending = false; @@ -369,7 +369,7 @@ export class Fido2Component implements OnInit, OnDestroy { private async handleUserVerification( userVerificationRequested: boolean, - cipher: CipherView + cipher: CipherView, ): Promise { const masterPasswordRepromptRequired = cipher && cipher.reprompt !== 0; diff --git a/apps/browser/src/vault/popup/components/vault/add-edit.component.html b/apps/browser/src/vault/popup/components/vault/add-edit.component.html index b2a42776e18..de8315d3022 100644 --- a/apps/browser/src/vault/popup/components/vault/add-edit.component.html +++ b/apps/browser/src/vault/popup/components/vault/add-edit.component.html @@ -136,7 +136,7 @@
{{ "typePasskey" | i18n }} {{ "dateCreated" | i18n }} - {{ cipher.login.fido2Credentials[0].creationDate | date : "short" }} + {{ cipher.login.fido2Credentials[0].creationDate | date: "short" }}
@@ -241,7 +241,7 @@ type="text" name="Card.ExpYear" [(ngModel)]="cipher.card.expYear" - placeholder="{{ 'ex' | i18n }} {{ currentDate | date : 'yyyy' }}" + placeholder="{{ 'ex' | i18n }} {{ currentDate | date: 'yyyy' }}" [readonly]="!cipher.edit && editMode" />
@@ -483,7 +483,7 @@ class="box-content-row box-content-row-multi" appBoxRow *ngFor="let u of cipher.login.uris; let i = index; trackBy: trackByFunction" - attr.aria-label="{{ 'uriPosition' | i18n : i + 1 }}" + attr.aria-label="{{ 'uriPosition' | i18n: i + 1 }}" >
- + { // We are bypassing user verification pending implementation of PIN and biometric support. return true; @@ -322,7 +322,7 @@ export class AddEditComponent extends BaseAddEditComponent { this.platformUtilsService.showToast( "info", null, - this.i18nService.t("passwordRepromptDisabledAutofillOnPageLoad") + this.i18nService.t("passwordRepromptDisabledAutofillOnPageLoad"), ); return; } @@ -330,14 +330,14 @@ export class AddEditComponent extends BaseAddEditComponent { this.platformUtilsService.showToast( "info", null, - this.i18nService.t("autofillOnPageLoadSetToDefault") + this.i18nService.t("autofillOnPageLoadSetToDefault"), ); } private inAddEditPopoutWindow() { return BrowserPopupUtils.inSingleActionPopout( window, - this.singleActionKey || VaultPopoutType.addEditVaultItem + this.singleActionKey || VaultPopoutType.addEditVaultItem, ); } } diff --git a/apps/browser/src/vault/popup/components/vault/attachments.component.ts b/apps/browser/src/vault/popup/components/vault/attachments.component.ts index 5047c9bfd1e..8faeb86de62 100644 --- a/apps/browser/src/vault/popup/components/vault/attachments.component.ts +++ b/apps/browser/src/vault/popup/components/vault/attachments.component.ts @@ -33,7 +33,7 @@ export class AttachmentsComponent extends BaseAttachmentsComponent { stateService: StateService, logService: LogService, fileDownloadService: FileDownloadService, - dialogService: DialogService + dialogService: DialogService, ) { super( cipherService, @@ -45,7 +45,7 @@ export class AttachmentsComponent extends BaseAttachmentsComponent { logService, stateService, fileDownloadService, - dialogService + dialogService, ); } diff --git a/apps/browser/src/vault/popup/components/vault/collections.component.ts b/apps/browser/src/vault/popup/components/vault/collections.component.ts index d4615e165b2..acbdab36852 100644 --- a/apps/browser/src/vault/popup/components/vault/collections.component.ts +++ b/apps/browser/src/vault/popup/components/vault/collections.component.ts @@ -23,7 +23,7 @@ export class CollectionsComponent extends BaseCollectionsComponent { cipherService: CipherService, private route: ActivatedRoute, private location: Location, - logService: LogService + logService: LogService, ) { super(collectionService, platformUtilsService, i18nService, cipherService, logService); } diff --git a/apps/browser/src/vault/popup/components/vault/current-tab.component.html b/apps/browser/src/vault/popup/components/vault/current-tab.component.html index 4ff7adf4678..c971f6c9371 100644 --- a/apps/browser/src/vault/popup/components/vault/current-tab.component.html +++ b/apps/browser/src/vault/popup/components/vault/current-tab.component.html @@ -1,4 +1,4 @@ -
+

{{ "currentTab" | i18n }}

@@ -11,7 +11,7 @@
-
+
diff --git a/apps/browser/src/vault/popup/components/vault/current-tab.component.ts b/apps/browser/src/vault/popup/components/vault/current-tab.component.ts index 86c6551fb4f..872f6c8d97a 100644 --- a/apps/browser/src/vault/popup/components/vault/current-tab.component.ts +++ b/apps/browser/src/vault/popup/components/vault/current-tab.component.ts @@ -67,7 +67,7 @@ export class CurrentTabComponent implements OnInit, OnDestroy { private stateService: StateService, private passwordRepromptService: PasswordRepromptService, private organizationService: OrganizationService, - private vaultFilterService: VaultFilterService + private vaultFilterService: VaultFilterService, ) {} async ngOnInit() { @@ -124,7 +124,7 @@ export class CurrentTabComponent implements OnInit, OnDestroy { this.platformUtilsService.showToast( "info", null, - this.i18nService.t("autofillPageLoadPolicyActivated") + this.i18nService.t("autofillPageLoadPolicyActivated"), ); } } @@ -252,7 +252,7 @@ export class CurrentTabComponent implements OnInit, OnDestroy { const ciphers = await this.cipherService.getAllDecryptedForUrl( this.url, - otherTypes.length > 0 ? otherTypes : null + otherTypes.length > 0 ? otherTypes : null, ); this.loginCiphers = []; @@ -278,7 +278,7 @@ export class CurrentTabComponent implements OnInit, OnDestroy { }); this.loginCiphers = this.loginCiphers.sort((a, b) => - this.cipherService.sortCiphersByLastUsedThenName(a, b) + this.cipherService.sortCiphersByLastUsedThenName(a, b), ); this.isLoading = this.loaded = true; } diff --git a/apps/browser/src/vault/popup/components/vault/password-history.component.html b/apps/browser/src/vault/popup/components/vault/password-history.component.html index 7d468c87f25..6286aa1022d 100644 --- a/apps/browser/src/vault/popup/components/vault/password-history.component.html +++ b/apps/browser/src/vault/popup/components/vault/password-history.component.html @@ -17,7 +17,7 @@ class="text monospaced no-ellipsis" [innerHTML]="h.password | colorPassword" > - {{ h.lastUsedDate | date : "medium" }} + {{ h.lastUsedDate | date: "medium" }}
diff --git a/apps/browser/src/vault/popup/components/vault/password-history.component.ts b/apps/browser/src/vault/popup/components/vault/password-history.component.ts index 1dd7697a7d5..05986aad51f 100644 --- a/apps/browser/src/vault/popup/components/vault/password-history.component.ts +++ b/apps/browser/src/vault/popup/components/vault/password-history.component.ts @@ -19,7 +19,7 @@ export class PasswordHistoryComponent extends BasePasswordHistoryComponent { platformUtilsService: PlatformUtilsService, i18nService: I18nService, private location: Location, - private route: ActivatedRoute + private route: ActivatedRoute, ) { super(cipherService, platformUtilsService, i18nService, window); } diff --git a/apps/browser/src/vault/popup/components/vault/share.component.ts b/apps/browser/src/vault/popup/components/vault/share.component.ts index 80188646baf..7f99ea72db2 100644 --- a/apps/browser/src/vault/popup/components/vault/share.component.ts +++ b/apps/browser/src/vault/popup/components/vault/share.component.ts @@ -24,7 +24,7 @@ export class ShareComponent extends BaseShareComponent { cipherService: CipherService, private route: ActivatedRoute, private router: Router, - organizationService: OrganizationService + organizationService: OrganizationService, ) { super( collectionService, @@ -32,7 +32,7 @@ export class ShareComponent extends BaseShareComponent { i18nService, cipherService, logService, - organizationService + organizationService, ); } diff --git a/apps/browser/src/vault/popup/components/vault/vault-filter.component.ts b/apps/browser/src/vault/popup/components/vault/vault-filter.component.ts index c2bfb262fd4..61f21a8750a 100644 --- a/apps/browser/src/vault/popup/components/vault/vault-filter.component.ts +++ b/apps/browser/src/vault/popup/components/vault/vault-filter.component.ts @@ -85,7 +85,7 @@ export class VaultFilterComponent implements OnInit, OnDestroy { private searchService: SearchService, private location: Location, private browserStateService: BrowserStateService, - private vaultFilterService: VaultFilterService + private vaultFilterService: VaultFilterService, ) { this.noFolderListSize = 100; } @@ -177,7 +177,7 @@ export class VaultFilterComponent implements OnInit, OnDestroy { async loadCollections() { const allCollections = await this.vaultFilterService.buildCollections( - this.selectedOrganization + this.selectedOrganization, ); this.collections = allCollections.fullList; this.nestedCollections = allCollections.nestedList; @@ -185,7 +185,7 @@ export class VaultFilterComponent implements OnInit, OnDestroy { async loadFolders() { const allFolders = await firstValueFrom( - this.vaultFilterService.buildNestedFolders(this.selectedOrganization) + this.vaultFilterService.buildNestedFolders(this.selectedOrganization), ); this.folders = allFolders.fullList; this.nestedFolders = allFolders.nestedList; @@ -202,10 +202,10 @@ export class VaultFilterComponent implements OnInit, OnDestroy { this.ciphers = await this.searchService.searchCiphers( this.searchText, filterDeleted, - this.allCiphers + this.allCiphers, ); this.ciphers = this.ciphers.filter( - (c) => !this.vaultFilterService.filterCipherForSelectedVault(c) + (c) => !this.vaultFilterService.filterCipherForSelectedVault(c), ); return; } @@ -218,11 +218,11 @@ export class VaultFilterComponent implements OnInit, OnDestroy { this.ciphers = await this.searchService.searchCiphers( this.searchText, filterDeleted, - this.allCiphers + this.allCiphers, ); } this.ciphers = this.ciphers.filter( - (c) => !this.vaultFilterService.filterCipherForSelectedVault(c) + (c) => !this.vaultFilterService.filterCipherForSelectedVault(c), ); this.searchPending = false; }, timeout); @@ -301,7 +301,7 @@ export class VaultFilterComponent implements OnInit, OnDestroy { const typeCounts = new Map(); this.deletedCount = this.allCiphers.filter( - (c) => c.isDeleted && !this.vaultFilterService.filterCipherForSelectedVault(c) + (c) => c.isDeleted && !this.vaultFilterService.filterCipherForSelectedVault(c), ).length; this.ciphers?.forEach((c) => { diff --git a/apps/browser/src/vault/popup/components/vault/vault-items.component.ts b/apps/browser/src/vault/popup/components/vault/vault-items.component.ts index 70d9d079c6e..1e29a17981e 100644 --- a/apps/browser/src/vault/popup/components/vault/vault-items.component.ts +++ b/apps/browser/src/vault/popup/components/vault/vault-items.component.ts @@ -64,7 +64,7 @@ export class VaultItemsComponent extends BaseVaultItemsComponent implements OnIn private collectionService: CollectionService, private platformUtilsService: PlatformUtilsService, cipherService: CipherService, - private vaultFilterService: VaultFilterService + private vaultFilterService: VaultFilterService, ) { super(searchService, cipherService); this.applySavedState = @@ -143,7 +143,7 @@ export class VaultItemsComponent extends BaseVaultItemsComponent implements OnIn : null; } await this.load( - (c) => c.collectionIds != null && c.collectionIds.indexOf(this.collectionId) > -1 + (c) => c.collectionIds != null && c.collectionIds.indexOf(this.collectionId) > -1, ); } else { this.showVaultFilter = true; diff --git a/apps/browser/src/vault/popup/components/vault/vault-select.component.ts b/apps/browser/src/vault/popup/components/vault/vault-select.component.ts index 30b5533fcc4..de6a33724d1 100644 --- a/apps/browser/src/vault/popup/components/vault/vault-select.component.ts +++ b/apps/browser/src/vault/popup/components/vault/vault-select.component.ts @@ -32,7 +32,7 @@ import { VaultFilterService } from "../../../services/vault-filter.service"; "void", style({ opacity: 0, - }) + }), ), transition( "void => open", @@ -40,8 +40,8 @@ import { VaultFilterService } from "../../../services/vault-filter.service"; "100ms linear", style({ opacity: 1, - }) - ) + }), + ), ), transition("* => void", animate("100ms linear", style({ opacity: 0 }))), ]), @@ -87,7 +87,7 @@ export class VaultSelectComponent implements OnInit, OnDestroy { private overlay: Overlay, private viewContainerRef: ViewContainerRef, private platformUtilsService: PlatformUtilsService, - private organizationService: OrganizationService + private organizationService: OrganizationService, ) {} @HostListener("document:keydown.escape", ["$event"]) @@ -118,14 +118,14 @@ export class VaultSelectComponent implements OnInit, OnDestroy { this._selectedVault.next(this.i18nService.t(this.vaultFilterService.myVault)); } else if (this.vaultFilterService.vaultFilter.selectedOrganizationId != null) { const selectedOrganization = organizations.find( - (o) => o.id === this.vaultFilterService.vaultFilter.selectedOrganizationId + (o) => o.id === this.vaultFilterService.vaultFilter.selectedOrganizationId, ); this._selectedVault.next(selectedOrganization.name); } else { this._selectedVault.next(this.i18nService.t(this.vaultFilterService.allVaults)); } } - }) + }), ) .pipe(takeUntil(this._destroy)) .subscribe(); @@ -167,7 +167,7 @@ export class VaultSelectComponent implements OnInit, OnDestroy { merge( this.overlayRef.outsidePointerEvents(), this.overlayRef.backdropClick(), - this.overlayRef.detachments() + this.overlayRef.detachments(), // eslint-disable-next-line rxjs-angular/prefer-takeuntil ).subscribe(() => { this.close(); @@ -187,7 +187,7 @@ export class VaultSelectComponent implements OnInit, OnDestroy { this.platformUtilsService.showToast( "error", null, - this.i18nService.t("disabledOrganizationFilterError") + this.i18nService.t("disabledOrganizationFilterError"), ); } else { this._selectedVault.next(organization.name); diff --git a/apps/browser/src/vault/popup/components/vault/view.component.html b/apps/browser/src/vault/popup/components/vault/view.component.html index a4b2ae200c8..dcdb1cab12b 100644 --- a/apps/browser/src/vault/popup/components/vault/view.component.html +++ b/apps/browser/src/vault/popup/components/vault/view.component.html @@ -149,7 +149,7 @@
{{ "typePasskey" | i18n }} {{ "dateCreated" | i18n }} - {{ cipher.login.fido2Credentials[0].creationDate | date : "short" }} + {{ cipher.login.fido2Credentials[0].creationDate | date: "short" }}
@@ -233,10 +233,10 @@ >{{ "number" | i18n }} {{ - cipher.card.maskedNumber | creditCardNumber : cipher.card.brand + cipher.card.maskedNumber | creditCardNumber: cipher.card.brand }} {{ - cipher.card.number | creditCardNumber : cipher.card.brand + cipher.card.number | creditCardNumber: cipher.card.brand }}
@@ -655,15 +655,15 @@