Skip to content

feat(pass-extension): add autofill keyboard shortcut command - #481

Open
yuribodo wants to merge 1666 commits into
ProtonMail:mainfrom
yuribodo:feat/autofill-keyboard-shortcut
Open

yuribodo wants to merge 1666 commits into
ProtonMail:mainfrom
yuribodo:feat/autofill-keyboard-shortcut

Conversation

@yuribodo

@yuribodo yuribodo commented Mar 29, 2026

Copy link
Copy Markdown

Summary

Adds a keyboard shortcut to trigger autofill in the Proton Pass browser extension, as discussed and approved in #453.

  • Registers a new autofill command in Chrome and Firefox manifests with Ctrl+Shift+U as the default shortcut
  • Handles the command in the background script by sending an AUTOFILL_TRIGGER message to the active tab's content script
  • Content script finds the first detected login field and opens the autofill dropdown, reusing the existing inline dropdown flow

Closes #453

Changes

File Change
manifest-chrome.json Added "autofill" command entry
manifest-firefox.json Added "autofill" command entry
src/types/messages.ts Added AUTOFILL_TRIGGER to WorkerMessageType enum
src/lib/extension/commands.ts Added async handler for the "autofill" command
src/lib/extension/commands.spec.ts Added 4 unit tests for command handling
src/app/content/services/autofill/autofill.service.ts Registered AUTOFILL_TRIGGER handler using withContext pattern

How it works

  1. User presses Ctrl+Shift+U
  2. browser.commands.onCommand fires in the background script
  3. Background queries the active tab and sends AUTOFILL_TRIGGER via browser.tabs.sendMessage
  4. Content script's FrameMessageBroker receives the message
  5. Handler finds the first field with DropdownAction.AUTOFILL_LOGIN via formManager.getFields()
  6. Opens the autofill dropdown on that field using inline.dropdown.toggle()
  7. User selects credentials → normal autofill flow proceeds

If no login field is detected on the page, the shortcut is a no-op.

Design decisions

Default shortcut Ctrl+Shift+U: Chosen to avoid conflicts with existing commands (Ctrl+Shift+X for popup, Ctrl+Shift+L for larger window). The industry standard for autofill is Ctrl+Shift+L (used by Bitwarden and LastPass). If the team is open to it, reassigning Ctrl+Shift+L from open-larger-window to autofill in a follow-up would align with user expectations from competing password managers.

Dropdown approach (not direct autofill): Opens the dropdown instead of auto-filling the top match. This is safer, gives the user control when multiple credentials match, and reuses the existing dropdown infrastructure with minimal new code.

Safari excluded: Safari does not support the browser.commands API. The command listener is already gated by BUILD_TARGET !== 'safari' in worker/index.ts.

Test plan

  • Unit tests for handleExtensionCommand (4 tests passing):
    • Opens larger window for open-larger-window command
    • Sends AUTOFILL_TRIGGER to active tab for autofill command
    • Does not send message when no active tab is found
    • Does nothing for unknown commands
  • Manual: press Ctrl+Shift+U on a login page → dropdown opens on first login field
  • Manual: press Ctrl+Shift+U on a page with no login form → nothing happens
  • Manual: verify Ctrl+Shift+X and Ctrl+Shift+L still work as before
  • Manual: verify new shortcut appears in Settings → Shortcuts

L10n bot and others added 30 commits May 28, 2026 09:37
i18n(weekly-mr:app): Upgrade translations from crowdin (2d51304). for webapps

See merge request web/clients!25193
Add support for system groups

See merge request web/clients!25054
Extracting reused withTimeout to package/meet/utils

See merge request web/clients!25060
Disable auto-complete on chat

See merge request web/clients!25115
Scopes usernames with bdi

See merge request web/clients!25117
fix(account): remove access check in categories settings

See merge request web/clients!25198
Do not scroll element in view when changing page

See merge request web/clients!25197
Rework byoe access check

See merge request web/clients!25202
[REALTIME-240] Homepage SDK - folder events

See merge request web/clients!25110
i18n(weekly-mr:app): Upgrade translations from crowdin (53310fd). for webapps

See merge request web/clients!25210
Fix upsell path

See merge request web/clients!25217
Sheets: Fix issue with settings dialog menu item

