Bug 1662013 - Update NotifyLayersUpdated to use the ScrollPositionUpdates. r=tnikkel,botond

This rewrites a big chunk of the NotifyLayersUpdated code (most of the code that
deals with incoming scroll requests from the main-thread) to instead iterate
through the list of ScrollPositionUpdates on the metadata and apply them in
order. A bunch of the ApplyXXXUpdateFrom functions on FrameMetrics have their
innards deduplicated and boil down to a single line, which is then inlined, so
those functions get removed entirely.

Note that this rewrite doesn't yet handle all the possible types of
ScrollPositionUpdate instances, just the ones that the old code handled. In the
future this support will be fleshed out with tests to exercise the relevant
codepaths.

There is also a change to nsGfxScrollFrame which slightly modifies the semantics
of mApzScrollPos to handle the case where multiple relative scrolls happen in a
single transaction. As the implementation now requires multiple relative
ScrollPositionUpdates rather than a single "unified" relative scroll in the
FrameMetrics, we need to update mApzScrollPos for each relative
ScrollPositionUpdate we generate.

Differential Revision: https://phabricator.services.mozilla.com/D88744
This commit is contained in:
Kartikaya Gupta 2020-09-12 13:06:10 +00:00
Родитель f334f15463
Коммит c942c614e3
5 изменённых файлов: 152 добавлений и 186 удалений

Просмотреть файл

