Merge mozilla-central to autoland. CLOSED TREE
[gecko.git] / toolkit / components / formautofill / FormAutofillChild.ios.sys.mjs
blob3183319fd937663bed693cfa1174eaa387c071b3
1 /* This Source Code Form is subject to the terms of the Mozilla Public
2  * License, v. 2.0. If a copy of the MPL was not distributed with this
3  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 /* eslint-disable no-undef,mozilla/balanced-listeners */
6 import { FormAutofillUtils } from "resource://gre/modules/shared/FormAutofillUtils.sys.mjs";
7 import { FormStateManager } from "resource://gre/modules/shared/FormStateManager.sys.mjs";
8 import { CreditCardRecord } from "resource://gre/modules/shared/CreditCardRecord.sys.mjs";
9 import { AddressRecord } from "resource://gre/modules/shared/AddressRecord.sys.mjs";
11 export class FormAutofillChild {
12   /**
13    * Creates an instance of FormAutofillChild.
14    *
15    * @param {object} callbacks - An object containing callback functions.
16    * @param {object} callbacks.address - Callbacks related to addresses.
17    * @param {Function} callbacks.address.autofill - Function called to autofill address fields.
18    * @param {Function} callbacks.address.submit - Function called on address form submission.
19    * @param {object} callbacks.creditCard - Callbacks related to credit cards.
20    * @param {Function} callbacks.creditCard.autofill - Function called to autofill credit card fields.
21    * @param {Function} callbacks.creditCard.submit - Function called on credit card form submission.
22    */
23   constructor(callbacks) {
24     this.onFocusIn = this.onFocusIn.bind(this);
25     this.onSubmit = this.onSubmit.bind(this);
27     this.callbacks = callbacks;
29     this.fieldDetailsManager = new FormStateManager();
31     document.addEventListener("focusin", this.onFocusIn);
32     document.addEventListener("submit", this.onSubmit);
33   }
35   _doIdentifyAutofillFields(element) {
36     this.fieldDetailsManager.updateActiveInput(element);
37     this.fieldDetailsManager.identifyAutofillFields(element);
39     const activeFieldName =
40       this.fieldDetailsManager.activeFieldDetail?.fieldName;
42     const activeFieldDetails =
43       this.fieldDetailsManager.activeSection?.fieldDetails;
45     // Only ping swift if current field is either a cc or address field
46     if (!activeFieldDetails?.find(field => field.element === element)) {
47       return;
48     }
50     const fieldNamesWithValues =
51       this.transformToFieldNamesWithValues(activeFieldDetails);
52     if (FormAutofillUtils.isAddressField(activeFieldName)) {
53       this.callbacks.address.autofill(fieldNamesWithValues);
54     } else if (FormAutofillUtils.isCreditCardField(activeFieldName)) {
55       // Normalize record format so we always get a consistent
56       // credit card record format: {cc-number, cc-name, cc-exp-month, cc-exp-year}
57       CreditCardRecord.normalizeFields(fieldNamesWithValues);
58       this.callbacks.creditCard.autofill(fieldNamesWithValues);
59     }
60   }
62   transformToFieldNamesWithValues(details) {
63     return details?.reduce(
64       (acc, field) => ({
65         ...acc,
66         [field.fieldName]: field.element.value,
67       }),
68       {}
69     );
70   }
72   onFocusIn(evt) {
73     const element = evt.target;
74     this.fieldDetailsManager.updateActiveInput(element);
75     if (!FormAutofillUtils.isCreditCardOrAddressFieldType(element)) {
76       return;
77     }
78     this._doIdentifyAutofillFields(element);
79   }
81   onSubmit(_event) {
82     if (!this.fieldDetailsManager.activeHandler) {
83       return;
84     }
86     this.fieldDetailsManager.activeHandler.onFormSubmitted();
87     const records = this.fieldDetailsManager.activeHandler.createRecords();
89     if (records.creditCard.length) {
90       // Normalize record format so we always get a consistent
91       // credit card record format: {cc-number, cc-name, cc-exp-month, cc-exp-year}
92       const creditCardRecords = records.creditCard.map(entry => {
93         CreditCardRecord.normalizeFields(entry.record);
94         return entry.record;
95       });
96       this.callbacks.creditCard.submit(creditCardRecords);
97     }
99     // TODO(FXSP-133 Phase 3): Support address capture
100     // this.callbacks.address.submit();
101   }
103   fillFormFields(payload) {
104     // 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;
105     // 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
106     // all transformations, computations, and normalization processes within AddressRecord.sys.mjs to maintain a unified implementation across both platforms.
107     // This will be addressed in FXCM-810, aiming to simplify our data representation for both credit cards and addresses.
108     if (
109       FormAutofillUtils.isAddressField(
110         this.fieldDetailsManager.activeFieldDetail?.fieldName
111       )
112     ) {
113       AddressRecord.computeFields(payload);
114     }
115     this.fieldDetailsManager.activeHandler.autofillFormFields(payload);
116   }
119 export default FormAutofillChild;