tor-browser

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

browser_caching_value.js (13246B)


      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 "use strict";
      6 
      7 /* import-globals-from ../../mochitest/states.js */
      8 loadScripts({ name: "states.js", dir: MOCHITESTS_DIR });
      9 
     10 /**
     11 * Test data has the format of:
     12 * {
     13 *   desc      {String}            description for better logging
     14 *   id        {String}            given accessible DOMNode ID
     15 *   expected  {String}            expected value for a given accessible
     16 *   action    {?AsyncFunction}    an optional action that awaits a value change
     17 *   attrs     {?Array}            an optional list of attributes to update
     18 *   waitFor   {?Number}           an optional value change event to wait for
     19 * }
     20 */
     21 const valueTests = [
     22  {
     23    desc: "Initially value is set to 1st element of select",
     24    id: "select",
     25    expected: "1st",
     26  },
     27  {
     28    desc: "Value should update to 3rd when 3 is pressed",
     29    id: "select",
     30    async action(browser) {
     31      await invokeFocus(browser, "select");
     32      await invokeContentTask(browser, [], () => {
     33        const { ContentTaskUtils } = ChromeUtils.importESModule(
     34          "resource://testing-common/ContentTaskUtils.sys.mjs"
     35        );
     36        const EventUtils = ContentTaskUtils.getEventUtils(content);
     37        EventUtils.synthesizeKey("3", {}, content);
     38      });
     39    },
     40    waitFor: EVENT_TEXT_VALUE_CHANGE,
     41    expected: "3rd",
     42  },
     43  {
     44    desc: "Initially value is set to @aria-valuenow for slider",
     45    id: "slider",
     46    expected: ["5", 5, 0, 7, 0],
     47  },
     48  {
     49    desc: "Value should change when @aria-valuenow is updated",
     50    id: "slider",
     51    attrs: [
     52      {
     53        attr: "aria-valuenow",
     54        value: "6",
     55      },
     56    ],
     57    waitFor: EVENT_VALUE_CHANGE,
     58    expected: ["6", 6, 0, 7, 0],
     59  },
     60  {
     61    desc: "Value should change when @aria-valuetext is set",
     62    id: "slider",
     63    attrs: [
     64      {
     65        attr: "aria-valuetext",
     66        value: "plain",
     67      },
     68    ],
     69    waitFor: EVENT_TEXT_VALUE_CHANGE,
     70    expected: ["plain", 6, 0, 7, 0],
     71  },
     72  {
     73    desc: "Value should change when @aria-valuetext is updated",
     74    id: "slider",
     75    attrs: [
     76      {
     77        attr: "aria-valuetext",
     78        value: "hey!",
     79      },
     80    ],
     81    waitFor: EVENT_TEXT_VALUE_CHANGE,
     82    expected: ["hey!", 6, 0, 7, 0],
     83  },
     84  {
     85    desc: "Value should change to @aria-valuetext when @aria-valuenow is removed",
     86    id: "slider",
     87    attrs: [
     88      {
     89        attr: "aria-valuenow",
     90      },
     91    ],
     92    expected: ["hey!", 3.5, 0, 7, 0],
     93  },
     94  {
     95    desc: "Initially value is not set for combobox",
     96    id: "combobox",
     97    expected: "",
     98  },
     99  {
    100    desc: "Value should change when @value attribute is updated",
    101    id: "combobox",
    102    attrs: [
    103      {
    104        attr: "value",
    105        value: "hello",
    106      },
    107    ],
    108    waitFor: EVENT_TEXT_VALUE_CHANGE,
    109    expected: "hello",
    110  },
    111  {
    112    desc: "Initially value corresponds to @value attribute for progress",
    113    id: "progress",
    114    expected: "22%",
    115  },
    116  {
    117    desc: "Value should change when @value attribute is updated",
    118    id: "progress",
    119    attrs: [
    120      {
    121        attr: "value",
    122        value: "50",
    123      },
    124    ],
    125    waitFor: EVENT_VALUE_CHANGE,
    126    expected: "50%",
    127  },
    128  {
    129    desc: "Setting currentValue on a progress accessible should fail",
    130    id: "progress",
    131    async action(browser, acc) {
    132      acc.QueryInterface(nsIAccessibleValue);
    133      try {
    134        acc.currentValue = 25;
    135        ok(false, "Setting currValue on progress element should fail");
    136      } catch (e) {}
    137    },
    138    expected: "50%",
    139  },
    140  {
    141    desc: "Initially value corresponds to @value attribute for range",
    142    id: "range",
    143    expected: "6",
    144  },
    145  {
    146    desc: "Value should change when slider is moved",
    147    id: "range",
    148    async action(browser) {
    149      await invokeFocus(browser, "range");
    150      await invokeContentTask(browser, [], () => {
    151        const { ContentTaskUtils } = ChromeUtils.importESModule(
    152          "resource://testing-common/ContentTaskUtils.sys.mjs"
    153        );
    154        const EventUtils = ContentTaskUtils.getEventUtils(content);
    155        EventUtils.synthesizeKey("VK_LEFT", {}, content);
    156      });
    157    },
    158    waitFor: EVENT_VALUE_CHANGE,
    159    expected: "5",
    160  },
    161  {
    162    desc: "Value should change when currentValue is called",
    163    id: "range",
    164    async action(browser, acc) {
    165      acc.QueryInterface(nsIAccessibleValue);
    166      acc.currentValue = 4;
    167    },
    168    waitFor: EVENT_VALUE_CHANGE,
    169    expected: "4",
    170  },
    171  {
    172    desc: "Initially textbox value is text subtree",
    173    id: "textbox",
    174    expected: "Some rich text",
    175  },
    176  {
    177    desc: "Textbox value changes when subtree changes",
    178    id: "textbox",
    179    async action(browser) {
    180      await invokeContentTask(browser, [], () => {
    181        let boldText = content.document.createElement("strong");
    182        boldText.textContent = " bold";
    183        content.document.getElementById("textbox").appendChild(boldText);
    184      });
    185    },
    186    waitFor: EVENT_TEXT_VALUE_CHANGE,
    187    expected: "Some rich text bold",
    188  },
    189 ];
    190 
    191 /**
    192 * Like testValue in accessible/tests/mochitest/value.js, but waits for cache
    193 * updates.
    194 */
    195 async function testValue(acc, value, currValue, minValue, maxValue, minIncr) {
    196  const pretty = prettyName(acc);
    197  await untilCacheIs(() => acc.value, value, `Wrong value of ${pretty}`);
    198 
    199  await untilCacheIs(
    200    () => acc.currentValue,
    201    currValue,
    202    `Wrong current value of ${pretty}`
    203  );
    204  await untilCacheIs(
    205    () => acc.minimumValue,
    206    minValue,
    207    `Wrong minimum value of ${pretty}`
    208  );
    209  await untilCacheIs(
    210    () => acc.maximumValue,
    211    maxValue,
    212    `Wrong maximum value of ${pretty}`
    213  );
    214  await untilCacheIs(
    215    () => acc.minimumIncrement,
    216    minIncr,
    217    `Wrong minimum increment value of ${pretty}`
    218  );
    219 }
    220 
    221 /**
    222 * Test caching of accessible object values
    223 */
    224 addAccessibleTask(
    225  `
    226  <div id="slider" role="slider" aria-valuenow="5"
    227       aria-valuemin="0" aria-valuemax="7">slider</div>
    228  <select id="select">
    229    <option>1st</option>
    230    <option>2nd</option>
    231    <option>3rd</option>
    232  </select>
    233  <input id="combobox" role="combobox" aria-autocomplete="inline">
    234  <progress id="progress" value="22" max="100"></progress>
    235  <input type="range" id="range" min="0" max="10" value="6">
    236  <div contenteditable="yes" role="textbox" id="textbox">Some <a href="#">rich</a> text</div>`,
    237  async function (browser, accDoc) {
    238    for (let { desc, id, action, attrs, expected, waitFor } of valueTests) {
    239      info(desc);
    240      let acc = findAccessibleChildByID(accDoc, id);
    241      let onUpdate;
    242 
    243      if (waitFor) {
    244        onUpdate = waitForEvent(waitFor, id);
    245      }
    246 
    247      if (action) {
    248        await action(browser, acc);
    249      } else if (attrs) {
    250        for (let { attr, value } of attrs) {
    251          await invokeSetAttribute(browser, id, attr, value);
    252        }
    253      }
    254 
    255      await onUpdate;
    256      if (Array.isArray(expected)) {
    257        acc.QueryInterface(nsIAccessibleValue);
    258        await testValue(acc, ...expected);
    259      } else {
    260        is(acc.value, expected, `Correct value for ${prettyName(acc)}`);
    261      }
    262    }
    263  },
    264  { iframe: true, remoteIframe: true }
    265 );
    266 
    267 /**
    268 * Test caching of link URL values.
    269 */
    270 addAccessibleTask(
    271  `<a id="link" href="https://example.com/">Test</a>`,
    272  async function (browser, docAcc) {
    273    let link = findAccessibleChildByID(docAcc, "link");
    274    is(link.value, "https://example.com/", "link initial value correct");
    275    const textLeaf = link.firstChild;
    276    is(textLeaf.value, "https://example.com/", "link initial value correct");
    277 
    278    info("Changing link href");
    279    await invokeSetAttribute(browser, "link", "href", "https://example.net/");
    280    await untilCacheIs(
    281      () => link.value,
    282      "https://example.net/",
    283      "link value correct after change"
    284    );
    285 
    286    info("Removing link href");
    287    let onRecreation = waitForEvents({
    288      expected: [
    289        [EVENT_HIDE, link],
    290        [EVENT_SHOW, "link"],
    291      ],
    292    });
    293    await invokeSetAttribute(browser, "link", "href");
    294    await onRecreation;
    295    link = findAccessibleChildByID(docAcc, "link");
    296    await untilCacheIs(() => link.value, "", "link value empty after removal");
    297 
    298    info("Setting link href");
    299    onRecreation = waitForEvents({
    300      expected: [
    301        [EVENT_HIDE, link],
    302        [EVENT_SHOW, "link"],
    303      ],
    304    });
    305    await invokeSetAttribute(browser, "link", "href", "https://example.com/");
    306    await onRecreation;
    307    link = findAccessibleChildByID(docAcc, "link");
    308    await untilCacheIs(
    309      () => link.value,
    310      "https://example.com/",
    311      "link value correct after change"
    312    );
    313  },
    314  { chrome: true, topLevel: true, iframe: true, remoteIframe: true }
    315 );
    316 
    317 /**
    318 * Test caching of active state for select options - see bug 1788143.
    319 */
    320 addAccessibleTask(
    321  `
    322  <select id="select">
    323    <option id="first_option">First</option>
    324    <option id="second_option">Second</option>
    325  </select>`,
    326  async function (browser, docAcc) {
    327    const select = findAccessibleChildByID(docAcc, "select");
    328    is(select.value, "First", "Select initial value correct");
    329 
    330    // Focus the combo box.
    331    await invokeFocus(browser, "select");
    332 
    333    // Select the second option (drop-down collapsed).
    334    let p = waitForEvents({
    335      expected: [
    336        [EVENT_SELECTION, "second_option"],
    337        [EVENT_TEXT_VALUE_CHANGE, "select"],
    338      ],
    339      unexpected: [
    340        stateChangeEventArgs("second_option", EXT_STATE_ACTIVE, true, true),
    341        stateChangeEventArgs("first_option", EXT_STATE_ACTIVE, false, true),
    342      ],
    343    });
    344    await invokeContentTask(browser, [], () => {
    345      content.document.getElementById("select").selectedIndex = 1;
    346    });
    347    await p;
    348 
    349    is(select.value, "Second", "Select value correct after changing option");
    350 
    351    // Expand the combobox dropdown.
    352    p = waitForEvent(EVENT_STATE_CHANGE, "ContentSelectDropdown");
    353    EventUtils.synthesizeKey("VK_SPACE");
    354    await p;
    355 
    356    p = waitForEvents({
    357      expected: [
    358        [EVENT_SELECTION, "first_option"],
    359        [EVENT_TEXT_VALUE_CHANGE, "select"],
    360        [EVENT_HIDE, "ContentSelectDropdown"],
    361      ],
    362      unexpected: [
    363        stateChangeEventArgs("first_option", EXT_STATE_ACTIVE, true, true),
    364        stateChangeEventArgs("second_option", EXT_STATE_ACTIVE, false, true),
    365      ],
    366    });
    367 
    368    // Press the up arrow to select the first option (drop-down expanded).
    369    // Then, press Enter to confirm the selection and close the dropdown.
    370    // We do both of these together to unify testing across platforms, since
    371    // events are not entirely consistent on Windows vs. Linux + macOS.
    372    EventUtils.synthesizeKey("VK_UP");
    373    EventUtils.synthesizeKey("VK_RETURN");
    374    await p;
    375 
    376    is(
    377      select.value,
    378      "First",
    379      "Select value correct after changing option back"
    380    );
    381  },
    382  { chrome: true, topLevel: true, iframe: true, remoteIframe: true }
    383 );
    384 
    385 /**
    386 * Test combobox values for non-editable comboboxes.
    387 */
    388 addAccessibleTask(
    389  `
    390  <div id="combo-div-1" role="combobox">value</div>
    391  <div id="combo-div-2" role="combobox">
    392    <div role="listbox">
    393      <div role="option">value</div>
    394    </div>
    395  </div>
    396  <div id="combo-div-3" role="combobox">
    397    <div role="group">value</div>
    398  </div>
    399  <div id="combo-div-4" role="combobox">foo
    400    <div role="listbox">
    401      <div role="option">bar</div>
    402    </div>
    403  </div>
    404 
    405  <input id="combo-input-1" role="combobox" value="value" disabled></input>
    406  <input id="combo-input-2" role="combobox" value="value" disabled>testing</input>
    407 
    408  <div id="combo-div-selected" role="combobox">
    409    <div role="listbox">
    410      <div aria-selected="true" role="option">value</div>
    411    </div>
    412  </div>
    413 `,
    414  async function (browser, docAcc) {
    415    const comboDiv1 = findAccessibleChildByID(docAcc, "combo-div-1");
    416    const comboDiv2 = findAccessibleChildByID(docAcc, "combo-div-2");
    417    const comboDiv3 = findAccessibleChildByID(docAcc, "combo-div-3");
    418    const comboDiv4 = findAccessibleChildByID(docAcc, "combo-div-4");
    419    const comboInput1 = findAccessibleChildByID(docAcc, "combo-input-1");
    420    const comboInput2 = findAccessibleChildByID(docAcc, "combo-input-2");
    421    const comboDivSelected = findAccessibleChildByID(
    422      docAcc,
    423      "combo-div-selected"
    424    );
    425 
    426    // Text as a descendant of the combobox: included in the value.
    427    is(comboDiv1.value, "value", "Combobox value correct");
    428 
    429    // Text as the descendant of a listbox: excluded from the value.
    430    is(comboDiv2.value, "", "Combobox value correct");
    431 
    432    // Text as the descendant of some other role that includes text in name computation.
    433    // Here, the group role contains the text node with "value" in it.
    434    is(comboDiv3.value, "value", "Combobox value correct");
    435 
    436    // Some descendant text included, but text descendant of a listbox excluded.
    437    is(comboDiv4.value, "foo", "Combobox value correct");
    438 
    439    // Combobox inputs with explicit value report that value.
    440    is(comboInput1.value, "value", "Combobox value correct");
    441    is(comboInput2.value, "value", "Combobox value correct");
    442 
    443    // Combobox role with aria-selected reports correct value.
    444    is(comboDivSelected.value, "value", "Combobox value correct");
    445  },
    446  { chrome: true, iframe: true, remoteIframe: true }
    447 );