Skip to content

Commit 10215bc

Browse files
Refactor - auto update credential provider script
1 parent dd0f3ae commit 10215bc

10 files changed

+100
-244
lines changed

firefox-ios/Client/Assets/CC_Script/AutofillFormFactory.sys.mjs

+4
Original file line numberDiff line numberDiff line change
@@ -39,4 +39,8 @@ export const AutofillFormFactory = {
3939
}
4040
return lazy.FormLikeFactory.createFromField(aField, { ignoreForm });
4141
},
42+
43+
createFromDocumentRoot(aDocRoot) {
44+
return lazy.FormLikeFactory.createFromDocumentRoot(aDocRoot);
45+
},
4246
};

firefox-ios/Client/Assets/CC_Script/AutofillTelemetry.sys.mjs

-47
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,6 @@ class AutofillTelemetryBase {
1212
EVENT_CATEGORY = null;
1313
EVENT_OBJECT_FORM_INTERACTION = null;
1414

15-
HISTOGRAM_NUM_USES = null;
16-
HISTOGRAM_PROFILE_NUM_USES = null;
17-
HISTOGRAM_PROFILE_NUM_USES_KEY = null;
18-
1915
#initFormEventExtra(value) {
2016
let extra = {};
2117
for (const field of Object.values(this.SUPPORTED_FIELDS)) {
@@ -183,17 +179,6 @@ class AutofillTelemetryBase {
183179
throw new Error("Not implemented.");
184180
}
185181

186-
recordNumberOfUse(records) {
187-
let histogram = Services.telemetry.getKeyedHistogramById(
188-
this.HISTOGRAM_PROFILE_NUM_USES
189-
);
190-
histogram.clear();
191-
192-
for (let record of records) {
193-
histogram.add(this.HISTOGRAM_PROFILE_NUM_USES_KEY, record.timesUsed);
194-
}
195-
}
196-
197182
recordIframeLayoutDetection(flowId, fieldDetails) {
198183
const fieldsInMainFrame = [];
199184
const fieldsInIframe = [];
@@ -238,9 +223,6 @@ export class AddressTelemetry extends AutofillTelemetryBase {
238223
EVENT_OBJECT_FORM_INTERACTION = "AddressForm";
239224
EVENT_OBJECT_FORM_INTERACTION_EXT = "AddressFormExt";
240225

241-
HISTOGRAM_PROFILE_NUM_USES = "AUTOFILL_PROFILE_NUM_USES";
242-
HISTOGRAM_PROFILE_NUM_USES_KEY = "address";
243-
244226
// Fields that are recorded in `address_form` and `address_form_ext` telemetry
245227
SUPPORTED_FIELDS = {
246228
"street-address": "street_address",
@@ -316,10 +298,6 @@ class CreditCardTelemetry extends AutofillTelemetryBase {
316298
EVENT_CATEGORY = "creditcard";
317299
EVENT_OBJECT_FORM_INTERACTION = "CcFormV2";
318300

319-
HISTOGRAM_NUM_USES = "CREDITCARD_NUM_USES";
320-
HISTOGRAM_PROFILE_NUM_USES = "AUTOFILL_PROFILE_NUM_USES";
321-
HISTOGRAM_PROFILE_NUM_USES_KEY = "credit_card";
322-
323301
// Mapping of field name used in formautofill code to the field name
324302
// used in the telemetry.
325303
SUPPORTED_FIELDS = {
@@ -369,23 +347,6 @@ class CreditCardTelemetry extends AutofillTelemetryBase {
369347
}
370348
}
371349

372-
recordNumberOfUse(records) {
373-
super.recordNumberOfUse(records);
374-
375-
if (!this.HISTOGRAM_NUM_USES) {
376-
return;
377-
}
378-
379-
let histogram = Services.telemetry.getHistogramById(
380-
this.HISTOGRAM_NUM_USES
381-
);
382-
histogram.clear();
383-
384-
for (let record of records) {
385-
histogram.add(record.timesUsed);
386-
}
387-
}
388-
389350
recordAutofillProfileCount(count) {
390351
Glean.formautofillCreditcards.autofillProfilesCount.set(count);
391352
}
@@ -463,14 +424,6 @@ export class AutofillTelemetry {
463424
telemetry.recordAutofillProfileCount(count);
464425
}
465426

466-
/**
467-
* Utility functions for address/credit card number of use
468-
*/
469-
static recordNumberOfUse(type, records) {
470-
const telemetry = this.#getTelemetryByType(type);
471-
telemetry.recordNumberOfUse(records);
472-
}
473-
474427
static recordFormSubmissionHeuristicCount(label) {
475428
Glean.formautofill.formSubmissionHeuristic[label].add(1);
476429
}

firefox-ios/Client/Assets/CC_Script/FormAutofillHandler.sys.mjs

+48-18
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ const { FIELD_STATES } = FormAutofillUtils;
2424
export const FORM_CHANGE_REASON = {
2525
NODES_ADDED: "nodes-added",
2626
NODES_REMOVED: "nodes-removed",
27+
SELECT_OPTIONS_CHANGED: "select-options-changed",
2728
ELEMENT_INVISIBLE: "visible-element-became-invisible",
2829
ELEMENT_VISIBLE: "invisible-element-became-visible",
2930
};
@@ -268,6 +269,11 @@ export class FormAutofillHandler {
268269
return false;
269270
}
270271

272+
updateFormByElement(element) {
273+
const formLike = lazy.AutofillFormFactory.createFromField(element);
274+
this._updateForm(formLike);
275+
}
276+
271277
/**
272278
* Update the form with a new FormLike, and the related fields should be
273279
* updated or clear to ensure the data consistency.
@@ -344,7 +350,7 @@ export class FormAutofillHandler {
344350
}
345351

346352
/**
347-
* Resetting the state element's fieldDetail after it was removed from the form
353+
* Resetting the filled state after an element was removed from the form
348354
* Todo: We'll need to update this.filledResult in FormAutofillParent (Bug 1948077).
349355
*
350356
* @param {HTMLElement} element that was removed
@@ -353,8 +359,7 @@ export class FormAutofillHandler {
353359
if (this.getFilledStateByElement(element) != FIELD_STATES.AUTO_FILLED) {
354360
return;
355361
}
356-
const fieldDetail = this.getFieldDetailByElement(element);
357-
this.#filledStateByElement.delete(fieldDetail);
362+
this.#filledStateByElement.delete(element);
358363
}
359364

360365
/**
@@ -493,6 +498,14 @@ export class FormAutofillHandler {
493498
} else if (HTMLSelectElement.isInstance(element)) {
494499
const option = this.matchSelectOptions(fieldDetail, profile);
495500
if (!option) {
501+
if (
502+
this.getFilledStateByElement(element) == FIELD_STATES.AUTO_FILLED
503+
) {
504+
// The select element was previously autofilled, but there
505+
// is no matching option under the current set of options anymore.
506+
// Changing the state will also remove the highlighting from the element
507+
this.changeFieldState(fieldDetail, FIELD_STATES.NORMAL);
508+
}
496509
continue;
497510
}
498511

@@ -684,18 +697,21 @@ export class FormAutofillHandler {
684697
const mutationObserver = new this.window.MutationObserver(
685698
(mutations, _) => {
686699
const collectMutatedNodes = mutations => {
687-
let removedNodes = [];
688-
let addedNodes = [];
700+
let removedNodes = new Set();
701+
let addedNodes = new Set();
702+
let changedSelectElements = new Set();
689703
mutations.forEach(mutation => {
690704
if (mutation.type == "childList") {
691-
if (mutation.addedNodes.length) {
692-
addedNodes.push(...mutation.addedNodes);
705+
if (HTMLSelectElement.isInstance(mutation.target)) {
706+
changedSelectElements.add(mutation.target);
707+
} else if (mutation.addedNodes.length) {
708+
addedNodes.add(...mutation.addedNodes);
693709
} else if (mutation.removedNodes.length) {
694-
removedNodes.push(...mutation.removedNodes);
710+
removedNodes.add(...mutation.removedNodes);
695711
}
696712
}
697713
});
698-
return [addedNodes, removedNodes];
714+
return [addedNodes, removedNodes, changedSelectElements];
699715
};
700716

701717
const collectAllSubtreeElements = node => {
@@ -715,22 +731,35 @@ export class FormAutofillHandler {
715731
);
716732
};
717733

718-
let [addedNodes, removedNodes] = collectMutatedNodes(mutations);
719-
let relevantAddedElements = getCCAndAddressElements(addedNodes);
720-
// We only care about removed elements that might change the
721-
// currently detected fieldDetails
722-
let relevantRemovedElements = getCCAndAddressElements(
723-
removedNodes
724-
).filter(
734+
const [addedNodes, removedNodes, changedSelectElements] =
735+
collectMutatedNodes(mutations);
736+
let relevantAddedElements = getCCAndAddressElements([...addedNodes]);
737+
// We only care about removed elements and changed select options
738+
// from the current set of detected fieldDetails
739+
let relevantRemovedElements = getCCAndAddressElements([
740+
...removedNodes,
741+
]).filter(
742+
element =>
743+
this.#fieldDetails && !!this.getFieldDetailByElement(element)
744+
);
745+
let relevantChangedSelectElements = [...changedSelectElements].filter(
725746
element =>
726747
this.#fieldDetails && !!this.getFieldDetailByElement(element)
727748
);
728749

729-
if (!relevantRemovedElements.length && !relevantAddedElements.length) {
750+
if (
751+
!relevantRemovedElements.length &&
752+
!relevantAddedElements.length &&
753+
!relevantChangedSelectElements.length
754+
) {
730755
return;
731756
}
732757

733758
let changes = {};
759+
if (relevantChangedSelectElements.length) {
760+
changes[FORM_CHANGE_REASON.SELECT_OPTIONS_CHANGED] =
761+
relevantChangedSelectElements;
762+
}
734763
if (relevantRemovedElements.length) {
735764
changes[FORM_CHANGE_REASON.NODES_REMOVED] = relevantRemovedElements;
736765
}
@@ -875,7 +904,8 @@ export class FormAutofillHandler {
875904
const value = profile[fieldName];
876905

877906
let option = cache[value]?.deref();
878-
if (!option) {
907+
908+
if (!option || !option.isConnected) {
879909
option = FormAutofillUtils.findSelectOption(element, profile, fieldName);
880910

881911
if (option) {

firefox-ios/Client/Assets/CC_Script/FormAutofillSection.sys.mjs

+22-17
Original file line numberDiff line numberDiff line change
@@ -333,27 +333,32 @@ export class FormAutofillSection {
333333
return data;
334334
}
335335

336+
shouldAutofillField(fieldDetail) {
337+
// We don't save security code, but if somehow the profile has securty code,
338+
// make sure we don't autofill it.
339+
if (fieldDetail.fieldName == "cc-csc") {
340+
return false;
341+
}
342+
343+
// When both visible and invisible elements exist, we only autofill the
344+
// visible element.
345+
if (!fieldDetail.isVisible) {
346+
return !this.fieldDetails.some(
347+
field => field.fieldName == fieldDetail.fieldName && field.isVisible
348+
);
349+
}
350+
351+
return true;
352+
}
353+
336354
/**
337355
* Heuristics to determine which fields to autofill when a section contains
338356
* multiple fields of the same type.
339357
*/
340358
getAutofillFields() {
341-
return this.fieldDetails.filter(fieldDetail => {
342-
// We don't save security code, but if somehow the profile has securty code,
343-
// make sure we don't autofill it.
344-
if (fieldDetail.fieldName == "cc-csc") {
345-
return false;
346-
}
347-
348-
// When both visible and invisible elements exist, we only autofill the
349-
// visible element.
350-
if (!fieldDetail.isVisible) {
351-
return !this.fieldDetails.some(
352-
field => field.fieldName == fieldDetail.fieldName && field.isVisible
353-
);
354-
}
355-
return true;
356-
});
359+
return this.fieldDetails.filter(fieldDetail =>
360+
this.shouldAutofillField(fieldDetail)
361+
);
357362
}
358363

359364
/*
@@ -645,7 +650,6 @@ export class FormAutofillCreditCardSection extends FormAutofillSection {
645650
result = decrypted ? "success" : "fail_user_canceled";
646651
} catch (ex) {
647652
result = "fail_error";
648-
throw ex;
649653
} finally {
650654
Glean.formautofill.promptShownOsReauth.record({
651655
trigger: "autofill",
@@ -677,6 +681,7 @@ export class FormAutofillCreditCardSection extends FormAutofillSection {
677681
} catch (e) {
678682
errorResult = e.result;
679683
if (e.result != Cr.NS_ERROR_ABORT) {
684+
this.log.warn(`Decryption failed with result: ${e.result}`);
680685
throw e;
681686
}
682687
this.log.warn("User canceled encryption login");

firefox-ios/Client/Assets/CC_Script/FormAutofillUtils.sys.mjs

+3-1
Original file line numberDiff line numberDiff line change
@@ -456,7 +456,9 @@ FormAutofillUtils = {
456456
* @returns {boolean} true if the element can be autofilled
457457
*/
458458
isFieldAutofillable(element) {
459-
return element && !element.readOnly && !element.disabled;
459+
return (
460+
element && !element.readOnly && !element.disabled && element.isConnected
461+
);
460462
},
461463

462464
/**

firefox-ios/Client/Assets/CC_Script/Helpers.ios.mjs

+3-14
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,6 @@
55
import { IOSAppConstants } from "resource://gre/modules/shared/Constants.ios.mjs";
66
import Overrides from "resource://gre/modules/Overrides.ios.js";
77

8-
const EMPTY_MODULE_PATH = "EmptyModule.sys.mjs";
9-
108
/* eslint mozilla/use-isInstance: 0 */
119
HTMLSelectElement.isInstance = element => element instanceof HTMLSelectElement;
1210
HTMLInputElement.isInstance = element => element instanceof HTMLInputElement;
@@ -30,13 +28,6 @@ Object.defineProperty(HTMLInputElement.prototype, "hasBeenTypePassword", {
3028
configurable: true,
3129
});
3230

33-
Object.defineProperty(HTMLInputElement.prototype, "nodePrincipal", {
34-
get() {
35-
return { isNullPrincipal: false };
36-
},
37-
configurable: true,
38-
});
39-
4031
function setUserInput(value) {
4132
this.value = value;
4233

@@ -109,15 +100,13 @@ const internalModuleResolvers = {
109100
const moduleName = moduleURI.split("/").pop();
110101
const modulePath =
111102
"./" + (Overrides.ModuleOverrides[moduleName] ?? moduleName);
112-
return { module: moduleResolver(modulePath), path: modulePath };
103+
return moduleResolver(modulePath);
113104
},
114105

115106
resolveModules(obj, modules) {
116107
for (const [exportName, moduleURI] of Object.entries(modules)) {
117108
const resolvedModule = this.resolveModule(moduleURI);
118-
obj[exportName] = resolvedModule.path.includes(EMPTY_MODULE_PATH)
119-
? resolvedModule.module?.default
120-
: resolvedModule.module?.[exportName];
109+
obj[exportName] = resolvedModule?.[exportName];
121110
}
122111
},
123112
};
@@ -158,7 +147,7 @@ export const ChromeUtils = withNotImplementedError({
158147
internalModuleResolvers.resolveModules(obj, modules);
159148
},
160149
importESModule(moduleURI) {
161-
return internalModuleResolvers.resolveModule(moduleURI)?.module;
150+
return internalModuleResolvers.resolveModule(moduleURI);
162151
},
163152
});
164153
window.ChromeUtils = ChromeUtils;

firefox-ios/Client/Assets/CC_Script/HeuristicsRegExp.sys.mjs

+5-2
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,8 @@ export const HeuristicsRegExp = {
5656
"address-line3": "addrline3|address_3|addl3",
5757
"address-level2": "città", // it-IT
5858
"address-housenumber":
59-
"house\\s*number|hausnummer|haus|house[a-z\-]*n(r|o)",
59+
"(house|building)\\s*number|hausnummer|haus|house[a-z\-]*n(r|o)" +
60+
"|n[úu]mero",
6061
"address-level3":
6162
"(^address-?level-?3$)" +
6263
"|neighbou*rhood|barrio|bairro|colonia|suburb", // en/es/pt/mx/au/nz
@@ -653,10 +654,12 @@ export const HeuristicsRegExp = {
653654
"address-line2":
654655
"address|line" +
655656
"|house|building|apartment|floor" + // de-DE
657+
"|apartamento|" + // pt
656658
"|adresse" + // fr-FR
657659
"|indirizzo" + // it-IT
658660
"|地址" + // zh-CN
659-
"|주소", // ko-KR
661+
"|주소" + // ko-KR
662+
"|mieszkan(ie|ia)", // pl-PL
660663
},
661664
],
662665

0 commit comments

Comments
 (0)