Skip to content

Commit dc2fc17

Browse files
Selection should work across shadow boundary when initiated by a mouse 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
1 parent 5a7b63e commit dc2fc17

34 files changed

+598
-12
lines changed

LayoutTests/ChangeLog

+29
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,32 @@
1+
2018-09-26 Ryosuke Niwa <[email protected]>
2+
3+
Selection should work across shadow boundary when initiated by a mouse drag
4+
https://bugs.webkit.org/show_bug.cgi?id=151380
5+
<rdar://problem/24363872>
6+
7+
Reviewed by Antti Koivisto and Wenson Hsieh.
8+
9+
Added regression tests using ref tests since getSelection() doesn't expose any node inside a shadow tree.
10+
11+
* editing/selection/selection-across-shadow-boundaries-mixed-editability-1-expected.html: Added.
12+
* editing/selection/selection-across-shadow-boundaries-mixed-editability-1.html: Added.
13+
* editing/selection/selection-across-shadow-boundaries-mixed-editability-2-expected.html: Added.
14+
* editing/selection/selection-across-shadow-boundaries-mixed-editability-2.html: Added.
15+
* editing/selection/selection-across-shadow-boundaries-mixed-editability-3-expected.html: Added.
16+
* editing/selection/selection-across-shadow-boundaries-mixed-editability-3.html: Added.
17+
* editing/selection/selection-across-shadow-boundaries-mixed-editability-4-expected.html: Added.
18+
* editing/selection/selection-across-shadow-boundaries-mixed-editability-4.html: Added.
19+
* editing/selection/selection-across-shadow-boundaries-mixed-editability-5-expected.html: Added.
20+
* editing/selection/selection-across-shadow-boundaries-mixed-editability-5.html: Added.
21+
* editing/selection/selection-across-shadow-boundaries-readonly-1-expected.html: Added.
22+
* editing/selection/selection-across-shadow-boundaries-readonly-1.html: Added.
23+
* editing/selection/selection-across-shadow-boundaries-readonly-2-expected.html: Added.
24+
* editing/selection/selection-across-shadow-boundaries-readonly-2.html: Added.
25+
* editing/selection/selection-across-shadow-boundaries-readonly-3-expected.html: Added.
26+
* editing/selection/selection-across-shadow-boundaries-readonly-3.html: Added.
27+
* editing/selection/selection-across-shadow-boundaries-user-select-all-1-expected.html: Added.
28+
* editing/selection/selection-across-shadow-boundaries-user-select-all-1.html: Added.
29+
130
2018-09-26 Alicia Boya García <[email protected]>
231

