tor-browser

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

browser_textPatterns.js (106479B)


      1 /* This Source Code Form is subject to the terms of the Mozilla Public
      2 /* This Source Code Form is subject to the terms of the Mozilla Public
      3 * License, v. 2.0. If a copy of the MPL was not distributed with this
      4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      5 
      6 "use strict";
      7 
      8 /* import-globals-from ../../../mochitest/text.js */
      9 loadScripts({ name: "text.js", dir: MOCHITESTS_DIR });
     10 
     11 /* eslint-disable camelcase */
     12 const SupportedTextSelection_None = 0;
     13 const SupportedTextSelection_Multiple = 2;
     14 /* eslint-enable camelcase */
     15 
     16 /**
     17 * Test the Text pattern's DocumentRange property. This also tests where the
     18 * Text pattern is exposed.
     19 */
     20 addUiaTask(
     21  `
     22 <div><input id="input" value="input"></div>
     23 <textarea id="textarea">textarea</textarea>
     24 <div id="contentEditable" contenteditable><p>content</p><p>editable</p></div>
     25 <p id="p">p</p>
     26 <a id="link" href="#">link</a>
     27  `,
     28  async function testTextDocumentRange() {
     29    await definePyVar("doc", `getDocUia()`);
     30    await definePyVar("pattern", `getUiaPattern(doc, "Text")`);
     31    ok(await runPython(`bool(pattern)`), "doc has Text pattern");
     32    // The IA2 -> UIA proxy adds spaces between elements that don't exist.
     33    if (gIsUiaEnabled) {
     34      is(
     35        await runPython(`pattern.DocumentRange.GetText(-1)`),
     36        "inputtextareacontenteditableplink",
     37        "document DocumentRange Text correct"
     38      );
     39    }
     40 
     41    await assignPyVarToUiaWithId("input");
     42    await definePyVar("pattern", `getUiaPattern(input, "Text")`);
     43    ok(await runPython(`bool(pattern)`), "input has Text pattern");
     44    is(
     45      await runPython(`pattern.DocumentRange.GetText(-1)`),
     46      "input",
     47      "input DocumentRange Text correct"
     48    );
     49 
     50    await assignPyVarToUiaWithId("textarea");
     51    await definePyVar("pattern", `getUiaPattern(textarea, "Text")`);
     52    ok(await runPython(`bool(pattern)`), "textarea has Text pattern");
     53    is(
     54      await runPython(`pattern.DocumentRange.GetText(-1)`),
     55      "textarea",
     56      "textarea DocumentRange Text correct"
     57    );
     58 
     59    // The IA2 -> UIA proxy doesn't expose the Text pattern on contentEditables
     60    // without role="textbox".
     61    if (gIsUiaEnabled) {
     62      await assignPyVarToUiaWithId("contentEditable");
     63      await definePyVar("pattern", `getUiaPattern(contentEditable, "Text")`);
     64      ok(await runPython(`bool(pattern)`), "contentEditable has Text pattern");
     65      is(
     66        await runPython(`pattern.DocumentRange.GetText(-1)`),
     67        "contenteditable",
     68        "contentEditable DocumentRange Text correct"
     69      );
     70    }
     71 
     72    await testPatternAbsent("p", "Text");
     73    // The IA2 -> UIA proxy doesn't expose the Text pattern on this text leaf.
     74    if (gIsUiaEnabled) {
     75      await runPython(`
     76        global pLeaf
     77        p = findUiaByDomId(doc, "p")
     78        pLeaf = uiaClient.RawViewWalker.GetFirstChildElement(p)
     79      `);
     80      await definePyVar("pattern", `getUiaPattern(pLeaf, "Text")`);
     81      ok(await runPython(`bool(pattern)`), "pLeaf has Text pattern");
     82      is(
     83        await runPython(`pattern.DocumentRange.GetText(-1)`),
     84        "p",
     85        "pLeaf DocumentRange Text correct"
     86      );
     87    }
     88 
     89    await testPatternAbsent("link", "Text");
     90    // The IA2 -> UIA proxy doesn't expose this text leaf at all.
     91    if (gIsUiaEnabled) {
     92      await runPython(`
     93        global linkLeaf
     94        link = findUiaByDomId(doc, "link")
     95        linkLeaf = uiaClient.RawViewWalker.GetFirstChildElement(link)
     96      `);
     97      await definePyVar("pattern", `getUiaPattern(linkLeaf, "Text")`);
     98      ok(await runPython(`bool(pattern)`), "linkLeaf has Text pattern");
     99      is(
    100        await runPython(`pattern.DocumentRange.GetText(-1)`),
    101        "link",
    102        "linkLeaf DocumentRange Text correct"
    103      );
    104    }
    105  }
    106 );
    107 
    108 /**
    109 * Test the TextRange pattern's GetText method.
    110 */
    111 addUiaTask(
    112  `<div id="editable" contenteditable role="textbox">a <span>b</span>`,
    113  async function testTextRangeGetText() {
    114    await runPython(`
    115      doc = getDocUia()
    116      editable = findUiaByDomId(doc, "editable")
    117      text = getUiaPattern(editable, "Text")
    118      global range
    119      range = text.DocumentRange
    120    `);
    121    is(await runPython(`range.GetText(-1)`), "a b", "GetText(-1) correct");
    122    is(await runPython(`range.GetText(0)`), "", "GetText(0) correct");
    123    is(await runPython(`range.GetText(1)`), "a", "GetText(1) correct");
    124    is(await runPython(`range.GetText(2)`), "a ", "GetText(2) correct");
    125    is(await runPython(`range.GetText(3)`), "a b", "GetText(3) correct");
    126    is(await runPython(`range.GetText(4)`), "a b", "GetText(4) correct");
    127  }
    128 );
    129 
    130 /**
    131 * Test the TextRange pattern's Clone method.
    132 */
    133 addUiaTask(
    134  `<input id="input" type="text" value="testing">`,
    135  async function testTextRangeClone() {
    136    await runPython(`
    137      doc = getDocUia()
    138      input = findUiaByDomId(doc, "input")
    139      text = getUiaPattern(input, "Text")
    140      global origRange
    141      origRange = text.DocumentRange
    142    `);
    143    is(
    144      await runPython(`origRange.GetText(-1)`),
    145      "testing",
    146      "origRange text correct"
    147    );
    148    await runPython(`
    149      global clonedRange
    150      clonedRange = origRange.Clone()
    151    `);
    152    is(
    153      await runPython(`clonedRange.GetText(-1)`),
    154      "testing",
    155      "clonedRange text correct"
    156    );
    157 
    158    // Test that modifying clonedRange doesn't impact origRange.
    159    info("Collapsing clonedRange to start");
    160    await runPython(
    161      `clonedRange.MoveEndpointByRange(TextPatternRangeEndpoint_End, clonedRange, TextPatternRangeEndpoint_Start)`
    162    );
    163    is(
    164      await runPython(`clonedRange.GetText(-1)`),
    165      "",
    166      "clonedRange text correct"
    167    );
    168    is(
    169      await runPython(`origRange.GetText(-1)`),
    170      "testing",
    171      "origRange text correct"
    172    );
    173  }
    174 );
    175 
    176 /**
    177 * Test the TextRange pattern's Compare method.
    178 */
    179 addUiaTask(
    180  `<input id="input" type="text" value="testing">`,
    181  async function testTextRangeCompare() {
    182    await runPython(`
    183      doc = getDocUia()
    184      input = findUiaByDomId(doc, "input")
    185      text = getUiaPattern(input, "Text")
    186      global range1, range2
    187      range1 = text.DocumentRange
    188      range2 = text.DocumentRange
    189    `);
    190    ok(
    191      await runPython(`range1.Compare(range2)`),
    192      "range1 Compare range2 correct"
    193    );
    194    ok(
    195      await runPython(`range2.Compare(range1)`),
    196      "range2 Compare range1 correct"
    197    );
    198    info("Collapsing range2 to start");
    199    await runPython(
    200      `range2.MoveEndpointByRange(TextPatternRangeEndpoint_End, range2, TextPatternRangeEndpoint_Start)`
    201    );
    202    ok(
    203      !(await runPython(`range1.Compare(range2)`)),
    204      "range1 Compare range2 correct"
    205    );
    206    ok(
    207      !(await runPython(`range2.Compare(range1)`)),
    208      "range2 Compare range1 correct"
    209    );
    210  }
    211 );
    212 
    213 /**
    214 * Test the TextRange pattern's CompareEndpoints method.
    215 */
    216 addUiaTask(
    217  `
    218 <p>before</p>
    219 <div><input id="input" type="text" value="input"></div>
    220 <p>after</p>
    221  `,
    222  async function testTextRangeCompareEndpoints() {
    223    await runPython(`
    224      global doc, range1, range2
    225      doc = getDocUia()
    226      input = findUiaByDomId(doc, "input")
    227      text = getUiaPattern(input, "Text")
    228      range1 = text.DocumentRange
    229      range2 = text.DocumentRange
    230    `);
    231    is(
    232      await runPython(
    233        `range1.CompareEndpoints(TextPatternRangeEndpoint_Start, range1, TextPatternRangeEndpoint_Start)`
    234      ),
    235      0,
    236      "Compare range1 start to range1 start correct"
    237    );
    238    is(
    239      await runPython(
    240        `range1.CompareEndpoints(TextPatternRangeEndpoint_End, range1, TextPatternRangeEndpoint_End)`
    241      ),
    242      0,
    243      "Compare range1 end to range1 end correct"
    244    );
    245    is(
    246      await runPython(
    247        `range1.CompareEndpoints(TextPatternRangeEndpoint_Start, range1, TextPatternRangeEndpoint_End)`
    248      ),
    249      -1,
    250      "Compare range1 start to range1 end correct"
    251    );
    252    is(
    253      await runPython(
    254        `range1.CompareEndpoints(TextPatternRangeEndpoint_End, range1, TextPatternRangeEndpoint_Start)`
    255      ),
    256      1,
    257      "Compare range1 end to range1 start correct"
    258    );
    259    // Compare different ranges.
    260    is(
    261      await runPython(
    262        `range1.CompareEndpoints(TextPatternRangeEndpoint_Start, range2, TextPatternRangeEndpoint_Start)`
    263      ),
    264      0,
    265      "Compare range1 start to range2 start correct"
    266    );
    267    is(
    268      await runPython(
    269        `range1.CompareEndpoints(TextPatternRangeEndpoint_End, range2, TextPatternRangeEndpoint_End)`
    270      ),
    271      0,
    272      "Compare range1 end to range2 end correct"
    273    );
    274    is(
    275      await runPython(
    276        `range1.CompareEndpoints(TextPatternRangeEndpoint_Start, range2, TextPatternRangeEndpoint_End)`
    277      ),
    278      -1,
    279      "Compare range1 start to range2 end correct"
    280    );
    281    is(
    282      await runPython(
    283        `range1.CompareEndpoints(TextPatternRangeEndpoint_End, range2, TextPatternRangeEndpoint_Start)`
    284      ),
    285      1,
    286      "Compare range1 end to range2 start correct"
    287    );
    288    // Compare ranges created using different elements.
    289    await definePyVar("range3", `getUiaPattern(doc, "Text").DocumentRange`);
    290    is(
    291      await runPython(
    292        `range1.CompareEndpoints(TextPatternRangeEndpoint_Start, range3, TextPatternRangeEndpoint_Start)`
    293      ),
    294      1,
    295      "Compare range1 start to range3 start correct"
    296    );
    297    is(
    298      await runPython(
    299        `range1.CompareEndpoints(TextPatternRangeEndpoint_End, range3, TextPatternRangeEndpoint_End)`
    300      ),
    301      -1,
    302      "Compare range1 end to range3 end correct"
    303    );
    304  }
    305 );
    306 
    307 /**
    308 * Test the TextRange pattern's ExpandToEnclosingUnit method.
    309 */
    310 addUiaTask(
    311  `
    312 <p>before</p>
    313 <div><textarea id="textarea" cols="5">ab cd ef gh</textarea></div>
    314 <div>after <input id="input" value="input"></div>
    315  `,
    316  async function testTextRangeExpandToEnclosingUnit() {
    317    info("Getting DocumentRange from textarea");
    318    await runPython(`
    319      global doc, range
    320      doc = getDocUia()
    321      textarea = findUiaByDomId(doc, "textarea")
    322      text = getUiaPattern(textarea, "Text")
    323      range = text.DocumentRange
    324    `);
    325    is(
    326      await runPython(`range.GetText(-1)`),
    327      "ab cd ef gh",
    328      "range text correct"
    329    );
    330    // Expand should shrink the range because it's too big.
    331    info("Expanding to character");
    332    await runPython(`range.ExpandToEnclosingUnit(TextUnit_Character)`);
    333    is(await runPython(`range.GetText(-1)`), "a", "range text correct");
    334    info("Collapsing to end");
    335    await runPython(
    336      `range.MoveEndpointByRange(TextPatternRangeEndpoint_Start, range, TextPatternRangeEndpoint_End)`
    337    );
    338    is(await runPython(`range.GetText(-1)`), "", "range text correct");
    339    // range is now collapsed at "b".
    340    info("Expanding to character");
    341    await runPython(`range.ExpandToEnclosingUnit(TextUnit_Character)`);
    342    is(await runPython(`range.GetText(-1)`), "b", "range text correct");
    343    info("Expanding to word");
    344    await runPython(`range.ExpandToEnclosingUnit(TextUnit_Word)`);
    345    is(await runPython(`range.GetText(-1)`), "ab ", "range text correct");
    346    info("Collapsing to end");
    347    await runPython(
    348      `range.MoveEndpointByRange(TextPatternRangeEndpoint_Start, range, TextPatternRangeEndpoint_End)`
    349    );
    350    // range is now collapsed at "c".
    351    info("Expanding to word");
    352    await runPython(`range.ExpandToEnclosingUnit(TextUnit_Word)`);
    353    is(await runPython(`range.GetText(-1)`), "cd ", "range text correct");
    354    info("Expanding to line");
    355    await runPython(`range.ExpandToEnclosingUnit(TextUnit_Line)`);
    356    is(await runPython(`range.GetText(-1)`), "ab cd ", "range text correct");
    357    info("Collapsing to end");
    358    await runPython(
    359      `range.MoveEndpointByRange(TextPatternRangeEndpoint_Start, range, TextPatternRangeEndpoint_End)`
    360    );
    361    // range is now collapsed at "e".
    362    info("Expanding to line");
    363    await runPython(`range.ExpandToEnclosingUnit(TextUnit_Line)`);
    364    // The IA2 -> UIA proxy gets most things below this wrong.
    365    if (!gIsUiaEnabled) {
    366      return;
    367    }
    368    is(await runPython(`range.GetText(-1)`), "ef gh", "range text correct");
    369    info("Expanding to document");
    370    await runPython(`range.ExpandToEnclosingUnit(TextUnit_Document)`);
    371    is(
    372      await runPython(`range.GetText(-1)`),
    373      "beforeab cd ef ghafter input",
    374      "range text correct"
    375    );
    376 
    377    // Test expanding to a line which crosses elements.
    378    info("Getting DocumentRange from input");
    379    await runPython(`
    380      input = findUiaByDomId(doc, "input")
    381      text = getUiaPattern(input, "Text")
    382      global range
    383      range = text.DocumentRange
    384    `);
    385    info("Expanding to line");
    386    await runPython(`range.ExpandToEnclosingUnit(TextUnit_Line)`);
    387    is(
    388      await runPython(`range.GetText(-1)`),
    389      "after input",
    390      "range text correct"
    391    );
    392    info("Collapsing to end");
    393    await runPython(
    394      `range.MoveEndpointByRange(TextPatternRangeEndpoint_Start, range, TextPatternRangeEndpoint_End)`
    395    );
    396    // range is now collapsed at the end of the document.
    397    info("Expanding to line");
    398    await runPython(`range.ExpandToEnclosingUnit(TextUnit_Line)`);
    399    is(
    400      await runPython(`range.GetText(-1)`),
    401      "after input",
    402      "range text correct"
    403    );
    404  }
    405 );
    406 
    407 /**
    408 * Test the Format TextUnit. Exercises ExpandToEnclosingUnit, Move, and
    409 * MoveEndpointByUnit. Tested here separately since the setup and implementation
    410 * is somewhat different from other TextUnits.
    411 */
    412 addUiaTask(
    413  `
    414 <div id="bold-container">a <b>bcd</b> ef</div>
    415 <div id="container-container">a <span tabindex="0">bcd</span> ef</div>
    416 <textarea id="textarea" spellcheck="true">test tset test</textarea>
    417 `,
    418  async function testTextRangeMove(browser, docAcc) {
    419    info("Constructing range on bold text run");
    420    await runPython(`
    421      global doc, docText, range
    422      doc = getDocUia()
    423      docText = getUiaPattern(doc, "Text")
    424      boldContainerAcc = findUiaByDomId(doc, "bold-container")
    425      range = docText.RangeFromChild(boldContainerAcc)
    426    `);
    427    is(await runPython(`range.GetText(-1)`), "a bcd ef", "range text correct");
    428    info("Moving to bold text run");
    429    is(
    430      await runPython(`range.Move(TextUnit_Format, 1)`),
    431      1,
    432      "Move return correct"
    433    );
    434    is(await runPython(`range.GetText(-1)`), "bcd", "range text correct");
    435 
    436    // Testing ExpandToEnclosingUnit (on formatting boundaries)
    437    info("Expanding to character (shrinking the range)");
    438    await runPython(`range.ExpandToEnclosingUnit(TextUnit_Character)`);
    439    is(await runPython(`range.GetText(-1)`), "b", "range text correct");
    440    info("Expanding to Format");
    441    await runPython(`range.ExpandToEnclosingUnit(TextUnit_Format)`);
    442    is(await runPython(`range.GetText(-1)`), "bcd", "range text correct");
    443 
    444    info("Making range larger than the Format unit");
    445    is(
    446      await runPython(
    447        `range.MoveEndpointByUnit(TextPatternRangeEndpoint_End, TextUnit_Character, 1)`
    448      ),
    449      1,
    450      "MoveEndpointByUnit return correct"
    451    );
    452    is(await runPython(`range.GetText(-1)`), "bcd ", "range text correct");
    453 
    454    info("Expanding to Format (shrinking the range)");
    455    await runPython(`range.ExpandToEnclosingUnit(TextUnit_Format)`);
    456    is(await runPython(`range.GetText(-1)`), "bcd", "range text correct");
    457 
    458    // Testing Move (on formatting boundaries)
    459    info("Moving 1 Format unit");
    460    is(
    461      await runPython(`range.Move(TextUnit_Format, 1)`),
    462      1,
    463      "Move return correct"
    464    );
    465    is(await runPython(`range.GetText(-1)`), " ef", "range text correct");
    466    info("Moving -3 Format units (but only -2 are left)");
    467    is(
    468      await runPython(`range.Move(TextUnit_Format, -3)`),
    469      -2,
    470      "Move return correct"
    471    );
    472    is(await runPython(`range.GetText(-1)`), "a ", "range text correct");
    473 
    474    // Testing MoveEndpointByUnit (on formatting boundaries)
    475    info("Moving end 1 Format unit");
    476    is(
    477      await runPython(
    478        `range.MoveEndpointByUnit(TextPatternRangeEndpoint_End, TextUnit_Format, 1)`
    479      ),
    480      1,
    481      "MoveEndpointByUnit return correct"
    482    );
    483    is(await runPython(`range.GetText(-1)`), "a bcd", "range text correct");
    484    info("Moving start 1 Format unit");
    485    is(
    486      await runPython(
    487        `range.MoveEndpointByUnit(TextPatternRangeEndpoint_Start, TextUnit_Format, 1)`
    488      ),
    489      1,
    490      "MoveEndpointByUnit return correct"
    491    );
    492    is(await runPython(`range.GetText(-1)`), "bcd", "range text correct");
    493 
    494    // Testing above three methods on text runs defined by container boundaries
    495    info("Constructing range on text run defined by container boundaries");
    496    await runPython(`
    497      global doc, docText, range
    498      containerContainer = findUiaByDomId(doc, "container-container")
    499      range = docText.RangeFromChild(containerContainer)
    500    `);
    501    is(await runPython(`range.GetText(-1)`), "a bcd ef", "range text correct");
    502    info("Expanding to Format");
    503    await runPython(`range.ExpandToEnclosingUnit(TextUnit_Format)`);
    504    is(await runPython(`range.GetText(-1)`), "a ", "range text correct");
    505    info("Moving 1 Format unit");
    506    is(
    507      await runPython(`range.Move(TextUnit_Format, 1)`),
    508      1,
    509      "Move return correct"
    510    );
    511    is(await runPython(`range.GetText(-1)`), "bcd", "range text correct");
    512    info("Moving start -1 Format unit");
    513    is(
    514      await runPython(
    515        `range.MoveEndpointByUnit(TextPatternRangeEndpoint_Start, TextUnit_Format, -1)`
    516      ),
    517      -1,
    518      "MoveEndpointByUnit return correct"
    519    );
    520    is(await runPython(`range.GetText(-1)`), "a bcd", "range text correct");
    521 
    522    // Trigger spelling errors so we can test text offset attributes
    523    const textarea = findAccessibleChildByID(docAcc, "textarea");
    524    textarea.takeFocus();
    525    await waitForEvent(EVENT_TEXT_ATTRIBUTE_CHANGED);
    526 
    527    // Testing above three methods on text offset attributes
    528    info("Constructing range on italic text run");
    529    await runPython(`
    530      global doc, docText, range
    531      textarea = findUiaByDomId(doc, "textarea")
    532      range = docText.RangeFromChild(textarea)
    533    `);
    534    is(
    535      await runPython(`range.GetText(-1)`),
    536      "test tset test",
    537      "range text correct"
    538    );
    539    info("Expanding to Format");
    540    await runPython(`range.ExpandToEnclosingUnit(TextUnit_Format)`);
    541    is(await runPython(`range.GetText(-1)`), "test ", "range text correct");
    542    info("Moving 1 Format unit");
    543    is(
    544      await runPython(`range.Move(TextUnit_Format, 1)`),
    545      1,
    546      "Move return correct"
    547    );
    548    is(await runPython(`range.GetText(-1)`), "tset", "range text correct");
    549    info("Moving start -1 Format unit");
    550    is(
    551      await runPython(
    552        `range.MoveEndpointByUnit(TextPatternRangeEndpoint_Start, TextUnit_Format, -1)`
    553      ),
    554      -1,
    555      "MoveEndpointByUnit return correct"
    556    );
    557    is(await runPython(`range.GetText(-1)`), "test tset", "range text correct");
    558  }
    559 );
    560 
    561 /**
    562 * Test the GetAttributeValue method. Verify the behavior of various UIA
    563 * Attribute IDs.
    564 */
    565 addUiaTask(
    566  `
    567 <div id="font-weight-container">a <span tabindex="0"><b>bcd</b></span><b> ef</b></div>
    568 <div id="font-size-container">a <span style="font-size:20px">bcd</span> ef</div>
    569 <div id="font-family-container">a <span style="font-family:Arial">bcd</span> ef</div>
    570 <div id="italic-container">a <span style="font-style:italic">bcd</span> ef</div>
    571 <div id="subscript-container">a <sub>bcd</sub> ef</div>
    572 <div id="superscript-container">a <sup>bcd</sup> ef</div>
    573 <div id="not-hidden-container">a bcd ef</div>
    574 <div id="readonly-container">a <span contenteditable="true">bcd</span> ef</div>
    575 <input id="text-input"/>
    576 <div id="spelling-error-container">a <span aria-invalid="spelling">bcd</span> ef</div>
    577 <div id="grammar-error-container">a <span aria-invalid="grammar">bcd</span> ef</div>
    578 <div id="data-validation-error-container">a <span aria-invalid="true">bcd</span> ef</div>
    579 <div id="highlight-container">a highlighted phrase ef</div>
    580 <div id="heading-container">ab<h3>h3</h3>cd</div>
    581 <div id="blockquote-container">ab<blockquote>quote</blockquote>cd</div>
    582 <div id="emphasis-container">ab<em>emph</em>cd</div>
    583 `,
    584  async function testTextRangeGetAttributeValue() {
    585    // ================== UIA_FontWeightAttributeId ==================
    586    info("Constructing range on bold text run");
    587    await runPython(`
    588      global doc, docText, range
    589      doc = getDocUia()
    590      docText = getUiaPattern(doc, "Text")
    591      fontWeightContainerAcc = findUiaByDomId(doc, "font-weight-container")
    592      range = docText.RangeFromChild(fontWeightContainerAcc)
    593    `);
    594    is(await runPython(`range.GetText(-1)`), "a bcd ef", "range text correct");
    595 
    596    info("checking mixed font weights");
    597    ok(
    598      await runPython(`
    599        val = range.GetAttributeValue(UIA_FontWeightAttributeId)
    600        return val == uiaClient.ReservedMixedAttributeValue
    601      `),
    602      "FontWeight correct (mixed)"
    603    );
    604 
    605    info("Moving to bold text run");
    606    is(
    607      await runPython(`range.Move(TextUnit_Format, 1)`),
    608      1,
    609      "Move return correct"
    610    );
    611    is(await runPython(`range.GetText(-1)`), "bcd", "range text correct");
    612 
    613    info("checking FontWeight");
    614    is(
    615      await runPython(`range.GetAttributeValue(UIA_FontWeightAttributeId)`),
    616      700,
    617      "FontWeight correct"
    618    );
    619 
    620    info("Moving end 1 Format unit");
    621    is(
    622      await runPython(
    623        `range.MoveEndpointByUnit(TextPatternRangeEndpoint_End, TextUnit_Format, 1)`
    624      ),
    625      1,
    626      "MoveEndpointByUnit return correct"
    627    );
    628    is(await runPython(`range.GetText(-1)`), "bcd ef", "range text correct");
    629    info(
    630      "checking font weight (across equivalent container-separated Format runs)"
    631    );
    632    is(
    633      await runPython(`range.GetAttributeValue(UIA_FontWeightAttributeId)`),
    634      700,
    635      "FontWeight correct"
    636    );
    637 
    638    // ================== UIA_FontSizeAttributeId ==================
    639    await runPython(`
    640      global range
    641      fontSizeContainerAcc = findUiaByDomId(doc, "font-size-container")
    642      range = docText.RangeFromChild(fontSizeContainerAcc)
    643    `);
    644    is(await runPython(`range.GetText(-1)`), "a bcd ef", "range text correct");
    645    info("checking mixed font weights");
    646    ok(
    647      await runPython(`
    648        val = range.GetAttributeValue(UIA_FontSizeAttributeId)
    649        return val == uiaClient.ReservedMixedAttributeValue
    650      `),
    651      "FontSize correct (mixed)"
    652    );
    653    info("Moving to increased font-size text run");
    654    is(
    655      await runPython(`range.Move(TextUnit_Format, 1)`),
    656      1,
    657      "Move return correct"
    658    );
    659    is(await runPython(`range.GetText(-1)`), "bcd", "range text correct");
    660    info("checking FontSize");
    661    is(
    662      await runPython(`range.GetAttributeValue(UIA_FontSizeAttributeId)`),
    663      15,
    664      "FontSize correct"
    665    );
    666 
    667    // ================== UIA_FontNameAttributeId ==================
    668    await runPython(`
    669      global range
    670      fontFamilyContainerAcc = findUiaByDomId(doc, "font-family-container")
    671      range = docText.RangeFromChild(fontFamilyContainerAcc)
    672    `);
    673    is(await runPython(`range.GetText(-1)`), "a bcd ef", "range text correct");
    674    info("checking mixed font families");
    675    ok(
    676      await runPython(`
    677        val = range.GetAttributeValue(UIA_FontNameAttributeId)
    678        return val == uiaClient.ReservedMixedAttributeValue
    679      `),
    680      "FontName correct (mixed)"
    681    );
    682    info("Moving to sans-serif font-family text run");
    683    is(
    684      await runPython(`range.Move(TextUnit_Format, 1)`),
    685      1,
    686      "Move return correct"
    687    );
    688    is(await runPython(`range.GetText(-1)`), "bcd", "range text correct");
    689    info("checking FontName");
    690    is(
    691      await runPython(`range.GetAttributeValue(UIA_FontNameAttributeId)`),
    692      "Arial",
    693      "FontName correct"
    694    );
    695 
    696    // ================== UIA_IsItalicAttributeId ==================
    697    await runPython(`
    698      global range
    699      italicContainerAcc = findUiaByDomId(doc, "italic-container")
    700      range = docText.RangeFromChild(italicContainerAcc)
    701    `);
    702    is(await runPython(`range.GetText(-1)`), "a bcd ef", "range text correct");
    703    info("checking mixed IsItalic properties");
    704    ok(
    705      await runPython(`
    706        val = range.GetAttributeValue(UIA_IsItalicAttributeId)
    707        return val == uiaClient.ReservedMixedAttributeValue
    708      `),
    709      "IsItalic correct (mixed)"
    710    );
    711    info("Moving to italic text run");
    712    is(
    713      await runPython(`range.Move(TextUnit_Format, 1)`),
    714      1,
    715      "Move return correct"
    716    );
    717    is(await runPython(`range.GetText(-1)`), "bcd", "range text correct");
    718    info("checking IsItalic");
    719    is(
    720      await runPython(`range.GetAttributeValue(UIA_IsItalicAttributeId)`),
    721      true,
    722      "IsItalic correct"
    723    );
    724 
    725    // ================== UIA_IsSubscriptAttributeId ==================
    726    await runPython(`
    727      global range
    728      subscriptContainerAcc = findUiaByDomId(doc, "subscript-container")
    729      range = docText.RangeFromChild(subscriptContainerAcc)
    730    `);
    731    is(await runPython(`range.GetText(-1)`), "a bcd ef", "range text correct");
    732    info("checking mixed IsSubscript properties");
    733    ok(
    734      await runPython(`
    735        val = range.GetAttributeValue(UIA_IsSubscriptAttributeId)
    736        return val == uiaClient.ReservedMixedAttributeValue
    737      `),
    738      "IsSubscript correct (mixed)"
    739    );
    740    info("Moving to subscript text run");
    741    is(
    742      await runPython(`range.Move(TextUnit_Format, 1)`),
    743      1,
    744      "Move return correct"
    745    );
    746    is(await runPython(`range.GetText(-1)`), "bcd", "range text correct");
    747    info("checking IsSubscript");
    748    is(
    749      await runPython(`range.GetAttributeValue(UIA_IsSubscriptAttributeId)`),
    750      true,
    751      "IsSubscript correct"
    752    );
    753 
    754    // ================== UIA_IsSuperscriptAttributeId ==================
    755    await runPython(`
    756      global range
    757      superscriptContainerAcc = findUiaByDomId(doc, "superscript-container")
    758      range = docText.RangeFromChild(superscriptContainerAcc)
    759    `);
    760    is(await runPython(`range.GetText(-1)`), "a bcd ef", "range text correct");
    761    info("checking mixed IsSuperscript properties");
    762    ok(
    763      await runPython(`
    764        val = range.GetAttributeValue(UIA_IsSuperscriptAttributeId)
    765        return val == uiaClient.ReservedMixedAttributeValue
    766      `),
    767      "IsSuperscript correct (mixed)"
    768    );
    769    info("Moving to superscript text run");
    770    is(
    771      await runPython(`range.Move(TextUnit_Format, 1)`),
    772      1,
    773      "Move return correct"
    774    );
    775    is(await runPython(`range.GetText(-1)`), "bcd", "range text correct");
    776    info("checking IsSuperscript");
    777    is(
    778      await runPython(`range.GetAttributeValue(UIA_IsSuperscriptAttributeId)`),
    779      true,
    780      "IsSuperscript correct"
    781    );
    782 
    783    // ================== UIA_IsHiddenAttributeId ==================
    784    // Testing the "true" case is not really possible since these Accessible
    785    // nodes are not present in the tree. Verify the "false" case.
    786    await runPython(`
    787      global range
    788      notHiddenContainerAcc = findUiaByDomId(doc, "not-hidden-container")
    789      range = docText.RangeFromChild(notHiddenContainerAcc)
    790    `);
    791    is(await runPython(`range.GetText(-1)`), "a bcd ef", "range text correct");
    792    info("checking mixed IsHidden properties");
    793    ok(
    794      await runPython(`
    795        val = range.GetAttributeValue(UIA_IsHiddenAttributeId)
    796        return val != uiaClient.ReservedMixedAttributeValue
    797      `),
    798      "IsHidden correct (not mixed)"
    799    );
    800 
    801    // ================== UIA_IsReadOnlyAttributeId ==================
    802    await runPython(`
    803      global range
    804      readonlyContainerAcc = findUiaByDomId(doc, "readonly-container")
    805      range = docText.RangeFromChild(readonlyContainerAcc)
    806    `);
    807    is(await runPython(`range.GetText(-1)`), "a bcd ef", "range text correct");
    808    info("checking mixed ReadOnly properties");
    809    ok(
    810      await runPython(`
    811        val = range.GetAttributeValue(UIA_IsReadOnlyAttributeId)
    812        return val == uiaClient.ReservedMixedAttributeValue
    813      `),
    814      "ReadOnly correct (mixed)"
    815    );
    816    info("Moving to editable text run");
    817    is(
    818      await runPython(`range.Move(TextUnit_Format, 1)`),
    819      1,
    820      "Move return correct"
    821    );
    822    is(await runPython(`range.GetText(-1)`), "bcd", "range text correct");
    823    info("checking IsReadOnly");
    824    is(
    825      await runPython(`range.GetAttributeValue(UIA_IsReadOnlyAttributeId)`),
    826      false,
    827      "IsReadOnly correct"
    828    );
    829 
    830    // Verify that text inputs are not read-only by default.
    831    await runPython(`
    832      global range
    833      textInputAcc = findUiaByDomId(doc, "text-input")
    834      range = docText.RangeFromChild(textInputAcc)
    835    `);
    836    info("checking IsReadOnly");
    837    is(
    838      await runPython(`range.GetAttributeValue(UIA_IsReadOnlyAttributeId)`),
    839      false,
    840      "IsReadOnly correct for text input"
    841    );
    842 
    843    // ================== UIA_AnnotationTypesAttributeId - AnnotationType_SpellingError ==================
    844    await runPython(`
    845      global range
    846      spellingErrorContainerAcc = findUiaByDomId(doc, "spelling-error-container")
    847      range = docText.RangeFromChild(spellingErrorContainerAcc)
    848    `);
    849    is(await runPython(`range.GetText(-1)`), "a bcd ef", "range text correct");
    850    info("checking mixed SpellingError properties");
    851    ok(
    852      await runPython(`
    853        val = range.GetAttributeValue(UIA_AnnotationTypesAttributeId)
    854        return val == uiaClient.ReservedMixedAttributeValue
    855      `),
    856      "SpellingError correct (mixed)"
    857    );
    858    info('Moving to aria-invalid="spelling" text run');
    859    is(
    860      await runPython(`range.Move(TextUnit_Format, 1)`),
    861      1,
    862      "Move return correct"
    863    );
    864    is(await runPython(`range.GetText(-1)`), "bcd", "range text correct");
    865    info("checking SpellingError");
    866    ok(
    867      await runPython(`
    868        annotations = range.GetAttributeValue(UIA_AnnotationTypesAttributeId)
    869        return annotations == (AnnotationType_SpellingError,)
    870      `),
    871      "SpellingError correct"
    872    );
    873 
    874    // ================== UIA_AnnotationTypesAttributeId - AnnotationType_GrammarError ==================
    875    await runPython(`
    876      global range
    877      grammarErrorContainerAcc = findUiaByDomId(doc, "grammar-error-container")
    878      range = docText.RangeFromChild(grammarErrorContainerAcc)
    879    `);
    880    is(await runPython(`range.GetText(-1)`), "a bcd ef", "range text correct");
    881    info("checking mixed GrammarError properties");
    882    ok(
    883      await runPython(`
    884        val = range.GetAttributeValue(UIA_AnnotationTypesAttributeId)
    885        return val == uiaClient.ReservedMixedAttributeValue
    886      `),
    887      "GrammarError correct (mixed)"
    888    );
    889    info('Moving to aria-invalid="grammar" text run');
    890    is(
    891      await runPython(`range.Move(TextUnit_Format, 1)`),
    892      1,
    893      "Move return correct"
    894    );
    895    is(await runPython(`range.GetText(-1)`), "bcd", "range text correct");
    896    info("checking GrammarError");
    897    ok(
    898      await runPython(`
    899        annotations = range.GetAttributeValue(UIA_AnnotationTypesAttributeId)
    900        return annotations == (AnnotationType_GrammarError,)
    901      `),
    902      "GrammarError correct"
    903    );
    904 
    905    // ================== UIA_AnnotationTypesAttributeId - AnnotationType_DataValidationError ==================
    906    // The IA2 -> UIA bridge does not work for aria-invalid=true or highlights.
    907    if (gIsUiaEnabled) {
    908      await runPython(`
    909      global range
    910      dataValidationErrorContainerAcc = findUiaByDomId(doc, "data-validation-error-container")
    911      range = docText.RangeFromChild(dataValidationErrorContainerAcc)
    912    `);
    913      is(
    914        await runPython(`range.GetText(-1)`),
    915        "a bcd ef",
    916        "range text correct"
    917      );
    918      info("checking mixed DataValidationError properties");
    919      ok(
    920        await runPython(`
    921        val = range.GetAttributeValue(UIA_AnnotationTypesAttributeId)
    922        return val == uiaClient.ReservedMixedAttributeValue
    923      `),
    924        "DataValidationError correct (mixed)"
    925      );
    926      info('Moving to aria-invalid="true" text run');
    927      is(
    928        await runPython(`range.Move(TextUnit_Format, 1)`),
    929        1,
    930        "Move return correct"
    931      );
    932      is(await runPython(`range.GetText(-1)`), "bcd", "range text correct");
    933      info("checking DataValidationError");
    934      ok(
    935        await runPython(`
    936        annotations = range.GetAttributeValue(UIA_AnnotationTypesAttributeId)
    937        return annotations == (AnnotationType_DataValidationError,)
    938      `),
    939        "DataValidationError correct"
    940      );
    941 
    942      // ================== UIA_AnnotationTypesAttributeId - AnnotationType_Highlighted ==================
    943      await runPython(`
    944      global range
    945      highlightContainerAcc = findUiaByDomId(doc, "highlight-container")
    946      range = docText.RangeFromChild(highlightContainerAcc)
    947    `);
    948      is(
    949        await runPython(`range.GetText(-1)`),
    950        "a highlighted phrase ef",
    951        "range text correct"
    952      );
    953      info("checking mixed Highlighted properties");
    954      ok(
    955        await runPython(`
    956        val = range.GetAttributeValue(UIA_AnnotationTypesAttributeId)
    957        return val == uiaClient.ReservedMixedAttributeValue
    958      `),
    959        "Highlighted correct (mixed)"
    960      );
    961      info("Moving to highlighted text run");
    962      is(
    963        await runPython(`range.Move(TextUnit_Format, 1)`),
    964        1,
    965        "Move return correct"
    966      );
    967      is(
    968        await runPython(`range.GetText(-1)`),
    969        "highlighted phrase",
    970        "range text correct"
    971      );
    972      info("checking Highlighted");
    973      ok(
    974        await runPython(`
    975        annotations = range.GetAttributeValue(UIA_AnnotationTypesAttributeId)
    976        return annotations == (AnnotationType_Highlighted,)
    977      `),
    978        "Highlighted correct"
    979      );
    980    }
    981 
    982    // The IA2 -> UIA bridge does not work correctly here.
    983    if (gIsUiaEnabled) {
    984      // ================== UIA_StyleIdAttributeId - StyleId_Heading* ==================
    985      await runPython(`
    986        global range
    987        headingContainerAcc = findUiaByDomId(doc, "heading-container")
    988        range = docText.RangeFromChild(headingContainerAcc)
    989      `);
    990      is(await runPython(`range.GetText(-1)`), "abh3cd", "range text correct");
    991      info("checking mixed StyleId properties");
    992      ok(
    993        await runPython(`
    994          val = range.GetAttributeValue(UIA_StyleIdAttributeId)
    995          return val == uiaClient.ReservedMixedAttributeValue
    996        `),
    997        "StyleId correct (mixed)"
    998      );
    999      info("Moving to h3 text run");
   1000      is(
   1001        await runPython(`range.Move(TextUnit_Format, 1)`),
   1002        1,
   1003        "Move return correct"
   1004      );
   1005      is(await runPython(`range.GetText(-1)`), "h3", "range text correct");
   1006      info("checking StyleId");
   1007      ok(
   1008        await runPython(`
   1009          styleId = range.GetAttributeValue(UIA_StyleIdAttributeId)
   1010          return styleId == StyleId_Heading3
   1011        `),
   1012        "StyleId correct"
   1013      );
   1014 
   1015      // ================== UIA_StyleIdAttributeId - StyleId_Quote ==================
   1016      await runPython(`
   1017        global range
   1018        blockquoteContainerAcc = findUiaByDomId(doc, "blockquote-container")
   1019        range = docText.RangeFromChild(blockquoteContainerAcc)
   1020      `);
   1021      is(
   1022        await runPython(`range.GetText(-1)`),
   1023        "abquotecd",
   1024        "range text correct"
   1025      );
   1026      info("checking mixed StyleId properties");
   1027      ok(
   1028        await runPython(`
   1029          val = range.GetAttributeValue(UIA_StyleIdAttributeId)
   1030          return val == uiaClient.ReservedMixedAttributeValue
   1031        `),
   1032        "StyleId correct (mixed)"
   1033      );
   1034      info("Moving to blockquote text run");
   1035      is(
   1036        await runPython(`range.Move(TextUnit_Format, 1)`),
   1037        1,
   1038        "Move return correct"
   1039      );
   1040      is(await runPython(`range.GetText(-1)`), "quote", "range text correct");
   1041      info("checking StyleId");
   1042      ok(
   1043        await runPython(`
   1044          styleId = range.GetAttributeValue(UIA_StyleIdAttributeId)
   1045          return styleId == StyleId_Quote
   1046        `),
   1047        "StyleId correct"
   1048      );
   1049 
   1050      // ================== UIA_StyleIdAttributeId - StyleId_Emphasis ==================
   1051      await runPython(`
   1052        global range
   1053        emphasisContainerAcc = findUiaByDomId(doc, "emphasis-container")
   1054        range = docText.RangeFromChild(emphasisContainerAcc)
   1055      `);
   1056      is(
   1057        await runPython(`range.GetText(-1)`),
   1058        "abemphcd",
   1059        "range text correct"
   1060      );
   1061      info("checking mixed StyleId properties");
   1062      ok(
   1063        await runPython(`
   1064          val = range.GetAttributeValue(UIA_StyleIdAttributeId)
   1065          return val == uiaClient.ReservedMixedAttributeValue
   1066        `),
   1067        "StyleId correct (mixed)"
   1068      );
   1069      info("Moving to emphasized text run");
   1070      is(
   1071        await runPython(`range.Move(TextUnit_Format, 1)`),
   1072        1,
   1073        "Move return correct"
   1074      );
   1075      is(await runPython(`range.GetText(-1)`), "emph", "range text correct");
   1076      info("checking StyleId");
   1077      ok(
   1078        await runPython(`
   1079          styleId = range.GetAttributeValue(UIA_StyleIdAttributeId)
   1080          return styleId == StyleId_Emphasis
   1081        `),
   1082        "StyleId correct"
   1083      );
   1084    }
   1085  },
   1086  { urlSuffix: "#:~:text=highlighted%20phrase" }
   1087 );
   1088 
   1089 /**
   1090 * Test the GetAttributeValue method when backspacing a character at the end of
   1091 * a document.
   1092 */
   1093 addUiaTask(
   1094  `a<input id="input" value="bc">`,
   1095  async function testTextRangeGetAttributeValueBackspaceAtDocEnd(
   1096    browser,
   1097    docAcc
   1098  ) {
   1099    const input = findAccessibleChildByID(docAcc, "input");
   1100    info("Focusing input");
   1101    let moved = waitForEvent(EVENT_TEXT_CARET_MOVED, input);
   1102    input.takeFocus();
   1103    await moved;
   1104    info("Pressing end");
   1105    moved = waitForEvent(EVENT_TEXT_CARET_MOVED, input);
   1106    EventUtils.synthesizeKey("KEY_End");
   1107    await moved;
   1108    await runPython(`
   1109      global doc, range
   1110      doc = getDocUia()
   1111      input = findUiaByDomId(doc, "input")
   1112      text = getUiaPattern(input, "Text")
   1113      range = text.GetSelection().GetElement(0)
   1114    `);
   1115    // `range` is collapsed at the end of the input, after "c".
   1116    is(
   1117      await runPython(`range.GetAttributeValue(UIA_IsReadOnlyAttributeId)`),
   1118      false,
   1119      "IsReadOnly correct"
   1120    );
   1121    info("Backspacing c");
   1122    moved = waitForEvent(EVENT_TEXT_CARET_MOVED, input);
   1123    EventUtils.synthesizeKey("KEY_Backspace");
   1124    await moved;
   1125    is(
   1126      await runPython(`range.GetAttributeValue(UIA_IsReadOnlyAttributeId)`),
   1127      false,
   1128      "IsReadOnly correct"
   1129    );
   1130  }
   1131 );
   1132 
   1133 /**
   1134 * Test the TextRange pattern's Move method.
   1135 */
   1136 addUiaTask(
   1137  `
   1138 <p>ab</p>
   1139 <textarea id="textarea">cd ef gh</textarea>
   1140 <p>ij</p>
   1141  `,
   1142  async function testTextRangeMove() {
   1143    await runPython(`
   1144      doc = getDocUia()
   1145      textarea = findUiaByDomId(doc, "textarea")
   1146      text = getUiaPattern(textarea, "Text")
   1147      global range
   1148      range = text.DocumentRange
   1149    `);
   1150    is(await runPython(`range.GetText(-1)`), "cd ef gh", "range text correct");
   1151    info("Moving 1 word");
   1152    is(
   1153      await runPython(`range.Move(TextUnit_Word, 1)`),
   1154      1,
   1155      "Move return correct"
   1156    );
   1157    is(await runPython(`range.GetText(-1)`), "ef ", "range text correct");
   1158    info("Moving 3 words");
   1159    // There are only 2 words after.
   1160    is(
   1161      await runPython(`range.Move(TextUnit_Word, 3)`),
   1162      2,
   1163      "Move return correct"
   1164    );
   1165    // The IA2 -> UIA proxy gets most things below this wrong.
   1166    if (!gIsUiaEnabled) {
   1167      return;
   1168    }
   1169    is(await runPython(`range.GetText(-1)`), "ij", "range text correct");
   1170    info("Moving -5 words");
   1171    // There are only 4 words before.
   1172    is(
   1173      await runPython(`range.Move(TextUnit_Word, -5)`),
   1174      -4,
   1175      "Move return correct"
   1176    );
   1177    is(await runPython(`range.GetText(-1)`), "ab", "range text correct");
   1178    info("Moving 1 word");
   1179    is(
   1180      await runPython(`range.Move(TextUnit_Word, 1)`),
   1181      1,
   1182      "Move return correct"
   1183    );
   1184    is(await runPython(`range.GetText(-1)`), "cd ", "range text correct");
   1185    info("Moving 1 character");
   1186    is(
   1187      await runPython(`range.Move(TextUnit_Character, 1)`),
   1188      1,
   1189      "Move return correct"
   1190    );
   1191    is(await runPython(`range.GetText(-1)`), "d", "range text correct");
   1192    // When the range is not collapsed, Move moves backward to the start of the
   1193    // unit before moving to the requested unit.
   1194    info("Moving -1 word");
   1195    is(
   1196      await runPython(`range.Move(TextUnit_Word, -1)`),
   1197      -1,
   1198      "Move return correct"
   1199    );
   1200    is(await runPython(`range.GetText(-1)`), "ab", "range text correct");
   1201    info("Collapsing to start");
   1202    await runPython(
   1203      `range.MoveEndpointByRange(TextPatternRangeEndpoint_End, range, TextPatternRangeEndpoint_Start)`
   1204    );
   1205    is(await runPython(`range.GetText(-1)`), "", "range text correct");
   1206    // range is now collapsed at "a".
   1207    info("Moving 1 word");
   1208    is(
   1209      await runPython(`range.Move(TextUnit_Word, 1)`),
   1210      1,
   1211      "Move return correct"
   1212    );
   1213    // range is now collapsed at "c".
   1214    is(await runPython(`range.GetText(-1)`), "", "range text correct");
   1215    info("Expanding to character");
   1216    await runPython(`range.ExpandToEnclosingUnit(TextUnit_Character)`);
   1217    is(await runPython(`range.GetText(-1)`), "c", "range text correct");
   1218    info("Collapsing to end");
   1219    await runPython(
   1220      `range.MoveEndpointByRange(TextPatternRangeEndpoint_Start, range, TextPatternRangeEndpoint_End)`
   1221    );
   1222    // range is now collapsed at "d".
   1223    // When the range is collapsed, Move does *not* first move back to the start
   1224    // of the unit.
   1225    info("Moving -1 word");
   1226    is(
   1227      await runPython(`range.Move(TextUnit_Word, -1)`),
   1228      -1,
   1229      "Move return correct"
   1230    );
   1231    // range is collapsed at "c".
   1232    is(await runPython(`range.GetText(-1)`), "", "range text correct");
   1233    await runPython(`range.ExpandToEnclosingUnit(TextUnit_Word)`);
   1234    is(await runPython(`range.GetText(-1)`), "cd ", "range text correct");
   1235  }
   1236 );
   1237 
   1238 /**
   1239 * Test the TextRange pattern's MoveEndpointByRange method.
   1240 */
   1241 addUiaTask(
   1242  `
   1243 <p>ab</p>
   1244 <div><textarea id="textarea">cd ef gh</textarea></div>
   1245 <p>ij</p>
   1246  `,
   1247  async function testTextRangeMoveEndpointByRange() {
   1248    await runPython(`
   1249      global doc, taRange, range
   1250      doc = getDocUia()
   1251      textarea = findUiaByDomId(doc, "textarea")
   1252      text = getUiaPattern(textarea, "Text")
   1253      taRange = text.DocumentRange
   1254      range = text.DocumentRange
   1255    `);
   1256    is(await runPython(`range.GetText(-1)`), "cd ef gh", "range text correct");
   1257    info("Expanding to character");
   1258    await runPython(`range.ExpandToEnclosingUnit(TextUnit_Character)`);
   1259    is(await runPython(`range.GetText(-1)`), "c", "range text correct");
   1260    is(
   1261      await runPython(
   1262        `range.CompareEndpoints(TextPatternRangeEndpoint_Start, range, TextPatternRangeEndpoint_End)`
   1263      ),
   1264      -1,
   1265      "start < end"
   1266    );
   1267    info("Moving end to start");
   1268    await runPython(
   1269      `range.MoveEndpointByRange(TextPatternRangeEndpoint_End, range, TextPatternRangeEndpoint_Start)`
   1270    );
   1271    is(
   1272      await runPython(
   1273        `range.CompareEndpoints(TextPatternRangeEndpoint_Start, range, TextPatternRangeEndpoint_End)`
   1274      ),
   1275      0,
   1276      "start == end"
   1277    );
   1278    info("Moving range end to textarea end");
   1279    await runPython(
   1280      `range.MoveEndpointByRange(TextPatternRangeEndpoint_End, taRange, TextPatternRangeEndpoint_End)`
   1281    );
   1282    is(await runPython(`range.GetText(-1)`), "cd ef gh", "range text correct");
   1283    info("Expanding to character");
   1284    await runPython(`range.ExpandToEnclosingUnit(TextUnit_Character)`);
   1285    is(await runPython(`range.GetText(-1)`), "c", "range text correct");
   1286    info("Moving range start to textarea end");
   1287    await runPython(
   1288      `range.MoveEndpointByRange(TextPatternRangeEndpoint_Start, taRange, TextPatternRangeEndpoint_End)`
   1289    );
   1290    is(
   1291      await runPython(
   1292        `range.CompareEndpoints(TextPatternRangeEndpoint_Start, taRange, TextPatternRangeEndpoint_End)`
   1293      ),
   1294      0,
   1295      "range start == textarea end"
   1296    );
   1297    is(
   1298      await runPython(
   1299        `range.CompareEndpoints(TextPatternRangeEndpoint_End, taRange, TextPatternRangeEndpoint_End)`
   1300      ),
   1301      0,
   1302      "range end == textarea end"
   1303    );
   1304    info("Moving range end to textarea start");
   1305    await runPython(
   1306      `range.MoveEndpointByRange(TextPatternRangeEndpoint_End, taRange, TextPatternRangeEndpoint_Start)`
   1307    );
   1308    is(
   1309      await runPython(
   1310        `range.CompareEndpoints(TextPatternRangeEndpoint_Start, taRange, TextPatternRangeEndpoint_Start)`
   1311      ),
   1312      0,
   1313      "range start == textarea start"
   1314    );
   1315    is(
   1316      await runPython(
   1317        `range.CompareEndpoints(TextPatternRangeEndpoint_End, taRange, TextPatternRangeEndpoint_Start)`
   1318      ),
   1319      0,
   1320      "range end == textarea start"
   1321    );
   1322    await definePyVar("docRange", `getUiaPattern(doc, "Text").DocumentRange`);
   1323    info("Moving range start to document start");
   1324    await runPython(
   1325      `range.MoveEndpointByRange(TextPatternRangeEndpoint_Start, docRange, TextPatternRangeEndpoint_Start)`
   1326    );
   1327    info("Moving range end to document end");
   1328    await runPython(
   1329      `range.MoveEndpointByRange(TextPatternRangeEndpoint_End, docRange, TextPatternRangeEndpoint_End)`
   1330    );
   1331    is(
   1332      await runPython(
   1333        `range.CompareEndpoints(TextPatternRangeEndpoint_Start, docRange, TextPatternRangeEndpoint_Start)`
   1334      ),
   1335      0,
   1336      "range start == document start"
   1337    );
   1338    is(
   1339      await runPython(
   1340        `range.CompareEndpoints(TextPatternRangeEndpoint_End, docRange, TextPatternRangeEndpoint_End)`
   1341      ),
   1342      0,
   1343      "range end == document end"
   1344    );
   1345  }
   1346 );
   1347 
   1348 /**
   1349 * Test the TextRange pattern's MoveEndpointByUnit method.
   1350 */
   1351 addUiaTask(
   1352  `
   1353 <p>ab</p>
   1354 <textarea id="textarea">cd ef gh</textarea>
   1355 <p>ij</p>
   1356  `,
   1357  async function testTextRangeMoveEndpointByUnit() {
   1358    await runPython(`
   1359      doc = getDocUia()
   1360      textarea = findUiaByDomId(doc, "textarea")
   1361      text = getUiaPattern(textarea, "Text")
   1362      global range
   1363      range = text.DocumentRange
   1364    `);
   1365    is(await runPython(`range.GetText(-1)`), "cd ef gh", "range text correct");
   1366    info("Moving end -1 word");
   1367    is(
   1368      await runPython(
   1369        `range.MoveEndpointByUnit(TextPatternRangeEndpoint_End, TextUnit_Word, -1)`
   1370      ),
   1371      -1,
   1372      "MoveEndpointByUnit return correct"
   1373    );
   1374    is(await runPython(`range.GetText(-1)`), "cd ef ", "range text correct");
   1375    info("Moving end -4 words");
   1376    // There are only 3 words before.
   1377    is(
   1378      await runPython(
   1379        `range.MoveEndpointByUnit(TextPatternRangeEndpoint_End, TextUnit_Word, -4)`
   1380      ),
   1381      -3,
   1382      "MoveEndpointByUnit return correct"
   1383    );
   1384    is(await runPython(`range.GetText(-1)`), "", "range text correct");
   1385    info("Moving start 1 word");
   1386    is(
   1387      await runPython(
   1388        `range.MoveEndpointByUnit(TextPatternRangeEndpoint_Start, TextUnit_Word, 1)`
   1389      ),
   1390      1,
   1391      "MoveEndpointByUnit return correct"
   1392    );
   1393    is(await runPython(`range.GetText(-1)`), "", "range text correct");
   1394    info("Moving end 1 character");
   1395    is(
   1396      await runPython(
   1397        `range.MoveEndpointByUnit(TextPatternRangeEndpoint_End, TextUnit_Character, 1)`
   1398      ),
   1399      1,
   1400      "MoveEndpointByUnit return correct"
   1401    );
   1402    is(await runPython(`range.GetText(-1)`), "c", "range text correct");
   1403    info("Moving start 5 words");
   1404    // There are only 4 word boundaries after.
   1405    is(
   1406      await runPython(
   1407        `range.MoveEndpointByUnit(TextPatternRangeEndpoint_Start, TextUnit_Word, 5)`
   1408      ),
   1409      4,
   1410      "MoveEndpointByUnit return correct"
   1411    );
   1412    is(await runPython(`range.GetText(-1)`), "", "range text correct");
   1413    info("Moving end -1 word");
   1414    is(
   1415      await runPython(
   1416        `range.MoveEndpointByUnit(TextPatternRangeEndpoint_End, TextUnit_Word, -1)`
   1417      ),
   1418      -1,
   1419      "MoveEndpointByUnit return correct"
   1420    );
   1421    is(await runPython(`range.GetText(-1)`), "", "range text correct");
   1422    info("Moving end 1 character");
   1423    is(
   1424      await runPython(
   1425        `range.MoveEndpointByUnit(TextPatternRangeEndpoint_End, TextUnit_Character, 1)`
   1426      ),
   1427      1,
   1428      "MoveEndpointByUnit return correct"
   1429    );
   1430    is(await runPython(`range.GetText(-1)`), "i", "range text correct");
   1431  }
   1432 );
   1433 
   1434 /**
   1435 * Test the Text pattern's SupportedTextSelection property.
   1436 */
   1437 addUiaTask(
   1438  `
   1439 <style>
   1440 body {
   1441  user-select: none;
   1442 }
   1443 </style>
   1444 <input id="input">
   1445 <p id="p">p</p>
   1446  `,
   1447  async function testTextSupportedTextSelection() {
   1448    let result = await runPython(`
   1449      global doc
   1450      doc = getDocUia()
   1451      input = findUiaByDomId(doc, "input")
   1452      text = getUiaPattern(input, "Text")
   1453      return text.SupportedTextSelection
   1454    `);
   1455    is(
   1456      result,
   1457      SupportedTextSelection_Multiple,
   1458      "input SupportedTextSelection correct"
   1459    );
   1460    if (gIsUiaEnabled) {
   1461      // The IA2 -> UIA proxy doesn't expose the Text pattern on this text leaf.
   1462      is(
   1463        await runPython(`
   1464          p = findUiaByDomId(doc, "p")
   1465          pLeaf = uiaClient.RawViewWalker.GetFirstChildElement(p)
   1466          text = getUiaPattern(pLeaf, "Text")
   1467          return text.SupportedTextSelection
   1468        `),
   1469        SupportedTextSelection_None,
   1470        "pLeaf SupportedTextSelection correct"
   1471      );
   1472      // The IA2 -> UIA proxy doesn't understand that text isn't selectable in
   1473      // this document.
   1474      is(
   1475        await runPython(`getUiaPattern(doc, "Text").SupportedTextSelection`),
   1476        SupportedTextSelection_None,
   1477        "doc SupportedTextSelection correct"
   1478      );
   1479    }
   1480  }
   1481 );
   1482 
   1483 /**
   1484 * Test the Text pattern's SupportedTextSelection property on a document with a
   1485 * selectable body.
   1486 */
   1487 addUiaTask(
   1488  `<p id="p">p</p>`,
   1489  async function testTextSupportedTextSelectionSelectableBody() {
   1490    is(
   1491      await runPython(`
   1492        global doc
   1493        doc = getDocUia()
   1494        text = getUiaPattern(doc, "Text")
   1495        return text.SupportedTextSelection
   1496      `),
   1497      SupportedTextSelection_Multiple,
   1498      "doc SupportedTextSelection correct"
   1499    );
   1500    // The IA2 -> UIA proxy doesn't expose the Text pattern on this text leaf.
   1501    if (gIsUiaEnabled) {
   1502      is(
   1503        await runPython(`
   1504          p = findUiaByDomId(doc, "p")
   1505          pLeaf = uiaClient.RawViewWalker.GetFirstChildElement(p)
   1506          text = getUiaPattern(pLeaf, "Text")
   1507          return text.SupportedTextSelection
   1508        `),
   1509        SupportedTextSelection_Multiple,
   1510        "pLeaf SupportedTextSelection correct"
   1511      );
   1512    }
   1513  }
   1514 );
   1515 
   1516 /**
   1517 * Test the Text pattern's GetSelection method with the caret.
   1518 */
   1519 addUiaTask(
   1520  `<textarea id="textarea" cols="2">ab cd</textarea>`,
   1521  async function testTextGetSelectionCaret(browser, docAcc) {
   1522    await runPython(`
   1523      doc = getDocUia()
   1524      textarea = findUiaByDomId(doc, "textarea")
   1525      global text
   1526      text = getUiaPattern(textarea, "Text")
   1527    `);
   1528    is(await runPython(`text.GetSelection().Length`), 0, "No selection");
   1529    info("Focusing textarea");
   1530    const textarea = findAccessibleChildByID(docAcc, "textarea", [
   1531      nsIAccessibleText,
   1532    ]);
   1533    let moved = waitForEvent(EVENT_TEXT_CARET_MOVED, textarea);
   1534    textarea.takeFocus();
   1535    await moved;
   1536    is(await runPython(`text.GetSelection().Length`), 1, "1 selection");
   1537    await definePyVar("range", `text.GetSelection().GetElement(0)`);
   1538    ok(await runPython(`bool(range)`), "Got selection range 0");
   1539    info("Expanding to character");
   1540    await runPython(`range.ExpandToEnclosingUnit(TextUnit_Character)`);
   1541    is(await runPython(`range.GetText(-1)`), "a", "range text correct");
   1542 
   1543    info("Pressing ArrowRight");
   1544    moved = waitForEvent(EVENT_TEXT_CARET_MOVED, textarea);
   1545    EventUtils.synthesizeKey("KEY_ArrowRight");
   1546    await moved;
   1547    is(await runPython(`text.GetSelection().Length`), 1, "1 selection");
   1548    await definePyVar("range", `text.GetSelection().GetElement(0)`);
   1549    ok(await runPython(`bool(range)`), "Got selection range 0");
   1550    info("Expanding to character");
   1551    await runPython(`range.ExpandToEnclosingUnit(TextUnit_Character)`);
   1552    is(await runPython(`range.GetText(-1)`), "b", "range text correct");
   1553 
   1554    // The IA2 -> UIA proxy doesn't handle the insertion point at the end of a
   1555    // line correctly.
   1556    if (!gIsUiaEnabled) {
   1557      return;
   1558    }
   1559 
   1560    // Test the insertion point at the end of a wrapped line.
   1561    info("Pressing End");
   1562    moved = waitForEvent(EVENT_TEXT_CARET_MOVED, textarea);
   1563    EventUtils.synthesizeKey("KEY_End");
   1564    await moved;
   1565    is(await runPython(`text.GetSelection().Length`), 1, "1 selection");
   1566    await definePyVar("range", `text.GetSelection().GetElement(0)`);
   1567    ok(await runPython(`bool(range)`), "Got selection range 0");
   1568    info("Expanding to character");
   1569    await runPython(`range.ExpandToEnclosingUnit(TextUnit_Character)`);
   1570    is(await runPython(`range.GetText(-1)`), "", "range text correct");
   1571    info("Moving end 1 character");
   1572    await runPython(
   1573      `range.MoveEndpointByUnit(TextPatternRangeEndpoint_End, TextUnit_Character, 1)`
   1574    );
   1575    is(await runPython(`range.GetText(-1)`), "c", "range text correct");
   1576    info("Expanding to line at caret");
   1577    await definePyVar("range", `text.GetSelection().GetElement(0)`);
   1578    await runPython(`range.ExpandToEnclosingUnit(TextUnit_Line)`);
   1579    is(await runPython(`range.GetText(-1)`), "ab ", "range text correct");
   1580 
   1581    // Test the insertion point at the end of the textarea.
   1582    info("Pressing Ctrl+End");
   1583    moved = waitForEvent(EVENT_TEXT_CARET_MOVED, textarea);
   1584    EventUtils.synthesizeKey("KEY_End", { ctrlKey: true });
   1585    await moved;
   1586    is(await runPython(`text.GetSelection().Length`), 1, "1 selection");
   1587    await definePyVar("range", `text.GetSelection().GetElement(0)`);
   1588    ok(await runPython(`bool(range)`), "Got selection range 0");
   1589    info("Expanding to character");
   1590    await runPython(`range.ExpandToEnclosingUnit(TextUnit_Character)`);
   1591    is(await runPython(`range.GetText(-1)`), "", "range text correct");
   1592    info("Expanding to line");
   1593    await definePyVar("range", `text.GetSelection().GetElement(0)`);
   1594    await runPython(`range.ExpandToEnclosingUnit(TextUnit_Line)`);
   1595    is(await runPython(`range.GetText(-1)`), "cd", "range text correct");
   1596 
   1597    info("Clicking mouse at b");
   1598    // BrowserTestUtils.synthesizeMouseAtPoint takes coordinates relative to the document.
   1599    const docX = {};
   1600    const docY = {};
   1601    docAcc.getBounds(docX, docY, {}, {});
   1602    let charX = {};
   1603    let charY = {};
   1604    textarea.getCharacterExtents(
   1605      1,
   1606      charX,
   1607      charY,
   1608      {},
   1609      {},
   1610      COORDTYPE_SCREEN_RELATIVE
   1611    );
   1612    moved = waitForEvent(EVENT_TEXT_CARET_MOVED, textarea);
   1613    await BrowserTestUtils.synthesizeMouseAtPoint(
   1614      charX.value - docX.value,
   1615      charY.value - docY.value,
   1616      {},
   1617      docAcc.browsingContext
   1618    );
   1619    await moved;
   1620    is(await runPython(`text.GetSelection().Length`), 1, "1 selection");
   1621    await definePyVar("range", `text.GetSelection().GetElement(0)`);
   1622    ok(await runPython(`bool(range)`), "Got selection range 0");
   1623    info("Expanding to character");
   1624    await runPython(`range.ExpandToEnclosingUnit(TextUnit_Character)`);
   1625    is(await runPython(`range.GetText(-1)`), "b", "range text correct");
   1626  }
   1627 );
   1628 
   1629 /**
   1630 * Test the Text pattern's GetSelection method with selection.
   1631 */
   1632 addUiaTask(
   1633  `<textarea id="textarea" cols="3">ab cd</textarea>`,
   1634  async function testTextGetSelectionSelection(browser, docAcc) {
   1635    await runPython(`
   1636      doc = getDocUia()
   1637      textarea = findUiaByDomId(doc, "textarea")
   1638      global text
   1639      text = getUiaPattern(textarea, "Text")
   1640    `);
   1641    is(await runPython(`text.GetSelection().Length`), 0, "No selection");
   1642    info("Focusing textarea");
   1643    const textarea = findAccessibleChildByID(docAcc, "textarea", [
   1644      nsIAccessibleText,
   1645    ]);
   1646    let moved = waitForEvent(EVENT_TEXT_CARET_MOVED, textarea);
   1647    textarea.takeFocus();
   1648    await moved;
   1649    is(await runPython(`text.GetSelection().Length`), 1, "1 selection");
   1650    await definePyVar("range", `text.GetSelection().GetElement(0)`);
   1651    ok(await runPython(`bool(range)`), "Got selection range 0");
   1652    is(await runPython(`range.GetText(-1)`), "", "range text correct");
   1653 
   1654    info("Selecting ab");
   1655    moved = waitForEvent(EVENT_TEXT_SELECTION_CHANGED, textarea);
   1656    textarea.addSelection(0, 2);
   1657    await moved;
   1658    is(await runPython(`text.GetSelection().Length`), 1, "1 selection");
   1659    await definePyVar("range", `text.GetSelection().GetElement(0)`);
   1660    ok(await runPython(`bool(range)`), "Got selection range 0");
   1661    is(await runPython(`range.GetText(-1)`), "ab", "range text correct");
   1662 
   1663    info("Adding cd to selection");
   1664    moved = waitForEvent(EVENT_TEXT_SELECTION_CHANGED, textarea);
   1665    textarea.addSelection(3, 5);
   1666    await moved;
   1667    is(await runPython(`text.GetSelection().Length`), 2, "2 selections");
   1668    await definePyVar("range", `text.GetSelection().GetElement(0)`);
   1669    ok(await runPython(`bool(range)`), "Got selection range 0");
   1670    is(await runPython(`range.GetText(-1)`), "ab", "range text correct");
   1671    await definePyVar("range", `text.GetSelection().GetElement(1)`);
   1672    ok(await runPython(`bool(range)`), "Got selection range 1");
   1673    is(await runPython(`range.GetText(-1)`), "cd", "range text correct");
   1674  }
   1675 );
   1676 
   1677 /**
   1678 * Test the Text pattern's TextSelectionChanged event.
   1679 */
   1680 addUiaTask(
   1681  `
   1682 <input id="input" value="abc">
   1683 <div id="editable" contenteditable role="textbox"><p>de</p><p>f</p></div>
   1684  `,
   1685  async function testTextTextSelectionChanged(browser) {
   1686    info("Focusing input");
   1687    await setUpWaitForUiaEvent("Text_TextSelectionChanged", "input");
   1688    await invokeContentTask(browser, [], () => {
   1689      content.document.getElementById("input").focus();
   1690    });
   1691    await waitForUiaEvent();
   1692    ok(true, "input got TextSelectionChanged event");
   1693    info("Moving caret to b");
   1694    await setUpWaitForUiaEvent("Text_TextSelectionChanged", "input");
   1695    await invokeContentTask(browser, [], () => {
   1696      content.document.getElementById("input").setSelectionRange(1, 1);
   1697    });
   1698    await waitForUiaEvent();
   1699    ok(true, "input got TextSelectionChanged event");
   1700    info("Selecting bc");
   1701    await setUpWaitForUiaEvent("Text_TextSelectionChanged", "input");
   1702    await invokeContentTask(browser, [], () => {
   1703      content.document.getElementById("input").setSelectionRange(1, 3);
   1704    });
   1705    await waitForUiaEvent();
   1706    ok(true, "input got TextSelectionChanged event");
   1707 
   1708    info("Focusing editable");
   1709    await setUpWaitForUiaEvent("Text_TextSelectionChanged", "editable");
   1710    await invokeContentTask(browser, [], () => {
   1711      content._editable = content.document.getElementById("editable");
   1712      content._editable.focus();
   1713    });
   1714    await waitForUiaEvent();
   1715    ok(true, "editable got TextSelectionChanged event");
   1716    info("Moving caret to e");
   1717    await setUpWaitForUiaEvent("Text_TextSelectionChanged", "editable");
   1718    await invokeContentTask(browser, [], () => {
   1719      content._de = content._editable.firstChild.firstChild;
   1720      content.getSelection().setBaseAndExtent(content._de, 1, content._de, 1);
   1721    });
   1722    await waitForUiaEvent();
   1723    ok(true, "editable got TextSelectionChanged event");
   1724    info("Selecting ef");
   1725    await setUpWaitForUiaEvent("Text_TextSelectionChanged", "editable");
   1726    await invokeContentTask(browser, [], () => {
   1727      const f = content._editable.children[1].firstChild;
   1728      content.getSelection().setBaseAndExtent(content._de, 1, f, 1);
   1729    });
   1730    await waitForUiaEvent();
   1731    ok(true, "editable got TextSelectionChanged event");
   1732  }
   1733 );
   1734 
   1735 /**
   1736 * Test the Text pattern's TextChanged event.
   1737 */
   1738 addUiaTask(
   1739  `
   1740 <input id="input" value="abc">
   1741 <div id="editable" contenteditable role="textbox">
   1742  <p id="de">de</p>
   1743  <p>f</p>
   1744 </div>
   1745  `,
   1746  async function testTextTextChanged(browser) {
   1747    info("Focusing input");
   1748    let moved = waitForEvent(EVENT_TEXT_CARET_MOVED, "input");
   1749    await invokeContentTask(browser, [], () => {
   1750      content.document.getElementById("input").focus();
   1751    });
   1752    await moved;
   1753    info("Deleting a");
   1754    await setUpWaitForUiaEvent("Text_TextChanged", "input");
   1755    await invokeContentTask(browser, [], () => {
   1756      content.document.execCommand("forwardDelete");
   1757    });
   1758    await waitForUiaEvent();
   1759    ok(true, "input got TextChanged event");
   1760    info("Inserting a");
   1761    await setUpWaitForUiaEvent("Text_TextChanged", "input");
   1762    await invokeContentTask(browser, [], () => {
   1763      content.document.execCommand("insertText", false, "a");
   1764    });
   1765    await waitForUiaEvent();
   1766    ok(true, "input got TextChanged event");
   1767 
   1768    info("Focusing editable");
   1769    moved = waitForEvent(EVENT_TEXT_CARET_MOVED, "de");
   1770    await invokeContentTask(browser, [], () => {
   1771      content.document.getElementById("editable").focus();
   1772    });
   1773    await moved;
   1774    info("Deleting a");
   1775    await setUpWaitForUiaEvent("Text_TextChanged", "editable");
   1776    await invokeContentTask(browser, [], () => {
   1777      content.document.execCommand("forwardDelete");
   1778    });
   1779    await waitForUiaEvent();
   1780    ok(true, "editable got TextChanged event");
   1781    info("Inserting a");
   1782    await setUpWaitForUiaEvent("Text_TextChanged", "editable");
   1783    await invokeContentTask(browser, [], () => {
   1784      content.document.execCommand("insertText", false, "a");
   1785    });
   1786    await waitForUiaEvent();
   1787    ok(true, "editable got TextChanged event");
   1788  }
   1789 );
   1790 
   1791 /**
   1792 * Test the TextRange pattern's GetEnclosingElement method.
   1793 */
   1794 addUiaTask(
   1795  `
   1796 <div id="editable" contenteditable role="textbox">
   1797  ab
   1798  <mark id="cdef"><span>cd</span> <a id="ef" href="/">ef</a></mark>
   1799  <a href="/"><img id="g" src="https://example.com/a11y/accessible/tests/mochitest/moz.png" alt="g"></a>
   1800  <p><button id="h">h</button></p>
   1801 </div>
   1802  `,
   1803  async function testTextRangeGetEnclosingElement() {
   1804    info("Getting editable DocumentRange");
   1805    await runPython(`
   1806      doc = getDocUia()
   1807      editable = findUiaByDomId(doc, "editable")
   1808      text = getUiaPattern(editable, "Text")
   1809      global range
   1810      range = text.DocumentRange
   1811    `);
   1812    is(
   1813      await runPython(`range.GetEnclosingElement().CurrentAutomationId`),
   1814      "editable",
   1815      "EnclosingElement is editable"
   1816    );
   1817    info("Expanding to word");
   1818    await runPython(`range.ExpandToEnclosingUnit(TextUnit_Word)`);
   1819    // Range is now "ab ".
   1820    // The IA2 -> UIA proxy gets this wrong.
   1821    if (gIsUiaEnabled) {
   1822      is(
   1823        await runPython(`range.GetEnclosingElement().CurrentName`),
   1824        "ab ",
   1825        "EnclosingElement is ab text leaf"
   1826      );
   1827    }
   1828    info("Moving 1 word");
   1829    await runPython(`range.Move(TextUnit_Word, 1)`);
   1830    // Range is now "cd ".
   1831    // The "cd" text leaf doesn't include the space, so the enclosing element is
   1832    // its parent.
   1833    is(
   1834      await runPython(`range.GetEnclosingElement().CurrentAutomationId`),
   1835      "cdef",
   1836      "EnclosingElement is cdef"
   1837    );
   1838    info("Moving end -1 character");
   1839    await runPython(
   1840      `range.MoveEndpointByUnit(TextPatternRangeEndpoint_End, TextUnit_Character, -1)`
   1841    );
   1842    // Range is now "cd".
   1843    // The IA2 -> UIA proxy gets this wrong.
   1844    if (gIsUiaEnabled) {
   1845      is(
   1846        await runPython(`range.GetEnclosingElement().CurrentName`),
   1847        "cd",
   1848        "EnclosingElement is cd text leaf"
   1849      );
   1850    }
   1851    info("Moving 1 word");
   1852    await runPython(`range.Move(TextUnit_Word, 1)`);
   1853    // Range is now "ef ".
   1854    // Neither the "ef" text leaf/link nor the "cdef" mark include the trailing
   1855    // space, so the enclosing element is cdef's parent.
   1856    is(
   1857      await runPython(`range.GetEnclosingElement().CurrentAutomationId`),
   1858      "editable",
   1859      "EnclosingElement is editable"
   1860    );
   1861    info("Moving end -1 character");
   1862    await runPython(
   1863      `range.MoveEndpointByUnit(TextPatternRangeEndpoint_End, TextUnit_Character, -1)`
   1864    );
   1865    // Range is now "ef". The innermost element is the text leaf, but "ef" is a
   1866    // link and that's what Narrator wants.
   1867    is(
   1868      await runPython(`range.GetEnclosingElement().CurrentAutomationId`),
   1869      "ef",
   1870      "EnclosingElement is ef"
   1871    );
   1872    // The IA2 -> UIA proxy gets the rest of this wrong.
   1873    if (!gIsUiaEnabled) {
   1874      return;
   1875    }
   1876    info("Moving 1 word");
   1877    await runPython(`range.Move(TextUnit_Word, 1)`);
   1878    // Range is now the embedded object character for the img (g).
   1879    is(
   1880      await runPython(`range.GetEnclosingElement().CurrentAutomationId`),
   1881      "g",
   1882      "EnclosingElement is g"
   1883    );
   1884    info("Moving 1 word");
   1885    await runPython(`range.Move(TextUnit_Word, 1)`);
   1886    // Range is now "h". "h" is a button and buttons prune their children, so
   1887    // UIA doesn't see the text leaf.
   1888    is(
   1889      await runPython(`range.GetEnclosingElement().CurrentAutomationId`),
   1890      "h",
   1891      "EnclosingElement is h"
   1892    );
   1893  }
   1894 );
   1895 
   1896 /**
   1897 * Test the TextRange pattern's GetChildren method.
   1898 */
   1899 addUiaTask(
   1900  `<div id="editable" contenteditable role="textbox">ab <span id="cdef" role="button"><span>cd</span> <a id="ef" href="/">ef</a> </span><img id="g" src="https://example.com/a11y/accessible/tests/mochitest/moz.png" alt="g"></div>`,
   1901  async function testTextRangeGetChildren() {
   1902    info("Getting editable DocumentRange");
   1903    await runPython(`
   1904      doc = getDocUia()
   1905      editable = findUiaByDomId(doc, "editable")
   1906      text = getUiaPattern(editable, "Text")
   1907      global r
   1908      r = text.DocumentRange
   1909    `);
   1910    await isUiaElementArray(
   1911      `r.GetChildren()`,
   1912      ["cdef", "g"],
   1913      "Children are correct"
   1914    );
   1915    info("Expanding to word");
   1916    await runPython(`r.ExpandToEnclosingUnit(TextUnit_Word)`);
   1917    // Range is now "ab ".
   1918    await isUiaElementArray(`r.GetChildren()`, [], "Children are correct");
   1919    info("Moving 1 word");
   1920    await runPython(`r.Move(TextUnit_Word, 1)`);
   1921    // Range is now "cd ".
   1922    await isUiaElementArray(`r.GetChildren()`, [], "Children are correct");
   1923    info("Moving 1 word");
   1924    await runPython(`r.Move(TextUnit_Word, 1)`);
   1925    // Range is now "ef ". The range includes the link but is not completely
   1926    // enclosed by the link.
   1927    await isUiaElementArray(`r.GetChildren()`, ["ef"], "Children are correct");
   1928    info("Moving end -1 character");
   1929    await runPython(
   1930      `r.MoveEndpointByUnit(TextPatternRangeEndpoint_End, TextUnit_Character, -1)`
   1931    );
   1932    // Range is now "ef". The range encloses the link, so there are no children.
   1933    await isUiaElementArray(`r.GetChildren()`, [], "Children are correct");
   1934    info("Moving 1 word");
   1935    await runPython(`r.Move(TextUnit_Word, 1)`);
   1936    // Range is now the embedded object character for the img (g). The range is
   1937    // completely enclosed by the image.
   1938    // The IA2 -> UIA proxy gets this wrong.
   1939    if (gIsUiaEnabled) {
   1940      await isUiaElementArray(`r.GetChildren()`, [], "Children are correct");
   1941    }
   1942  }
   1943 );
   1944 
   1945 /**
   1946 * Test the Text pattern's RangeFromChild method.
   1947 */
   1948 addUiaTask(
   1949  `<div id="editable" contenteditable role="textbox">ab <mark id="cdef"><span>cd</span> <a id="ef" href="/">ef</a></mark> <img id="g" src="https://example.com/a11y/accessible/tests/mochitest/moz.png" alt="g"></div>`,
   1950  async function testTextRangeFromChild() {
   1951    await runPython(`
   1952      global doc, docText, editable, edText
   1953      doc = getDocUia()
   1954      docText = getUiaPattern(doc, "Text")
   1955      editable = findUiaByDomId(doc, "editable")
   1956      edText = getUiaPattern(editable, "Text")
   1957    `);
   1958    is(
   1959      await runPython(`docText.RangeFromChild(editable).GetText(-1)`),
   1960      `ab cd ef ${kEmbedChar}`,
   1961      "doc returned correct range for editable"
   1962    );
   1963    await testPythonRaises(
   1964      `edText.RangeFromChild(editable)`,
   1965      "editable correctly failed to return range for editable"
   1966    );
   1967    is(
   1968      await runPython(`docText.RangeFromChild(editable).GetText(-1)`),
   1969      `ab cd ef ${kEmbedChar}`,
   1970      "doc returned correct range for editable"
   1971    );
   1972    let text = await runPython(`
   1973      ab = uiaClient.RawViewWalker.GetFirstChildElement(editable)
   1974      range = docText.RangeFromChild(ab)
   1975      return range.GetText(-1)
   1976    `);
   1977    is(text, "ab ", "doc returned correct range for ab");
   1978    text = await runPython(`
   1979      global cdef
   1980      cdef = findUiaByDomId(doc, "cdef")
   1981      range = docText.RangeFromChild(cdef)
   1982      return range.GetText(-1)
   1983    `);
   1984    is(text, "cd ef", "doc returned correct range for cdef");
   1985    text = await runPython(`
   1986      cd = uiaClient.RawViewWalker.GetFirstChildElement(cdef)
   1987      range = docText.RangeFromChild(cd)
   1988      return range.GetText(-1)
   1989    `);
   1990    is(text, "cd", "doc returned correct range for cd");
   1991    text = await runPython(`
   1992      global efLink
   1993      efLink = findUiaByDomId(doc, "ef")
   1994      range = docText.RangeFromChild(efLink)
   1995      return range.GetText(-1)
   1996    `);
   1997    is(text, "ef", "doc returned correct range for ef link");
   1998    text = await runPython(`
   1999      efLeaf = uiaClient.RawViewWalker.GetFirstChildElement(efLink)
   2000      range = docText.RangeFromChild(efLeaf)
   2001      return range.GetText(-1)
   2002    `);
   2003    is(text, "ef", "doc returned correct range for ef leaf");
   2004    text = await runPython(`
   2005      g = findUiaByDomId(doc, "g")
   2006      range = docText.RangeFromChild(g)
   2007      return range.GetText(-1)
   2008    `);
   2009    is(text, kEmbedChar, "doc returned correct range for g");
   2010  },
   2011  // The IA2 -> UIA proxy has too many quirks/bugs here.
   2012  { uiaEnabled: true, uiaDisabled: false }
   2013 );
   2014 
   2015 /**
   2016 * Test the Text pattern's RangeFromPoint method.
   2017 */
   2018 addUiaTask(
   2019  `<div id="test">a <span>b </span>c</div>`,
   2020  async function testTextRangeFromPoint(browser, docAcc) {
   2021    const acc = findAccessibleChildByID(docAcc, "test", [nsIAccessibleText]);
   2022    await runPython(`
   2023      global doc, docText
   2024      doc = getDocUia()
   2025      docText = getUiaPattern(doc, "Text")
   2026    `);
   2027 
   2028    // Walk through every offset in the accessible and hit test each. Verify
   2029    // that the returned range is empty, and that it hit the right character.
   2030    for (let offset = 0; offset < acc.characterCount; ++offset) {
   2031      const x = {};
   2032      const y = {};
   2033      acc.getCharacterExtents(offset, x, y, {}, {}, COORDTYPE_SCREEN_RELATIVE);
   2034      await runPython(`
   2035        global range
   2036        range = docText.RangeFromPoint(POINT(${x.value}, ${y.value}))`);
   2037      is(
   2038        await runPython(`range.GetText(-1)`),
   2039        ``,
   2040        "doc returned correct empty range"
   2041      );
   2042      await runPython(`range.ExpandToEnclosingUnit(TextUnit_Character)`);
   2043      const charAtOffset = acc.getCharacterAtOffset(offset);
   2044      is(
   2045        await runPython(`range.GetText(-1)`),
   2046        `${charAtOffset}`,
   2047        "doc returned correct range"
   2048      );
   2049    }
   2050 
   2051    // An arbitrary invalid point should cause an invalid argument error.
   2052    await testPythonRaises(
   2053      `docText.RangeFromPoint(POINT(9999999999, 9999999999))`,
   2054      "no text leaves at invalid point"
   2055    );
   2056  },
   2057  { uiaEnabled: true, uiaDisabled: true }
   2058 );
   2059 
   2060 /**
   2061 * Test the TextRange pattern's GetBoundingRectangles method.
   2062 */
   2063 addUiaTask(
   2064  `
   2065 <div id="test"><p id="line1">abc</p><p id="line2">d</p><p id="line3"></p></div>
   2066 <div id="offscreen" style="position:absolute; left:200vw;">xyz</div>
   2067  `,
   2068  async function testTextRangeGetBoundingRectangles(browser, docAcc) {
   2069    const line1 = findAccessibleChildByID(docAcc, "line1", [nsIAccessibleText]);
   2070    const line2 = findAccessibleChildByID(docAcc, "line2", [nsIAccessibleText]);
   2071 
   2072    const lineRects = await runPython(`
   2073      global doc, docText, testAcc, range
   2074      doc = getDocUia()
   2075      docText = getUiaPattern(doc, "Text")
   2076      testAcc = findUiaByDomId(doc, "test")
   2077      range = docText.RangeFromChild(testAcc)
   2078      return range.GetBoundingRectangles()
   2079    `);
   2080 
   2081    is(lineRects.length, 8, "GetBoundingRectangles returned two rectangles");
   2082    const firstLineRect = [
   2083      lineRects[0],
   2084      lineRects[1],
   2085      lineRects[2],
   2086      lineRects[3],
   2087    ];
   2088    const secondLineRect = [
   2089      lineRects[4],
   2090      lineRects[5],
   2091      lineRects[6],
   2092      lineRects[7],
   2093    ];
   2094    testTextBounds(line1, 0, -1, firstLineRect, COORDTYPE_SCREEN_RELATIVE);
   2095    testTextBounds(line2, 0, -1, secondLineRect, COORDTYPE_SCREEN_RELATIVE);
   2096    // line3 has no rectangle - GetBoundingRectangles shouldn't return anything for empty lines.
   2097 
   2098    // GetBoundingRectangles shouldn't return anything for offscreen lines.
   2099    const offscreenRects = await runPython(`
   2100      global offscreenAcc, range
   2101      offscreenAcc = findUiaByDomId(doc, "offscreen")
   2102      range = docText.RangeFromChild(offscreenAcc)
   2103      return range.GetBoundingRectangles()
   2104    `);
   2105    is(
   2106      offscreenRects.length,
   2107      0,
   2108      "GetBoundingRectangles returned no rectangles"
   2109    );
   2110  },
   2111  { uiaEnabled: true, uiaDisabled: true, chrome: true }
   2112 );
   2113 
   2114 /**
   2115 * Test char bounds with the TextRange pattern's GetBoundingRectangles method.
   2116 */
   2117 addUiaTask(
   2118  `<div id="test">abc</div>`,
   2119  async function testTextRangeGetBoundingRectanglesChar(browser, docAcc) {
   2120    const testAcc = findAccessibleChildByID(docAcc, "test", [
   2121      nsIAccessibleText,
   2122    ]);
   2123    const charX = {};
   2124    const charY = {};
   2125    const charW = {};
   2126    const charH = {};
   2127    testAcc.getCharacterExtents(
   2128      0,
   2129      charX,
   2130      charY,
   2131      charW,
   2132      charH,
   2133      COORDTYPE_SCREEN_RELATIVE
   2134    );
   2135 
   2136    await runPython(`
   2137      global doc, docText, testAcc, range
   2138      doc = getDocUia()
   2139      docText = getUiaPattern(doc, "Text")
   2140      testAcc = findUiaByDomId(doc, "test")
   2141      range = docText.RangeFromChild(testAcc)
   2142      range.ExpandToEnclosingUnit(TextUnit_Character)
   2143    `);
   2144    is(await runPython(`range.GetText(-1)`), "a", "range text correct");
   2145 
   2146    const uiaRect = await runPython(`range.GetBoundingRectangles()`);
   2147    is(uiaRect.length, 4, "GetBoundingRectangles returned one rectangle");
   2148    is(uiaRect[0], charX.value, "UIA char rect X matches core char rect X");
   2149    is(uiaRect[1], charY.value, "UIA char rect Y matches core char rect Y");
   2150    is(uiaRect[2], charW.value, "UIA char rect W matches core char rect W");
   2151    is(uiaRect[3], charH.value, "UIA char rect H matches core char rect H");
   2152  },
   2153  { uiaEnabled: true, uiaDisabled: true, chrome: true }
   2154 );
   2155 
   2156 /**
   2157 * Test special case line bounds with the TextRange pattern's
   2158 * GetBoundingRectangles method.
   2159 */
   2160 addUiaTask(
   2161  `
   2162 <div><span id="line-break" tabindex="-1">ABC<br/></span>DEF</div>
   2163 <p style="width: 1px;"><span id="wrapping" tabindex="-1">ABC</span> DEF</p>
   2164  `,
   2165  async function testTextRangeGetBoundingRectanglesLine(browser, docAcc) {
   2166    const lineBreakAcc = findAccessibleChildByID(docAcc, "line-break", [
   2167      nsIAccessibleText,
   2168    ]);
   2169    const wrappingAcc = findAccessibleChildByID(docAcc, "wrapping", [
   2170      nsIAccessibleText,
   2171    ]);
   2172 
   2173    let lineRects = await runPython(`
   2174      global doc, docText, testAcc
   2175      doc = getDocUia()
   2176      docText = getUiaPattern(doc, "Text")
   2177      testAcc = findUiaByDomId(doc, "line-break")
   2178      range = docText.RangeFromChild(testAcc)
   2179      return range.GetBoundingRectangles()
   2180    `);
   2181 
   2182    is(lineRects.length, 4, "GetBoundingRectangles returned one rectangle");
   2183    const lineBreakLineRect = [
   2184      lineRects[0],
   2185      lineRects[1],
   2186      lineRects[2],
   2187      lineRects[3],
   2188    ];
   2189    testTextBounds(
   2190      lineBreakAcc,
   2191      0,
   2192      -1,
   2193      lineBreakLineRect,
   2194      COORDTYPE_SCREEN_RELATIVE
   2195    );
   2196 
   2197    lineRects = await runPython(`
   2198      global doc, docText, testAcc
   2199      testAcc = findUiaByDomId(doc, "wrapping")
   2200      range = docText.RangeFromChild(testAcc)
   2201      return range.GetBoundingRectangles()
   2202    `);
   2203    is(lineRects.length, 4, "GetBoundingRectangles returned one rectangle");
   2204    const wrappingLineRect = [
   2205      lineRects[0],
   2206      lineRects[1],
   2207      lineRects[2],
   2208      lineRects[3],
   2209    ];
   2210    testTextBounds(
   2211      wrappingAcc,
   2212      0,
   2213      -1,
   2214      wrappingLineRect,
   2215      COORDTYPE_SCREEN_RELATIVE
   2216    );
   2217  },
   2218  { uiaEnabled: true, uiaDisabled: true, chrome: true }
   2219 );
   2220 
   2221 /**
   2222 * Test the Text pattern's GetBoundingRectangles method with the caret.
   2223 */
   2224 addUiaTask(
   2225  `
   2226 <style>
   2227  @font-face {
   2228    font-family: Ahem;
   2229    src: url(${CURRENT_CONTENT_DIR}e10s/fonts/Ahem.sjs);
   2230  }
   2231  textarea {
   2232    font: 10px/10px Ahem;
   2233    width: 10px;
   2234    height: 80px;
   2235  }
   2236 </style>
   2237 <div id="editable" contenteditable role="textbox">
   2238  <div id="ce0">a</div>
   2239  <div id="ce1"><br></div>
   2240  <div id="ce2">b</div>
   2241 </div>
   2242 <textarea id="textarea">ab
   2243 
   2244 </textarea>
   2245 <input id="empty">
   2246  `,
   2247  async function testTextRangeGetBoundingRectanglesCaret(browser, docAcc) {
   2248    info("Focusing editable");
   2249    const editable = findAccessibleChildByID(docAcc, "editable");
   2250    const ce0 = findAccessibleChildByID(docAcc, "ce0");
   2251    let moved = waitForEvent(EVENT_TEXT_CARET_MOVED, ce0);
   2252    editable.takeFocus();
   2253    await moved;
   2254    await runPython(`
   2255      global doc, text
   2256      doc = getDocUia()
   2257      editable = findUiaByDomId(doc, "editable")
   2258      text = getUiaPattern(editable, "Text")
   2259    `);
   2260    let uiaRects = await runPython(
   2261      `text.GetSelection().GetElement(0).GetBoundingRectangles()`
   2262    );
   2263    testTextPos(ce0, 0, [uiaRects[0], uiaRects[1]], COORDTYPE_SCREEN_RELATIVE);
   2264    let [prevX, prevY] = uiaRects;
   2265 
   2266    info("ArrowRight to end of line");
   2267    moved = waitForEvent(EVENT_TEXT_CARET_MOVED, ce0);
   2268    EventUtils.synthesizeKey("KEY_ArrowRight");
   2269    await moved;
   2270    uiaRects = await runPython(
   2271      `text.GetSelection().GetElement(0).GetBoundingRectangles()`
   2272    );
   2273    Assert.greater(uiaRects[0], prevX, "x > prevX");
   2274    is(uiaRects[1], prevY, "y == prevY");
   2275 
   2276    info("ArrowRight to line feed on blank line");
   2277    const ce1 = findAccessibleChildByID(docAcc, "ce1");
   2278    moved = waitForEvent(EVENT_TEXT_CARET_MOVED, ce1);
   2279    EventUtils.synthesizeKey("KEY_ArrowRight");
   2280    await moved;
   2281    uiaRects = await runPython(
   2282      `text.GetSelection().GetElement(0).GetBoundingRectangles()`
   2283    );
   2284    testTextPos(ce1, 0, [uiaRects[0], uiaRects[1]], COORDTYPE_SCREEN_RELATIVE);
   2285 
   2286    info("ArrowRight to b");
   2287    const ce2 = findAccessibleChildByID(docAcc, "ce2");
   2288    moved = waitForEvent(EVENT_TEXT_CARET_MOVED, ce2);
   2289    EventUtils.synthesizeKey("KEY_ArrowRight");
   2290    await moved;
   2291    uiaRects = await runPython(
   2292      `text.GetSelection().GetElement(0).GetBoundingRectangles()`
   2293    );
   2294    testTextPos(ce2, 0, [uiaRects[0], uiaRects[1]], COORDTYPE_SCREEN_RELATIVE);
   2295    [prevX, prevY] = uiaRects;
   2296 
   2297    info("ArrowRight to end of line");
   2298    moved = waitForEvent(EVENT_TEXT_CARET_MOVED, ce2);
   2299    EventUtils.synthesizeKey("KEY_ArrowRight");
   2300    await moved;
   2301    uiaRects = await runPython(
   2302      `text.GetSelection().GetElement(0).GetBoundingRectangles()`
   2303    );
   2304    Assert.greater(uiaRects[0], prevX, "x > prevX");
   2305    is(uiaRects[1], prevY, "y == prevY");
   2306 
   2307    info("Focusing textarea");
   2308    const textarea = findAccessibleChildByID(docAcc, "textarea");
   2309    moved = waitForEvent(EVENT_TEXT_CARET_MOVED, textarea);
   2310    textarea.takeFocus();
   2311    await moved;
   2312    await runPython(`
   2313      global text
   2314      textarea = findUiaByDomId(doc, "textarea")
   2315      text = getUiaPattern(textarea, "Text")
   2316    `);
   2317    uiaRects = await runPython(
   2318      `text.GetSelection().GetElement(0).GetBoundingRectangles()`
   2319    );
   2320    testTextPos(
   2321      textarea,
   2322      0,
   2323      [uiaRects[0], uiaRects[1]],
   2324      COORDTYPE_SCREEN_RELATIVE
   2325    );
   2326    [prevX, prevY] = uiaRects;
   2327 
   2328    info("ArrowRight to end of line");
   2329    moved = waitForEvent(EVENT_TEXT_CARET_MOVED, textarea);
   2330    EventUtils.synthesizeKey("KEY_ArrowRight");
   2331    await moved;
   2332    uiaRects = await runPython(
   2333      `text.GetSelection().GetElement(0).GetBoundingRectangles()`
   2334    );
   2335    Assert.greater(uiaRects[0], prevX, "x > prevX");
   2336    is(uiaRects[1], prevY, "y == prevY");
   2337 
   2338    info("ArrowRight to b");
   2339    moved = waitForEvent(EVENT_TEXT_CARET_MOVED, textarea);
   2340    EventUtils.synthesizeKey("KEY_ArrowRight");
   2341    await moved;
   2342    uiaRects = await runPython(
   2343      `text.GetSelection().GetElement(0).GetBoundingRectangles()`
   2344    );
   2345    testTextPos(
   2346      textarea,
   2347      1,
   2348      [uiaRects[0], uiaRects[1]],
   2349      COORDTYPE_SCREEN_RELATIVE
   2350    );
   2351 
   2352    info("ArrowRight to line feed");
   2353    moved = waitForEvent(EVENT_TEXT_CARET_MOVED, textarea);
   2354    EventUtils.synthesizeKey("KEY_ArrowRight");
   2355    await moved;
   2356    uiaRects = await runPython(
   2357      `text.GetSelection().GetElement(0).GetBoundingRectangles()`
   2358    );
   2359    testTextPos(
   2360      textarea,
   2361      2,
   2362      [uiaRects[0], uiaRects[1]],
   2363      COORDTYPE_SCREEN_RELATIVE
   2364    );
   2365 
   2366    info("ArrowRight to line feed on first blank line");
   2367    moved = waitForEvent(EVENT_TEXT_CARET_MOVED, textarea);
   2368    EventUtils.synthesizeKey("KEY_ArrowRight");
   2369    await moved;
   2370    uiaRects = await runPython(
   2371      `text.GetSelection().GetElement(0).GetBoundingRectangles()`
   2372    );
   2373    testTextPos(
   2374      textarea,
   2375      3,
   2376      [uiaRects[0], uiaRects[1]],
   2377      COORDTYPE_SCREEN_RELATIVE
   2378    );
   2379    [prevX, prevY] = uiaRects;
   2380 
   2381    info("ArrowRight to second blank line (end of textarea)");
   2382    moved = waitForEvent(EVENT_TEXT_CARET_MOVED, textarea);
   2383    EventUtils.synthesizeKey("KEY_ArrowRight");
   2384    await moved;
   2385    uiaRects = await runPython(
   2386      `text.GetSelection().GetElement(0).GetBoundingRectangles()`
   2387    );
   2388    is(uiaRects[0], prevX, "x == prevX");
   2389    Assert.greater(uiaRects[1], prevY, "y > prevY");
   2390 
   2391    info("Focusing empty");
   2392    const empty = findAccessibleChildByID(docAcc, "empty", [nsIAccessibleText]);
   2393    moved = waitForEvent(EVENT_TEXT_CARET_MOVED, empty);
   2394    empty.takeFocus();
   2395    await moved;
   2396    await runPython(`
   2397      global text
   2398      empty = findUiaByDomId(doc, "empty")
   2399      text = getUiaPattern(empty, "Text")
   2400    `);
   2401    uiaRects = await runPython(
   2402      `text.GetSelection().GetElement(0).GetBoundingRectangles()`
   2403    );
   2404    const caretX = {};
   2405    const caretY = {};
   2406    const caretW = {};
   2407    const caretH = {};
   2408    empty.getCaretRect(caretX, caretY, caretW, caretH);
   2409    is(uiaRects[0], caretX.value, "x == caretX");
   2410    is(uiaRects[1], caretY.value, "y == caretY");
   2411    is(uiaRects[2], caretW.value, "w == caretW");
   2412    is(uiaRects[3], caretH.value, "h == caretH");
   2413  },
   2414  // The IA2 -> UIA proxy doesn't support this.
   2415  { uiaEnabled: true, uiaDisabled: false }
   2416 );
   2417 
   2418 /**
   2419 * Test the TextRange pattern's ScrollIntoView method.
   2420 */
   2421 addUiaTask(
   2422  `
   2423 <style>
   2424  body {
   2425    margin: 0;
   2426  }
   2427 </style>
   2428 <p>p1</p>
   2429 <hr style="height: 200vh;">
   2430 <p id="p2">p2</p>
   2431 <hr style="height: 200vh;">
   2432 <p>p3</p>
   2433  `,
   2434  async function testTextRangeScrollIntoView(browser, docAcc) {
   2435    const [docLeft, docTop, , docBottom] = await runPython(`
   2436      global doc
   2437      doc = getDocUia()
   2438      rect = doc.CurrentBoundingRectangle
   2439      return (rect.left, rect.top, rect.right, rect.bottom)
   2440    `);
   2441 
   2442    info("Scrolling p2 to top");
   2443    let scrolled = waitForEvent(EVENT_SCROLLING_END, docAcc);
   2444    await runPython(`
   2445      global docText, p2, range
   2446      docText = getUiaPattern(doc, "Text")
   2447      p2 = findUiaByDomId(doc, "p2")
   2448      range = docText.RangeFromChild(p2)
   2449      range.ScrollIntoView(True)
   2450    `);
   2451    await scrolled;
   2452    let [left, top, , height] = await runPython(
   2453      `range.GetBoundingRectangles()`
   2454    );
   2455    is(left, docLeft, "range is at left of document");
   2456    is(top, docTop, "range is at top of document");
   2457 
   2458    info("Scrolling p2 to bottom");
   2459    scrolled = waitForEvent(EVENT_SCROLLING_END, docAcc);
   2460    await runPython(`
   2461      range.ScrollIntoView(False)
   2462    `);
   2463    await scrolled;
   2464    [left, top, , height] = await runPython(`range.GetBoundingRectangles()`);
   2465    is(left, docLeft, "range is at left of document");
   2466    is(top + height, docBottom, "range is at bottom of document");
   2467  }
   2468 );
   2469 
   2470 /**
   2471 * Test the TextRange pattern's Select method.
   2472 */
   2473 addUiaTask(
   2474  `
   2475 <p id="p">ab<a id="link" href="/">c</a></p>
   2476 <input id="input" type="text" value="ab">
   2477 <div id="contenteditable" contenteditable role="textbox">ab</div>
   2478  `,
   2479  async function testTextRangeSelect(browser, docAcc) {
   2480    info("Moving caret to b in p");
   2481    const p = findAccessibleChildByID(docAcc, "p", [nsIAccessibleText]);
   2482    let moved = waitForEvent(EVENT_TEXT_CARET_MOVED, p);
   2483    await runPython(`
   2484      global doc
   2485      doc = getDocUia()
   2486      p = findUiaByDomId(doc, "p")
   2487      textChild = getUiaPattern(p, "TextChild")
   2488      global pbRange
   2489      pbRange = textChild.TextRange
   2490      # Encompass "b".
   2491      pbRange.Move(TextUnit_Character, 1)
   2492      # Collapse.
   2493      pbRange.MoveEndpointByRange(TextPatternRangeEndpoint_End, pbRange, TextPatternRangeEndpoint_Start)
   2494      pbRange.Select()
   2495    `);
   2496    await moved;
   2497    testTextSelectionCount(p, 0);
   2498    is(p.caretOffset, 1, "caret at 1");
   2499    // When using the IA2 -> UIA proxy, the focus changes when moving the caret,
   2500    // but this isn't what UIA clients want.
   2501    if (gIsUiaEnabled) {
   2502      info("Moving caret to c in link");
   2503      const link = findAccessibleChildByID(docAcc, "link", [nsIAccessibleText]);
   2504      moved = waitForEvents({
   2505        expected: [[EVENT_TEXT_CARET_MOVED, link]],
   2506        unexpected: [[EVENT_FOCUS, link]],
   2507      });
   2508      await runPython(`
   2509        link = findUiaByDomId(doc, "link")
   2510        textChild = getUiaPattern(link, "TextChild")
   2511        range = textChild.TextRange
   2512        # Collapse to "a".
   2513        range.MoveEndpointByRange(TextPatternRangeEndpoint_End, range, TextPatternRangeEndpoint_Start)
   2514        range.Select()
   2515      `);
   2516      await moved;
   2517      testTextSelectionCount(link, 0);
   2518      is(p.caretOffset, 2, "p caret at 2");
   2519      is(link.caretOffset, 0, "link caret at 0");
   2520      info("Focusing link");
   2521      moved = waitForEvent(EVENT_FOCUS, link);
   2522      link.takeFocus();
   2523      await moved;
   2524      info("Moving caret back to b in p");
   2525      moved = waitForEvents({
   2526        expected: [[EVENT_TEXT_CARET_MOVED, p]],
   2527        unexpected: [[EVENT_FOCUS, docAcc]],
   2528      });
   2529      await runPython(`
   2530        pbRange.Select()
   2531      `);
   2532      await moved;
   2533      testTextSelectionCount(p, 0);
   2534      is(p.caretOffset, 1, "p caret at 1");
   2535    }
   2536 
   2537    // <input> and contentEditable should behave the same.
   2538    for (const id of ["input", "contenteditable"]) {
   2539      info(`Focusing ${id}`);
   2540      const acc = findAccessibleChildByID(docAcc, id, [nsIAccessibleText]);
   2541      moved = waitForEvents([
   2542        [EVENT_FOCUS, acc],
   2543        [EVENT_TEXT_CARET_MOVED, acc],
   2544      ]);
   2545      acc.takeFocus();
   2546      await moved;
   2547 
   2548      info("Selecting a");
   2549      moved = waitForEvents([
   2550        [EVENT_TEXT_SELECTION_CHANGED, acc],
   2551        [EVENT_TEXT_CARET_MOVED, acc],
   2552      ]);
   2553      await runPython(`
   2554        acc = findUiaByDomId(doc, "${id}")
   2555        text = getUiaPattern(acc, "Text")
   2556        global range
   2557        range = text.DocumentRange
   2558        range.ExpandToEnclosingUnit(TextUnit_Character)
   2559        range.Select()
   2560      `);
   2561      await moved;
   2562      testTextSelectionCount(acc, 1);
   2563      testTextGetSelection(acc, 0, 1, 0);
   2564 
   2565      info("Moving caret to b");
   2566      moved = waitForEvent(EVENT_TEXT_CARET_MOVED, acc);
   2567      await runPython(`
   2568        # Collapse to b.
   2569        range.MoveEndpointByUnit(TextPatternRangeEndpoint_Start, TextUnit_Character, 1)
   2570        range.Select()
   2571      `);
   2572      await moved;
   2573      testTextSelectionCount(acc, 0);
   2574      is(acc.caretOffset, 1, "caret at 1");
   2575    }
   2576  }
   2577 );
   2578 
   2579 /**
   2580 * Test the TextRange pattern's AddToSelection method.
   2581 */
   2582 addUiaTask(
   2583  `
   2584 <input id="input" type="text" value="abc">
   2585 <div id="contenteditable" contenteditable role="textbox">abc</div>
   2586  `,
   2587  async function testTextRangeAddToSelection(browser, docAcc) {
   2588    // <input> and contentEditable should behave the same.
   2589    for (const id of ["input", "contenteditable"]) {
   2590      info(`Focusing ${id}`);
   2591      const acc = findAccessibleChildByID(docAcc, id, [nsIAccessibleText]);
   2592      let moved = waitForEvents([
   2593        [EVENT_FOCUS, acc],
   2594        [EVENT_TEXT_CARET_MOVED, acc],
   2595      ]);
   2596      acc.takeFocus();
   2597      await moved;
   2598 
   2599      info("Adding a to selection");
   2600      moved = waitForEvents([
   2601        [EVENT_TEXT_SELECTION_CHANGED, acc],
   2602        [EVENT_TEXT_CARET_MOVED, acc],
   2603      ]);
   2604      await runPython(`
   2605        doc = getDocUia()
   2606        acc = findUiaByDomId(doc, "${id}")
   2607        text = getUiaPattern(acc, "Text")
   2608        global range
   2609        range = text.DocumentRange
   2610        range.ExpandToEnclosingUnit(TextUnit_Character)
   2611        range.AddToSelection()
   2612      `);
   2613      await moved;
   2614      testTextSelectionCount(acc, 1);
   2615      testTextGetSelection(acc, 0, 1, 0);
   2616 
   2617      info("Adding c to selection");
   2618      moved = waitForEvent(EVENT_TEXT_CARET_MOVED, acc);
   2619      await runPython(`
   2620        # Move start to c.
   2621        range.MoveEndpointByUnit(TextPatternRangeEndpoint_Start, TextUnit_Character, 2)
   2622        range.ExpandToEnclosingUnit(TextUnit_Character)
   2623        range.AddToSelection()
   2624      `);
   2625      await moved;
   2626      testTextSelectionCount(acc, 2);
   2627      testTextGetSelection(acc, 0, 1, 0);
   2628      testTextGetSelection(acc, 2, 3, 1);
   2629    }
   2630  }
   2631 );
   2632 
   2633 /**
   2634 * Test the TextRange pattern's RemoveFromSelection method.
   2635 */
   2636 addUiaTask(
   2637  `
   2638 <input id="input" type="text" value="abc">
   2639 <div id="contenteditable" contenteditable role="textbox">abc</div>
   2640  `,
   2641  async function testTextRangeRemoveFromSelection(browser, docAcc) {
   2642    // <input> and contentEditable should behave the same.
   2643    for (const id of ["input", "contenteditable"]) {
   2644      info(`Focusing ${id}`);
   2645      const acc = findAccessibleChildByID(docAcc, id, [nsIAccessibleText]);
   2646      let moved = waitForEvents([
   2647        [EVENT_FOCUS, acc],
   2648        [EVENT_TEXT_CARET_MOVED, acc],
   2649      ]);
   2650      acc.takeFocus();
   2651      await moved;
   2652 
   2653      info("Adding a to selection");
   2654      moved = waitForEvents([
   2655        [EVENT_TEXT_SELECTION_CHANGED, acc],
   2656        [EVENT_TEXT_CARET_MOVED, acc],
   2657      ]);
   2658      acc.addSelection(0, 1);
   2659      await moved;
   2660      info("Adding c to selection");
   2661      moved = waitForEvents([
   2662        [EVENT_TEXT_SELECTION_CHANGED, acc],
   2663        [EVENT_TEXT_CARET_MOVED, acc],
   2664      ]);
   2665      acc.addSelection(2, 3);
   2666      await moved;
   2667 
   2668      info("Removing a from selection");
   2669      moved = waitForEvents([
   2670        [EVENT_TEXT_SELECTION_CHANGED, acc],
   2671        [EVENT_TEXT_CARET_MOVED, acc],
   2672      ]);
   2673      await runPython(`
   2674        doc = getDocUia()
   2675        acc = findUiaByDomId(doc, "${id}")
   2676        text = getUiaPattern(acc, "Text")
   2677        global range
   2678        range = text.DocumentRange
   2679        range.ExpandToEnclosingUnit(TextUnit_Character)
   2680        range.RemoveFromSelection()
   2681      `);
   2682      await moved;
   2683      testTextSelectionCount(acc, 1);
   2684      testTextGetSelection(acc, 2, 3, 0);
   2685 
   2686      info("Removing b from selection even though it isn't selected");
   2687      await runPython(`
   2688        # Move start to b.
   2689        range.MoveEndpointByUnit(TextPatternRangeEndpoint_Start, TextUnit_Character, 1)
   2690        range.ExpandToEnclosingUnit(TextUnit_Character)
   2691      `);
   2692      await testPythonRaises(
   2693        `range.RemoveFromSelection()`,
   2694        "RemoveFromSelection failed"
   2695      );
   2696 
   2697      info("Removing c from selection");
   2698      moved = waitForEvent(EVENT_TEXT_SELECTION_CHANGED, acc);
   2699      await runPython(`
   2700        # Move start to c.
   2701        range.MoveEndpointByUnit(TextPatternRangeEndpoint_Start, TextUnit_Character, 1)
   2702        range.ExpandToEnclosingUnit(TextUnit_Character)
   2703        range.RemoveFromSelection()
   2704      `);
   2705      await moved;
   2706      testTextSelectionCount(acc, 0);
   2707    }
   2708  },
   2709  // The IA2 -> UIA proxy doesn't support RemoveFromSelection correctly.
   2710  { uiaEnabled: true, uiaDisabled: false }
   2711 );
   2712 
   2713 /**
   2714 * Test the TextRange pattern's FindAttribute method.
   2715 */
   2716 addUiaTask(
   2717  `
   2718 <div id="font-weight-container">a <span tabindex="0"><b>bcd</b></span><b> ef</b> ghi</div>
   2719  `,
   2720  async function testTextRangeFindAttribute(_browser, _docAcc) {
   2721    info("Constructing range on bold text run");
   2722    await runPython(`
   2723      global doc, docText, range, fontWeightContainerAcc
   2724      doc = getDocUia()
   2725      docText = getUiaPattern(doc, "Text")
   2726      fontWeightContainerAcc = findUiaByDomId(doc, "font-weight-container")
   2727      range = docText.RangeFromChild(fontWeightContainerAcc)
   2728    `);
   2729    is(
   2730      await runPython(`range.GetText(-1)`),
   2731      "a bcd ef ghi",
   2732      "range text correct"
   2733    );
   2734 
   2735    info("Finding first font-weight 400 text range");
   2736    await runPython(`
   2737      global subrange
   2738      subrange = range.FindAttribute(UIA_FontWeightAttributeId, 400, False)
   2739    `);
   2740    is(await runPython(`subrange.GetText(-1)`), "a ", "range text correct");
   2741 
   2742    info("Finding first font-weight 700 text range");
   2743    await runPython(`
   2744      global subrange
   2745      subrange = range.FindAttribute(UIA_FontWeightAttributeId, 700, False)
   2746    `);
   2747    is(await runPython(`subrange.GetText(-1)`), "bcd ef", "range text correct");
   2748 
   2749    info("Finding last font-weight 700 text range");
   2750    await runPython(`
   2751      global subrange
   2752      subrange = range.FindAttribute(UIA_FontWeightAttributeId, 700, True)
   2753    `);
   2754    is(await runPython(`subrange.GetText(-1)`), "bcd ef", "range text correct");
   2755 
   2756    info("Finding last font-weight 400 text range");
   2757    await runPython(`
   2758      global subrange
   2759      subrange = range.FindAttribute(UIA_FontWeightAttributeId, 400, True)
   2760    `);
   2761    is(await runPython(`subrange.GetText(-1)`), " ghi", "range text correct");
   2762 
   2763    // The IA2 -> UIA proxy gets things below this wrong.
   2764    if (!gIsUiaEnabled) {
   2765      return;
   2766    }
   2767    info("Moving range to the middle of a text attribute run");
   2768    is(
   2769      await runPython(
   2770        `range.MoveEndpointByUnit(TextPatternRangeEndpoint_Start, TextUnit_Character, 4)`
   2771      ),
   2772      4,
   2773      "MoveEndpointByUnit return correct"
   2774    );
   2775    is(await runPython(`range.GetText(-1)`), "cd ef ghi", "range text correct");
   2776 
   2777    info(
   2778      "Finding first font-weight 700 text range (range starts in middle of text attribute run)"
   2779    );
   2780    await runPython(`
   2781      global subrange
   2782      subrange = range.FindAttribute(UIA_FontWeightAttributeId, 700, False)
   2783    `);
   2784    is(await runPython(`subrange.GetText(-1)`), "cd ef", "range text correct");
   2785 
   2786    await runPython(`
   2787      global range
   2788      range = docText.RangeFromChild(fontWeightContainerAcc)
   2789    `);
   2790    is(
   2791      await runPython(`range.GetText(-1)`),
   2792      "a bcd ef ghi",
   2793      "range text correct"
   2794    );
   2795    is(
   2796      await runPython(
   2797        `range.MoveEndpointByUnit(TextPatternRangeEndpoint_End, TextUnit_Character, -5)`
   2798      ),
   2799      -5,
   2800      "MoveEndpointByUnit return correct"
   2801    );
   2802    is(await runPython(`range.GetText(-1)`), "a bcd e", "range text correct");
   2803 
   2804    info(
   2805      "Finding last font-weight 700 text range (range ends in middle of text attribute run)"
   2806    );
   2807    await runPython(`
   2808      global subrange
   2809      subrange = range.FindAttribute(UIA_FontWeightAttributeId, 700, True)
   2810    `);
   2811    is(await runPython(`subrange.GetText(-1)`), "bcd e", "range text correct");
   2812 
   2813    info("Collapsing range at start");
   2814    await runPython(`
   2815      global subrange
   2816      subrange = range.Clone()
   2817      subrange.MoveEndpointByRange(TextPatternRangeEndpoint_End, subrange, TextPatternRangeEndpoint_Start)
   2818    `);
   2819    is(await runPython(`subrange.GetText(-1)`), "", "subrange text correct");
   2820    info("Finding last font-weight 400 text range on collapsed range");
   2821    await runPython(`
   2822      global subrange
   2823      subrange = subrange.FindAttribute(UIA_FontWeightAttributeId, 400, True)
   2824    `);
   2825    is(await runPython(`subrange.GetText(-1)`), "", "subrange text correct");
   2826  },
   2827  { uiaEnabled: true, uiaDisabled: true }
   2828 );
   2829 
   2830 /**
   2831 * Test the Text pattern's GetVisibleRanges method.
   2832 */
   2833 addUiaTask(
   2834  `
   2835 <div id="div">
   2836  <p>line1</p>
   2837  <p><strong id="strong">line</strong>2</p>
   2838  <p style="position: absolute; left: -10000px; width: 1px;">line3</p>
   2839  <p>line4</p>
   2840 </div>
   2841 <!-- We use 0.5lh so the second line is definitely fully scrolled out.
   2842     With 1lh, it could be partially visible and thus included. -->
   2843 <textarea id="textarea" style="height: 0.5lh;">line5
   2844 line6
   2845 line7</textarea>
   2846 <hr aria-hidden="true" style="height: 100vh;">
   2847 <p>line8</p>
   2848  `,
   2849  async function testTextGetVisibleRanges() {
   2850    await runPython(`
   2851      global doc, docText, ranges
   2852      doc = getDocUia()
   2853      docText = getUiaPattern(doc, "Text")
   2854      ranges = docText.GetVisibleRanges()
   2855    `);
   2856    // XXX This should be 4 once we fix the scrolling case below.
   2857    is(
   2858      await runPython(`ranges.Length`),
   2859      6,
   2860      "doc has correct number of visible ranges"
   2861    );
   2862    is(
   2863      await runPython(`ranges.GetElement(0).GetText(-1)`),
   2864      "line1",
   2865      "range 0 text correct"
   2866    );
   2867    is(
   2868      await runPython(`ranges.GetElement(1).GetText(-1)`),
   2869      "line2",
   2870      "range 1 text correct"
   2871    );
   2872    // line3 is off-screen and thus not visible.
   2873    is(
   2874      await runPython(`ranges.GetElement(2).GetText(-1)`),
   2875      "line4",
   2876      "range 2 text correct"
   2877    );
   2878    is(
   2879      await runPython(`ranges.GetElement(3).GetText(-1)`),
   2880      "line5\n",
   2881      "range 3 text correct"
   2882    );
   2883    // XXX line6 and line7 are scrolled off screen by the textarea, but we
   2884    // incorrectly return them for now (ranges 4 and 5).
   2885    // line8 is scrolled off screen by the document.
   2886 
   2887    await runPython(`
   2888      textarea = findUiaByDomId(doc, "textarea")
   2889      textareaText = getUiaPattern(textarea, "Text")
   2890      global ranges
   2891      ranges = textareaText.GetVisibleRanges()
   2892    `);
   2893    is(
   2894      await runPython(`ranges.Length`),
   2895      1,
   2896      "textarea has correct number of visible ranges"
   2897    );
   2898    is(
   2899      await runPython(`ranges.GetElement(0).GetText(-1)`),
   2900      "line5\n",
   2901      "range 0 text correct"
   2902    );
   2903    // line6 and line7 are scrolled off screen by the textarea.
   2904 
   2905    await runPython(`
   2906      strong = findUiaByDomId(doc, "strong")
   2907      strongLeaf = uiaClient.RawViewWalker.GetFirstChildElement(strong)
   2908      strongText = getUiaPattern(strongLeaf, "Text")
   2909      global ranges
   2910      ranges = strongText.GetVisibleRanges()
   2911    `);
   2912    is(
   2913      await runPython(`ranges.Length`),
   2914      1,
   2915      "strong leaf has correct number of visible ranges"
   2916    );
   2917    is(
   2918      await runPython(`ranges.GetElement(0).GetText(-1)`),
   2919      "line",
   2920      "range 0 text correct"
   2921    );
   2922  },
   2923  // The IA2 -> UIA proxy doesn't support GetVisibleRanges.
   2924  { uiaEnabled: true, uiaDisabled: false }
   2925 );
   2926 
   2927 /**
   2928 * Test the TextRange pattern's FindText method.
   2929 */
   2930 addUiaTask(
   2931  `
   2932 <div id="container"><b>abc</b>TEST<div id="inner">def</div>TEST<p>ghi</p></div>
   2933 <textarea id="textarea">This is a test.</textarea>
   2934  `,
   2935  async function testTextRangeFindText() {
   2936    info("container tests");
   2937    await runPython(`
   2938      global doc, docText, container, range
   2939      doc = getDocUia()
   2940      docText = getUiaPattern(doc, "Text")
   2941      container = findUiaByDomId(doc, "container")
   2942      range = docText.RangeFromChild(container)
   2943    `);
   2944    // The IA2 -> UIA bridge inserts a space at the end of the text.
   2945    if (gIsUiaEnabled) {
   2946      is(
   2947        await runPython(`range.GetText(-1)`),
   2948        `abcTESTdefTESTghi`,
   2949        "doc returned correct range for container"
   2950      );
   2951    }
   2952    info("Finding 'abc', searching from the start");
   2953    await runPython(`
   2954      global subrange
   2955      subrange = range.FindText("abc", False, False)
   2956      `);
   2957    is(await runPython(`subrange.GetText(-1)`), "abc", "range text correct");
   2958 
   2959    info("Finding 'abc', searching from the end");
   2960    await runPython(`
   2961      global subrange
   2962      subrange = range.FindText("abc", True, False)
   2963      `);
   2964    is(await runPython(`subrange.GetText(-1)`), "abc", "range text correct");
   2965 
   2966    info("Finding 'ghi', searching from the start");
   2967    await runPython(`
   2968      global subrange
   2969      subrange = range.FindText("ghi", False, False)
   2970      `);
   2971    is(await runPython(`subrange.GetText(-1)`), "ghi", "range text correct");
   2972 
   2973    info("Finding 'ghi', searching from the end");
   2974    await runPython(`
   2975      global subrange
   2976      subrange = range.FindText("ghi", True, False)
   2977      `);
   2978    is(await runPython(`subrange.GetText(-1)`), "ghi", "range text correct");
   2979 
   2980    info("Finding 'TEST', searching from the start");
   2981    await runPython(`
   2982      global subrange
   2983      subrange = range.FindText("TEST", False, False)
   2984      `);
   2985    is(await runPython(`subrange.GetText(-1)`), "TEST", "range text correct");
   2986    info("Finding 'TEST', searching from the end");
   2987    await runPython(`
   2988      global subrange2
   2989      subrange2 = range.FindText("TEST", True, False)
   2990      `);
   2991    is(await runPython(`subrange2.GetText(-1)`), "TEST", "range text correct");
   2992    ok(
   2993      !(await runPython(`subrange.compare(subrange2)`)),
   2994      "ranges are not equal"
   2995    );
   2996 
   2997    info("Finding 'test', searching from the start, case-sensitive");
   2998    await runPython(`
   2999      global subrange
   3000      subrange = range.FindText("test", False, False)
   3001      `);
   3002    ok(await runPython(`not subrange`), "range not found");
   3003    info("Finding 'test', searching from the start, case-insensitive");
   3004    await runPython(`
   3005      global subrange
   3006      subrange = range.FindText("test", False, True)
   3007      `);
   3008    is(await runPython(`subrange.GetText(-1)`), "TEST", "range text correct");
   3009 
   3010    info("textarea tests");
   3011    await runPython(`
   3012      global range
   3013      textarea = findUiaByDomId(doc, "textarea")
   3014      range = docText.RangeFromChild(textarea)
   3015    `);
   3016    is(
   3017      await runPython(`range.GetText(-1)`),
   3018      "This is a test.",
   3019      "doc returned correct range for textarea"
   3020    );
   3021 
   3022    info("Finding 'is', searching from the start");
   3023    await runPython(`
   3024      global subrange
   3025      subrange = range.FindText("is", False, False)
   3026      `);
   3027    is(await runPython(`subrange.GetText(-1)`), "is", "range text correct");
   3028    info("Expanding to word");
   3029    await runPython(`subrange.ExpandToEnclosingUnit(TextUnit_Word)`);
   3030    is(await runPython(`subrange.GetText(-1)`), "This ", "range text correct");
   3031 
   3032    info("Creating range for 'is a test.'");
   3033    await runPython(`
   3034      global partRange
   3035      partRange = range.Clone()
   3036      partRange.MoveEndpointByRange(TextPatternRangeEndpoint_Start, subrange, TextPatternRangeEndpoint_End)
   3037    `);
   3038    is(
   3039      await runPython(`partRange.GetText(-1)`),
   3040      "is a test.",
   3041      "range text correct"
   3042    );
   3043 
   3044    info("Finding 'is', searching forward in 'is a test.'");
   3045    await runPython(`
   3046      global subrange
   3047      subrange = partRange.FindText("is", False, False)
   3048    `);
   3049    is(await runPython(`subrange.GetText(-1)`), "is", "range text correct");
   3050    info("Expanding to word");
   3051    await runPython(`subrange.ExpandToEnclosingUnit(TextUnit_Word)`);
   3052    is(await runPython(`subrange.GetText(-1)`), "is ", "range text correct");
   3053  },
   3054  { uiaEnabled: true, uiaDisabled: true }
   3055 );
   3056 
   3057 const textChildSnippet = `
   3058 <p id="p">p</p>
   3059 <a id="a" href="/">a</a>
   3060 <img id="img" src="https://example.com/a11y/accessible/tests/mochitest/moz.png" alt="img">
   3061 <div id="textbox" contenteditable role="textbox">textboxLeaf
   3062  <p id="textboxP">textboxP</p>
   3063 </div>
   3064 `;
   3065 
   3066 /**
   3067 * Test the TextChild pattern's TextContainer property.
   3068 */
   3069 addUiaTask(textChildSnippet, async function testTextChildTextContainer() {
   3070  ok(
   3071    await runPython(`
   3072        global doc, p
   3073        doc = getDocUia()
   3074        p = findUiaByDomId(doc, "p")
   3075        tc = getUiaPattern(p, "TextChild")
   3076        return uiaClient.CompareElements(tc.TextContainer, doc)
   3077      `),
   3078    "p TextContainer is doc"
   3079  );
   3080  // The IA2 -> UIA proxy doesn't support the TextChild pattern on text
   3081  // leaves.
   3082  if (gIsUiaEnabled) {
   3083    ok(
   3084      await runPython(`
   3085        pLeaf = uiaClient.RawViewWalker.GetFirstChildElement(p)
   3086        tc = getUiaPattern(pLeaf, "TextChild")
   3087        return uiaClient.CompareElements(tc.TextContainer, doc)
   3088      `),
   3089      "p leaf TextContainer is doc"
   3090    );
   3091  }
   3092  ok(
   3093    await runPython(`
   3094        a = findUiaByDomId(doc, "a")
   3095        tc = getUiaPattern(a, "TextChild")
   3096        return uiaClient.CompareElements(tc.TextContainer, doc)
   3097      `),
   3098    "a TextContainer is doc"
   3099  );
   3100  ok(
   3101    await runPython(`
   3102        img = findUiaByDomId(doc, "img")
   3103        tc = getUiaPattern(img, "TextChild")
   3104        return uiaClient.CompareElements(tc.TextContainer, doc)
   3105      `),
   3106    "img TextContainer is doc"
   3107  );
   3108  ok(
   3109    await runPython(`
   3110        global textbox
   3111        textbox = findUiaByDomId(doc, "textbox")
   3112        tc = getUiaPattern(textbox, "TextChild")
   3113        return uiaClient.CompareElements(tc.TextContainer, doc)
   3114      `),
   3115    "textbox TextContainer is doc"
   3116  );
   3117  // The IA2 -> UIA proxy doesn't support the TextChild pattern on text
   3118  // leaves.
   3119  if (gIsUiaEnabled) {
   3120    ok(
   3121      await runPython(`
   3122        textboxLeaf = uiaClient.RawViewWalker.GetFirstChildElement(textbox)
   3123        tc = getUiaPattern(textboxLeaf, "TextChild")
   3124        return uiaClient.CompareElements(tc.TextContainer, textbox)
   3125      `),
   3126      "textbox leaf  TextContainer is textbox"
   3127    );
   3128  }
   3129  ok(
   3130    await runPython(`
   3131        textboxP = findUiaByDomId(doc, "textboxP")
   3132        tc = getUiaPattern(textboxP, "TextChild")
   3133        return uiaClient.CompareElements(tc.TextContainer, textbox)
   3134      `),
   3135    "textboxP TextContainer is textbox"
   3136  );
   3137 });
   3138 
   3139 /**
   3140 * Test the TextChild pattern's TextRange property.
   3141 */
   3142 addUiaTask(textChildSnippet, async function testTextChildTextRange() {
   3143  is(
   3144    await runPython(`
   3145        global doc, p
   3146        doc = getDocUia()
   3147        p = findUiaByDomId(doc, "p")
   3148        tc = getUiaPattern(p, "TextChild")
   3149        return tc.TextRange.GetText(-1)
   3150      `),
   3151    "p",
   3152    "p text correct"
   3153  );
   3154  // The IA2 -> UIA proxy doesn't support the TextChild pattern on text
   3155  // leaves.
   3156  if (gIsUiaEnabled) {
   3157    is(
   3158      await runPython(`
   3159        pLeaf = uiaClient.RawViewWalker.GetFirstChildElement(p)
   3160        tc = getUiaPattern(pLeaf, "TextChild")
   3161        return tc.TextRange.GetText(-1)
   3162      `),
   3163      "p",
   3164      "p leaf  text correct"
   3165    );
   3166  }
   3167  is(
   3168    await runPython(`
   3169        a = findUiaByDomId(doc, "a")
   3170        tc = getUiaPattern(a, "TextChild")
   3171        return tc.TextRange.GetText(-1)
   3172      `),
   3173    "a",
   3174    "a text correct"
   3175  );
   3176  if (gIsUiaEnabled) {
   3177    // The IA2 -> UIA proxy doesn't expose an embedded object character for
   3178    // images.
   3179    is(
   3180      await runPython(`
   3181        img = findUiaByDomId(doc, "img")
   3182        tc = getUiaPattern(img, "TextChild")
   3183        return tc.TextRange.GetText(-1)
   3184      `),
   3185      kEmbedChar,
   3186      "img text correct"
   3187    );
   3188    // The IA2 -> UIA proxy adds spaces between elements that don't exist.
   3189    is(
   3190      await runPython(`
   3191        global textbox
   3192        textbox = findUiaByDomId(doc, "textbox")
   3193        tc = getUiaPattern(textbox, "TextChild")
   3194        return tc.TextRange.GetText(-1)
   3195      `),
   3196      "textboxLeaf textboxP",
   3197      "textbox text correct"
   3198    );
   3199    // The IA2 -> UIA proxy doesn't support the TextChild pattern on text
   3200    // leaves.
   3201    is(
   3202      await runPython(`
   3203        textboxLeaf = uiaClient.RawViewWalker.GetFirstChildElement(textbox)
   3204        tc = getUiaPattern(textboxLeaf, "TextChild")
   3205        return tc.TextRange.GetText(-1)
   3206      `),
   3207      "textboxLeaf ",
   3208      "textbox leaf  text correct"
   3209    );
   3210  }
   3211  is(
   3212    await runPython(`
   3213        textboxP = findUiaByDomId(doc, "textboxP")
   3214        tc = getUiaPattern(textboxP, "TextChild")
   3215        return tc.TextRange.GetText(-1)
   3216      `),
   3217    "textboxP",
   3218    "textboxP text correct"
   3219  );
   3220 });