Skip to content

Commit

Permalink
Bug 1943714 [wpt PR 50283] - Add UpdateSelectionBehavior to Range::Up…
Browse files Browse the repository at this point in the history
…dateSelectionIfAddedToSelection, a=testonly

Automatic update from web-platform-tests
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}

--

wpt-commits: e679be39aa83e65a06627a8a5b911648f5312f13
wpt-pr: 50283
  • Loading branch information
dizhang168 authored and moz-wptsync-bot committed Jan 28, 2025
1 parent 0231f67 commit 9ac561e
Showing 1 changed file with 190 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
<!DOCTYPE html>
<html>
<body>
<meta name="assert" content="Selection's getComposedRanges should be updated when its associated live range changes">
<link rel="help" href="https://w3c.github.io/selection-api/#dom-selection-getcomposedranges">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>

<div id="light">Start outside shadow DOM</div>
<div id="outerHost">outerHost
<template shadowrootmode="open">
<slot></slot>
<div id="innerHost">innerHost
<template shadowrootmode="open">
<slot></slot>
</template>
</div>
</template>
</div>

<script>

const selection = getSelection();
const outerHost = document.getElementById('outerHost')
const outerRoot = outerHost.shadowRoot;
const innerHost = outerRoot.getElementById('innerHost');
const innerRoot = innerHost.shadowRoot;

test(() => {
// Step 1: Setting a composed live range that crosses boundaries
selection.setBaseAndExtent(light.firstChild, 10, innerHost.firstChild, 5);
const liveRange = selection.getRangeAt(0);
const composedRange = selection.getComposedRanges({ shadowRoots: [outerRoot, innerRoot] })[0];

assert_equals(liveRange.startContainer, innerHost.firstChild);
assert_equals(liveRange.startOffset, 5);
assert_equals(liveRange.endContainer, innerHost.firstChild);
assert_equals(liveRange.endOffset, 5);

assert_equals(selection.anchorNode, innerHost.firstChild);
assert_equals(selection.anchorOffset, 5);
assert_equals(selection.focusNode, innerHost.firstChild);
assert_equals(selection.focusOffset, 5);

assert_equals(composedRange.startContainer, light.firstChild);
assert_equals(composedRange.startOffset, 10);
assert_equals(composedRange.endContainer, innerHost.firstChild);
assert_equals(composedRange.endOffset, 5);

// Step 2: Update the live range only using setEnd
liveRange.setEnd(innerHost.firstChild, 6);
const composedRange2 = selection.getComposedRanges({ shadowRoots: [outerRoot, innerRoot] })[0];

assert_equals(liveRange.startContainer, innerHost.firstChild);
assert_equals(liveRange.startOffset, 5);
assert_equals(liveRange.endContainer, innerHost.firstChild);
assert_equals(liveRange.endOffset, 6);

assert_equals(selection.anchorNode, innerHost.firstChild);
assert_equals(selection.anchorOffset, 5);
assert_equals(selection.focusNode, innerHost.firstChild);
assert_equals(selection.focusOffset, 6);

assert_equals(composedRange2.startContainer, light.firstChild);
assert_equals(composedRange2.startOffset, 10);
assert_equals(composedRange2.endContainer, innerHost.firstChild);
assert_equals(composedRange2.endOffset, 6);

// Step 3: selectNode() calls both setStart/setEnd
liveRange.selectNode(innerHost);
const composedRange3 = selection.getComposedRanges({ shadowRoots: [outerRoot, innerRoot] })[0];

assert_equals(liveRange.startContainer, outerRoot);
assert_equals(liveRange.startOffset, 3);
assert_equals(liveRange.endContainer, outerRoot);
assert_equals(liveRange.endOffset, 4);

assert_equals(selection.anchorNode, outerRoot);
assert_equals(selection.anchorOffset, 3);
assert_equals(selection.focusNode, outerRoot);
assert_equals(selection.focusOffset, 4);

assert_equals(composedRange3.startContainer, outerRoot);
assert_equals(composedRange3.startOffset, 3);
assert_equals(composedRange3.endContainer, outerRoot);
assert_equals(composedRange3.endOffset, 4);

// Step 4: collapse(false) calls setEnd only
liveRange.collapse();
const composedRange4 = selection.getComposedRanges({ shadowRoots: [outerRoot, innerRoot] })[0];

assert_equals(liveRange.startContainer, outerRoot);
assert_equals(liveRange.startOffset, 4);
assert_equals(liveRange.endContainer, outerRoot);
assert_equals(liveRange.endOffset, 4);

assert_equals(selection.anchorNode, outerRoot);
assert_equals(selection.anchorOffset, 4);
assert_equals(selection.focusNode, outerRoot);
assert_equals(selection.focusOffset, 4);

assert_equals(composedRange4.startContainer, outerRoot);
assert_equals(composedRange4.startOffset, 4);
assert_equals(composedRange4.endContainer, outerRoot);
assert_equals(composedRange4.endOffset, 4);
}, 'modify getRangeAt() range.');

