Skip to content

Commit

Permalink
Selection should work across shadow boundary when initiated by a mous…
Browse files Browse the repository at this point in the history
…e drag

https://bugs.webkit.org/show_bug.cgi?id=151380
<rdar://problem/24363872>

Source/WebCore:

Reviewed by Antti Koivisto and Wenson Hsieh.

This patch adds the basic support for selecting content across shadow DOM boundaries to VisibleSelection,
which is enough to allow users to select content across shadow DOM boundaries via a mouse drag.

This is the first step in allowing users to select, copy and paste content across shadow DOM boundaries,
which is a serious user experience regression right now. The new behavior is disabled by default under
an interal debug feature flag: selectionAcrossShadowBoundariesEnabled.

Like Chrome, we are not going to support selecting editable content across shadow DOM boundaries since
we'd have to generalize every editing commands to make that work, and there aren't any HTML editors that
use shadow DOM boundaries within an editable region yet. For simplicity, we also don't support extending
a selection out of a shadow root which resides inside an editing region.

The keyboard based navigation & manipulation of selection as well as allowing copy & paste of content
across shadow DOM boundaries will be implemented by separate patches. DOMSelection will not expose this new
behavior either. This is tracked in the spec as WICG/webcomponents#79

Tests: editing/selection/selection-across-shadow-boundaries-mixed-editability-1.html
       editing/selection/selection-across-shadow-boundaries-mixed-editability-2.html
       editing/selection/selection-across-shadow-boundaries-mixed-editability-3.html
       editing/selection/selection-across-shadow-boundaries-mixed-editability-4.html
       editing/selection/selection-across-shadow-boundaries-mixed-editability-5.html
       editing/selection/selection-across-shadow-boundaries-readonly-1.html
       editing/selection/selection-across-shadow-boundaries-readonly-2.html
       editing/selection/selection-across-shadow-boundaries-readonly-3.html
       editing/selection/selection-across-shadow-boundaries-user-select-all-1.html

* editing/VisibleSelection.cpp:
(WebCore::isInUserAgentShadowRootOrHasEditableShadowAncestor): Added.
(WebCore::VisibleSelection::adjustSelectionToAvoidCrossingShadowBoundaries): When the feature is enabled,
allow crossing shadow DOM boundaries except when either end is inside an user agent shadow root, or one of
its shadow includign ancestor is inside an editable region. The latter check is needed to disallow
an extension of a selection starting in a shadow tree inside a non-editable region inside an editable region
to outside the editable region. The rest of the editing code is not ready to deal with selection like that.
* page/Settings.yaml: Added an internal debug feature to enable this new behavior.

Source/WebKit:

Reviewed by Antti Koivisto.

Added SelectionAcrossShadowBoundariesEnabled as an internal debug feature,
and moved CSSCustomPropertiesAndValuesEnabled to where other experimental features are located.

* Shared/WebPreferences.yaml:

Source/WebKitLegacy/mac:

Reviewed by Wenson Hsieh.

Added selectionAcrossShadowBoundariesEnabled as a preference to be used in DumpRenderTree.

* WebView/WebPreferenceKeysPrivate.h:
* WebView/WebPreferences.mm:
(+[WebPreferences initialize]):
(-[WebPreferences selectionAcrossShadowBoundariesEnabled]):
(-[WebPreferences setSelectionAcrossShadowBoundariesEnabled:]):
* WebView/WebPreferencesPrivate.h:
* WebView/WebView.mm:
(-[WebView _preferencesChanged:]):

Tools:

Reviewed by Wenson Hsieh.

Added the support for internal:selectionAcrossShadowBoundariesEnabled test option.

* DumpRenderTree/TestOptions.cpp:
(TestOptions::TestOptions):
* DumpRenderTree/TestOptions.h:
* DumpRenderTree/mac/DumpRenderTree.mm:
(resetWebPreferencesToConsistentValues):
(setWebPreferencesForTestOptions):

LayoutTests:

Reviewed by Antti Koivisto and Wenson Hsieh.

Added regression tests using ref tests since getSelection() doesn't expose any node inside a shadow tree.

