editDialog.mjs (6241B)
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/. */ 4 5 /* eslint-disable mozilla/balanced-listeners */ // Not relevant since the document gets unloaded. 6 7 import { 8 getCurrentFormData, 9 canSubmitForm, 10 } from "chrome://formautofill/content/addressFormLayout.mjs"; 11 12 const lazy = {}; 13 ChromeUtils.defineESModuleGetters(lazy, { 14 AutofillTelemetry: "resource://gre/modules/shared/AutofillTelemetry.sys.mjs", 15 formAutofillStorage: "resource://autofill/FormAutofillStorage.sys.mjs", 16 }); 17 18 class AutofillEditDialog { 19 constructor(subStorageName, elements, record) { 20 this._storageInitPromise = lazy.formAutofillStorage.initialize(); 21 this._subStorageName = subStorageName; 22 this._elements = elements; 23 this._record = record; 24 this.localizeDocument(); 25 window.addEventListener("load", this, { once: true }); 26 } 27 28 async init() { 29 this.updateSaveButtonState(); 30 this.attachEventListeners(); 31 // For testing only: signal to tests that the dialog is ready for testing. 32 // This is likely no longer needed since retrieving from storage is fully 33 // handled in manageDialog.js now. 34 window.dispatchEvent(new CustomEvent("FormReadyForTests")); 35 } 36 37 /** 38 * Get storage and ensure it has been initialized. 39 * 40 * @returns {object} 41 */ 42 async getStorage() { 43 await this._storageInitPromise; 44 return lazy.formAutofillStorage[this._subStorageName]; 45 } 46 47 /** 48 * Asks FormAutofillParent to save or update an record. 49 * 50 * @param {object} record 51 * @param {string} guid [optional] 52 */ 53 async saveRecord(record, guid) { 54 let storage = await this.getStorage(); 55 if (guid) { 56 await storage.update(guid, record); 57 } else { 58 await storage.add(record); 59 } 60 } 61 62 /** 63 * Handle events 64 * 65 * @param {DOMEvent} event 66 */ 67 handleEvent(event) { 68 switch (event.type) { 69 case "load": { 70 this.init(); 71 break; 72 } 73 case "click": { 74 this.handleClick(event); 75 break; 76 } 77 case "input": { 78 this.handleInput(event); 79 break; 80 } 81 case "keypress": { 82 this.handleKeyPress(event); 83 break; 84 } 85 case "contextmenu": { 86 if ( 87 !HTMLInputElement.isInstance(event.target) && 88 !HTMLTextAreaElement.isInstance(event.target) 89 ) { 90 event.preventDefault(); 91 } 92 break; 93 } 94 } 95 } 96 97 /** 98 * Handle click events 99 * 100 * @param {DOMEvent} event 101 */ 102 handleClick(event) { 103 if (event.target == this._elements.cancel) { 104 window.close(); 105 } 106 if (event.target == this._elements.save) { 107 this.handleSubmit(); 108 } 109 } 110 111 /** 112 * Handle input events 113 */ 114 handleInput(_e) { 115 this.updateSaveButtonState(); 116 } 117 118 /** 119 * Handle key press events 120 * 121 * @param {DOMEvent} event 122 */ 123 handleKeyPress(event) { 124 if (event.keyCode == KeyEvent.DOM_VK_ESCAPE) { 125 window.close(); 126 } 127 } 128 129 updateSaveButtonState() { 130 // Toggle disabled attribute on the save button based on 131 // whether the form is filled or empty. 132 if (!Object.keys(this._elements.fieldContainer.buildFormObject()).length) { 133 this._elements.save.setAttribute("disabled", true); 134 } else { 135 this._elements.save.removeAttribute("disabled"); 136 } 137 } 138 139 /** 140 * Attach event listener 141 */ 142 attachEventListeners() { 143 window.addEventListener("keypress", this); 144 window.addEventListener("contextmenu", this); 145 this._elements.save.addEventListener("click", this); 146 this._elements.cancel.addEventListener("click", this); 147 document.addEventListener("input", this); 148 } 149 150 // An interface to be inherited. 151 localizeDocument() {} 152 153 recordFormSubmit() { 154 let method = this._record?.guid ? "edit" : "add"; 155 lazy.AutofillTelemetry.recordManageEvent(this.telemetryType, method); 156 } 157 } 158 159 export class EditAddressDialog extends AutofillEditDialog { 160 telemetryType = lazy.AutofillTelemetry.ADDRESS; 161 162 constructor(elements, record) { 163 super("addresses", elements, record); 164 if (record) { 165 lazy.AutofillTelemetry.recordManageEvent( 166 this.telemetryType, 167 "show_entry" 168 ); 169 } 170 } 171 172 localizeDocument() { 173 if (this._record?.guid) { 174 document.l10n.setAttributes( 175 this._elements.title, 176 "autofill-edit-address-title" 177 ); 178 } 179 } 180 181 updateSaveButtonState() { 182 // Toggle disabled attribute on the save button based on 183 // whether the form is filled or empty. 184 if (!canSubmitForm()) { 185 this._elements.save.setAttribute("disabled", true); 186 } else { 187 this._elements.save.removeAttribute("disabled"); 188 } 189 } 190 191 async handleSubmit() { 192 await this.saveRecord( 193 getCurrentFormData(), 194 this._record ? this._record.guid : null 195 ); 196 this.recordFormSubmit(); 197 198 window.close(); 199 } 200 } 201 202 export class EditCreditCardDialog extends AutofillEditDialog { 203 telemetryType = lazy.AutofillTelemetry.CREDIT_CARD; 204 205 constructor(elements, record) { 206 elements.fieldContainer._elements.billingAddress.disabled = true; 207 super("creditCards", elements, record); 208 elements.fieldContainer._elements.ccNumber.addEventListener( 209 "blur", 210 this._onCCNumberFieldBlur.bind(this) 211 ); 212 if (record) { 213 lazy.AutofillTelemetry.recordManageEvent( 214 this.telemetryType, 215 "show_entry" 216 ); 217 } 218 } 219 220 _onCCNumberFieldBlur() { 221 let elem = this._elements.fieldContainer._elements.ccNumber; 222 this._elements.fieldContainer.updateCustomValidity(elem); 223 } 224 225 localizeDocument() { 226 if (this._record?.guid) { 227 document.l10n.setAttributes( 228 this._elements.title, 229 "autofill-edit-card-title2" 230 ); 231 } 232 } 233 234 async handleSubmit() { 235 let creditCard = this._elements.fieldContainer.buildFormObject(); 236 if (!this._elements.fieldContainer._elements.form.reportValidity()) { 237 return; 238 } 239 240 try { 241 await this.saveRecord( 242 creditCard, 243 this._record ? this._record.guid : null 244 ); 245 246 this.recordFormSubmit(); 247 248 window.close(); 249 } catch (ex) { 250 console.error(ex); 251 } 252 } 253 }