Bug 1895555 - Text Fragments: Implement same-document navigation. r=farre,dom-core
Same-document navigation follows a different code path than normal navigation and was therefore not covered in the initial implementation for text fragments. Same-document navigation does not set a URI in the `Document`, which is the way cross-document navigation would parse text directives from the URL. Instead, `nsDocShell::ScrollToAnchor()` is called via `nsDocShell::InternalLoad()`-> `nsDocShell::HandleSameDocumentNavigation()`. This code path needs to parse and remove the fragment directive from the new fragment to be able to find text fragments and to allow for element-id fallback. `nsDocShell::ScrollToAnchor()` needs to start an attempt to scroll to the text fragment if it exists. It must not, however, clear the uninvoked text directives, because a same-document navigation could happen before the document is fully loaded, hence the target text might not be part of the DOM tree. As per spec, a second attempt to scroll to the text fragment is done after the load is completed. This is done by `Document::ScrollToRef()`, which is called by `nsDocumentViewer::LoadComplete()` after the load has finished. This call will clear the uninvoked directives. Differential Revision: https://phabricator.services.mozilla.com/D209726
This commit is contained in:
@@ -64,6 +64,7 @@
|
||||
#include "mozilla/dom/ContentFrameMessageManager.h"
|
||||
#include "mozilla/dom/DocGroup.h"
|
||||
#include "mozilla/dom/Element.h"
|
||||
#include "mozilla/dom/FragmentDirective.h"
|
||||
#include "mozilla/dom/HTMLAnchorElement.h"
|
||||
#include "mozilla/dom/HTMLIFrameElement.h"
|
||||
#include "mozilla/dom/PerformanceNavigation.h"
|
||||
@@ -8476,6 +8477,17 @@ bool nsDocShell::IsSameDocumentNavigation(nsDocShellLoadState* aLoadState,
|
||||
rvURINew = aLoadState->URI()->GetHasRef(&aState.mNewURIHasRef);
|
||||
}
|
||||
|
||||
// A Fragment Directive must be removed from the new hash in order to allow
|
||||
// fallback element id scroll. Additionally, the extracted parsed text
|
||||
// directives need to be stored for further use.
|
||||
nsTArray<TextDirective> textDirectives;
|
||||
if (FragmentDirective::ParseAndRemoveFragmentDirectiveFromFragmentString(
|
||||
aState.mNewHash, &textDirectives)) {
|
||||
if (Document* doc = GetDocument()) {
|
||||
doc->FragmentDirective()->SetTextDirectives(std::move(textDirectives));
|
||||
}
|
||||
}
|
||||
|
||||
if (currentURI && NS_SUCCEEDED(rvURINew)) {
|
||||
nsresult rvURIOld = currentURI->GetRef(aState.mCurrentHash);
|
||||
if (NS_SUCCEEDED(rvURIOld)) {
|
||||
@@ -10701,6 +10713,24 @@ nsresult nsDocShell::ScrollToAnchor(bool aCurHasRef, bool aNewHasRef,
|
||||
rootScroll->ClearDidHistoryRestore();
|
||||
}
|
||||
|
||||
// If it's a load from history, we don't have any anchor jumping to do.
|
||||
// Scrollbar position will be restored by the caller based on positions stored
|
||||
// in session history.
|
||||
bool scroll = aLoadType != LOAD_HISTORY && aLoadType != LOAD_RELOAD_NORMAL;
|
||||
// If the load contains text directives, try to apply them. This may fail if
|
||||
// the load is a same-document load that was initiated before the document was
|
||||
// fully loaded and the target is not yet included in the DOM tree.
|
||||
// For this case, the `uninvokedTextDirectives` are not cleared, so that
|
||||
// `Document::ScrollToRef()` can re-apply the text directive.
|
||||
// `Document::ScrollToRef()` is (presumably) the second "async" call mentioned
|
||||
// in sec. 7.4.2.3.3 in the HTML spec, "Fragment navigations":
|
||||
// https://html.spec.whatwg.org/#scroll-to-fragid:~:text=This%20algorithm%20will%20be%20called%20twice
|
||||
const bool hasScrolledToTextFragment =
|
||||
presShell->HighlightAndGoToTextFragment(scroll);
|
||||
if (hasScrolledToTextFragment) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// If we have no new anchor, we do not want to scroll, unless there is a
|
||||
// current anchor and we are doing a history load. So return if we have no
|
||||
// new anchor, and there is no current anchor or the load is not a history
|
||||
@@ -10712,11 +10742,6 @@ nsresult nsDocShell::ScrollToAnchor(bool aCurHasRef, bool aNewHasRef,
|
||||
// Both the new and current URIs refer to the same page. We can now
|
||||
// browse to the hash stored in the new URI.
|
||||
|
||||
// If it's a load from history, we don't have any anchor jumping to do.
|
||||
// Scrollbar position will be restored by the caller based on positions stored
|
||||
// in session history.
|
||||
bool scroll = aLoadType != LOAD_HISTORY && aLoadType != LOAD_RELOAD_NORMAL;
|
||||
|
||||
if (aNewHash.IsEmpty()) {
|
||||
// 2. If fragment is the empty string, then return the special value top of
|
||||
// the document.
|
||||
|
||||
Reference in New Issue
Block a user