* editing/selection/selection-across-shadow-boundaries-mixed-editability-1-expected.html: Added.
* editing/selection/selection-across-shadow-boundaries-mixed-editability-1.html: Added.
* editing/selection/selection-across-shadow-boundaries-mixed-editability-2-expected.html: Added.
* editing/selection/selection-across-shadow-boundaries-mixed-editability-2.html: Added.
* editing/selection/selection-across-shadow-boundaries-mixed-editability-3-expected.html: Added.
* editing/selection/selection-across-shadow-boundaries-mixed-editability-3.html: Added.
* editing/selection/selection-across-shadow-boundaries-mixed-editability-4-expected.html: Added.
* editing/selection/selection-across-shadow-boundaries-mixed-editability-4.html: Added.
* editing/selection/selection-across-shadow-boundaries-mixed-editability-5-expected.html: Added.
* editing/selection/selection-across-shadow-boundaries-mixed-editability-5.html: Added.
* editing/selection/selection-across-shadow-boundaries-readonly-1-expected.html: Added.
* editing/selection/selection-across-shadow-boundaries-readonly-1.html: Added.
* editing/selection/selection-across-shadow-boundaries-readonly-2-expected.html: Added.
* editing/selection/selection-across-shadow-boundaries-readonly-2.html: Added.
* editing/selection/selection-across-shadow-boundaries-readonly-3-expected.html: Added.
* editing/selection/selection-across-shadow-boundaries-readonly-3.html: Added.
* editing/selection/selection-across-shadow-boundaries-user-select-all-1-expected.html: Added.
* editing/selection/selection-across-shadow-boundaries-user-select-all-1.html: Added.


git-svn-id: http://svn.webkit.org/repository/webkit/trunk@236519 268f45cc-cd09-0410-ab3c-d52691b4dbfc
  • Loading branch information
rniwa@webkit.org committed Sep 26, 2018
1 parent 5a7b63e commit dc2fc17
Show file tree
Hide file tree
Showing 34 changed files with 598 additions and 12 deletions.
29 changes: 29 additions & 0 deletions LayoutTests/ChangeLog
Original file line number Diff line number Diff line change
@@ -1,3 +1,32 @@
2018-09-26 Ryosuke Niwa <rniwa@webkit.org>

Selection should work across shadow boundary when initiated by a mouse drag
https://bugs.webkit.org/show_bug.cgi?id=151380
<rdar://problem/24363872>

Reviewed by Antti Koivisto and Wenson Hsieh.

Added regression tests using ref tests since getSelection() doesn't expose any node inside a shadow tree.

* editing/selection/selection-across-shadow-boundaries-mixed-editability-1-expected.html: Added.
* editing/selection/selection-across-shadow-boundaries-mixed-editability-1.html: Added.
* editing/selection/selection-across-shadow-boundaries-mixed-editability-2-expected.html: Added.
* editing/selection/selection-across-shadow-boundaries-mixed-editability-2.html: Added.
* editing/selection/selection-across-shadow-boundaries-mixed-editability-3-expected.html: Added.
* editing/selection/selection-across-shadow-boundaries-mixed-editability-3.html: Added.
* editing/selection/selection-across-shadow-boundaries-mixed-editability-4-expected.html: Added.
* editing/selection/selection-across-shadow-boundaries-mixed-editability-4.html: Added.
* editing/selection/selection-across-shadow-boundaries-mixed-editability-5-expected.html: Added.
* editing/selection/selection-across-shadow-boundaries-mixed-editability-5.html: Added.
* editing/selection/selection-across-shadow-boundaries-readonly-1-expected.html: Added.
* editing/selection/selection-across-shadow-boundaries-readonly-1.html: Added.
* editing/selection/selection-across-shadow-boundaries-readonly-2-expected.html: Added.
* editing/selection/selection-across-shadow-boundaries-readonly-2.html: Added.
* editing/selection/selection-across-shadow-boundaries-readonly-3-expected.html: Added.
* editing/selection/selection-across-shadow-boundaries-readonly-3.html: Added.
* editing/selection/selection-across-shadow-boundaries-user-select-all-1-expected.html: Added.
* editing/selection/selection-across-shadow-boundaries-user-select-all-1.html: Added.

2018-09-26 Alicia Boya García <aboya@igalia.com>