@ -93,7 +93,7 @@ void FrameMetrics::KeepLayoutViewportEnclosingVisualViewport(
aLayoutViewport = aLayoutViewport.MoveInsideAndClamp(aScrollableRect);
}
void FrameMetrics::ApplyScrollUpdateFrom(const FrameMetrics& aContentMetrics) {
void FrameMetrics::ApplyScrollUpdateFrom(const ScrollPositionUpdate& aUpdate) {
// In applying a main-thread scroll update, try to preserve the relative
// offset between the visual and layout viewports.
CSSPoint relativeOffset = GetVisualScrollOffset() - GetLayoutScrollOffset();
@ -101,11 +101,8 @@ void FrameMetrics::ApplyScrollUpdateFrom(const FrameMetrics& aContentMetrics) {
// We need to set the two offsets together, otherwise a subsequent
// RecalculateLayoutViewportOffset() could see divergent layout and
// visual offsets.
SetLayoutScrollOffset(aContentMetrics.GetLayoutScrollOffset());
ClampAndSetVisualScrollOffset(aContentMetrics.GetLayoutScrollOffset() +
relativeOffset);
mScrollGeneration = aContentMetrics.mScrollGeneration;
SetLayoutScrollOffset(aUpdate.GetDestination());
ClampAndSetVisualScrollOffset(aUpdate.GetDestination() + relativeOffset);
}
ScrollSnapInfo::ScrollSnapInfo()

Просмотреть файл

@ -262,13 +262,7 @@ struct FrameMetrics {
aContentFrameMetrics.GetVisualScrollOffset();
}
void ApplyScrollUpdateFrom(const FrameMetrics& aContentMetrics);
void ApplySmoothScrollUpdateFrom(const FrameMetrics& aOther) {
mSmoothScrollOffset = aOther.mSmoothScrollOffset;
mScrollGeneration = aOther.mScrollGeneration;
mDoSmoothScroll = aOther.mDoSmoothScroll;
}
void ApplyScrollUpdateFrom(const ScrollPositionUpdate& aUpdate);
/**
* Applies the relative scroll offset update contained in aOther to the
@ -277,48 +271,14 @@ struct FrameMetrics {
*
* @returns The clamped scroll offset delta that was applied
*/
CSSPoint ApplyRelativeScrollUpdateFrom(const FrameMetrics& aOther) {
MOZ_ASSERT(aOther.IsRelative());
CSSPoint ApplyRelativeScrollUpdateFrom(const ScrollPositionUpdate& aUpdate) {
MOZ_ASSERT(aUpdate.GetType() == ScrollUpdateType::Relative);
CSSPoint origin = GetVisualScrollOffset();
CSSPoint delta =
(aOther.GetLayoutScrollOffset() - aOther.mBaseScrollOffset);
CSSPoint delta = (aUpdate.GetDestination() - aUpdate.GetSource());
ClampAndSetVisualScrollOffset(origin + delta);
mScrollGeneration = aOther.mScrollGeneration;
return GetVisualScrollOffset() - origin;
}
/**
* Applies the relative scroll offset update contained in aOther to the smooth
* scroll destination offset contained in this, or to the provided existing
* destination, if one is provided. The scroll delta is clamped to the
* scrollable region.
*/
void ApplyRelativeSmoothScrollUpdateFrom(
const FrameMetrics& aOther, const Maybe<CSSPoint>& aExistingDestination) {
MOZ_ASSERT(aOther.IsRelative());
CSSPoint delta = (aOther.mSmoothScrollOffset - aOther.mBaseScrollOffset);
ClampAndSetSmoothScrollOffset(
aExistingDestination.valueOr(GetVisualScrollOffset()) + delta);
mScrollGeneration = aOther.mScrollGeneration;
mDoSmoothScroll = aOther.mDoSmoothScroll;
}
void ApplyPureRelativeSmoothScrollUpdateFrom(
const FrameMetrics& aOther, const Maybe<CSSPoint>& aExistingDestination,
bool aApplyToSmoothScroll) {
MOZ_ASSERT(aOther.IsPureRelative() && aOther.mPureRelativeOffset.isSome());
// See AsyncPanZoomController::NotifyLayersUpdated where
// pureRelativeSmoothScrollRequested is handled for the explanation for the
// logic in this function.
ClampAndSetSmoothScrollOffset(
(aApplyToSmoothScroll
? mSmoothScrollOffset
: aExistingDestination.valueOr(GetVisualScrollOffset())) +
*aOther.mPureRelativeOffset);
mScrollGeneration = aOther.mScrollGeneration;
mDoSmoothScroll = true;
}
void UpdatePendingScrollInfo(const ScrollPositionUpdate& aInfo) {
SetLayoutScrollOffset(aInfo.GetDestination());
mScrollGeneration = aInfo.GetGeneration();
@ -1032,6 +992,10 @@ struct ScrollMetadata {
mScrollUpdates = aUpdates;
}
const nsTArray<ScrollPositionUpdate>& GetScrollUpdates() const {
return mScrollUpdates;
}
void UpdatePendingScrollInfo(const ScrollPositionUpdate& aInfo) {
mMetrics.UpdatePendingScrollInfo(aInfo);

Просмотреть файл

@ -215,6 +215,7 @@ void AppendToString(std::stringstream& aStream, const ScrollMetadata& m,
AppendToString(aStream, overscrollY, "] [overscroll-y=");
}
}
aStream << "] [" << m.GetScrollUpdates().Length() << " scrollupdates";
aStream << "] }" << sfx;
}

Просмотреть файл

@ -4550,30 +4550,6 @@ void AsyncPanZoomController::NotifyLayersUpdated(
}
}
// If the layers update was not triggered by our own repaint request, then
// we want to take the new scroll offset. Check the scroll generation as well
// to filter duplicate calls to NotifyLayersUpdated with the same scroll
// offset update message.
bool scrollOffsetUpdated =
aLayerMetrics.GetScrollOffsetUpdated() &&
(aLayerMetrics.GetScrollGeneration() != Metrics().GetScrollGeneration());
if (scrollOffsetUpdated && userScrolled &&
aLayerMetrics.GetScrollUpdateType() == FrameMetrics::eRestore) {
APZC_LOG(
"%p dropping scroll update of type eRestore because of user scroll\n",
this);
scrollOffsetUpdated = false;
}
bool smoothScrollRequested =
aLayerMetrics.GetDoSmoothScroll() &&
(aLayerMetrics.GetScrollGeneration() != Metrics().GetScrollGeneration());
bool pureRelativeSmoothScrollRequested =
aLayerMetrics.IsPureRelative() &&
(aLayerMetrics.GetScrollGeneration() != Metrics().GetScrollGeneration());
// If `isDefault` is true, this APZC is a "new" one (this is the first time
// it's getting a NotifyLayersUpdated call). In this case we want to apply the
// visual scroll offset from the main thread to our scroll offset.
@ -4589,10 +4565,8 @@ void AsyncPanZoomController::NotifyLayersUpdated(
bool visualScrollOffsetUpdated =
isDefault ||
aLayerMetrics.GetVisualScrollUpdateType() != FrameMetrics::eNone;
if ((aLayerMetrics.GetScrollUpdateType() == FrameMetrics::eMainThread &&
aLayerMetrics.GetVisualScrollUpdateType() !=
FrameMetrics::eMainThread) ||
smoothScrollRequested || pureRelativeSmoothScrollRequested) {
if (aLayerMetrics.GetScrollUpdateType() == FrameMetrics::eMainThread &&
aLayerMetrics.GetVisualScrollUpdateType() != FrameMetrics::eMainThread) {
visualScrollOffsetUpdated = false;
}
@ -4602,13 +4576,19 @@ void AsyncPanZoomController::NotifyLayersUpdated(
bool needContentRepaint = false;
RepaintUpdateType contentRepaintType = RepaintUpdateType::eNone;
bool viewportSizeUpdated = false;
bool needToReclampScroll = false;
if ((aIsFirstPaint && aThisLayerTreeUpdated) || isDefault) {
// Initialize our internal state to something sane when the content
// that was just painted is something we knew nothing about previously
CancelAnimation();
// Keep our existing scroll generation, as we'll update that when processing
// the scroll update array below. Eventually we can move this out of the
// metrics entirely and won't need to do this.
uint32_t oldScrollGeneration = Metrics().GetScrollGeneration();
mScrollMetadata = aScrollMetadata;
Metrics().SetScrollGeneration(oldScrollGeneration);
mExpectedGeckoMetrics.UpdateFrom(aLayerMetrics);
ShareCompositorFrameMetrics();
@ -4679,18 +4659,16 @@ void AsyncPanZoomController::NotifyLayersUpdated(
Metrics().SetDevPixelsPerCSSPixel(
aLayerMetrics.GetDevPixelsPerCSSPixel());
}
bool scrollableRectChanged = false;
bool compositionBoundsChanged = false;
if (!Metrics().GetScrollableRect().IsEqualEdges(
aLayerMetrics.GetScrollableRect())) {
Metrics().SetScrollableRect(aLayerMetrics.GetScrollableRect());
needContentRepaint = true;
scrollableRectChanged = true;
needToReclampScroll = true;
}
if (!Metrics().GetCompositionBounds().IsEqualEdges(
aLayerMetrics.GetCompositionBounds())) {
Metrics().SetCompositionBounds(aLayerMetrics.GetCompositionBounds());
compositionBoundsChanged = true;
needToReclampScroll = true;
}
Metrics().SetRootCompositionSize(aLayerMetrics.GetRootCompositionSize());
Metrics().SetPresShellResolution(aLayerMetrics.GetPresShellResolution());
@ -4714,123 +4692,147 @@ void AsyncPanZoomController::NotifyLayersUpdated(
aScrollMetadata.GetDisregardedDirection());
mScrollMetadata.SetOverscrollBehavior(
aScrollMetadata.GetOverscrollBehavior());
}
bool scrollOffsetUpdated = false;
for (const auto& scrollUpdate : aScrollMetadata.GetScrollUpdates()) {
APZC_LOG("%p processing scroll update %s\n", this,
Stringify(scrollUpdate).c_str());
if (scrollUpdate.GetGeneration() <= Metrics().GetScrollGeneration()) {
// This is stale, let's ignore it
// XXX maybe use a 64-bit value for the scroll generation, or add some
// overflow detection heuristic here
APZC_LOG("%p scrollupdate generation stale, dropping\n", this);
continue;
}
Metrics().SetScrollGeneration(scrollUpdate.GetGeneration());
MOZ_ASSERT(scrollUpdate.GetOrigin() != ScrollOrigin::Apz);
if (userScrolled &&
!nsLayoutUtils::CanScrollOriginClobberApz(scrollUpdate.GetOrigin())) {
APZC_LOG("%p scrollupdate cannot clobber APZ userScrolled\n", this);
continue;
}
// XXX: if we get here, |scrollUpdate| is clobbering APZ, so we may want
// to reset |userScrolled| back to false so that subsequent scrollUpdates
// in this loop don't get dropped by the check above. Need to add a test
// that exercises this scenario, as we don't currently have one.
scrollOffsetUpdated = true;
if (scrollUpdate.GetMode() == ScrollMode::Smooth ||
scrollUpdate.GetMode() == ScrollMode::SmoothMsd) {
// Requests to animate the visual scroll position override requests to
// simply update the visual scroll offset to a particular point. Since
// we have an animation request, we reset visualScrollOffsetUpdated
// to false to indicate we don't need to apply the visual scroll update
// in aLayerMetrics.
visualScrollOffsetUpdated = false;
// For relative updates we want to add the relative offset to any existing
// destination, or the current visual offset if there is no existing
// destination.
CSSPoint base = GetCurrentAnimationDestination(lock).valueOr(
Metrics().GetVisualScrollOffset());
if (scrollOffsetUpdated) {
Maybe<CSSPoint> relativeDelta;
if (StaticPrefs::apz_relative_update_enabled() &&
aLayerMetrics.IsRelative()) {
APZC_LOG("%p relative updating scroll offset from %s by %s\n", this,
ToString(Metrics().GetVisualScrollOffset()).c_str(),
ToString(aLayerMetrics.GetLayoutScrollOffset() -
aLayerMetrics.GetBaseScrollOffset())
.c_str());
// It's possible that the main thread has ignored an APZ scroll offset
// update for the pending relative scroll that we have just received.
// When this happens, we need to send a new scroll offset update with
// the combined scroll offset or else the main thread may have an
// incorrect scroll offset for a period of time.
if (Metrics().HasPendingScroll(aLayerMetrics)) {
needContentRepaint = true;
contentRepaintType = RepaintUpdateType::eUserAction;
}
relativeDelta =
Some(Metrics().ApplyRelativeScrollUpdateFrom(aLayerMetrics));
scrollUpdate.GetType() == ScrollUpdateType::Relative) {
CSSPoint delta =
scrollUpdate.GetDestination() - scrollUpdate.GetSource();
APZC_LOG("%p relative smooth scrolling from %s by %s\n", this,
ToString(base).c_str(), ToString(delta).c_str());
Metrics().ClampAndSetSmoothScrollOffset(base + delta);
} else if (scrollUpdate.GetType() == ScrollUpdateType::PureRelative) {
CSSPoint delta = scrollUpdate.GetDelta();
APZC_LOG("%p pure-relative smooth scrolling from %s by %s\n", this,
ToString(base).c_str(), ToString(delta).c_str());
Metrics().ClampAndSetSmoothScrollOffset(base + delta);
} else {
APZC_LOG("%p updating scroll offset from %s to %s\n", this,
ToString(Metrics().GetVisualScrollOffset()).c_str(),
ToString(aLayerMetrics.GetLayoutScrollOffset()).c_str());
Metrics().ApplyScrollUpdateFrom(aLayerMetrics);
APZC_LOG("%p smooth scrolling to %s\n", this,
ToString(scrollUpdate.GetDestination()).c_str());
Metrics().SetSmoothScrollOffset(scrollUpdate.GetDestination());
}
SmoothScrollTo(Metrics().GetSmoothScrollOffset());
continue;
}
MOZ_ASSERT(scrollUpdate.GetMode() == ScrollMode::Instant ||
scrollUpdate.GetMode() == ScrollMode::Normal);
Maybe<CSSPoint> relativeDelta;
if (StaticPrefs::apz_relative_update_enabled() &&
scrollUpdate.GetType() == ScrollUpdateType::Relative) {
APZC_LOG(
"%p relative updating scroll offset from %s by %s\n", this,
ToString(Metrics().GetVisualScrollOffset()).c_str(),
ToString(scrollUpdate.GetDestination() - scrollUpdate.GetSource())
.c_str());
// It's possible that the main thread has ignored an APZ scroll offset
// update for the pending relative scroll that we have just received.
// When this happens, we need to send a new scroll offset update with
// the combined scroll offset or else the main thread may have an
// incorrect scroll offset for a period of time.
if (Metrics().HasPendingScroll(aLayerMetrics)) {
needContentRepaint = true;
contentRepaintType = RepaintUpdateType::eUserAction;
}
relativeDelta =
Some(Metrics().ApplyRelativeScrollUpdateFrom(scrollUpdate));
Metrics().RecalculateLayoutViewportOffset();
} else {
APZC_LOG("%p updating scroll offset from %s to %s\n", this,
ToString(Metrics().GetVisualScrollOffset()).c_str(),
ToString(scrollUpdate.GetDestination()).c_str());
Metrics().ApplyScrollUpdateFrom(scrollUpdate);
Metrics().RecalculateLayoutViewportOffset();
}
for (auto& sampledState : mSampledState) {
sampledState.UpdateScrollProperties(Metrics());
}
// Because of the scroll generation update, any inflight paint requests
// are going to be ignored by layout, and so mExpectedGeckoMetrics becomes
// incorrect for the purposes of calculating the LD transform. To correct
// this we need to update mExpectedGeckoMetrics to be the last thing we
// know was painted by Gecko.
mExpectedGeckoMetrics.UpdateFrom(aLayerMetrics);
// If an animation is underway, tell it about the scroll offset update.
// Some animations can handle some scroll offset updates and continue
// running. Those that can't will return false, and we cancel them.
if (ShouldCancelAnimationForScrollUpdate(relativeDelta)) {
// Cancel the animation (which might also trigger a repaint request)
// after we update the scroll offset above. Otherwise we can be left
// in a state where things are out of sync.
CancelAnimation();
}
// Since the scroll offset has changed, we need to recompute the
// displayport margins and send them to layout. Otherwise there might be
// scenarios where for example we scroll from the top of a page (where the
// top displayport margin is zero) to the bottom of a page, which will
// result in a displayport that doesn't extend upwards at all.
// Note that even if the CancelAnimation call above requested a repaint
// this is fine because we already have repaint request deduplication.
needContentRepaint = true;
// Since the main-thread scroll offset changed we should trigger a
// recomposite to make sure it becomes user-visible.
ScheduleComposite();
} else if (scrollableRectChanged || compositionBoundsChanged) {
// Even if we didn't accept a new scroll offset from content, the
// scrollable rect or composition bounds may have changed in a way that
// makes our local scroll offset out of bounds, so re-clamp it.
ClampAndSetVisualScrollOffset(Metrics().GetVisualScrollOffset());
for (auto& sampledState : mSampledState) {
sampledState.ClampVisualScrollOffset(Metrics());
}
// If an animation is underway, tell it about the scroll offset update.
// Some animations can handle some scroll offset updates and continue
// running. Those that can't will return false, and we cancel them.
if (ShouldCancelAnimationForScrollUpdate(relativeDelta)) {
// Cancel the animation (which might also trigger a repaint request)
// after we update the scroll offset above. Otherwise we can be left
// in a state where things are out of sync.
CancelAnimation();
}
}
if (smoothScrollRequested || pureRelativeSmoothScrollRequested) {
// A smooth scroll has been requested for animation on the compositor
// thread. This flag will be reset by the main thread when it receives
// the scroll update acknowledgement.
APZC_LOG("%p smooth scrolling from %s to %s in state %d\n", this,
Stringify(Metrics().GetVisualScrollOffset()).c_str(),
Stringify(aLayerMetrics.GetSmoothScrollOffset()).c_str(), mState);
// For relative updates we want to add the relative offset to any existing
// destination.
Maybe<CSSPoint> destination = GetCurrentAnimationDestination(lock);
if (smoothScrollRequested) {
// See comment on the similar code in the |if (scrollOffsetUpdated)| block
// above.
if (StaticPrefs::apz_relative_update_enabled() &&
aLayerMetrics.IsRelative()) {
Metrics().ApplyRelativeSmoothScrollUpdateFrom(aLayerMetrics,
destination);
} else {
Metrics().ApplySmoothScrollUpdateFrom(aLayerMetrics);
}
if (scrollOffsetUpdated) {
for (auto& sampledState : mSampledState) {
sampledState.UpdateScrollProperties(Metrics());
}
if (pureRelativeSmoothScrollRequested) {
MOZ_ASSERT(aLayerMetrics.IsPureRelative());
MOZ_ASSERT(gfxPlatform::UseDesktopZoomingScrollbars());
// If smoothScrollRequested is true then mSmoothScrollOffset of our
// metrics has the base we want to add our relative offset to from above;
// |destination| has already been included if necessary. If
// smoothScrollRequested if false then we want to use destination as our
// base offset if it is Some. Otherwise mScrollOffset is our base.
// ApplyPureRelativeSmoothScrollUpdateFrom handles this logic for us.
Metrics().ApplyPureRelativeSmoothScrollUpdateFrom(
aLayerMetrics, destination, smoothScrollRequested);
}
needContentRepaint = true;
// Because of the scroll generation update, any inflight paint requests
// are going to be ignored by layout, and so mExpectedGeckoMetrics becomes
// incorrect for the purposes of calculating the LD transform. To correct
// this we need to update mExpectedGeckoMetrics to be the last thing we
// know was painted by Gecko.
mExpectedGeckoMetrics.UpdateFrom(aLayerMetrics);
SmoothScrollTo(Metrics().GetSmoothScrollOffset());
// Since the scroll offset has changed, we need to recompute the
// displayport margins and send them to layout. Otherwise there might be
// scenarios where for example we scroll from the top of a page (where the
// top displayport margin is zero) to the bottom of a page, which will
// result in a displayport that doesn't extend upwards at all.
// Note that even if the CancelAnimation call above requested a repaint
// this is fine because we already have repaint request deduplication.
needContentRepaint = true;
// Since the main-thread scroll offset changed we should trigger a
// recomposite to make sure it becomes user-visible.
ScheduleComposite();
} else if (needToReclampScroll) {
// Even if we didn't accept a new scroll offset from content, the
// scrollable rect or composition bounds may have changed in a way that
// makes our local scroll offset out of bounds, so re-clamp it.
ClampAndSetVisualScrollOffset(Metrics().GetVisualScrollOffset());
for (auto& sampledState : mSampledState) {
sampledState.ClampVisualScrollOffset(Metrics());
}
}
if (visualScrollOffsetUpdated) {
@ -4841,7 +4843,8 @@ void AsyncPanZoomController::NotifyLayersUpdated(
aLayerMetrics.GetVisualDestination());
// The rest of this branch largely follows the code in the
// |if (scrollOffsetUpdated)| branch above.
// |if (scrollOffsetUpdated)| branch above. Eventually it should get
// merged into that branch.
Metrics().RecalculateLayoutViewportOffset();
for (auto& sampledState : mSampledState) {
sampledState.UpdateScrollProperties(Metrics());

Просмотреть файл

@ -3037,6 +3037,7 @@ void ScrollFrameHelper::ScrollToImpl(nsPoint aPt, const nsRect& aRange,
MOZ_ASSERT(mLastScrollOrigin == ScrollOrigin::Relative);
mScrollUpdates.AppendElement(ScrollPositionUpdate::NewRelativeScroll(
mScrollGeneration, mApzScrollPos, pt));
mApzScrollPos = pt;
} else if (aOrigin != ScrollOrigin::Apz) {
mScrollUpdates.AppendElement(ScrollPositionUpdate::NewScroll(
mScrollGeneration, mLastScrollOrigin, pt));