Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Define Selection.rangeCount when an input field is focused #166

Open
whsieh opened this issue Aug 9, 2023 · 3 comments
Open

Define Selection.rangeCount when an input field is focused #166

whsieh opened this issue Aug 9, 2023 · 3 comments

Comments

@whsieh
Copy link

whsieh commented Aug 9, 2023

Hello!

In the process of investigating a web compat issue in WebKit that affects Quip's editor, I stumbled into the fact that Selection.rangeCount behaves differently across FF, Chrome and Safari when the selection is moved into a text field.

Consider the following reduced test case:

<input />
<script>
document.querySelector("input").focus();
document.write(`rangeCount := ${getSelection().rangeCount}`);
</script>

Safari 16 and Chrome will show a rangeCount of 1. However, Safari 17 (with Live Range Selection enabled, at time of writing) and Firefox both show a rangeCount of 0. However, if I adjust the test case to additionally set the DOM selection prior to focusing the input:

<input />
<script>
getSelection().setPosition(document.body, 0);
document.querySelector("input").focus();
document.write(`rangeCount := ${getSelection().rangeCount}`);
</script>

...then Firefox outputs 1 instead of 0. The behavior in Firefox seems to be that moving focus into an input field doesn't affect selection ranges, whereas behavior in Safari 16 and Chrome is that the selection will move to the start of the containing shadow host (in this case, the input element). In Safari 17, with Live Range Selection enabled, we'll now expose a rangeCount of 0 after moving focus into a text field.

Edit: this is essentially a small piece of #83, that's just about the rangeCount.

webkit-commit-queue pushed a commit to whsieh/WebKit that referenced this issue Aug 10, 2023
https://bugs.webkit.org/show_bug.cgi?id=259944
rdar://113229516

Reviewed by Ryosuke Niwa.

Currently, the `@math` feature in Quip is broken in Safari with Live Range Selection enabled; this
is because Quip's JavaScript editor focuses an input element, and expects `selection.rangeCount` to
return `1` instead of `0` (the latter of which causes Quip to believe that the user is no longer
editing the text field, and therefore leads to Quip dismissing the input field immediately after
presenting it).

This happens because the `rangeCount` and ranges we expose on `DOMSelection` are different with live
range selection in Safari 17, as opposed to both Safari 16 and Chrome (testing against both stable
and canary). Whereas we used to expose a single range that was collapsed to the start of the text
field, we now expose no ranges at all.

To avoid this incompatibility, we restore shipping behavior when the selection is inside of shadow
roots, by exposing a selection range that's equivalent to a caret before the shadow host.

See also: <w3c/selection-api#166>.

Test: fast/forms/shadow-tree-exposure-live-range.html

* LayoutTests/fast/forms/shadow-tree-exposure-live-range-expected.txt:
* LayoutTests/fast/forms/shadow-tree-exposure-live-range.html:

Rebaseline this layout test, such that it verifies that the ranges exposed via the Selection API
have the same behavior without live ranges, vs. with live ranges enabled; this also matches the
behavior in latest Chrome Canary.

* LayoutTests/imported/w3c/web-platform-tests/editing/other/editable-state-and-focus-in-shadow-dom-in-designMode.tentative-expected.txt:
* LayoutTests/platform/glib/imported/w3c/web-platform-tests/editing/other/editable-state-and-focus-in-shadow-dom-in-designMode.tentative-expected.txt:
* LayoutTests/platform/ios/imported/w3c/web-platform-tests/editing/other/editable-state-and-focus-in-shadow-dom-in-designMode.tentative-expected.txt:

Rebaseline a WPT — attempts to change the selection in a UA shadow root in this test now result in
the wrong base/anchor nodes and offsets after this patch, rather than the `IndexSizeError`s we
currently get.

* Source/WebCore/page/DOMSelection.cpp:
(WebCore::selectionShadowAncestor):

Remove an assertion that's no longer relevant, now that we use this in both live/legacy codepaths.

(WebCore::DOMSelection::type const):

Keep both `fast/forms/shadow-tree-exposure-live-range.html` and
`editing/selection/user-select-js-property.html` passing by adjusting our logic for returning the
selection type. Currently, a selection in a shadow root always results in a type of `None`, but this
diverges from Chrome (and Safari 16's) behavior, which returns the actual type of the selected
range, regardless of whether it's in the shadow tree.

(WebCore::DOMSelection::rangeCount const):

This is the main fix — return a `rangeCount` of 1 in the case where there are no associated live
ranges, if the range is still inside a shadow root.

(WebCore::createLiveRangeBeforeShadowHostWithSelection):
(WebCore::DOMSelection::getRangeAt):

Keep `getRangeAt` consistent with `rangeCount` by returning the caret range at the start of the
shadow host in the case where the selected range is in the shadow tree.

(WebCore::DOMSelection::shadowAdjustedNode const):
(WebCore::DOMSelection::shadowAdjustedOffset const):

These methods need to be adjusted as well, to keep the `(base|anchor|focus|extent)(Node|Offset)`
properties consistent with the `rangeCount` and selection ranges in the case where the selection is
inside of a shadow root. Since the behavior with or without live ranges when the selection is in a
shadow tree is now consistent, we no longer require live-range-specific codepaths, so we can
simplify this code a bit by just removing the `liveRangeSelectionEnabled()` checks.

Canonical link: https://commits.webkit.org/266781@main
@annevk
Copy link
Member

annevk commented Sep 12, 2023

cc @smaug---- @mfreed7

@smaug----
Copy link

FWIW, input type=text element in Firefox doesn't have shadow DOM inside it.

@masayuki-nakano
Copy link

Firefox may have multiple Selection objects per document. One is for the Document which is exposed with getSelection(). The others are independent objects for each <input> and <textarea> to make the selection range(s) restored easy at re-focus. And Firefox does not move the document selection at moving focus to a form control with a mouse click, but otherwise, e.g., tab navigation or Element.focus(), collapse Selection before the form control.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants