formautofill_parent_utils.js (7727B)
1 /* eslint-env mozilla/chrome-script */ 2 3 "use strict"; 4 5 const { FormAutofill } = ChromeUtils.importESModule( 6 "resource://autofill/FormAutofill.sys.mjs" 7 ); 8 const { FormAutofillUtils } = ChromeUtils.importESModule( 9 "resource://gre/modules/shared/FormAutofillUtils.sys.mjs" 10 ); 11 const { OSKeyStoreTestUtils } = ChromeUtils.importESModule( 12 "resource://testing-common/OSKeyStoreTestUtils.sys.mjs" 13 ); 14 15 let { formAutofillStorage } = ChromeUtils.importESModule( 16 "resource://autofill/FormAutofillStorage.sys.mjs" 17 ); 18 19 const { ADDRESSES_COLLECTION_NAME, CREDITCARDS_COLLECTION_NAME } = 20 FormAutofillUtils; 21 22 let destroyed = false; 23 24 var ParentUtils = { 25 getFormAutofillActor() { 26 let win = Services.wm.getMostRecentWindow("navigator:browser"); 27 let selectedBrowser = win.gBrowser.selectedBrowser; 28 return selectedBrowser.browsingContext.currentWindowGlobal.getActor( 29 "FormAutofill" 30 ); 31 }, 32 33 _getRecords(collectionName) { 34 return this.getFormAutofillActor() 35 .receiveMessage({ 36 name: "FormAutofill:GetRecords", 37 data: { 38 searchString: "", 39 collectionName, 40 }, 41 }) 42 .then(result => result.records); 43 }, 44 45 async _storageChangeObserved({ 46 topic = "formautofill-storage-changed", 47 type, 48 times = 1, 49 }) { 50 let count = times; 51 52 return new Promise(resolve => { 53 Services.obs.addObserver(function observer(subject, obsTopic, data) { 54 if ((type && data != type) || !!--count) { 55 return; 56 } 57 58 // every notification type should have the collection name. 59 // We're not allowed to trigger assertions during mochitest 60 // cleanup functions. 61 if (!destroyed) { 62 let allowedNames = [ 63 ADDRESSES_COLLECTION_NAME, 64 CREDITCARDS_COLLECTION_NAME, 65 ]; 66 assert.ok( 67 allowedNames.includes(subject.wrappedJSObject.collectionName), 68 "should include the collection name" 69 ); 70 // every notification except removeAll should have a guid. 71 if (data != "removeAll") { 72 assert.ok(subject.wrappedJSObject.guid, "should have a guid"); 73 } 74 } 75 Services.obs.removeObserver(observer, obsTopic); 76 resolve(); 77 }, topic); 78 }); 79 }, 80 81 async _operateRecord(collectionName, type, msgData) { 82 let msgName, times, topic; 83 84 if (collectionName == ADDRESSES_COLLECTION_NAME) { 85 switch (type) { 86 case "add": { 87 msgName = "FormAutofill:SaveAddress"; 88 break; 89 } 90 case "update": { 91 msgName = "FormAutofill:SaveAddress"; 92 break; 93 } 94 case "remove": { 95 msgName = "FormAutofill:RemoveAddresses"; 96 times = msgData.guids.length; 97 break; 98 } 99 default: 100 return; 101 } 102 } else { 103 switch (type) { 104 case "add": { 105 msgData = Object.assign({}, msgData); 106 msgName = "FormAutofill:SaveCreditCard"; 107 break; 108 } 109 case "remove": { 110 msgName = "FormAutofill:RemoveCreditCards"; 111 times = msgData.guids.length; 112 break; 113 } 114 default: 115 return; 116 } 117 } 118 119 let storageChangePromise = this._storageChangeObserved({ 120 type, 121 times, 122 topic, 123 }); 124 this.getFormAutofillActor().receiveMessage({ 125 name: msgName, 126 data: msgData, 127 }); 128 await storageChangePromise; 129 }, 130 131 async operateAddress(type, msgData) { 132 await this._operateRecord(ADDRESSES_COLLECTION_NAME, type, msgData); 133 }, 134 135 async operateCreditCard(type, msgData) { 136 await this._operateRecord(CREDITCARDS_COLLECTION_NAME, type, msgData); 137 }, 138 139 async cleanUpAddresses() { 140 const guids = (await this._getRecords(ADDRESSES_COLLECTION_NAME)).map( 141 record => record.guid 142 ); 143 144 if (!guids.length) { 145 return; 146 } 147 148 await this.operateAddress("remove", { guids }); 149 }, 150 151 async cleanUpCreditCards() { 152 if (!FormAutofill.isAutofillCreditCardsAvailable) { 153 return; 154 } 155 const guids = (await this._getRecords(CREDITCARDS_COLLECTION_NAME)).map( 156 record => record.guid 157 ); 158 159 if (!guids.length) { 160 return; 161 } 162 163 await this.operateCreditCard("remove", { guids }); 164 }, 165 166 setup() { 167 OSKeyStoreTestUtils.setup(); 168 }, 169 170 async cleanup() { 171 await this.cleanUpAddresses(); 172 await this.cleanUpCreditCards(); 173 await OSKeyStoreTestUtils.cleanup(); 174 175 Services.obs.removeObserver(this, "formautofill-storage-changed"); 176 }, 177 178 _areRecordsMatching(recordA, recordB, collectionName) { 179 for (let field of formAutofillStorage[collectionName].VALID_FIELDS) { 180 if (recordA[field] !== recordB[field]) { 181 return false; 182 } 183 } 184 // Check the internal field if both addresses have valid value. 185 for (let field of formAutofillStorage.INTERNAL_FIELDS) { 186 if ( 187 field in recordA && 188 field in recordB && 189 recordA[field] !== recordB[field] 190 ) { 191 return false; 192 } 193 } 194 return true; 195 }, 196 197 async _checkRecords(collectionName, expectedRecords) { 198 const records = await this._getRecords(collectionName); 199 200 if (records.length !== expectedRecords.length) { 201 return false; 202 } 203 204 for (let record of records) { 205 let matching = expectedRecords.some(expectedRecord => { 206 return ParentUtils._areRecordsMatching( 207 record, 208 expectedRecord, 209 collectionName 210 ); 211 }); 212 213 if (!matching) { 214 return false; 215 } 216 } 217 218 return true; 219 }, 220 221 async checkAddresses({ expectedAddresses }) { 222 return this._checkRecords(ADDRESSES_COLLECTION_NAME, expectedAddresses); 223 }, 224 225 async checkCreditCards({ expectedCreditCards }) { 226 return this._checkRecords(CREDITCARDS_COLLECTION_NAME, expectedCreditCards); 227 }, 228 229 observe(subject, topic, data) { 230 if (!destroyed) { 231 assert.ok(topic === "formautofill-storage-changed"); 232 } 233 sendAsyncMessage("formautofill-storage-changed", { 234 subject: null, 235 topic, 236 data, 237 }); 238 }, 239 }; 240 241 Services.obs.addObserver(ParentUtils, "formautofill-storage-changed"); 242 243 Services.mm.addMessageListener("FormAutofill:FieldsIdentified", () => { 244 return null; 245 }); 246 247 addMessageListener("FormAutofillTest:AddAddress", msg => { 248 return ParentUtils.operateAddress("add", msg); 249 }); 250 251 addMessageListener("FormAutofillTest:RemoveAddress", msg => { 252 return ParentUtils.operateAddress("remove", msg); 253 }); 254 255 addMessageListener("FormAutofillTest:UpdateAddress", msg => { 256 return ParentUtils.operateAddress("update", msg); 257 }); 258 259 addMessageListener("FormAutofillTest:CheckAddresses", msg => { 260 return ParentUtils.checkAddresses(msg); 261 }); 262 263 addMessageListener("FormAutofillTest:CleanUpAddresses", _msg => { 264 return ParentUtils.cleanUpAddresses(); 265 }); 266 267 addMessageListener("FormAutofillTest:AddCreditCard", msg => { 268 return ParentUtils.operateCreditCard("add", msg); 269 }); 270 271 addMessageListener("FormAutofillTest:RemoveCreditCard", msg => { 272 return ParentUtils.operateCreditCard("remove", msg); 273 }); 274 275 addMessageListener("FormAutofillTest:CheckCreditCards", msg => { 276 return ParentUtils.checkCreditCards(msg); 277 }); 278 279 addMessageListener("FormAutofillTest:CleanUpCreditCards", _msg => { 280 return ParentUtils.cleanUpCreditCards(); 281 }); 282 283 addMessageListener("FormAutofillTest:CanTestOSKeyStoreLogin", _msg => { 284 return { canTest: OSKeyStoreTestUtils.canTestOSKeyStoreLogin() }; 285 }); 286 287 addMessageListener("FormAutofillTest:OSKeyStoreLogin", msg => { 288 OSKeyStoreTestUtils.waitForOSKeyStoreLogin(msg.login); 289 }); 290 291 addMessageListener("setup", async _msg => ParentUtils.setup()); 292 293 addMessageListener("cleanup", async _msg => { 294 destroyed = true; 295 await ParentUtils.cleanup(); 296 });