From c9d0cd207e1b3976ec82918085e85378fb918a11 Mon Sep 17 00:00:00 2001 From: Cesar Gonzalez Date: Thu, 25 Jul 2024 14:01:24 -0500 Subject: [PATCH] [PM-2858] Inline menu identity autofill (#9900) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [PM-2857] Introducing logic that handles adding a credit card from the inline menu * [PM-9342] Incorporating logic to handle multiple autocomplete values within a captured set of page details * [PM-9342] Incorporating logic to handle multiple autocomplete values within a captured set of page details * [PM-9342] Changing logic for how we identify new password fields to reflect a more assertive qualification * [PM-2857] Fixing an issue with how we identify ciphers in the inline menu * [PM-2857] Working through issues when adding a cipher from the inline menu for credit card ciphers * [PM-2857] Working through issues when adding a cipher from the inline menu for credit card ciphers * [PM-2857] Fixing an issue encountered with updating credit card info within the add/edit view * [PM-9342] Adding feedback from code review * [PM-5189] Fixing an issue where the port key for an inline menu element could potentially be undefined if the window focus changes too quickly * [PM-2857] Refactoring implementation for how we getCipherViews to ensure we only query card items when necessary * [PM-2857] Refactoring implementation to simplify how we create cipherViews when adding a new item * [PM-2857] Fixing an issue with how we store identity and card cipher views * [PM-2857] Fixing an issue with how we store identity and card cipher views * [PM-2857] Finalizing implementation, writing jest tests, refactoring smaller elements * [PM-2857] Finalizing implementation, writing jest tests, refactoring smaller elements * [PM-2857] Finalizing implementation, writing jest tests, refactoring smaller elements * [PM-2857] Finalizing implementation, writing jest tests, refactoring smaller elements * [PM-2857] Fixing an issue with how we store identity and card cipher views * [PM-2857] Finalizing jest tests * [PM-2857] Finalizing jest tests * [PM-2857] Adjusting an aspect of the inline menu icon * [PM-2857] Adjusting aspect of inline menu field qualification * [PM-2858] Inline menu identities autofill * [PM-2857] Adjusting aspect of inline menu field qualification * [PM-2858] Inline menu identities autofill * [PM-2858] Incorporating logic required to selectively show and fill identity ciphers * [PM-2858] Updating copy for unlock state to be generic * [PM-2857] Updating copy for unlock state to be generic * [PM-2857] Updating copy for unlock state to be generic * [PM-2858] Updating copy for unlock state to be generic * [PM-5189] Fixing an issue where we can potentially show the inline menu incorrectly after a user switches account * [PM-5189] Fixing an issue where we can potentially show the inline menu incorrectly after a user switches account * PM-4950 - Fix hint and verify delete components that had the data in the wrong place (#9877) * PM-4661: Add passkey.username as item.username (#9756) * Add incoming passkey.username as item.username * Driveby fix, was sending wrong username * added username to new-cipher too * Guarded the if-block * Update apps/browser/src/vault/popup/components/vault/add-edit.component.ts Co-authored-by: Justin Baur <19896123+justindbaur@users.noreply.github.com> * Fixed broken test * fixed username on existing ciphers --------- Co-authored-by: Justin Baur <19896123+justindbaur@users.noreply.github.com> * PM-4878: Add passkey information to items when signing in (#9835) * Added username to subtitle * Added subName to cipher * Moved subName to component * Update apps/browser/src/vault/popup/components/fido2/fido2-cipher-row.component.ts Co-authored-by: SmithThe4th * Fixed double code and added comment * Added changeDetection: ChangeDetectionStrategy.OnPush as per review --------- Co-authored-by: SmithThe4th * [AC-2791] Members page - finish component library refactors (#9727) * Replace PlatformUtilsService with ToastService * Remove unneeded templates * Implement table filtering function * Move member-only methods from base class to subclass * Move utility functions inside new MemberTableDataSource * Rename PeopleComponent to MembersComponent * [deps] Platform: Update angular-cli monorepo to v16.2.14 (#9380) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * [PM-8789] Move desktop_native into subcrate (#9682) * Move desktop_native into subcrate * Add publish = false to crates * [PM-6394] remove policy evaluator cache (#9807) * [PM-9364] Copy for Aggregate auto-scaling invoices for Teams and Enterprise customers (#9875) * Change the seat adjustment message * Move changes from en_GB file to en file * revert changes in en_GB file * Add feature flag to the change * use user verification as a part of key rotation (#9722) * Add the ability for custom validation logic to be injected into `UserVerificationDialogComponent` (#8770) * Introduce `verificationType` * Update template to use `verificationType` * Implement a path for `verificationType = 'custom'` * Delete `clientSideOnlyVerification` * Update `EnrollMasterPasswordResetComponent` to include a server-side hash check * Better describe the custom scenerio through comments * Add an example of the custom verficiation scenerio * Move execution of verification function into try/catch * Migrate existing uses of `clientSideOnlyVerification` * Use generic type option instead of casting * Change "given" to "determined" in a comment * Restructure the `org-redirect` guard to be Angular 17+ compliant (#9552) * Document the `org-redirect` guard in code * Make assertions about the way the `org-redirect` guard should behave * Restructure the `org-redirect` guard to be Angular 17+ compliant * Convert data parameter to function parameter * Convert a data parameter to a function parameter that was missed * Pass redirect function to default organization route * don't initialize kdf with validators, do it on first set (#9754) * add testids for attachments (#9892) * Bug fix - error toast in 2fa (#9623) * Bug fix - error toast in 2fa * Bug fix - Yubikey code obscured * 2FA error fix * [PM-5189] Fixing an issue where we can potentially show the inline menu incorrectly after a user switches account * [PM-5189] Fixing an issue where we can potentially show the inline menu incorrectly after a user switches account * [PM-5189] Fixing an issue where we can potentially show the inline menu incorrectly after a user switches account * [PM-2858] Fixing icon color * [PM-2858] Adding subtitle for identity inline menu list items * [PM-2858] Fixing jest tests * [PM-2858] Working through implementation of conditional identity fill logic on inline menu * [PM-2858] Working through implementation of conditional identity fill logic on inline menu * [PM-2858] Working through implementation of conditional identity fill logic on inline menu * [PM-2858] Working through implementation of conditional identity fill logic on inline menu * [PM-2858] Working through implementation of conditional identity fill logic on inline menu * [PM-2858] Working through implementation of conditional identity fill logic on inline menu * [PM-2858] Working through implementation of conditional identity fill logic on inline menu * [PM-2858] Working through implementation of conditional identity fill logic on inline menu * [PM-2858] Working through identity field qualification for the inline menu * [PM-2858] Working through identity field qualification for the inline menu * [PM-2858] Working through identity field qualification for the inline menu * [PM-2858] Working through identity field qualification for the inline menu * [PM-2858] Working through identity field qualification for the inline menu * [PM-2858] Working through identity field qualification for the inline menu * [PM-2858] Scaffolding add new identity logic * [PM-2858] Implementing add new identity * [PM-2858] Implementing add new identity * [PM-2858] Scaffolding add new identity logic * [PM-2858] Scaffolding add new identity logic * [PM-2858] Scaffolding add new identity logic * [PM-2857] Fixing an issue with how we parse the last digits for credit card aria description * [PM-2857] Setting up logic to ensrue we use a set email address as a fallback for a username * [PM-2857] Fixing an issue with how we parse the last digits for credit card aria description * [PM-2858] Reverting forced email address in inline menu identity autofill * Restructure the `is-paid-org` guard to be Angular 17+ compliant (#9598) * Document that `is-paid-org` guard in code * Remove unused `MessagingService` dependency * Make assertions about the way the is-paid-org guard should behave * Restructure the `is-paid-org` guard to be Angular 17+ compliant * Random commit to get the build job moving * Undo previous commit * Bumped client version(s) (#9895) * [PM-9344] Clarify accepted user state (#9861) * Prefer `Needs confirmation` to `Accepted` display status This emphasizes that action is still required to complete setup. * Remove unused message * Bumped client version(s) (#9906) * Revert "Bumped client version(s) (#9906)" (#9907) This reverts commit 78c28297938eda53e7731fdf9f63d7baa7068d0d. * fix duo subscriptions and org vs individual duo setup (#9859) * [PM-5024] Migrate tax-info component (#9872) * Changes for the tax info migration * Return for invalid formgroup * Restructure the `org-permissions` guard to be Angular 17+ compliant (#9631) * Document the `org-permissions` guard in code * Restructure the `org-permissions` guard to be Angular 17+ compliant * Update the `org-permissions` guard to use `ToastService` * Simplify callback function sigantures * Remove unused test object * Fix updated route from merge * Restructure the `provider-permissions` guard to be Angular 17+ compliant (#9609) * Document the `provider-permissions` guard in code * Restructure the `provider-permissions` guard to be Angular 17+ compliant * [deps] Platform: Update @types/argon2-browser to v1.18.4 (#8180) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * Bumped client version(s) (#9914) * [PM-7162] Cipher Form - Item Details (#9758) * [PM-7162] Fix weird angular error regarding disabled component bit-select * [PM-7162] Introduce CipherFormConfigService and related types * [PM-7162] Introduce CipherFormService * [PM-7162] Introduce the Item Details section component and the CipherFormContainer interface * [PM-7162] Introduce the CipherForm component * [PM-7162] Add strongly typed QueryParams to the add-edit-v2.component * [PM-7162] Export CipherForm from Vault Lib * [PM-7162] Use the CipherForm in Browser AddEditV2 * [PM-7162] Introduce CipherForm storybook * [PM-7162] Remove VaultPopupListFilterService dependency from NewItemDropDownV2 component * [PM-7162] Add support for content projection of attachment button * [PM-7162] Fix typo * [PM-7162] Cipher form service cleanup * [PM-7162] Move readonly collection notice to bit-hint * [PM-7162] Refactor CipherFormConfig type to enforce required properties with Typescript * [PM-7162] Fix storybook after config changes * [PM-7162] Use new add-edit component for clone route * [deps]: Update @yao-pkg/pkg to ^5.12.0 (#9820) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * Autosync the updated translations (#9922) Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com> * Autosync the updated translations (#9923) Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com> * Autosync the updated translations (#9924) Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com> * [AC-2830] Unable to create a free organization (#9917) * Resolve the issue free org creation * Check that the taxForm is touched * [PM-7162] Fix broken getter when original cipher is null (#9927) * [PM-8525] Edit Card (#9901) * initial add of card details section * add card number * update card brand when the card number changes * add year and month fields * add security code field * hide number and security code by default * add `id` for all form fields * update select options to match existing options * make year input numerical * only display card details for card ciphers * use style to set input height * handle numerical values for year * update heading when a brand is available * remove unused ref * use cardview types for the form * fix numerical input type * disable card details when in partial-edit mode * remove hardcoded height * update types for formBuilder * [PM-9440] Fix: handle undefined value in migration 66 (#9908) * fix: handle undefined value in migration 66 * fix: the if-statement was typo * Rename "encryptionAlgorithm" to "hashAlgorithmForEncryption" for clarity (#9891) * [PM-7972] Account switching integration with "remember email" functionality (#9750) * add account switching logic to login email service * enforce boolean and fix desktop account switcher order * [PM-9442] Add tests for undefined state values and proper emulation of ElectronStorageService in tests (#9910) * fix: handle undefined value in migration 66 * fix: the if-statement was typo * feat: duplicate error behavior in fake storage service * feat: fix all migrations that were setting undefined values * feat: add test for disabled fingrint in migration 66 * fix: default single user state saving undefined value to state * revert: awaiting floating promise gonna fix this in a separate PR * Revert "feat: fix all migrations that were setting undefined values" This reverts commit 034713256cee9a8e164295c88157fe33d8372c81. * feat: automatically convert save to remove * Revert "fix: default single user state saving undefined value to state" This reverts commit 6c36da6ba52f6886d0de2b502b3aaff7f122c3a7. * [AC-2805] Consolidated Billing UI Updates (#9893) * Add empty state for invoices * Make cards on create client dialog tabbable * Add space in $ / month per member * Mute text, remove (Monthly) and right align menu on clients table * Made used seats account for all users and fixed column sort for used/remaining * Resize pricing cards * Rename assignedSeats to occupiedSeats * [PM-9460][deps] Tools: Update electron to v31 (#9921) * [deps] Tools: Update electron to v31 * Bump version in electron-builder --------- Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Daniel James Smith * [AC-1452] Restrict access to 'Organization Info' and 'Two-Step Login' settings pages with a permission check (#9483) * Guard Organization Info route - Owners only * Guard TwoFactor route - Owners only and Organization must be able to use 2FA * Update guards to use function syntax --------- Co-authored-by: Addison Beck * [PM-9437] Use CollectionAccessDetailsResponse type now that is always the type returned from the API (#9951) * Add required env variables to desktop native build script (#9869) * [AC-2676] Remove paging logic from GroupsComponent (#9705) * remove infinite scroll, use virtual scroll instead * use TableDataSource for search * allow sorting by name * replacing PlatformUtilsService.showToast with ToastService * misc FIXMEs * [PM-9441] Catch and log exceptions during migration (#9905) * feat: catch and log exceptions during migration * Revert "feat: catch and log exceptions during migration" This reverts commit d68733b7e58120298974b350e496bb3e0c9af0d2. * feat: use log service to log migration errors * Autosync the updated translations (#9972) Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com> * Autosync the updated translations (#9973) Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com> * Updated codeowners for new design system team (#9913) * Updated codeowners for new design system team. * Moved Angular and Bootstrap dependencies * Moved additional dependencies. * Updated ownership Co-authored-by: Will Martin --------- Co-authored-by: Will Martin * [SM-1016] Fix new access token dialog (#9918) * swap to bit-dialog title & subtitle * remove dialogRef.disableClose & use toastService * Add shared two-factor-options component (#9767) * Communicate the upcoming client vault privacy changes to MSPs (#9994) * Add a banner notification to the provider portal * Feature flag the banner * Move banner copy to messages.json * Allow for dismissing the banner * Auth/PM-7321 - Registration with Email Verification - Registration Finish Component Implementation (#9653) * PM-7321 - Temp add input password * PM-7321 - update input password based on latest PR changes to test. * PM-7321 - Progress on testing input password component + RegistrationFinishComponent checks * PM-7321 - more progress on registration finish. * PM-7321 - Wire up RegistrationFinishRequest model + AccountApiService abstraction + implementation changes for new method. * PM-7321 - WIP Registration Finish - wiring up request building and API call on submit. * PM-7321 - WIP registratin finish * PM-7321 - WIP on creating registration-finish service + web override to add org invite handling * PM-7321 - (1) Move web-registration-finish svc to web (2) Wire up exports (3) wire up RegistrationFinishComponent to call registration finish service * PM-7321 - Get CLI building * PM-7321 - Move all finish registration service and content to registration-finish feature folder. * PM-7321 - Fix RegistrationFinishService config * PM-7321 - RegistrationFinishComponent- handlePasswordFormSubmit - error handling WIP * PM-7321 - InputPasswordComp - Update to accept masterPasswordPolicyOptions as input instead of retrieving it as parent components in different scenarios will need to retrieve the policies differently (e.g., orgInvite token in registration vs direct call via org id post SSO on set password) * PM-7321 - Registration Finish - Add web specific logic for retrieving master password policies and passing them into the input password component. * PM-7321 - Registration Start - Send email via query param to registration finish page so it can create masterKey * PM-7321 - InputPassword comp - (1) Add loading input (2) Add email validation to submit logic. * PM-7321 - Registration Finish - Add submitting state and pass into input password so that the rest of the registration process keeps the child form disabled. * PM-7321 - Registration Finish - use validation service for error handling. * PM-7321 - All register routes must be dynamic and change if the feature flag changes. * PM-7321 - Test registration finish services. * PM-7321 - RegisterRouteService - Add comment documenting why the service exists. * PM-7321 - Add missing input password translations to browser & desktop * PM-7321 - WebRegistrationFinishSvc - apply PR feedback * [deps] Autofill: Update rimraf to v5.0.8 (#10008) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * [PM-9318] Fix username on protonpass import (#9889) * Fix username field used for ProtonPass import ProtonPass has changed their export format and userName is not itemEmail * Import additional field itemUsername --------- Co-authored-by: Daniel James Smith * [PM-8943] Update QRious script initialization in Authenticator two-factor provider (#9926) * create onload() for qrious as well as error messaging if QR code cannot be displayed * button and message updates and formpromise removal * load QR script async * rename and reorder methods * Delete Unused Bits of StateService (#9858) * Delete Unused Bits of StateService * Fix Tests * remove getBgService for auth request service (#10020) * [PM-2858] Fixing an issue found when the first or last names of an identity are not filled * [PM-2858] Fixing an issue found where keyboard navigation can potentially close the inline menu * [PM-2858] Fixing jest tests within inline menu list * [PM-2858] Fixing jest tests within inline menu list * [PM-2858] Setting up login items to be presented when an account creation form is shown to the user * [PM-2858] Refactoring implementation used for creating the inline menu cipher data * [PM-2858] Refactoring implementation used for creating the inline menu cipher data * [PM-2858] Refactoring implementation used for creating the inline menu cipher data * [PM-2858] Refactoring implementation * [PM-2858] Refactoring implementation * [PM-2858] Refactoring implementation * [PM-2858] Refactoring implementation * [PM-2858] Changing how we populate login ciphers within create account * [PM-2858] Adding documentation * [PM-2858] Working through jest tests for the OverlayBackground * [PM-2858] Working through jest tests for the OverlayBackground * [PM-2858] Working through jest tests for the AutofillInlineMenuList class * [PM-2858] Adding documentation to inline menu list methods * [PM-2857] Fixing a jest test * [PM-2858] Fixing jest tests within inline menu list * [PM-2858] Addressing jest tests within AutofillOverlayContentService * [PM-2858] Addressing jest tests within AutofillOverlayContentService * [PM-2858] Addressing jest tests within InlineMenuFieldQualificationService * [PM-9267] Implement feature flag for inline menu re-architecture (#9845) * [PM-9267] Implement Feature Flag for Inline Menu Re-Architecture * [PM-9267] Incorporating legacy OverlayBackground implementation * [PM-9267] Incorporating legacy overlay content scripts * [PM-9267] Incorporating legacy overlay content scripts * [PM-9267] Incorporating legacy overlay content scripts * [PM-9267] Incorporating legacy overlay content scripts * [PM-9267] Finalizing feature flag implementation * [PM-9267] Finalizing feature flag implementation * [PM-9267] Finalizing feature flag implementation * [PM-9267] Finalizing feature flag implementation * [PM-9267] Finalizing feature flag implementation * [PM-9267] Finalizing feature flag implementation * [PM-9267] Finalizing feature flag implementation * [PM-9267] Finalizing feature flag implementation * [PM-9267] Adjusting naming convention for page files * [PM-9267] Adjusting naming convention for page files * [PM-5189] Fixing an issue where we can potentially show the inline menu incorrectly after a user switches account * PM-4950 - Fix hint and verify delete components that had the data in the wrong place (#9877) * PM-4661: Add passkey.username as item.username (#9756) * Add incoming passkey.username as item.username * Driveby fix, was sending wrong username * added username to new-cipher too * Guarded the if-block * Update apps/browser/src/vault/popup/components/vault/add-edit.component.ts Co-authored-by: Justin Baur <19896123+justindbaur@users.noreply.github.com> * Fixed broken test * fixed username on existing ciphers --------- Co-authored-by: Justin Baur <19896123+justindbaur@users.noreply.github.com> * PM-4878: Add passkey information to items when signing in (#9835) * Added username to subtitle * Added subName to cipher * Moved subName to component * Update apps/browser/src/vault/popup/components/fido2/fido2-cipher-row.component.ts Co-authored-by: SmithThe4th * Fixed double code and added comment * Added changeDetection: ChangeDetectionStrategy.OnPush as per review --------- Co-authored-by: SmithThe4th * [AC-2791] Members page - finish component library refactors (#9727) * Replace PlatformUtilsService with ToastService * Remove unneeded templates * Implement table filtering function * Move member-only methods from base class to subclass * Move utility functions inside new MemberTableDataSource * Rename PeopleComponent to MembersComponent * [deps] Platform: Update angular-cli monorepo to v16.2.14 (#9380) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * [PM-8789] Move desktop_native into subcrate (#9682) * Move desktop_native into subcrate * Add publish = false to crates * [PM-6394] remove policy evaluator cache (#9807) * [PM-9364] Copy for Aggregate auto-scaling invoices for Teams and Enterprise customers (#9875) * Change the seat adjustment message * Move changes from en_GB file to en file * revert changes in en_GB file * Add feature flag to the change * use user verification as a part of key rotation (#9722) * Add the ability for custom validation logic to be injected into `UserVerificationDialogComponent` (#8770) * Introduce `verificationType` * Update template to use `verificationType` * Implement a path for `verificationType = 'custom'` * Delete `clientSideOnlyVerification` * Update `EnrollMasterPasswordResetComponent` to include a server-side hash check * Better describe the custom scenerio through comments * Add an example of the custom verficiation scenerio * Move execution of verification function into try/catch * Migrate existing uses of `clientSideOnlyVerification` * Use generic type option instead of casting * Change "given" to "determined" in a comment * Restructure the `org-redirect` guard to be Angular 17+ compliant (#9552) * Document the `org-redirect` guard in code * Make assertions about the way the `org-redirect` guard should behave * Restructure the `org-redirect` guard to be Angular 17+ compliant * Convert data parameter to function parameter * Convert a data parameter to a function parameter that was missed * Pass redirect function to default organization route * don't initialize kdf with validators, do it on first set (#9754) * add testids for attachments (#9892) * Bug fix - error toast in 2fa (#9623) * Bug fix - error toast in 2fa * Bug fix - Yubikey code obscured * 2FA error fix * Restructure the `is-paid-org` guard to be Angular 17+ compliant (#9598) * Document that `is-paid-org` guard in code * Remove unused `MessagingService` dependency * Make assertions about the way the is-paid-org guard should behave * Restructure the `is-paid-org` guard to be Angular 17+ compliant * Random commit to get the build job moving * Undo previous commit * Bumped client version(s) (#9895) * [PM-9344] Clarify accepted user state (#9861) * Prefer `Needs confirmation` to `Accepted` display status This emphasizes that action is still required to complete setup. * Remove unused message * Bumped client version(s) (#9906) * Revert "Bumped client version(s) (#9906)" (#9907) This reverts commit 78c28297938eda53e7731fdf9f63d7baa7068d0d. * fix duo subscriptions and org vs individual duo setup (#9859) * [PM-5024] Migrate tax-info component (#9872) * Changes for the tax info migration * Return for invalid formgroup * Restructure the `org-permissions` guard to be Angular 17+ compliant (#9631) * Document the `org-permissions` guard in code * Restructure the `org-permissions` guard to be Angular 17+ compliant * Update the `org-permissions` guard to use `ToastService` * Simplify callback function sigantures * Remove unused test object * Fix updated route from merge * Restructure the `provider-permissions` guard to be Angular 17+ compliant (#9609) * Document the `provider-permissions` guard in code * Restructure the `provider-permissions` guard to be Angular 17+ compliant * [deps] Platform: Update @types/argon2-browser to v1.18.4 (#8180) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * Bumped client version(s) (#9914) * [PM-7162] Cipher Form - Item Details (#9758) * [PM-7162] Fix weird angular error regarding disabled component bit-select * [PM-7162] Introduce CipherFormConfigService and related types * [PM-7162] Introduce CipherFormService * [PM-7162] Introduce the Item Details section component and the CipherFormContainer interface * [PM-7162] Introduce the CipherForm component * [PM-7162] Add strongly typed QueryParams to the add-edit-v2.component * [PM-7162] Export CipherForm from Vault Lib * [PM-7162] Use the CipherForm in Browser AddEditV2 * [PM-7162] Introduce CipherForm storybook * [PM-7162] Remove VaultPopupListFilterService dependency from NewItemDropDownV2 component * [PM-7162] Add support for content projection of attachment button * [PM-7162] Fix typo * [PM-7162] Cipher form service cleanup * [PM-7162] Move readonly collection notice to bit-hint * [PM-7162] Refactor CipherFormConfig type to enforce required properties with Typescript * [PM-7162] Fix storybook after config changes * [PM-7162] Use new add-edit component for clone route * [deps]: Update @yao-pkg/pkg to ^5.12.0 (#9820) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * Autosync the updated translations (#9922) Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com> * Autosync the updated translations (#9923) Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com> * Autosync the updated translations (#9924) Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com> * [AC-2830] Unable to create a free organization (#9917) * Resolve the issue free org creation * Check that the taxForm is touched * [PM-7162] Fix broken getter when original cipher is null (#9927) * [PM-8525] Edit Card (#9901) * initial add of card details section * add card number * update card brand when the card number changes * add year and month fields * add security code field * hide number and security code by default * add `id` for all form fields * update select options to match existing options * make year input numerical * only display card details for card ciphers * use style to set input height * handle numerical values for year * update heading when a brand is available * remove unused ref * use cardview types for the form * fix numerical input type * disable card details when in partial-edit mode * remove hardcoded height * update types for formBuilder * [PM-9440] Fix: handle undefined value in migration 66 (#9908) * fix: handle undefined value in migration 66 * fix: the if-statement was typo * Rename "encryptionAlgorithm" to "hashAlgorithmForEncryption" for clarity (#9891) * [PM-7972] Account switching integration with "remember email" functionality (#9750) * add account switching logic to login email service * enforce boolean and fix desktop account switcher order * [PM-9442] Add tests for undefined state values and proper emulation of ElectronStorageService in tests (#9910) * fix: handle undefined value in migration 66 * fix: the if-statement was typo * feat: duplicate error behavior in fake storage service * feat: fix all migrations that were setting undefined values * feat: add test for disabled fingrint in migration 66 * fix: default single user state saving undefined value to state * revert: awaiting floating promise gonna fix this in a separate PR * Revert "feat: fix all migrations that were setting undefined values" This reverts commit 034713256cee9a8e164295c88157fe33d8372c81. * feat: automatically convert save to remove * Revert "fix: default single user state saving undefined value to state" This reverts commit 6c36da6ba52f6886d0de2b502b3aaff7f122c3a7. * [AC-2805] Consolidated Billing UI Updates (#9893) * Add empty state for invoices * Make cards on create client dialog tabbable * Add space in $ / month per member * Mute text, remove (Monthly) and right align menu on clients table * Made used seats account for all users and fixed column sort for used/remaining * Resize pricing cards * Rename assignedSeats to occupiedSeats * [PM-9460][deps] Tools: Update electron to v31 (#9921) * [deps] Tools: Update electron to v31 * Bump version in electron-builder --------- Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Daniel James Smith * [AC-1452] Restrict access to 'Organization Info' and 'Two-Step Login' settings pages with a permission check (#9483) * Guard Organization Info route - Owners only * Guard TwoFactor route - Owners only and Organization must be able to use 2FA * Update guards to use function syntax --------- Co-authored-by: Addison Beck * [PM-9437] Use CollectionAccessDetailsResponse type now that is always the type returned from the API (#9951) * Add required env variables to desktop native build script (#9869) * [AC-2676] Remove paging logic from GroupsComponent (#9705) * remove infinite scroll, use virtual scroll instead * use TableDataSource for search * allow sorting by name * replacing PlatformUtilsService.showToast with ToastService * misc FIXMEs * [PM-9441] Catch and log exceptions during migration (#9905) * feat: catch and log exceptions during migration * Revert "feat: catch and log exceptions during migration" This reverts commit d68733b7e58120298974b350e496bb3e0c9af0d2. * feat: use log service to log migration errors * Autosync the updated translations (#9972) Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com> * Autosync the updated translations (#9973) Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com> * Updated codeowners for new design system team (#9913) * Updated codeowners for new design system team. * Moved Angular and Bootstrap dependencies * Moved additional dependencies. * Updated ownership Co-authored-by: Will Martin --------- Co-authored-by: Will Martin * [SM-1016] Fix new access token dialog (#9918) * swap to bit-dialog title & subtitle * remove dialogRef.disableClose & use toastService * Add shared two-factor-options component (#9767) * Communicate the upcoming client vault privacy changes to MSPs (#9994) * Add a banner notification to the provider portal * Feature flag the banner * Move banner copy to messages.json * Allow for dismissing the banner * Auth/PM-7321 - Registration with Email Verification - Registration Finish Component Implementation (#9653) * PM-7321 - Temp add input password * PM-7321 - update input password based on latest PR changes to test. * PM-7321 - Progress on testing input password component + RegistrationFinishComponent checks * PM-7321 - more progress on registration finish. * PM-7321 - Wire up RegistrationFinishRequest model + AccountApiService abstraction + implementation changes for new method. * PM-7321 - WIP Registration Finish - wiring up request building and API call on submit. * PM-7321 - WIP registratin finish * PM-7321 - WIP on creating registration-finish service + web override to add org invite handling * PM-7321 - (1) Move web-registration-finish svc to web (2) Wire up exports (3) wire up RegistrationFinishComponent to call registration finish service * PM-7321 - Get CLI building * PM-7321 - Move all finish registration service and content to registration-finish feature folder. * PM-7321 - Fix RegistrationFinishService config * PM-7321 - RegistrationFinishComponent- handlePasswordFormSubmit - error handling WIP * PM-7321 - InputPasswordComp - Update to accept masterPasswordPolicyOptions as input instead of retrieving it as parent components in different scenarios will need to retrieve the policies differently (e.g., orgInvite token in registration vs direct call via org id post SSO on set password) * PM-7321 - Registration Finish - Add web specific logic for retrieving master password policies and passing them into the input password component. * PM-7321 - Registration Start - Send email via query param to registration finish page so it can create masterKey * PM-7321 - InputPassword comp - (1) Add loading input (2) Add email validation to submit logic. * PM-7321 - Registration Finish - Add submitting state and pass into input password so that the rest of the registration process keeps the child form disabled. * PM-7321 - Registration Finish - use validation service for error handling. * PM-7321 - All register routes must be dynamic and change if the feature flag changes. * PM-7321 - Test registration finish services. * PM-7321 - RegisterRouteService - Add comment documenting why the service exists. * PM-7321 - Add missing input password translations to browser & desktop * PM-7321 - WebRegistrationFinishSvc - apply PR feedback * [deps] Autofill: Update rimraf to v5.0.8 (#10008) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * [PM-9318] Fix username on protonpass import (#9889) * Fix username field used for ProtonPass import ProtonPass has changed their export format and userName is not itemEmail * Import additional field itemUsername --------- Co-authored-by: Daniel James Smith * [PM-8943] Update QRious script initialization in Authenticator two-factor provider (#9926) * create onload() for qrious as well as error messaging if QR code cannot be displayed * button and message updates and formpromise removal * load QR script async * rename and reorder methods * Delete Unused Bits of StateService (#9858) * Delete Unused Bits of StateService * Fix Tests * remove getBgService for auth request service (#10020) --------- Co-authored-by: Jared Snider <116684653+JaredSnider-Bitwarden@users.noreply.github.com> Co-authored-by: Anders Åberg Co-authored-by: Justin Baur <19896123+justindbaur@users.noreply.github.com> Co-authored-by: SmithThe4th Co-authored-by: Thomas Rittson <31796059+eliykat@users.noreply.github.com> Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Daniel García Co-authored-by: ✨ Audrey ✨ Co-authored-by: cyprain-okeke <108260115+cyprain-okeke@users.noreply.github.com> Co-authored-by: Jake Fink Co-authored-by: Addison Beck Co-authored-by: Nick Krantz <125900171+nick-livefront@users.noreply.github.com> Co-authored-by: vinith-kovan <156108204+vinith-kovan@users.noreply.github.com> Co-authored-by: Bitwarden DevOps <106330231+bitwarden-devops-bot@users.noreply.github.com> Co-authored-by: Matt Gibson Co-authored-by: Opeyemi Co-authored-by: Shane Melton Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Andreas Coroiu Co-authored-by: Bernd Schoolmann Co-authored-by: Alex Morask <144709477+amorask-bitwarden@users.noreply.github.com> Co-authored-by: Daniel James Smith Co-authored-by: Rui Tomé <108268980+r-tome@users.noreply.github.com> Co-authored-by: Addison Beck Co-authored-by: Todd Martin <106564991+trmartin4@users.noreply.github.com> Co-authored-by: Will Martin Co-authored-by: Thomas Avery <43214426+Thomas-Avery@users.noreply.github.com> Co-authored-by: Daniel James Smith <2670567+djsmith85@users.noreply.github.com> Co-authored-by: Ike <137194738+ike-kottlowski@users.noreply.github.com> * [PM-2858] Fixing an issue found where password fields addedin new account forms do not properly pull their value into the add cipher flow * [PM-2858] Adjusting scrollbar stylings * [PM-2858] Adjusting how we handle instantiating the feature flag guarded overlay background and how we handle instantiating identities and card ciphers in the inline menu * [PM-2858] Adjusting how we handle instantiating the feature flag guarded overlay background and how we handle instantiating identities and card ciphers in the inline menu * [PM-2858] Adjusting how we handle instantiating the feature flag guarded overlay background and how we handle instantiating identities and card ciphers in the inline menu * [PM-2858] Incorporating some changes that ensure the inline menu list fades in as expected * [PM-2858] Incorporating some changes that ensure the inline menu list fades in as expected * [PM-2858] Incorporating some changes that ensure the inline menu list fades in as expected * [PM-2858] Adjusting how we inject translations for a couple of aria label elements * [PM-2858] Fixing duplicate globalThis references --------- Co-authored-by: Jared Snider <116684653+JaredSnider-Bitwarden@users.noreply.github.com> Co-authored-by: Anders Åberg Co-authored-by: Justin Baur <19896123+justindbaur@users.noreply.github.com> Co-authored-by: SmithThe4th Co-authored-by: Thomas Rittson <31796059+eliykat@users.noreply.github.com> Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Daniel García Co-authored-by: ✨ Audrey ✨ Co-authored-by: cyprain-okeke <108260115+cyprain-okeke@users.noreply.github.com> Co-authored-by: Jake Fink Co-authored-by: Addison Beck Co-authored-by: Nick Krantz <125900171+nick-livefront@users.noreply.github.com> Co-authored-by: vinith-kovan <156108204+vinith-kovan@users.noreply.github.com> Co-authored-by: Bitwarden DevOps <106330231+bitwarden-devops-bot@users.noreply.github.com> Co-authored-by: Matt Gibson Co-authored-by: Opeyemi Co-authored-by: Shane Melton Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Andreas Coroiu Co-authored-by: Bernd Schoolmann Co-authored-by: Alex Morask <144709477+amorask-bitwarden@users.noreply.github.com> Co-authored-by: Daniel James Smith Co-authored-by: Rui Tomé <108268980+r-tome@users.noreply.github.com> Co-authored-by: Addison Beck Co-authored-by: Todd Martin <106564991+trmartin4@users.noreply.github.com> Co-authored-by: Will Martin Co-authored-by: Thomas Avery <43214426+Thomas-Avery@users.noreply.github.com> Co-authored-by: Daniel James Smith <2670567+djsmith85@users.noreply.github.com> Co-authored-by: Ike <137194738+ike-kottlowski@users.noreply.github.com> --- apps/browser/src/_locales/en/messages.json | 20 +- .../abstractions/overlay.background.ts | 29 +- .../background/overlay.background.spec.ts | 346 ++++++++- .../autofill/background/overlay.background.ts | 331 ++++++++- .../content/autofill-init.deprecated.spec.ts | 11 +- ...ofill-overlay-list.deprecated.spec.ts.snap | 9 +- .../autofill/enums/autofill-field.enums.ts | 29 + .../src/autofill/models/autofill-field.ts | 6 + .../abstractions/autofill-inline-menu-list.ts | 2 + .../autofill-inline-menu-content.service.ts | 7 +- .../autofill-inline-menu-iframe.service.ts | 19 +- .../autofill-inline-menu-list.spec.ts.snap | 693 ++++++++++++++++-- .../list/autofill-inline-menu-list.spec.ts | 165 ++++- .../pages/list/autofill-inline-menu-list.ts | 260 +++++-- .../overlay/inline-menu/pages/list/list.scss | 57 +- .../autofill-inline-menu-page-element.ts | 6 +- ...nline-menu-field-qualifications.service.ts | 21 + .../autofill/services/autofill-constants.ts | 16 +- .../autofill-overlay-content.service.spec.ts | 398 +++++++++- .../autofill-overlay-content.service.ts | 214 +++++- .../src/autofill/services/autofill.service.ts | 5 +- .../collect-autofill-content.service.ts | 4 +- ...e-menu-field-qualification.service.spec.ts | 73 +- ...inline-menu-field-qualification.service.ts | 479 +++++++++++- .../src/autofill/spec/autofill-mocks.ts | 9 +- apps/browser/src/autofill/utils/index.spec.ts | 23 +- apps/browser/src/autofill/utils/index.ts | 12 +- apps/browser/src/autofill/utils/svg-icons.ts | 2 +- .../browser/src/background/main.background.ts | 96 +-- 29 files changed, 3020 insertions(+), 322 deletions(-) create mode 100644 apps/browser/src/autofill/enums/autofill-field.enums.ts diff --git a/apps/browser/src/_locales/en/messages.json b/apps/browser/src/_locales/en/messages.json index d908b267f4..bf2b26a11b 100644 --- a/apps/browser/src/_locales/en/messages.json +++ b/apps/browser/src/_locales/en/messages.json @@ -3054,6 +3054,10 @@ "message": "Unlock account", "description": "Button text to display in overlay when the account is locked." }, + "unlockAccountAria": { + "message": "Unlock your account, opens in a new window", + "description": "Screen reader text (aria-label) for unlock account button in overlay" + }, "fillCredentialsFor": { "message": "Fill credentials for", "description": "Screen reader text for when overlay item is in focused" @@ -3078,18 +3082,26 @@ "message": "New login", "description": "Button text to display within inline menu when there are no matching items on a login field" }, - "addNewLoginItem": { - "message": "Add new vault login item", + "addNewLoginItemAria": { + "message": "Add new vault login item, opens in a new window", "description": "Screen reader text (aria-label) for new login button within inline menu" }, "newCard": { "message": "New card", "description": "Button text to display within inline menu when there are no matching items on a credit card field" }, - "addNewCardItem": { - "message": "Add new vault card item", + "addNewCardItemAria": { + "message": "Add new vault card item, opens in a new window", "description": "Screen reader text (aria-label) for new card button within inline menu" }, + "newIdentity": { + "message": "New identity", + "description": "Button text to display within inline menu when there are no matching items on an identity field" + }, + "addNewIdentityItemAria": { + "message": "Add new vault identity item, opens in a new window", + "description": "Screen reader text (aria-label) for new identity button within inline menu" + }, "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" diff --git a/apps/browser/src/autofill/background/abstractions/overlay.background.ts b/apps/browser/src/autofill/background/abstractions/overlay.background.ts index 763261cae2..f1bfd3642f 100644 --- a/apps/browser/src/autofill/background/abstractions/overlay.background.ts +++ b/apps/browser/src/autofill/background/abstractions/overlay.background.ts @@ -37,6 +37,8 @@ export type FocusedFieldData = { filledByCipherType?: CipherType; tabId?: number; frameId?: number; + accountCreationFieldType?: string; + showInlineMenuAccountCreation?: boolean; }; export type InlineMenuElementPosition = { @@ -67,10 +69,30 @@ export type NewCardCipherData = { cvv: string; }; +export type NewIdentityCipherData = { + title: string; + firstName: string; + middleName: string; + lastName: string; + fullName: string; + address1: string; + address2: string; + address3: string; + city: string; + state: string; + postalCode: string; + country: string; + company: string; + phone: string; + email: string; + username: string; +}; + export type OverlayAddNewItemMessage = { addNewCipherType?: CipherType; login?: NewLoginCipherData; card?: NewCardCipherData; + identity?: NewIdentityCipherData; }; export type CloseInlineMenuMessage = { @@ -115,8 +137,13 @@ export type InlineMenuCipherData = { reprompt: CipherRepromptType; favorite: boolean; icon: WebsiteIconData; + accountCreationFieldType?: string; login?: { username: string }; card?: string; + identity?: { + fullName: string; + username?: string; + }; }; export type BackgroundMessageParam = { @@ -180,7 +207,7 @@ export type PortOnMessageHandlerParams = PortMessageParam & PortConnectionParam; export type InlineMenuButtonPortMessageHandlers = { [key: string]: CallableFunction; - triggerDelayedAutofillInlineMenuClosure: ({ port }: PortConnectionParam) => void; + triggerDelayedAutofillInlineMenuClosure: () => void; autofillInlineMenuButtonClicked: ({ port }: PortConnectionParam) => void; autofillInlineMenuBlurred: () => void; redirectAutofillInlineMenuFocusOut: ({ message, port }: PortOnMessageHandlerParams) => void; diff --git a/apps/browser/src/autofill/background/overlay.background.spec.ts b/apps/browser/src/autofill/background/overlay.background.spec.ts index de668cd817..3a3bb7dd5e 100644 --- a/apps/browser/src/autofill/background/overlay.background.spec.ts +++ b/apps/browser/src/autofill/background/overlay.background.spec.ts @@ -43,11 +43,11 @@ import { } from "../enums/autofill-overlay.enum"; import { AutofillService } from "../services/abstractions/autofill.service"; import { - createChromeTabMock, createAutofillPageDetailsMock, - createPortSpyMock, + createChromeTabMock, createFocusedFieldDataMock, createPageDetailMock, + createPortSpyMock, } from "../spec/autofill-mocks"; import { flushPromises, @@ -713,9 +713,22 @@ describe("OverlayBackground", () => { type: CipherType.Login, login: { username: "username-3", uri: url }, }); + const cipher4 = mock({ + id: "id-4", + localData: { lastUsedDate: 222 }, + name: "name-4", + type: CipherType.Identity, + identity: { + username: "username", + firstName: "Test", + lastName: "User", + email: "email@example.com", + }, + }); - beforeEach(() => { + beforeEach(async () => { activeAccountStatusMock$.next(AuthenticationStatus.Unlocked); + await initOverlayElementPorts(); }); it("skips updating the overlay ciphers if the user's auth status is not unlocked", async () => { @@ -767,7 +780,10 @@ describe("OverlayBackground", () => { await overlayBackground.updateOverlayCiphers(); expect(BrowserApi.getTabFromCurrentWindowId).toHaveBeenCalled(); - expect(cipherService.getAllDecryptedForUrl).toHaveBeenCalledWith(url, [CipherType.Card]); + expect(cipherService.getAllDecryptedForUrl).toHaveBeenCalledWith(url, [ + CipherType.Card, + CipherType.Identity, + ]); expect(cipherService.sortCiphersByLastUsedThenName).toHaveBeenCalled(); expect(overlayBackground["inlineMenuCiphers"]).toStrictEqual( new Map([ @@ -804,7 +820,10 @@ describe("OverlayBackground", () => { await overlayBackground.updateOverlayCiphers(false); expect(BrowserApi.getTabFromCurrentWindowId).toHaveBeenCalled(); - expect(cipherService.getAllDecryptedForUrl).toHaveBeenCalledWith(url, [CipherType.Card]); + expect(cipherService.getAllDecryptedForUrl).toHaveBeenCalledWith(url, [ + CipherType.Card, + CipherType.Identity, + ]); expect(cipherService.sortCiphersByLastUsedThenName).toHaveBeenCalled(); expect(overlayBackground["inlineMenuCiphers"]).toStrictEqual( new Map([ @@ -815,7 +834,6 @@ describe("OverlayBackground", () => { }); it("posts an `updateOverlayListCiphers` message to the overlay list port, and send a `updateAutofillInlineMenuListCiphers` message to the tab indicating that the list of ciphers is populated", async () => { - overlayBackground["inlineMenuListPort"] = mock(); overlayBackground["focusedFieldData"] = createFocusedFieldDataMock({ tabId: tab.id }); cipherService.getAllDecryptedForUrl.mockResolvedValue([cipher1, cipher2]); cipherService.sortCiphersByLastUsedThenName.mockReturnValue(-1); @@ -823,11 +841,12 @@ describe("OverlayBackground", () => { await overlayBackground.updateOverlayCiphers(); - expect(overlayBackground["inlineMenuListPort"].postMessage).toHaveBeenCalledWith({ + expect(listPortSpy.postMessage).toHaveBeenCalledWith({ command: "updateAutofillInlineMenuListCiphers", + showInlineMenuAccountCreation: false, ciphers: [ { - card: null, + accountCreationFieldType: undefined, favorite: cipher1.favorite, icon: { fallbackImage: "images/bwi-globe.png", @@ -846,6 +865,205 @@ describe("OverlayBackground", () => { ], }); }); + + it("updates the inline menu list with card ciphers", async () => { + overlayBackground["focusedFieldData"] = createFocusedFieldDataMock({ + tabId: tab.id, + filledByCipherType: CipherType.Card, + }); + cipherService.getAllDecryptedForUrl.mockResolvedValue([cipher1, cipher2]); + cipherService.sortCiphersByLastUsedThenName.mockReturnValue(-1); + getTabFromCurrentWindowIdSpy.mockResolvedValueOnce(tab); + + await overlayBackground.updateOverlayCiphers(); + + expect(listPortSpy.postMessage).toHaveBeenCalledWith({ + command: "updateAutofillInlineMenuListCiphers", + showInlineMenuAccountCreation: false, + ciphers: [ + { + accountCreationFieldType: undefined, + favorite: cipher2.favorite, + icon: { + fallbackImage: "", + icon: "bwi-credit-card", + image: undefined, + imageEnabled: true, + }, + id: "inline-menu-cipher-0", + card: cipher2.card.subTitle, + name: cipher2.name, + reprompt: cipher2.reprompt, + type: CipherType.Card, + }, + ], + }); + }); + + describe("updating ciphers for an account creation inline menu", () => { + it("updates the ciphers with a list of identity ciphers that contain a username", async () => { + overlayBackground["focusedFieldData"] = createFocusedFieldDataMock({ + tabId: tab.id, + accountCreationFieldType: "text", + showInlineMenuAccountCreation: true, + }); + cipherService.getAllDecryptedForUrl.mockResolvedValue([cipher4, cipher2]); + cipherService.sortCiphersByLastUsedThenName.mockReturnValue(-1); + getTabFromCurrentWindowIdSpy.mockResolvedValueOnce(tab); + + await overlayBackground.updateOverlayCiphers(); + + expect(listPortSpy.postMessage).toHaveBeenCalledWith({ + command: "updateAutofillInlineMenuListCiphers", + showInlineMenuAccountCreation: true, + ciphers: [ + { + accountCreationFieldType: "text", + favorite: cipher4.favorite, + icon: { + fallbackImage: "", + icon: "bwi-id-card", + image: undefined, + imageEnabled: true, + }, + id: "inline-menu-cipher-1", + name: cipher4.name, + reprompt: cipher4.reprompt, + type: CipherType.Identity, + identity: { + fullName: `${cipher4.identity.firstName} ${cipher4.identity.lastName}`, + username: cipher4.identity.username, + }, + }, + ], + }); + }); + + it("appends any found login ciphers to the list of identity ciphers", async () => { + overlayBackground["focusedFieldData"] = createFocusedFieldDataMock({ + tabId: tab.id, + accountCreationFieldType: "text", + showInlineMenuAccountCreation: true, + }); + cipherService.getAllDecryptedForUrl.mockResolvedValue([cipher1, cipher4]); + cipherService.sortCiphersByLastUsedThenName.mockReturnValue(-1); + getTabFromCurrentWindowIdSpy.mockResolvedValueOnce(tab); + + await overlayBackground.updateOverlayCiphers(); + + expect(listPortSpy.postMessage).toHaveBeenCalledWith({ + command: "updateAutofillInlineMenuListCiphers", + showInlineMenuAccountCreation: true, + ciphers: [ + { + accountCreationFieldType: "text", + favorite: cipher4.favorite, + icon: { + fallbackImage: "", + icon: "bwi-id-card", + image: undefined, + imageEnabled: true, + }, + id: "inline-menu-cipher-0", + name: cipher4.name, + reprompt: cipher4.reprompt, + type: CipherType.Identity, + identity: { + fullName: `${cipher4.identity.firstName} ${cipher4.identity.lastName}`, + username: cipher4.identity.username, + }, + }, + { + accountCreationFieldType: "text", + favorite: cipher1.favorite, + icon: { + fallbackImage: "images/bwi-globe.png", + icon: "bwi-globe", + image: "https://icons.bitwarden.com//jest-testing-website.com/icon.png", + imageEnabled: true, + }, + id: "inline-menu-cipher-1", + login: { + username: cipher1.login.username, + }, + name: cipher1.name, + reprompt: cipher1.reprompt, + type: CipherType.Login, + }, + ], + }); + }); + + it("skips any identity ciphers that do not contain a username or an email address", async () => { + overlayBackground["focusedFieldData"] = createFocusedFieldDataMock({ + tabId: tab.id, + accountCreationFieldType: "email", + showInlineMenuAccountCreation: true, + }); + const identityCipherWithoutUsername = mock({ + id: "id-5", + localData: { lastUsedDate: 222 }, + name: "name-5", + type: CipherType.Identity, + identity: { + username: "", + email: "", + }, + }); + cipherService.getAllDecryptedForUrl.mockResolvedValue([ + cipher4, + identityCipherWithoutUsername, + ]); + cipherService.sortCiphersByLastUsedThenName.mockReturnValue(-1); + getTabFromCurrentWindowIdSpy.mockResolvedValueOnce(tab); + + await overlayBackground.updateOverlayCiphers(); + + expect(listPortSpy.postMessage).toHaveBeenCalledWith({ + command: "updateAutofillInlineMenuListCiphers", + showInlineMenuAccountCreation: true, + ciphers: [ + { + accountCreationFieldType: "email", + favorite: cipher4.favorite, + icon: { + fallbackImage: "", + icon: "bwi-id-card", + image: undefined, + imageEnabled: true, + }, + id: "inline-menu-cipher-1", + name: cipher4.name, + reprompt: cipher4.reprompt, + type: CipherType.Identity, + identity: { + fullName: `${cipher4.identity.firstName} ${cipher4.identity.lastName}`, + username: cipher4.identity.email, + }, + }, + ], + }); + }); + + it("does not add the identity ciphers if the field is for a password field", async () => { + overlayBackground["focusedFieldData"] = createFocusedFieldDataMock({ + tabId: tab.id, + accountCreationFieldType: "password", + showInlineMenuAccountCreation: true, + }); + cipherService.getAllDecryptedForUrl.mockResolvedValue([cipher4]); + cipherService.sortCiphersByLastUsedThenName.mockReturnValue(-1); + getTabFromCurrentWindowIdSpy.mockResolvedValueOnce(tab); + + await overlayBackground.updateOverlayCiphers(); + + expect(listPortSpy.postMessage).toHaveBeenCalledWith({ + command: "updateAutofillInlineMenuListCiphers", + showInlineMenuAccountCreation: true, + ciphers: [], + }); + }); + }); }); describe("extension message handlers", () => { @@ -954,6 +1172,95 @@ describe("OverlayBackground", () => { expect(sendMessageSpy).toHaveBeenCalledWith("inlineAutofillMenuRefreshAddEditCipher"); expect(openAddEditVaultItemPopoutSpy).toHaveBeenCalled(); }); + + describe("creating a new identity cipher", () => { + it("populates an identity cipher view and creates it", async () => { + sendMockExtensionMessage( + { + command: "autofillOverlayAddNewVaultItem", + addNewCipherType: CipherType.Identity, + identity: { + title: "title", + firstName: "firstName", + middleName: "middleName", + lastName: "lastName", + fullName: "fullName", + address1: "address1", + address2: "address2", + address3: "address3", + city: "city", + state: "state", + postalCode: "postalCode", + country: "country", + company: "company", + phone: "phone", + email: "email", + username: "username", + }, + }, + sender, + ); + await flushPromises(); + + expect(cipherService.setAddEditCipherInfo).toHaveBeenCalled(); + expect(sendMessageSpy).toHaveBeenCalledWith("inlineAutofillMenuRefreshAddEditCipher"); + expect(openAddEditVaultItemPopoutSpy).toHaveBeenCalled(); + }); + + it("saves the first name based on the full name value", async () => { + sendMockExtensionMessage( + { + command: "autofillOverlayAddNewVaultItem", + addNewCipherType: CipherType.Identity, + identity: { + firstName: "", + lastName: "", + fullName: "fullName", + }, + }, + sender, + ); + await flushPromises(); + + expect(cipherService.setAddEditCipherInfo).toHaveBeenCalled(); + }); + + it("saves the first and middle names based on the full name value", async () => { + sendMockExtensionMessage( + { + command: "autofillOverlayAddNewVaultItem", + addNewCipherType: CipherType.Identity, + identity: { + firstName: "", + lastName: "", + fullName: "firstName middleName", + }, + }, + sender, + ); + await flushPromises(); + + expect(cipherService.setAddEditCipherInfo).toHaveBeenCalled(); + }); + + it("saves the first, middle, and last names based on the full name value", async () => { + sendMockExtensionMessage( + { + command: "autofillOverlayAddNewVaultItem", + addNewCipherType: CipherType.Identity, + identity: { + firstName: "", + lastName: "", + fullName: "firstName middleName lastName", + }, + }, + sender, + ); + await flushPromises(); + + expect(cipherService.setAddEditCipherInfo).toHaveBeenCalled(); + }); + }); }); describe("checkIsInlineMenuCiphersPopulated message handler", () => { @@ -1030,6 +1337,29 @@ describe("OverlayBackground", () => { { frameId: firstSender.frameId }, ); }); + + it("triggers an update of the identity ciphers present on a login field", async () => { + await initOverlayElementPorts(); + activeAccountStatusMock$.next(AuthenticationStatus.Unlocked); + const tab = createChromeTabMock({ id: 2 }); + overlayBackground["focusedFieldData"] = createFocusedFieldDataMock(); + overlayBackground["isInlineMenuButtonVisible"] = true; + const sender = mock({ tab, frameId: 100 }); + const focusedFieldData = createFocusedFieldDataMock({ + tabId: tab.id, + frameId: sender.frameId, + showInlineMenuAccountCreation: true, + }); + + sendMockExtensionMessage({ command: "updateFocusedFieldData", focusedFieldData }, sender); + await flushPromises(); + + expect(listPortSpy.postMessage).toHaveBeenCalledWith({ + command: "updateAutofillInlineMenuListCiphers", + ciphers: [], + showInlineMenuAccountCreation: true, + }); + }); }); describe("checkIsFieldCurrentlyFocused message handler", () => { diff --git a/apps/browser/src/autofill/background/overlay.background.ts b/apps/browser/src/autofill/background/overlay.background.ts index 76b0f4b76e..eea72979dd 100644 --- a/apps/browser/src/autofill/background/overlay.background.ts +++ b/apps/browser/src/autofill/background/overlay.background.ts @@ -21,6 +21,7 @@ import { CipherType } from "@bitwarden/common/vault/enums"; import { buildCipherIcon } from "@bitwarden/common/vault/icon/build-cipher-icon"; import { CardView } from "@bitwarden/common/vault/models/view/card.view"; import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; +import { IdentityView } from "@bitwarden/common/vault/models/view/identity.view"; import { LoginUriView } from "@bitwarden/common/vault/models/view/login-uri.view"; import { LoginView } from "@bitwarden/common/vault/models/view/login.view"; @@ -40,23 +41,24 @@ import { generateRandomChars } from "../utils"; import { LockedVaultPendingNotificationsData } from "./abstractions/notification.background"; import { + CloseInlineMenuMessage, FocusedFieldData, + InlineMenuButtonPortMessageHandlers, + InlineMenuCipherData, + InlineMenuListPortMessageHandlers, + InlineMenuPosition, + NewCardCipherData, + NewIdentityCipherData, + NewLoginCipherData, OverlayAddNewItemMessage, OverlayBackground as OverlayBackgroundInterface, OverlayBackgroundExtensionMessage, OverlayBackgroundExtensionMessageHandlers, - InlineMenuButtonPortMessageHandlers, - InlineMenuCipherData, - InlineMenuListPortMessageHandlers, OverlayPortMessage, PageDetailsForTab, SubFrameOffsetData, SubFrameOffsetsForTab, - CloseInlineMenuMessage, - InlineMenuPosition, ToggleInlineMenuHiddenMessage, - NewLoginCipherData, - NewCardCipherData, } from "./abstractions/overlay.background"; export class OverlayBackground implements OverlayBackgroundInterface { @@ -125,7 +127,7 @@ export class OverlayBackground implements OverlayBackgroundInterface { deletedCipher: () => this.updateOverlayCiphers(), }; private readonly inlineMenuButtonPortMessageHandlers: InlineMenuButtonPortMessageHandlers = { - triggerDelayedAutofillInlineMenuClosure: ({ port }) => this.triggerDelayedInlineMenuClosure(), + triggerDelayedAutofillInlineMenuClosure: () => this.triggerDelayedInlineMenuClosure(), autofillInlineMenuButtonClicked: ({ port }) => this.handleInlineMenuButtonClicked(port), autofillInlineMenuBlurred: () => this.checkInlineMenuListFocused(), redirectAutofillInlineMenuFocusOut: ({ message, port }) => @@ -249,6 +251,7 @@ export class OverlayBackground implements OverlayBackgroundInterface { this.inlineMenuListPort?.postMessage({ command: "updateAutofillInlineMenuListCiphers", ciphers, + showInlineMenuAccountCreation: this.showInlineMenuAccountCreation(), }); } @@ -285,15 +288,25 @@ export class OverlayBackground implements OverlayBackgroundInterface { this.cardAndIdentityCiphers.clear(); const cipherViews = ( - await this.cipherService.getAllDecryptedForUrl(currentTab.url, [CipherType.Card]) + await this.cipherService.getAllDecryptedForUrl(currentTab.url, [ + CipherType.Card, + CipherType.Identity, + ]) ).sort((a, b) => this.cipherService.sortCiphersByLastUsedThenName(a, b)); for (let cipherIndex = 0; cipherIndex < cipherViews.length; cipherIndex++) { const cipherView = cipherViews[cipherIndex]; - if (cipherView.type === CipherType.Card && !this.cardAndIdentityCiphers.has(cipherView)) { + if ( + !this.cardAndIdentityCiphers.has(cipherView) && + [CipherType.Card, CipherType.Identity].includes(cipherView.type) + ) { this.cardAndIdentityCiphers.add(cipherView); } } + if (!this.cardAndIdentityCiphers.size) { + this.cardAndIdentityCiphers = null; + } + return cipherViews; } @@ -304,6 +317,75 @@ export class OverlayBackground implements OverlayBackgroundInterface { private async getInlineMenuCipherData(): Promise { const showFavicons = await firstValueFrom(this.domainSettingsService.showFavicons$); const inlineMenuCiphersArray = Array.from(this.inlineMenuCiphers); + let inlineMenuCipherData: InlineMenuCipherData[] = []; + + if (this.showInlineMenuAccountCreation()) { + inlineMenuCipherData = this.buildInlineMenuAccountCreationCiphers( + inlineMenuCiphersArray, + true, + ); + } else { + inlineMenuCipherData = this.buildInlineMenuCiphers(inlineMenuCiphersArray, showFavicons); + } + + this.currentInlineMenuCiphersCount = inlineMenuCipherData.length; + return inlineMenuCipherData; + } + + /** + * Builds the inline menu ciphers for a form field that is meant for account creation. + * + * @param inlineMenuCiphersArray - Array of inline menu ciphers + * @param showFavicons - Identifies whether favicons should be shown + */ + private buildInlineMenuAccountCreationCiphers( + inlineMenuCiphersArray: [string, CipherView][], + showFavicons: boolean, + ) { + const inlineMenuCipherData: InlineMenuCipherData[] = []; + const accountCreationLoginCiphers: InlineMenuCipherData[] = []; + + for (let cipherIndex = 0; cipherIndex < inlineMenuCiphersArray.length; cipherIndex++) { + const [inlineMenuCipherId, cipher] = inlineMenuCiphersArray[cipherIndex]; + + if (cipher.type === CipherType.Login) { + accountCreationLoginCiphers.push( + this.buildCipherData(inlineMenuCipherId, cipher, showFavicons, true), + ); + continue; + } + + if (cipher.type !== CipherType.Identity || !this.focusedFieldData?.accountCreationFieldType) { + continue; + } + + const identity = this.getIdentityCipherData(cipher, true); + if (!identity?.username) { + continue; + } + + inlineMenuCipherData.push( + this.buildCipherData(inlineMenuCipherId, cipher, showFavicons, true, identity), + ); + } + + if (accountCreationLoginCiphers.length) { + return inlineMenuCipherData.concat(accountCreationLoginCiphers); + } + + return inlineMenuCipherData; + } + + /** + * Builds the inline menu ciphers for a form field that is not meant for account creation. + * + * @param inlineMenuCiphersArray - Array of inline menu ciphers + * @param showFavicons - Identifies whether favicons should be shown + */ + private buildInlineMenuCiphers( + inlineMenuCiphersArray: [string, CipherView][], + showFavicons: boolean, + ) { const inlineMenuCipherData: InlineMenuCipherData[] = []; for (let cipherIndex = 0; cipherIndex < inlineMenuCiphersArray.length; cipherIndex++) { @@ -312,22 +394,111 @@ export class OverlayBackground implements OverlayBackgroundInterface { continue; } - inlineMenuCipherData.push({ - id: inlineMenuCipherId, - name: cipher.name, - type: cipher.type, - reprompt: cipher.reprompt, - favorite: cipher.favorite, - icon: buildCipherIcon(this.iconsServerUrl, cipher, showFavicons), - login: cipher.type === CipherType.Login ? { username: cipher.login.username } : null, - card: cipher.type === CipherType.Card ? cipher.card.subTitle : null, - }); + inlineMenuCipherData.push(this.buildCipherData(inlineMenuCipherId, cipher, showFavicons)); } - this.currentInlineMenuCiphersCount = inlineMenuCipherData.length; return inlineMenuCipherData; } + /** + * Builds the cipher data for the inline menu list. + * + * @param inlineMenuCipherId - The ID of the inline menu cipher + * @param cipher - The cipher to build data for + * @param showFavicons - Identifies whether favicons should be shown + * @param showInlineMenuAccountCreation - Identifies whether the inline menu is for account creation + * @param identityData - Pre-created identity data + */ + private buildCipherData( + inlineMenuCipherId: string, + cipher: CipherView, + showFavicons: boolean, + showInlineMenuAccountCreation: boolean = false, + identityData?: { fullName: string; username?: string }, + ): InlineMenuCipherData { + const inlineMenuData: InlineMenuCipherData = { + id: inlineMenuCipherId, + name: cipher.name, + type: cipher.type, + reprompt: cipher.reprompt, + favorite: cipher.favorite, + icon: buildCipherIcon(this.iconsServerUrl, cipher, showFavicons), + accountCreationFieldType: this.focusedFieldData?.accountCreationFieldType, + }; + + if (cipher.type === CipherType.Login) { + inlineMenuData.login = { username: cipher.login.username }; + return inlineMenuData; + } + + if (cipher.type === CipherType.Card) { + inlineMenuData.card = cipher.card.subTitle; + return inlineMenuData; + } + + inlineMenuData.identity = + identityData || this.getIdentityCipherData(cipher, showInlineMenuAccountCreation); + return inlineMenuData; + } + + /** + * Gets the identity data for a cipher based on whether the inline menu is for account creation. + * + * @param cipher - The cipher to get the identity data for + * @param showInlineMenuAccountCreation - Identifies whether the inline menu is for account creation + */ + private getIdentityCipherData( + cipher: CipherView, + showInlineMenuAccountCreation: boolean = false, + ): { fullName: string; username?: string } { + const { firstName, lastName } = cipher.identity; + + let fullName = ""; + if (firstName) { + fullName += firstName; + } + + if (lastName) { + fullName += ` ${lastName}`; + fullName = fullName.trim(); + } + + if ( + !showInlineMenuAccountCreation || + !this.focusedFieldData?.accountCreationFieldType || + this.focusedFieldData.accountCreationFieldType === "password" + ) { + return { fullName }; + } + + return { + fullName, + username: + this.focusedFieldData.accountCreationFieldType === "email" + ? cipher.identity.email + : cipher.identity.username, + }; + } + + /** + * Identifies whether the inline menu is being shown on an account creation field. + */ + private showInlineMenuAccountCreation(): boolean { + if (typeof this.focusedFieldData?.showInlineMenuAccountCreation !== "undefined") { + return this.focusedFieldData?.showInlineMenuAccountCreation; + } + + if (this.focusedFieldData?.filledByCipherType !== CipherType.Login) { + return false; + } + + if (this.cardAndIdentityCiphers) { + return this.inlineMenuCiphers.size === this.cardAndIdentityCiphers.size; + } + + return this.inlineMenuCiphers.size === 0; + } + /** * Gets the currently focused field and closes the inline menu on that tab. */ @@ -926,7 +1097,37 @@ export class OverlayBackground implements OverlayBackgroundInterface { ); } + const previousFocusedFieldData = this.focusedFieldData; this.focusedFieldData = { ...focusedFieldData, tabId: sender.tab.id, frameId: sender.frameId }; + + const accountCreationFieldBlurred = + previousFocusedFieldData?.showInlineMenuAccountCreation && + !this.focusedFieldData.showInlineMenuAccountCreation; + + if (accountCreationFieldBlurred || this.showInlineMenuAccountCreation()) { + void this.updateIdentityCiphersOnLoginField(previousFocusedFieldData); + } + } + + /** + * Triggers an update of populated identity ciphers when a login field is focused. + * + * @param previousFocusedFieldData - The data set of the previously focused field + */ + private async updateIdentityCiphersOnLoginField(previousFocusedFieldData: FocusedFieldData) { + if ( + !previousFocusedFieldData || + !this.isInlineMenuButtonVisible || + (await this.getAuthStatus()) !== AuthenticationStatus.Unlocked + ) { + return; + } + + this.inlineMenuListPort?.postMessage({ + command: "updateAutofillInlineMenuListCiphers", + ciphers: await this.getInlineMenuCipherData(), + showInlineMenuAccountCreation: this.showInlineMenuAccountCreation(), + }); } /** @@ -1116,6 +1317,7 @@ export class OverlayBackground implements OverlayBackgroundInterface { listPageTitle: this.i18nService.translate("bitwardenVault"), unlockYourAccount: this.i18nService.translate("unlockYourAccountToViewAutofillSuggestions"), unlockAccount: this.i18nService.translate("unlockAccount"), + unlockAccountAria: this.i18nService.translate("unlockAccountAria"), fillCredentialsFor: this.i18nService.translate("fillCredentialsFor"), username: this.i18nService.translate("username")?.toLowerCase(), view: this.i18nService.translate("view"), @@ -1123,9 +1325,11 @@ export class OverlayBackground implements OverlayBackgroundInterface { newItem: this.i18nService.translate("newItem"), addNewVaultItem: this.i18nService.translate("addNewVaultItem"), newLogin: this.i18nService.translate("newLogin"), - addNewLoginItem: this.i18nService.translate("addNewLoginItem"), + addNewLoginItem: this.i18nService.translate("addNewLoginItemAria"), newCard: this.i18nService.translate("newCard"), - addNewCardItem: this.i18nService.translate("addNewCardItem"), + addNewCardItem: this.i18nService.translate("addNewCardItemAria"), + newIdentity: this.i18nService.translate("newIdentity"), + addNewIdentityItem: this.i18nService.translate("addNewIdentityItemAria"), cardNumberEndsWith: this.i18nService.translate("cardNumberEndsWith"), }; } @@ -1184,10 +1388,11 @@ export class OverlayBackground implements OverlayBackgroundInterface { * @param addNewCipherType - The type of cipher to add * @param login - The login data captured from the extension message * @param card - The card data captured from the extension message + * @param identity - The identity data captured from the extension message * @param sender - The sender of the extension message */ private async addNewVaultItem( - { addNewCipherType, login, card }: OverlayAddNewItemMessage, + { addNewCipherType, login, card, identity }: OverlayAddNewItemMessage, sender: chrome.runtime.MessageSender, ) { if (!addNewCipherType) { @@ -1198,6 +1403,7 @@ export class OverlayBackground implements OverlayBackgroundInterface { addNewCipherType, login, card, + identity, }); if (cipherView) { @@ -1218,8 +1424,14 @@ export class OverlayBackground implements OverlayBackgroundInterface { * @param addNewCipherType - The type of cipher to add * @param login - The login data captured from the extension message * @param card - The card data captured from the extension message + * @param identity - The identity data captured from the extension message */ - private buildNewVaultItemCipherView({ addNewCipherType, login, card }: OverlayAddNewItemMessage) { + private buildNewVaultItemCipherView({ + addNewCipherType, + login, + card, + identity, + }: OverlayAddNewItemMessage) { if (login && addNewCipherType === CipherType.Login) { return this.buildLoginCipherView(login); } @@ -1227,6 +1439,10 @@ export class OverlayBackground implements OverlayBackgroundInterface { if (card && addNewCipherType === CipherType.Card) { return this.buildCardCipherView(card); } + + if (identity && addNewCipherType === CipherType.Identity) { + return this.buildIdentityCipherView(identity); + } } /** @@ -1275,6 +1491,68 @@ export class OverlayBackground implements OverlayBackgroundInterface { return cipherView; } + /** + * Builds a new identity cipher view with the provided identity data. + * + * @param identity - The identity data captured from the extension message + */ + private buildIdentityCipherView(identity: NewIdentityCipherData) { + const identityView = new IdentityView(); + identityView.title = identity.title || ""; + identityView.firstName = identity.firstName || ""; + identityView.middleName = identity.middleName || ""; + identityView.lastName = identity.lastName || ""; + identityView.address1 = identity.address1 || ""; + identityView.address2 = identity.address2 || ""; + identityView.address3 = identity.address3 || ""; + identityView.city = identity.city || ""; + identityView.state = identity.state || ""; + identityView.postalCode = identity.postalCode || ""; + identityView.country = identity.country || ""; + identityView.company = identity.company || ""; + identityView.phone = identity.phone || ""; + identityView.email = identity.email || ""; + identityView.username = identity.username || ""; + + if (identity.fullName && !identityView.firstName && !identityView.lastName) { + this.buildIdentityNameParts(identity, identityView); + } + + const cipherView = new CipherView(); + cipherView.name = ""; + cipherView.folderId = null; + cipherView.type = CipherType.Identity; + cipherView.identity = identityView; + + return cipherView; + } + + /** + * Splits the identity full name into first, middle, and last name parts. + * + * @param identity - The identity data captured from the extension message + * @param identityView - The identity view to update + */ + private buildIdentityNameParts(identity: NewIdentityCipherData, identityView: IdentityView) { + const fullNameParts = identity.fullName.split(" "); + if (fullNameParts.length === 1) { + identityView.firstName = fullNameParts[0] || ""; + + return; + } + + if (fullNameParts.length === 2) { + identityView.firstName = fullNameParts[0] || ""; + identityView.lastName = fullNameParts[1] || ""; + + return; + } + + identityView.firstName = fullNameParts[0] || ""; + identityView.middleName = fullNameParts[1] || ""; + identityView.lastName = fullNameParts[2] || ""; + } + /** * Updates the property that identifies if a form field set up for the inline menu is currently focused. * @@ -1523,7 +1801,7 @@ export class OverlayBackground implements OverlayBackgroundInterface { Promise.resolve(messageResponse) .then((response) => sendResponse(response)) - .catch(this.logService.error); + .catch((error) => this.logService.error(error)); return true; }; @@ -1598,6 +1876,7 @@ export class OverlayBackground implements OverlayBackgroundInterface { ? AutofillOverlayPort.ListMessageConnector : AutofillOverlayPort.ButtonMessageConnector, filledByCipherType: this.focusedFieldData?.filledByCipherType, + showInlineMenuAccountCreation: this.showInlineMenuAccountCreation(), }); void this.updateInlineMenuPosition( { diff --git a/apps/browser/src/autofill/deprecated/content/autofill-init.deprecated.spec.ts b/apps/browser/src/autofill/deprecated/content/autofill-init.deprecated.spec.ts index 96d5e85ca3..6153a5c926 100644 --- a/apps/browser/src/autofill/deprecated/content/autofill-init.deprecated.spec.ts +++ b/apps/browser/src/autofill/deprecated/content/autofill-init.deprecated.spec.ts @@ -61,13 +61,10 @@ describe("AutofillInit", () => { autofillInit.init(); jest.advanceTimersByTime(250); - expect(chrome.runtime.sendMessage).toHaveBeenCalledWith( - { - command: "bgCollectPageDetails", - sender: "autofillInit", - }, - expect.any(Function), - ); + expect(chrome.runtime.sendMessage).toHaveBeenCalledWith({ + command: "bgCollectPageDetails", + sender: "autofillInit", + }); }); it("registers a window load listener to collect the page details if the document is not in a `complete` ready state", () => { diff --git a/apps/browser/src/autofill/deprecated/overlay/pages/list/__snapshots__/autofill-overlay-list.deprecated.spec.ts.snap b/apps/browser/src/autofill/deprecated/overlay/pages/list/__snapshots__/autofill-overlay-list.deprecated.spec.ts.snap index 6ee8e737cb..d11fbd5079 100644 --- a/apps/browser/src/autofill/deprecated/overlay/pages/list/__snapshots__/autofill-overlay-list.deprecated.spec.ts.snap +++ b/apps/browser/src/autofill/deprecated/overlay/pages/list/__snapshots__/autofill-overlay-list.deprecated.spec.ts.snap @@ -506,16 +506,17 @@ exports[`AutofillOverlayList initAutofillOverlayList the overlay with an empty l aria-hidden="true" fill="none" height="17" - viewBox="0 0 16 17" - width="16" + width="17" xmlns="http://www.w3.org/2000/svg" > @@ -523,7 +524,7 @@ exports[`AutofillOverlayList initAutofillOverlayList the overlay with an empty l id="a" > diff --git a/apps/browser/src/autofill/enums/autofill-field.enums.ts b/apps/browser/src/autofill/enums/autofill-field.enums.ts new file mode 100644 index 0000000000..4fd7c0fe88 --- /dev/null +++ b/apps/browser/src/autofill/enums/autofill-field.enums.ts @@ -0,0 +1,29 @@ +export const AutofillFieldQualifier = { + password: "password", + username: "username", + cardholderName: "cardholderName", + cardNumber: "cardNumber", + cardExpirationMonth: "cardExpirationMonth", + cardExpirationYear: "cardExpirationYear", + cardExpirationDate: "cardExpirationDate", + cardCvv: "cardCvv", + identityTitle: "identityTitle", + identityFirstName: "identityFirstName", + identityMiddleName: "identityMiddleName", + identityLastName: "identityLastName", + identityFullName: "identityFullName", + identityAddress1: "identityAddress1", + identityAddress2: "identityAddress2", + identityAddress3: "identityAddress3", + identityCity: "identityCity", + identityState: "identityState", + identityPostalCode: "identityPostalCode", + identityCountry: "identityCountry", + identityCompany: "identityCompany", + identityPhone: "identityPhone", + identityEmail: "identityEmail", + identityUsername: "identityUsername", +} as const; + +export type AutofillFieldQualifierType = + (typeof AutofillFieldQualifier)[keyof typeof AutofillFieldQualifier]; diff --git a/apps/browser/src/autofill/models/autofill-field.ts b/apps/browser/src/autofill/models/autofill-field.ts index 26f01bdeac..5a95b92899 100644 --- a/apps/browser/src/autofill/models/autofill-field.ts +++ b/apps/browser/src/autofill/models/autofill-field.ts @@ -1,5 +1,7 @@ import { CipherType } from "@bitwarden/common/vault/enums"; +import { AutofillFieldQualifierType } from "../enums/autofill-field.enums"; + /** * Represents a single field that is collected from the page source and is potentially autofilled. */ @@ -110,4 +112,8 @@ export default class AutofillField { checked?: boolean; filledByCipherType?: CipherType; + + showInlineMenuAccountCreation?: boolean; + + fieldQualifier?: AutofillFieldQualifierType; } diff --git a/apps/browser/src/autofill/overlay/inline-menu/abstractions/autofill-inline-menu-list.ts b/apps/browser/src/autofill/overlay/inline-menu/abstractions/autofill-inline-menu-list.ts index 5a00ffbaaa..090fb7887c 100644 --- a/apps/browser/src/autofill/overlay/inline-menu/abstractions/autofill-inline-menu-list.ts +++ b/apps/browser/src/autofill/overlay/inline-menu/abstractions/autofill-inline-menu-list.ts @@ -7,6 +7,7 @@ type AutofillInlineMenuListMessage = { command: string }; export type UpdateAutofillInlineMenuListCiphersMessage = AutofillInlineMenuListMessage & { ciphers: InlineMenuCipherData[]; + showInlineMenuAccountCreation?: boolean; }; export type InitAutofillInlineMenuListMessage = AutofillInlineMenuListMessage & { @@ -16,6 +17,7 @@ export type InitAutofillInlineMenuListMessage = AutofillInlineMenuListMessage & translations: Record; ciphers?: InlineMenuCipherData[]; filledByCipherType?: CipherType; + showInlineMenuAccountCreation?: boolean; portKey: string; }; diff --git a/apps/browser/src/autofill/overlay/inline-menu/content/autofill-inline-menu-content.service.ts b/apps/browser/src/autofill/overlay/inline-menu/content/autofill-inline-menu-content.service.ts index b8702c7443..ae94741591 100644 --- a/apps/browser/src/autofill/overlay/inline-menu/content/autofill-inline-menu-content.service.ts +++ b/apps/browser/src/autofill/overlay/inline-menu/content/autofill-inline-menu-content.service.ts @@ -373,7 +373,7 @@ export class AutofillInlineMenuContentService implements AutofillInlineMenuConte * ensure that the inline menu elements are always present at the bottom of the * body element. */ - private handleBodyElementMutationObserverUpdate = async () => { + private handleBodyElementMutationObserverUpdate = () => { if ( (!this.buttonElement && !this.listElement) || this.isTriggeringExcessiveMutationObserverIterations() @@ -410,17 +410,18 @@ export class AutofillInlineMenuContentService implements AutofillInlineMenuConte return; } + const isInlineMenuListVisible = await this.isInlineMenuListVisible(); if ( !lastChild || (lastChildIsInlineMenuList && secondToLastChildIsInlineMenuButton) || - (lastChildIsInlineMenuButton && !(await this.isInlineMenuListVisible())) + (lastChildIsInlineMenuButton && !isInlineMenuListVisible) ) { return; } if ( (lastChildIsInlineMenuList && !secondToLastChildIsInlineMenuButton) || - (lastChildIsInlineMenuButton && (await this.isInlineMenuListVisible())) + (lastChildIsInlineMenuButton && isInlineMenuListVisible) ) { globalThis.document.body.insertBefore(this.buttonElement, this.listElement); return; diff --git a/apps/browser/src/autofill/overlay/inline-menu/iframe-content/autofill-inline-menu-iframe.service.ts b/apps/browser/src/autofill/overlay/inline-menu/iframe-content/autofill-inline-menu-iframe.service.ts index afa2548930..fd305d23c9 100644 --- a/apps/browser/src/autofill/overlay/inline-menu/iframe-content/autofill-inline-menu-iframe.service.ts +++ b/apps/browser/src/autofill/overlay/inline-menu/iframe-content/autofill-inline-menu-iframe.service.ts @@ -80,10 +80,11 @@ export class AutofillInlineMenuIframeService implements AutofillInlineMenuIframe this.defaultIframeAttributes.title = this.iframeTitle; this.iframe = globalThis.document.createElement("iframe"); - this.updateElementStyles(this.iframe, { ...this.iframeStyles, ...this.initStyles }); for (const [attribute, value] of Object.entries(this.defaultIframeAttributes)) { this.iframe.setAttribute(attribute, value); } + this.iframeStyles = { ...this.iframeStyles, ...this.initStyles }; + this.setElementStyles(this.iframe, this.iframeStyles, true); this.iframe.addEventListener(EVENTS.LOAD, this.setupPortMessageListener); if (this.ariaAlert) { @@ -91,6 +92,7 @@ export class AutofillInlineMenuIframeService implements AutofillInlineMenuIframe } this.shadow.appendChild(this.iframe); + this.observeIframe(); } /** @@ -143,7 +145,10 @@ export class AutofillInlineMenuIframeService implements AutofillInlineMenuIframe clearTimeout(this.ariaAlertTimeout); } - this.ariaAlertTimeout = setTimeout(() => this.shadow.appendChild(this.ariaAlertElement), 2000); + this.ariaAlertTimeout = globalThis.setTimeout( + () => this.shadow.appendChild(this.ariaAlertElement), + 2000, + ); } /** @@ -255,7 +260,9 @@ export class AutofillInlineMenuIframeService implements AutofillInlineMenuIframe return; } - this.clearFadeInTimeout(); + if (this.fadeInTimeout) { + this.handleFadeInInlineMenuIframe(); + } this.updateElementStyles(this.iframe, position); this.announceAriaAlert(); } @@ -325,6 +332,7 @@ export class AutofillInlineMenuIframeService implements AutofillInlineMenuIframe private clearFadeInTimeout() { if (this.fadeInTimeout) { clearTimeout(this.fadeInTimeout); + this.fadeInTimeout = null; } } @@ -442,7 +450,10 @@ export class AutofillInlineMenuIframeService implements AutofillInlineMenuIframe } this.mutationObserverIterations++; - this.mutationObserverIterationsResetTimeout = setTimeout(() => resetCounters(), 2000); + this.mutationObserverIterationsResetTimeout = globalThis.setTimeout( + () => resetCounters(), + 2000, + ); if (this.mutationObserverIterations > 20) { clearTimeout(this.mutationObserverIterationsResetTimeout); diff --git a/apps/browser/src/autofill/overlay/inline-menu/pages/list/__snapshots__/autofill-inline-menu-list.spec.ts.snap b/apps/browser/src/autofill/overlay/inline-menu/pages/list/__snapshots__/autofill-inline-menu-list.spec.ts.snap index 3b0e84514f..a8a4d5c4a7 100644 --- a/apps/browser/src/autofill/overlay/inline-menu/pages/list/__snapshots__/autofill-inline-menu-list.spec.ts.snap +++ b/apps/browser/src/autofill/overlay/inline-menu/pages/list/__snapshots__/autofill-inline-menu-list.spec.ts.snap @@ -13,8 +13,8 @@ exports[`AutofillInlineMenuList initAutofillInlineMenuList the inline menu with class="inline-menu-list-button-container" > + + +`; + +exports[`AutofillInlineMenuList initAutofillInlineMenuList the inline menu with an empty list of ciphers creates the views for the no results inline menu that should be filled by an identity cipher 1`] = ` +
+
+ noItemsToShow +
+
+ +
+
+`; + +exports[`AutofillInlineMenuList initAutofillInlineMenuList the list of ciphers for an authenticated user account creation elements creates the inline menu account creation view 1`] = ` +
+
    +
  • +
    + + +
    +
  • +
+
+ + +
+ + +
+`; + +exports[`AutofillInlineMenuList initAutofillInlineMenuList the list of ciphers for an authenticated user creates the views for a list of identity ciphers 1`] = ` +
+
    +
  • +
    + + +
    +
  • +
  • +
    + + +
    +
  • +
  • +
    + + +
    +
  • +
  • +
    + + +
    +
  • +
  • +
    + + +
    +
  • +
  • +
    +