tor-browser

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

browser_liveRegions.js (6414B)


      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 // From https://learn.microsoft.com/en-us/windows/win32/api/uiautomationcore/ne-uiautomationcore-livesetting
      8 const LiveSetting = {
      9  Off: 0,
     10  Polite: 1,
     11  Assertive: 2,
     12 };
     13 
     14 /**
     15 * Test the LiveSetting property.
     16 */
     17 addUiaTask(
     18  `
     19 <div id="polite" aria-live="polite">
     20  <div id="inner">polite</div>
     21 </div>
     22 <div id="assertive" aria-live="assertive">assertive</div>
     23 <div id="off" aria-live="off">off</div>
     24 <output id="output">output</output>
     25 <div id="none">none</div>
     26  `,
     27  async function testLiveSettingProp() {
     28    await definePyVar("doc", `getDocUia()`);
     29    is(
     30      await runPython(`findUiaByDomId(doc, "polite").CurrentLiveSetting`),
     31      LiveSetting.Polite,
     32      "polite has correct LiveSetting"
     33    );
     34    // LiveSetting should only be exposed on the root of a live region.
     35    // The IA2 -> UIA proxy disagrees, but:
     36    // 1. The UIA documentation doesn't specify whether descendants should
     37    // expose this.
     38    // 2. Chromium only exposes it on the root. Given that live regions work in
     39    // Chromium but not with the IA2 -> UIA proxy, it makes sense to follow
     40    // Chromium in the absence of good documentation.
     41    // 3. It's cheaper to expose it only on the root, since that avoids many
     42    // ancestor walks.
     43    if (gIsUiaEnabled) {
     44      is(
     45        await runPython(`findUiaByDomId(doc, "inner").CurrentLiveSetting`),
     46        LiveSetting.Off,
     47        "inner has correct LiveSetting"
     48      );
     49    }
     50    is(
     51      await runPython(`findUiaByDomId(doc, "assertive").CurrentLiveSetting`),
     52      LiveSetting.Assertive,
     53      "assertive has correct LiveSetting"
     54    );
     55    is(
     56      await runPython(`findUiaByDomId(doc, "off").CurrentLiveSetting`),
     57      LiveSetting.Off,
     58      "off has correct LiveSetting"
     59    );
     60    is(
     61      await runPython(`findUiaByDomId(doc, "output").CurrentLiveSetting`),
     62      LiveSetting.Polite,
     63      "output has correct LiveSetting"
     64    );
     65    is(
     66      await runPython(`findUiaByDomId(doc, "none").CurrentLiveSetting`),
     67      LiveSetting.Off,
     68      "none has correct LiveSetting"
     69    );
     70  }
     71 );
     72 
     73 /**
     74 * Test exposure of aria-atomic via the AriaProperties property.
     75 */
     76 addUiaTask(
     77  `
     78 <div id="implicit" aria-live="polite">live</div>
     79 <div id="false" aria-live="polite" aria-atomic="false">false</div>
     80 <div id="true" aria-live="polite" aria-atomic="true">true</div>
     81 <div id="none">none</div>
     82  `,
     83  async function testAtomic() {
     84    await definePyVar("doc", `getDocUia()`);
     85    let result = await runPython(
     86      `findUiaByDomId(doc, "implicit").CurrentAriaProperties`
     87    );
     88    isnot(
     89      result.indexOf("atomic=false"),
     90      -1,
     91      "AriaProperties for implicit contains atomic=false"
     92    );
     93    result = await runPython(
     94      `findUiaByDomId(doc, "false").CurrentAriaProperties`
     95    );
     96    isnot(
     97      result.indexOf("atomic=false"),
     98      -1,
     99      "AriaProperties for false contains atomic=false"
    100    );
    101    result = await runPython(
    102      `findUiaByDomId(doc, "true").CurrentAriaProperties`
    103    );
    104    isnot(
    105      result.indexOf("atomic=true"),
    106      -1,
    107      "AriaProperties for true contains atomic=true"
    108    );
    109    result = await runPython(
    110      `findUiaByDomId(doc, "none").CurrentAriaProperties`
    111    );
    112    is(
    113      result.indexOf("atomic"),
    114      -1,
    115      "AriaProperties for none doesn't contain atomic"
    116    );
    117  },
    118  // The IA2 -> UIA proxy doesn't support atomic.
    119  { uiaEnabled: true, uiaDisabled: false }
    120 );
    121 
    122 /**
    123 * Test that a live region is exposed as a control element.
    124 */
    125 addUiaTask(
    126  `
    127 <div id="live" aria-live="polite">
    128  <div id="inner">live</div>
    129 </div>
    130 <div id="notLive">notLive</div>
    131  `,
    132  async function testIsControl() {
    133    await definePyVar("doc", `getDocUia()`);
    134    ok(
    135      await runPython(`findUiaByDomId(doc, "live").CurrentIsControlElement`),
    136      "live is a control element"
    137    );
    138    // The IA2 -> UIA proxy gets this wrong.
    139    if (gIsUiaEnabled) {
    140      ok(
    141        !(await runPython(
    142          `findUiaByDomId(doc, "inner").CurrentIsControlElement`
    143        )),
    144        "inner is not a control element"
    145      );
    146    }
    147    ok(
    148      !(await runPython(
    149        `findUiaByDomId(doc, "notLive").CurrentIsControlElement`
    150      )),
    151      "notLive is not a control element"
    152    );
    153  }
    154 );
    155 
    156 /**
    157 * Test LiveRegionChanged events.
    158 */
    159 addUiaTask(
    160  `
    161 <div id="live" aria-live="polite">
    162  a
    163  <div id="b" hidden>b</div>
    164  <span id="c" hidden role="none">c</span>
    165  <button id="d" aria-label="before">button</button>
    166 </div>
    167  `,
    168  async function testLiveRegionChangedEvent(browser) {
    169    await definePyVar("doc", `getDocUia()`);
    170    info("Showing b");
    171    await setUpWaitForUiaEvent("LiveRegionChanged", "live");
    172    await invokeContentTask(browser, [], () => {
    173      content.document.getElementById("b").hidden = false;
    174    });
    175    await waitForUiaEvent();
    176    ok(true, "Got LiveRegionChanged on live");
    177 
    178    // The c span doesn't get an Accessible, so this tests that we get an event
    179    // when a text leaf is added directly.
    180    info("Showing c");
    181    await setUpWaitForUiaEvent("LiveRegionChanged", "live");
    182    await invokeContentTask(browser, [], () => {
    183      content.document.getElementById("c").hidden = false;
    184    });
    185    await waitForUiaEvent();
    186    ok(true, "Got LiveRegionChanged on live");
    187 
    188    info("Setting d's aria-label");
    189    await setUpWaitForUiaEvent("LiveRegionChanged", "live");
    190    await invokeSetAttribute(browser, "d", "aria-label", "d");
    191    await waitForUiaEvent();
    192    ok(true, "Got LiveRegionChanged on live");
    193 
    194    info("Setting live textContent (new text leaf)");
    195    await setUpWaitForUiaEvent("LiveRegionChanged", "live");
    196    await invokeContentTask(browser, [], () => {
    197      content.document.getElementById("live").textContent = "replaced";
    198    });
    199    await waitForUiaEvent();
    200    ok(true, "Got LiveRegionChanged on live");
    201 
    202    info("Mutating live's text node (same text leaf)");
    203    await setUpWaitForUiaEvent("LiveRegionChanged", "live");
    204    await invokeContentTask(browser, [], () => {
    205      content.document.getElementById("live").firstChild.data = "again";
    206    });
    207    await waitForUiaEvent();
    208    ok(true, "Got LiveRegionChanged on live");
    209  },
    210  // The IA2 -> UIA proxy doesn't fire LiveRegionChanged.
    211  { uiaEnabled: true, uiaDisabled: false }
    212 );