[GTK] Unreviewed test gardening
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<!DOCTYPE html>
<html>
<body>
<p>This tests selecting content starting in an editable element to a shadow tree.<br>
To manually test, select "hello world" below by a mouse drag from "h" to "d". WebKit should only select "hello".</p>
<div id="container" contenteditable>hello <div>world</div></div>
<script>
container.focus();
getSelection().setBaseAndExtent(container, 0, container, 1);
</script>
</body>
</html>
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<!DOCTYPE html><!-- webkit-test-runner [ internal:selectionAcrossShadowBoundariesEnabled=true ] -->
<html>
<body>
<p>This tests selecting content starting in an editable element to a shadow tree.<br>
To manually test, select "hello world" below by a mouse drag from "h" to "d". WebKit should only select "hello".</p>
<div id="container" contenteditable>hello </div>
<script>

const host = document.createElement('div');
container.appendChild(host);
const shadowRoot = host.attachShadow({mode: 'closed'});
shadowRoot.textContent = 'world';

if (window.eventSender) {
eventSender.dragMode = false;
eventSender.mouseMoveTo(container.offsetLeft + 1, container.offsetTop + 5);
eventSender.mouseDown();
eventSender.mouseMoveTo(container.offsetLeft + container.offsetWidth - 5, container.offsetTop + container.offsetHeight - 5);
eventSender.mouseUp();
if (getSelection().startContainer.getRootNode() != document)
document.write("The start container's root node was not the document");
if (getSelection().startContainer != getSelection().endContainer)
document.write("The end container was different from the start container");
if (getSelection().getRangeAt(0).startContainer.getRootNode() != document)
document.write("The range's start container's root node was not the document");
if (getSelection().getRangeAt(0).endContainer != getSelection().getRangeAt(0).startContainer)
document.write("The range's end container was different from its start container");
}

</script>
</body>
</html>
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<!DOCTYPE html>
<html>
<body>
<p>This tests selecting content starting in a shadow tree out to an editable element.<br>
To manually test, select "hello world" below by a mouse drag from "h" to "d". WebKit should only select "hello".</p>
<div id="container" contenteditable>hello <div>world</div></div>
<script>
container.focus();
getSelection().setBaseAndExtent(container, 0, container, 1);
</script>
</body>
</html>
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<!DOCTYPE html><!-- webkit-test-runner [ internal:selectionAcrossShadowBoundariesEnabled=true ] -->
<html>
<body>
<p>This tests selecting content starting in a shadow tree out to an editable element.<br>
To manually test, select "hello world" below by a mouse drag from "h" to "d". WebKit should only select "hello".</p>
<div id="container" contenteditable>world</div>
<script>

const host = document.createElement('div');
container.prepend(host);
const shadowRoot = host.attachShadow({mode: 'closed'});
shadowRoot.textContent = 'hello ';

if (window.eventSender) {
eventSender.dragMode = false;
eventSender.mouseMoveTo(container.offsetLeft + 1, container.offsetTop + 5);
eventSender.mouseDown();
eventSender.mouseMoveTo(container.offsetLeft + container.offsetWidth - 5, container.offsetTop + container.offsetHeight - 5);
eventSender.mouseUp();
if (getSelection().startContainer.getRootNode() != document)
document.write("The start container's root node was not the document");
if (getSelection().startContainer != getSelection().endContainer)
document.write("The end container was different from the start container");
if (getSelection().getRangeAt(0).startContainer.getRootNode() != document)
document.write("The range's start container's root node was not the document");
if (getSelection().getRangeAt(0).endContainer != getSelection().getRangeAt(0).startContainer)
document.write("The range's end container was different from its start container");
}

</script>
</body>
</html>
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<!DOCTYPE html>
<html>
<body>
<p>This tests selecting content starting in an editable region in a shadow tree to an editable region in the document tree.<br>
To manually test, select "hello world" below by a mouse drag from "h" to "d". WebKit should only select "hello".</p>
<div id="container"><div contenteditable>hello</div> world</div>
<script>
container.focus();
getSelection().selectAllChildren(container.firstChild);
</script>
</body>
</html>
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<!DOCTYPE html><!-- webkit-test-runner [ internal:selectionAcrossShadowBoundariesEnabled=true ] -->
<html>
<body>
<p>This tests selecting content starting in an editable region in a shadow tree to an editable region in the document tree.<br>
To manually test, select "hello world" below by a mouse drag from "h" to "d". WebKit should only select "hello".</p>
<div id="container" contenteditable>world</div>
<script>

const host = document.createElement('div');
container.prepend(host);
const shadowRoot = host.attachShadow({mode: 'closed'});
shadowRoot.innerHTML = '<div contenteditable>hello </div>';

