browser_treeupdate_ariaowns.js (21074B)
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 /* import-globals-from ../../mochitest/role.js */ 8 loadScripts({ name: "role.js", dir: MOCHITESTS_DIR }); 9 /* import-globals-from ../../mochitest/states.js */ 10 loadScripts({ name: "states.js", dir: MOCHITESTS_DIR }); 11 12 requestLongerTimeout(2); 13 14 function invokeSetAriaOwns( 15 browser, 16 id, 17 children = null, 18 elementReflection = false 19 ) { 20 if (!elementReflection) { 21 return invokeSetAttribute(browser, id, "aria-owns", children); 22 } 23 24 return invokeContentTask( 25 browser, 26 [id, children], 27 (contentId, contentChildrenIds) => { 28 let elm = content.document.getElementById(contentId); 29 if (contentChildrenIds) { 30 elm.ariaOwnsElements = contentChildrenIds 31 .split(" ") 32 .map(childId => content.document.getElementById(childId)); 33 } else { 34 elm.ariaOwnsElements = null; 35 } 36 } 37 ); 38 } 39 40 async function testContainer1(browser, accDoc, elementReflection = false) { 41 const id = "t1_container"; 42 const docID = getAccessibleDOMNodeID(accDoc); 43 const acc = findAccessibleChildByID(accDoc, id); 44 45 /* ================= Initial tree test ==================================== */ 46 // children are swapped by ARIA owns 47 let tree = { 48 SECTION: [{ CHECKBUTTON: [{ SECTION: [] }] }, { PUSHBUTTON: [] }], 49 }; 50 testAccessibleTree(acc, tree); 51 52 /* ================ Change ARIA owns ====================================== */ 53 let onReorder = waitForEvent(EVENT_REORDER, id); 54 await invokeSetAriaOwns( 55 browser, 56 id, 57 "t1_button t1_subdiv", 58 elementReflection 59 ); 60 await onReorder; 61 62 // children are swapped again, button and subdiv are appended to 63 // the children. 64 tree = { 65 SECTION: [ 66 { CHECKBUTTON: [] }, // checkbox, native order 67 { PUSHBUTTON: [] }, // button, rearranged by ARIA own 68 { SECTION: [] }, // subdiv from the subtree, ARIA owned 69 ], 70 }; 71 testAccessibleTree(acc, tree); 72 73 /* ================ Remove ARIA owns ====================================== */ 74 onReorder = waitForEvent(EVENT_REORDER, id); 75 await invokeSetAriaOwns(browser, id, null, elementReflection); 76 await onReorder; 77 78 // children follow the DOM order 79 tree = { 80 SECTION: [{ PUSHBUTTON: [] }, { CHECKBUTTON: [{ SECTION: [] }] }], 81 }; 82 testAccessibleTree(acc, tree); 83 84 /* ================ Set ARIA owns ========================================= */ 85 onReorder = waitForEvent(EVENT_REORDER, id); 86 await invokeSetAriaOwns( 87 browser, 88 id, 89 "t1_button t1_subdiv", 90 elementReflection 91 ); 92 await onReorder; 93 94 // children are swapped again, button and subdiv are appended to 95 // the children. 96 tree = { 97 SECTION: [ 98 { CHECKBUTTON: [] }, // checkbox 99 { PUSHBUTTON: [] }, // button, rearranged by ARIA own 100 { SECTION: [] }, // subdiv from the subtree, ARIA owned 101 ], 102 }; 103 testAccessibleTree(acc, tree); 104 105 /* ================ Add ID to ARIA owns =================================== */ 106 onReorder = waitForEvent(EVENT_REORDER, docID); 107 await invokeSetAttribute( 108 browser, 109 id, 110 "aria-owns", 111 "t1_button t1_subdiv t1_group" 112 ); 113 await onReorder; 114 115 // children are swapped again, button and subdiv are appended to 116 // the children. 117 tree = { 118 SECTION: [ 119 { CHECKBUTTON: [] }, // t1_checkbox 120 { PUSHBUTTON: [] }, // button, t1_button 121 { SECTION: [] }, // subdiv from the subtree, t1_subdiv 122 { GROUPING: [] }, // group from outside, t1_group 123 ], 124 }; 125 testAccessibleTree(acc, tree); 126 127 /* ================ Append element ======================================== */ 128 onReorder = waitForEvent(EVENT_REORDER, id); 129 await invokeContentTask(browser, [id], contentId => { 130 let div = content.document.createElement("div"); 131 div.setAttribute("id", "t1_child3"); 132 div.setAttribute("role", "radio"); 133 content.document.getElementById(contentId).appendChild(div); 134 }); 135 await onReorder; 136 137 // children are invalidated, they includes aria-owns swapped kids and 138 // newly inserted child. 139 tree = { 140 SECTION: [ 141 { CHECKBUTTON: [] }, // existing explicit, t1_checkbox 142 { RADIOBUTTON: [] }, // new explicit, t1_child3 143 { PUSHBUTTON: [] }, // ARIA owned, t1_button 144 { SECTION: [] }, // ARIA owned, t1_subdiv 145 { GROUPING: [] }, // ARIA owned, t1_group 146 ], 147 }; 148 testAccessibleTree(acc, tree); 149 150 /* ================ Remove element ======================================== */ 151 onReorder = waitForEvent(EVENT_REORDER, id); 152 await invokeContentTask(browser, [], () => { 153 content.document.getElementById("t1_span").remove(); 154 }); 155 await onReorder; 156 157 // subdiv should go away 158 tree = { 159 SECTION: [ 160 { CHECKBUTTON: [] }, // explicit, t1_checkbox 161 { RADIOBUTTON: [] }, // explicit, t1_child3 162 { PUSHBUTTON: [] }, // ARIA owned, t1_button 163 { GROUPING: [] }, // ARIA owned, t1_group 164 ], 165 }; 166 testAccessibleTree(acc, tree); 167 168 /* ================ Remove ID ============================================= */ 169 onReorder = waitForEvent(EVENT_REORDER, docID); 170 await invokeSetAttribute(browser, "t1_group", "id"); 171 await onReorder; 172 173 tree = { 174 SECTION: [ 175 { CHECKBUTTON: [] }, 176 { RADIOBUTTON: [] }, 177 { PUSHBUTTON: [] }, // ARIA owned, t1_button 178 ], 179 }; 180 testAccessibleTree(acc, tree); 181 182 /* ================ Set ID ================================================ */ 183 onReorder = waitForEvent(EVENT_REORDER, docID); 184 await invokeSetAttribute(browser, "t1_grouptmp", "id", "t1_group"); 185 await onReorder; 186 187 tree = { 188 SECTION: [ 189 { CHECKBUTTON: [] }, 190 { RADIOBUTTON: [] }, 191 { PUSHBUTTON: [] }, // ARIA owned, t1_button 192 { GROUPING: [] }, // ARIA owned, t1_group, previously t1_grouptmp 193 ], 194 }; 195 testAccessibleTree(acc, tree); 196 } 197 198 async function removeContainer(browser, accDoc) { 199 const id = "t2_container1"; 200 const acc = findAccessibleChildByID(accDoc, id); 201 202 let tree = { 203 SECTION: [ 204 { CHECKBUTTON: [] }, // ARIA owned, 't2_owned' 205 ], 206 }; 207 testAccessibleTree(acc, tree); 208 209 let onReorder = waitForEvent(EVENT_REORDER, id); 210 await invokeContentTask(browser, [], () => { 211 content.document 212 .getElementById("t2_container2") 213 .removeChild(content.document.getElementById("t2_container3")); 214 }); 215 await onReorder; 216 217 tree = { 218 SECTION: [], 219 }; 220 testAccessibleTree(acc, tree); 221 } 222 223 async function stealAndRecacheChildren(browser, accDoc, elementReflection) { 224 const id1 = "t3_container1"; 225 const id2 = "t3_container2"; 226 const acc1 = findAccessibleChildByID(accDoc, id1); 227 const acc2 = findAccessibleChildByID(accDoc, id2); 228 229 /* ================ Attempt to steal from other ARIA owns ================= */ 230 let onReorder = waitForEvent(EVENT_REORDER, id2); 231 await invokeSetAriaOwns(browser, id2, "t3_child", elementReflection); 232 await invokeContentTask(browser, [id2], id => { 233 let div = content.document.createElement("div"); 234 div.setAttribute("role", "radio"); 235 content.document.getElementById(id).appendChild(div); 236 }); 237 await onReorder; 238 239 let tree = { 240 SECTION: [ 241 { CHECKBUTTON: [] }, // ARIA owned 242 ], 243 }; 244 testAccessibleTree(acc1, tree); 245 246 tree = { 247 SECTION: [{ RADIOBUTTON: [] }], 248 }; 249 testAccessibleTree(acc2, tree); 250 } 251 252 async function showHiddenElement(browser, accDoc) { 253 const id = "t4_container1"; 254 const acc = findAccessibleChildByID(accDoc, id); 255 256 let tree = { 257 SECTION: [{ RADIOBUTTON: [] }], 258 }; 259 testAccessibleTree(acc, tree); 260 261 let onReorder = waitForEvent(EVENT_REORDER, id); 262 await invokeSetStyle(browser, "t4_child1", "display", "block"); 263 await onReorder; 264 265 tree = { 266 SECTION: [{ CHECKBUTTON: [] }, { RADIOBUTTON: [] }], 267 }; 268 testAccessibleTree(acc, tree); 269 } 270 271 async function rearrangeARIAOwns(browser, accDoc, elementReflection) { 272 const id = "t5_container"; 273 const acc = findAccessibleChildByID(accDoc, id); 274 const tests = [ 275 { 276 val: "t5_checkbox t5_radio t5_button", 277 roleList: ["CHECKBUTTON", "RADIOBUTTON", "PUSHBUTTON"], 278 }, 279 { 280 val: "t5_radio t5_button t5_checkbox", 281 roleList: ["RADIOBUTTON", "PUSHBUTTON", "CHECKBUTTON"], 282 }, 283 ]; 284 285 for (let { val, roleList } of tests) { 286 let onReorder = waitForEvent(EVENT_REORDER, id); 287 await invokeSetAriaOwns(browser, id, val, elementReflection); 288 await onReorder; 289 290 let tree = { SECTION: [] }; 291 for (let role of roleList) { 292 let ch = {}; 293 ch[role] = []; 294 tree.SECTION.push(ch); 295 } 296 testAccessibleTree(acc, tree); 297 } 298 } 299 300 async function removeNotARIAOwnedEl(browser, accDoc) { 301 const id = "t6_container"; 302 const acc = findAccessibleChildByID(accDoc, id); 303 304 let tree = { 305 SECTION: [{ TEXT_LEAF: [] }, { GROUPING: [] }], 306 }; 307 testAccessibleTree(acc, tree); 308 309 let onReorder = waitForEvent(EVENT_REORDER, id); 310 await invokeContentTask(browser, [id], contentId => { 311 content.document 312 .getElementById(contentId) 313 .removeChild(content.document.getElementById("t6_span")); 314 }); 315 await onReorder; 316 317 tree = { 318 SECTION: [{ GROUPING: [] }], 319 }; 320 testAccessibleTree(acc, tree); 321 } 322 323 addAccessibleTask( 324 "e10s/doc_treeupdate_ariaowns.html", 325 async function (browser, accDoc) { 326 await testContainer1(browser, accDoc); 327 await removeContainer(browser, accDoc); 328 await stealAndRecacheChildren(browser, accDoc); 329 await showHiddenElement(browser, accDoc); 330 await rearrangeARIAOwns(browser, accDoc); 331 await removeNotARIAOwnedEl(browser, accDoc); 332 }, 333 { iframe: true, remoteIframe: true } 334 ); 335 336 addAccessibleTask( 337 "e10s/doc_treeupdate_ariaowns.html", 338 async function (browser, accDoc) { 339 await testContainer1(browser, accDoc, true); 340 await removeContainer(browser, accDoc); 341 await stealAndRecacheChildren(browser, accDoc, true); 342 await showHiddenElement(browser, accDoc); 343 await rearrangeARIAOwns(browser, accDoc, true); 344 await removeNotARIAOwnedEl(browser, accDoc); 345 }, 346 { iframe: true, remoteIframe: true } 347 ); 348 349 // Test owning an ancestor which isn't created yet with an iframe in the 350 // subtree. 351 addAccessibleTask( 352 ` 353 <span id="a"> 354 <div id="b" aria-owns="c"></div> 355 </span> 356 <div id="c"> 357 <iframe></iframe> 358 </div> 359 <script> 360 document.getElementById("c").setAttribute("aria-owns", "a"); 361 </script> 362 `, 363 async function (browser, accDoc) { 364 testAccessibleTree(accDoc, { 365 DOCUMENT: [ 366 { 367 // b 368 SECTION: [ 369 { 370 // c 371 SECTION: [{ INTERNAL_FRAME: [{ DOCUMENT: [] }] }], 372 }, 373 ], 374 }, 375 ], 376 }); 377 } 378 ); 379 380 // Verify that removing the parent of a DOM-sibling aria-owned child keeps the 381 // formerly-owned child in the tree. 382 addAccessibleTask( 383 `<input id='x'></input><div aria-owns='x'></div>`, 384 async function (browser, accDoc) { 385 testAccessibleTree(accDoc, { 386 DOCUMENT: [{ SECTION: [{ ENTRY: [] }] }], 387 }); 388 389 info("Removing the div that aria-owns a DOM sibling"); 390 let onReorder = waitForEvent(EVENT_REORDER, accDoc); 391 await invokeContentTask(browser, [], () => { 392 content.document.querySelector("div").remove(); 393 }); 394 await onReorder; 395 396 info("Verifying that the formerly-owned child is still present"); 397 testAccessibleTree(accDoc, { 398 DOCUMENT: [{ ENTRY: [] }], 399 }); 400 }, 401 { chrome: true, iframe: true, remoteIframe: true } 402 ); 403 404 // Verify that removing the parent of multiple DOM-sibling aria-owned children 405 // keeps all formerly-owned children in the tree. 406 addAccessibleTask( 407 `<input id='x'></input><input id='y'><div aria-owns='x y'></div>`, 408 async function (browser, accDoc) { 409 testAccessibleTree(accDoc, { 410 DOCUMENT: [ 411 { 412 SECTION: [{ ENTRY: [] }, { ENTRY: [] }], 413 }, 414 ], 415 }); 416 417 info("Removing the div that aria-owns DOM siblings"); 418 let onReorder = waitForEvent(EVENT_REORDER, accDoc); 419 await invokeContentTask(browser, [], () => { 420 content.document.querySelector("div").remove(); 421 }); 422 await onReorder; 423 424 info("Verifying that the formerly-owned children are still present"); 425 testAccessibleTree(accDoc, { 426 DOCUMENT: [{ ENTRY: [] }, { ENTRY: [] }], 427 }); 428 }, 429 { chrome: true, iframe: true, remoteIframe: true } 430 ); 431 432 // Verify that reordering owned elements by changing the aria-owns attribute 433 // properly reorders owned elements. 434 addAccessibleTask( 435 ` 436 <div id="container" aria-owns="b d c a"> 437 <div id="a" role="button"></div> 438 <div id="b" role="checkbox"></div> 439 </div> 440 <div id="c" role="radio"></div> 441 <div id="d"></div>`, 442 async function (browser, accDoc) { 443 testAccessibleTree(accDoc, { 444 DOCUMENT: [ 445 { 446 SECTION: [ 447 { CHECKBUTTON: [] }, // b 448 { SECTION: [] }, // d 449 { RADIOBUTTON: [] }, // c 450 { PUSHBUTTON: [] }, // a 451 ], 452 }, 453 ], 454 }); 455 456 info("Removing the div that aria-owns other elements"); 457 let onReorder = waitForEvent(EVENT_REORDER, accDoc); 458 await invokeContentTask(browser, [], () => { 459 content.document.querySelector("#container").remove(); 460 }); 461 await onReorder; 462 463 info( 464 "Verify DOM children are removed, order of remaining elements is correct" 465 ); 466 testAccessibleTree(accDoc, { 467 DOCUMENT: [ 468 { RADIOBUTTON: [] }, // c 469 { SECTION: [] }, // d 470 ], 471 }); 472 }, 473 { chrome: true, iframe: true, remoteIframe: true } 474 ); 475 476 // Verify that we avoid sending unwanted hide events when doing multiple 477 // aria-owns relocations in a single tick. Note that we're avoiding testing 478 // chrome here since parent process locals don't track moves in the same way, 479 // meaning our mechanism for avoiding duplicate hide events doesn't work. 480 addAccessibleTask( 481 ` 482 <div id='b' aria-owns='a'></div> 483 <div id='d'></div> 484 <dd id='f'> 485 <div id='a' aria-owns='d'></div> 486 </dd> 487 `, 488 async function (browser, accDoc) { 489 const b = findAccessibleChildByID(accDoc, "b"); 490 const waitFor = { 491 expected: [ 492 [EVENT_HIDE, b], 493 [EVENT_SHOW, "d"], 494 [EVENT_REORDER, accDoc], 495 ], 496 unexpected: [ 497 [EVENT_HIDE, "d"], 498 [EVENT_REORDER, "a"], 499 ], 500 }; 501 info( 502 "Verifying that events are fired properly after doing two aria-owns relocations" 503 ); 504 await contentSpawnMutation(browser, waitFor, function () { 505 content.document.querySelector("#b").remove(); 506 content.document.querySelector("#f").remove(); 507 }); 508 }, 509 { chrome: false, iframe: true, remoteIframe: true } 510 ); 511 512 /** 513 * Test relation defaults via element internals 514 */ 515 addAccessibleTask( 516 ` 517 518 <div role="listbox"> 519 <div role="listitem" id="l1"></div> 520 <div role="listitem" id="l2"></div> 521 <div role="listitem" id="l3"></div> 522 </div> 523 <custom-listbox id="listbox"></custom-listbox> 524 <div role="listbox"> 525 <div role="listitem" id="l4"></div> 526 </div> 527 528 <script> 529 customElements.define("custom-listbox", 530 class extends HTMLElement { 531 constructor() { 532 super(); 533 this.tabIndex = "0" 534 this._internals = this.attachInternals(); 535 this._internals.role = "listbox"; 536 this._internals.ariaOwnsElements = Array.from(this.previousElementSibling.children) 537 } 538 } 539 ); 540 </script>`, 541 async function (browser, accDoc) { 542 let listbox = findAccessibleChildByID(accDoc, "listbox"); 543 is(listbox.children.length, 3, "got children"); 544 let onReorder = waitForEvent(EVENT_REORDER, "listbox"); 545 invokeSetAriaOwns(browser, "listbox", "l4"); 546 await onReorder; 547 } 548 ); 549 550 /** 551 * Test insertion of relocated by ID child after initial load 552 */ 553 addAccessibleTask( 554 `<div id='a' aria-owns='b'></div>`, 555 async function (browser, accDoc) { 556 const a = findAccessibleChildByID(accDoc, "a"); 557 is(a.children.length, 0, "'a' has no children"); 558 const waitFor = { 559 expected: [ 560 [EVENT_SHOW, "b"], 561 [EVENT_INNER_REORDER, a], 562 [EVENT_REORDER, accDoc], 563 ], 564 }; 565 await contentSpawnMutation(browser, waitFor, function () { 566 const b = content.document.createElement("div"); 567 b.id = "b"; 568 content.document.body.appendChild(b); 569 }); 570 is(getAccessibleDOMNodeID(a.firstChild), "b", "'a' owns relocated child"); 571 } 572 ); 573 574 /** 575 * Test insertion of relocated by child element reflection after initial load 576 */ 577 addAccessibleTask(`<div id='a'></div>`, async function (browser, accDoc) { 578 const a = findAccessibleChildByID(accDoc, "a"); 579 is(a.children.length, 0, "'a' has no children"); 580 581 // Create div and add it to a's ariaOwnsElements. 582 // The refresh ticks called in contentSpawnMutation 583 // will cause a relocation to be scheduled and performed. 584 // Nothing will happen because 'b' is not parented yet. 585 let waitFor = { 586 unexpected: [ 587 [EVENT_SHOW, "b"], 588 [EVENT_INNER_REORDER, a], 589 [EVENT_REORDER, accDoc], 590 ], 591 }; 592 await contentSpawnMutation(browser, waitFor, function () { 593 content.b = content.document.createElement("div"); 594 content.b.id = "b"; 595 content.document.getElementById("a").ariaOwnsElements = [content.b]; 596 }); 597 598 // Parent 'b'. It should relocate into 'a'. 599 waitFor = { 600 expected: [ 601 [EVENT_SHOW, "b"], 602 [EVENT_INNER_REORDER, a], 603 [EVENT_REORDER, accDoc], 604 ], 605 }; 606 await contentSpawnMutation(browser, waitFor, function () { 607 content.document.body.appendChild(content.b); 608 }); 609 is(getAccessibleDOMNodeID(a.firstChild), "b", "'a' owns relocated child"); 610 }); 611 612 /* 613 * Test to assure that aria-owned elements are not relocated into an editable subtree. 614 */ 615 addAccessibleTask( 616 ` 617 <button id="btn">World</button> 618 <div contentEditable="true" id="textbox" role="textbox"> 619 <p id="p" aria-owns="btn">Hello</p> 620 </div> 621 `, 622 async function (browser, accDoc) { 623 const p = findAccessibleChildByID(accDoc, "p"); 624 const textbox = findAccessibleChildByID(accDoc, "textbox"); 625 626 testStates(textbox, 0, EXT_STATE_EDITABLE, 0, 0); 627 isnot(getAccessibleDOMNodeID(p.lastChild), "btn", "'p' owns relocated btn"); 628 is(textbox.value, "Hello"); 629 630 let expectedEvents = Promise.all([ 631 waitForStateChange(textbox, EXT_STATE_EDITABLE, false, true), 632 waitForEvent(EVENT_INNER_REORDER, p), 633 ]); 634 await invokeContentTask(browser, [], () => { 635 content.document.getElementById("textbox").contentEditable = false; 636 }); 637 await expectedEvents; 638 is(getAccessibleDOMNodeID(p.lastChild), "btn", "'p' owns relocated btn"); 639 is(textbox.value, "Hello World"); 640 641 expectedEvents = Promise.all([ 642 waitForStateChange(textbox, EXT_STATE_EDITABLE, true, true), 643 waitForEvent(EVENT_INNER_REORDER, p), 644 ]); 645 await invokeContentTask(browser, [], () => { 646 content.document.getElementById("textbox").contentEditable = true; 647 }); 648 await expectedEvents; 649 isnot(getAccessibleDOMNodeID(p.lastChild), "btn", "'p' owns relocated btn"); 650 is(textbox.value, "Hello"); 651 } 652 ); 653 654 /* 655 * Test to ensure that aria-owned elements are not relocated out of editable subtree. 656 */ 657 addAccessibleTask( 658 ` 659 <div contentEditable="true" id="textbox" role="textbox"> 660 <button id="btn">World</button> 661 </div> 662 <p id="p" aria-owns="btn">Hello</p> 663 <p id="p2" aria-owns="textbox"></p> 664 `, 665 async function (browser, accDoc) { 666 const p = findAccessibleChildByID(accDoc, "p"); 667 const textbox = findAccessibleChildByID(accDoc, "textbox"); 668 testStates(textbox, 0, EXT_STATE_EDITABLE, 0, 0); 669 670 is( 671 getAccessibleDOMNodeID(textbox.parent), 672 "p2", 673 "editable root can be relocated" 674 ); 675 isnot( 676 getAccessibleDOMNodeID(p.lastChild), 677 "btn", 678 "editable element cannot be relocated" 679 ); 680 is(textbox.value, "World"); 681 682 let expectedEvents = Promise.all([ 683 waitForStateChange(textbox, EXT_STATE_EDITABLE, false, true), 684 waitForEvent(EVENT_REORDER, p), 685 ]); 686 await invokeContentTask(browser, [], () => { 687 content.document.getElementById("textbox").contentEditable = false; 688 }); 689 await expectedEvents; 690 is( 691 getAccessibleDOMNodeID(p.lastChild), 692 "btn", 693 "'p' owns readonly relocated btn" 694 ); 695 is(textbox.value, ""); 696 is( 697 getAccessibleDOMNodeID(textbox.parent), 698 "p2", 699 "textbox is still relocated" 700 ); 701 } 702 ); 703 704 /** 705 * Test relocating a child within its parent while also moving the caret. This 706 * is based on a fuzzing test case. 707 */ 708 addAccessibleTask( 709 ` 710 <address id="a" contenteditable="true"></address> 711 AAAAAAAA 712 <label> 713 `, 714 async function testRelocateChildWithCaretMove(browser, docAcc) { 715 let moved = waitForEvent(EVENT_TEXT_CARET_MOVED, docAcc); 716 await invokeContentTask(browser, [], () => { 717 content.document.body.setAttribute("aria-owns", "a"); 718 content.getSelection().selectAllChildren(content.document.body); 719 content.document.documentElement.style.display = "none"; 720 content.document.documentElement.getBoundingClientRect(); 721 content.document.documentElement.style.display = ""; 722 content.getSelection().modify("extend", "right", "line"); 723 }); 724 await moved; 725 } 726 );