tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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 });