if (window.eventSender) {
eventSender.dragMode = false;
eventSender.mouseMoveTo(container.offsetLeft + 1, container.offsetTop + 5);
eventSender.mouseDown();
eventSender.mouseMoveTo(container.offsetLeft + container.offsetWidth - 5, container.offsetTop + container.offsetHeight - 5);
eventSender.mouseUp();
if (getSelection().startContainer.getRootNode() != document)
document.write("The start container's root node was not the document");
if (getSelection().startContainer != getSelection().endContainer)
document.write("The end container was different from the start container");
if (getSelection().getRangeAt(0).startContainer.getRootNode() != document)
document.write("The range's start container's root node was not the document");
if (getSelection().getRangeAt(0).endContainer != getSelection().getRangeAt(0).startContainer)
document.write("The range's end container was different from its start container");
}

</script>
</body>
</html>
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<!DOCTYPE html>
<html>
<body>
<p>This tests selecting content starting in a shadow tree inside an editable region to the outside.<br>
To manually test, select "hello world" below by a mouse drag from the bottom right to the top left.<br>
WebKit should not extend the selection to the editable region outside the shadow tree.</p>
<div id="container" contenteditable>hello <div>world</div></div>
<script>
container.focus();
getSelection().selectAllChildren(container.lastChild);
</script>
</body>
</html>
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<!DOCTYPE html><!-- webkit-test-runner [ internal:selectionAcrossShadowBoundariesEnabled=true ] -->
<html>
<body>
<p>This tests selecting content starting in a shadow tree inside an editable region to the outside.<br>
To manually test, select "hello world" below by a mouse drag from the bottom right to the top left.<br>
WebKit should not extend the selection to the editable region outside the shadow tree.</p>
<div id="container" contenteditable>hello </div>
<script>

const host = document.createElement('div');
container.appendChild(host);
const shadowRoot = host.attachShadow({mode: 'closed'});
shadowRoot.textContent = 'world';

if (window.eventSender) {
eventSender.dragMode = false;
eventSender.mouseMoveTo(container.offsetLeft + container.offsetWidth - 5, container.offsetTop + container.offsetHeight - 5);
eventSender.mouseDown();
eventSender.mouseMoveTo(container.offsetLeft - 1, container.offsetTop - 5);
eventSender.mouseUp();
if (getSelection().startContainer.getRootNode() != document)
document.write("The start container's root node was not the document");
if (getSelection().startContainer != getSelection().endContainer)
document.write("The end container was different from the start container");
if (getSelection().getRangeAt(0).startContainer.getRootNode() != document)
document.write("The range's start container's root node was not the document");
if (getSelection().getRangeAt(0).endContainer != getSelection().getRangeAt(0).startContainer)
document.write("The range's end container was different from its start container");
}

</script>
</body>
</html>
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<!DOCTYPE html>
<html>
<body>
<p>This tests selecting content starting in a shadow tree inside a non-editable region in an editable region to outside the editable region.<br>
To manually test, select "hello world WebKit" below by a mouse drag from the bottom right to the top left.<br>
WebKit should not extend the selection to outside the shadow tree.</p>
<div id="container" contenteditable>hello<br>world<div>WebKit</div></div>
<script>
container.focus();
getSelection().selectAllChildren(container.lastChild);
</script>
</body>
</html>
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<!DOCTYPE html><!-- webkit-test-runner [ internal:selectionAcrossShadowBoundariesEnabled=true ] -->
<html>
<body>
<p>This tests selecting content starting in a shadow tree inside a non-editable region in an editable region to outside the editable region.<br>
To manually test, select "hello world WebKit" below by a mouse drag from the bottom right to the top left.<br>
WebKit should not extend the selection to outside the shadow tree.</p>
<div id="container" contenteditable>hello <div contenteditable="false">world</div></div>
<script>

const host = document.createElement('div');
container.lastChild.append(host);
const shadowRoot = host.attachShadow({mode: 'closed'});
shadowRoot.textContent = ' WebKit';

