browser_relations_general.js (10994B)
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 requestLongerTimeout(2); 7 8 /** 9 * A test specification that has the following format: 10 * [ 11 * attr relevant aria attribute 12 * hostRelation corresponding host relation type 13 * dependantRelation corresponding dependant relation type 14 * ] 15 */ 16 const attrRelationsSpec = [ 17 ["aria-labelledby", RELATION_LABELLED_BY, RELATION_LABEL_FOR], 18 ["aria-describedby", RELATION_DESCRIBED_BY, RELATION_DESCRIPTION_FOR], 19 ["aria-controls", RELATION_CONTROLLER_FOR, RELATION_CONTROLLED_BY], 20 ["aria-flowto", RELATION_FLOWS_TO, RELATION_FLOWS_FROM], 21 ["aria-details", RELATION_DETAILS, RELATION_DETAILS_FOR], 22 ["aria-errormessage", RELATION_ERRORMSG, RELATION_ERRORMSG_FOR], 23 ["aria-actions", RELATION_ACTION, RELATION_ACTION_FOR], 24 ]; 25 26 /** 27 * Test caching of relations between accessible objects. 28 */ 29 addAccessibleTask( 30 ` 31 <div id="dependant1">label</div> 32 <div id="dependant2">label2</div> 33 <div role="checkbox" id="host"></div>`, 34 async function (browser, accDoc) { 35 for (let spec of attrRelationsSpec) { 36 await testRelated(browser, accDoc, ...spec); 37 } 38 }, 39 { iframe: true, remoteIframe: true } 40 ); 41 42 /** 43 * Test caching of relations with respect to label objects and their "for" attr. 44 */ 45 addAccessibleTask( 46 ` 47 <input type="checkbox" id="dependant"> 48 <label id="host">label</label>`, 49 async function (browser, accDoc) { 50 const host = findAccessibleChildByID(accDoc, "host"); 51 const dependant = findAccessibleChildByID(accDoc, "dependant"); 52 async function testLabel(hasLabel) { 53 await testCachedRelation( 54 host, 55 RELATION_LABEL_FOR, 56 hasLabel ? dependant : [] 57 ); 58 await testCachedRelation( 59 dependant, 60 RELATION_LABELLED_BY, 61 hasLabel ? host : [] 62 ); 63 } 64 65 await testLabel(false); 66 await invokeSetAttribute(browser, "host", "for", "dependant"); 67 await testLabel(true); 68 await invokeSetAttribute(browser, "dependant", "id", "invalid"); 69 await testLabel(false); 70 }, 71 { chrome: true, iframe: true, remoteIframe: true } 72 ); 73 74 /** 75 * Test rel caching for element with existing relation attribute. 76 */ 77 addAccessibleTask( 78 `<div id="label">label</div><button id="button" aria-labelledby="label">`, 79 async function (browser, accDoc) { 80 const button = findAccessibleChildByID(accDoc, "button"); 81 const label = findAccessibleChildByID(accDoc, "label"); 82 83 await testCachedRelation(button, RELATION_LABELLED_BY, label); 84 await testCachedRelation(label, RELATION_LABEL_FOR, button); 85 }, 86 { iframe: true, remoteIframe: true } 87 ); 88 89 /** 90 * Test caching of relations with respect to output objects and their "for" attr. 91 */ 92 addAccessibleTask( 93 ` 94 <form oninput="host.value=parseInt(dependant1.value)+parseInt(dependant2.value)"> 95 <input type="number" id="dependant1" value="50"> + 96 <input type="number" id="dependant2" value="25"> = 97 <output name="host" id="host"></output> 98 </form>`, 99 async function (browser, accDoc) { 100 await testRelated( 101 browser, 102 accDoc, 103 "for", 104 RELATION_CONTROLLED_BY, 105 RELATION_CONTROLLER_FOR 106 ); 107 }, 108 { iframe: true, remoteIframe: true } 109 ); 110 111 /** 112 * Test rel caching for <label> element with existing "for" attribute. 113 */ 114 addAccessibleTask( 115 `data:text/html,<label id="label" for="input">label</label><input id="input">`, 116 async function (browser, accDoc) { 117 const input = findAccessibleChildByID(accDoc, "input"); 118 const label = findAccessibleChildByID(accDoc, "label"); 119 await testCachedRelation(input, RELATION_LABELLED_BY, label); 120 await testCachedRelation(label, RELATION_LABEL_FOR, input); 121 }, 122 { iframe: true, remoteIframe: true } 123 ); 124 125 /* 126 * Test caching of relations with respect to label objects that are ancestors of 127 * their target. 128 */ 129 addAccessibleTask( 130 ` 131 <label id="host"> 132 <input type="checkbox" id="dependant1"> 133 </label>`, 134 async function (browser, accDoc) { 135 const input = findAccessibleChildByID(accDoc, "dependant1"); 136 const label = findAccessibleChildByID(accDoc, "host"); 137 138 await testCachedRelation(input, RELATION_LABELLED_BY, label); 139 await testCachedRelation(label, RELATION_LABEL_FOR, input); 140 }, 141 { iframe: true, remoteIframe: true } 142 ); 143 144 /* 145 * Test EMBEDS on root accessible. 146 */ 147 addAccessibleTask( 148 `hello world`, 149 async function (browser, primaryDocAcc, secondaryDocAcc) { 150 // The root accessible should EMBED the top level 151 // content document. If this test runs in an iframe, 152 // the test harness will pass in doc accs for both the 153 // iframe (primaryDocAcc) and the top level remote 154 // browser (secondaryDocAcc). We should use the second 155 // one. 156 // If this is not in an iframe, we'll only get 157 // a single docAcc (primaryDocAcc) which refers to 158 // the top level content doc. 159 const topLevelDoc = secondaryDocAcc ? secondaryDocAcc : primaryDocAcc; 160 await testRelation( 161 getRootAccessible(document), 162 RELATION_EMBEDS, 163 topLevelDoc 164 ); 165 }, 166 { chrome: true, iframe: true, remoteIframe: true } 167 ); 168 169 /** 170 * Test embeds while a modal dialog is displayed which blocks all UI and 171 * documents behind it. 172 */ 173 addAccessibleTask(`test`, async function testEmbedsModal(browser, tabDoc) { 174 const root = getRootAccessible(document); 175 testRelation(root, RELATION_EMBEDS, tabDoc); 176 ok(tabDoc.parent, "tabDoc has a parent"); 177 178 info("Showing modal"); 179 const modal = document.createElement("dialog"); 180 let shown = waitForEvent(EVENT_SHOW, modal); 181 document.body.append(modal); 182 modal.showModal(); 183 await shown; 184 ok(!tabDoc.parent, "tabDoc has no parent"); 185 testRelation(root, RELATION_EMBEDS, null); 186 187 info("Removing modal"); 188 let hidden = waitForEvent(EVENT_HIDE, modal); 189 modal.remove(); 190 await hidden; 191 ok(tabDoc.parent, "tabDoc has a parent"); 192 testRelation(root, RELATION_EMBEDS, tabDoc); 193 }); 194 195 /** 196 * Test CONTAINING_TAB_PANE 197 */ 198 addAccessibleTask( 199 `<p id="p">hello world</p>`, 200 async function (browser, primaryDocAcc, secondaryDocAcc) { 201 // The CONTAINING_TAB_PANE of any acc should be the top level 202 // content document. If this test runs in an iframe, 203 // the test harness will pass in doc accs for both the 204 // iframe (primaryDocAcc) and the top level remote 205 // browser (secondaryDocAcc). We should use the second 206 // one. 207 // If this is not in an iframe, we'll only get 208 // a single docAcc (primaryDocAcc) which refers to 209 // the top level content doc. 210 const topLevelDoc = secondaryDocAcc ? secondaryDocAcc : primaryDocAcc; 211 await testCachedRelation( 212 findAccessibleChildByID(primaryDocAcc, "p"), 213 RELATION_CONTAINING_TAB_PANE, 214 topLevelDoc 215 ); 216 }, 217 { 218 chrome: true, 219 topLevel: true, 220 iframe: true, 221 remoteIframe: true, 222 } 223 ); 224 225 /* 226 * Test relation caching on link 227 */ 228 addAccessibleTask( 229 ` 230 <a id="link" href="#item">a</a> 231 <div id="item">hello</div> 232 <div id="item2">world</div> 233 <a id="link2" href="#anchor">b</a> 234 <a id="namedLink" name="anchor">c</a>`, 235 async function (browser, accDoc) { 236 const link = findAccessibleChildByID(accDoc, "link"); 237 const link2 = findAccessibleChildByID(accDoc, "link2"); 238 const namedLink = findAccessibleChildByID(accDoc, "namedLink"); 239 const item = findAccessibleChildByID(accDoc, "item"); 240 const item2 = findAccessibleChildByID(accDoc, "item2"); 241 242 await testCachedRelation(link, RELATION_LINKS_TO, item); 243 await testCachedRelation(link2, RELATION_LINKS_TO, namedLink); 244 245 await invokeContentTask(browser, [], () => { 246 content.document.getElementById("link").href = ""; 247 content.document.getElementById("namedLink").name = "newName"; 248 }); 249 250 await testCachedRelation(link, RELATION_LINKS_TO, []); 251 await testCachedRelation(link2, RELATION_LINKS_TO, []); 252 253 await invokeContentTask(browser, [], () => { 254 content.document.getElementById("link").href = "#item2"; 255 }); 256 257 await testCachedRelation(link, RELATION_LINKS_TO, item2); 258 }, 259 { 260 chrome: true, 261 // IA2 doesn't have a LINKS_TO relation and Windows non-cached 262 // RemoteAccessible uses IA2, so we can't run these tests in this case. 263 topLevel: true, 264 iframe: true, 265 remoteIframe: true, 266 } 267 ); 268 269 /* 270 * Test relation caching for NODE_CHILD_OF and NODE_PARENT_OF with aria trees. 271 */ 272 addAccessibleTask( 273 ` 274 <div role="tree" id="tree"> 275 <div role="treeitem" id="treeitem">test</div> 276 <div role="treeitem" id="treeitem2">test</div> 277 </div>`, 278 async function (browser, accDoc) { 279 const tree = findAccessibleChildByID(accDoc, "tree"); 280 const treeItem = findAccessibleChildByID(accDoc, "treeitem"); 281 const treeItem2 = findAccessibleChildByID(accDoc, "treeitem2"); 282 283 await testCachedRelation(tree, RELATION_NODE_PARENT_OF, [ 284 treeItem, 285 treeItem2, 286 ]); 287 await testCachedRelation(treeItem, RELATION_NODE_CHILD_OF, tree); 288 }, 289 { chrome: true, iframe: true, remoteIframe: true } 290 ); 291 292 /* 293 * Test relation caching for NODE_CHILD_OF and NODE_PARENT_OF with aria lists. 294 */ 295 addAccessibleTask( 296 ` 297 <div id="l1" role="list"> 298 <div id="l1i1" role="listitem" aria-level="1">a</div> 299 <div id="l1i2" role="listitem" aria-level="2">b</div> 300 <div id="l1i3" role="listitem" aria-level="1">c</div> 301 </div>`, 302 async function (browser, accDoc) { 303 const list = findAccessibleChildByID(accDoc, "l1"); 304 const listItem1 = findAccessibleChildByID(accDoc, "l1i1"); 305 const listItem2 = findAccessibleChildByID(accDoc, "l1i2"); 306 const listItem3 = findAccessibleChildByID(accDoc, "l1i3"); 307 308 await testCachedRelation(list, RELATION_NODE_PARENT_OF, [ 309 listItem1, 310 listItem3, 311 ]); 312 await testCachedRelation(listItem1, RELATION_NODE_CHILD_OF, list); 313 await testCachedRelation(listItem3, RELATION_NODE_CHILD_OF, list); 314 315 await testCachedRelation(listItem1, RELATION_NODE_PARENT_OF, listItem2); 316 await testCachedRelation(listItem2, RELATION_NODE_CHILD_OF, listItem1); 317 }, 318 { chrome: true, iframe: true, remoteIframe: true } 319 ); 320 321 /* 322 * Test NODE_CHILD_OF relation caching for JAWS window emulation special case. 323 */ 324 addAccessibleTask( 325 ``, 326 async function (browser, accDoc) { 327 await testCachedRelation(accDoc, RELATION_NODE_CHILD_OF, accDoc.parent); 328 }, 329 { topLevel: true, chrome: true } 330 ); 331 332 /* 333 * Test relation caching for LABELLED_BY and LABEL_FOR with legend/fieldset. 334 */ 335 addAccessibleTask( 336 ` 337 <fieldset id="fs"> 338 <legend id="leg">legend</legend> 339 inner content 340 </fieldset>`, 341 async function testFieldsetLegendLabels(browser, accDoc) { 342 const fs = findAccessibleChildByID(accDoc, "fs"); 343 const leg = findAccessibleChildByID(accDoc, "leg"); 344 345 await testCachedRelation(fs, RELATION_LABELLED_BY, leg); 346 await testCachedRelation(leg, RELATION_LABEL_FOR, fs); 347 }, 348 { chrome: true, iframe: true, remoteIframe: true } 349 );