Skip to content

Commit

Permalink
AX: TextInputMarkedTextMarkerRange should be cached during initializa…
Browse files Browse the repository at this point in the history
…tion

https://bugs.webkit.org/show_bug.cgi?id=265434
rdar://problem/118866640

Reviewed by Tyler Wilcock.

When isolated objects are created for text controls, we do not cache the initial TextInputMarkedTextMarkerRange
value. This becomes an issue when an object is initialized while there is already marked text. In that case, not
caching that value initially will return a null text marker range, which is incorrect.

This patch caches that value when initializing the object, as well as updates related tests to (1) verify this
behavior and (2) retrieve that range asynchronously.

* LayoutTests/accessibility/mac/text-input-marked-range-expected.txt:
* LayoutTests/accessibility/mac/text-input-marked-range.html:
* LayoutTests/accessibility/mac/text-input-marked-text-marker-range-expected.txt:
* LayoutTests/accessibility/mac/text-input-marked-text-marker-range.html:
* Source/WebCore/accessibility/isolatedtree/AXIsolatedObject.cpp:
(WebCore::AXIsolatedObject::initializeProperties):

Canonical link: https://commits.webkit.org/271251@main
  • Loading branch information
hoffmanjoshua authored and twilco committed Nov 29, 2023
1 parent 2302494 commit 5965a5a
Show file tree
Hide file tree
Showing 5 changed files with 59 additions and 46 deletions.
Original file line number Diff line number Diff line change
@@ -1,12 +1,9 @@
This test ensures that the input method marked range is available to accessibility clients.

On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
PASS: element.textInputMarkedRange === '{0, 4}'
PASS: !element.textInputMarkedRange === true
PASS: element.textInputMarkedRange === '{1, 5}'


PASS !element.textInputMarkedRange is true
PASS element.textInputMarkedRange is '{0, 4}'
PASS !element.textInputMarkedRange is true
PASS element.textInputMarkedRange is '{1, 5}'
PASS successfullyParsed is true

TEST COMPLETE
Expand Down
41 changes: 23 additions & 18 deletions LayoutTests/accessibility/mac/text-input-marked-range.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,25 +2,30 @@
<html>
<head>
<script src="../../resources/js-test.js"></script>
<script src="../../resources/accessibility-helper.js"></script>
</head>
<body>
<input id="editable" type="text"></input>
<script type="text/javascript">
description("This test ensures that the input method marked range is available to accessibility clients.");
editable.focus();
if (window.textInputController && window.accessibilityController) {
var element = accessibilityController.accessibleElementById("editable");
shouldBeTrue("!element.textInputMarkedRange");

textInputController.setMarkedText("test", 1, 0);
shouldBe("element.textInputMarkedRange", "'{0, 4}'");

textInputController.insertText("t");
shouldBeTrue("!element.textInputMarkedRange");

textInputController.setMarkedText("test2", 1, 0);
shouldBe("element.textInputMarkedRange", "'{1, 5}'");
}
</script>
<input id="editable" type="text"></input>
<script type="text/javascript">
let output = "This test ensures that the input method marked range is available to accessibility clients.\n\n";
editable.focus();
if (window.textInputController && window.accessibilityController) {
window.jsTestIsAsync = true;
textInputController.setMarkedText("test", 1, 0);
var element = accessibilityController.accessibleElementById("editable");
output += expect("element.textInputMarkedRange", "'{0, 4}'");

setTimeout(async function() {
textInputController.insertText("t");
output += await expectAsync("!element.textInputMarkedRange", "true");

textInputController.setMarkedText("test2", 1, 0);
output += await expectAsync("element.textInputMarkedRange", "'{1, 5}'");

debug(output);
finishJSTest();
});
}
</script>
</body>
</html>
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
This test ensures that the input method marked range is available to accessibility clients as text marker range.

On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
PASS: !text === true
PASS: element.stringForTextMarkerRange(element.textInputMarkedTextMarkerRange()) === 'test'
PASS: !element.stringForTextMarkerRange(element.textInputMarkedTextMarkerRange()) === true
PASS: element.stringValue === 'AXValue: test'
PASS: text === ' message'
PASS: element.stringValue === 'AXValue: test message'


PASS !text is true
PASS text is 'test'
PASS !text is true
PASS element.stringValue is 'AXValue: test'
PASS text is ' message'
PASS element.stringValue is 'AXValue: test message'
PASS successfullyParsed is true

TEST COMPLETE
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,31 +2,39 @@
<html>
<head>
<script src="../../resources/js-test.js"></script>
<script src="../../resources/accessibility-helper.js"></script>
</head>
<body>
<input id="editable" type="text">
<script type="text/javascript">
description("This test ensures that the input method marked range is available to accessibility clients as text marker range.");
let output = "This test ensures that the input method marked range is available to accessibility clients as text marker range.\n\n";
editable.focus();
if (window.textInputController && window.accessibilityController) {
window.jsTestIsAsync = true;
var element = accessibilityController.accessibleElementById("editable");

var text = element.stringForTextMarkerRange(element.textInputMarkedTextMarkerRange());
shouldBeTrue("!text");

output += expect("!text", "true");
textInputController.setMarkedText("test");
text = element.stringForTextMarkerRange(element.textInputMarkedTextMarkerRange());
shouldBe("text", "'test'");

textInputController.unmarkText();
text = element.stringForTextMarkerRange(element.textInputMarkedTextMarkerRange());
shouldBeTrue("!text");
shouldBe("element.stringValue", "'AXValue: test'");
setTimeout(async function() {
output += await expectAsync("element.stringForTextMarkerRange(element.textInputMarkedTextMarkerRange())", "'test'");

textInputController.unmarkText();
output += await expectAsync("!element.stringForTextMarkerRange(element.textInputMarkedTextMarkerRange())", "true");
output += await expectAsync("element.stringValue", "'AXValue: test'");

textInputController.setMarkedText(" message", 4, " message".length);
await waitFor(() => {
text = element.stringForTextMarkerRange(element.textInputMarkedTextMarkerRange());
return text == " message";
});
output += await expectAsync("text", "' message'");
output += await expectAsync("element.stringValue", "'AXValue: test message'");

textInputController.setMarkedText(" message", 4, " message".length);
text = element.stringForTextMarkerRange(element.textInputMarkedTextMarkerRange());
shouldBe("text", "' message'");
shouldBe("element.stringValue", "'AXValue: test message'");
debug(output);
finishJSTest();
});
}
</script>
</body>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -343,9 +343,14 @@ void AXIsolatedObject::initializeProperties(const Ref<AccessibilityObject>& axOb
if (descriptor.length())
setProperty(AXPropertyName::ExtendedDescription, descriptor.isolatedCopy());

if (object.isTextControl())
if (object.isTextControl()) {
setProperty(AXPropertyName::SelectedTextRange, object.selectedTextRange());

auto range = object.textInputMarkedTextMarkerRange();
if (auto characterRange = range.characterRange(); range && characterRange)
setProperty(AXPropertyName::TextInputMarkedTextMarkerRange, std::pair<AXID, CharacterRange>(range.start().objectID(), *characterRange));
}

// These properties are only needed on the AXCoreObject interface due to their use in ATSPI,
// so only cache them for ATSPI.
#if PLATFORM(ATSPI)
Expand Down

0 comments on commit 5965a5a

Please sign in to comment.