332
[GTK] Unreviewed test gardening
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<!DOCTYPE html>
2+
<html>
3+
<body>
4+
<p>This tests selecting content starting in an editable element to a shadow tree.<br>
5+
To manually test, select "hello world" below by a mouse drag from "h" to "d". WebKit should only select "hello".</p>
6+
<div id="container" contenteditable>hello <div>world</div></div>
7+
<script>
8+
container.focus();
9+
getSelection().setBaseAndExtent(container, 0, container, 1);
10+
</script>
11+
</body>
12+
</html>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
<!DOCTYPE html><!-- webkit-test-runner [ internal:selectionAcrossShadowBoundariesEnabled=true ] -->
2+
<html>
3+
<body>
4+
<p>This tests selecting content starting in an editable element to a shadow tree.<br>
5+
To manually test, select "hello world" below by a mouse drag from "h" to "d". WebKit should only select "hello".</p>
6+
<div id="container" contenteditable>hello </div>
7+
<script>
8+
9+
const host = document.createElement('div');
10+
container.appendChild(host);
11+
const shadowRoot = host.attachShadow({mode: 'closed'});
12+
shadowRoot.textContent = 'world';
13+
14+
if (window.eventSender) {
15+
eventSender.dragMode = false;
16+
eventSender.mouseMoveTo(container.offsetLeft + 1, container.offsetTop + 5);
17+
eventSender.mouseDown();
18+
eventSender.mouseMoveTo(container.offsetLeft + container.offsetWidth - 5, container.offsetTop + container.offsetHeight - 5);
19+
eventSender.mouseUp();
20+
if (getSelection().startContainer.getRootNode() != document)
21+
document.write("The start container's root node was not the document");
22+
if (getSelection().startContainer != getSelection().endContainer)
23+
document.write("The end container was different from the start container");
24+
if (getSelection().getRangeAt(0).startContainer.getRootNode() != document)
25+
document.write("The range's start container's root node was not the document");
26+
if (getSelection().getRangeAt(0).endContainer != getSelection().getRangeAt(0).startContainer)
27+
document.write("The range's end container was different from its start container");
28+
}
29+
30+
</script>
31+
</body>
32+
</html>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<!DOCTYPE html>
2+
<html>
3+
<body>
4+
<p>This tests selecting content starting in a shadow tree out to an editable element.<br>
5+
To manually test, select "hello world" below by a mouse drag from "h" to "d". WebKit should only select "hello".</p>
6+
<div id="container" contenteditable>hello <div>world</div></div>
7+
<script>
8+
container.focus();
9+
getSelection().setBaseAndExtent(container, 0, container, 1);
10+
</script>
11+
</body>
12+
</html>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
<!DOCTYPE html><!-- webkit-test-runner [ internal:selectionAcrossShadowBoundariesEnabled=true ] -->
2+
<html>
3+
<body>
4+
<p>This tests selecting content starting in a shadow tree out to an editable element.<br>
5+
To manually test, select "hello world" below by a mouse drag from "h" to "d". WebKit should only select "hello".</p>
6+
<div id="container" contenteditable>world</div>
7+
<script>
8+
9+
const host = document.createElement('div');
10+
container.prepend(host);
11+
const shadowRoot = host.attachShadow({mode: 'closed'});
12+
shadowRoot.textContent = 'hello ';
13+
14+
if (window.eventSender) {
15+
eventSender.dragMode = false;
16+
eventSender.mouseMoveTo(container.offsetLeft + 1, container.offsetTop + 5);
17+
eventSender.mouseDown();
18+
eventSender.mouseMoveTo(container.offsetLeft + container.offsetWidth - 5, container.offsetTop + container.offsetHeight - 5);
19+
eventSender.mouseUp();
20+
if (getSelection().startContainer.getRootNode() != document)
21+
document.write("The start container's root node was not the document");
22+
if (getSelection().startContainer != getSelection().endContainer)
23+
document.write("The end container was different from the start container");
24+
if (getSelection().getRangeAt(0).startContainer.getRootNode() != document)
25+
document.write("The range's start container's root node was not the document");
26+
if (getSelection().getRangeAt(0).endContainer != getSelection().getRangeAt(0).startContainer)
27+
document.write("The range's end container was different from its start container");
28+
}
29+
30+
</script>
31+
</body>
32+
</html>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<!DOCTYPE html>
2+
<html>
3+
<body>
4+
<p>This tests selecting content starting in an editable region in a shadow tree to an editable region in the document tree.<br>
5+
To manually test, select "hello world" below by a mouse drag from "h" to "d". WebKit should only select "hello".</p>
6+
<div id="container"><div contenteditable>hello</div> world</div>
7+
<script>
8+
container.focus();
9+
getSelection().selectAllChildren(container.firstChild);
10+
</script>
11+
</body>
12+
</html>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
<!DOCTYPE html><!-- webkit-test-runner [ internal:selectionAcrossShadowBoundariesEnabled=true ] -->
2+
<html>
3+
<body>
4+
<p>This tests selecting content starting in an editable region in a shadow tree to an editable region in the document tree.<br>
5+
To manually test, select "hello world" below by a mouse drag from "h" to "d". WebKit should only select "hello".</p>
6+
<div id="container" contenteditable>world</div>
7+
<script>
8+
9+
const host = document.createElement('div');
10+
container.prepend(host);
11+
const shadowRoot = host.attachShadow({mode: 'closed'});
12+
shadowRoot.innerHTML = '<div contenteditable>hello </div>';
13+
14+
if (window.eventSender) {
15+
eventSender.dragMode = false;
16+
eventSender.mouseMoveTo(container.offsetLeft + 1, container.offsetTop + 5);
17+
eventSender.mouseDown();
18+
eventSender.mouseMoveTo(container.offsetLeft + container.offsetWidth - 5, container.offsetTop + container.offsetHeight - 5);
19+
eventSender.mouseUp();
20+
if (getSelection().startContainer.getRootNode() != document)
21+
document.write("The start container's root node was not the document");
22+
if (getSelection().startContainer != getSelection().endContainer)
23+
document.write("The end container was different from the start container");
24+
if (getSelection().getRangeAt(0).startContainer.getRootNode() != document)
25+
document.write("The range's start container's root node was not the document");
26+
if (getSelection().getRangeAt(0).endContainer != getSelection().getRangeAt(0).startContainer)
27+
document.write("The range's end container was different from its start container");
28+
}
29+
30+
</script>
31+
</body>
32+
</html>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<!DOCTYPE html>
2+
<html>
3+
<body>
4+
<p>This tests selecting content starting in a shadow tree inside an editable region to the outside.<br>
5+
To manually test, select "hello world" below by a mouse drag from the bottom right to the top left.<br>
6+
WebKit should not extend the selection to the editable region outside the shadow tree.</p>
7+
<div id="container" contenteditable>hello <div>world</div></div>
8+
<script>
9+
container.focus();
10+
getSelection().selectAllChildren(container.lastChild);
11+
</script>
12+
</body>
13+
</html>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
<!DOCTYPE html><!-- webkit-test-runner [ internal:selectionAcrossShadowBoundariesEnabled=true ] -->
2+
<html>
3+
<body>
4+
<p>This tests selecting content starting in a shadow tree inside an editable region to the outside.<br>
5+
To manually test, select "hello world" below by a mouse drag from the bottom right to the top left.<br>
6+
WebKit should not extend the selection to the editable region outside the shadow tree.</p>
7+
<div id="container" contenteditable>hello </div>
8+
<script>
9+
10+
const host = document.createElement('div');
11+
container.appendChild(host);
12+
const shadowRoot = host.attachShadow({mode: 'closed'});
13+
shadowRoot.textContent = 'world';
14+
15+
if (window.eventSender) {
16+
eventSender.dragMode = false;
17+
eventSender.mouseMoveTo(container.offsetLeft + container.offsetWidth - 5, container.offsetTop + container.offsetHeight - 5);
18+
eventSender.mouseDown();
19+
eventSender.mouseMoveTo(container.offsetLeft - 1, container.offsetTop - 5);
20+
eventSender.mouseUp();
21+
if (getSelection().startContainer.getRootNode() != document)
22+
document.write("The start container's root node was not the document");
23+
if (getSelection().startContainer != getSelection().endContainer)
24+
document.write("The end container was different from the start container");
25+
if (getSelection().getRangeAt(0).startContainer.getRootNode() != document)
26+
document.write("The range's start container's root node was not the document");
27+
if (getSelection().getRangeAt(0).endContainer != getSelection().getRangeAt(0).startContainer)
28+
document.write("The range's end container was different from its start container");
29+
}
30+
31+
</script>
32+
</body>
33+
</html>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<!DOCTYPE html>
2+
<html>
3+
<body>
4+
<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>
5+
To manually test, select "hello world WebKit" below by a mouse drag from the bottom right to the top left.<br>
6+
WebKit should not extend the selection to outside the shadow tree.</p>
7+
<div id="container" contenteditable>hello<br>world<div>WebKit</div></div>
8+
<script>
9+
container.focus();
10+
getSelection().selectAllChildren(container.lastChild);
11+
</script>
12+
</body>
13+
</html>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
<!DOCTYPE html><!-- webkit-test-runner [ internal:selectionAcrossShadowBoundariesEnabled=true ] -->
2+
<html>
3+
<body>
4+
<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>
5+
To manually test, select "hello world WebKit" below by a mouse drag from the bottom right to the top left.<br>
6+
WebKit should not extend the selection to outside the shadow tree.</p>
7+
<div id="container" contenteditable>hello <div contenteditable="false">world</div></div>
8+
<script>
9+
10+
const host = document.createElement('div');
11+
container.lastChild.append(host);
12+
const shadowRoot = host.attachShadow({mode: 'closed'});
13+
shadowRoot.textContent = ' WebKit';
14+
15+
if (window.eventSender) {
16+
eventSender.dragMode = false;
17+
eventSender.mouseMoveTo(container.offsetLeft + container.offsetWidth - 5, container.offsetTop + container.offsetHeight - 5);
18+
eventSender.mouseDown();
19+
eventSender.mouseMoveTo(container.offsetLeft + 1, container.offsetTop + 5);
20+
eventSender.mouseUp();
21+
if (getSelection().startContainer.getRootNode() != document)
22+
document.write("The start container's root node was not the document");
23+
if (getSelection().startContainer != getSelection().endContainer)
24+
document.write("The end container was different from the start container");
25+
if (getSelection().getRangeAt(0).startContainer.getRootNode() != document)
26+
document.write("The range's start container's root node was not the document");
27+
if (getSelection().getRangeAt(0).endContainer != getSelection().getRangeAt(0).startContainer)
28+
document.write("The range's end container was different from its start container");
29+
}
30+
31+
</script>
32+
</body>
33+
</html>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<!DOCTYPE html>
2+
<html>
3+
<body>
4+
<p>This tests selecting content starting in the document into a shadow tree.<br>
5+
To manually test, select "hello world" below by a mouse drag. WebKit should select the phrase.</p>
6+
<div id="container">hello <div>world</div></div>
7+
<script>
8+
getSelection().selectAllChildren(container);
9+
</script>
10+
</body>
11+
</html>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
<!DOCTYPE html><!-- webkit-test-runner [ internal:selectionAcrossShadowBoundariesEnabled=true ] -->
2+
<html>
3+
<body>
4+
<p>This tests selecting content starting in the document into a shadow tree.<br>
5+
To manually test, select "hello world" below by a mouse drag. WebKit should select the phrase.</p>
6+
<div id="container">hello </div>
7+
<script>
8+
9+
const host = document.createElement('div');
10+
container.appendChild(host);
11+
const shadowRoot = host.attachShadow({mode: 'closed'});
12+
shadowRoot.textContent = 'world';
13+
14+
if (window.eventSender) {
15+
eventSender.dragMode = false;
16+
eventSender.mouseMoveTo(container.offsetLeft - 2, container.offsetTop + 5);
17+
eventSender.mouseDown();
18+
eventSender.mouseMoveTo(container.offsetLeft + container.offsetWidth - 5, container.offsetTop + container.offsetHeight - 5);
19+
eventSender.mouseUp();
20+
if (getSelection().startContainer.getRootNode() != document)
21+
document.write("The start container's root node was not the document");
22+
if (getSelection().endContainer.getRootNode() != document)
23+
document.write("The end container's root node was not the document");
24+
if (getSelection().getRangeAt(0).startContainer.getRootNode() != document)
25+
document.write("The range's start container's root node was not the document");
26+
if (getSelection().getRangeAt(0).endContainer.getRootNode() != document)
27+
document.write("The range's end container's root node was not the document");
28+
}
29+
30+
</script>
31+
</body>
32+
</html>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<!DOCTYPE html>
2+
<html>
3+
<body>
4+
<p>This tests selecting content starting in a shadow tree out to the document.<br>
5+
To manually test, select "hello world" below by a mouse drag. WebKit should select the phrase.</p>
6+
<div id="container">hello <div>world</div></div>
7+
<script>
8+
getSelection().selectAllChildren(container);
9+
</script>
10+
</body>
11+
</html>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
<!DOCTYPE html><!-- webkit-test-runner [ internal:selectionAcrossShadowBoundariesEnabled=true ] -->
2+
<html>
3+
<body>
4+
<p>This tests selecting content starting in a shadow tree out to the document.<br>
5+
To manually test, select "hello world" below by a mouse drag. WebKit should select the phrase.</p>
6+
<div id="container">world</div>
7+
<script>
8+
9+
const host = document.createElement('div');
10+
container.prepend(host);
11+
const shadowRoot = host.attachShadow({mode: 'closed'});
12+
shadowRoot.textContent = 'hello ';
13+
14+
if (window.eventSender) {
15+
eventSender.dragMode = false;
16+
eventSender.mouseMoveTo(container.offsetLeft - 2, container.offsetTop + 5);
17+
eventSender.mouseDown();
18+
eventSender.mouseMoveTo(container.offsetLeft + container.offsetWidth - 5, container.offsetTop + container.offsetHeight - 5);
19+
eventSender.mouseUp();
20+
if (getSelection().startContainer.getRootNode() != document)
21+
document.write("The start container's root node was not the document");
22+
if (getSelection().endContainer.getRootNode() != document)
23+
document.write("The end container's root node was not the document");
24+
if (getSelection().getRangeAt(0).startContainer.getRootNode() != document)
25+
document.write("The range's start container's root node was not the document");
26+
if (getSelection().getRangeAt(0).endContainer.getRootNode() != document)
27+
document.write("The range's end container's root node was not the document");
28+
}
29+
30+
</script>
31+
</body>
32+
</html>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<!DOCTYPE html>
2+
<html>
3+
<body>
4+
<p>This tests selecting content starting in one shadow tree and ending another shadow tree.<br>
5+
To manually test, select "hello world" below by a mouse drag. WebKit should select the phrase.</p>
6+
<div id="container">hello <div>world</div></div>
7+
<script>
8+
getSelection().selectAllChildren(container);
9+
</script>
10+
</body>
11+
</html>

0 commit comments

Comments
 (0)