/* This Source Code Form is subject to the terms of the Mozilla Public /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ "use strict"; /* import-globals-from ../../../mochitest/text.js */ loadScripts({ name: "text.js", dir: MOCHITESTS_DIR }); /* eslint-disable camelcase */ const SupportedTextSelection_None = 0; const SupportedTextSelection_Multiple = 2; /* eslint-enable camelcase */ /** * Test the Text pattern's DocumentRange property. This also tests where the * Text pattern is exposed. */ addUiaTask( `

content

editable

p

link `, async function testTextDocumentRange() { await definePyVar("doc", `getDocUia()`); await definePyVar("pattern", `getUiaPattern(doc, "Text")`); ok(await runPython(`bool(pattern)`), "doc has Text pattern"); // The IA2 -> UIA proxy adds spaces between elements that don't exist. if (gIsUiaEnabled) { is( await runPython(`pattern.DocumentRange.GetText(-1)`), "inputtextareacontenteditableplink", "document DocumentRange Text correct" ); } await assignPyVarToUiaWithId("input"); await definePyVar("pattern", `getUiaPattern(input, "Text")`); ok(await runPython(`bool(pattern)`), "input has Text pattern"); is( await runPython(`pattern.DocumentRange.GetText(-1)`), "input", "input DocumentRange Text correct" ); await assignPyVarToUiaWithId("textarea"); await definePyVar("pattern", `getUiaPattern(textarea, "Text")`); ok(await runPython(`bool(pattern)`), "textarea has Text pattern"); is( await runPython(`pattern.DocumentRange.GetText(-1)`), "textarea", "textarea DocumentRange Text correct" ); // The IA2 -> UIA proxy doesn't expose the Text pattern on contentEditables // without role="textbox". if (gIsUiaEnabled) { await assignPyVarToUiaWithId("contentEditable"); await definePyVar("pattern", `getUiaPattern(contentEditable, "Text")`); ok(await runPython(`bool(pattern)`), "contentEditable has Text pattern"); is( await runPython(`pattern.DocumentRange.GetText(-1)`), "contenteditable", "contentEditable DocumentRange Text correct" ); } await testPatternAbsent("p", "Text"); // The IA2 -> UIA proxy doesn't expose the Text pattern on this text leaf. if (gIsUiaEnabled) { await runPython(` global pLeaf p = findUiaByDomId(doc, "p") pLeaf = uiaClient.RawViewWalker.GetFirstChildElement(p) `); await definePyVar("pattern", `getUiaPattern(pLeaf, "Text")`); ok(await runPython(`bool(pattern)`), "pLeaf has Text pattern"); is( await runPython(`pattern.DocumentRange.GetText(-1)`), "p", "pLeaf DocumentRange Text correct" ); } await testPatternAbsent("link", "Text"); // The IA2 -> UIA proxy doesn't expose this text leaf at all. if (gIsUiaEnabled) { await runPython(` global linkLeaf link = findUiaByDomId(doc, "link") linkLeaf = uiaClient.RawViewWalker.GetFirstChildElement(link) `); await definePyVar("pattern", `getUiaPattern(linkLeaf, "Text")`); ok(await runPython(`bool(pattern)`), "linkLeaf has Text pattern"); is( await runPython(`pattern.DocumentRange.GetText(-1)`), "link", "linkLeaf DocumentRange Text correct" ); } } ); /** * Test the TextRange pattern's GetText method. */ addUiaTask( `
a b`, async function testTextRangeGetText() { await runPython(` doc = getDocUia() editable = findUiaByDomId(doc, "editable") text = getUiaPattern(editable, "Text") global range range = text.DocumentRange `); is(await runPython(`range.GetText(-1)`), "a b", "GetText(-1) correct"); is(await runPython(`range.GetText(0)`), "", "GetText(0) correct"); is(await runPython(`range.GetText(1)`), "a", "GetText(1) correct"); is(await runPython(`range.GetText(2)`), "a ", "GetText(2) correct"); is(await runPython(`range.GetText(3)`), "a b", "GetText(3) correct"); is(await runPython(`range.GetText(4)`), "a b", "GetText(4) correct"); } ); /** * Test the TextRange pattern's Clone method. */ addUiaTask( ``, async function testTextRangeClone() { await runPython(` doc = getDocUia() input = findUiaByDomId(doc, "input") text = getUiaPattern(input, "Text") global origRange origRange = text.DocumentRange `); is( await runPython(`origRange.GetText(-1)`), "testing", "origRange text correct" ); await runPython(` global clonedRange clonedRange = origRange.Clone() `); is( await runPython(`clonedRange.GetText(-1)`), "testing", "clonedRange text correct" ); // Test that modifying clonedRange doesn't impact origRange. info("Collapsing clonedRange to start"); await runPython( `clonedRange.MoveEndpointByRange(TextPatternRangeEndpoint_End, clonedRange, TextPatternRangeEndpoint_Start)` ); is( await runPython(`clonedRange.GetText(-1)`), "", "clonedRange text correct" ); is( await runPython(`origRange.GetText(-1)`), "testing", "origRange text correct" ); } ); /** * Test the TextRange pattern's Compare method. */ addUiaTask( ``, async function testTextRangeCompare() { await runPython(` doc = getDocUia() input = findUiaByDomId(doc, "input") text = getUiaPattern(input, "Text") global range1, range2 range1 = text.DocumentRange range2 = text.DocumentRange `); ok( await runPython(`range1.Compare(range2)`), "range1 Compare range2 correct" ); ok( await runPython(`range2.Compare(range1)`), "range2 Compare range1 correct" ); info("Collapsing range2 to start"); await runPython( `range2.MoveEndpointByRange(TextPatternRangeEndpoint_End, range2, TextPatternRangeEndpoint_Start)` ); ok( !(await runPython(`range1.Compare(range2)`)), "range1 Compare range2 correct" ); ok( !(await runPython(`range2.Compare(range1)`)), "range2 Compare range1 correct" ); } ); /** * Test the TextRange pattern's CompareEndpoints method. */ addUiaTask( `

before

after

`, async function testTextRangeCompareEndpoints() { await runPython(` global doc, range1, range2 doc = getDocUia() input = findUiaByDomId(doc, "input") text = getUiaPattern(input, "Text") range1 = text.DocumentRange range2 = text.DocumentRange `); is( await runPython( `range1.CompareEndpoints(TextPatternRangeEndpoint_Start, range1, TextPatternRangeEndpoint_Start)` ), 0, "Compare range1 start to range1 start correct" ); is( await runPython( `range1.CompareEndpoints(TextPatternRangeEndpoint_End, range1, TextPatternRangeEndpoint_End)` ), 0, "Compare range1 end to range1 end correct" ); is( await runPython( `range1.CompareEndpoints(TextPatternRangeEndpoint_Start, range1, TextPatternRangeEndpoint_End)` ), -1, "Compare range1 start to range1 end correct" ); is( await runPython( `range1.CompareEndpoints(TextPatternRangeEndpoint_End, range1, TextPatternRangeEndpoint_Start)` ), 1, "Compare range1 end to range1 start correct" ); // Compare different ranges. is( await runPython( `range1.CompareEndpoints(TextPatternRangeEndpoint_Start, range2, TextPatternRangeEndpoint_Start)` ), 0, "Compare range1 start to range2 start correct" ); is( await runPython( `range1.CompareEndpoints(TextPatternRangeEndpoint_End, range2, TextPatternRangeEndpoint_End)` ), 0, "Compare range1 end to range2 end correct" ); is( await runPython( `range1.CompareEndpoints(TextPatternRangeEndpoint_Start, range2, TextPatternRangeEndpoint_End)` ), -1, "Compare range1 start to range2 end correct" ); is( await runPython( `range1.CompareEndpoints(TextPatternRangeEndpoint_End, range2, TextPatternRangeEndpoint_Start)` ), 1, "Compare range1 end to range2 start correct" ); // Compare ranges created using different elements. await definePyVar("range3", `getUiaPattern(doc, "Text").DocumentRange`); is( await runPython( `range1.CompareEndpoints(TextPatternRangeEndpoint_Start, range3, TextPatternRangeEndpoint_Start)` ), 1, "Compare range1 start to range3 start correct" ); is( await runPython( `range1.CompareEndpoints(TextPatternRangeEndpoint_End, range3, TextPatternRangeEndpoint_End)` ), -1, "Compare range1 end to range3 end correct" ); } ); /** * Test the TextRange pattern's ExpandToEnclosingUnit method. */ addUiaTask( `

before

after
`, async function testTextRangeExpandToEnclosingUnit() { info("Getting DocumentRange from textarea"); await runPython(` global doc, range doc = getDocUia() textarea = findUiaByDomId(doc, "textarea") text = getUiaPattern(textarea, "Text") range = text.DocumentRange `); is( await runPython(`range.GetText(-1)`), "ab cd ef gh", "range text correct" ); // Expand should shrink the range because it's too big. info("Expanding to character"); await runPython(`range.ExpandToEnclosingUnit(TextUnit_Character)`); is(await runPython(`range.GetText(-1)`), "a", "range text correct"); info("Collapsing to end"); await runPython( `range.MoveEndpointByRange(TextPatternRangeEndpoint_Start, range, TextPatternRangeEndpoint_End)` ); is(await runPython(`range.GetText(-1)`), "", "range text correct"); // range is now collapsed at "b". info("Expanding to character"); await runPython(`range.ExpandToEnclosingUnit(TextUnit_Character)`); is(await runPython(`range.GetText(-1)`), "b", "range text correct"); info("Expanding to word"); await runPython(`range.ExpandToEnclosingUnit(TextUnit_Word)`); is(await runPython(`range.GetText(-1)`), "ab ", "range text correct"); info("Collapsing to end"); await runPython( `range.MoveEndpointByRange(TextPatternRangeEndpoint_Start, range, TextPatternRangeEndpoint_End)` ); // range is now collapsed at "c". info("Expanding to word"); await runPython(`range.ExpandToEnclosingUnit(TextUnit_Word)`); is(await runPython(`range.GetText(-1)`), "cd ", "range text correct"); info("Expanding to line"); await runPython(`range.ExpandToEnclosingUnit(TextUnit_Line)`); is(await runPython(`range.GetText(-1)`), "ab cd ", "range text correct"); info("Collapsing to end"); await runPython( `range.MoveEndpointByRange(TextPatternRangeEndpoint_Start, range, TextPatternRangeEndpoint_End)` ); // range is now collapsed at "e". info("Expanding to line"); await runPython(`range.ExpandToEnclosingUnit(TextUnit_Line)`); // The IA2 -> UIA proxy gets most things below this wrong. if (!gIsUiaEnabled) { return; } is(await runPython(`range.GetText(-1)`), "ef gh", "range text correct"); info("Expanding to document"); await runPython(`range.ExpandToEnclosingUnit(TextUnit_Document)`); is( await runPython(`range.GetText(-1)`), "beforeab cd ef ghafter input", "range text correct" ); // Test expanding to a line which crosses elements. info("Getting DocumentRange from input"); await runPython(` input = findUiaByDomId(doc, "input") text = getUiaPattern(input, "Text") global range range = text.DocumentRange `); info("Expanding to line"); await runPython(`range.ExpandToEnclosingUnit(TextUnit_Line)`); is( await runPython(`range.GetText(-1)`), "after input", "range text correct" ); info("Collapsing to end"); await runPython( `range.MoveEndpointByRange(TextPatternRangeEndpoint_Start, range, TextPatternRangeEndpoint_End)` ); // range is now collapsed at the end of the document. info("Expanding to line"); await runPython(`range.ExpandToEnclosingUnit(TextUnit_Line)`); is( await runPython(`range.GetText(-1)`), "after input", "range text correct" ); } ); /** * Test the Format TextUnit. Exercises ExpandToEnclosingUnit, Move, and * MoveEndpointByUnit. Tested here separately since the setup and implementation * is somewhat different from other TextUnits. */ addUiaTask( `
a bcd ef
a bcd ef
`, async function testTextRangeMove(browser, docAcc) { info("Constructing range on bold text run"); await runPython(` global doc, docText, range doc = getDocUia() docText = getUiaPattern(doc, "Text") boldContainerAcc = findUiaByDomId(doc, "bold-container") range = docText.RangeFromChild(boldContainerAcc) `); is(await runPython(`range.GetText(-1)`), "a bcd ef", "range text correct"); info("Moving to bold text run"); is( await runPython(`range.Move(TextUnit_Format, 1)`), 1, "Move return correct" ); is(await runPython(`range.GetText(-1)`), "bcd", "range text correct"); // Testing ExpandToEnclosingUnit (on formatting boundaries) info("Expanding to character (shrinking the range)"); await runPython(`range.ExpandToEnclosingUnit(TextUnit_Character)`); is(await runPython(`range.GetText(-1)`), "b", "range text correct"); info("Expanding to Format"); await runPython(`range.ExpandToEnclosingUnit(TextUnit_Format)`); is(await runPython(`range.GetText(-1)`), "bcd", "range text correct"); info("Making range larger than the Format unit"); is( await runPython( `range.MoveEndpointByUnit(TextPatternRangeEndpoint_End, TextUnit_Character, 1)` ), 1, "MoveEndpointByUnit return correct" ); is(await runPython(`range.GetText(-1)`), "bcd ", "range text correct"); info("Expanding to Format (shrinking the range)"); await runPython(`range.ExpandToEnclosingUnit(TextUnit_Format)`); is(await runPython(`range.GetText(-1)`), "bcd", "range text correct"); // Testing Move (on formatting boundaries) info("Moving 1 Format unit"); is( await runPython(`range.Move(TextUnit_Format, 1)`), 1, "Move return correct" ); is(await runPython(`range.GetText(-1)`), " ef", "range text correct"); info("Moving -3 Format units (but only -2 are left)"); is( await runPython(`range.Move(TextUnit_Format, -3)`), -2, "Move return correct" ); is(await runPython(`range.GetText(-1)`), "a ", "range text correct"); // Testing MoveEndpointByUnit (on formatting boundaries) info("Moving end 1 Format unit"); is( await runPython( `range.MoveEndpointByUnit(TextPatternRangeEndpoint_End, TextUnit_Format, 1)` ), 1, "MoveEndpointByUnit return correct" ); is(await runPython(`range.GetText(-1)`), "a bcd", "range text correct"); info("Moving start 1 Format unit"); is( await runPython( `range.MoveEndpointByUnit(TextPatternRangeEndpoint_Start, TextUnit_Format, 1)` ), 1, "MoveEndpointByUnit return correct" ); is(await runPython(`range.GetText(-1)`), "bcd", "range text correct"); // Testing above three methods on text runs defined by container boundaries info("Constructing range on text run defined by container boundaries"); await runPython(` global doc, docText, range containerContainer = findUiaByDomId(doc, "container-container") range = docText.RangeFromChild(containerContainer) `); is(await runPython(`range.GetText(-1)`), "a bcd ef", "range text correct"); info("Expanding to Format"); await runPython(`range.ExpandToEnclosingUnit(TextUnit_Format)`); is(await runPython(`range.GetText(-1)`), "a ", "range text correct"); info("Moving 1 Format unit"); is( await runPython(`range.Move(TextUnit_Format, 1)`), 1, "Move return correct" ); is(await runPython(`range.GetText(-1)`), "bcd", "range text correct"); info("Moving start -1 Format unit"); is( await runPython( `range.MoveEndpointByUnit(TextPatternRangeEndpoint_Start, TextUnit_Format, -1)` ), -1, "MoveEndpointByUnit return correct" ); is(await runPython(`range.GetText(-1)`), "a bcd", "range text correct"); // Trigger spelling errors so we can test text offset attributes const textarea = findAccessibleChildByID(docAcc, "textarea"); textarea.takeFocus(); await waitForEvent(EVENT_TEXT_ATTRIBUTE_CHANGED); // Testing above three methods on text offset attributes info("Constructing range on italic text run"); await runPython(` global doc, docText, range textarea = findUiaByDomId(doc, "textarea") range = docText.RangeFromChild(textarea) `); is( await runPython(`range.GetText(-1)`), "test tset test", "range text correct" ); info("Expanding to Format"); await runPython(`range.ExpandToEnclosingUnit(TextUnit_Format)`); is(await runPython(`range.GetText(-1)`), "test ", "range text correct"); info("Moving 1 Format unit"); is( await runPython(`range.Move(TextUnit_Format, 1)`), 1, "Move return correct" ); is(await runPython(`range.GetText(-1)`), "tset", "range text correct"); info("Moving start -1 Format unit"); is( await runPython( `range.MoveEndpointByUnit(TextPatternRangeEndpoint_Start, TextUnit_Format, -1)` ), -1, "MoveEndpointByUnit return correct" ); is(await runPython(`range.GetText(-1)`), "test tset", "range text correct"); } ); /** * Test the GetAttributeValue method. Verify the behavior of various UIA * Attribute IDs. */ addUiaTask( `
a bcd ef
a bcd ef
a bcd ef
a bcd ef
a bcd ef
a bcd ef
a bcd ef
a bcd ef
a bcd ef
a bcd ef
a bcd ef
a highlighted phrase ef
ab

h3

cd
ab
quote
cd
abemphcd
`, async function testTextRangeGetAttributeValue() { // ================== UIA_FontWeightAttributeId ================== info("Constructing range on bold text run"); await runPython(` global doc, docText, range doc = getDocUia() docText = getUiaPattern(doc, "Text") fontWeightContainerAcc = findUiaByDomId(doc, "font-weight-container") range = docText.RangeFromChild(fontWeightContainerAcc) `); is(await runPython(`range.GetText(-1)`), "a bcd ef", "range text correct"); info("checking mixed font weights"); ok( await runPython(` val = range.GetAttributeValue(UIA_FontWeightAttributeId) return val == uiaClient.ReservedMixedAttributeValue `), "FontWeight correct (mixed)" ); info("Moving to bold text run"); is( await runPython(`range.Move(TextUnit_Format, 1)`), 1, "Move return correct" ); is(await runPython(`range.GetText(-1)`), "bcd", "range text correct"); info("checking FontWeight"); is( await runPython(`range.GetAttributeValue(UIA_FontWeightAttributeId)`), 700, "FontWeight correct" ); info("Moving end 1 Format unit"); is( await runPython( `range.MoveEndpointByUnit(TextPatternRangeEndpoint_End, TextUnit_Format, 1)` ), 1, "MoveEndpointByUnit return correct" ); is(await runPython(`range.GetText(-1)`), "bcd ef", "range text correct"); info( "checking font weight (across equivalent container-separated Format runs)" ); is( await runPython(`range.GetAttributeValue(UIA_FontWeightAttributeId)`), 700, "FontWeight correct" ); // ================== UIA_FontSizeAttributeId ================== await runPython(` global range fontSizeContainerAcc = findUiaByDomId(doc, "font-size-container") range = docText.RangeFromChild(fontSizeContainerAcc) `); is(await runPython(`range.GetText(-1)`), "a bcd ef", "range text correct"); info("checking mixed font weights"); ok( await runPython(` val = range.GetAttributeValue(UIA_FontSizeAttributeId) return val == uiaClient.ReservedMixedAttributeValue `), "FontSize correct (mixed)" ); info("Moving to increased font-size text run"); is( await runPython(`range.Move(TextUnit_Format, 1)`), 1, "Move return correct" ); is(await runPython(`range.GetText(-1)`), "bcd", "range text correct"); info("checking FontSize"); is( await runPython(`range.GetAttributeValue(UIA_FontSizeAttributeId)`), 15, "FontSize correct" ); // ================== UIA_FontNameAttributeId ================== await runPython(` global range fontFamilyContainerAcc = findUiaByDomId(doc, "font-family-container") range = docText.RangeFromChild(fontFamilyContainerAcc) `); is(await runPython(`range.GetText(-1)`), "a bcd ef", "range text correct"); info("checking mixed font families"); ok( await runPython(` val = range.GetAttributeValue(UIA_FontNameAttributeId) return val == uiaClient.ReservedMixedAttributeValue `), "FontName correct (mixed)" ); info("Moving to sans-serif font-family text run"); is( await runPython(`range.Move(TextUnit_Format, 1)`), 1, "Move return correct" ); is(await runPython(`range.GetText(-1)`), "bcd", "range text correct"); info("checking FontName"); is( await runPython(`range.GetAttributeValue(UIA_FontNameAttributeId)`), "Arial", "FontName correct" ); // ================== UIA_IsItalicAttributeId ================== await runPython(` global range italicContainerAcc = findUiaByDomId(doc, "italic-container") range = docText.RangeFromChild(italicContainerAcc) `); is(await runPython(`range.GetText(-1)`), "a bcd ef", "range text correct"); info("checking mixed IsItalic properties"); ok( await runPython(` val = range.GetAttributeValue(UIA_IsItalicAttributeId) return val == uiaClient.ReservedMixedAttributeValue `), "IsItalic correct (mixed)" ); info("Moving to italic text run"); is( await runPython(`range.Move(TextUnit_Format, 1)`), 1, "Move return correct" ); is(await runPython(`range.GetText(-1)`), "bcd", "range text correct"); info("checking IsItalic"); is( await runPython(`range.GetAttributeValue(UIA_IsItalicAttributeId)`), true, "IsItalic correct" ); // ================== UIA_IsSubscriptAttributeId ================== await runPython(` global range subscriptContainerAcc = findUiaByDomId(doc, "subscript-container") range = docText.RangeFromChild(subscriptContainerAcc) `); is(await runPython(`range.GetText(-1)`), "a bcd ef", "range text correct"); info("checking mixed IsSubscript properties"); ok( await runPython(` val = range.GetAttributeValue(UIA_IsSubscriptAttributeId) return val == uiaClient.ReservedMixedAttributeValue `), "IsSubscript correct (mixed)" ); info("Moving to subscript text run"); is( await runPython(`range.Move(TextUnit_Format, 1)`), 1, "Move return correct" ); is(await runPython(`range.GetText(-1)`), "bcd", "range text correct"); info("checking IsSubscript"); is( await runPython(`range.GetAttributeValue(UIA_IsSubscriptAttributeId)`), true, "IsSubscript correct" ); // ================== UIA_IsSuperscriptAttributeId ================== await runPython(` global range superscriptContainerAcc = findUiaByDomId(doc, "superscript-container") range = docText.RangeFromChild(superscriptContainerAcc) `); is(await runPython(`range.GetText(-1)`), "a bcd ef", "range text correct"); info("checking mixed IsSuperscript properties"); ok( await runPython(` val = range.GetAttributeValue(UIA_IsSuperscriptAttributeId) return val == uiaClient.ReservedMixedAttributeValue `), "IsSuperscript correct (mixed)" ); info("Moving to superscript text run"); is( await runPython(`range.Move(TextUnit_Format, 1)`), 1, "Move return correct" ); is(await runPython(`range.GetText(-1)`), "bcd", "range text correct"); info("checking IsSuperscript"); is( await runPython(`range.GetAttributeValue(UIA_IsSuperscriptAttributeId)`), true, "IsSuperscript correct" ); // ================== UIA_IsHiddenAttributeId ================== // Testing the "true" case is not really possible since these Accessible // nodes are not present in the tree. Verify the "false" case. await runPython(` global range notHiddenContainerAcc = findUiaByDomId(doc, "not-hidden-container") range = docText.RangeFromChild(notHiddenContainerAcc) `); is(await runPython(`range.GetText(-1)`), "a bcd ef", "range text correct"); info("checking mixed IsHidden properties"); ok( await runPython(` val = range.GetAttributeValue(UIA_IsHiddenAttributeId) return val != uiaClient.ReservedMixedAttributeValue `), "IsHidden correct (not mixed)" ); // ================== UIA_IsReadOnlyAttributeId ================== await runPython(` global range readonlyContainerAcc = findUiaByDomId(doc, "readonly-container") range = docText.RangeFromChild(readonlyContainerAcc) `); is(await runPython(`range.GetText(-1)`), "a bcd ef", "range text correct"); info("checking mixed ReadOnly properties"); ok( await runPython(` val = range.GetAttributeValue(UIA_IsReadOnlyAttributeId) return val == uiaClient.ReservedMixedAttributeValue `), "ReadOnly correct (mixed)" ); info("Moving to editable text run"); is( await runPython(`range.Move(TextUnit_Format, 1)`), 1, "Move return correct" ); is(await runPython(`range.GetText(-1)`), "bcd", "range text correct"); info("checking IsReadOnly"); is( await runPython(`range.GetAttributeValue(UIA_IsReadOnlyAttributeId)`), false, "IsReadOnly correct" ); // Verify that text inputs are not read-only by default. await runPython(` global range textInputAcc = findUiaByDomId(doc, "text-input") range = docText.RangeFromChild(textInputAcc) `); info("checking IsReadOnly"); is( await runPython(`range.GetAttributeValue(UIA_IsReadOnlyAttributeId)`), false, "IsReadOnly correct for text input" ); // ================== UIA_AnnotationTypesAttributeId - AnnotationType_SpellingError ================== await runPython(` global range spellingErrorContainerAcc = findUiaByDomId(doc, "spelling-error-container") range = docText.RangeFromChild(spellingErrorContainerAcc) `); is(await runPython(`range.GetText(-1)`), "a bcd ef", "range text correct"); info("checking mixed SpellingError properties"); ok( await runPython(` val = range.GetAttributeValue(UIA_AnnotationTypesAttributeId) return val == uiaClient.ReservedMixedAttributeValue `), "SpellingError correct (mixed)" ); info('Moving to aria-invalid="spelling" text run'); is( await runPython(`range.Move(TextUnit_Format, 1)`), 1, "Move return correct" ); is(await runPython(`range.GetText(-1)`), "bcd", "range text correct"); info("checking SpellingError"); ok( await runPython(` annotations = range.GetAttributeValue(UIA_AnnotationTypesAttributeId) return annotations == (AnnotationType_SpellingError,) `), "SpellingError correct" ); // ================== UIA_AnnotationTypesAttributeId - AnnotationType_GrammarError ================== await runPython(` global range grammarErrorContainerAcc = findUiaByDomId(doc, "grammar-error-container") range = docText.RangeFromChild(grammarErrorContainerAcc) `); is(await runPython(`range.GetText(-1)`), "a bcd ef", "range text correct"); info("checking mixed GrammarError properties"); ok( await runPython(` val = range.GetAttributeValue(UIA_AnnotationTypesAttributeId) return val == uiaClient.ReservedMixedAttributeValue `), "GrammarError correct (mixed)" ); info('Moving to aria-invalid="grammar" text run'); is( await runPython(`range.Move(TextUnit_Format, 1)`), 1, "Move return correct" ); is(await runPython(`range.GetText(-1)`), "bcd", "range text correct"); info("checking GrammarError"); ok( await runPython(` annotations = range.GetAttributeValue(UIA_AnnotationTypesAttributeId) return annotations == (AnnotationType_GrammarError,) `), "GrammarError correct" ); // ================== UIA_AnnotationTypesAttributeId - AnnotationType_DataValidationError ================== // The IA2 -> UIA bridge does not work for aria-invalid=true or highlights. if (gIsUiaEnabled) { await runPython(` global range dataValidationErrorContainerAcc = findUiaByDomId(doc, "data-validation-error-container") range = docText.RangeFromChild(dataValidationErrorContainerAcc) `); is( await runPython(`range.GetText(-1)`), "a bcd ef", "range text correct" ); info("checking mixed DataValidationError properties"); ok( await runPython(` val = range.GetAttributeValue(UIA_AnnotationTypesAttributeId) return val == uiaClient.ReservedMixedAttributeValue `), "DataValidationError correct (mixed)" ); info('Moving to aria-invalid="true" text run'); is( await runPython(`range.Move(TextUnit_Format, 1)`), 1, "Move return correct" ); is(await runPython(`range.GetText(-1)`), "bcd", "range text correct"); info("checking DataValidationError"); ok( await runPython(` annotations = range.GetAttributeValue(UIA_AnnotationTypesAttributeId) return annotations == (AnnotationType_DataValidationError,) `), "DataValidationError correct" ); // ================== UIA_AnnotationTypesAttributeId - AnnotationType_Highlighted ================== await runPython(` global range highlightContainerAcc = findUiaByDomId(doc, "highlight-container") range = docText.RangeFromChild(highlightContainerAcc) `); is( await runPython(`range.GetText(-1)`), "a highlighted phrase ef", "range text correct" ); info("checking mixed Highlighted properties"); ok( await runPython(` val = range.GetAttributeValue(UIA_AnnotationTypesAttributeId) return val == uiaClient.ReservedMixedAttributeValue `), "Highlighted correct (mixed)" ); info("Moving to highlighted text run"); is( await runPython(`range.Move(TextUnit_Format, 1)`), 1, "Move return correct" ); is( await runPython(`range.GetText(-1)`), "highlighted phrase", "range text correct" ); info("checking Highlighted"); ok( await runPython(` annotations = range.GetAttributeValue(UIA_AnnotationTypesAttributeId) return annotations == (AnnotationType_Highlighted,) `), "Highlighted correct" ); } // The IA2 -> UIA bridge does not work correctly here. if (gIsUiaEnabled) { // ================== UIA_StyleIdAttributeId - StyleId_Heading* ================== await runPython(` global range headingContainerAcc = findUiaByDomId(doc, "heading-container") range = docText.RangeFromChild(headingContainerAcc) `); is(await runPython(`range.GetText(-1)`), "abh3cd", "range text correct"); info("checking mixed StyleId properties"); ok( await runPython(` val = range.GetAttributeValue(UIA_StyleIdAttributeId) return val == uiaClient.ReservedMixedAttributeValue `), "StyleId correct (mixed)" ); info("Moving to h3 text run"); is( await runPython(`range.Move(TextUnit_Format, 1)`), 1, "Move return correct" ); is(await runPython(`range.GetText(-1)`), "h3", "range text correct"); info("checking StyleId"); ok( await runPython(` styleId = range.GetAttributeValue(UIA_StyleIdAttributeId) return styleId == StyleId_Heading3 `), "StyleId correct" ); // ================== UIA_StyleIdAttributeId - StyleId_Quote ================== await runPython(` global range blockquoteContainerAcc = findUiaByDomId(doc, "blockquote-container") range = docText.RangeFromChild(blockquoteContainerAcc) `); is( await runPython(`range.GetText(-1)`), "abquotecd", "range text correct" ); info("checking mixed StyleId properties"); ok( await runPython(` val = range.GetAttributeValue(UIA_StyleIdAttributeId) return val == uiaClient.ReservedMixedAttributeValue `), "StyleId correct (mixed)" ); info("Moving to blockquote text run"); is( await runPython(`range.Move(TextUnit_Format, 1)`), 1, "Move return correct" ); is(await runPython(`range.GetText(-1)`), "quote", "range text correct"); info("checking StyleId"); ok( await runPython(` styleId = range.GetAttributeValue(UIA_StyleIdAttributeId) return styleId == StyleId_Quote `), "StyleId correct" ); // ================== UIA_StyleIdAttributeId - StyleId_Emphasis ================== await runPython(` global range emphasisContainerAcc = findUiaByDomId(doc, "emphasis-container") range = docText.RangeFromChild(emphasisContainerAcc) `); is( await runPython(`range.GetText(-1)`), "abemphcd", "range text correct" ); info("checking mixed StyleId properties"); ok( await runPython(` val = range.GetAttributeValue(UIA_StyleIdAttributeId) return val == uiaClient.ReservedMixedAttributeValue `), "StyleId correct (mixed)" ); info("Moving to emphasized text run"); is( await runPython(`range.Move(TextUnit_Format, 1)`), 1, "Move return correct" ); is(await runPython(`range.GetText(-1)`), "emph", "range text correct"); info("checking StyleId"); ok( await runPython(` styleId = range.GetAttributeValue(UIA_StyleIdAttributeId) return styleId == StyleId_Emphasis `), "StyleId correct" ); } }, { urlSuffix: "#:~:text=highlighted%20phrase" } ); /** * Test the GetAttributeValue method when backspacing a character at the end of * a document. */ addUiaTask( `a`, async function testTextRangeGetAttributeValueBackspaceAtDocEnd( browser, docAcc ) { const input = findAccessibleChildByID(docAcc, "input"); info("Focusing input"); let moved = waitForEvent(EVENT_TEXT_CARET_MOVED, input); input.takeFocus(); await moved; info("Pressing end"); moved = waitForEvent(EVENT_TEXT_CARET_MOVED, input); EventUtils.synthesizeKey("KEY_End"); await moved; await runPython(` global doc, range doc = getDocUia() input = findUiaByDomId(doc, "input") text = getUiaPattern(input, "Text") range = text.GetSelection().GetElement(0) `); // `range` is collapsed at the end of the input, after "c". is( await runPython(`range.GetAttributeValue(UIA_IsReadOnlyAttributeId)`), false, "IsReadOnly correct" ); info("Backspacing c"); moved = waitForEvent(EVENT_TEXT_CARET_MOVED, input); EventUtils.synthesizeKey("KEY_Backspace"); await moved; is( await runPython(`range.GetAttributeValue(UIA_IsReadOnlyAttributeId)`), false, "IsReadOnly correct" ); } ); /** * Test the TextRange pattern's Move method. */ addUiaTask( `

ab

ij

`, async function testTextRangeMove() { await runPython(` doc = getDocUia() textarea = findUiaByDomId(doc, "textarea") text = getUiaPattern(textarea, "Text") global range range = text.DocumentRange `); is(await runPython(`range.GetText(-1)`), "cd ef gh", "range text correct"); info("Moving 1 word"); is( await runPython(`range.Move(TextUnit_Word, 1)`), 1, "Move return correct" ); is(await runPython(`range.GetText(-1)`), "ef ", "range text correct"); info("Moving 3 words"); // There are only 2 words after. is( await runPython(`range.Move(TextUnit_Word, 3)`), 2, "Move return correct" ); // The IA2 -> UIA proxy gets most things below this wrong. if (!gIsUiaEnabled) { return; } is(await runPython(`range.GetText(-1)`), "ij", "range text correct"); info("Moving -5 words"); // There are only 4 words before. is( await runPython(`range.Move(TextUnit_Word, -5)`), -4, "Move return correct" ); is(await runPython(`range.GetText(-1)`), "ab", "range text correct"); info("Moving 1 word"); is( await runPython(`range.Move(TextUnit_Word, 1)`), 1, "Move return correct" ); is(await runPython(`range.GetText(-1)`), "cd ", "range text correct"); info("Moving 1 character"); is( await runPython(`range.Move(TextUnit_Character, 1)`), 1, "Move return correct" ); is(await runPython(`range.GetText(-1)`), "d", "range text correct"); // When the range is not collapsed, Move moves backward to the start of the // unit before moving to the requested unit. info("Moving -1 word"); is( await runPython(`range.Move(TextUnit_Word, -1)`), -1, "Move return correct" ); is(await runPython(`range.GetText(-1)`), "ab", "range text correct"); info("Collapsing to start"); await runPython( `range.MoveEndpointByRange(TextPatternRangeEndpoint_End, range, TextPatternRangeEndpoint_Start)` ); is(await runPython(`range.GetText(-1)`), "", "range text correct"); // range is now collapsed at "a". info("Moving 1 word"); is( await runPython(`range.Move(TextUnit_Word, 1)`), 1, "Move return correct" ); // range is now collapsed at "c". is(await runPython(`range.GetText(-1)`), "", "range text correct"); info("Expanding to character"); await runPython(`range.ExpandToEnclosingUnit(TextUnit_Character)`); is(await runPython(`range.GetText(-1)`), "c", "range text correct"); info("Collapsing to end"); await runPython( `range.MoveEndpointByRange(TextPatternRangeEndpoint_Start, range, TextPatternRangeEndpoint_End)` ); // range is now collapsed at "d". // When the range is collapsed, Move does *not* first move back to the start // of the unit. info("Moving -1 word"); is( await runPython(`range.Move(TextUnit_Word, -1)`), -1, "Move return correct" ); // range is collapsed at "c". is(await runPython(`range.GetText(-1)`), "", "range text correct"); await runPython(`range.ExpandToEnclosingUnit(TextUnit_Word)`); is(await runPython(`range.GetText(-1)`), "cd ", "range text correct"); } ); /** * Test the TextRange pattern's MoveEndpointByRange method. */ addUiaTask( `

ab

ij

`, async function testTextRangeMoveEndpointByRange() { await runPython(` global doc, taRange, range doc = getDocUia() textarea = findUiaByDomId(doc, "textarea") text = getUiaPattern(textarea, "Text") taRange = text.DocumentRange range = text.DocumentRange `); is(await runPython(`range.GetText(-1)`), "cd ef gh", "range text correct"); info("Expanding to character"); await runPython(`range.ExpandToEnclosingUnit(TextUnit_Character)`); is(await runPython(`range.GetText(-1)`), "c", "range text correct"); is( await runPython( `range.CompareEndpoints(TextPatternRangeEndpoint_Start, range, TextPatternRangeEndpoint_End)` ), -1, "start < end" ); info("Moving end to start"); await runPython( `range.MoveEndpointByRange(TextPatternRangeEndpoint_End, range, TextPatternRangeEndpoint_Start)` ); is( await runPython( `range.CompareEndpoints(TextPatternRangeEndpoint_Start, range, TextPatternRangeEndpoint_End)` ), 0, "start == end" ); info("Moving range end to textarea end"); await runPython( `range.MoveEndpointByRange(TextPatternRangeEndpoint_End, taRange, TextPatternRangeEndpoint_End)` ); is(await runPython(`range.GetText(-1)`), "cd ef gh", "range text correct"); info("Expanding to character"); await runPython(`range.ExpandToEnclosingUnit(TextUnit_Character)`); is(await runPython(`range.GetText(-1)`), "c", "range text correct"); info("Moving range start to textarea end"); await runPython( `range.MoveEndpointByRange(TextPatternRangeEndpoint_Start, taRange, TextPatternRangeEndpoint_End)` ); is( await runPython( `range.CompareEndpoints(TextPatternRangeEndpoint_Start, taRange, TextPatternRangeEndpoint_End)` ), 0, "range start == textarea end" ); is( await runPython( `range.CompareEndpoints(TextPatternRangeEndpoint_End, taRange, TextPatternRangeEndpoint_End)` ), 0, "range end == textarea end" ); info("Moving range end to textarea start"); await runPython( `range.MoveEndpointByRange(TextPatternRangeEndpoint_End, taRange, TextPatternRangeEndpoint_Start)` ); is( await runPython( `range.CompareEndpoints(TextPatternRangeEndpoint_Start, taRange, TextPatternRangeEndpoint_Start)` ), 0, "range start == textarea start" ); is( await runPython( `range.CompareEndpoints(TextPatternRangeEndpoint_End, taRange, TextPatternRangeEndpoint_Start)` ), 0, "range end == textarea start" ); await definePyVar("docRange", `getUiaPattern(doc, "Text").DocumentRange`); info("Moving range start to document start"); await runPython( `range.MoveEndpointByRange(TextPatternRangeEndpoint_Start, docRange, TextPatternRangeEndpoint_Start)` ); info("Moving range end to document end"); await runPython( `range.MoveEndpointByRange(TextPatternRangeEndpoint_End, docRange, TextPatternRangeEndpoint_End)` ); is( await runPython( `range.CompareEndpoints(TextPatternRangeEndpoint_Start, docRange, TextPatternRangeEndpoint_Start)` ), 0, "range start == document start" ); is( await runPython( `range.CompareEndpoints(TextPatternRangeEndpoint_End, docRange, TextPatternRangeEndpoint_End)` ), 0, "range end == document end" ); } ); /** * Test the TextRange pattern's MoveEndpointByUnit method. */ addUiaTask( `

ab

ij

`, async function testTextRangeMoveEndpointByUnit() { await runPython(` doc = getDocUia() textarea = findUiaByDomId(doc, "textarea") text = getUiaPattern(textarea, "Text") global range range = text.DocumentRange `); is(await runPython(`range.GetText(-1)`), "cd ef gh", "range text correct"); info("Moving end -1 word"); is( await runPython( `range.MoveEndpointByUnit(TextPatternRangeEndpoint_End, TextUnit_Word, -1)` ), -1, "MoveEndpointByUnit return correct" ); is(await runPython(`range.GetText(-1)`), "cd ef ", "range text correct"); info("Moving end -4 words"); // There are only 3 words before. is( await runPython( `range.MoveEndpointByUnit(TextPatternRangeEndpoint_End, TextUnit_Word, -4)` ), -3, "MoveEndpointByUnit return correct" ); is(await runPython(`range.GetText(-1)`), "", "range text correct"); info("Moving start 1 word"); is( await runPython( `range.MoveEndpointByUnit(TextPatternRangeEndpoint_Start, TextUnit_Word, 1)` ), 1, "MoveEndpointByUnit return correct" ); is(await runPython(`range.GetText(-1)`), "", "range text correct"); info("Moving end 1 character"); is( await runPython( `range.MoveEndpointByUnit(TextPatternRangeEndpoint_End, TextUnit_Character, 1)` ), 1, "MoveEndpointByUnit return correct" ); is(await runPython(`range.GetText(-1)`), "c", "range text correct"); info("Moving start 5 words"); // There are only 4 word boundaries after. is( await runPython( `range.MoveEndpointByUnit(TextPatternRangeEndpoint_Start, TextUnit_Word, 5)` ), 4, "MoveEndpointByUnit return correct" ); is(await runPython(`range.GetText(-1)`), "", "range text correct"); info("Moving end -1 word"); is( await runPython( `range.MoveEndpointByUnit(TextPatternRangeEndpoint_End, TextUnit_Word, -1)` ), -1, "MoveEndpointByUnit return correct" ); is(await runPython(`range.GetText(-1)`), "", "range text correct"); info("Moving end 1 character"); is( await runPython( `range.MoveEndpointByUnit(TextPatternRangeEndpoint_End, TextUnit_Character, 1)` ), 1, "MoveEndpointByUnit return correct" ); is(await runPython(`range.GetText(-1)`), "i", "range text correct"); } ); /** * Test the Text pattern's SupportedTextSelection property. */ addUiaTask( `

p

`, async function testTextSupportedTextSelection() { let result = await runPython(` global doc doc = getDocUia() input = findUiaByDomId(doc, "input") text = getUiaPattern(input, "Text") return text.SupportedTextSelection `); is( result, SupportedTextSelection_Multiple, "input SupportedTextSelection correct" ); if (gIsUiaEnabled) { // The IA2 -> UIA proxy doesn't expose the Text pattern on this text leaf. is( await runPython(` p = findUiaByDomId(doc, "p") pLeaf = uiaClient.RawViewWalker.GetFirstChildElement(p) text = getUiaPattern(pLeaf, "Text") return text.SupportedTextSelection `), SupportedTextSelection_None, "pLeaf SupportedTextSelection correct" ); // The IA2 -> UIA proxy doesn't understand that text isn't selectable in // this document. is( await runPython(`getUiaPattern(doc, "Text").SupportedTextSelection`), SupportedTextSelection_None, "doc SupportedTextSelection correct" ); } } ); /** * Test the Text pattern's SupportedTextSelection property on a document with a * selectable body. */ addUiaTask( `

p

`, async function testTextSupportedTextSelectionSelectableBody() { is( await runPython(` global doc doc = getDocUia() text = getUiaPattern(doc, "Text") return text.SupportedTextSelection `), SupportedTextSelection_Multiple, "doc SupportedTextSelection correct" ); // The IA2 -> UIA proxy doesn't expose the Text pattern on this text leaf. if (gIsUiaEnabled) { is( await runPython(` p = findUiaByDomId(doc, "p") pLeaf = uiaClient.RawViewWalker.GetFirstChildElement(p) text = getUiaPattern(pLeaf, "Text") return text.SupportedTextSelection `), SupportedTextSelection_Multiple, "pLeaf SupportedTextSelection correct" ); } } ); /** * Test the Text pattern's GetSelection method with the caret. */ addUiaTask( ``, async function testTextGetSelectionCaret(browser, docAcc) { await runPython(` doc = getDocUia() textarea = findUiaByDomId(doc, "textarea") global text text = getUiaPattern(textarea, "Text") `); is(await runPython(`text.GetSelection().Length`), 0, "No selection"); info("Focusing textarea"); const textarea = findAccessibleChildByID(docAcc, "textarea", [ nsIAccessibleText, ]); let moved = waitForEvent(EVENT_TEXT_CARET_MOVED, textarea); textarea.takeFocus(); await moved; is(await runPython(`text.GetSelection().Length`), 1, "1 selection"); await definePyVar("range", `text.GetSelection().GetElement(0)`); ok(await runPython(`bool(range)`), "Got selection range 0"); info("Expanding to character"); await runPython(`range.ExpandToEnclosingUnit(TextUnit_Character)`); is(await runPython(`range.GetText(-1)`), "a", "range text correct"); info("Pressing ArrowRight"); moved = waitForEvent(EVENT_TEXT_CARET_MOVED, textarea); EventUtils.synthesizeKey("KEY_ArrowRight"); await moved; is(await runPython(`text.GetSelection().Length`), 1, "1 selection"); await definePyVar("range", `text.GetSelection().GetElement(0)`); ok(await runPython(`bool(range)`), "Got selection range 0"); info("Expanding to character"); await runPython(`range.ExpandToEnclosingUnit(TextUnit_Character)`); is(await runPython(`range.GetText(-1)`), "b", "range text correct"); // The IA2 -> UIA proxy doesn't handle the insertion point at the end of a // line correctly. if (!gIsUiaEnabled) { return; } // Test the insertion point at the end of a wrapped line. info("Pressing End"); moved = waitForEvent(EVENT_TEXT_CARET_MOVED, textarea); EventUtils.synthesizeKey("KEY_End"); await moved; is(await runPython(`text.GetSelection().Length`), 1, "1 selection"); await definePyVar("range", `text.GetSelection().GetElement(0)`); ok(await runPython(`bool(range)`), "Got selection range 0"); info("Expanding to character"); await runPython(`range.ExpandToEnclosingUnit(TextUnit_Character)`); is(await runPython(`range.GetText(-1)`), "", "range text correct"); info("Moving end 1 character"); await runPython( `range.MoveEndpointByUnit(TextPatternRangeEndpoint_End, TextUnit_Character, 1)` ); is(await runPython(`range.GetText(-1)`), "c", "range text correct"); info("Expanding to line at caret"); await definePyVar("range", `text.GetSelection().GetElement(0)`); await runPython(`range.ExpandToEnclosingUnit(TextUnit_Line)`); is(await runPython(`range.GetText(-1)`), "ab ", "range text correct"); // Test the insertion point at the end of the textarea. info("Pressing Ctrl+End"); moved = waitForEvent(EVENT_TEXT_CARET_MOVED, textarea); EventUtils.synthesizeKey("KEY_End", { ctrlKey: true }); await moved; is(await runPython(`text.GetSelection().Length`), 1, "1 selection"); await definePyVar("range", `text.GetSelection().GetElement(0)`); ok(await runPython(`bool(range)`), "Got selection range 0"); info("Expanding to character"); await runPython(`range.ExpandToEnclosingUnit(TextUnit_Character)`); is(await runPython(`range.GetText(-1)`), "", "range text correct"); info("Expanding to line"); await definePyVar("range", `text.GetSelection().GetElement(0)`); await runPython(`range.ExpandToEnclosingUnit(TextUnit_Line)`); is(await runPython(`range.GetText(-1)`), "cd", "range text correct"); info("Clicking mouse at b"); // BrowserTestUtils.synthesizeMouseAtPoint takes coordinates relative to the document. const docX = {}; const docY = {}; docAcc.getBounds(docX, docY, {}, {}); let charX = {}; let charY = {}; textarea.getCharacterExtents( 1, charX, charY, {}, {}, COORDTYPE_SCREEN_RELATIVE ); moved = waitForEvent(EVENT_TEXT_CARET_MOVED, textarea); await BrowserTestUtils.synthesizeMouseAtPoint( charX.value - docX.value, charY.value - docY.value, {}, docAcc.browsingContext ); await moved; is(await runPython(`text.GetSelection().Length`), 1, "1 selection"); await definePyVar("range", `text.GetSelection().GetElement(0)`); ok(await runPython(`bool(range)`), "Got selection range 0"); info("Expanding to character"); await runPython(`range.ExpandToEnclosingUnit(TextUnit_Character)`); is(await runPython(`range.GetText(-1)`), "b", "range text correct"); } ); /** * Test the Text pattern's GetSelection method with selection. */ addUiaTask( ``, async function testTextGetSelectionSelection(browser, docAcc) { await runPython(` doc = getDocUia() textarea = findUiaByDomId(doc, "textarea") global text text = getUiaPattern(textarea, "Text") `); is(await runPython(`text.GetSelection().Length`), 0, "No selection"); info("Focusing textarea"); const textarea = findAccessibleChildByID(docAcc, "textarea", [ nsIAccessibleText, ]); let moved = waitForEvent(EVENT_TEXT_CARET_MOVED, textarea); textarea.takeFocus(); await moved; is(await runPython(`text.GetSelection().Length`), 1, "1 selection"); await definePyVar("range", `text.GetSelection().GetElement(0)`); ok(await runPython(`bool(range)`), "Got selection range 0"); is(await runPython(`range.GetText(-1)`), "", "range text correct"); info("Selecting ab"); moved = waitForEvent(EVENT_TEXT_SELECTION_CHANGED, textarea); textarea.addSelection(0, 2); await moved; is(await runPython(`text.GetSelection().Length`), 1, "1 selection"); await definePyVar("range", `text.GetSelection().GetElement(0)`); ok(await runPython(`bool(range)`), "Got selection range 0"); is(await runPython(`range.GetText(-1)`), "ab", "range text correct"); info("Adding cd to selection"); moved = waitForEvent(EVENT_TEXT_SELECTION_CHANGED, textarea); textarea.addSelection(3, 5); await moved; is(await runPython(`text.GetSelection().Length`), 2, "2 selections"); await definePyVar("range", `text.GetSelection().GetElement(0)`); ok(await runPython(`bool(range)`), "Got selection range 0"); is(await runPython(`range.GetText(-1)`), "ab", "range text correct"); await definePyVar("range", `text.GetSelection().GetElement(1)`); ok(await runPython(`bool(range)`), "Got selection range 1"); is(await runPython(`range.GetText(-1)`), "cd", "range text correct"); } ); /** * Test the Text pattern's TextSelectionChanged event. */ addUiaTask( ``, async function testTextTextSelectionChanged(browser) { info("Focusing input"); await setUpWaitForUiaEvent("Text_TextSelectionChanged", "input"); await invokeContentTask(browser, [], () => { content.document.getElementById("input").focus(); }); await waitForUiaEvent(); ok(true, "input got TextSelectionChanged event"); info("Moving caret to b"); await setUpWaitForUiaEvent("Text_TextSelectionChanged", "input"); await invokeContentTask(browser, [], () => { content.document.getElementById("input").setSelectionRange(1, 1); }); await waitForUiaEvent(); ok(true, "input got TextSelectionChanged event"); info("Selecting bc"); await setUpWaitForUiaEvent("Text_TextSelectionChanged", "input"); await invokeContentTask(browser, [], () => { content.document.getElementById("input").setSelectionRange(1, 3); }); await waitForUiaEvent(); ok(true, "input got TextSelectionChanged event"); } ); /** * Test the Text pattern's TextChanged event. */ addUiaTask( ``, async function testTextTextChanged(browser) { info("Focusing input"); let moved = waitForEvent(EVENT_TEXT_CARET_MOVED, "input"); await invokeContentTask(browser, [], () => { content.document.getElementById("input").focus(); }); await moved; info("Deleting a"); await setUpWaitForUiaEvent("Text_TextChanged", "input"); await invokeContentTask(browser, [], () => { content.document.execCommand("forwardDelete"); }); await waitForUiaEvent(); ok(true, "input got TextChanged event"); info("Inserting a"); await setUpWaitForUiaEvent("Text_TextChanged", "input"); await invokeContentTask(browser, [], () => { content.document.execCommand("insertText", false, "a"); }); await waitForUiaEvent(); ok(true, "input got TextChanged event"); } ); /** * Test the TextRange pattern's GetEnclosingElement method. */ addUiaTask( `
ab cd ef g

`, async function testTextRangeGetEnclosingElement() { info("Getting editable DocumentRange"); await runPython(` doc = getDocUia() editable = findUiaByDomId(doc, "editable") text = getUiaPattern(editable, "Text") global range range = text.DocumentRange `); is( await runPython(`range.GetEnclosingElement().CurrentAutomationId`), "editable", "EnclosingElement is editable" ); info("Expanding to word"); await runPython(`range.ExpandToEnclosingUnit(TextUnit_Word)`); // Range is now "ab ". // The IA2 -> UIA proxy gets this wrong. if (gIsUiaEnabled) { is( await runPython(`range.GetEnclosingElement().CurrentName`), "ab ", "EnclosingElement is ab text leaf" ); } info("Moving 1 word"); await runPython(`range.Move(TextUnit_Word, 1)`); // Range is now "cd ". // The "cd" text leaf doesn't include the space, so the enclosing element is // its parent. is( await runPython(`range.GetEnclosingElement().CurrentAutomationId`), "cdef", "EnclosingElement is cdef" ); info("Moving end -1 character"); await runPython( `range.MoveEndpointByUnit(TextPatternRangeEndpoint_End, TextUnit_Character, -1)` ); // Range is now "cd". // The IA2 -> UIA proxy gets this wrong. if (gIsUiaEnabled) { is( await runPython(`range.GetEnclosingElement().CurrentName`), "cd", "EnclosingElement is cd text leaf" ); } info("Moving 1 word"); await runPython(`range.Move(TextUnit_Word, 1)`); // Range is now "ef ". // Neither the "ef" text leaf/link nor the "cdef" mark include the trailing // space, so the enclosing element is cdef's parent. is( await runPython(`range.GetEnclosingElement().CurrentAutomationId`), "editable", "EnclosingElement is editable" ); info("Moving end -1 character"); await runPython( `range.MoveEndpointByUnit(TextPatternRangeEndpoint_End, TextUnit_Character, -1)` ); // Range is now "ef". The innermost element is the text leaf, but "ef" is a // link and that's what Narrator wants. is( await runPython(`range.GetEnclosingElement().CurrentAutomationId`), "ef", "EnclosingElement is ef" ); // The IA2 -> UIA proxy gets the rest of this wrong. if (!gIsUiaEnabled) { return; } info("Moving 1 word"); await runPython(`range.Move(TextUnit_Word, 1)`); // Range is now the embedded object character for the img (g). is( await runPython(`range.GetEnclosingElement().CurrentAutomationId`), "g", "EnclosingElement is g" ); info("Moving 1 word"); await runPython(`range.Move(TextUnit_Word, 1)`); // Range is now "h". "h" is a button and buttons prune their children, so // UIA doesn't see the text leaf. is( await runPython(`range.GetEnclosingElement().CurrentAutomationId`), "h", "EnclosingElement is h" ); } ); /** * Test the TextRange pattern's GetChildren method. */ addUiaTask( `
ab cd ef g
`, async function testTextRangeGetChildren() { info("Getting editable DocumentRange"); await runPython(` doc = getDocUia() editable = findUiaByDomId(doc, "editable") text = getUiaPattern(editable, "Text") global r r = text.DocumentRange `); await isUiaElementArray( `r.GetChildren()`, ["cdef", "g"], "Children are correct" ); info("Expanding to word"); await runPython(`r.ExpandToEnclosingUnit(TextUnit_Word)`); // Range is now "ab ". await isUiaElementArray(`r.GetChildren()`, [], "Children are correct"); info("Moving 1 word"); await runPython(`r.Move(TextUnit_Word, 1)`); // Range is now "cd ". await isUiaElementArray(`r.GetChildren()`, [], "Children are correct"); info("Moving 1 word"); await runPython(`r.Move(TextUnit_Word, 1)`); // Range is now "ef ". The range includes the link but is not completely // enclosed by the link. await isUiaElementArray(`r.GetChildren()`, ["ef"], "Children are correct"); info("Moving end -1 character"); await runPython( `r.MoveEndpointByUnit(TextPatternRangeEndpoint_End, TextUnit_Character, -1)` ); // Range is now "ef". The range encloses the link, so there are no children. await isUiaElementArray(`r.GetChildren()`, [], "Children are correct"); info("Moving 1 word"); await runPython(`r.Move(TextUnit_Word, 1)`); // Range is now the embedded object character for the img (g). The range is // completely enclosed by the image. // The IA2 -> UIA proxy gets this wrong. if (gIsUiaEnabled) { await isUiaElementArray(`r.GetChildren()`, [], "Children are correct"); } } ); /** * Test the Text pattern's RangeFromChild method. */ addUiaTask( `
ab cd ef g
`, async function testTextRangeFromChild() { await runPython(` global doc, docText, editable, edText doc = getDocUia() docText = getUiaPattern(doc, "Text") editable = findUiaByDomId(doc, "editable") edText = getUiaPattern(editable, "Text") `); is( await runPython(`docText.RangeFromChild(editable).GetText(-1)`), `ab cd ef ${kEmbedChar}`, "doc returned correct range for editable" ); await testPythonRaises( `edText.RangeFromChild(editable)`, "editable correctly failed to return range for editable" ); is( await runPython(`docText.RangeFromChild(editable).GetText(-1)`), `ab cd ef ${kEmbedChar}`, "doc returned correct range for editable" ); let text = await runPython(` ab = uiaClient.RawViewWalker.GetFirstChildElement(editable) range = docText.RangeFromChild(ab) return range.GetText(-1) `); is(text, "ab ", "doc returned correct range for ab"); text = await runPython(` global cdef cdef = findUiaByDomId(doc, "cdef") range = docText.RangeFromChild(cdef) return range.GetText(-1) `); is(text, "cd ef", "doc returned correct range for cdef"); text = await runPython(` cd = uiaClient.RawViewWalker.GetFirstChildElement(cdef) range = docText.RangeFromChild(cd) return range.GetText(-1) `); is(text, "cd", "doc returned correct range for cd"); text = await runPython(` global efLink efLink = findUiaByDomId(doc, "ef") range = docText.RangeFromChild(efLink) return range.GetText(-1) `); is(text, "ef", "doc returned correct range for ef link"); text = await runPython(` efLeaf = uiaClient.RawViewWalker.GetFirstChildElement(efLink) range = docText.RangeFromChild(efLeaf) return range.GetText(-1) `); is(text, "ef", "doc returned correct range for ef leaf"); text = await runPython(` g = findUiaByDomId(doc, "g") range = docText.RangeFromChild(g) return range.GetText(-1) `); is(text, kEmbedChar, "doc returned correct range for g"); }, // The IA2 -> UIA proxy has too many quirks/bugs here. { uiaEnabled: true, uiaDisabled: false } ); /** * Test the Text pattern's RangeFromPoint method. */ addUiaTask( `
a b c
`, async function testTextRangeFromPoint(browser, docAcc) { const acc = findAccessibleChildByID(docAcc, "test", [nsIAccessibleText]); await runPython(` global doc, docText doc = getDocUia() docText = getUiaPattern(doc, "Text") `); // Walk through every offset in the accessible and hit test each. Verify // that the returned range is empty, and that it hit the right character. for (let offset = 0; offset < acc.characterCount; ++offset) { const x = {}; const y = {}; acc.getCharacterExtents(offset, x, y, {}, {}, COORDTYPE_SCREEN_RELATIVE); await runPython(` global range range = docText.RangeFromPoint(POINT(${x.value}, ${y.value}))`); is( await runPython(`range.GetText(-1)`), ``, "doc returned correct empty range" ); await runPython(`range.ExpandToEnclosingUnit(TextUnit_Character)`); const charAtOffset = acc.getCharacterAtOffset(offset); is( await runPython(`range.GetText(-1)`), `${charAtOffset}`, "doc returned correct range" ); } // An arbitrary invalid point should cause an invalid argument error. await testPythonRaises( `docText.RangeFromPoint(POINT(9999999999, 9999999999))`, "no text leaves at invalid point" ); }, { uiaEnabled: true, uiaDisabled: true } ); /** * Test the TextRange pattern's GetBoundingRectangles method. */ addUiaTask( `

abc

d

xyz
`, async function testTextRangeGetBoundingRectangles(browser, docAcc) { const line1 = findAccessibleChildByID(docAcc, "line1", [nsIAccessibleText]); const line2 = findAccessibleChildByID(docAcc, "line2", [nsIAccessibleText]); const lineRects = await runPython(` global doc, docText, testAcc, range doc = getDocUia() docText = getUiaPattern(doc, "Text") testAcc = findUiaByDomId(doc, "test") range = docText.RangeFromChild(testAcc) return range.GetBoundingRectangles() `); is(lineRects.length, 8, "GetBoundingRectangles returned two rectangles"); const firstLineRect = [ lineRects[0], lineRects[1], lineRects[2], lineRects[3], ]; const secondLineRect = [ lineRects[4], lineRects[5], lineRects[6], lineRects[7], ]; testTextBounds(line1, 0, -1, firstLineRect, COORDTYPE_SCREEN_RELATIVE); testTextBounds(line2, 0, -1, secondLineRect, COORDTYPE_SCREEN_RELATIVE); // line3 has no rectangle - GetBoundingRectangles shouldn't return anything for empty lines. // GetBoundingRectangles shouldn't return anything for offscreen lines. const offscreenRects = await runPython(` global offscreenAcc, range offscreenAcc = findUiaByDomId(doc, "offscreen") range = docText.RangeFromChild(offscreenAcc) return range.GetBoundingRectangles() `); is( offscreenRects.length, 0, "GetBoundingRectangles returned no rectangles" ); }, { uiaEnabled: true, uiaDisabled: true, chrome: true } ); /** * Test char bounds with the TextRange pattern's GetBoundingRectangles method. */ addUiaTask( `
abc
`, async function testTextRangeGetBoundingRectanglesChar(browser, docAcc) { const testAcc = findAccessibleChildByID(docAcc, "test", [ nsIAccessibleText, ]); const charX = {}; const charY = {}; const charW = {}; const charH = {}; testAcc.getCharacterExtents( 0, charX, charY, charW, charH, COORDTYPE_SCREEN_RELATIVE ); await runPython(` global doc, docText, testAcc, range doc = getDocUia() docText = getUiaPattern(doc, "Text") testAcc = findUiaByDomId(doc, "test") range = docText.RangeFromChild(testAcc) range.ExpandToEnclosingUnit(TextUnit_Character) `); is(await runPython(`range.GetText(-1)`), "a", "range text correct"); const uiaRect = await runPython(`range.GetBoundingRectangles()`); is(uiaRect.length, 4, "GetBoundingRectangles returned one rectangle"); is(uiaRect[0], charX.value, "UIA char rect X matches core char rect X"); is(uiaRect[1], charY.value, "UIA char rect Y matches core char rect Y"); is(uiaRect[2], charW.value, "UIA char rect W matches core char rect W"); is(uiaRect[3], charH.value, "UIA char rect H matches core char rect H"); }, { uiaEnabled: true, uiaDisabled: true, chrome: true } ); /** * Test special case line bounds with the TextRange pattern's * GetBoundingRectangles method. */ addUiaTask( `
ABC
DEF

ABC DEF

`, async function testTextRangeGetBoundingRectanglesLine(browser, docAcc) { const lineBreakAcc = findAccessibleChildByID(docAcc, "line-break", [ nsIAccessibleText, ]); const wrappingAcc = findAccessibleChildByID(docAcc, "wrapping", [ nsIAccessibleText, ]); let lineRects = await runPython(` global doc, docText, testAcc doc = getDocUia() docText = getUiaPattern(doc, "Text") testAcc = findUiaByDomId(doc, "line-break") range = docText.RangeFromChild(testAcc) return range.GetBoundingRectangles() `); is(lineRects.length, 4, "GetBoundingRectangles returned one rectangle"); const lineBreakLineRect = [ lineRects[0], lineRects[1], lineRects[2], lineRects[3], ]; testTextBounds( lineBreakAcc, 0, -1, lineBreakLineRect, COORDTYPE_SCREEN_RELATIVE ); lineRects = await runPython(` global doc, docText, testAcc testAcc = findUiaByDomId(doc, "wrapping") range = docText.RangeFromChild(testAcc) return range.GetBoundingRectangles() `); is(lineRects.length, 4, "GetBoundingRectangles returned one rectangle"); const wrappingLineRect = [ lineRects[0], lineRects[1], lineRects[2], lineRects[3], ]; testTextBounds( wrappingAcc, 0, -1, wrappingLineRect, COORDTYPE_SCREEN_RELATIVE ); }, { uiaEnabled: true, uiaDisabled: true, chrome: true } ); /** * Test the Text pattern's GetBoundingRectangles method with the caret. */ addUiaTask( `
a

b
`, async function testTextRangeGetBoundingRectanglesCaret(browser, docAcc) { info("Focusing editable"); const editable = findAccessibleChildByID(docAcc, "editable"); const ce0 = findAccessibleChildByID(docAcc, "ce0"); let moved = waitForEvent(EVENT_TEXT_CARET_MOVED, ce0); editable.takeFocus(); await moved; await runPython(` global text doc = getDocUia() editable = findUiaByDomId(doc, "editable") text = getUiaPattern(editable, "Text") `); let uiaRects = await runPython( `text.GetSelection().GetElement(0).GetBoundingRectangles()` ); testTextPos(ce0, 0, [uiaRects[0], uiaRects[1]], COORDTYPE_SCREEN_RELATIVE); info("ArrowRight to end of line"); moved = waitForEvent(EVENT_TEXT_CARET_MOVED, ce0); EventUtils.synthesizeKey("KEY_ArrowRight"); await moved; uiaRects = await runPython( `text.GetSelection().GetElement(0).GetBoundingRectangles()` ); // Bug 1966812: We would ideally return a rect here. is(uiaRects.length, 0, "GetBoundingRectangles returned nothing"); info("ArrowRight to line feed on blank line"); const ce1 = findAccessibleChildByID(docAcc, "ce1"); moved = waitForEvent(EVENT_TEXT_CARET_MOVED, ce1); EventUtils.synthesizeKey("KEY_ArrowRight"); await moved; uiaRects = await runPython( `text.GetSelection().GetElement(0).GetBoundingRectangles()` ); testTextPos(ce1, 0, [uiaRects[0], uiaRects[1]], COORDTYPE_SCREEN_RELATIVE); info("ArrowRight to b"); const ce2 = findAccessibleChildByID(docAcc, "ce2"); moved = waitForEvent(EVENT_TEXT_CARET_MOVED, ce2); EventUtils.synthesizeKey("KEY_ArrowRight"); await moved; uiaRects = await runPython( `text.GetSelection().GetElement(0).GetBoundingRectangles()` ); testTextPos(ce2, 0, [uiaRects[0], uiaRects[1]], COORDTYPE_SCREEN_RELATIVE); }, // The IA2 -> UIA proxy doesn't support this. { uiaEnabled: true, uiaDisabled: false } ); /** * Test the TextRange pattern's ScrollIntoView method. */ addUiaTask( `

p1


p2


p3

`, async function testTextRangeScrollIntoView(browser, docAcc) { const [docLeft, docTop, , docBottom] = await runPython(` global doc doc = getDocUia() rect = doc.CurrentBoundingRectangle return (rect.left, rect.top, rect.right, rect.bottom) `); info("Scrolling p2 to top"); let scrolled = waitForEvent(EVENT_SCROLLING_END, docAcc); await runPython(` global docText, p2, range docText = getUiaPattern(doc, "Text") p2 = findUiaByDomId(doc, "p2") range = docText.RangeFromChild(p2) range.ScrollIntoView(True) `); await scrolled; let [left, top, , height] = await runPython( `range.GetBoundingRectangles()` ); is(left, docLeft, "range is at left of document"); is(top, docTop, "range is at top of document"); info("Scrolling p2 to bottom"); scrolled = waitForEvent(EVENT_SCROLLING_END, docAcc); await runPython(` range.ScrollIntoView(False) `); await scrolled; [left, top, , height] = await runPython(`range.GetBoundingRectangles()`); is(left, docLeft, "range is at left of document"); is(top + height, docBottom, "range is at bottom of document"); } ); /** * Test the TextRange pattern's Select method. */ addUiaTask( `

ab

ab
`, async function testTextRangeSelect(browser, docAcc) { info("Moving caret to b in p"); const p = findAccessibleChildByID(docAcc, "p", [nsIAccessibleText]); let moved = waitForEvent(EVENT_TEXT_CARET_MOVED, p); await runPython(` global doc doc = getDocUia() p = findUiaByDomId(doc, "p") textChild = getUiaPattern(p, "TextChild") global range range = textChild.TextRange # Encompass "b". range.Move(TextUnit_Character, 1) # Collapse. range.MoveEndpointByRange(TextPatternRangeEndpoint_End, range, TextPatternRangeEndpoint_Start) range.Select() `); await moved; testTextSelectionCount(p, 0); is(p.caretOffset, 1, "caret at 1"); // and contentEditable should behave the same. for (const id of ["input", "contenteditable"]) { info(`Focusing ${id}`); const acc = findAccessibleChildByID(docAcc, id, [nsIAccessibleText]); moved = waitForEvents([ [EVENT_FOCUS, acc], [EVENT_TEXT_CARET_MOVED, acc], ]); acc.takeFocus(); await moved; info("Selecting a"); moved = waitForEvents([ [EVENT_TEXT_SELECTION_CHANGED, acc], [EVENT_TEXT_CARET_MOVED, acc], ]); await runPython(` acc = findUiaByDomId(doc, "${id}") text = getUiaPattern(acc, "Text") global range range = text.DocumentRange range.ExpandToEnclosingUnit(TextUnit_Character) range.Select() `); await moved; testTextSelectionCount(acc, 1); testTextGetSelection(acc, 0, 1, 0); info("Moving caret to b"); moved = waitForEvent(EVENT_TEXT_CARET_MOVED, acc); await runPython(` # Collapse to b. range.MoveEndpointByUnit(TextPatternRangeEndpoint_Start, TextUnit_Character, 1) range.Select() `); await moved; testTextSelectionCount(acc, 0); is(acc.caretOffset, 1, "caret at 1"); } } ); /** * Test the TextRange pattern's AddToSelection method. */ addUiaTask( `
abc
`, async function testTextRangeAddToSelection(browser, docAcc) { // and contentEditable should behave the same. for (const id of ["input", "contenteditable"]) { info(`Focusing ${id}`); const acc = findAccessibleChildByID(docAcc, id, [nsIAccessibleText]); let moved = waitForEvents([ [EVENT_FOCUS, acc], [EVENT_TEXT_CARET_MOVED, acc], ]); acc.takeFocus(); await moved; info("Adding a to selection"); moved = waitForEvents([ [EVENT_TEXT_SELECTION_CHANGED, acc], [EVENT_TEXT_CARET_MOVED, acc], ]); await runPython(` doc = getDocUia() acc = findUiaByDomId(doc, "${id}") text = getUiaPattern(acc, "Text") global range range = text.DocumentRange range.ExpandToEnclosingUnit(TextUnit_Character) range.AddToSelection() `); await moved; testTextSelectionCount(acc, 1); testTextGetSelection(acc, 0, 1, 0); info("Adding c to selection"); moved = waitForEvent(EVENT_TEXT_CARET_MOVED, acc); await runPython(` # Move start to c. range.MoveEndpointByUnit(TextPatternRangeEndpoint_Start, TextUnit_Character, 2) range.ExpandToEnclosingUnit(TextUnit_Character) range.AddToSelection() `); await moved; testTextSelectionCount(acc, 2); testTextGetSelection(acc, 0, 1, 0); testTextGetSelection(acc, 2, 3, 1); } } ); /** * Test the TextRange pattern's RemoveFromSelection method. */ addUiaTask( `
abc
`, async function testTextRangeRemoveFromSelection(browser, docAcc) { // and contentEditable should behave the same. for (const id of ["input", "contenteditable"]) { info(`Focusing ${id}`); const acc = findAccessibleChildByID(docAcc, id, [nsIAccessibleText]); let moved = waitForEvents([ [EVENT_FOCUS, acc], [EVENT_TEXT_CARET_MOVED, acc], ]); acc.takeFocus(); await moved; info("Adding a to selection"); moved = waitForEvents([ [EVENT_TEXT_SELECTION_CHANGED, acc], [EVENT_TEXT_CARET_MOVED, acc], ]); acc.addSelection(0, 1); await moved; info("Adding c to selection"); moved = waitForEvents([ [EVENT_TEXT_SELECTION_CHANGED, acc], [EVENT_TEXT_CARET_MOVED, acc], ]); acc.addSelection(2, 3); await moved; info("Removing a from selection"); moved = waitForEvents([ [EVENT_TEXT_SELECTION_CHANGED, acc], [EVENT_TEXT_CARET_MOVED, acc], ]); await runPython(` doc = getDocUia() acc = findUiaByDomId(doc, "${id}") text = getUiaPattern(acc, "Text") global range range = text.DocumentRange range.ExpandToEnclosingUnit(TextUnit_Character) range.RemoveFromSelection() `); await moved; testTextSelectionCount(acc, 1); testTextGetSelection(acc, 2, 3, 0); info("Removing b from selection even though it isn't selected"); await runPython(` # Move start to b. range.MoveEndpointByUnit(TextPatternRangeEndpoint_Start, TextUnit_Character, 1) range.ExpandToEnclosingUnit(TextUnit_Character) `); await testPythonRaises( `range.RemoveFromSelection()`, "RemoveFromSelection failed" ); info("Removing c from selection"); moved = waitForEvent(EVENT_TEXT_SELECTION_CHANGED, acc); await runPython(` # Move start to c. range.MoveEndpointByUnit(TextPatternRangeEndpoint_Start, TextUnit_Character, 1) range.ExpandToEnclosingUnit(TextUnit_Character) range.RemoveFromSelection() `); await moved; testTextSelectionCount(acc, 0); } }, // The IA2 -> UIA proxy doesn't support RemoveFromSelection correctly. { uiaEnabled: true, uiaDisabled: false } ); /** * Test the TextRange pattern's FindAttribute method. */ addUiaTask( `
a bcd ef ghi
`, async function testTextRangeFindAttribute(_browser, _docAcc) { info("Constructing range on bold text run"); await runPython(` global doc, docText, range, fontWeightContainerAcc doc = getDocUia() docText = getUiaPattern(doc, "Text") fontWeightContainerAcc = findUiaByDomId(doc, "font-weight-container") range = docText.RangeFromChild(fontWeightContainerAcc) `); is( await runPython(`range.GetText(-1)`), "a bcd ef ghi", "range text correct" ); info("Finding first font-weight 400 text range"); await runPython(` global subrange subrange = range.FindAttribute(UIA_FontWeightAttributeId, 400, False) `); is(await runPython(`subrange.GetText(-1)`), "a ", "range text correct"); info("Finding first font-weight 700 text range"); await runPython(` global subrange subrange = range.FindAttribute(UIA_FontWeightAttributeId, 700, False) `); is(await runPython(`subrange.GetText(-1)`), "bcd ef", "range text correct"); info("Finding last font-weight 700 text range"); await runPython(` global subrange subrange = range.FindAttribute(UIA_FontWeightAttributeId, 700, True) `); is(await runPython(`subrange.GetText(-1)`), "bcd ef", "range text correct"); info("Finding last font-weight 400 text range"); await runPython(` global subrange subrange = range.FindAttribute(UIA_FontWeightAttributeId, 400, True) `); is(await runPython(`subrange.GetText(-1)`), " ghi", "range text correct"); // The IA2 -> UIA proxy gets things below this wrong. if (!gIsUiaEnabled) { return; } info("Moving range to the middle of a text attribute run"); is( await runPython( `range.MoveEndpointByUnit(TextPatternRangeEndpoint_Start, TextUnit_Character, 4)` ), 4, "MoveEndpointByUnit return correct" ); is(await runPython(`range.GetText(-1)`), "cd ef ghi", "range text correct"); info( "Finding first font-weight 700 text range (range starts in middle of text attribute run)" ); await runPython(` global subrange subrange = range.FindAttribute(UIA_FontWeightAttributeId, 700, False) `); is(await runPython(`subrange.GetText(-1)`), "cd ef", "range text correct"); await runPython(` global range range = docText.RangeFromChild(fontWeightContainerAcc) `); is( await runPython(`range.GetText(-1)`), "a bcd ef ghi", "range text correct" ); is( await runPython( `range.MoveEndpointByUnit(TextPatternRangeEndpoint_End, TextUnit_Character, -5)` ), -5, "MoveEndpointByUnit return correct" ); is(await runPython(`range.GetText(-1)`), "a bcd e", "range text correct"); info( "Finding last font-weight 700 text range (range ends in middle of text attribute run)" ); await runPython(` global subrange subrange = range.FindAttribute(UIA_FontWeightAttributeId, 700, True) `); is(await runPython(`subrange.GetText(-1)`), "bcd e", "range text correct"); info("Collapsing range at start"); await runPython(` global subrange subrange = range.Clone() subrange.MoveEndpointByRange(TextPatternRangeEndpoint_End, subrange, TextPatternRangeEndpoint_Start) `); is(await runPython(`subrange.GetText(-1)`), "", "subrange text correct"); info("Finding last font-weight 400 text range on collapsed range"); await runPython(` global subrange subrange = subrange.FindAttribute(UIA_FontWeightAttributeId, 400, True) `); is(await runPython(`subrange.GetText(-1)`), "", "subrange text correct"); }, { uiaEnabled: true, uiaDisabled: true } ); /** * Test the Text pattern's GetVisibleRanges method. */ addUiaTask( `

line1

line2

line3

line4

line8

`, async function testTextGetVisibleRanges() { await runPython(` global doc, docText, ranges doc = getDocUia() docText = getUiaPattern(doc, "Text") ranges = docText.GetVisibleRanges() `); // XXX This should be 4 once we fix the scrolling case below. is( await runPython(`ranges.Length`), 6, "doc has correct number of visible ranges" ); is( await runPython(`ranges.GetElement(0).GetText(-1)`), "line1", "range 0 text correct" ); is( await runPython(`ranges.GetElement(1).GetText(-1)`), "line2", "range 1 text correct" ); // line3 is off-screen and thus not visible. is( await runPython(`ranges.GetElement(2).GetText(-1)`), "line4", "range 2 text correct" ); is( await runPython(`ranges.GetElement(3).GetText(-1)`), "line5\n", "range 3 text correct" ); // XXX line6 and line7 are scrolled off screen by the textarea, but we // incorrectly return them for now (ranges 4 and 5). // line8 is scrolled off screen by the document. await runPython(` textarea = findUiaByDomId(doc, "textarea") textareaText = getUiaPattern(textarea, "Text") global ranges ranges = textareaText.GetVisibleRanges() `); is( await runPython(`ranges.Length`), 1, "textarea has correct number of visible ranges" ); is( await runPython(`ranges.GetElement(0).GetText(-1)`), "line5\n", "range 0 text correct" ); // line6 and line7 are scrolled off screen by the textarea. await runPython(` strong = findUiaByDomId(doc, "strong") strongLeaf = uiaClient.RawViewWalker.GetFirstChildElement(strong) strongText = getUiaPattern(strongLeaf, "Text") global ranges ranges = strongText.GetVisibleRanges() `); is( await runPython(`ranges.Length`), 1, "strong leaf has correct number of visible ranges" ); is( await runPython(`ranges.GetElement(0).GetText(-1)`), "line", "range 0 text correct" ); }, // The IA2 -> UIA proxy doesn't support GetVisibleRanges. { uiaEnabled: true, uiaDisabled: false } ); /** * Test the TextRange pattern's FindText method. */ addUiaTask( `
abcTEST
def
TEST

ghi

`, async function testTextRangeFromChild() { await runPython(` global doc, docText, container, range doc = getDocUia() docText = getUiaPattern(doc, "Text") container = findUiaByDomId(doc, "container") range = docText.RangeFromChild(container) `); // The IA2 -> UIA bridge inserts a space at the end of the text. if (gIsUiaEnabled) { is( await runPython(`range.GetText(-1)`), `abcTESTdefTESTghi`, "doc returned correct range for container" ); } info("Finding 'abc', searching from the start"); await runPython(` global subrange subrange = range.FindText("abc", False, False) `); is(await runPython(`subrange.GetText(-1)`), "abc", "range text correct"); info("Finding 'abc', searching from the end"); await runPython(` global subrange subrange = range.FindText("abc", True, False) `); is(await runPython(`subrange.GetText(-1)`), "abc", "range text correct"); info("Finding 'ghi', searching from the start"); await runPython(` global subrange subrange = range.FindText("ghi", False, False) `); is(await runPython(`subrange.GetText(-1)`), "ghi", "range text correct"); info("Finding 'ghi', searching from the end"); await runPython(` global subrange subrange = range.FindText("ghi", True, False) `); is(await runPython(`subrange.GetText(-1)`), "ghi", "range text correct"); info("Finding 'TEST', searching from the start"); await runPython(` global subrange subrange = range.FindText("TEST", False, False) `); is(await runPython(`subrange.GetText(-1)`), "TEST", "range text correct"); info("Finding 'TEST', searching from the end"); await runPython(` global subrange2 subrange2 = range.FindText("TEST", True, False) `); is(await runPython(`subrange2.GetText(-1)`), "TEST", "range text correct"); ok( !(await runPython(`subrange.compare(subrange2)`)), "ranges are not equal" ); info("Finding 'test', searching from the start, case-sensitive"); await runPython(` global subrange subrange = range.FindText("test", False, False) `); ok(await runPython(`not subrange`), "range not found"); info("Finding 'test', searching from the start, case-insensitive"); await runPython(` global subrange subrange = range.FindText("test", False, True) `); is(await runPython(`subrange.GetText(-1)`), "TEST", "range text correct"); }, { uiaEnabled: true, uiaDisabled: true } ); const textChildSnippet = `

p

a img
textboxLeaf

textboxP

`; /** * Test the TextChild pattern's TextContainer property. */ addUiaTask(textChildSnippet, async function testTextChildTextContainer() { ok( await runPython(` global doc, p doc = getDocUia() p = findUiaByDomId(doc, "p") tc = getUiaPattern(p, "TextChild") return uiaClient.CompareElements(tc.TextContainer, doc) `), "p TextContainer is doc" ); // The IA2 -> UIA proxy doesn't support the TextChild pattern on text // leaves. if (gIsUiaEnabled) { ok( await runPython(` pLeaf = uiaClient.RawViewWalker.GetFirstChildElement(p) tc = getUiaPattern(pLeaf, "TextChild") return uiaClient.CompareElements(tc.TextContainer, doc) `), "p leaf TextContainer is doc" ); } ok( await runPython(` a = findUiaByDomId(doc, "a") tc = getUiaPattern(a, "TextChild") return uiaClient.CompareElements(tc.TextContainer, doc) `), "a TextContainer is doc" ); ok( await runPython(` img = findUiaByDomId(doc, "img") tc = getUiaPattern(img, "TextChild") return uiaClient.CompareElements(tc.TextContainer, doc) `), "img TextContainer is doc" ); ok( await runPython(` global textbox textbox = findUiaByDomId(doc, "textbox") tc = getUiaPattern(textbox, "TextChild") return uiaClient.CompareElements(tc.TextContainer, doc) `), "textbox TextContainer is doc" ); // The IA2 -> UIA proxy doesn't support the TextChild pattern on text // leaves. if (gIsUiaEnabled) { ok( await runPython(` textboxLeaf = uiaClient.RawViewWalker.GetFirstChildElement(textbox) tc = getUiaPattern(textboxLeaf, "TextChild") return uiaClient.CompareElements(tc.TextContainer, textbox) `), "textbox leaf TextContainer is textbox" ); } ok( await runPython(` textboxP = findUiaByDomId(doc, "textboxP") tc = getUiaPattern(textboxP, "TextChild") return uiaClient.CompareElements(tc.TextContainer, textbox) `), "textboxP TextContainer is textbox" ); }); /** * Test the TextChild pattern's TextRange property. */ addUiaTask(textChildSnippet, async function testTextChildTextRange() { is( await runPython(` global doc, p doc = getDocUia() p = findUiaByDomId(doc, "p") tc = getUiaPattern(p, "TextChild") return tc.TextRange.GetText(-1) `), "p", "p text correct" ); // The IA2 -> UIA proxy doesn't support the TextChild pattern on text // leaves. if (gIsUiaEnabled) { is( await runPython(` pLeaf = uiaClient.RawViewWalker.GetFirstChildElement(p) tc = getUiaPattern(pLeaf, "TextChild") return tc.TextRange.GetText(-1) `), "p", "p leaf text correct" ); } is( await runPython(` a = findUiaByDomId(doc, "a") tc = getUiaPattern(a, "TextChild") return tc.TextRange.GetText(-1) `), "a", "a text correct" ); if (gIsUiaEnabled) { // The IA2 -> UIA proxy doesn't expose an embedded object character for // images. is( await runPython(` img = findUiaByDomId(doc, "img") tc = getUiaPattern(img, "TextChild") return tc.TextRange.GetText(-1) `), kEmbedChar, "img text correct" ); // The IA2 -> UIA proxy adds spaces between elements that don't exist. is( await runPython(` global textbox textbox = findUiaByDomId(doc, "textbox") tc = getUiaPattern(textbox, "TextChild") return tc.TextRange.GetText(-1) `), "textboxLeaf textboxP", "textbox text correct" ); // The IA2 -> UIA proxy doesn't support the TextChild pattern on text // leaves. is( await runPython(` textboxLeaf = uiaClient.RawViewWalker.GetFirstChildElement(textbox) tc = getUiaPattern(textboxLeaf, "TextChild") return tc.TextRange.GetText(-1) `), "textboxLeaf ", "textbox leaf text correct" ); } is( await runPython(` textboxP = findUiaByDomId(doc, "textboxP") tc = getUiaPattern(textboxP, "TextChild") return tc.TextRange.GetText(-1) `), "textboxP", "textboxP text correct" ); });