Autofill Event

Draft Community Group Report,

This version:
https://wicg.github.io/autofill-event/
Issue Tracking:
GitHub
Editor:
(Shopify)

Abstract

This specification defines an event that fires when the user agent is about to autofill form fields, allowing developers to adapt forms dynamically based on the autofilled values.

Status of this document

This specification was published by the Web Platform Incubator Community Group. It is not a W3C Standard nor is it on the W3C Standards Track. Please note that under the W3C Community Contributor License Agreement (CLA) there is a limited opt-out and other conditions apply. Learn more about W3C Community and Business Groups.

1. Introduction

This section is non-normative.

Autofill is a key feature of the web that reduces friction for millions of users everyday. It is widely used on login screens, e-commerce, and contact forms, to name a few examples. For commerce and checkout flows in particular, autofill provides significant benefit to both the buyer experience and merchant outcomes.

At the same time, autofill on the web has several deficiencies: partial or incomplete fill, cross-browser interoperability issues, and high cost to implement and maintain for developers.

One key example is address autofill which, when implemented correctly, is a dynamic form: different geographies have different shapes and requirements for address input. Selecting a country requires changing the form (re-ordering fields, adding and removing fields) and depends on input from the user, but autofill mediates this interaction and may not respond correctly to the rendered form.

The current 'industry standard' solution requires use of hidden form fields that try to anticipate and capture the right information and then surface it to the user. This solution is brittle and complex. Worse, it entrenches use of hidden fields to power legitimate use cases, but the same technique can be and is also abused by bad actors.

This specification introduces an AutofillEvent that fires before autofill values are committed to form fields, allowing developers to:

  1. Inspect the values that are about to be autofilled

  2. Adapt the form dynamically based on those values (e.g., showing country-specific address fields)

  3. Signal to the user agent when the form is ready to accept the autofill values

1.1. Goals

1.2. Example

A checkout form that dynamically adds country-specific address fields based on the autofilled country value:
<form id="checkout">
  <input autocomplete="name" placeholder="Full name">
  <input autocomplete="street-address" placeholder="Street address">
  <input autocomplete="address-level2" placeholder="City">
  <input autocomplete="postal-code" placeholder="Postal code">
  <input autocomplete="country" placeholder="Country">
  <!-- State/province field will be added dynamically for countries that need it -->
</form>

<script>
document.addEventListener('autofill', async function(event) {
  // Find the country value in the autofill values
  let countryValue = null;
  let formElement = null;
  
  // Find the country element and value
  for (const [element, value] of event.values) {
    if (element.autocomplete === 'country') {
      countryValue = value;
      formElement = element.form;
      break;
    }
  }
  
  // If filling a US address, we need to add a state selector
  if (event.refill !== null) {
    if (countryValue === 'US') {
        // Check if we already have a state field
        const existingState = formElement.querySelector('[autocomplete="address-level1"]');
        if (!existingState) {
        // Create and insert a state selector for US addresses
        const stateSelect = document.createElement('select');
        stateSelect.autocomplete = 'address-level1';
        stateSelect.name = 'state';
        stateSelect.innerHTML = `
            <option value="">Select state...</option>
            <option value="AL">Alabama</option>
            <option value="AK">Alaska</option>
            <option value="AZ">Arizona</option>
            <option value="CA">California</option>
            <option value="CO">Colorado</option>
            <!-- ... other states ... -->
            <option value="WY">Wyoming</option>
        `;
        
        // Insert before the postal code field
        const postalCode = formElement.querySelector('[autocomplete="postal-code"]');
        postalCode.parentNode.insertBefore(stateSelect, postalCode);
        
        // Signal that the form has been modified and autofill should be refilled
        await event.refill();
        }
    } else if (countryValue === 'UK') {
        ... Add UK-specific logic
    }
  } else {
    // The UA doesn't support refilling. Extract values from hidden fields,
    // or notify the user they need to complete values manually.
  }
});
</script>