See merge request web/clients!25218
/** Only arm the field action-trap when the anchor field is the active element — i.e. the
* focus-recovery scenario. The shortcut flow opens the dropdown without focusing the field,
* so arming it there would needlessly suppress the field's normal autofocus dropdown. */
if (anchor.current?.type === 'field' && isActiveElement(anchor.current.field.element)) {

@edvincandon edvincandon Jun 24, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For your use-case : could we rather drill a boolean flag down onWillFocus & onFocusRequest rather than relying on an active element check here ? The reason being : by-passing focus-traps is time-sensitive and the document.activeElement may be stale when onWillFocus triggers from an actual dropdown initiated focus request (but not through your new exposed handle).

Comment on lines +380 to +400
const onAutofillTrigger: FrameMessageHandler<WorkerMessageType.AUTOFILL_TRIGGER> = withContext(async (ctx) => {
const dropdown = ctx?.service.inline.dropdown;
const fields = ctx?.service.formManager.getFields();
const loginField = fields?.find((field) => field.action?.type === DropdownAction.AUTOFILL_LOGIN);

if (!dropdown || !loginField) return;

dropdown.toggle({
type: 'field',
action: DropdownAction.AUTOFILL_LOGIN,
autofocused: false,
autofilled: loginField.autofilled !== null,
field: loginField,
});

/** Keyboard-only flow: once the dropdown is visible, move keyboard focus into it
* so the user can navigate the login suggestions with the arrow keys and select
* one with Enter — without touching the mouse. Unlike the focus-on-field flow,
* the shortcut opens the dropdown with `autofocused: false`, so nothing has moved
* focus into the iframe yet. */
await waitUntil(() => dropdown.getState().then(({ visible }) => visible), 25, DROPDOWN_AUTOFOCUS_TIMEOUT)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is a small type error when running yarn check-types inside applications/pass-extension, this shouldn't be async:

Suggested change
const onAutofillTrigger: FrameMessageHandler<WorkerMessageType.AUTOFILL_TRIGGER> = withContext(async (ctx) => {
const dropdown = ctx?.service.inline.dropdown;
const fields = ctx?.service.formManager.getFields();
const loginField = fields?.find((field) => field.action?.type === DropdownAction.AUTOFILL_LOGIN);
if (!dropdown || !loginField) return;
dropdown.toggle({
type: 'field',
action: DropdownAction.AUTOFILL_LOGIN,
autofocused: false,
autofilled: loginField.autofilled !== null,
field: loginField,
});
/** Keyboard-only flow: once the dropdown is visible, move keyboard focus into it
* so the user can navigate the login suggestions with the arrow keys and select
* one with Enter without touching the mouse. Unlike the focus-on-field flow,
* the shortcut opens the dropdown with `autofocused: false`, so nothing has moved
* focus into the iframe yet. */
await waitUntil(() => dropdown.getState().then(({ visible }) => visible), 25, DROPDOWN_AUTOFOCUS_TIMEOUT)
const onAutofillTrigger: FrameMessageHandler<WorkerMessageType.AUTOFILL_TRIGGER> = withContext((ctx) => {
const dropdown = ctx?.service.inline.dropdown;
const fields = ctx?.service.formManager.getFields();
const loginField = fields?.find((field) => field.action?.type === DropdownAction.AUTOFILL_LOGIN);
if (!dropdown || !loginField) return;
dropdown.toggle({
type: 'field',
action: DropdownAction.AUTOFILL_LOGIN,
autofocused: false,
autofilled: loginField.autofilled !== null,
field: loginField,
});
/** Keyboard-only flow: once the dropdown is visible, move keyboard focus into it
* so the user can navigate the login suggestions with the arrow keys and select
* one with Enter without touching the mouse. Unlike the focus-on-field flow,
* the shortcut opens the dropdown with `autofocused: false`, so nothing has moved
* focus into the iframe yet. */
void waitUntil(() => dropdown.getState().then(({ visible }) => visible), 25, DROPDOWN_AUTOFOCUS_TIMEOUT)

Note that my suggestion may be changed if you implement #481 (comment)

@mmso
mmso force-pushed the main branch 9 times, most recently from 6e496e5 to bfa5069 Compare July 31, 2026 08:17
@mmso
mmso force-pushed the main branch 4 times, most recently from d9d8f8f to 95ce65c Compare August 12, 2026 08:17
@ericxob77

Copy link
Copy Markdown

Please get this feature implemented! It has been a request for YEARS.

@mmso
mmso force-pushed the main branch 9 times, most recently from 5eb5e79 to 28d81d1 Compare September 11, 2026 16:16
@mmso
mmso force-pushed the main branch 5 times, most recently from 40a6c9b to 079f4b5 Compare September 16, 2026 16:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Proton Pass Web Extension - Autofill keyboard shortcut contribution