if (window.eventSender) {
eventSender.dragMode = false;
eventSender.mouseMoveTo(container.offsetLeft + container.offsetWidth - 5, container.offsetTop + container.offsetHeight - 5);
eventSender.mouseDown();
eventSender.mouseMoveTo(container.offsetLeft + 1, container.offsetTop + 5);
eventSender.mouseUp();
if (getSelection().startContainer.getRootNode() != document)
document.write("The start container's root node was not the document");
if (getSelection().startContainer != getSelection().endContainer)
document.write("The end container was different from the start container");
if (getSelection().getRangeAt(0).startContainer.getRootNode() != document)
document.write("The range's start container's root node was not the document");
if (getSelection().getRangeAt(0).endContainer != getSelection().getRangeAt(0).startContainer)
document.write("The range's end container was different from its start container");
}

</script>
</body>
</html>
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<!DOCTYPE html>
<html>
<body>
<p>This tests selecting content starting in the document into a shadow tree.<br>
To manually test, select "hello world" below by a mouse drag. WebKit should select the phrase.</p>
<div id="container">hello <div>world</div></div>
<script>
getSelection().selectAllChildren(container);
</script>
</body>
</html>
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<!DOCTYPE html><!-- webkit-test-runner [ internal:selectionAcrossShadowBoundariesEnabled=true ] -->
<html>
<body>
<p>This tests selecting content starting in the document into a shadow tree.<br>
To manually test, select "hello world" below by a mouse drag. WebKit should select the phrase.</p>
<div id="container">hello </div>
<script>

const host = document.createElement('div');
container.appendChild(host);
const shadowRoot = host.attachShadow({mode: 'closed'});
shadowRoot.textContent = 'world';

if (window.eventSender) {
eventSender.dragMode = false;
eventSender.mouseMoveTo(container.offsetLeft - 2, container.offsetTop + 5);
eventSender.mouseDown();
eventSender.mouseMoveTo(container.offsetLeft + container.offsetWidth - 5, container.offsetTop + container.offsetHeight - 5);
eventSender.mouseUp();
if (getSelection().startContainer.getRootNode() != document)
document.write("The start container's root node was not the document");
if (getSelection().endContainer.getRootNode() != document)
document.write("The end container's root node was not the document");
if (getSelection().getRangeAt(0).startContainer.getRootNode() != document)
document.write("The range's start container's root node was not the document");
if (getSelection().getRangeAt(0).endContainer.getRootNode() != document)
document.write("The range's end container's root node was not the document");
}

</script>
</body>
</html>
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<!DOCTYPE html>
<html>
<body>
<p>This tests selecting content starting in a shadow tree out to the document.<br>
To manually test, select "hello world" below by a mouse drag. WebKit should select the phrase.</p>
<div id="container">hello <div>world</div></div>
<script>
getSelection().selectAllChildren(container);
</script>
</body>
</html>
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<!DOCTYPE html><!-- webkit-test-runner [ internal:selectionAcrossShadowBoundariesEnabled=true ] -->
<html>
<body>
<p>This tests selecting content starting in a shadow tree out to the document.<br>
To manually test, select "hello world" below by a mouse drag. WebKit should select the phrase.</p>
<div id="container">world</div>
<script>

const host = document.createElement('div');
container.prepend(host);
const shadowRoot = host.attachShadow({mode: 'closed'});
shadowRoot.textContent = 'hello ';

if (window.eventSender) {
eventSender.dragMode = false;
eventSender.mouseMoveTo(container.offsetLeft - 2, container.offsetTop + 5);
eventSender.mouseDown();
eventSender.mouseMoveTo(container.offsetLeft + container.offsetWidth - 5, container.offsetTop + container.offsetHeight - 5);
eventSender.mouseUp();
if (getSelection().startContainer.getRootNode() != document)
document.write("The start container's root node was not the document");
if (getSelection().endContainer.getRootNode() != document)
document.write("The end container's root node was not the document");
if (getSelection().getRangeAt(0).startContainer.getRootNode() != document)
document.write("The range's start container's root node was not the document");
if (getSelection().getRangeAt(0).endContainer.getRootNode() != document)
document.write("The range's end container's root node was not the document");
}

</script>
</body>
</html>
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<!DOCTYPE html>
<html>
<body>
<p>This tests selecting content starting in one shadow tree and ending another shadow tree.<br>
To manually test, select "hello world" below by a mouse drag. WebKit should select the phrase.</p>
<div id="container">hello <div>world</div></div>
<script>
getSelection().selectAllChildren(container);
</script>
</body>
</html>

0 comments on commit dc2fc17

Please sign in to comment.