Skip to content
Permalink
Browse files
Hold the Scroll to the Text Fragment until a user scroll happens.
https://bugs.webkit.org/show_bug.cgi?id=245207
rdar://99668200

Reviewed by Tim Horton.

For each layout that comes in until the user scrolls we need to reset
the scroll to keep the fragment in the middle of the page.
Otherwise, more content can come in the scroll the fragment off the
page, which can be very confusing to the user.

* Source/WebCore/page/FrameView.cpp:
(WebCore::FrameView::reset):
(WebCore::FrameView::scrollToFragment):
(WebCore::FrameView::maintainScrollPositionAtRange):
(WebCore::FrameView::textFragmentIndicatorTimerFired):
(WebCore::FrameView::cancelScheduledTextFragmentIndicatorTimer):
(WebCore::FrameView::scrollToRange):
(WebCore::FrameView::performPostLayoutTasks):
* Source/WebCore/page/FrameView.h:

Canonical link: https://commits.webkit.org/254507@main
  • Loading branch information
megangardner committed Sep 15, 2022
1 parent 2bf7fb9 commit 4dc689b8cf6e04d32ec66f41d8c3d9615a7ec4a0
Show file tree
Hide file tree
Showing 2 changed files with 53 additions and 10 deletions.
@@ -267,6 +267,8 @@ void FrameView::reset()
m_shouldScrollToFocusedElement = false;
m_delayedScrollToFocusedElementTimer.stop();
m_delayedTextFragmentIndicatorTimer.stop();
m_pendingTextFragmentIndicatorRange.reset();
m_pendingTextFragmentIndicatorText = String();
m_lastViewportSize = IntSize();
m_lastZoomFactor = 1.0f;
m_isTrackingRepaints = false;
@@ -2229,8 +2231,7 @@ bool FrameView::scrollToFragment(const URL& url)
if (highlightRanges.size()) {
auto range = highlightRanges.first();
TemporarySelectionChange selectionChange(document, { range }, { TemporarySelectionOption::DelegateMainFrameScroll, TemporarySelectionOption::RevealSelectionBounds, TemporarySelectionOption::UserTriggered, TemporarySelectionOption::ForceCenterScroll });
m_pendingTextFragmentIndicatorRange = range;
m_pendingTextFragmentIndicatorText = plainText(range);
maintainScrollPositionAtScrollToTextFragmentRange(range);
if (frame().settings().scrollToTextFragmentIndicatorEnabled())
m_delayedTextFragmentIndicatorTimer.startOneShot(100_ms);
return true;
@@ -2321,6 +2322,18 @@ void FrameView::maintainScrollPositionAtAnchor(ContainerNode* anchorNode)
scrollToAnchor();
}

void FrameView::maintainScrollPositionAtScrollToTextFragmentRange(SimpleRange& range)
{
LOG(Scrolling, "FrameView::maintainScrollPositionAtScrollToTextFragmentRange at %p", range);

m_pendingTextFragmentIndicatorRange = range;
m_pendingTextFragmentIndicatorText = plainText(range);
if (!m_pendingTextFragmentIndicatorRange)
return;

scrollToTextFragmentRange();
}

