From b5fd872e8dfa3afb9c059738dbc5106c0d9da5f4 Mon Sep 17 00:00:00 2001 From: Markus Stange Date: Thu, 17 Dec 2015 17:19:30 -0500 Subject: [PATCH] Bug 1209970 - Fire scroll events early in the refresh tick. r=mats With APZ we want to be firing scroll events to content more consistently, so we tie them to the refresh driver tick rather than firing them on paint or haphazardly on the next spin of the event loop. Patch by Markus Stange, test fixes by Kartikaya Gupta --- .../test/browser_scrollPositions.js | 2 ++ layout/generic/nsGfxScrollFrame.cpp | 29 ++++++++++++------- layout/generic/nsGfxScrollFrame.h | 13 +++++---- .../mochitest/tests/SimpleTest/EventUtils.js | 4 ++- widget/tests/window_wheeltransaction.xul | 1 + 5 files changed, 32 insertions(+), 17 deletions(-) diff --git a/browser/components/sessionstore/test/browser_scrollPositions.js b/browser/components/sessionstore/test/browser_scrollPositions.js index d59676fcb18..15a6754ce59 100644 --- a/browser/components/sessionstore/test/browser_scrollPositions.js +++ b/browser/components/sessionstore/test/browser_scrollPositions.js @@ -16,6 +16,8 @@ const SCROLL2_X = Math.round(300 * (1 + Math.random())); const SCROLL2_Y = Math.round(400 * (1 + Math.random())); const SCROLL2_STR = SCROLL2_X + "," + SCROLL2_Y; +requestLongerTimeout(2); + /** * This test ensures that we properly serialize and restore scroll positions * for an average page without any frames. diff --git a/layout/generic/nsGfxScrollFrame.cpp b/layout/generic/nsGfxScrollFrame.cpp index 19ebdaa671b..fe73db6afb3 100644 --- a/layout/generic/nsGfxScrollFrame.cpp +++ b/layout/generic/nsGfxScrollFrame.cpp @@ -4225,18 +4225,28 @@ void ScrollFrameHelper::CurPosAttributeChanged(nsIContent* aContent) /* ============= Scroll events ========== */ -NS_IMETHODIMP -ScrollFrameHelper::ScrollEvent::Run() +ScrollFrameHelper::ScrollEvent::ScrollEvent(ScrollFrameHelper* aHelper) + : mHelper(aHelper) { - if (mHelper) - mHelper->FireScrollEvent(); - return NS_OK; + mHelper->mOuter->PresContext()->RefreshDriver()->AddRefreshObserver(this, Flush_Style); +} + +ScrollFrameHelper::ScrollEvent::~ScrollEvent() +{ + mHelper->mOuter->PresContext()->RefreshDriver()->RemoveRefreshObserver(this, Flush_Style); +} + +void +ScrollFrameHelper::ScrollEvent::WillRefresh(mozilla::TimeStamp aTime) +{ + mHelper->FireScrollEvent(); } void ScrollFrameHelper::FireScrollEvent() { - mScrollEvent.Forget(); + MOZ_ASSERT(mScrollEvent); + mScrollEvent = nullptr; ActiveLayerTracker::SetCurrentScrollHandlerFrame(mOuter); WidgetGUIEvent event(true, eScroll, nullptr); @@ -4263,14 +4273,11 @@ ScrollFrameHelper::FireScrollEvent() void ScrollFrameHelper::PostScrollEvent() { - if (mScrollEvent.IsPending()) + if (mScrollEvent) return; - nsRootPresContext* rpc = mOuter->PresContext()->GetRootPresContext(); - if (!rpc) - return; + // The ScrollEvent constructor registers itself with the refresh driver. mScrollEvent = new ScrollEvent(this); - rpc->AddWillPaintObserver(mScrollEvent.get()); } NS_IMETHODIMP diff --git a/layout/generic/nsGfxScrollFrame.h b/layout/generic/nsGfxScrollFrame.h index f8c554e6027..5ee4f1195fa 100644 --- a/layout/generic/nsGfxScrollFrame.h +++ b/layout/generic/nsGfxScrollFrame.h @@ -19,6 +19,7 @@ #include "nsIReflowCallback.h" #include "nsBoxLayoutState.h" #include "nsQueryFrame.h" +#include "nsRefreshDriver.h" #include "nsExpirationTracker.h" #include "TextOverflow.h" #include "ScrollVelocityQueue.h" @@ -101,11 +102,13 @@ public: bool IsSmoothScrollingEnabled(); - class ScrollEvent : public nsRunnable { + class ScrollEvent : public nsARefreshObserver { public: - NS_DECL_NSIRUNNABLE - explicit ScrollEvent(ScrollFrameHelper *helper) : mHelper(helper) {} - void Revoke() { mHelper = nullptr; } + NS_INLINE_DECL_REFCOUNTING(ScrollEvent, override) + explicit ScrollEvent(ScrollFrameHelper *helper); + void WillRefresh(mozilla::TimeStamp aTime) override; + protected: + virtual ~ScrollEvent(); private: ScrollFrameHelper *mHelper; }; @@ -417,7 +420,7 @@ public: nsCOMPtr mScrollCornerContent; nsCOMPtr mResizerContent; - nsRevocableEventPtr mScrollEvent; + RefPtr mScrollEvent; nsRevocableEventPtr mAsyncScrollPortEvent; nsRevocableEventPtr mScrolledAreaEvent; nsIFrame* mHScrollbarBox; diff --git a/testing/mochitest/tests/SimpleTest/EventUtils.js b/testing/mochitest/tests/SimpleTest/EventUtils.js index 8f35605b993..20f1e9e26c0 100644 --- a/testing/mochitest/tests/SimpleTest/EventUtils.js +++ b/testing/mochitest/tests/SimpleTest/EventUtils.js @@ -526,8 +526,10 @@ function sendWheelAndPaint(aTarget, aOffsetX, aOffsetY, aEvent, aCallback, aWind setTimeout(function() { utils.advanceTimeAndRefresh(1000); - if (!aCallback) + if (!aCallback) { + utils.advanceTimeAndRefresh(0); return; + } var waitForPaints = function () { SpecialPowers.Services.obs.removeObserver(waitForPaints, "apz-repaints-flushed", false); diff --git a/widget/tests/window_wheeltransaction.xul b/widget/tests/window_wheeltransaction.xul index e69396051f6..8573eb3a4a9 100644 --- a/widget/tests/window_wheeltransaction.xul +++ b/widget/tests/window_wheeltransaction.xul @@ -1034,6 +1034,7 @@ function initElements() resetScrollPosition(gSubView1); resetScrollPosition(gSubView2); resetScrollPosition(gSubView3); + _getDOMWindowUtils(window).advanceTimeAndRefresh(0); runNextTestStep(); }