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 Jul 4, 2024
1 parent 05e7312 commit 20548ce
Show file tree
Hide file tree
Showing 5 changed files with 61 additions and 202 deletions.
122 changes: 2 additions & 120 deletions firefox-ios/Client/Assets/CC_Script/AutofillTelemetry.sys.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -120,8 +120,7 @@ class AutofillTelemetryBase {
let element = fieldDetail.element;
let state = profile[fieldDetail.fieldName] ? "filled" : "not_filled";
if (
section.handler.getFilledStateByElement(element) ==
FIELD_STATES.NORMAL &&
element.autofillState == FIELD_STATES.NORMAL &&
(HTMLSelectElement.isInstance(element) ||
(HTMLInputElement.isInstance(element) && element.value.length))
) {
Expand Down Expand Up @@ -374,128 +373,11 @@ class CreditCardTelemetry extends AutofillTelemetryBase {
"cc-exp-year": "cc_exp_year",
};

recordLegacyFormEvent(method, flowId, extra = null) {
Services.telemetry.recordEvent(
this.EVENT_CATEGORY,
method,
"cc_form",
flowId,
extra
);
}

recordGleanFormEvent(eventName, flowId, extra) {
extra.flow_id = flowId;
Glean.formautofillCreditcards[eventName].record(extra);
}

recordFormDetected(section) {
super.recordFormDetected(section);

let identified = new Set();
section.fieldDetails.forEach(detail => {
identified.add(detail.fieldName);
});
let extra = {
cc_name_found: identified.has("cc-name") ? "true" : "false",
cc_number_found: identified.has("cc-number") ? "true" : "false",
cc_exp_found:
identified.has("cc-exp") ||
(identified.has("cc-exp-month") && identified.has("cc-exp-year"))
? "true"
: "false",
};

this.recordLegacyFormEvent("detected", section.flowId, extra);
}

recordPopupShown(section, fieldName) {
super.recordPopupShown(section, fieldName);

this.recordLegacyFormEvent("popup_shown", section.flowId);
}

recordFormFilled(section, profile) {
super.recordFormFilled(section, profile);
// Calculate values for telemetry
let extra = {
cc_name: "unavailable",
cc_number: "unavailable",
cc_exp: "unavailable",
};

for (let fieldDetail of section.fieldDetails) {
let element = fieldDetail.element;
let state = profile[fieldDetail.fieldName] ? "filled" : "not_filled";
if (
section.handler.getFilledStateByElement(element) ==
FIELD_STATES.NORMAL &&
(HTMLSelectElement.isInstance(element) ||
(HTMLInputElement.isInstance(element) && element.value.length))
) {
state = "user_filled";
}
switch (fieldDetail.fieldName) {
case "cc-name":
extra.cc_name = state;
break;
case "cc-number":
extra.cc_number = state;
break;
case "cc-exp":
case "cc-exp-month":
case "cc-exp-year":
extra.cc_exp = state;
break;
}
}

this.recordLegacyFormEvent("filled", section.flowId, extra);
}

recordFilledModified(section, fieldName) {
super.recordFilledModified(section, fieldName);

let extra = { field_name: fieldName };
this.recordLegacyFormEvent("filled_modified", section.flowId, extra);
}

/**
* Called when a credit card form is submitted
*
* @param {object} section Section that produces this record
* @param {object} record Credit card record filled in the form.
* @param {Array<HTMLForm>} form Form that contains the section
*/
recordFormSubmitted(section, record, form) {
super.recordFormSubmitted(section, record, form);

// For legacy cc_form event telemetry
let extra = {
fields_not_auto: "0",
fields_auto: "0",
fields_modified: "0",
};

if (record.guid !== null) {
let totalCount = form.elements.length;
let autofilledCount = Object.keys(record.record).length;
let unmodifiedCount = record.untouchedFields.length;

extra.fields_not_auto = (totalCount - autofilledCount).toString();
extra.fields_auto = autofilledCount.toString();
extra.fields_modified = (autofilledCount - unmodifiedCount).toString();
} else {
// If the `guid` is null, we're filling a new form.
// In that case, all not-null fields are manually filled.
extra.fields_not_auto = Array.from(form.elements)
.filter(element => !!element.value?.trim().length)
.length.toString();
}

this.recordLegacyFormEvent("submitted", section.flowId, extra);
}

recordNumberOfUse(records) {
super.recordNumberOfUse(records);

Expand Down Expand Up @@ -572,7 +454,7 @@ export class AutofillTelemetry {
* Utility functions for form event (defined in Events.yaml)
*
* Category: address or creditcard
* Event name: cc_form, cc_form_v2, or address_form
* Event name: cc_form_v2, or address_form
*/

static recordFormInteractionEvent(
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
34 changes: 16 additions & 18 deletions firefox-ios/Client/Assets/CC_Script/FormAutofillSection.sys.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,13 @@ export class FormAutofillSection {
return this.fieldDetails.find(detail => detail.fieldName == fieldName);
}

getFieldDetailByNamePreferVisible(fieldName) {
let fieldDetail = this.fieldDetails.find(
detail => detail.fieldName == fieldName && detail.isVisible
);
return fieldDetail || this.getFieldDetailByName(fieldName);
}

get allFieldNames() {
if (!this._cacheValue.allFieldNames) {
this._cacheValue.allFieldNames = this.fieldDetails.map(
Expand All @@ -175,7 +182,7 @@ export class FormAutofillSection {
}

for (const fieldName in profile) {
const fieldDetail = this.getFieldDetailByName(fieldName);
const fieldDetail = this.getFieldDetailByNamePreferVisible(fieldName);
const element = fieldDetail?.element;

if (!HTMLSelectElement.isInstance(element)) {
Expand Down Expand Up @@ -362,8 +369,7 @@ export class FormAutofillSection {
element == this.#focusedInput ||
(element != this.#focusedInput &&
(!element.value || element.value == element.defaultValue)) ||
this.handler.getFilledStateByElement(element) ==
FIELD_STATES.AUTO_FILLED
element.autofillState == FIELD_STATES.AUTO_FILLED
) {
this.fillFieldValue(element, value);
this.handler.changeFieldState(fieldDetail, FIELD_STATES.AUTO_FILLED);
Expand Down Expand Up @@ -453,16 +459,15 @@ export class FormAutofillSection {
// when clear the target set, such as <select>.
dimFieldDetails.push(fieldDetail);
} else {
isAutofilled |=
this.handler.getFilledStateByElement(element) ==
FIELD_STATES.AUTO_FILLED;
isAutofilled |= element.autofillState == FIELD_STATES.AUTO_FILLED;
}
}
if (!isAutofilled) {
// Restore the dim fields to initial state as well once we knew
// that user had intention to clear the filled form manually.
for (const fieldDetail of dimFieldDetails) {
// If we can't find a selected option, then we should just reset to the first option's value

let element = fieldDetail.element;
this._resetSelectElementValue(element);
this.handler.changeFieldState(fieldDetail, FIELD_STATES.NORMAL);
Expand All @@ -488,10 +493,7 @@ export class FormAutofillSection {

// We keep the state if this field has
// already been auto-filled.
if (
this.handler.getFilledStateByElement(element) ==
FIELD_STATES.AUTO_FILLED
) {
if (element.autofillState == FIELD_STATES.AUTO_FILLED) {
continue;
}

Expand All @@ -510,10 +512,7 @@ export class FormAutofillSection {
continue;
}

if (
this.handler.getFilledStateByElement(element) ==
FIELD_STATES.AUTO_FILLED
) {
if (element.autofillState == FIELD_STATES.AUTO_FILLED) {
if (HTMLInputElement.isInstance(element)) {
element.setUserInput("");
} else if (HTMLSelectElement.isInstance(element)) {
Expand All @@ -522,6 +521,8 @@ export class FormAutofillSection {
}
}
}

this.filledRecordGUID = null;
}

resetFieldStates() {
Expand Down Expand Up @@ -611,10 +612,7 @@ export class FormAutofillSection {

data.record[detail.fieldName] = value;

if (
this.handler.getFilledStateByElement(element) ==
FIELD_STATES.AUTO_FILLED
) {
if (element.autofillState == FIELD_STATES.AUTO_FILLED) {
data.untouchedFields.push(detail.fieldName);
}
});
Expand Down
Loading

0 comments on commit 20548ce

Please sign in to comment.