The values attribute returns a list of autofill value entries, where each entry is a tuple of the target HTMLElement and the value to be filled. The developer can iterate over these entries to inspect the pending autofill data and determine if the form needs to be adapted.

Once the form structure was changed (e.g., to add country-specific fields, potentially asynchronously), the developer calls refill. This signals to the user agent that the form has been modified and that it should retry the autofill operation with the updated form structure.

Note: The refill attribute is null on the second dispatch of the event (after form modifications), preventing infinite loops.

2. Concepts

2.1. Autofill Value Entries

An autofill value entry is a tuple consisting of:

  1. An HTMLElement — the form control that will receive the autofilled value

  2. A DOMString — the value from the user’s autofill profile to be filled

The user agent matches form controls to autofill data based on the control’s autocomplete attribute (see autofill field name in [HTML]). It may also use implementation-defined heuristics.

2.2. Refill Operation

A refill operation allows the developer to signal that the form structure was modified in response to the autofill values, and that the user agent should attempt to fill the form again.

3. The AutofillEvent Interface

[Exposed=Window]
interface AutofillEvent : Event {
  constructor(DOMString type, optional AutofillEventInit eventInitDict = {});
  readonly attribute FrozenArray<AutofillValueEntry> values;
  readonly attribute RefillCallback? refill;
};

callback RefillCallback = Promise<undefined> ();

dictionary AutofillEventInit : EventInit {
  sequence<AutofillValueEntry> values = [];
  boolean allowRefill = true;
};

typedef sequence<any> AutofillValueEntry;
// AutofillValueEntry is a tuple of [HTMLElement, DOMString]
// where the first element is the form control and the second is the value to fill

The AutofillEvent interface represents an event that is dispatched when the user agent is about to autofill form fields.

3.1. Attributes

The values attribute returns a list of autofill value entries. Each entry is a tuple where the first element is an HTMLElement (the form control to be filled) and the second element is a DOMString (the value to fill).

The refill attribute returns a RefillCallback or null. When not null, calling this callback returns a Promise that, when awaited, signals to the user agent that the form structure has been modified and autofill should be retried.

The refill attribute is null when:

This prevents infinite loops where a page continuously modifies the form and requests refills.

Each AutofillEvent has an associated values list (a list of autofill value entries), initially an empty list.

Each AutofillEvent has an associated allow refill flag (a boolean), initially true.

Each AutofillEvent has an associated dispatch timestamp (a DOMHighResTimeStamp), initially 0.

Each AutofillEvent has an associated refill pending flag (a boolean), initially false.

4. Processing Model

4.1. Firing the Autofill Event

To fire an autofill event given a document document, a list of autofill value entries entries, and a boolean allowRefill:
  1. Let event be the result of creating an event using AutofillEvent.

  2. Initialize event’s type attribute to "autofill".

  3. Initialize event’s bubbles attribute to true.

  4. Initialize event’s cancelable attribute to false.

  5. Set event’s values list to entries.

  6. Set event’s allow refill flag to allowRefill.

  7. Set event’s dispatch timestamp to the current high resolution time.

  8. Dispatch event at document.

  9. Perform the autofill operation on document with entries.

Note: The autofill operation is performed immediately after the event is dispatched. The refill callback allows the page to request an additional autofill pass after modifying the form structure, within an implementation-defined timeout window.

4.2. Processing a Refill Request

To process a refill request given an AutofillEvent event:
  1. Let now be the current high resolution time.

  2. Let elapsed be now minus event’s dispatch timestamp.

  3. Let refillTimeout be an implementation-defined duration.

  4. If elapsed is greater than refillTimeout, return a promise rejected with an "InvalidStateError" DOMException.

  5. If event’s allow refill flag is false, return a promise rejected with an "InvalidStateError" DOMException.

  6. If event’s refill pending flag is true, return a promise rejected with an "InvalidStateError" DOMException.

  7. Set event’s refill pending flag to true.

  8. Let promise be a new promise.

  9. Let document be event’s relevant document.

  10. Return promise and in parallel:

    1. Let entries be the updated autofill value entries (re-matching the user’s autofill data against the modified form).

    2. Queue a task to:

      1. Fire an autofill event with document, entries, and false.

      2. Perform the autofill operation on document with entries.

      3. Resolve promise with undefined.

