Bug 1906134 - Text Fragments: Ensure to scroll the first text directive on a page into the center of the view. r=emilio

Before this patch, it could happen that `PresShell::GoToAnchor()` was called while loading a document.
This method did not know about text fragments before,
and would scroll the anchor (ie., the first text directive) to the top of the view.
This was noticeable for pages that took longer to load, where the text directive was scrolled to the center first, and then to the top.

This patch saves the desired vertical scroll position as a member in `PresShell` and makes `GoToAnchor()` scroll to the center as well.

Additionally, this patch updates the test file target document to make it more mobile-friendly.

Differential Revision: https://phabricator.services.mozilla.com/D216039
This commit is contained in:
Jan-Niklas Jaeschke 2024-07-16 09:16:48 +00:00
parent 0e5d229536
commit 5ce2766d95
5 changed files with 61 additions and 1 deletions

View File

@ -749,6 +749,7 @@ PresShell::PresShell(Document* aDocument)
: mDocument(aDocument),
mViewManager(nullptr),
mAutoWeakFrames(nullptr),
mLastAnchorVerticalScrollViewPosition(WhereToScroll::Start),
#ifdef ACCESSIBILITY
mDocAccessible(nullptr),
#endif // ACCESSIBILITY
@ -3172,6 +3173,7 @@ nsresult PresShell::GoToAnchor(const nsAString& aAnchorName,
if (ScrollContainerFrame* rootScroll = GetRootScrollContainerFrame()) {
mLastAnchorScrolledTo = target;
mLastAnchorScrollPositionY = rootScroll->GetScrollPosition().y;
mLastAnchorVerticalScrollViewPosition = verticalScrollPosition;
}
}
@ -3279,7 +3281,8 @@ nsresult PresShell::ScrollToAnchor() {
return NS_OK;
}
return ScrollContentIntoView(
lastAnchor, ScrollAxis(WhereToScroll::Start, WhenToScroll::Always),
lastAnchor,
ScrollAxis(mLastAnchorVerticalScrollViewPosition, WhenToScroll::Always),
ScrollAxis(), ScrollFlags::AnchorScrollFlags);
}

View File

@ -2989,6 +2989,11 @@ class PresShell final : public nsStubDocumentObserver,
nsCOMPtr<nsIContent> mLastAnchorScrolledTo;
// Text directives are supposed to be scrolled to the center of the viewport.
// Since `ScrollToAnchor()` might get called after `GoToAnchor()` during a
// load, the vertical view position should be preserved.
WhereToScroll mLastAnchorVerticalScrollViewPosition;
// Information needed to properly handle scrolling content into view if the
// pre-scroll reflow flush can be interrupted. mContentToScrollTo is non-null
// between the initial scroll attempt and the first time we finish processing

View File

@ -0,0 +1,26 @@
<!DOCTYPE html>
<title>
Ensuring a text directive is scrolled to the center of the view port instead of the top.
</title>
<script src="stash.js"></script>
<script>
function checkScroll() {
const results = {hasScrolled: window.scrollY != 0};
let key = (new URL(document.location)).searchParams.get("key");
stashResultsThenClose(key, results);
};
window.onload = () => {
window.requestAnimationFrame(function() {
window.requestAnimationFrame(checkScroll);
})
}
</script>
<body>
<script>
document.addEventListener("DOMContentLoaded", () => {
// trigger a layout flush
_ = document.body.getBoundingClientRect();
});
</script>
<div style="margin-top: 20vh; margin-bottom: 100vh">Scroll to me</div>
</body>

View File

@ -0,0 +1,25 @@
<!doctype html>
<title>Navigating to a text fragment directive</title>
<meta charset=utf-8>
<link rel="help" href="https://wicg.github.io/ScrollToTextFragment/">
<meta name="timeout" content="long">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/resources/testdriver.js"></script>
<script src="/resources/testdriver-vendor.js"></script>
<script src="/common/utils.js"></script>
<script src="stash.js"></script>
<script>
promise_test(t => new Promise((resolve, reject) => {
let key = token();
test_driver.bless('Open a URL with a text fragment directive', () => {
window.open(`scroll-to-text-fragment-scroll-to-center-target.html?key=${key}#:~:text=Scroll`, "_blank", "noopener");
fetchResults(key, resolve, reject);
})
}).then(data => {
assert_false(data.hasScrolled, "Expected text directive to be scrolled to.");
})
);
</script>

View File

@ -1,6 +1,7 @@
<!doctype html>
<title>Navigating to a text fragment anchor</title>
<script src="stash.js"></script>
<meta name="viewport" content="width=device-width,initial-scale=1">
<script>
function isInView(element) {
let rect = element.getBoundingClientRect();