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