Skip to content

Commit e679be3

Browse files
dizhang168chromium-wpt-export-bot
authored andcommitted
Add UpdateSelectionBehavior to Range::UpdateSelectionIfAddedToSelection
When a live range attached to a document is modified, this change is upstreamed to the FrameSelection. New spec [1] says to only update the composed live range (frame selection)'s start position if setStart is called and only update end position if setEnd is called: whatwg/dom#1342 This is the proposal (B) discussed here: whatwg/dom#772 (comment) To do this, we define enum UpdateSelectionIfAddedToSelection to have three possible update selection behavior: 1. kAll, set selection to have the same start and end as range. --> Default case, when both setStart, setEnd are called. 2. kStartOnly, set selection to have the same start as range only. --> When only setStart is called. 3. kEndOnly, set selection to have the same end as range only. --> When only setEnd is called. We add a WPT test for this new behavior, which only affects the output of getComposedRanges() as it is the only API that accesses the frame selection's endpoints directly. Change-Id: I51ea53fe6156164ba3fbe38b14bc47ff502633b1 Bug: 40286116 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/6188157 Reviewed-by: Siye Liu <[email protected]> Commit-Queue: Di Zhang <[email protected]> Cr-Commit-Position: refs/heads/main@{#1411209}
1 parent 07ddf2d commit e679be3

File tree

1 file changed

+190
-0
lines changed

1 file changed

+190
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,190 @@
1+
<!DOCTYPE html>
2+
<html>
3+
<body>
4+
<meta name="assert" content="Selection's getComposedRanges should be updated when its associated live range changes">
5+
<link rel="help" href="https://w3c.github.io/selection-api/#dom-selection-getcomposedranges">
6+
<script src="/resources/testharness.js"></script>
7+
<script src="/resources/testharnessreport.js"></script>
8+
9+
<div id="light">Start outside shadow DOM</div>
10+
<div id="outerHost">outerHost
11+
<template shadowrootmode="open">
12+
<slot></slot>
13+
<div id="innerHost">innerHost
14+
<template shadowrootmode="open">
15+
<slot></slot>
16+
</template>
17+
</div>
18+
</template>
19+
</div>
20+
21+
<script>
22+
23+
const selection = getSelection();
24+
const outerHost = document.getElementById('outerHost')
25+
const outerRoot = outerHost.shadowRoot;
26+
const innerHost = outerRoot.getElementById('innerHost');
27+
const innerRoot = innerHost.shadowRoot;
28+
29+
test(() => {
30+
// Step 1: Setting a composed live range that crosses boundaries
31+
selection.setBaseAndExtent(light.firstChild, 10, innerHost.firstChild, 5);
32+
const liveRange = selection.getRangeAt(0);
33+
const composedRange = selection.getComposedRanges({ shadowRoots: [outerRoot, innerRoot] })[0];
34+
35+
assert_equals(liveRange.startContainer, innerHost.firstChild);
36+
assert_equals(liveRange.startOffset, 5);
37+
assert_equals(liveRange.endContainer, innerHost.firstChild);
38+
assert_equals(liveRange.endOffset, 5);
39+
40+
assert_equals(selection.anchorNode, innerHost.firstChild);
41+
assert_equals(selection.anchorOffset, 5);
42+
assert_equals(selection.focusNode, innerHost.firstChild);
43+
assert_equals(selection.focusOffset, 5);
44+
45+
assert_equals(composedRange.startContainer, light.firstChild);
46+
assert_equals(composedRange.startOffset, 10);
47+
assert_equals(composedRange.endContainer, innerHost.firstChild);
48+
assert_equals(composedRange.endOffset, 5);
49+
50+
// Step 2: Update the live range only using setEnd
51+
liveRange.setEnd(innerHost.firstChild, 6);
52+
const composedRange2 = selection.getComposedRanges({ shadowRoots: [outerRoot, innerRoot] })[0];
53+
54+
assert_equals(liveRange.startContainer, innerHost.firstChild);
55+
assert_equals(liveRange.startOffset, 5);
56+
assert_equals(liveRange.endContainer, innerHost.firstChild);
57+
assert_equals(liveRange.endOffset, 6);
58+
59+
assert_equals(selection.anchorNode, innerHost.firstChild);
60+
assert_equals(selection.anchorOffset, 5);
61+
assert_equals(selection.focusNode, innerHost.firstChild);
62+
assert_equals(selection.focusOffset, 6);
63+
64+
assert_equals(composedRange2.startContainer, light.firstChild);
65+
assert_equals(composedRange2.startOffset, 10);
66+
assert_equals(composedRange2.endContainer, innerHost.firstChild);
67+
assert_equals(composedRange2.endOffset, 6);
68+
69+
// Step 3: selectNode() calls both setStart/setEnd
70+
liveRange.selectNode(innerHost);
71+
const composedRange3 = selection.getComposedRanges({ shadowRoots: [outerRoot, innerRoot] })[0];
72+
73+
assert_equals(liveRange.startContainer, outerRoot);
74+
assert_equals(liveRange.startOffset, 3);
75+
assert_equals(liveRange.endContainer, outerRoot);
76+
assert_equals(liveRange.endOffset, 4);
77+
78+
assert_equals(selection.anchorNode, outerRoot);
79+
assert_equals(selection.anchorOffset, 3);
80+
assert_equals(selection.focusNode, outerRoot);
81+
assert_equals(selection.focusOffset, 4);
82+
83+
assert_equals(composedRange3.startContainer, outerRoot);
84+
assert_equals(composedRange3.startOffset, 3);
85+
assert_equals(composedRange3.endContainer, outerRoot);
86+
assert_equals(composedRange3.endOffset, 4);
87+
88+
// Step 4: collapse(false) calls setEnd only
89+
liveRange.collapse();
90+
const composedRange4 = selection.getComposedRanges({ shadowRoots: [outerRoot, innerRoot] })[0];
91+
92+
assert_equals(liveRange.startContainer, outerRoot);
93+
assert_equals(liveRange.startOffset, 4);
94+
assert_equals(liveRange.endContainer, outerRoot);
95+
assert_equals(liveRange.endOffset, 4);
96+
97+
assert_equals(selection.anchorNode, outerRoot);
98+
assert_equals(selection.anchorOffset, 4);
99+
assert_equals(selection.focusNode, outerRoot);
100+
assert_equals(selection.focusOffset, 4);
101+
102+
assert_equals(composedRange4.startContainer, outerRoot);
103+
assert_equals(composedRange4.startOffset, 4);
104+
assert_equals(composedRange4.endContainer, outerRoot);
105+
assert_equals(composedRange4.endOffset, 4);
106+
}, 'modify getRangeAt() range.');
107+
108+
test(() => {
109+
// Step 1: Creating a live range and only setting its end/anchor
110+
selection.removeAllRanges();
111+
const liveRange = document.createRange();
112+
liveRange.setEnd(innerHost.firstChild, 5);
113+
const composedRanges = selection.getComposedRanges({ shadowRoots: [outerRoot, innerRoot] });
114+
115+
assert_equals(liveRange.startContainer, innerHost.firstChild);
116+
assert_equals(liveRange.startOffset, 5);
117+
assert_equals(liveRange.endContainer, innerHost.firstChild);
118+
assert_equals(liveRange.endOffset, 5);
119+
120+
assert_equals(selection.anchorNode, null);
121+
assert_equals(selection.anchorOffset, 0);
122+
assert_equals(selection.focusNode, null);
123+
assert_equals(selection.focusOffset, 0);
124+
125+
assert_equals(composedRanges.length, 0);
126+
127+
// Step 2: Add range to selection so range API updates will change selection
128+
selection.addRange(liveRange);
129+
const composedRange = selection.getComposedRanges({ shadowRoots: [outerRoot, innerRoot] })[0];
130+
131+
assert_equals(liveRange.startContainer, innerHost.firstChild);
132+
assert_equals(liveRange.startOffset, 5);
133+
assert_equals(liveRange.endContainer, innerHost.firstChild);
134+
assert_equals(liveRange.endOffset, 5);
135+
136+
assert_equals(selection.anchorNode, innerHost.firstChild);
137+
assert_equals(selection.anchorOffset, 5);
138+
assert_equals(selection.focusNode, innerHost.firstChild);
139+
assert_equals(selection.focusOffset, 5);
140+
141+
assert_equals(composedRange.startContainer, innerHost.firstChild);
142+
assert_equals(composedRange.startOffset, 5);
143+
assert_equals(composedRange.endContainer, innerHost.firstChild);
144+
assert_equals(composedRange.endOffset, 5);
145+
}, 'modify createRange() range added to selection after setEnd call.');
146+
147+
test(() => {
148+
// Step 1: Creating a live range and only setting its end/anchor
149+
selection.removeAllRanges();
150+
const liveRange = document.createRange();
151+
// Add range to selection so range API updates will change selection
152+
selection.addRange(liveRange);
153+
liveRange.setEnd(innerHost.firstChild, 5);
154+
const composedRange = selection.getComposedRanges({ shadowRoots: [outerRoot, innerRoot] })[0];
155+
156+
assert_equals(liveRange.startContainer, innerHost.firstChild);
157+
assert_equals(liveRange.startOffset, 5);
158+
assert_equals(liveRange.endContainer, innerHost.firstChild);
159+
assert_equals(liveRange.endOffset, 5);
160+
161+
assert_equals(selection.anchorNode, innerHost.firstChild);
162+
assert_equals(selection.anchorOffset, 5);
163+
assert_equals(selection.focusNode, innerHost.firstChild);
164+
assert_equals(selection.focusOffset, 5);
165+
166+
assert_equals(composedRange.startContainer, document);
167+
assert_equals(composedRange.startOffset, 0);
168+
assert_equals(composedRange.endContainer, innerHost.firstChild);
169+
assert_equals(composedRange.endOffset, 5);
170+
171+
// Step 2: Update the live range by setting its start/focus
172+
liveRange.setStart(light.firstChild, 10);
173+
const composedRangeAfter = selection.getComposedRanges({ shadowRoots: [outerRoot, innerRoot] })[0];
174+
175+
assert_equals(liveRange.startContainer, light.firstChild);
176+
assert_equals(liveRange.startOffset, 10);
177+
assert_equals(liveRange.endContainer, light.firstChild);
178+
assert_equals(liveRange.endOffset, 10);
179+
180+
assert_equals(selection.anchorNode, light.firstChild);
181+
assert_equals(selection.anchorOffset, 10);
182+
assert_equals(selection.focusNode, light.firstChild);
183+
assert_equals(selection.focusOffset, 10);
184+
185+
assert_equals(composedRangeAfter.startContainer, light.firstChild);
186+
assert_equals(composedRangeAfter.startOffset, 10);
187+
assert_equals(composedRangeAfter.endContainer, innerHost.firstChild);
188+
assert_equals(composedRangeAfter.endOffset, 5);
189+
}, 'modify createRange() range added to selection before setStart/setEnd calls.');
190+
</script>

0 commit comments

Comments
 (0)