browser_name_markup.js (40375B)
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 /* import-globals-from ../../mochitest/name.js */ 9 /* import-globals-from ../../mochitest/attributes.js */ 10 loadScripts( 11 { name: "name.js", dir: MOCHITESTS_DIR }, 12 { name: "attributes.js", dir: MOCHITESTS_DIR } 13 ); 14 15 /** 16 * Rules for name tests that are inspired by 17 * accessible/tests/mochitest/name/markuprules.xul 18 * 19 * Each element in the list of rules represents a name calculation rule for a 20 * particular test case. 21 * 22 * The rules have the following format: 23 * { attr } - calculated from attribute 24 * { elm } - calculated from another element 25 * { fromsubtree } - calculated from element's subtree 26 * 27 */ 28 const ARIARule = [{ attr: "aria-labelledby" }, { attr: "aria-label" }]; 29 const HTMLControlHeadRule = [...ARIARule, { elm: "label" }]; 30 const rules = { 31 CSSContent: [{ elm: "style" }, { fromsubtree: true }], 32 HTMLCell: [...ARIARule, { fromsubtree: true }, { attr: "title" }], 33 HTMLControl: [ 34 ...HTMLControlHeadRule, 35 { fromsubtree: true }, 36 { attr: "title" }, 37 ], 38 HTMLElm: [...ARIARule, { attr: "title" }], 39 HTMLImg: [...ARIARule, { attr: "alt" }, { attr: "title" }], 40 HTMLImgEmptyAlt: [...ARIARule, { attr: "title" }, { attr: "alt" }], 41 HTMLInputButton: [ 42 ...HTMLControlHeadRule, 43 { attr: "value" }, 44 { attr: "title" }, 45 ], 46 HTMLInputImage: [ 47 ...HTMLControlHeadRule, 48 { attr: "alt" }, 49 { attr: "value" }, 50 { attr: "title" }, 51 ], 52 HTMLInputImageNoValidSrc: [ 53 ...HTMLControlHeadRule, 54 { attr: "alt" }, 55 { attr: "value" }, 56 ], 57 HTMLInputReset: [...HTMLControlHeadRule, { attr: "value" }], 58 HTMLInputSubmit: [...HTMLControlHeadRule, { attr: "value" }], 59 HTMLLink: [...ARIARule, { fromsubtree: true }, { attr: "title" }], 60 HTMLLinkImage: [...ARIARule, { fromsubtree: true }, { attr: "title" }], 61 HTMLOption: [ 62 ...ARIARule, 63 { attr: "label" }, 64 { fromsubtree: true }, 65 { attr: "title" }, 66 ], 67 HTMLTable: [ 68 ...ARIARule, 69 { elm: "caption" }, 70 { attr: "summary" }, 71 { attr: "title" }, 72 ], 73 }; 74 75 const markupTests = [ 76 { 77 id: "btn", 78 ruleset: "HTMLControl", 79 markup: ` 80 <span id="l1">test2</span> 81 <span id="l2">test3</span> 82 <label for="btn">test4</label> 83 <button id="btn" 84 aria-label="test1" 85 aria-labelledby="l1 l2" 86 title="test5">press me</button>`, 87 expected: ["test2 test3", "test1", "test4", "press me", "test5"], 88 }, 89 { 90 id: "btn", 91 ruleset: "HTMLInputButton", 92 markup: ` 93 <span id="l1">test2</span> 94 <span id="l2">test3</span> 95 <label for="btn">test4</label> 96 <input id="btn" 97 type="button" 98 aria-label="test1" 99 aria-labelledby="l1 l2" 100 value="name from value" 101 alt="no name from al" 102 src="no name from src" 103 data="no name from data" 104 title="name from title"/>`, 105 expected: [ 106 "test2 test3", 107 "test1", 108 "test4", 109 "name from value", 110 "name from title", 111 ], 112 }, 113 { 114 id: "btn-submit", 115 ruleset: "HTMLInputSubmit", 116 markup: ` 117 <span id="l1">test2</span> 118 <span id="l2">test3</span> 119 <label for="btn-submit">test4</label> 120 <input id="btn-submit" 121 type="submit" 122 aria-label="test1" 123 aria-labelledby="l1 l2" 124 value="name from value" 125 alt="no name from atl" 126 src="no name from src" 127 data="no name from data" 128 title="no name from title"/>`, 129 expected: ["test2 test3", "test1", "test4", "name from value"], 130 }, 131 { 132 id: "btn-reset", 133 ruleset: "HTMLInputReset", 134 markup: ` 135 <span id="l1">test2</span> 136 <span id="l2">test3</span> 137 <label for="btn-reset">test4</label> 138 <input id="btn-reset" 139 type="reset" 140 aria-label="test1" 141 aria-labelledby="l1 l2" 142 value="name from value" 143 alt="no name from alt" 144 src="no name from src" 145 data="no name from data" 146 title="no name from title"/>`, 147 expected: ["test2 test3", "test1", "test4", "name from value"], 148 }, 149 { 150 id: "btn-image", 151 ruleset: "HTMLInputImage", 152 markup: ` 153 <span id="l1">test2</span> 154 <span id="l2">test3</span> 155 <label for="btn-image">test4</label> 156 <input id="btn-image" 157 type="image" 158 aria-label="test1" 159 aria-labelledby="l1 l2" 160 alt="name from alt" 161 value="name from value" 162 src="http://example.com/a11y/accessible/tests/mochitest/moz.png" 163 data="no name from data" 164 title="name from title"/>`, 165 expected: [ 166 "test2 test3", 167 "test1", 168 "test4", 169 "name from alt", 170 "name from value", 171 "name from title", 172 ], 173 }, 174 { 175 id: "btn-image", 176 ruleset: "HTMLInputImageNoValidSrc", 177 markup: ` 178 <span id="l1">test2</span> 179 <span id="l2">test3</span> 180 <label for="btn-image">test4</label> 181 <input id="btn-image" 182 type="image" 183 aria-label="test1" 184 aria-labelledby="l1 l2" 185 alt="name from alt" 186 value="name from value" 187 data="no name from data" 188 title="no name from title"/>`, 189 expected: [ 190 "test2 test3", 191 "test1", 192 "test4", 193 "name from alt", 194 "name from value", 195 ], 196 }, 197 { 198 id: "opt", 199 ruleset: "HTMLOption", 200 markup: ` 201 <span id="l1">test2</span> 202 <span id="l2">test3</span> 203 <select> 204 <option id="opt" 205 aria-label="test1" 206 aria-labelledby="l1 l2" 207 label="test4" 208 title="test5">option1</option> 209 <option>option2</option> 210 </select>`, 211 expected: ["test2 test3", "test1", "test4", "option1", "test5"], 212 }, 213 { 214 id: "img", 215 ruleset: "HTMLImg", 216 markup: ` 217 <span id="l1">test2</span> 218 <span id="l2">test3</span> 219 <img id="img" 220 aria-label="Logo of Mozilla" 221 aria-labelledby="l1 l2" 222 alt="Mozilla logo" 223 title="This is a logo" 224 src="http://example.com/a11y/accessible/tests/mochitest/moz.png"/>`, 225 expected: [ 226 "test2 test3", 227 "Logo of Mozilla", 228 "Mozilla logo", 229 "This is a logo", 230 ], 231 }, 232 { 233 id: "tc", 234 ruleset: "HTMLCell", 235 markup: ` 236 <span id="l1">test2</span> 237 <span id="l2">test3</span> 238 <label for="tc">test4</label> 239 <table> 240 <tr> 241 <td id="tc" 242 aria-label="test1" 243 aria-labelledby="l1 l2" 244 title="test5"> 245 <p>This is a paragraph</p> 246 <a href="#">This is a link</a> 247 <ul> 248 <li>This is a list</li> 249 </ul> 250 </td> 251 </tr> 252 </table>`, 253 expected: [ 254 "test2 test3", 255 "test1", 256 "This is a paragraph This is a link \u2022 This is a list", 257 "test5", 258 ], 259 }, 260 { 261 id: "gc", 262 ruleset: "HTMLCell", 263 markup: ` 264 <span id="l1">test2</span> 265 <span id="l2">test3</span> 266 <label for="gc">test4</label> 267 <table> 268 <tr> 269 <td id="gc" 270 role="gridcell" 271 aria-label="test1" 272 aria-labelledby="l1 l2" 273 title="This is a paragraph This is a link This is a list"> 274 <p>This is a paragraph</p> 275 <a href="#">This is a link</a> 276 <ul> 277 <li>Listitem1</li> 278 <li>Listitem2</li> 279 </ul> 280 </td> 281 </tr> 282 </table>`, 283 expected: [ 284 "test2 test3", 285 "test1", 286 "This is a paragraph This is a link \u2022 Listitem1 \u2022 Listitem2", 287 "This is a paragraph This is a link This is a list", 288 ], 289 }, 290 { 291 id: "t", 292 ruleset: "HTMLTable", 293 markup: ` 294 <span id="l1">lby_tst6_1</span> 295 <span id="l2">lby_tst6_2</span> 296 <label for="t">label_tst6</label> 297 <table id="t" 298 aria-label="arialabel_tst6" 299 aria-labelledby="l1 l2" 300 summary="summary_tst6" 301 title="title_tst6"> 302 <caption>caption_tst6</caption> 303 <tr> 304 <td>cell1</td> 305 <td>cell2</td> 306 </tr> 307 </table>`, 308 expected: [ 309 "lby_tst6_1 lby_tst6_2", 310 "arialabel_tst6", 311 "caption_tst6", 312 "summary_tst6", 313 "title_tst6", 314 ], 315 }, 316 { 317 id: "btn", 318 ruleset: "CSSContent", 319 markup: ` 320 <div role="main"> 321 <style> 322 button::before { 323 content: "do not "; 324 } 325 </style> 326 <button id="btn">press me</button> 327 </div>`, 328 expected: ["do not press me", "press me"], 329 }, 330 { 331 // TODO: uncomment when Bug-1256382 is resoved. 332 // id: 'li', 333 // ruleset: 'CSSContent', 334 // markup: ` 335 // <style> 336 // ul { 337 // list-style-type: decimal; 338 // } 339 // </style> 340 // <ul id="ul"> 341 // <li id="li">Listitem</li> 342 // </ul>`, 343 // expected: ['1. Listitem', `${String.fromCharCode(0x2022)} Listitem`] 344 // }, { 345 id: "a", 346 ruleset: "HTMLLink", 347 markup: ` 348 <span id="l1">test2</span> 349 <span id="l2">test3</span> 350 <a id="a" 351 href="" 352 aria-label="test1" 353 aria-labelledby="l1 l2" 354 title="test4">test5</a>`, 355 expected: ["test2 test3", "test1", "test5", "test4"], 356 }, 357 { 358 id: "a-img", 359 ruleset: "HTMLLinkImage", 360 markup: ` 361 <span id="l1">test2</span> 362 <span id="l2">test3</span> 363 <a id="a-img" 364 href="" 365 aria-label="test1" 366 aria-labelledby="l1 l2" 367 title="test4"><img alt="test5"/></a>`, 368 expected: ["test2 test3", "test1", "test5", "test4"], 369 }, 370 ]; 371 372 /** 373 * Test accessible name that is calculated from an attribute, remove the 374 * attribute before proceeding to the next name test. If attribute removal 375 * results in a reorder or text inserted event - wait for it. If accessible 376 * becomes defunct, update its reference using the one that is attached to one 377 * of the above events. 378 * 379 * @param {object} browser current "tabbrowser" element 380 * @param {object} target { acc, id } structure that contains an 381 * accessible and its content element 382 * id. 383 * @param {object} rule current attr rule for name calculation 384 * @param {[type]} expected expected name value 385 */ 386 async function testAttrRule(browser, target, rule, expected) { 387 let { id, acc } = target; 388 let { attr } = rule; 389 390 testName(acc, expected); 391 392 let nameChange = waitForEvent(EVENT_NAME_CHANGE, id); 393 await invokeContentTask(browser, [id, attr], (contentId, contentAttr) => { 394 content.document.getElementById(contentId).removeAttribute(contentAttr); 395 }); 396 let event = await nameChange; 397 398 // Update accessible just in case it is now defunct. 399 target.acc = findAccessibleChildByID(event.accessible, id); 400 } 401 402 /** 403 * Test accessible name that is calculated from an element name, remove the 404 * element before proceeding to the next name test. If element removal results 405 * in a reorder event - wait for it. If accessible becomes defunct, update its 406 * reference using the one that is attached to a possible reorder event. 407 * 408 * @param {object} browser current "tabbrowser" element 409 * @param {object} target { acc, id } structure that contains an 410 * accessible and its content element 411 * id. 412 * @param {object} rule current elm rule for name calculation 413 * @param {[type]} expected expected name value 414 */ 415 async function testElmRule(browser, target, rule, expected) { 416 let { id, acc } = target; 417 let { elm } = rule; 418 419 testName(acc, expected); 420 let nameChange = waitForEvent(EVENT_NAME_CHANGE, id); 421 422 await invokeContentTask(browser, [elm], contentElm => { 423 content.document.querySelector(`${contentElm}`).remove(); 424 }); 425 let event = await nameChange; 426 427 // Update accessible just in case it is now defunct. 428 target.acc = findAccessibleChildByID(event.accessible, id); 429 } 430 431 /** 432 * Test accessible name that is calculated from its subtree, remove the subtree 433 * and wait for a reorder event before proceeding to the next name test. If 434 * accessible becomes defunct, update its reference using the one that is 435 * attached to a reorder event. 436 * 437 * @param {object} browser current "tabbrowser" element 438 * @param {object} target { acc, id } structure that contains an 439 * accessible and its content element 440 * id. 441 * @param {object} rule current subtree rule for name calculation 442 * @param {[type]} expected expected name value 443 */ 444 async function testSubtreeRule(browser, target, rule, expected) { 445 let { id, acc } = target; 446 447 testName(acc, expected); 448 let nameChange = waitForEvent(EVENT_NAME_CHANGE, id); 449 450 await invokeContentTask(browser, [id], contentId => { 451 let elm = content.document.getElementById(contentId); 452 while (elm.firstChild) { 453 elm.firstChild.remove(); 454 } 455 }); 456 let event = await nameChange; 457 458 // Update accessible just in case it is now defunct. 459 target.acc = findAccessibleChildByID(event.accessible, id); 460 } 461 462 /** 463 * Iterate over a list of rules and test accessible names for each one of the 464 * rules. 465 * 466 * @param {object} browser current "tabbrowser" element 467 * @param {object} target { acc, id } structure that contains an 468 * accessible and its content element 469 * id. 470 * @param {Array} ruleset A list of rules to test a target with 471 * @param {Array} expected A list of expected name value for each rule 472 */ 473 async function testNameRule(browser, target, ruleset, expected) { 474 for (let i = 0; i < ruleset.length; ++i) { 475 let rule = ruleset[i]; 476 let testFn; 477 if (rule.attr) { 478 testFn = testAttrRule; 479 } else if (rule.elm) { 480 testFn = testElmRule; 481 } else if (rule.fromsubtree) { 482 testFn = testSubtreeRule; 483 } 484 await testFn(browser, target, rule, expected[i]); 485 } 486 } 487 488 markupTests.forEach(({ id, ruleset, markup, expected }) => 489 addAccessibleTask( 490 markup, 491 async function (browser, accDoc) { 492 const observer = { 493 observe(subject) { 494 const event = subject.QueryInterface(nsIAccessibleEvent); 495 console.log(eventToString(event)); 496 }, 497 }; 498 Services.obs.addObserver(observer, "accessible-event"); 499 // Find a target accessible from an accessible subtree. 500 let acc = findAccessibleChildByID(accDoc, id); 501 let target = { id, acc }; 502 await testNameRule(browser, target, rules[ruleset], expected); 503 Services.obs.removeObserver(observer, "accessible-event"); 504 }, 505 { iframe: true, remoteIframe: true } 506 ) 507 ); 508 509 /** 510 * Generic test cases ported from mochitest/name/test_general.html 511 */ 512 addAccessibleTask( 513 ` 514 <!-- aria-label, simple label --> 515 <span id="btn_simple_aria_label" role="button" aria-label="I am a button"/> 516 <br/> 517 <!-- aria-label plus aria-labelledby --> 518 <span id="btn_both_aria_labels" role="button" aria-label="I am a button, two" 519 aria-labelledby="labelledby_text btn_both_aria_labels"/> 520 <br/> 521 522 <!-- aria-labelledby, single relation --> 523 <span id="labelledby_text">text</span> 524 <button id="btn_labelledby_text" 525 aria-labelledby="labelledby_text">1</button> 526 <br/> 527 528 <!-- aria-labelledby, multiple relations --> 529 <span id="labelledby_text1">text1</span> 530 <span id="labelledby_text2">text2</span> 531 <button id="btn_labelledby_texts" 532 aria-labelledby="labelledby_text1 labelledby_text2">2</button> 533 <br/> 534 535 <!-- name from named accessible --> 536 <input id="labelledby_namedacc" type="checkbox" 537 aria-label="Data" /> 538 <input id="input_labelledby_namedacc" 539 aria-labelledby="labelledby_namedacc" /> 540 541 <!-- the name from subtree, mixed content --> 542 <span id="labelledby_mixed">no<span>more text</span></span> 543 <button id="btn_labelledby_mixed" 544 aria-labelledby="labelledby_mixed">3</button> 545 <br/> 546 547 <!-- the name from subtree, mixed/hidden content --> 548 <span id="labelledby_mixed_hidden_child"> 549 no<span>more 550 <span style="display: none;">hidden</span> 551 text2 552 <span style="visibility: hidden">hidden2</span> 553 </span> 554 </span> 555 <button id="btn_labelledby_mixed_hidden_child" 556 aria-labelledby="labelledby_mixed_hidden_child">3.1</button> 557 <br/> 558 559 <!-- the name from subtree, mixed/completely hidden content --> 560 <span id="labelledby_mixed_hidden" 561 style="display: none;">lala <span>more hidden </span>text</span> 562 <button id="btn_labelledby_mixed_hidden" 563 aria-labelledby="labelledby_mixed_hidden">3.2</button> 564 <br/> 565 566 <!-- the name from subtree, mixed content, block structure --> 567 <div id="labelledby_mixed_block"><div>text</div>more text</div></div> 568 <button id="btn_labelledby_mixed_block" 569 aria-labelledby="labelledby_mixed_block">4</button> 570 <br/> 571 572 <!-- the name from subtree, mixed content, table structure --> 573 <table><tr> 574 <td id="labelledby_mixed_table">text<span>TEXT</span>text</td> 575 </tr></table> 576 <button id="btn_labelledby_mixed_table" 577 aria-labelledby="labelledby_mixed_table">5</button> 578 <br/> 579 580 <!-- the name from subtree, child img --> 581 <span id="labelledby_mixed_img">text<img alt="image"/></span> 582 <button id="btn_labelledby_mixed_img" 583 aria-labelledby="labelledby_mixed_img">6</button> 584 <br/> 585 586 <!-- the name from subtree, child inputs --> 587 <span id="labelledby_mixed_input"> 588 <input type="button" id="input_button" title="input button"/> 589 <input type="submit" id="input_submit"/> 590 <input type="reset" id="input_reset"/> 591 <input type="image" id="input_image" title="input image"/> 592 </span> 593 <button id="btn_labelledby_mixed_input" 594 aria-labelledby="labelledby_mixed_input">7</button> 595 <br/> 596 597 <!-- the name from subtree, child object --> 598 <span id="labelledby_mixed_object"> 599 <object data="about:blank" title="object"></object> 600 </span> 601 <button id="btn_labelledby_mixed_object" 602 aria-labelledby="labelledby_mixed_object">8</button> 603 <br/> 604 605 <!-- the name from subtree, child br --> 606 <span id="labelledby_mixed_br">text<br/>text</span> 607 <button id="btn_labelledby_mixed_br" 608 aria-labelledby="labelledby_mixed_br">9</button> 609 <br/> 610 611 <!-- the name from subtree, name from label content rather than from its title 612 attribute --> 613 <label for="from_label_ignoretitle" 614 title="Select your country of origin">Country:</label> 615 <select id="from_label_ignoretitle"> 616 <option>Germany</option> 617 <option>Russia</option> 618 </select> 619 620 <!-- the name from subtree, name from html:p content rather than from its 621 title attribute --> 622 <p id="p_ignoretitle" 623 title="Select your country of origin">Choose country from.</p> 624 <select id="from_p_ignoretitle" aria-labelledby="p_ignoretitle"> 625 <option>Germany</option> 626 <option>Russia</option> 627 </select> 628 629 <!-- the name from subtree, name from html:input value rather than from its 630 title attribute --> 631 <p id="from_input_ignoretitle" aria-labelledby="input_ignoretitle">Country</p> 632 <input id="input_ignoretitle" 633 value="Custom country" 634 title="Input your country of origin"/> 635 636 <!-- name from subtree, surround control by spaces to not jam the text --> 637 <label id="insert_spaces_around_control"> 638 start<input value="value">end 639 </label> 640 641 <!-- no name from subtree because it holds whitespaces only --> 642 <a id="from_label_ignore_ws_subtree" href="about:mozilla" title="about"> </a> 643 644 <!-- Don't use subtree unless referenced by aria-labelledby. --> 645 <div id="alert" role="alert">Error</div> 646 <input type="text" id="inputLabelledByAlert" aria-labelledby="alert"> 647 648 <!-- label element, label contains control --> 649 <label>text<button id="btn_label_inside">10</button>text</label> 650 <br/> 651 652 <!-- label element, label and the button are in the same form --> 653 <form> 654 <label for="btn_label_inform">in form</label> 655 <button id="btn_label_inform">11</button> 656 </form> 657 658 <!-- label element, label is outside of the form of the button --> 659 <label for="btn_label_outform">out form</label> 660 <form> 661 <button id="btn_label_outform">12</button> 662 </form> 663 664 <!-- label element, label and the button are in the same document --> 665 <label for="btn_label_indocument">in document</label> 666 <button id="btn_label_indocument">13</button> 667 668 <!-- multiple label elements for single button --> 669 <label for="btn_label_multi">label1</label> 670 <label for="btn_label_multi">label2</label> 671 <button id="btn_label_multi">button</button> 672 673 <!-- a label containing more than one controls --> 674 <label> 675 Enable <input id="ctrl_in_label_1" type="checkbox"> a 676 <input id="ctrl_in_label_2" type="button" value="button"> control 677 </label> 678 679 <!-- name from children --> 680 <span id="btn_children" role="button">14</span> 681 682 <!-- no name from content, ARIA role overrides this rule --> 683 <button id="btn_nonamefromcontent" role="img">1</button> 684 685 <!-- name from children, hidden children --> 686 <div role="listbox" tabindex="0"> 687 <div id="lb_opt1_children_hidden" role="option" tabindex="0"> 688 <span>i am visible</span> 689 <span style="display:none">i am hidden</span> 690 </div> 691 </div> 692 693 <table role="menu"> 694 <tr role="menuitem" id="tablemenuitem"> 695 <td>menuitem 1</td> 696 </tr> 697 <tr role="menuitem"> 698 <td>menuitem 2</td> 699 </tr> 700 </table> 701 702 <label id="label_with_acronym"> 703 <acronym title="O A T F">OATF</acronym> 704 <abbr title="World Wide Web">WWW</abbr> 705 </label> 706 707 <div id="testArticle" role="article" title="Test article"> 708 <p>This is a paragraph inside the article.</p> 709 </div> 710 711 <h1 id="h1" title="oops">heading</h1> 712 <div role="heading" id="aria_heading">aria_heading</div> 713 714 <!-- name from title attribute --> 715 <span id="btn_title" role="group" title="title">15</span> 716 717 <!-- A textarea nested in a label with a text child (bug #453371). --> 718 <form> 719 <label>Story 720 <textarea id="textareawithchild" name="name">Foo</textarea> 721 is ended. 722 </label> 723 </form> 724 725 <!-- controls having a value used as part of computed name --> 726 <input type="checkbox" id="ctrlvalue_progressbar:input"> 727 <label for="ctrlvalue_progressbar:input"> 728 foo <span role="progressbar" 729 aria-valuenow="5" aria-valuemin="1" 730 aria-valuemax="10">5</span> baz 731 </label> 732 733 <input type="checkbox" id="ctrlvalue_scrollbar:input" /> 734 <label for="ctrlvalue_scrollbar:input"> 735 foo <span role="scrollbar" 736 aria-valuenow="5" aria-valuemin="1" 737 aria-valuemax="10">5</span> baz 738 </label> 739 740 <input type="checkbox" id="ctrlvalue_slider:input"> 741 <label for="ctrlvalue_slider:input"> 742 foo <input role="slider" type="range" 743 value="5" min="1" max="10" 744 aria-valuenow="5" aria-valuemin="1" 745 aria-valuemax="10"> baz 746 </label> 747 748 <input type="checkbox" id="ctrlvalue_spinbutton:input"> 749 <label for="ctrlvalue_spinbutton:input"> 750 foo <input role="spinbutton" type="number" 751 value="5" min="1" max="10" 752 aria-valuenow="5" aria-valuemin="1" 753 aria-valuemax="10"> 754 baz 755 </label> 756 757 <input type="checkbox" id="ctrlvalue_combobox:input"> 758 <label for="ctrlvalue_combobox:input"> 759 foo 760 <div role="combobox"> 761 <div role="textbox"></div> 762 <ul role="listbox" style="list-style-type: none;"> 763 <li role="option">1</li> 764 <li role="option" aria-selected="true">5</li> 765 <li role="option">3</li> 766 </ul> 767 </div> 768 baz 769 </label> 770 771 <input type="checkbox" id="ctrlvalue_meter:input"> 772 <label for="ctrlvalue_meter:input"> 773 foo 774 <meter>5</meter> 775 baz 776 </label> 777 778 <!-- a label with a nested control in the start, middle and end --> 779 <form> 780 <!-- at the start (without and with whitespaces) --> 781 <label id="comboinstart"><select id="combo3"> 782 <option>One</option> 783 <option>Two</option> 784 </select> 785 day(s). 786 </label> 787 788 <label id="textboxinstart"> 789 <input id="textbox1" value="Two"> 790 days. 791 </label> 792 793 <!-- in the middle --> 794 <label id="comboinmiddle"> 795 Subscribe to 796 <select id="combo4" name="occupation"> 797 <option>ATOM</option> 798 <option>RSS</option> 799 </select> 800 feed. 801 </label> 802 803 <label id="comboinmiddle2" for="checkbox">Play the 804 <select id="combo5"> 805 <option>Haliluya</option> 806 <option>Hurra</option> 807 </select> 808 sound when new mail arrives 809 </label> 810 <input id="checkbox" type="checkbox" /> 811 812 <label id="comboinmiddle3" for="combo6">Play the 813 <select id="combo6"> 814 <option>Haliluya</option> 815 <option>Hurra</option> 816 </select> 817 sound when new mail arrives 818 </label> 819 820 <!-- at the end (without and with whitespaces) --> 821 <label id="comboinend"> 822 This day was 823 <select id="combo7" name="occupation"> 824 <option>sunny</option> 825 <option>rainy</option> 826 </select></label> 827 828 <label id="textboxinend"> 829 This day was 830 <input id="textbox2" value="sunny"> 831 </label> 832 </form> 833 834 <!-- placeholder --> 835 <input id="ph_password" type="password" value="" placeholder="a placeholder" /> 836 <input id="ph_text" type="text" placeholder="a placeholder" /> 837 <textarea id="ph_textarea" cols="5" placeholder="a placeholder"></textarea> 838 839 <!-- placeholder does not win --> 840 <input id="ph_text2" type="text" aria-label="a label" placeholder="meh" /> 841 <textarea id="ph_textarea2" cols="5" aria-labelledby="ph_text2" 842 placeholder="meh"></textarea> 843 844 <label for="ph_text3">a label</label> 845 <input id="ph_text3" placeholder="meh" /> 846 847 <p>Image: 848 <img id="img_eq" role="math" src="foo" alt="x^2 + y^2 + z^2"> 849 <input type="image" id="input_img_eq" src="foo" alt="x^2 + y^2 + z^2"> 850 </p> 851 852 <p>Text: 853 <span id="txt_eq" role="math" title="x^2 + y^2 + z^2">x<sup>2</sup> + 854 y<sup>2</sup> + z<sup>2</sup></span> 855 </p> 856 857 <!-- duplicate announcement --> 858 <div id="test_note" role="note">subtree</div> 859 860 <!-- No name for tr from its sub tree --> 861 <table><tr id="NoNameForTR"><th>a</th><td>b</td></tr></table> 862 <table style="display: block;"> 863 <tr id="NoNameForNonStandardTR" style="display:block;"> 864 <th>a</th><td>b</td> 865 </tr> 866 </table> 867 868 <!-- Name from sub tree of tr, because it has a strong ARIA role --> 869 <table><tr id="NameForTRBecauseStrongARIA" role="row"><th>a</th><td>b</td></tr></table> 870 871 <!-- No name for tr because of weak (landmark) role --> 872 <table><tr id="NoNameForTRBecauseWeakARIA" role="main"><th>a</th><td>b</td></tr></table> 873 874 <!-- Name from subtree of grouping if requested by other object --> 875 <div id="grouping" role="group">label</div> 876 <button id="requested_name_from_grouping" aria-labelledby="grouping"></button> 877 <!-- Name from sub tree of tbody marked as display:block;, which is also a grouping --> 878 <div role="list"> 879 <div id="listitem_containing_block_tbody" role="listitem"> 880 <table> 881 <tbody style="display: block;"> 882 <tr><td>label</td></tr> 883 </tbody> 884 </table> 885 </div> 886 </div> 887 <!-- Name from subtree of treeitem containing grouping --> 888 <div role="tree"> 889 <div id="treeitem_containing_grouping" role="treeitem" aria-level="1" aria-expanded="true">root 890 <div role="group"> 891 <div role="treeitem" aria-level="2">sub</div> 892 </div> 893 </div> 894 </div> 895 896 <!-- Name from subtree of grouping labelled by an ancestor. --> 897 <div id="grouping_ancestor_label">label 898 <div id="grouping_labelledby_ancestor" role="group" aria-labelledby="grouping_ancestor_label"> 899 This content should not be included in the grouping's label. 900 </div> 901 </div> 902 903 <!-- Text nodes and inline elements. --> 904 <div role="listbox"> 905 <div id="container_text_inline" role="option">a<strong>b</strong>c</div> 906 <!-- Text nodes and block elements. --> 907 <div id="container_text_block" role="option">a<p>b</p>c</div> 908 <!-- Text nodes and empty block elements. --> 909 <div id="container_text_emptyblock" role="option">a<p></p><p></p>b</div> 910 </div> 911 912 <!-- aria-labelledby referring to a slot --> 913 <div id="shadowHost"> 914 shadowButtonVisibleText 915 <span slot="hiddenSlot">shadowButtonHiddenText</span> 916 </div> 917 <template id="shadowTemplate"> 918 <input type="button" id="shadowButtonVisibleText" aria-labelledby="visibleSlot"> 919 <slot id="visibleSlot"></slot> 920 <input type="button" id="shadowButtonHiddenText" aria-labelledby="hiddenSlot"> 921 <slot id="hiddenSlot" name="hiddenSlot" hidden></slot> 922 </template> 923 924 <!-- aria-labelledby referring to a hidden container with script/style --> 925 <button id="buttonScriptStyle" aria-labelledby="hiddenScriptStyle"></button> 926 <div id="hiddenScriptStyle" hidden> 927 <script> 42; </script> 928 <style> .noOp {} </style> 929 <span>content</span> 930 </div> 931 932 <!-- Name from subtree on link including <code>, etc. --> 933 <a id="linkWithCodeSupSubInsDel" href="#"> 934 before 935 <code>code</code> 936 <sup>sup</sup> 937 <sub>sub</sub> 938 <ins>ins</ins> 939 <del>del</del> 940 after 941 </a> 942 `, 943 async function testGeneric(browser, docAcc) { 944 // aria-label 945 function testName_(id, expectedName) { 946 const acc = findAccessibleChildByID(docAcc, id); 947 testName(acc, expectedName); 948 } 949 950 // Simple label provided via ARIA 951 testName_("btn_simple_aria_label", "I am a button"); 952 953 // aria-label and aria-labelledby, expect aria-labelledby 954 testName_("btn_both_aria_labels", "text I am a button, two"); 955 956 // //////////////////////////////////////////////////////////////////////// 957 // aria-labelledby 958 959 // Single relation. The value of 'aria-labelledby' contains the ID of 960 // an element. Gets the name from text node of that element. 961 testName_("btn_labelledby_text", "text"); 962 963 // Multiple relations. The value of 'aria-labelledby' contains the IDs 964 // of elements. Gets the name from text nodes of those elements. 965 testName_("btn_labelledby_texts", "text1 text2"); 966 967 // //////////////////////////////////////////////////////////////////////// 968 // Name from named accessible 969 970 testName_("input_labelledby_namedacc", "Data"); 971 972 // //////////////////////////////////////////////////////////////////////// 973 // Name from subtree (single relation labelled_by). 974 975 // Gets the name from text nodes contained by nested elements 976 testName_("btn_labelledby_mixed", "nomore text"); 977 978 // Gets the name from text nodes contained by nested elements, ignores 979 // hidden elements (bug 443081). 980 testName_("btn_labelledby_mixed_hidden_child", "nomore text2"); 981 982 // Gets the name from hidden text nodes contained by nested elements, 983 // (label element is hidden entirely), (bug 443081). 984 testName_("btn_labelledby_mixed_hidden", "lala more hidden text"); 985 986 // Gets the name from text nodes contained by nested elements having block 987 // representation (every text node value in the name should be divided by 988 // spaces) 989 testName_("btn_labelledby_mixed_block", "text more text"); 990 991 // Gets the name from text nodes contained by html:td. The text nodes 992 // should not be divided by spaces. 993 testName_("btn_labelledby_mixed_table", "textTEXTtext"); 994 995 // Gets the name from image accessible. 996 testName_("btn_labelledby_mixed_img", "text image"); 997 998 // Gets the name from input accessibles 999 // Note: if input have label elements then the name isn't calculated 1000 // from them. 1001 testName_( 1002 "btn_labelledby_mixed_input", 1003 "input button Submit Query Reset Submit Query" 1004 ); 1005 1006 // Gets the name from the title of object element. 1007 testName_("btn_labelledby_mixed_object", "object"); 1008 1009 // Gets the name from text nodes. Element br adds space between them. 1010 testName_("btn_labelledby_mixed_br", "text text"); 1011 1012 // Gets the name from label content which allows name from subtree, 1013 // ignore @title attribute on label 1014 testName_("from_label_ignoretitle", "Country:"); 1015 1016 // Gets the name from html:p content, which doesn't allow name from 1017 // subtree, ignore @title attribute on label 1018 testName_("from_p_ignoretitle", "Choose country from."); 1019 1020 // Gets the name from html:input value, ignore @title attribute on input 1021 testName_("from_input_ignoretitle", "Custom country"); 1022 1023 // Insert spaces around the control's value to not jam sibling text nodes 1024 testName_("insert_spaces_around_control", "start value end"); 1025 1026 // Gets the name from @title, ignore whitespace content 1027 testName_("from_label_ignore_ws_subtree", "about"); 1028 1029 // role="alert" doesn't get name from subtree... 1030 testName_("alert", null); 1031 // but the subtree is used if referenced by aria-labelledby. 1032 testName_("inputLabelledByAlert", "Error"); 1033 1034 // //////////////////////////////////////////////////////////////////////// 1035 // label element 1036 1037 // The label element contains the button. The name of the button is 1038 // calculated from the content of the label. 1039 // Note: the name does not contain the content of the button. 1040 testName_("btn_label_inside", "texttext"); 1041 1042 // The label element and the button are placed in the same form. Gets 1043 // the name from the label subtree. 1044 testName_("btn_label_inform", "in form"); 1045 1046 // The label element is placed outside of form where the button is. 1047 // Take into account the label. 1048 testName_("btn_label_outform", "out form"); 1049 1050 // The label element and the button are in the same document. Gets the 1051 // name from the label subtree. 1052 testName_("btn_label_indocument", "in document"); 1053 1054 // Multiple label elements for single button 1055 testName_("btn_label_multi", "label1 label2"); 1056 1057 // Multiple controls inside a label element 1058 testName_("ctrl_in_label_1", "Enable a button control"); 1059 testName_("ctrl_in_label_2", "button"); 1060 1061 // //////////////////////////////////////////////////////////////////////// 1062 // name from children 1063 1064 // ARIA role button is presented allowing the name calculation from 1065 // children. 1066 testName_("btn_children", "14"); 1067 1068 // html:button, no name from content 1069 testName_("btn_nonamefromcontent", null); 1070 1071 // ARIA role option is presented allowing the name calculation from 1072 // visible children (bug 443081). 1073 testName_("lb_opt1_children_hidden", "i am visible"); 1074 1075 // Get the name from subtree of menuitem crossing role nothing to get 1076 // the name from its children. 1077 testName_("tablemenuitem", "menuitem 1"); 1078 1079 // Get the name from child acronym title attribute rather than from 1080 // acronym content. 1081 testName_("label_with_acronym", "O A T F World Wide Web"); 1082 1083 testName_("testArticle", "Test article"); 1084 1085 testName_("h1", "heading"); 1086 testName_("aria_heading", "aria_heading"); 1087 1088 // //////////////////////////////////////////////////////////////////////// 1089 // title attribute 1090 1091 // If nothing is left. Let's try title attribute. 1092 testName_("btn_title", "title"); 1093 1094 // //////////////////////////////////////////////////////////////////////// 1095 // textarea name 1096 1097 // textarea's name should not have the value, which initially is specified 1098 // by a text child. 1099 testName_("textareawithchild", "Story is ended."); 1100 1101 // new textarea name should not reflect the value change. 1102 let valueChanged = waitForEvent( 1103 EVENT_TEXT_VALUE_CHANGE, 1104 "textareawithchild" 1105 ); 1106 await invokeContentTask(browser, [], () => { 1107 content.document.getElementById("textareawithchild").value = "Bar"; 1108 }); 1109 await valueChanged; 1110 1111 testName_("textareawithchild", "Story is ended."); 1112 1113 // //////////////////////////////////////////////////////////////////////// 1114 // controls having a value used as a part of computed name 1115 1116 testName_("ctrlvalue_progressbar:input", "foo 5 baz"); 1117 testName_("ctrlvalue_scrollbar:input", "foo 5 baz"); 1118 testName_("ctrlvalue_slider:input", "foo 5 baz"); 1119 testName_("ctrlvalue_spinbutton:input", "foo 5 baz"); 1120 testName_("ctrlvalue_combobox:input", "foo 5 baz"); 1121 testName_("ctrlvalue_meter:input", "foo 5 baz"); 1122 1123 // /////////////////////////////////////////////////////////////////////// 1124 // label with nested combobox (test for 'f' item of name computation guide) 1125 1126 testName_("comboinstart", "One day(s)."); 1127 testName_("combo3", "day(s)."); 1128 1129 testName_("textboxinstart", "Two days."); 1130 testName_("textbox1", "days."); 1131 1132 testName_("comboinmiddle", "Subscribe to ATOM feed."); 1133 testName_("combo4", "Subscribe to feed."); 1134 1135 testName_( 1136 "comboinmiddle2", 1137 "Play the Haliluya sound when new mail arrives" 1138 ); 1139 testName_("combo5", null); // label isn't used as a name for control 1140 testName_("checkbox", "Play the Haliluya sound when new mail arrives"); 1141 testName_( 1142 "comboinmiddle3", 1143 "Play the Haliluya sound when new mail arrives" 1144 ); 1145 testName_("combo6", "Play the sound when new mail arrives"); 1146 1147 testName_("comboinend", "This day was sunny"); 1148 testName_("combo7", "This day was"); 1149 1150 testName_("textboxinend", "This day was sunny"); 1151 testName_("textbox2", "This day was"); 1152 1153 // placeholder 1154 testName_("ph_password", "a placeholder"); 1155 testName_("ph_text", "a placeholder"); 1156 testName_("ph_textarea", "a placeholder"); 1157 testName_("ph_text2", "a label"); 1158 testName_("ph_textarea2", "a label"); 1159 testName_("ph_text3", "a label"); 1160 1161 // Test equation image 1162 testName_("img_eq", "x^2 + y^2 + z^2"); 1163 testName_("input_img_eq", "x^2 + y^2 + z^2"); 1164 testName_("txt_eq", "x^2 + y^2 + z^2"); 1165 1166 // ////////////////////////////////////////////////////////////////////// 1167 // tests for duplicate announcement of content 1168 1169 testName_("test_note", null); 1170 1171 // ////////////////////////////////////////////////////////////////////// 1172 // Tests for name from sub tree of tr element. 1173 1174 // By default, we want no name. 1175 testName_("NoNameForTR", null); 1176 testName_("NoNameForNonStandardTR", null); 1177 1178 // But we want it if the tr has an ARIA role. 1179 testName_("NameForTRBecauseStrongARIA", "a b"); 1180 1181 // But not if it is a weak (landmark) ARIA role 1182 testName_("NoNameForTRBecauseWeakARIA", null); 1183 1184 // Name from sub tree of grouping if requested by other accessible. 1185 testName_("grouping", null); 1186 testName_("requested_name_from_grouping", "label"); 1187 testName_("listitem_containing_block_tbody", "label"); 1188 // Groupings shouldn't be included when calculating from the subtree of 1189 // a treeitem. 1190 testName_("treeitem_containing_grouping", "root"); 1191 1192 // Name from subtree of grouping labelled by an ancestor. 1193 testName_("grouping_labelledby_ancestor", "label"); 1194 1195 // Name from subtree of a container containing text nodes and inline 1196 // elements. There should be no spaces because everything is inline. 1197 testName_("container_text_inline", "abc"); 1198 // Name from subtree of a container containing text nodes and block 1199 // elements. There should be a space on both sides of the block. 1200 testName_("container_text_block", "a b c"); 1201 // Name from subtree of a container containing text nodes and empty 1202 // block elements. There should be space on either side of the blocks, but 1203 // not a double space. 1204 testName_("container_text_emptyblock", "a b"); 1205 1206 let reordered = waitForEvent(EVENT_REORDER, "shadowHost"); 1207 await invokeContentTask(browser, [], () => { 1208 const shadowHost = content.document.getElementById("shadowHost"); 1209 const shadowRoot = shadowHost.attachShadow({ mode: "open" }); 1210 shadowRoot.append( 1211 content.document 1212 .getElementById("shadowTemplate") 1213 .content.cloneNode(true) 1214 ); 1215 }); 1216 await reordered; 1217 1218 const shadowRoot = findAccessibleChildByID(docAcc, "shadowHost"); 1219 testName(shadowRoot.firstChild, "shadowButtonVisibleText"); 1220 testName(shadowRoot.lastChild, "shadowButtonHiddenText"); 1221 1222 // Label from a hidden element containing script and style elements. 1223 testName_("buttonScriptStyle", "content"); 1224 1225 // Name from subtree on a link containing <code>, etc. 1226 testName_("linkWithCodeSupSubInsDel", "before code sup sub ins del after"); 1227 }, 1228 { topLevel: true, chrome: true } 1229 );