tor-browser

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

browser_formdata_xpath.js (6685B)


      1 /* Any copyright is dedicated to the Public Domain.
      2 * http://creativecommons.org/publicdomain/zero/1.0/ */
      3 
      4 "use strict";
      5 
      6 const URL = ROOT + "browser_formdata_xpath_sample.html";
      7 
      8 /**
      9 * Bug 346337 - Generic form data restoration tests.
     10 */
     11 add_setup(function () {
     12  // make sure we don't save form data at all (except for tab duplication)
     13  Services.prefs.setIntPref("browser.sessionstore.privacy_level", 2);
     14 
     15  registerCleanupFunction(() => {
     16    Services.prefs.clearUserPref("browser.sessionstore.privacy_level");
     17  });
     18 });
     19 
     20 const FILE1 = createFilePath("346337_test1.file");
     21 const FILE2 = createFilePath("346337_test2.file");
     22 
     23 const FIELDS = {
     24  "//input[@name='input']": Date.now().toString(16),
     25  "//input[@name='spaced 1']": Math.random().toString(),
     26  "//input[3]": "three",
     27  "//input[@type='checkbox']": true,
     28  "//input[@name='uncheck']": false,
     29  "//input[@type='radio'][1]": false,
     30  "//input[@type='radio'][2]": true,
     31  "//input[@type='radio'][3]": false,
     32  "//select": 2,
     33  "//select[@multiple]": [1, 3],
     34  "//textarea[1]": "",
     35  "//textarea[2]": "Some text... " + Math.random(),
     36  "//textarea[3]": "Some more text\n" + new Date(),
     37  "//input[@type='file'][1]": [FILE1],
     38  "//input[@type='file'][2]": [FILE1, FILE2],
     39 };
     40 
     41 add_task(async function test_form_data_restoration() {
     42  // Load page with some input fields.
     43  let tab = BrowserTestUtils.addTab(gBrowser, URL);
     44  let browser = tab.linkedBrowser;
     45  await promiseBrowserLoaded(browser);
     46 
     47  // Fill in some values.
     48  for (let xpath of Object.keys(FIELDS)) {
     49    await setFormValue(browser, xpath);
     50  }
     51 
     52  // Duplicate the tab.
     53  let tab2 = gBrowser.duplicateTab(tab);
     54  let browser2 = tab2.linkedBrowser;
     55  await promiseTabRestored(tab2);
     56 
     57  // Check that all form values have been duplicated.
     58  for (let xpath of Object.keys(FIELDS)) {
     59    let expected = JSON.stringify(FIELDS[xpath]);
     60    let actual = JSON.stringify(await getFormValue(browser2, xpath));
     61    is(
     62      actual,
     63      expected,
     64      'The value for "' + xpath + '" was correctly restored'
     65    );
     66  }
     67 
     68  // Remove all tabs.
     69  await promiseRemoveTabAndSessionState(tab2);
     70  await promiseRemoveTabAndSessionState(tab);
     71 
     72  // Restore one of the tabs again.
     73  tab = ss.undoCloseTab(window, 0);
     74  browser = tab.linkedBrowser;
     75  await promiseTabRestored(tab);
     76 
     77  // Check that none of the form values have been restored due to the privacy
     78  // level settings.
     79  for (let xpath of Object.keys(FIELDS)) {
     80    let expected = FIELDS[xpath];
     81    if (expected) {
     82      let actual = await getFormValue(browser, xpath, expected);
     83      isnot(
     84        actual,
     85        expected,
     86        'The value for "' + xpath + '" was correctly discarded'
     87      );
     88    }
     89  }
     90 
     91  // Cleanup.
     92  BrowserTestUtils.removeTab(tab);
     93 });
     94 
     95 function getPropertyOfXPath(browserContext, path, propName) {
     96  return SpecialPowers.spawn(
     97    browserContext,
     98    [path, propName],
     99    (pathChild, propNameChild) => {
    100      let doc = content.document;
    101      let xptype = doc.defaultView.XPathResult.FIRST_ORDERED_NODE_TYPE;
    102      let node = doc.evaluate(
    103        pathChild,
    104        doc,
    105        null,
    106        xptype,
    107        null
    108      ).singleNodeValue;
    109      return node[propNameChild];
    110    }
    111  );
    112 }
    113 
    114 function setPropertyOfXPath(browserContext, path, propName, newValue) {
    115  return SpecialPowers.spawn(
    116    browserContext,
    117    [path, propName, newValue],
    118    (pathChild, propNameChild, newValueChild) => {
    119      let doc = content.document;
    120      let xptype = doc.defaultView.XPathResult.FIRST_ORDERED_NODE_TYPE;
    121      let node = doc.evaluate(
    122        pathChild,
    123        doc,
    124        null,
    125        xptype,
    126        null
    127      ).singleNodeValue;
    128      node[propNameChild] = newValueChild;
    129 
    130      let event = node.ownerDocument.createEvent("UIEvents");
    131      event.initUIEvent("input", true, true, node.ownerGlobal, 0);
    132      node.dispatchEvent(event);
    133    }
    134  );
    135 }
    136 
    137 function execUsingXPath(browserContext, path, fnName, arg) {
    138  return SpecialPowers.spawn(
    139    browserContext,
    140    [path, fnName, arg],
    141    (pathChild, fnNameChild, argChild) => {
    142      let doc = content.document;
    143      let xptype = doc.defaultView.XPathResult.FIRST_ORDERED_NODE_TYPE;
    144      let node = doc.evaluate(
    145        pathChild,
    146        doc,
    147        null,
    148        xptype,
    149        null
    150      ).singleNodeValue;
    151 
    152      switch (fnNameChild) {
    153        case "getMultipleSelected":
    154          return Array.from(node.options, (opt, idx) => idx).filter(
    155            idx => node.options[idx].selected
    156          );
    157        case "setMultipleSelected":
    158          Array.prototype.forEach.call(
    159            node.options,
    160            (opt, idx) => (opt.selected = argChild.indexOf(idx) > -1)
    161          );
    162          break;
    163        case "getFileNameArray":
    164          return node.mozGetFileNameArray();
    165        case "setFileNameArray":
    166          node.mozSetFileNameArray(argChild, argChild.length);
    167          break;
    168      }
    169 
    170      let event = node.ownerDocument.createEvent("UIEvents");
    171      event.initUIEvent("input", true, true, node.ownerGlobal, 0);
    172      node.dispatchEvent(event);
    173      return undefined;
    174    }
    175  );
    176 }
    177 
    178 function createFilePath(leaf) {
    179  let file = Services.dirsvc.get("TmpD", Ci.nsIFile);
    180  file.append(leaf);
    181  return file.path;
    182 }
    183 
    184 function isArrayOfNumbers(value) {
    185  return Array.isArray(value) && value.every(n => typeof n === "number");
    186 }
    187 
    188 function isArrayOfStrings(value) {
    189  return Array.isArray(value) && value.every(n => typeof n === "string");
    190 }
    191 
    192 function getFormValue(browser, xpath) {
    193  let value = FIELDS[xpath];
    194 
    195  if (typeof value == "string") {
    196    return getPropertyOfXPath(browser, xpath, "value");
    197  }
    198 
    199  if (typeof value == "boolean") {
    200    return getPropertyOfXPath(browser, xpath, "checked");
    201  }
    202 
    203  if (typeof value == "number") {
    204    return getPropertyOfXPath(browser, xpath, "selectedIndex");
    205  }
    206 
    207  if (isArrayOfNumbers(value)) {
    208    return execUsingXPath(browser, xpath, "getMultipleSelected");
    209  }
    210 
    211  if (isArrayOfStrings(value)) {
    212    return execUsingXPath(browser, xpath, "getFileNameArray");
    213  }
    214 
    215  throw new Error("unknown input type");
    216 }
    217 
    218 function setFormValue(browser, xpath) {
    219  let value = FIELDS[xpath];
    220 
    221  if (typeof value == "string") {
    222    return setPropertyOfXPath(browser, xpath, "value", value);
    223  }
    224 
    225  if (typeof value == "boolean") {
    226    return setPropertyOfXPath(browser, xpath, "checked", value);
    227  }
    228 
    229  if (typeof value == "number") {
    230    return setPropertyOfXPath(browser, xpath, "selectedIndex", value);
    231  }
    232 
    233  if (isArrayOfNumbers(value)) {
    234    return execUsingXPath(browser, xpath, "setMultipleSelected", value);
    235  }
    236 
    237  if (isArrayOfStrings(value)) {
    238    return execUsingXPath(browser, xpath, "setFileNameArray", value);
    239  }
    240 
    241  throw new Error("unknown input type");
    242 }