Bug 285848
| Summary: | AX: AXTextOperation performs an incorrect selection on some shadow elements. | ||
|---|---|---|---|
| Product: | WebKit | Reporter: | Samar Sunkaria <samar.sunkaria> |
| Component: | Accessibility | Assignee: | Nobody <webkit-unassigned> |
| Status: | RESOLVED FIXED | ||
| Severity: | Normal | CC: | andresg_22, webkit-bug-importer |
| Priority: | P2 | Keywords: | InRadar |
| Version: | Safari 18 | ||
| Hardware: | All | ||
| OS: | All | ||
Samar Sunkaria
When using AXTextOperation to select or replace text in the Reddit comment or new post field, the selection is performed at an incorrect offset within the text field. The correct selection can be performed by setting the AXSelectedTextMarkerRange attribute.
I have yet to narrow down the exact configuration of the HTML document that causes this issue. However, within the WebKit sources, I have made the following observations:
`AccessibilityObject::performTextOperation()` converts the SimpleRanges (obtained from the text marker ranges provided as a parameter to AXTextOperation) into character ranges with respect to the "root editable element" corresponding to the range. The logic for this conversion is implemented in `textOperationRangeFromRange`. This works as expected in most cases.
However, `textOperationRangeFromRange` returns an incorrect result for the comment and new post field on Reddit (and perhaps other such fields implemented in a similar manner). The start of the SimpleRange corresponding to the "root editable element" _is greater than_ the start of the SimpleRange passed in (compared using `treeOrder`). This is unexpected because the "root editable element" must be a parent of the startContainer of the provided SimpleRange. What makes it weirder is that the `characterCount` from the start of the range to the _start of the editable root_ is the same as the `characterCount` from the start of the range to the _end of the editable root_, but the `characterCount` of the range of the root editable element is non-zero.
| Attachments | ||
|---|---|---|
| Add attachment proposed patch, testcase, etc. |
Radar WebKit Bug Importer
<rdar://problem/142814532>
Samar Sunkaria
Found a reduced case. It only happens for elements exposed via a slot from a shadow element.
<html>
<body>
<div id="top-div">
<div slot="slot-name" contenteditable="true">
The quick brown fox jumps over the lazy dog
</div>
</div>
<script>
const div = document.getElementById("top-div");
const shadow = div.attachShadow({ mode: "open" });
const slot = document.createElement("slot");
slot.name = "slot-name";
shadow.appendChild(slot);
</script>
</body>
</html>
Samar Sunkaria
It seems like `treeOrder` (for a ComposedTree) may incorrectly handle slotted elements in the light DOM that are rendered via the shadow DOM.
In my case, we end up comparing the BoundaryPoint for the start of the editable root (`top-div` + **offset: 1**) [A] against a BoundaryPoint within the textual content (#text + some offset) [B]. The expected result of this comparison should be A < B, however we get A > B.
We enter the following branch for `isOffsetBeforeChild` with the `child` being a ShadowRoot (ancestor of B), hence A > B:
```
// If the container is not the parent, the child is part of a shadow tree, which we sort between offset 0 and offset 1.
if (child.parentNode() != &container)
return false;
```
I would have expected that this comparison would correctly handle slotted elements.
Samar Sunkaria
I can work around this issue by getting the BoundaryPoint at the first and last position *in* the root editable element instead of the BoundaryPoints of the root editable element.
Samar Sunkaria
Pull request: https://github.com/WebKit/WebKit/pull/39190
EWS
Committed 289092@main (d9b88a76d943): <https://commits.webkit.org/289092@main>
Reviewed commits have been landed. Closing PR #39190 and removing active labels.