tor-browser

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

browser_selectionPatterns.js (8564B)


      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 const SNIPPET = `
      8 <select id="selectList" size="2">
      9  <option id="sl1" selected>sl1</option>
     10  <option id="sl2">sl2</option>
     11 </select>
     12 <select id="selectRequired" size="2" required>
     13  <option id="sr1">sr1</option>
     14 </select>
     15 <select id="selectMulti" size="2" multiple>
     16  <option id="sm1" selected>sm1</option>
     17  <option id="sm2">sm2</option>
     18  <option id="sm3" selected>sm3</option>
     19  <option id="sm4">sm4</option>
     20  <option id="sm5">sm5</option>
     21  <option id="sm6">sm6</option>
     22 </select>
     23 <select id="selectCombo" size="1">
     24  <option>sc1</option>
     25 </select>
     26 <div id="ariaListbox" role="listbox">
     27  <div id="al1" role="option" aria-selected="true">al1</div>
     28  <div id="al2" role="option">al2</div>
     29 </div>
     30 <div id="tablist" role="tablist">
     31  <div id="t1" role="tab">t1</div>
     32  <div id="t2" role="tab" aria-selected="true">t2</div>
     33 </div>
     34 <table id="grid" role="grid" aria-multiselectable="true">
     35  <tr>
     36    <td id="g1">g1</td>
     37    <td id="g2" role="gridcell" aria-selected="true">g2</td>
     38  </tr>
     39  <tr>
     40    <td id="g3">g3</td>
     41    <td id="g4" role="gridcell" aria-selected="true">g4</td>
     42  </tr>
     43 </table>
     44 <div id="radiogroup" role="radiogroup">
     45  <label><input id="r1" type="radio" name="r" checked>r1</label>
     46  <label><input id="r2" type="radio" name="r">r2</label>
     47 </div>
     48 <div id="menu" role="menu">
     49  <div id="m1" role="menuitem">m1</div>
     50  <div id="m2" role="menuitemradio">m2</div>
     51  <div id="m3" role="menuitemradio" aria-checked="true">m3</div>
     52 </div>
     53 <button id="button">button</button>
     54 `;
     55 
     56 async function testSelectionProps(id, selection, multiple, required) {
     57  await assignPyVarToUiaWithId(id);
     58  await definePyVar("pattern", `getUiaPattern(${id}, "Selection")`);
     59  ok(await runPython(`bool(pattern)`), `${id} has Selection pattern`);
     60  await isUiaElementArray(
     61    `pattern.GetCurrentSelection()`,
     62    selection,
     63    `${id} has correct Selection`
     64  );
     65  is(
     66    !!(await runPython(`pattern.CurrentCanSelectMultiple`)),
     67    multiple,
     68    `${id} has correct CanSelectMultiple`
     69  );
     70  // The IA2 -> UIA proxy doesn't reflect the required state correctly.
     71  if (gIsUiaEnabled) {
     72    is(
     73      !!(await runPython(`pattern.CurrentIsSelectionRequired`)),
     74      required,
     75      `${id} has correct IsSelectionRequired`
     76    );
     77  }
     78 }
     79 
     80 async function testSelectionItemProps(id, selected, container) {
     81  await assignPyVarToUiaWithId(id);
     82  await definePyVar("pattern", `getUiaPattern(${id}, "SelectionItem")`);
     83  ok(await runPython(`bool(pattern)`), `${id} has SelectionItem pattern`);
     84  is(
     85    !!(await runPython(`pattern.CurrentIsSelected`)),
     86    selected,
     87    `${id} has correct IsSelected`
     88  );
     89  if (container) {
     90    is(
     91      await runPython(`pattern.CurrentSelectionContainer.CurrentAutomationId`),
     92      container,
     93      `${id} has correct SelectionContainer`
     94    );
     95  } else {
     96    ok(
     97      !(await runPython(`bool(pattern.CurrentSelectionContainer)`)),
     98      `${id} has no SelectionContainer`
     99    );
    100  }
    101 }
    102 
    103 /**
    104 * Test the Selection pattern.
    105 */
    106 addUiaTask(SNIPPET, async function testSelection(browser) {
    107  await definePyVar("doc", `getDocUia()`);
    108  await testSelectionProps("selectList", ["sl1"], false, false);
    109  await testSelectionProps("selectRequired", [], false, true);
    110 
    111  await testSelectionProps("selectMulti", ["sm1", "sm3"], true, false);
    112  // The Selection pattern only has an event for a complete invalidation of the
    113  // container's selection, which only happens when there are too many selection
    114  // events. Smaller selection changes fire events in the SelectionItem pattern.
    115  info("Changing selectMulti selection");
    116  await setUpWaitForUiaEvent("Selection_Invalidated", "selectMulti");
    117  await invokeContentTask(browser, [], () => {
    118    const multi = content.document.getElementById("selectMulti");
    119    multi[0].selected = false;
    120    multi[1].selected = true;
    121    multi[2].selected = false;
    122    multi[3].selected = true;
    123    multi[4].selected = true;
    124    multi[5].selected = true;
    125  });
    126  await waitForUiaEvent();
    127  ok(true, "select got Invalidated event");
    128  await testSelectionProps(
    129    "selectMulti",
    130    ["sm2", "sm4", "sm5", "sm6"],
    131    true,
    132    false
    133  );
    134 
    135  await testPatternAbsent("selectCombo", "Selection");
    136 
    137  await testSelectionProps("ariaListbox", ["al1"], false, false);
    138  await testSelectionProps("tablist", ["t2"], false, false);
    139  // The IA2 -> UIA proxy doesn't expose the Selection pattern on grids.
    140  if (gIsUiaEnabled) {
    141    await testSelectionProps("grid", ["g2", "g4"], true, false);
    142  }
    143 
    144  // radio gets the SelectionItem pattern, but radiogroup doesn't get the
    145  // Selection pattern for now. Same for menu/menuitemradio.
    146  await testPatternAbsent("radiogroup", "Selection");
    147  await testPatternAbsent("menu", "Selection");
    148 
    149  await testPatternAbsent("button", "Selection");
    150 });
    151 
    152 /**
    153 * Test the SelectionItem pattern.
    154 */
    155 addUiaTask(SNIPPET, async function testSelection(browser) {
    156  await definePyVar("doc", `getDocUia()`);
    157  await testPatternAbsent("selectList", "SelectionItem");
    158  await testSelectionItemProps("sl1", true, "selectList");
    159  await testSelectionItemProps("sl2", false, "selectList");
    160  info("Calling Select on sl2");
    161  await setUpWaitForUiaEvent("SelectionItem_ElementSelected", "sl2");
    162  await runPython(`pattern.Select()`);
    163  await waitForUiaEvent();
    164  ok(true, "sl2 got ElementSelected event");
    165  await testSelectionItemProps("sl1", false, "selectList");
    166  await testSelectionItemProps("sl2", true, "selectList");
    167 
    168  await testSelectionItemProps("sr1", false, "selectRequired");
    169 
    170  await testSelectionItemProps("sm1", true, "selectMulti");
    171  await testSelectionItemProps("sm2", false, "selectMulti");
    172  info("Calling AddToSelection on sm2");
    173  await setUpWaitForUiaEvent("SelectionItem_ElementAddedToSelection", "sm2");
    174  await runPython(`pattern.AddToSelection()`);
    175  await waitForUiaEvent();
    176  ok(true, "sm2 got ElementAddedToSelection event");
    177  await testSelectionItemProps("sm2", true, "selectMulti");
    178  await testSelectionItemProps("sm3", true, "selectMulti");
    179  info("Calling RemoveFromSelection on sm3");
    180  await setUpWaitForUiaEvent(
    181    "SelectionItem_ElementRemovedFromSelection",
    182    "sm3"
    183  );
    184  await runPython(`pattern.RemoveFromSelection()`);
    185  await waitForUiaEvent();
    186  ok(true, "sm3 got ElementRemovedFromSelection event");
    187  await testSelectionItemProps("sm3", false, "selectMulti");
    188 
    189  await testSelectionItemProps("t2", true, "tablist");
    190  await testSelectionItemProps("t1", false, "tablist");
    191  // The IA2 -> UIA proxy gets this wrong.
    192  if (gIsUiaEnabled) {
    193    info("Calling Select on t1");
    194    // Select on a tab should click it.
    195    await invokeContentTask(browser, [], () => {
    196      content.document.getElementById("t1").addEventListener("click", evt => {
    197        evt.target.ariaSelected = "true";
    198        content.document.getElementById("t2").ariaSelected = "false";
    199      });
    200    });
    201    await setUpWaitForUiaEvent("SelectionItem_ElementSelected", "t1");
    202    await runPython(`pattern.Select()`);
    203    await waitForUiaEvent();
    204    ok(true, "t1 got ElementSelected event");
    205    await testSelectionItemProps("t1", true, "tablist");
    206    await testSelectionItemProps("t2", false, "tablist");
    207  }
    208 
    209  // The IA2 -> UIA proxy doesn't expose the SelectionItem pattern on grid
    210  // cells.
    211  if (gIsUiaEnabled) {
    212    await testSelectionItemProps("g1", false, "grid");
    213    await testSelectionItemProps("g2", true, "grid");
    214  }
    215 
    216  await testSelectionItemProps("r1", true, null);
    217  await testSelectionItemProps("r2", false, null);
    218  // The IA2 -> UIA proxy doesn't fire correct events for radio buttons.
    219  if (gIsUiaEnabled) {
    220    info("Calling Select on r2");
    221    await setUpWaitForUiaEvent("SelectionItem_ElementSelected", "r2");
    222    await runPython(`pattern.Select()`);
    223    await waitForUiaEvent();
    224    ok(true, "r2 got ElementSelected event");
    225    await testSelectionItemProps("r1", false, null);
    226    await testSelectionItemProps("r2", true, null);
    227    info("Calling RemoveFromSelection on r2");
    228    await testPythonRaises(
    229      `pattern.RemoveFromSelection()`,
    230      "RemoveFromSelection failed on r2"
    231    );
    232  }
    233 
    234  await testPatternAbsent("m1", "SelectionItem");
    235  // The IA2 -> UIA proxy doesn't expose the SelectionItem pattern for radio
    236  // menu items.
    237  if (gIsUiaEnabled) {
    238    await testSelectionItemProps("m2", false, null);
    239    await testSelectionItemProps("m3", true, null);
    240  }
    241 
    242  await testPatternAbsent("button", "SelectionItem");
    243 });