Bug 1650502 - Plumb whether an async APZ animation is in progress via RepaintRequest. r=botond

And store that information in the scroll frame for the purposes of
avoiding scroll anchoring and scroll restoration.

Differential Revision: https://phabricator.services.mozilla.com/D85157
This commit is contained in:
Emilio Cobos Álvarez 2020-08-02 16:58:29 +00:00
Родитель fff8c6763f
Коммит f08a5f293b
9 изменённых файлов: 99 добавлений и 44 удалений

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

@ -57,10 +57,12 @@ struct RepaintRequest {
mPaintRequestTime(),
mScrollUpdateType(eNone),
mIsRootContent(false),
mIsAnimationInProgress(false),
mIsScrollInfoLayer(false) {}
RepaintRequest(const FrameMetrics& aOther,
const ScrollOffsetUpdateType aScrollUpdateType)
const ScrollOffsetUpdateType aScrollUpdateType,
bool aIsAnimationInProgress)
: mScrollId(aOther.GetScrollId()),
mPresShellResolution(aOther.GetPresShellResolution()),
mCompositionBounds(aOther.GetCompositionBounds()),
@ -76,6 +78,7 @@ struct RepaintRequest {
mPaintRequestTime(aOther.GetPaintRequestTime()),
mScrollUpdateType(aScrollUpdateType),
mIsRootContent(aOther.IsRootContent()),
mIsAnimationInProgress(aIsAnimationInProgress),
mIsScrollInfoLayer(aOther.IsScrollInfoLayer()) {}
// Default copy ctor and operator= are fine
@ -97,6 +100,7 @@ struct RepaintRequest {
mPaintRequestTime == aOther.mPaintRequestTime &&
mScrollUpdateType == aOther.mScrollUpdateType &&
mIsRootContent == aOther.mIsRootContent &&
mIsAnimationInProgress == aOther.mIsAnimationInProgress &&
mIsScrollInfoLayer == aOther.mIsScrollInfoLayer;
}
@ -147,6 +151,8 @@ struct RepaintRequest {
return mDevPixelsPerCSSPixel;
}
bool IsAnimationInProgress() const { return mIsAnimationInProgress; }
bool IsRootContent() const { return mIsRootContent; }
const CSSPoint& GetScrollOffset() const { return mScrollOffset; }
@ -180,6 +186,10 @@ struct RepaintRequest {
bool IsScrollInfoLayer() const { return mIsScrollInfoLayer; }
protected:
void SetIsAnimationInProgress(bool aInProgress) {
mIsAnimationInProgress = aInProgress;
}
void SetIsRootContent(bool aIsRootContent) {
mIsRootContent = aIsRootContent;
}
@ -280,6 +290,9 @@ struct RepaintRequest {
// Whether or not this is the root scroll frame for the root content document.
bool mIsRootContent : 1;
// Whether or not we are in the middle of a scroll animation.
bool mIsAnimationInProgress : 1;
// True if this scroll frame is a scroll info layer. A scroll info layer is
// not layerized and its content cannot be truly async-scrolled, but its
// metrics are still sent to and updated by the compositor, with the updates

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

@ -3982,7 +3982,8 @@ void AsyncPanZoomController::RequestContentRepaint(
}
MOZ_ASSERT(controller->IsRepaintThread());
RepaintRequest request(aFrameMetrics, aUpdateType);
const bool isAnimationInProgress = !!mAnimation;
RepaintRequest request(aFrameMetrics, aUpdateType, isAnimationInProgress);
// If we're trying to paint what we already think is painted, discard this
// request since it's a pointless paint.
@ -3998,7 +3999,9 @@ void AsyncPanZoomController::RequestContentRepaint(
request.GetScrollGeneration() ==
mLastPaintRequestMetrics.GetScrollGeneration() &&
request.GetScrollUpdateType() ==
mLastPaintRequestMetrics.GetScrollUpdateType()) {
mLastPaintRequestMetrics.GetScrollUpdateType() &&
request.IsAnimationInProgress() ==
mLastPaintRequestMetrics.IsAnimationInProgress()) {
return;
}

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

@ -164,7 +164,8 @@ static ScreenMargin ScrollFrame(nsIContent* aContent,
nsIScrollableFrame* sf =
nsLayoutUtils::FindScrollableFrameFor(aRequest.GetScrollId());
if (sf) {
sf->ResetScrollInfoIfGeneration(aRequest.GetScrollGeneration());
sf->ResetScrollInfoIfNeeded(aRequest.GetScrollGeneration(),
aRequest.IsAnimationInProgress());
sf->SetScrollableByAPZ(!aRequest.IsScrollInfoLayer());
if (sf->IsRootScrollFrameOfDocument()) {
if (!APZCCallbackHelper::IsScrollInProgress(sf)) {
@ -834,10 +835,10 @@ void APZCCallbackHelper::NotifyFlushComplete(PresShell* aPresShell) {
/* static */
bool APZCCallbackHelper::IsScrollInProgress(nsIScrollableFrame* aFrame) {
return aFrame->IsProcessingAsyncScroll() ||
nsLayoutUtils::CanScrollOriginClobberApz(aFrame->LastScrollOrigin()) ||
aFrame->LastSmoothScrollOrigin() != ScrollOrigin::None ||
aFrame->GetRelativeOffset().isSome();
using IncludeApzAnimation = nsIScrollableFrame::IncludeApzAnimation;
return aFrame->IsScrollAnimating(IncludeApzAnimation::No) ||
nsLayoutUtils::CanScrollOriginClobberApz(aFrame->LastScrollOrigin());
}
/* static */

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

@ -179,8 +179,11 @@ class APZCCallbackHelper {
ScreenPoint aScrollDelta);
/*
* Check if the scrollable frame is currently in the middle of an async
* or smooth scroll. We want to discard certain scroll input if this is
* Check if the scrollable frame is currently in the middle of a main thread
* async or smooth scroll, or has already requested some other apz scroll that
* hasn't been acknowledged by apz.
*
* We want to discard apz updates to the main-thread scroll offset if this is
* true to prevent clobbering higher priority origins.
*/
static bool IsScrollInProgress(nsIScrollableFrame* aFrame);

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

@ -278,6 +278,7 @@ struct ParamTraits<mozilla::layers::RepaintRequest>
WriteParam(aMsg, aParam.mPaintRequestTime);
WriteParam(aMsg, aParam.mScrollUpdateType);
WriteParam(aMsg, aParam.mIsRootContent);
WriteParam(aMsg, aParam.mIsAnimationInProgress);
WriteParam(aMsg, aParam.mIsScrollInfoLayer);
}
@ -299,6 +300,8 @@ struct ParamTraits<mozilla::layers::RepaintRequest>
ReadParam(aMsg, aIter, &aResult->mScrollUpdateType) &&
ReadBoolForBitfield(aMsg, aIter, aResult,
&paramType::SetIsRootContent) &&
ReadBoolForBitfield(aMsg, aIter, aResult,
&paramType::SetIsAnimationInProgress) &&
ReadBoolForBitfield(aMsg, aIter, aResult,
&paramType::SetIsScrollInfoLayer));
}

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

@ -418,19 +418,17 @@ void ScrollAnchorContainer::ApplyAdjustments() {
if (!mAnchorNode || mAnchorNodeIsDirty || mDisabled ||
mScrollFrame->HasPendingScrollRestoration() ||
mScrollFrame->IsProcessingScrollEvent() ||
mScrollFrame->IsProcessingAsyncScroll() ||
mScrollFrame->mApzSmoothScrollDestination.isSome() ||
mScrollFrame->IsScrollAnimating() ||
mScrollFrame->GetScrollPosition() == nsPoint()) {
ANCHOR_LOG(
"Ignoring post-reflow (anchor=%p, dirty=%d, disabled=%d, "
"pendingRestoration=%d, scrollevent=%d, asyncScroll=%d, "
"apzSmoothDestination=%d, zeroScrollPos=%d pendingSuppression=%d, "
"pendingRestoration=%d, scrollevent=%d, animating=%d, "
"zeroScrollPos=%d pendingSuppression=%d, "
"container=%p).\n",
mAnchorNode, mAnchorNodeIsDirty, mDisabled,
mScrollFrame->HasPendingScrollRestoration(),
mScrollFrame->IsProcessingScrollEvent(),
mScrollFrame->IsProcessingAsyncScroll(),
mScrollFrame->mApzSmoothScrollDestination.isSome(),
mScrollFrame->IsScrollAnimating(),
mScrollFrame->GetScrollPosition() == nsPoint(),
mSuppressAnchorAdjustment, this);
if (mSuppressAnchorAdjustment) {

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

@ -2184,6 +2184,7 @@ ScrollFrameHelper::ScrollFrameHelper(nsContainerFrame* aOuter, bool aIsRoot)
mIsUsingMinimumScaleSize(false),
mMinimumScaleSizeChanged(false),
mProcessingScrollEvent(false),
mApzAnimationInProgress(false),
mVelocityQueue(aOuter->PresContext()) {
if (LookAndFeel::GetInt(LookAndFeel::IntID::UseOverlayScrollbars) != 0) {
mScrollbarActivity = new ScrollbarActivity(do_QueryFrame(aOuter));
@ -6185,8 +6186,7 @@ bool ScrollFrameHelper::ReflowFinished() {
// do anything.
nsPoint currentScrollPos = GetScrollPosition();
ScrollToImpl(currentScrollPos, nsRect(currentScrollPos, nsSize(0, 0)));
if (!mAsyncScroll && !mAsyncSmoothMSDScroll &&
!mApzSmoothScrollDestination) {
if (!IsScrollAnimating()) {
// We need to have mDestination track the current scroll position,
// in case it falls outside the new reflow area. mDestination is used
// by ScrollBy as its starting position.
@ -6826,6 +6826,16 @@ nscoord ScrollFrameHelper::GetCoordAttribute(nsIFrame* aBox, nsAtom* aAtom,
return aDefaultValue;
}
bool ScrollFrameHelper::IsScrollAnimating(
IncludeApzAnimation aIncludeApz) const {
if (aIncludeApz == IncludeApzAnimation::Yes && IsApzAnimationInProgress()) {
return true;
}
return mAsyncScroll || mAsyncSmoothMSDScroll ||
LastSmoothScrollOrigin() != ScrollOrigin::None ||
mRelativeOffset.isSome();
}
UniquePtr<PresState> ScrollFrameHelper::SaveState() const {
nsIScrollbarMediator* mediator = do_QueryFrame(GetScrolledFrame());
if (mediator) {
@ -6835,10 +6845,8 @@ UniquePtr<PresState> ScrollFrameHelper::SaveState() const {
// Don't store a scroll state if we never have been scrolled or restored
// a previous scroll state, and we're not in the middle of a smooth scroll.
bool isInSmoothScroll = IsProcessingAsyncScroll() ||
mLastSmoothScrollOrigin != ScrollOrigin::None ||
mRelativeOffset.isSome();
if (!mHasBeenScrolled && !mDidHistoryRestore && !isInSmoothScroll) {
bool isScrollAnimating = IsScrollAnimating(IncludeApzAnimation::No);
if (!mHasBeenScrolled && !mDidHistoryRestore && !isScrollAnimating) {
return nullptr;
}
@ -6856,7 +6864,7 @@ UniquePtr<PresState> ScrollFrameHelper::SaveState() const {
// effectively dropping it. Note that the mRestorePos will override the
// smooth scroll destination if both are present.
nsPoint pt = GetLogicalVisualViewportOffset();
if (isInSmoothScroll) {
if (isScrollAnimating) {
pt = mDestination;
allowScrollOriginDowngrade = false;
}

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

@ -376,9 +376,6 @@ class ScrollFrameHelper : public nsIReflowCallback {
return mWillBuildScrollableLayer;
}
bool IsMaybeScrollingActive() const;
bool IsProcessingAsyncScroll() const {
return mAsyncScroll != nullptr || mAsyncSmoothMSDScroll != nullptr;
}
void ResetScrollPositionForLayerPixelAlignment() {
mScrollPosForLayerPixelAlignment = GetScrollPosition();
}
@ -463,13 +460,22 @@ class ScrollFrameHelper : public nsIReflowCallback {
ScrollOrigin LastSmoothScrollOrigin() const {
return mLastSmoothScrollOrigin;
}
bool IsApzAnimationInProgress() const { return mApzAnimationInProgress; }
uint32_t CurrentScrollGeneration() const { return mScrollGeneration; }
nsPoint LastScrollDestination() const { return mDestination; }
void ResetScrollInfoIfGeneration(uint32_t aGeneration) {
using IncludeApzAnimation = nsIScrollableFrame::IncludeApzAnimation;
bool IsScrollAnimating(IncludeApzAnimation = IncludeApzAnimation::Yes) const;
void ResetScrollInfoIfNeeded(uint32_t aGeneration,
bool aApzAnimationInProgress) {
if (aGeneration == mScrollGeneration) {
mLastScrollOrigin = ScrollOrigin::NotSpecified;
mLastSmoothScrollOrigin = ScrollOrigin::None;
}
// We can reset this regardless of scroll generation, as this is only set
// here, as a response to APZ requesting a repaint.
mApzAnimationInProgress = aApzAnimationInProgress;
}
Maybe<nsPoint> GetRelativeOffset() const { return mRelativeOffset; }
bool WantAsyncScroll() const;
@ -705,6 +711,13 @@ class ScrollFrameHelper : public nsIReflowCallback {
// True if we're processing an scroll event.
bool mProcessingScrollEvent : 1;
// Whether an APZ animation is in progress. Note that this is only set to true
// when repainted via APZ, which means that there may be a request for an APZ
// animation in flight for example, while this is still false. In order to
// answer "is an APZ animation in the process of starting or in progress" you
// need to check both mLastSmoothScrollOrigin and this bit.
bool mApzAnimationInProgress : 1;
mozilla::layout::ScrollVelocityQueue mVelocityQueue;
protected:
@ -1018,9 +1031,6 @@ class nsHTMLScrollFrame : public nsContainerFrame,
bool IsMaybeAsynchronouslyScrolled() final {
return mHelper.IsMaybeAsynchronouslyScrolled();
}
bool IsProcessingAsyncScroll() final {
return mHelper.IsProcessingAsyncScroll();
}
void ResetScrollPositionForLayerPixelAlignment() final {
mHelper.ResetScrollPositionForLayerPixelAlignment();
}
@ -1037,14 +1047,18 @@ class nsHTMLScrollFrame : public nsContainerFrame,
ScrollOrigin LastSmoothScrollOrigin() final {
return mHelper.LastSmoothScrollOrigin();
}
bool IsScrollAnimating(IncludeApzAnimation aIncludeApz) final {
return mHelper.IsScrollAnimating(aIncludeApz);
}
uint32_t CurrentScrollGeneration() final {
return mHelper.CurrentScrollGeneration();
}
nsPoint LastScrollDestination() final {
return mHelper.LastScrollDestination();
}
void ResetScrollInfoIfGeneration(uint32_t aGeneration) final {
mHelper.ResetScrollInfoIfGeneration(aGeneration);
void ResetScrollInfoIfNeeded(uint32_t aGeneration,
bool aApzAnimationInProgress) final {
mHelper.ResetScrollInfoIfNeeded(aGeneration, aApzAnimationInProgress);
}
Maybe<nsPoint> GetRelativeOffset() const final {
return mHelper.GetRelativeOffset();
@ -1493,9 +1507,6 @@ class nsXULScrollFrame final : public nsBoxFrame,
bool IsMaybeAsynchronouslyScrolled() final {
return mHelper.IsMaybeAsynchronouslyScrolled();
}
bool IsProcessingAsyncScroll() final {
return mHelper.IsProcessingAsyncScroll();
}
void ResetScrollPositionForLayerPixelAlignment() final {
mHelper.ResetScrollPositionForLayerPixelAlignment();
}
@ -1512,14 +1523,18 @@ class nsXULScrollFrame final : public nsBoxFrame,
ScrollOrigin LastSmoothScrollOrigin() final {
return mHelper.LastSmoothScrollOrigin();
}
bool IsScrollAnimating(IncludeApzAnimation aIncludeApz) final {
return mHelper.IsScrollAnimating(aIncludeApz);
}
uint32_t CurrentScrollGeneration() final {
return mHelper.CurrentScrollGeneration();
}
nsPoint LastScrollDestination() final {
return mHelper.LastScrollDestination();
}
void ResetScrollInfoIfGeneration(uint32_t aGeneration) final {
mHelper.ResetScrollInfoIfGeneration(aGeneration);
void ResetScrollInfoIfNeeded(uint32_t aGeneration,
bool aApzAnimationInProgress) final {
mHelper.ResetScrollInfoIfNeeded(aGeneration, aApzAnimationInProgress);
}
Maybe<nsPoint> GetRelativeOffset() const final {
return mHelper.GetRelativeOffset();

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

@ -380,11 +380,6 @@ class nsIScrollableFrame : public nsIScrollbarMediator {
* which means that it can be called during display list building.
*/
virtual bool IsMaybeScrollingActive() const = 0;
/**
* Returns true if the scrollframe is currently processing an async
* or smooth scroll.
*/
virtual bool IsProcessingAsyncScroll() = 0;
/**
* Call this when the layer(s) induced by active scrolling are being
* completely redrawn.
@ -436,6 +431,19 @@ class nsIScrollableFrame : public nsIScrollbarMediator {
* compositor, this is set to nullptr to clear the smooth scroll.
*/
virtual ScrollOrigin LastSmoothScrollOrigin() = 0;
/**
* Returns whether there's an async scroll going on.
*
* The argument allows a subtle distinction that's needed for APZ. When
* `IncludeApzAnimation::No` is given, ongoing APZ animations that have
* already been synced to the main thread are not included, which is needed so
* that APZ can keep syncing the scroll offset properly.
*/
enum class IncludeApzAnimation : bool { No, Yes };
virtual bool IsScrollAnimating(
IncludeApzAnimation = IncludeApzAnimation::Yes) = 0;
/**
* Returns the current generation counter for the scroll. This counter
* increments every time the scroll position is set.
@ -449,9 +457,12 @@ class nsIScrollableFrame : public nsIScrollbarMediator {
/**
* Clears the "origin of last scroll" property stored in this frame, if
* the generation counter passed in matches the current scroll generation
* counter.
* counter, and clears the "origin of last smooth scroll" property if the
* generation counter matches. It also resets whether there's an ongoing apz
* animation.
*/
virtual void ResetScrollInfoIfGeneration(uint32_t aGeneration) = 0;
virtual void ResetScrollInfoIfNeeded(uint32_t aGeneration,
bool aApzAnimationInProgress) = 0;
/**
* Relative scrolling offset to be requested of apz.
*/