test(() => {
// Step 1: Creating a live range and only setting its end/anchor
selection.removeAllRanges();
const liveRange = document.createRange();
liveRange.setEnd(innerHost.firstChild, 5);
const composedRanges = selection.getComposedRanges({ shadowRoots: [outerRoot, innerRoot] });

assert_equals(liveRange.startContainer, innerHost.firstChild);
assert_equals(liveRange.startOffset, 5);
assert_equals(liveRange.endContainer, innerHost.firstChild);
assert_equals(liveRange.endOffset, 5);

assert_equals(selection.anchorNode, null);
assert_equals(selection.anchorOffset, 0);
assert_equals(selection.focusNode, null);
assert_equals(selection.focusOffset, 0);

assert_equals(composedRanges.length, 0);

// Step 2: Add range to selection so range API updates will change selection
selection.addRange(liveRange);
const composedRange = selection.getComposedRanges({ shadowRoots: [outerRoot, innerRoot] })[0];

assert_equals(liveRange.startContainer, innerHost.firstChild);
assert_equals(liveRange.startOffset, 5);
assert_equals(liveRange.endContainer, innerHost.firstChild);
assert_equals(liveRange.endOffset, 5);

assert_equals(selection.anchorNode, innerHost.firstChild);
assert_equals(selection.anchorOffset, 5);
assert_equals(selection.focusNode, innerHost.firstChild);
assert_equals(selection.focusOffset, 5);

assert_equals(composedRange.startContainer, innerHost.firstChild);
assert_equals(composedRange.startOffset, 5);
assert_equals(composedRange.endContainer, innerHost.firstChild);
assert_equals(composedRange.endOffset, 5);
}, 'modify createRange() range added to selection after setEnd call.');

test(() => {
// Step 1: Creating a live range and only setting its end/anchor
selection.removeAllRanges();
const liveRange = document.createRange();
// Add range to selection so range API updates will change selection
selection.addRange(liveRange);
liveRange.setEnd(innerHost.firstChild, 5);
const composedRange = selection.getComposedRanges({ shadowRoots: [outerRoot, innerRoot] })[0];

assert_equals(liveRange.startContainer, innerHost.firstChild);
assert_equals(liveRange.startOffset, 5);
assert_equals(liveRange.endContainer, innerHost.firstChild);
assert_equals(liveRange.endOffset, 5);

assert_equals(selection.anchorNode, innerHost.firstChild);
assert_equals(selection.anchorOffset, 5);
assert_equals(selection.focusNode, innerHost.firstChild);
assert_equals(selection.focusOffset, 5);

assert_equals(composedRange.startContainer, document);
assert_equals(composedRange.startOffset, 0);
assert_equals(composedRange.endContainer, innerHost.firstChild);
assert_equals(composedRange.endOffset, 5);

// Step 2: Update the live range by setting its start/focus
liveRange.setStart(light.firstChild, 10);
const composedRangeAfter = selection.getComposedRanges({ shadowRoots: [outerRoot, innerRoot] })[0];

assert_equals(liveRange.startContainer, light.firstChild);
assert_equals(liveRange.startOffset, 10);
assert_equals(liveRange.endContainer, light.firstChild);
assert_equals(liveRange.endOffset, 10);

assert_equals(selection.anchorNode, light.firstChild);
assert_equals(selection.anchorOffset, 10);
assert_equals(selection.focusNode, light.firstChild);
assert_equals(selection.focusOffset, 10);

assert_equals(composedRangeAfter.startContainer, light.firstChild);
assert_equals(composedRangeAfter.startOffset, 10);
assert_equals(composedRangeAfter.endContainer, innerHost.firstChild);
assert_equals(composedRangeAfter.endOffset, 5);
}, 'modify createRange() range added to selection before setStart/setEnd calls.');
</script>

0 comments on commit 9ac561e

Please sign in to comment.