Note: The timeout is implementation-defined to allow user agents flexibility in balancing responsiveness with giving pages adequate time to call refill. User agents SHOULD choose a timeout that provides a good user experience.

Note: On the retry dispatch (after refill was called), the refill attribute is null, preventing infinite loops.

4.3. Integration with HTML Autofill

When a user agent’s autofill mechanism is triggered (e.g., by user interaction with an autofill UI), and the user selects values to fill, the user agent MUST fire an autofill event before committing those values to form fields.

5. The "full-address" Autocomplete Token

This specification introduces a new autofill field name: "full-address".

When a form control has the autocomplete attribute set to "full-address", the user agent SHOULD request permission to access the user’s complete address data, including fields that may not be present in the current form.

This enables forms to receive comprehensive address information via the AutofillEvent, allowing them to dynamically adapt their structure to accommodate all relevant fields for the user’s address.

Using full-address to enable comprehensive address autofill:
<form autocomplete="full-address">
  <input name="country" autocomplete="country">
  <div id="dynamic-address-fields"></div>
</form>

6. Security and Privacy Considerations

6.1. Timing Attacks

The AutofillEvent exposes autofill values to JavaScript before they are committed to form fields. User agents SHOULD ensure that the event is only fired after explicit user consent to autofill has been given (e.g., by selecting an autofill suggestion from a dropdown).

6.2. Third-Party Autofill Providers

Browser extensions and third-party autofill providers (such as password managers) can utilize this API by constructing and dispatching AutofillEvents with the same structure, ensuring consistent behavior regardless of the autofill source.

Conformance

Document conventions

Conformance requirements are expressed with a combination of descriptive assertions and RFC 2119 terminology. The key words “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “MAY”, and “OPTIONAL” in the normative parts of this document are to be interpreted as described in RFC 2119. However, for readability, these words do not appear in all uppercase letters in this specification.

All of the text of this specification is normative except sections explicitly marked as non-normative, examples, and notes. [RFC2119]

Examples in this specification are introduced with the words “for example” or are set apart from the normative text with class="example", like this:

This is an example of an informative example.

Informative notes begin with the word “Note” and are set apart from the normative text with class="note", like this:

Note, this is an informative note.

Index

Terms defined by this specification

Terms defined by reference

References

Normative References

[DOM]
Anne van Kesteren. DOM Standard. Living Standard. URL: https://dom.spec.whatwg.org/
[HR-TIME-3]
Yoav Weiss. High Resolution Time. URL: https://w3c.github.io/hr-time/
[HTML]
Anne van Kesteren; et al. HTML Standard. Living Standard. URL: https://html.spec.whatwg.org/multipage/
[INFRA]
Anne van Kesteren; Domenic Denicola. Infra Standard. Living Standard. URL: https://infra.spec.whatwg.org/
[RFC2119]
S. Bradner. Key words for use in RFCs to Indicate Requirement Levels. March 1997. Best Current Practice. URL: https://datatracker.ietf.org/doc/html/rfc2119
[WEBIDL]
Edgar Chen; Timothy Gu. Web IDL Standard. Living Standard. URL: https://webidl.spec.whatwg.org/

IDL Index

[Exposed=Window]
interface AutofillEvent : Event {
  constructor(DOMString type, optional AutofillEventInit eventInitDict = {});
  readonly attribute FrozenArray<AutofillValueEntry> values;
  readonly attribute RefillCallback? refill;
};

callback RefillCallback = Promise<undefined> ();

dictionary AutofillEventInit : EventInit {
  sequence<AutofillValueEntry> values = [];
  boolean allowRefill = true;
};

typedef sequence<any> AutofillValueEntry;
// AutofillValueEntry is a tuple of [HTMLElement, DOMString]
// where the first element is the form control and the second is the value to fill