forked from mirrors/gecko-dev
Bug 1867939, part 5: Integrate find-text-directive algorithm into Document load. r=peterv,farre,dom-core
This patch integrates the algorithm to find a text fragment range into the document loading mechanism. Unlike described in the spec, the fragment directive is not stripped from the URL in the Session History Entry, instead it is stripped when setting the URI into the Document using `Document::SetURI()`, as well as when accessing the URL through `Location`. The `PresShell` class is extended by a new method which sets the ranges created from the text directives into the FrameSelection as TargetText selection and scrolls it into view. Security restrictions like force load at top and cross-origin iframes are not yet considered in this patch. Differential Revision: https://phabricator.services.mozilla.com/D195688
This commit is contained in:
parent
81f5e14057
commit
fda59c7c38
6 changed files with 130 additions and 14 deletions
|
|
@ -173,6 +173,7 @@
|
|||
#include "mozilla/dom/FeaturePolicyUtils.h"
|
||||
#include "mozilla/dom/FontFaceSet.h"
|
||||
#include "mozilla/dom/FragmentDirective.h"
|
||||
#include "mozilla/dom/fragmentdirectives_ffi_generated.h"
|
||||
#include "mozilla/dom/FromParser.h"
|
||||
#include "mozilla/dom/HighlightRegistry.h"
|
||||
#include "mozilla/dom/HTMLAllCollection.h"
|
||||
|
|
@ -4067,6 +4068,21 @@ void Document::StopDocumentLoad() {
|
|||
void Document::SetDocumentURI(nsIURI* aURI) {
|
||||
nsCOMPtr<nsIURI> oldBase = GetDocBaseURI();
|
||||
mDocumentURI = aURI;
|
||||
// This loosely implements §3.4.1 of Text Fragments
|
||||
// https://wicg.github.io/scroll-to-text-fragment/#invoking-text-directives
|
||||
// Unlike specified in the spec, the fragment directive is not stripped from
|
||||
// the URL in the session history entry. Instead it is removed when the URL is
|
||||
// set in the `Document`. Also, instead of storing the `uninvokedDirective` in
|
||||
// `Document` as mentioned in the spec, the extracted directives are moved to
|
||||
// the `FragmentDirective` object which deals with finding the ranges to
|
||||
// highlight in `ScrollToRef()`.
|
||||
// XXX(:jjaschke): This is only a temporary solution.
|
||||
// https://bugzil.la/1881429 is filed for revisiting this.
|
||||
nsTArray<TextDirective> textDirectives;
|
||||
FragmentDirective::ParseAndRemoveFragmentDirectiveFromFragment(
|
||||
mDocumentURI, &textDirectives);
|
||||
FragmentDirective()->SetTextDirectives(std::move(textDirectives));
|
||||
|
||||
nsIURI* newBase = GetDocBaseURI();
|
||||
|
||||
mChromeRulesEnabled = URLExtraData::ChromeRulesEnabled(aURI);
|
||||
|
|
@ -13081,25 +13097,29 @@ void Document::SetScrollToRef(nsIURI* aDocumentURI) {
|
|||
|
||||
// https://html.spec.whatwg.org/#scrolling-to-a-fragment
|
||||
void Document::ScrollToRef() {
|
||||
if (mScrolledToRefAlready) {
|
||||
RefPtr<PresShell> presShell = GetPresShell();
|
||||
if (presShell) {
|
||||
presShell->ScrollToAnchor();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// 2. If fragment is the empty string, then return the special value top of
|
||||
// the document.
|
||||
if (mScrollToRef.IsEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
RefPtr<PresShell> presShell = GetPresShell();
|
||||
if (!presShell) {
|
||||
return;
|
||||
}
|
||||
if (mScrolledToRefAlready) {
|
||||
presShell->ScrollToAnchor();
|
||||
return;
|
||||
}
|
||||
|
||||
// If text directives is non-null, then highlight the text directives and
|
||||
// scroll to the last one.
|
||||
// XXX(:jjaschke): Document policy integration should happen here
|
||||
// as soon as https://bugzil.la/1860915 lands.
|
||||
// XXX(:jjaschke): Same goes for User Activation and security aspects,
|
||||
// tracked in https://bugzil.la/1888756.
|
||||
const bool didScrollToTextFragment =
|
||||
presShell->HighlightAndGoToTextFragment(true);
|
||||
|
||||
// 2. If fragment is the empty string and no text directives have been
|
||||
// scrolled to, then return the special value top of the document.
|
||||
if (didScrollToTextFragment || mScrollToRef.IsEmpty()) {
|
||||
return;
|
||||
}
|
||||
// 3. Let potentialIndicatedElement be the result of finding a potential
|
||||
// indicated element given document and fragment.
|
||||
NS_ConvertUTF8toUTF16 ref(mScrollToRef);
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@
|
|||
#include "nsICSSDeclaration.h"
|
||||
#include "nsIFrame.h"
|
||||
#include "nsINode.h"
|
||||
#include "nsIURIMutator.h"
|
||||
#include "nsRange.h"
|
||||
#include "nsString.h"
|
||||
|
||||
|
|
@ -50,6 +51,34 @@ JSObject* FragmentDirective::WrapObject(JSContext* aCx,
|
|||
return FragmentDirective_Binding::Wrap(aCx, this, aGivenProto);
|
||||
}
|
||||
|
||||
void FragmentDirective::ParseAndRemoveFragmentDirectiveFromFragment(
|
||||
nsCOMPtr<nsIURI>& aURI, nsTArray<TextDirective>* aTextDirectives) {
|
||||
if (!aURI || !StaticPrefs::dom_text_fragments_enabled()) {
|
||||
return;
|
||||
}
|
||||
bool hasRef = false;
|
||||
aURI->GetHasRef(&hasRef);
|
||||
if (!hasRef) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsAutoCString hash;
|
||||
aURI->GetRef(hash);
|
||||
|
||||
ParsedFragmentDirectiveResult fragmentDirective;
|
||||
const bool hasRemovedFragmentDirective =
|
||||
parse_fragment_directive(&hash, &fragmentDirective);
|
||||
if (!hasRemovedFragmentDirective) {
|
||||
return;
|
||||
}
|
||||
Unused << NS_MutateURI(aURI)
|
||||
.SetRef(fragmentDirective.url_without_fragment_directive)
|
||||
.Finalize(aURI);
|
||||
if (aTextDirectives) {
|
||||
aTextDirectives->SwapElements(fragmentDirective.text_directives);
|
||||
}
|
||||
}
|
||||
|
||||
nsTArray<RefPtr<nsRange>> FragmentDirective::FindTextFragmentsInDocument() {
|
||||
MOZ_ASSERT(mDocument);
|
||||
mDocument->FlushPendingNotifications(FlushType::Frames);
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@
|
|||
#include "nsWrapperCache.h"
|
||||
|
||||
class nsINode;
|
||||
class nsIURI;
|
||||
class nsRange;
|
||||
namespace mozilla::dom {
|
||||
class Document;
|
||||
|
|
@ -84,6 +85,15 @@ class FragmentDirective final : public nsISupports, public nsWrapperCache {
|
|||
*/
|
||||
nsTArray<RefPtr<nsRange>> FindTextFragmentsInDocument();
|
||||
|
||||
/** Utility function which parses the fragment directive and removes it from
|
||||
* the hash of the given URI. This operation happens in-place.
|
||||
*
|
||||
* If aTextDirectives is nullptr, the parsed fragment directive is discarded.
|
||||
*/
|
||||
static void ParseAndRemoveFragmentDirectiveFromFragment(
|
||||
nsCOMPtr<nsIURI>& aURI,
|
||||
nsTArray<TextDirective>* aTextDirectives = nullptr);
|
||||
|
||||
private:
|
||||
RefPtr<nsRange> FindRangeForTextDirective(
|
||||
const TextDirective& aTextDirective);
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@
|
|||
#include "mozilla/Unused.h"
|
||||
#include "mozilla/dom/Document.h"
|
||||
#include "mozilla/dom/DocumentInlines.h"
|
||||
#include "mozilla/dom/FragmentDirective.h"
|
||||
#include "mozilla/dom/LocationBinding.h"
|
||||
#include "mozilla/dom/ScriptSettings.h"
|
||||
#include "ReferrerInfo.h"
|
||||
|
|
@ -105,6 +106,9 @@ nsresult Location::GetURI(nsIURI** aURI, bool aGetInnermostURI) {
|
|||
}
|
||||
|
||||
NS_ASSERTION(uri, "nsJARURI screwed up?");
|
||||
|
||||
// Remove the fragment directive from the url hash.
|
||||
FragmentDirective::ParseAndRemoveFragmentDirectiveFromFragment(uri);
|
||||
nsCOMPtr<nsIURI> exposableURI = net::nsIOService::CreateExposableURI(uri);
|
||||
exposableURI.forget(aURI);
|
||||
return NS_OK;
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@
|
|||
#include "mozilla/dom/AncestorIterator.h"
|
||||
#include "mozilla/dom/FontFaceSet.h"
|
||||
#include "mozilla/dom/ElementBinding.h"
|
||||
#include "mozilla/dom/FragmentDirective.h"
|
||||
#include "mozilla/dom/LargestContentfulPaint.h"
|
||||
#include "mozilla/dom/MouseEventBinding.h"
|
||||
#include "mozilla/dom/PerformanceMainThread.h"
|
||||
|
|
@ -3291,6 +3292,46 @@ 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.
|
||||
*
|
||||
|
|
|
|||
|
|
@ -1609,6 +1609,18 @@ class PresShell final : public nsStubDocumentObserver,
|
|||
*/
|
||||
MOZ_CAN_RUN_SCRIPT nsresult ScrollToAnchor();
|
||||
|
||||
/**
|
||||
* Finds text fragments ranes in the document, highlights the ranges and
|
||||
* scrolls to the last text fragment range on the page if
|
||||
* `aScrollToTextFragment` is true.
|
||||
*
|
||||
* @param aScrollToTextFragment If true, scrolls the view to the last text
|
||||
* fragment.
|
||||
* @return True if scrolling happened.
|
||||
*/
|
||||
MOZ_CAN_RUN_SCRIPT bool HighlightAndGoToTextFragment(
|
||||
bool aScrollToTextFragment);
|
||||
|
||||
/**
|
||||
* When scroll anchoring adjusts positions in the root frame during page load,
|
||||
* it may move our scroll position in the root frame.
|
||||
|
|
|
|||
Loading…
Reference in a new issue