Skip to content

Commit

Permalink
Refactor [vXXX] auto update credential provider script
Browse files Browse the repository at this point in the history
  • Loading branch information
github-actions[bot] authored Jun 8, 2024
1 parent efa6554 commit c66c21f
Show file tree
Hide file tree
Showing 14 changed files with 556 additions and 454 deletions.
11 changes: 5 additions & 6 deletions firefox-ios/Client/Assets/CC_Script/Constants.ios.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,16 @@ const IOS_DEFAULT_PREFERENCES = {
"extensions.formautofill.creditCards.heuristics.fathom.testConfidence": 0,
"extensions.formautofill.creditCards.heuristics.fathom.types":
"cc-number,cc-name",
"extensions.formautofill.addresses.capture.requiredFields":
"street-address,postal-code,address-level1,address-level2",
"extensions.formautofill.loglevel": "Warn",
"extensions.formautofill.addresses.supported": "off",
"extensions.formautofill.creditCards.supported": "detect",
"browser.search.region": "US",
"extensions.formautofill.creditCards.supportedCountries": "US,CA,GB,FR,DE",
"extensions.formautofill.addresses.enabled": false,
"extensions.formautofill.addresses.enabled": true,
"extensions.formautofill.addresses.experiments.enabled": true,
"extensions.formautofill.addresses.capture.enabled": false,
"extensions.formautofill.addresses.capture.v2.enabled": false,
"extensions.formautofill.addresses.supportedCountries": "",
"extensions.formautofill.creditCards.enabled": true,
"extensions.formautofill.reauth.enabled": true,
Expand All @@ -26,13 +28,10 @@ const IOS_DEFAULT_PREFERENCES = {
"extensions.formautofill.addresses.ignoreAutocompleteOff": true,
"extensions.formautofill.heuristics.enabled": true,
"extensions.formautofill.section.enabled": true,
// WebKit doesn't support the checkVisibility API, setting the threshold value to 0 to ensure
// `IsFieldVisible` function doesn't use it
"extensions.formautofill.heuristics.visibilityCheckThreshold": 0,
"extensions.formautofill.heuristics.interactivityCheckMode": "focusability",
"extensions.formautofill.heuristics.captureOnFormRemoval": false,
"extensions.formautofill.heuristics.captureOnPageNavigation": false,
"extensions.formautofill.focusOnAutofill": false,
"extensions.formautofill.test.ignoreVisibilityCheck": false,
};

// Used Mimic the behavior of .getAutocompleteInfo()
Expand Down
13 changes: 13 additions & 0 deletions firefox-ios/Client/Assets/CC_Script/FieldScanner.sys.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */

const lazy = {};
ChromeUtils.defineESModuleGetters(lazy, {
FormAutofillUtils: "resource://gre/modules/shared/FormAutofillUtils.sys.mjs",
});

/**
* Represents the detailed information about a form field, including
* the inferred field name, the approach used for inferring, and additional metadata.
Expand Down Expand Up @@ -73,6 +78,14 @@ export class FieldDetail {
get sectionName() {
return this.section || this.addressType;
}

#isVisible = null;
get isVisible() {
if (this.#isVisible == null) {
this.#isVisible = lazy.FormAutofillUtils.isFieldVisible(this.element);
}
return this.#isVisible;
}
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

import { FormAutofill } from "resource://autofill/FormAutofill.sys.mjs";

FormAutofill.defineLogGetter = (scope, logPrefix) => ({
FormAutofill.defineLogGetter = (_scope, _logPrefix) => ({
// TODO: Bug 1828405. Explore how logging should be handled.
// Maybe it makes more sense to do it on swift side and have JS just send messages.
info: () => {},
Expand Down
77 changes: 44 additions & 33 deletions firefox-ios/Client/Assets/CC_Script/FormAutofill.sys.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

import { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs";
import { Region } from "resource://gre/modules/Region.sys.mjs";
import { AddressMetaDataLoader } from "resource://gre/modules/shared/AddressMetaDataLoader.sys.mjs";

const AUTOFILL_ADDRESSES_AVAILABLE_PREF =
"extensions.formautofill.addresses.supported";
Expand All @@ -17,34 +18,33 @@ const ENABLED_AUTOFILL_ADDRESSES_PREF =
"extensions.formautofill.addresses.enabled";
const ENABLED_AUTOFILL_ADDRESSES_CAPTURE_PREF =
"extensions.formautofill.addresses.capture.enabled";
const ENABLED_AUTOFILL_ADDRESSES_CAPTURE_V2_PREF =
"extensions.formautofill.addresses.capture.v2.enabled";
const ENABLED_AUTOFILL_ADDRESSES_CAPTURE_REQUIRED_FIELDS_PREF =
"extensions.formautofill.addresses.capture.requiredFields";
const ENABLED_AUTOFILL_ADDRESSES_SUPPORTED_COUNTRIES_PREF =
"extensions.formautofill.addresses.supportedCountries";
const ENABLED_AUTOFILL_CREDITCARDS_PREF =
"extensions.formautofill.creditCards.enabled";
const ENABLED_AUTOFILL_CREDITCARDS_REAUTH_PREF =
"extensions.formautofill.reauth.enabled";
const AUTOFILL_CREDITCARDS_REAUTH_PREF =
"extensions.formautofill.creditCards.reauth.optout";
const AUTOFILL_CREDITCARDS_HIDE_UI_PREF =
"extensions.formautofill.creditCards.hideui";
const FORM_AUTOFILL_SUPPORT_RTL_PREF = "extensions.formautofill.supportRTL";
const AUTOFILL_CREDITCARDS_AUTOCOMPLETE_OFF_PREF =
"extensions.formautofill.creditCards.ignoreAutocompleteOff";
const AUTOFILL_ADDRESSES_AUTOCOMPLETE_OFF_PREF =
"extensions.formautofill.addresses.ignoreAutocompleteOff";
const ENABLED_AUTOFILL_CAPTURE_ON_FORM_REMOVAL =
const ENABLED_AUTOFILL_CAPTURE_ON_FORM_REMOVAL_PREF =
"extensions.formautofill.heuristics.captureOnFormRemoval";
const ENABLED_AUTOFILL_CAPTURE_ON_PAGE_NAVIGATION =
const ENABLED_AUTOFILL_CAPTURE_ON_PAGE_NAVIGATION_PREF =
"extensions.formautofill.heuristics.captureOnPageNavigation";

export const FormAutofill = {
ENABLED_AUTOFILL_ADDRESSES_PREF,
ENABLED_AUTOFILL_ADDRESSES_CAPTURE_PREF,
ENABLED_AUTOFILL_ADDRESSES_CAPTURE_V2_PREF,
ENABLED_AUTOFILL_CAPTURE_ON_FORM_REMOVAL,
ENABLED_AUTOFILL_CAPTURE_ON_PAGE_NAVIGATION,
ENABLED_AUTOFILL_CAPTURE_ON_FORM_REMOVAL_PREF,
ENABLED_AUTOFILL_CAPTURE_ON_PAGE_NAVIGATION_PREF,
ENABLED_AUTOFILL_CREDITCARDS_PREF,
ENABLED_AUTOFILL_CREDITCARDS_REAUTH_PREF,
AUTOFILL_CREDITCARDS_REAUTH_PREF,
AUTOFILL_CREDITCARDS_AUTOCOMPLETE_OFF_PREF,
AUTOFILL_ADDRESSES_AUTOCOMPLETE_OFF_PREF,

Expand Down Expand Up @@ -81,7 +81,9 @@ export const FormAutofill = {
return false;
},
isAutofillAddressesAvailableInCountry(country) {
return FormAutofill._addressAutofillSupportedCountries.includes(country);
return FormAutofill._addressAutofillSupportedCountries.includes(
country.toUpperCase()
);
},
get isAutofillEnabled() {
return this.isAutofillAddressesEnabled || this.isAutofillCreditCardsEnabled;
Expand All @@ -101,14 +103,25 @@ export const FormAutofill = {
/**
* Determines if the address autofill feature is available to use in the browser.
* If the feature is not available, then there are no user facing ways to enable it.
* Two conditions must be met for the autofill feature to be considered available:
* 1. Address autofill support is confirmed when:
* - `extensions.formautofill.addresses.supported` is set to `on`.
* - The user is located in a region supported by the feature
* (`extensions.formautofill.creditCards.supportedCountries`).
* 2. Address autofill is enabled through a Nimbus experiment:
* - The experiment pref `extensions.formautofill.addresses.experiments.enabled` is set to true.
*
* @returns {boolean} `true` if address autofill is available
*/
get isAutofillAddressesAvailable() {
return this._isSupportedRegion(
const isUserInSupportedRegion = this._isSupportedRegion(
FormAutofill._isAutofillAddressesAvailable,
FormAutofill._addressAutofillSupportedCountries
);
return (
isUserInSupportedRegion ||
FormAutofill._isAutofillAddressesAvailableInExperiment
);
},
/**
* Determines if the user has enabled or disabled credit card autofill.
Expand Down Expand Up @@ -208,11 +221,6 @@ XPCOMUtils.defineLazyPreferenceGetter(
"isAutofillAddressesCaptureEnabled",
ENABLED_AUTOFILL_ADDRESSES_CAPTURE_PREF
);
XPCOMUtils.defineLazyPreferenceGetter(
FormAutofill,
"isAutofillAddressesCaptureV2Enabled",
ENABLED_AUTOFILL_ADDRESSES_CAPTURE_V2_PREF
);
XPCOMUtils.defineLazyPreferenceGetter(
FormAutofill,
"_isAutofillCreditCardsAvailable",
Expand Down Expand Up @@ -261,25 +269,28 @@ XPCOMUtils.defineLazyPreferenceGetter(
XPCOMUtils.defineLazyPreferenceGetter(
FormAutofill,
"captureOnFormRemoval",
ENABLED_AUTOFILL_CAPTURE_ON_FORM_REMOVAL
ENABLED_AUTOFILL_CAPTURE_ON_FORM_REMOVAL_PREF
);
XPCOMUtils.defineLazyPreferenceGetter(
FormAutofill,
"captureOnPageNavigation",
ENABLED_AUTOFILL_CAPTURE_ON_PAGE_NAVIGATION
ENABLED_AUTOFILL_CAPTURE_ON_PAGE_NAVIGATION_PREF
);
XPCOMUtils.defineLazyPreferenceGetter(
FormAutofill,
"addressCaptureRequiredFields",
ENABLED_AUTOFILL_ADDRESSES_CAPTURE_REQUIRED_FIELDS_PREF,
null,
null,
val => val?.split(",").filter(v => !!v)
);

XPCOMUtils.defineLazyPreferenceGetter(
FormAutofill,
"_isAutofillAddressesAvailableInExperiment",
"extensions.formautofill.addresses.experiments.enabled"
);

// XXX: This should be invalidated on intl:app-locales-changed.
ChromeUtils.defineLazyGetter(FormAutofill, "countries", () => {
let availableRegionCodes =
Services.intl.getAvailableLocaleDisplayNames("region");
let displayNames = Services.intl.getRegionDisplayNames(
undefined,
availableRegionCodes
);
let result = new Map();
for (let i = 0; i < availableRegionCodes.length; i++) {
result.set(availableRegionCodes[i].toUpperCase(), displayNames[i]);
}
return result;
});
ChromeUtils.defineLazyGetter(FormAutofill, "countries", () =>
AddressMetaDataLoader.getCountries()
);
32 changes: 25 additions & 7 deletions firefox-ios/Client/Assets/CC_Script/FormAutofillChild.ios.sys.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import { FormAutofillUtils } from "resource://gre/modules/shared/FormAutofillUtils.sys.mjs";
import { FormStateManager } from "resource://gre/modules/shared/FormStateManager.sys.mjs";
import { CreditCardRecord } from "resource://gre/modules/shared/CreditCardRecord.sys.mjs";
import { AddressRecord } from "resource://gre/modules/shared/AddressRecord.sys.mjs";

export class FormAutofillChild {
/**
Expand Down Expand Up @@ -33,20 +34,21 @@ export class FormAutofillChild {

_doIdentifyAutofillFields(element) {
this.fieldDetailsManager.updateActiveInput(element);
const validDetails =
this.fieldDetailsManager.identifyAutofillFields(element);
this.fieldDetailsManager.identifyAutofillFields(element);

const activeFieldName =
this.fieldDetailsManager.activeFieldDetail?.fieldName;

const activeFieldDetails =
this.fieldDetailsManager.activeSection?.fieldDetails;

// Only ping swift if current field is either a cc or address field
if (!validDetails?.find(field => field.element === element)) {
if (!activeFieldDetails?.find(field => field.element === element)) {
return;
}

const fieldNamesWithValues =
this.transformToFieldNamesWithValues(validDetails);

this.transformToFieldNamesWithValues(activeFieldDetails);
if (FormAutofillUtils.isAddressField(activeFieldName)) {
this.callbacks.address.autofill(fieldNamesWithValues);
} else if (FormAutofillUtils.isCreditCardField(activeFieldName)) {
Expand Down Expand Up @@ -76,10 +78,15 @@ export class FormAutofillChild {
this._doIdentifyAutofillFields(element);
}

onSubmit(evt) {
onSubmit(_event) {
if (!this.fieldDetailsManager.activeHandler) {
return;
}

this.fieldDetailsManager.activeHandler.onFormSubmitted();
const records = this.fieldDetailsManager.activeHandler.createRecords();
if (records.creditCard) {

if (records.creditCard.length) {
// Normalize record format so we always get a consistent
// credit card record format: {cc-number, cc-name, cc-exp-month, cc-exp-year}
const creditCardRecords = records.creditCard.map(entry => {
Expand All @@ -94,6 +101,17 @@ export class FormAutofillChild {
}

fillFormFields(payload) {
// In iOS, we have access only to valid fields (https://github.com/mozilla/application-services/blob/9054db4bb5031881550ceab3448665ef6499a706/components/autofill/src/autofill.udl#L59-L76) for an address;
// all additional data must be computed. On Desktop, computed fields are handled in FormAutofillStorageBase.sys.mjs at the time of saving. Ideally, we should centralize
// all transformations, computations, and normalization processes within AddressRecord.sys.mjs to maintain a unified implementation across both platforms.
// This will be addressed in FXCM-810, aiming to simplify our data representation for both credit cards and addresses.
if (
FormAutofillUtils.isAddressField(
this.fieldDetailsManager.activeFieldDetail?.fieldName
)
) {
AddressRecord.computeFields(payload);
}
this.fieldDetailsManager.activeHandler.autofillFormFields(payload);
}
}
Expand Down
58 changes: 14 additions & 44 deletions firefox-ios/Client/Assets/CC_Script/FormAutofillHandler.sys.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,6 @@ export class FormAutofillHandler {
// The window to which this form belongs
window = null;

// A WindowUtils reference of which Window the form belongs
winUtils = null;

// DOM Form element to which this object is attached
form = null;

Expand All @@ -41,8 +38,10 @@ export class FormAutofillHandler {
// Caches the element to section mapping
#cachedSectionByElement = new WeakMap();

// Keeps track of filled state for all identified elements
// Keeps track of filled state for all identified elements,
// used only for telemetry.
#filledStateByElement = new WeakMap();

/**
* Array of collected data about relevant form fields. Each item is an object
* storing the identifying details of the field and a reference to the
Expand Down Expand Up @@ -76,17 +75,6 @@ export class FormAutofillHandler {
this._updateForm(form);

this.window = this.form.rootElement.ownerGlobal;
this.winUtils = this.window.windowUtils;

// Enum for form autofill MANUALLY_MANAGED_STATES values
this.FIELD_STATE_ENUM = {
// not themed
[FIELD_STATES.NORMAL]: null,
// highlighted
[FIELD_STATES.AUTO_FILLED]: "autofill",
// highlighted && grey color text
[FIELD_STATES.PREVIEW]: "-moz-autofill-preview",
};

/**
* This function is used if the form handler (or one of its sections)
Expand Down Expand Up @@ -125,6 +113,9 @@ export class FormAutofillHandler {
this.onAutofillCallback();
}

// This uses the #filledStateByElement map instead of
// autofillState as the state has already been cleared by the time
// the input event fires.
if (this.getFilledStateByElement(target) == FIELD_STATES.NORMAL) {
return;
}
Expand Down Expand Up @@ -287,10 +278,10 @@ export class FormAutofillHandler {
*
* @param {object} fieldDetail
* A fieldDetail of which its element is about to update the state.
* @param {string} nextState
* Used to determine the next state
* @param {string} state
* The state to apply.
*/
changeFieldState(fieldDetail, nextState) {
changeFieldState(fieldDetail, state) {
const element = fieldDetail.element;
if (!element) {
this.log.warn(
Expand All @@ -299,42 +290,21 @@ export class FormAutofillHandler {
);
return;
}
if (!(nextState in this.FIELD_STATE_ENUM)) {

if (!Object.values(FIELD_STATES).includes(state)) {
this.log.warn(
fieldDetail.fieldName,
"is trying to change to an invalid state"
);
return;
}

if (this.#filledStateByElement.get(element) == nextState) {
return;
}

let nextStateValue = null;
for (const [state, mmStateValue] of Object.entries(this.FIELD_STATE_ENUM)) {
// The NORMAL state is simply the absence of other manually
// managed states so we never need to add or remove it.
if (!mmStateValue) {
continue;
}

if (state == nextState) {
nextStateValue = mmStateValue;
} else {
this.winUtils.removeManuallyManagedState(element, mmStateValue);
}
}

if (nextStateValue) {
this.winUtils.addManuallyManagedState(element, nextStateValue);
}
element.autofillState = state;
this.#filledStateByElement.set(element, state);

if (nextState == FIELD_STATES.AUTO_FILLED) {
if (state == FIELD_STATES.AUTO_FILLED) {
element.addEventListener("input", this, { mozSystemGroup: true });
}

this.#filledStateByElement.set(element, nextState);
}

/**
Expand Down
Loading

0 comments on commit c66c21f

Please sign in to comment.