Bug 1897956 - Text Fragments: Set text fragment as :target. r=farre,dom-core
This patch makes some adjustments to follow the spec more strictly by separating highlighting a text fragment (ie. adding the ranges to a `eTargetText` Selection) from scrolling to it. Scrolling to the text fragment now follows the steps given in [0] more closely, in particular now the closest common ancestor of the target range is being used as `:target` element. This change also sets the focus to the start of the first text fragment, as indicated by the spec. This resets the normal selection on the page. The wpt test in /css/css-pseudo/target-text-005.html is based on the idea of having a same-doc text fragment navigation with a selection. This test fails with this patch applied, because the selection is reset. It is currently unclear what behavior is correct here, thus the test is kept for now. [0]: https://wicg.github.io/scroll-to-text-fragment/#invoking-text-directives Differential Revision: https://phabricator.services.mozilla.com/D211025
This commit is contained in:
@@ -3062,7 +3062,8 @@ UniquePtr<gfxContext> PresShell::CreateReferenceRenderingContext() {
|
||||
}
|
||||
|
||||
// https://html.spec.whatwg.org/#scroll-to-the-fragment-identifier
|
||||
nsresult PresShell::GoToAnchor(const nsAString& aAnchorName, bool aScroll,
|
||||
nsresult PresShell::GoToAnchor(const nsAString& aAnchorName,
|
||||
const nsRange* aFirstTextDirective, bool aScroll,
|
||||
ScrollFlags aAdditionalScrollFlags) {
|
||||
if (!mDocument) {
|
||||
return NS_ERROR_FAILURE;
|
||||
@@ -3081,12 +3082,31 @@ nsresult PresShell::GoToAnchor(const nsAString& aAnchorName, bool aScroll,
|
||||
// Hold a reference to the ESM in case event dispatch tears us down.
|
||||
RefPtr<EventStateManager> esm = mPresContext->EventStateManager();
|
||||
|
||||
// https://wicg.github.io/scroll-to-text-fragment/#invoking-text-directives
|
||||
// From "Monkeypatching HTML § 7.4.6.3 Scrolling to a fragment:"
|
||||
// 3.4. If target is a range, then:
|
||||
// 3.4.1 Set target to be the first common ancestor of target's start node and
|
||||
// end node.
|
||||
// 3.4.2 While target is non-null and is not an element, set target to
|
||||
// target's parent.
|
||||
Element* textFragmentTargetElement = [&aFirstTextDirective]() -> Element* {
|
||||
nsINode* node =
|
||||
aFirstTextDirective
|
||||
? aFirstTextDirective->GetClosestCommonInclusiveAncestor()
|
||||
: nullptr;
|
||||
while (node && !node->IsElement()) {
|
||||
node = node->GetParent();
|
||||
}
|
||||
return Element::FromNodeOrNull(node);
|
||||
}();
|
||||
const bool thereIsATextFragment = !!textFragmentTargetElement;
|
||||
|
||||
// 1. If there is no indicated part of the document, set the Document's target
|
||||
// element to null.
|
||||
//
|
||||
// FIXME(emilio): Per spec empty fragment string should take the same
|
||||
// code-path as "top"!
|
||||
if (aAnchorName.IsEmpty()) {
|
||||
if (aAnchorName.IsEmpty() && !thereIsATextFragment) {
|
||||
NS_ASSERTION(!aScroll, "can't scroll to empty anchor name");
|
||||
esm->SetContentState(nullptr, ElementState::URLTARGET);
|
||||
return NS_OK;
|
||||
@@ -3100,8 +3120,10 @@ nsresult PresShell::GoToAnchor(const nsAString& aAnchorName, bool aScroll,
|
||||
//
|
||||
// https://html.spec.whatwg.org/#target-element
|
||||
// https://html.spec.whatwg.org/#find-a-potential-indicated-element
|
||||
RefPtr<Element> target =
|
||||
nsContentUtils::GetTargetElement(mDocument, aAnchorName);
|
||||
RefPtr<Element> target = textFragmentTargetElement;
|
||||
if (!target) {
|
||||
target = nsContentUtils::GetTargetElement(mDocument, aAnchorName);
|
||||
}
|
||||
|
||||
// 1. If there is no indicated part of the document, set the Document's
|
||||
// target element to null.
|
||||
@@ -3120,6 +3142,13 @@ nsresult PresShell::GoToAnchor(const nsAString& aAnchorName, bool aScroll,
|
||||
|
||||
if (target) {
|
||||
if (aScroll) {
|
||||
// https://wicg.github.io/scroll-to-text-fragment/#invoking-text-directives
|
||||
// From "Monkeypatching HTML § 7.4.6.3 Scrolling to a fragment:"
|
||||
// 3.9 Let blockPosition be "center" if scrollTarget is a range, "start"
|
||||
// otherwise.
|
||||
const auto verticalScrollPosition =
|
||||
thereIsATextFragment ? WhereToScroll(WhereToScroll::Center)
|
||||
: WhereToScroll(WhereToScroll::Start);
|
||||
// 3.3. TODO: Run the ancestor details revealing algorithm on target.
|
||||
// 3.4. Scroll target into view, with behavior set to "auto", block set to
|
||||
// "start", and inline set to "nearest".
|
||||
@@ -3127,7 +3156,7 @@ nsresult PresShell::GoToAnchor(const nsAString& aAnchorName, bool aScroll,
|
||||
// smooth scroll for `top` regardless below, so maybe they should!).
|
||||
ScrollingInteractionContext scrollToAnchorContext(true);
|
||||
MOZ_TRY(ScrollContentIntoView(
|
||||
target, ScrollAxis(WhereToScroll::Start, WhenToScroll::Always),
|
||||
target, ScrollAxis(verticalScrollPosition, WhenToScroll::Always),
|
||||
ScrollAxis(),
|
||||
ScrollFlags::AnchorScrollFlags | aAdditionalScrollFlags));
|
||||
|
||||
@@ -3245,46 +3274,6 @@ nsresult PresShell::ScrollToAnchor() {
|
||||
ScrollAxis(), ScrollFlags::AnchorScrollFlags);
|
||||
}
|
||||
|
||||
bool PresShell::HighlightAndGoToTextFragment(bool aScrollToTextFragment) {
|
||||
MOZ_ASSERT(mDocument);
|
||||
if (!StaticPrefs::dom_text_fragments_enabled()) {
|
||||
return false;
|
||||
}
|
||||
const RefPtr<FragmentDirective> fragmentDirective =
|
||||
mDocument->FragmentDirective();
|
||||
|
||||
nsTArray<RefPtr<nsRange>> textDirectiveRanges =
|
||||
fragmentDirective->FindTextFragmentsInDocument();
|
||||
if (textDirectiveRanges.IsEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const RefPtr<Selection> targetTextSelection =
|
||||
GetCurrentSelection(SelectionType::eTargetText);
|
||||
if (!targetTextSelection) {
|
||||
return false;
|
||||
}
|
||||
for (RefPtr<nsRange> range : textDirectiveRanges) {
|
||||
targetTextSelection->AddRangeAndSelectFramesAndNotifyListeners(
|
||||
*range, IgnoreErrors());
|
||||
}
|
||||
if (!aScrollToTextFragment) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Scroll the last text directive into view.
|
||||
nsRange* lastRange = textDirectiveRanges.LastElement();
|
||||
MOZ_ASSERT(lastRange);
|
||||
if (RefPtr<nsIContent> lastRangeStartContent =
|
||||
nsIContent::FromNode(lastRange->GetStartContainer())) {
|
||||
return ScrollContentIntoView(
|
||||
lastRangeStartContent,
|
||||
ScrollAxis(WhereToScroll::Center, WhenToScroll::Always),
|
||||
ScrollAxis(), ScrollFlags::AnchorScrollFlags) == NS_OK;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Helper (per-continuation) for ScrollContentIntoView.
|
||||
*
|
||||
|
||||
Reference in New Issue
Block a user