void FrameView::scrollElementToRect(const Element& element, const IntRect& rect)
{
frame().document()->updateLayoutIgnorePendingStylesheets();
@@ -2457,25 +2470,24 @@ void FrameView::textFragmentIndicatorTimerFired()
ASSERT(frame().document());
auto& document = *frame().document();

if (!m_pendingTextFragmentIndicatorRange) {
cancelScheduledTextFragmentIndicatorTimer();
m_delayedTextFragmentIndicatorTimer.stop();

if (!m_pendingTextFragmentIndicatorRange)
return;
}

if (m_pendingTextFragmentIndicatorText != plainText(m_pendingTextFragmentIndicatorRange.value())) {
cancelScheduledTextFragmentIndicatorTimer();
if (m_pendingTextFragmentIndicatorText != plainText(m_pendingTextFragmentIndicatorRange.value()))
return;
}

auto range = m_pendingTextFragmentIndicatorRange.value();

TemporarySelectionChange selectionChange(document, { range }, { TemporarySelectionOption::DelegateMainFrameScroll, TemporarySelectionOption::RevealSelectionBounds, TemporarySelectionOption::UserTriggered, TemporarySelectionOption::ForceCenterScroll });

maintainScrollPositionAtScrollToTextFragmentRange(range);

auto textIndicator = TextIndicator::createWithRange(range, { TextIndicatorOption::DoNotClipToVisibleRect }, WebCore::TextIndicatorPresentationTransition::Bounce);

auto* page = frame().page();

cancelScheduledTextFragmentIndicatorTimer();

if (!page)
return;

@@ -2509,6 +2521,8 @@ void FrameView::textFragmentIndicatorTimerFired()

void FrameView::cancelScheduledTextFragmentIndicatorTimer()
{
if (m_skipScrollResetOfScrollToTextFragmentRange)
return;
m_pendingTextFragmentIndicatorRange.reset();
m_pendingTextFragmentIndicatorText = String();
m_delayedTextFragmentIndicatorTimer.stop();
@@ -3531,6 +3545,30 @@ void FrameView::scrollToAnchor()
cancelScheduledScrolls();
}

void FrameView::scrollToTextFragmentRange()
{
if (!m_pendingTextFragmentIndicatorRange)
return;

auto rangeText = plainText(m_pendingTextFragmentIndicatorRange.value());
if (m_pendingTextFragmentIndicatorText != plainText(m_pendingTextFragmentIndicatorRange.value()))
return;

auto range = m_pendingTextFragmentIndicatorRange.value();

LOG_WITH_STREAM(Scrolling, stream << *this << " scrollToTextFragmentRange() " << range);

if (!range.startContainer().renderer() || !range.endContainer().renderer())
return;

ASSERT(frame().document());
Ref document = *frame().document();

SetForScope skipScrollResetOfScrollToTextFragmentRange(m_skipScrollResetOfScrollToTextFragmentRange, true);

TemporarySelectionChange selectionChange(document, { range }, { TemporarySelectionOption::DelegateMainFrameScroll, TemporarySelectionOption::RevealSelectionBounds, TemporarySelectionOption::UserTriggered, TemporarySelectionOption::ForceCenterScroll });
}

void FrameView::updateEmbeddedObject(RenderEmbeddedObject& embeddedObject)
{
// No need to update if it's already crashed or known to be missing.
@@ -3654,6 +3692,8 @@ void FrameView::performPostLayoutTasks()
}

scrollToAnchor();

scrollToTextFragmentRange();

scheduleResizeEventIfNeeded();

@@ -472,6 +472,7 @@ class FrameView final : public ScrollView {

bool scrollToFragment(const URL&);
void maintainScrollPositionAtAnchor(ContainerNode*);
void maintainScrollPositionAtScrollToTextFragmentRange(SimpleRange&);
WEBCORE_EXPORT void scrollElementToRect(const Element&, const IntRect&);

// Coordinate systems:
@@ -845,6 +846,7 @@ class FrameView final : public ScrollView {

bool scrollToFragmentInternal(StringView);
void scrollToAnchor();
void scrollToTextFragmentRange();
void scrollPositionChanged(const ScrollPosition& oldPosition, const ScrollPosition& newPosition);
void scrollableAreaSetChanged();
void scheduleScrollEvent();
@@ -911,6 +913,7 @@ class FrameView final : public ScrollView {
RefPtr<Node> m_nodeToDraw;
std::optional<SimpleRange> m_pendingTextFragmentIndicatorRange;
String m_pendingTextFragmentIndicatorText;
bool m_skipScrollResetOfScrollToTextFragmentRange { false };

// Renderer to hold our custom scroll corner.
RenderPtr<RenderScrollbarPart> m_scrollCorner;

0 comments on commit 4dc689b

Please sign in to comment.