Commit 7d838af7 authored by Nick Burris's avatar Nick Burris Committed by Commit Bot

[scroll-to-text-fragment] Apply :target CSS pseudo-class to match.

Per the spec, the common ancestor element of the match range becomes the
target element[1] and should have the :target pseudo-class applied.

[1]
https://html.spec.whatwg.org/multipage/browsing-the-web.html#target-element

Bug: 959163
Change-Id: Idb462a17972f4758df9617653f03b88196fd565a
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1965273Reviewed-by: default avatarDavid Bokan <bokan@chromium.org>
Commit-Queue: Nick Burris <nburris@chromium.org>
Cr-Commit-Position: refs/heads/master@{#726031}
parent 360b04d3
...@@ -225,6 +225,10 @@ void TextFragmentAnchor::DidFindMatch(const EphemeralRangeInFlatTree& range) { ...@@ -225,6 +225,10 @@ void TextFragmentAnchor::DidFindMatch(const EphemeralRangeInFlatTree& range) {
return; return;
} }
// Apply :target to the first match
if (!did_find_match_)
ApplyTargetToCommonAncestor(range);
metrics_->DidFindMatch(PlainText(range)); metrics_->DidFindMatch(PlainText(range));
did_find_match_ = true; did_find_match_ = true;
...@@ -315,4 +319,18 @@ bool TextFragmentAnchor::Dismiss() { ...@@ -315,4 +319,18 @@ bool TextFragmentAnchor::Dismiss() {
return dismissed_; return dismissed_;
} }
void TextFragmentAnchor::ApplyTargetToCommonAncestor(
const EphemeralRangeInFlatTree& range) {
Node* common_node = range.CommonAncestorContainer();
while (common_node && common_node->getNodeType() != Node::kElementNode) {
common_node = common_node->parentNode();
}
DCHECK(common_node);
if (common_node) {
auto* target = DynamicTo<Element>(common_node);
frame_->GetDocument()->SetCSSTarget(target);
}
}
} // namespace blink } // namespace blink
...@@ -69,6 +69,8 @@ class CORE_EXPORT TextFragmentAnchor final : public FragmentAnchor, ...@@ -69,6 +69,8 @@ class CORE_EXPORT TextFragmentAnchor final : public FragmentAnchor,
// element fragment anchor if we didn't find a match. // element fragment anchor if we didn't find a match.
void DidFinishSearch(); void DidFinishSearch();
void ApplyTargetToCommonAncestor(const EphemeralRangeInFlatTree& range);
Vector<TextFragmentFinder> text_fragment_finders_; Vector<TextFragmentFinder> text_fragment_finders_;
Member<LocalFrame> frame_; Member<LocalFrame> frame_;
......
...@@ -29,7 +29,21 @@ function checkScroll() { ...@@ -29,7 +29,21 @@ function checkScroll() {
else if (isInView(document.getElementById('horizontal-scroll')) && window.scrollX > 0) else if (isInView(document.getElementById('horizontal-scroll')) && window.scrollX > 0)
position = 'horizontal-scroll'; position = 'horizontal-scroll';
let results = { scrollPosition: position, href: window.location.href }; let target = document.querySelector(":target");
if (!target && position == 'shadow-parent') {
let shadow = document.getElementById("shadow-parent").shadowRoot.firstElementChild;
if (shadow.matches(":target")) {
target = shadow;
position = 'shadow';
}
}
let results = {
scrollPosition: position,
href: window.location.href,
target: target ? target.id : 'undefined'
};
let key = (new URL(document.location)).searchParams.get("key"); let key = (new URL(document.location)).searchParams.get("key");
stashResults(key, results); stashResults(key, results);
...@@ -54,10 +68,10 @@ function checkScroll() { ...@@ -54,10 +68,10 @@ function checkScroll() {
<div id="element" class="scroll-section">Element</div> <div id="element" class="scroll-section">Element</div>
<p id="text" class="scroll-section">This is a test page !$'()*+./:;=?@_~ &,- &#x30cd;&#x30b3;</p> <p id="text" class="scroll-section">This is a test page !$'()*+./:;=?@_~ &,- &#x30cd;&#x30b3;</p>
<p id="more-text" class="scroll-section">More test page text</p> <p id="more-text" class="scroll-section">More test page text</p>
<div id="cross-node-context" class="scroll-section"> <div class="scroll-section">
<div> <div>
<p>prefix</p> <p>prefix</p>
<p>test page</p> <p id="cross-node-context">test page</p>
</div> </div>
<div><p>suffix</p></div> <div><p>suffix</p></div>
</div> </div>
...@@ -65,7 +79,7 @@ function checkScroll() { ...@@ -65,7 +79,7 @@ function checkScroll() {
<div id="shadow-parent" class="scroll-section"></div> <div id="shadow-parent" class="scroll-section"></div>
<script> <script>
let shadow = document.getElementById("shadow-parent").attachShadow({mode: 'open'}); let shadow = document.getElementById("shadow-parent").attachShadow({mode: 'open'});
shadow.innerHTML = '<p>shadow text</p>'; shadow.innerHTML = '<p id="shadow">shadow text</p>';
</script> </script>
<p id="hidden" class="scroll-section">hidden text</p> <p id="hidden" class="scroll-section">hidden text</p>
<p id="horizontal-scroll" class="scroll-section">horizontally scrolled text</p> <p id="horizontal-scroll" class="scroll-section">horizontally scrolled text</p>
......
...@@ -202,7 +202,7 @@ let test_cases = [ ...@@ -202,7 +202,7 @@ let test_cases = [
// Test text directive within shadow DOM // Test text directive within shadow DOM
{ {
fragment: '#:~:text=shadow%20text', fragment: '#:~:text=shadow%20text',
expect_position: 'shadow-parent', expect_position: 'shadow',
description: 'Text directive should match text within shadow DOM' description: 'Text directive should match text within shadow DOM'
}, },
// Test text directive within hidden and display none elements. These cases should not scroll into // Test text directive within hidden and display none elements. These cases should not scroll into
...@@ -236,6 +236,8 @@ for (const test_case of test_cases) { ...@@ -236,6 +236,8 @@ for (const test_case of test_cases) {
fetchResults(key, resolve, reject); fetchResults(key, resolve, reject);
}).then(data => { }).then(data => {
// If the position is not 'top', the :target element should be the positioned element.
assert_true(data.scrollPosition == 'top' || data.target == data.scrollPosition);
assert_equals(data.href.indexOf(':~:'), -1, 'Expected fragment directive to be stripped from the URL.'); assert_equals(data.href.indexOf(':~:'), -1, 'Expected fragment directive to be stripped from the URL.');
assert_equals(data.scrollPosition, test_case.expect_position, assert_equals(data.scrollPosition, test_case.expect_position,
`Expected ${test_case.fragment} (${test_case.description}) to scroll to ${test_case.expect_position}.`); `Expected ${test_case.fragment} (${test_case.description}) to scroll to ${test_case.expect_position}.`);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment