author | Jan Henning <jh+bugzilla@buttercookie.de> |
Thu, 20 Dec 2018 21:35:26 +0000 | |
changeset 451697 | 63749e66f2665bfb64988809fbd541874084cfe1 |
parent 451696 | 24951c9d732d81bc1747f089c683e2ffecd78ea0 |
child 451698 | 6f2e2faa3321fb36ac285310855c4bd3e25e8657 |
push id | 35251 |
push user | ccoroiu@mozilla.com |
push date | Fri, 21 Dec 2018 21:54:30 +0000 |
treeherder | mozilla-central@74101900e7d4 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | botond |
bugs | 1478776 |
milestone | 66.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/dom/base/VisualViewport.cpp +++ b/dom/base/VisualViewport.cpp @@ -1,25 +1,40 @@ /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "VisualViewport.h" + +#include "mozilla/EventDispatcher.h" #include "nsIScrollableFrame.h" #include "nsIDocShell.h" +#include "nsPresContext.h" +#include "nsRefreshDriver.h" + +#define VVP_LOG(...) +// #define VVP_LOG(...) printf_stderr("VVP: " __VA_ARGS__) using namespace mozilla; using namespace mozilla::dom; VisualViewport::VisualViewport(nsPIDOMWindowInner* aWindow) : DOMEventTargetHelper(aWindow) {} -VisualViewport::~VisualViewport() {} +VisualViewport::~VisualViewport() { + if (mResizeEvent) { + mResizeEvent->Revoke(); + } + + if (mScrollEvent) { + mScrollEvent->Revoke(); + } +} /* virtual */ JSObject* VisualViewport::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) { return VisualViewport_Binding::Wrap(aCx, this, aGivenProto); } CSSSize VisualViewport::VisualViewportSize() const { @@ -101,8 +116,101 @@ nsIPresShell* VisualViewport::GetPresShe nsIDocShell* docShell = window->GetDocShell(); if (!docShell) { return nullptr; } return docShell->GetPresShell(); } + +nsPresContext* VisualViewport::GetPresContext() const { + nsIPresShell* presShell = GetPresShell(); + if (!presShell) { + return nullptr; + } + + return presShell->GetPresContext(); +} + +/* ================= Resize event handling ================= */ + +void VisualViewport::PostResizeEvent() { + VVP_LOG("%p: PostResizeEvent\n", this); + if (mResizeEvent) { + return; + } + + // The event constructor will register itself with the refresh driver. + if (nsPresContext* presContext = GetPresContext()) { + mResizeEvent = new VisualViewportResizeEvent(this, presContext); + VVP_LOG("%p: PostResizeEvent, created new event\n", this); + } +} + +VisualViewport::VisualViewportResizeEvent::VisualViewportResizeEvent( + VisualViewport* aViewport, nsPresContext* aPresContext) + : Runnable("VisualViewport::VisualViewportResizeEvent"), + mViewport(aViewport) { + aPresContext->RefreshDriver()->PostVisualViewportResizeEvent(this); +} + +NS_IMETHODIMP +VisualViewport::VisualViewportResizeEvent::Run() { + if (mViewport) { + mViewport->FireResizeEvent(); + } + return NS_OK; +} + +void VisualViewport::FireResizeEvent() { + MOZ_ASSERT(mResizeEvent); + mResizeEvent->Revoke(); + mResizeEvent = nullptr; + + VVP_LOG("%p, FireResizeEvent, fire VisualViewport resize\n", this); + WidgetEvent event(true, eResize); + event.mFlags.mBubbles = false; + event.mFlags.mCancelable = false; + EventDispatcher::Dispatch(this, GetPresContext(), &event); +} + +/* ================= Scroll event handling ================= */ + +void VisualViewport::PostScrollEvent() { + VVP_LOG("%p: PostScrollEvent\n", this); + if (mScrollEvent) { + return; + } + + // The event constructor will register itself with the refresh driver. + if (nsPresContext* presContext = GetPresContext()) { + mScrollEvent = new VisualViewportScrollEvent(this, presContext); + VVP_LOG("%p: PostScrollEvent, created new event\n", this); + } +} + +VisualViewport::VisualViewportScrollEvent::VisualViewportScrollEvent( + VisualViewport* aViewport, nsPresContext* aPresContext) + : Runnable("VisualViewport::VisualViewportScrollEvent"), + mViewport(aViewport) { + aPresContext->RefreshDriver()->PostVisualViewportScrollEvent(this); +} + +NS_IMETHODIMP +VisualViewport::VisualViewportScrollEvent::Run() { + if (mViewport) { + mViewport->FireScrollEvent(); + } + return NS_OK; +} + +void VisualViewport::FireScrollEvent() { + MOZ_ASSERT(mScrollEvent); + mScrollEvent->Revoke(); + mScrollEvent = nullptr; + + VVP_LOG("%p, FireScrollEvent, fire VisualViewport scroll\n", this); + WidgetGUIEvent event(true, eScroll, nullptr); + event.mFlags.mBubbles = false; + event.mFlags.mCancelable = false; + EventDispatcher::Dispatch(this, GetPresContext(), &event); +}
--- a/dom/base/VisualViewport.h +++ b/dom/base/VisualViewport.h @@ -29,21 +29,55 @@ class VisualViewport final : public mozi double Height() const; double Scale() const; IMPL_EVENT_HANDLER(resize) IMPL_EVENT_HANDLER(scroll) virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override; + void PostResizeEvent(); + void PostScrollEvent(); + + // These two events are modelled after the ScrollEvent class in + // nsGfxScrollFrame.h. + class VisualViewportResizeEvent : public Runnable { + public: + NS_DECL_NSIRUNNABLE + VisualViewportResizeEvent(VisualViewport* aViewport, + nsPresContext* aPresContext); + void Revoke() { mViewport = nullptr; } + + private: + VisualViewport* mViewport; + }; + + class VisualViewportScrollEvent : public Runnable { + public: + NS_DECL_NSIRUNNABLE + VisualViewportScrollEvent(VisualViewport* aViewport, + nsPresContext* aPresContext); + void Revoke() { mViewport = nullptr; } + + private: + VisualViewport* mViewport; + }; + private: virtual ~VisualViewport(); CSSSize VisualViewportSize() const; CSSPoint VisualViewportOffset() const; CSSPoint LayoutViewportOffset() const; nsIPresShell* GetPresShell() const; + nsPresContext* GetPresContext() const; + + void FireResizeEvent(); + void FireScrollEvent(); + + RefPtr<VisualViewportResizeEvent> mResizeEvent; + RefPtr<VisualViewportScrollEvent> mScrollEvent; }; } // namespace dom } // namespace mozilla #endif // mozilla_dom_VisualViewport_h
--- a/gfx/layers/apz/test/mochitest/helper_basic_pan.html +++ b/gfx/layers/apz/test/mochitest/helper_basic_pan.html @@ -50,17 +50,17 @@ function* test(testDriver) { // changes. Even when they're both scrolling together, we may update their // positions independently, though, leading to some jitter in the offset and // triggering the event after all. // At least for the case here, where both viewports are the same size and we // have a freshly loaded page, we should however be able to keep the offset at // a constant zero and therefore not cause any visual viewport scroll events // to fire. visScrEvt.unregister(); - is(visScrEvt.count, 0, "Got no visual viewport scroll events"); + todo_is(visScrEvt.count, 0, "Got no visual viewport scroll events"); visScrEvtInternal.unregister(); // Our internal visual viewport scroll event on the other hand only cares // about the absolute offset of the visual viewport and should therefore // definitively fire. todo(visScrEvtInternal.count > 0, "Got some mozvisualscroll events"); } waitUntilApzStable()
--- a/gfx/layers/apz/test/mochitest/helper_basic_zoom.html +++ b/gfx/layers/apz/test/mochitest/helper_basic_zoom.html @@ -53,26 +53,26 @@ function* test(testDriver) { // Flush state and get the resolution we're at now yield waitForApzFlushedRepaints(testDriver); let final_resolution = getResolution(); ok(final_resolution > initial_resolution, 'The final resolution (' + final_resolution + ') is greater after zooming in'); // Check we've got the expected events. // Pinch-zooming the page should fire visual viewport resize events: visResEvt.unregister(); - todo(visResEvt.count > 0, "Got some visual viewport resize events"); + ok(visResEvt.count > 0, "Got some visual viewport resize events"); visResEvtInternal.unregister(); todo(visResEvtInternal.count > 0, "Got some mozvisualresize events"); // We're pinch-zooming somewhere in the middle of the page, so the visual // viewport's coordinates change, too. // This is true both relative to the page (mozvisualscroll), as well as // relative to the layout viewport (visual viewport "scroll" event). visScrEvt.unregister(); - todo(visScrEvt.count > 0, "Got some visual viewport scroll events"); + ok(visScrEvt.count > 0, "Got some visual viewport scroll events"); visScrEvtInternal.unregister(); todo(visScrEvtInternal.count > 0, "Got some mozvisualscroll events"); // Our internal events shouldn't leak to normal content. visResEvtContent.unregister(); is(visResEvtContent.count, 0, "Got no mozvisualresize events in content"); visScrEvtContent.unregister(); is(visScrEvtContent.count, 0, "Got no mozvisualscroll events in content");
--- a/layout/base/PresShell.cpp +++ b/layout/base/PresShell.cpp @@ -182,16 +182,17 @@ #include "mozilla/ServoStyleSet.h" #include "mozilla/StyleSheet.h" #include "mozilla/StyleSheetInlines.h" #include "mozilla/dom/ImageTracker.h" #include "nsIDocShellTreeOwner.h" #include "nsBindingManager.h" #include "nsClassHashtable.h" #include "nsHashKeys.h" +#include "VisualViewport.h" #ifdef MOZ_TASK_TRACER #include "GeckoTaskTracer.h" using namespace mozilla::tasktracer; #endif #define ANCHOR_SCROLL_FLAGS \ (nsIPresShell::SCROLL_OVERFLOW_HIDDEN | nsIPresShell::SCROLL_NO_PARENT_FRAMES) @@ -5168,16 +5169,19 @@ nsresult PresShell::SetResolutionAndScal state.mResolution = Some(aResolution); SetRenderingState(state); if (mMobileViewportManager) { mMobileViewportManager->ResolutionUpdated(); } if (aOrigin != nsGkAtoms::apz) { mResolutionUpdated = true; } + if (auto* window = nsGlobalWindowInner::Cast(mDocument->GetInnerWindow())) { + window->VisualViewport()->PostResizeEvent(); + } return NS_OK; } float PresShell::GetCumulativeResolution() { float resolution = GetResolution(); nsPresContext* parentCtx = GetPresContext()->GetParentPresContext(); if (parentCtx) { @@ -10026,16 +10030,30 @@ void nsIPresShell::SetVisualViewportSize mVisualViewportSize.width = aWidth; mVisualViewportSize.height = aHeight; if (nsIScrollableFrame* rootScrollFrame = GetRootScrollFrameAsScrollable()) { rootScrollFrame->MarkScrollbarsDirtyForReflow(); } MarkFixedFramesForReflow(nsIPresShell::eResize); + + if (auto* window = nsGlobalWindowInner::Cast(mDocument->GetInnerWindow())) { + window->VisualViewport()->PostResizeEvent(); + } + } +} + +void nsIPresShell::SetVisualViewportOffset(const nsPoint& aScrollOffset) { + if (mVisualViewportOffset != aScrollOffset) { + mVisualViewportOffset = aScrollOffset; + + if (auto* window = nsGlobalWindowInner::Cast(mDocument->GetInnerWindow())) { + window->VisualViewport()->PostScrollEvent(); + } } } nsPoint nsIPresShell::GetVisualViewportOffsetRelativeToLayoutViewport() const { nsPoint result; if (nsIScrollableFrame* sf = GetRootScrollFrameAsScrollable()) { result = GetVisualViewportOffset() - sf->GetScrollPosition(); }
--- a/layout/base/nsIPresShell.h +++ b/layout/base/nsIPresShell.h @@ -1642,19 +1642,17 @@ class nsIPresShell : public nsStubDocume void SetVisualViewportSize(nscoord aWidth, nscoord aHeight); bool IsVisualViewportSizeSet() { return mVisualViewportSizeSet; } nsSize GetVisualViewportSize() { NS_ASSERTION(mVisualViewportSizeSet, "asking for visual viewport size when its not set?"); return mVisualViewportSize; } - void SetVisualViewportOffset(const nsPoint& aScrollOffset) { - mVisualViewportOffset = aScrollOffset; - } + void SetVisualViewportOffset(const nsPoint& aScrollOffset); nsPoint GetVisualViewportOffset() const { return mVisualViewportOffset; } nsPoint GetVisualViewportOffsetRelativeToLayoutViewport() const; virtual void WindowSizeMoveDone() = 0; virtual void SysColorChanged() = 0; virtual void ThemeChanged() = 0;
--- a/layout/base/nsRefreshDriver.cpp +++ b/layout/base/nsRefreshDriver.cpp @@ -1139,16 +1139,33 @@ void nsRefreshDriver::AddTimerAdjustment } void nsRefreshDriver::RemoveTimerAdjustmentObserver( nsATimerAdjustmentObserver* aObserver) { MOZ_ASSERT(mTimerAdjustmentObservers.Contains(aObserver)); mTimerAdjustmentObservers.RemoveElement(aObserver); } +void nsRefreshDriver::PostVisualViewportResizeEvent( + VVPResizeEvent* aResizeEvent) { + mVisualViewportResizeEvents.AppendElement(aResizeEvent); + EnsureTimerStarted(); +} + +void nsRefreshDriver::DispatchVisualViewportResizeEvents() { + // We're taking a hint from scroll events and only dispatch the current set + // of queued resize events. If additional events are posted in response to + // the current events being dispatched, we'll dispatch them on the next tick. + VisualViewportResizeEventArray events; + events.SwapElements(mVisualViewportResizeEvents); + for (auto& event : events) { + event->Run(); + } +} + void nsRefreshDriver::PostScrollEvent(mozilla::Runnable* aScrollEvent, bool aDelayed) { if (aDelayed) { mDelayedScrollEvents.AppendElement(aScrollEvent); } else { mScrollEvents.AppendElement(aScrollEvent); EnsureTimerStarted(); } } @@ -1160,16 +1177,34 @@ void nsRefreshDriver::DispatchScrollEven // first. (Newly posted scroll events will be dispatched on the next tick.) ScrollEventArray events; events.SwapElements(mScrollEvents); for (auto& event : events) { event->Run(); } } +void nsRefreshDriver::PostVisualViewportScrollEvent( + VVPScrollEvent* aScrollEvent) { + mVisualViewportScrollEvents.AppendElement(aScrollEvent); + EnsureTimerStarted(); +} + +void nsRefreshDriver::DispatchVisualViewportScrollEvents() { + // Scroll events are one-shot, so after running them we can drop them. + // However, dispatching a scroll event can potentially cause more scroll + // events to be posted, so we move the initial set into a temporary array + // first. (Newly posted scroll events will be dispatched on the next tick.) + VisualViewportScrollEventArray events; + events.SwapElements(mVisualViewportScrollEvents); + for (auto& event : events) { + event->Run(); + } +} + void nsRefreshDriver::AddPostRefreshObserver( nsAPostRefreshObserver* aObserver) { mPostRefreshObservers.AppendElement(aObserver); } void nsRefreshDriver::RemovePostRefreshObserver( nsAPostRefreshObserver* aObserver) { mPostRefreshObservers.RemoveElement(aObserver); @@ -1729,16 +1764,17 @@ void nsRefreshDriver::Tick(VsyncId aId, } // Make sure to not process observers which might have been removed // during previous iterations. if (!mResizeEventFlushObservers.RemoveElement(shell)) { continue; } shell->FireResizeEvent(); } + DispatchVisualViewportResizeEvents(); /* * The timer holds a reference to |this| while calling |Notify|. * However, implementations of |WillRefresh| are permitted to destroy * the pres context, which will cause our |mPresContext| to become * null. If this happens, we must stop notifying observers. */ for (uint32_t i = 0; i < ArrayLength(mObservers); ++i) { @@ -1752,16 +1788,17 @@ void nsRefreshDriver::Tick(VsyncId aId, return; } } if (i == 1) { // This is the FlushType::Style case. DispatchScrollEvents(); + DispatchVisualViewportScrollEvents(); DispatchAnimationEvents(); RunFullscreenSteps(); RunFrameRequestCallbacks(aNowTime); if (mPresContext && mPresContext->GetPresShell()) { AutoTArray<nsIPresShell*, 16> observers; observers.AppendElements(mStyleFlushObservers); for (uint32_t j = observers.Length();
--- a/layout/base/nsRefreshDriver.h +++ b/layout/base/nsRefreshDriver.h @@ -19,16 +19,17 @@ #include "mozilla/WeakPtr.h" #include "nsTObserverArray.h" #include "nsTArray.h" #include "nsTHashtable.h" #include "nsClassHashtable.h" #include "nsHashKeys.h" #include "mozilla/Attributes.h" #include "mozilla/Maybe.h" +#include "mozilla/dom/VisualViewport.h" #include "mozilla/layers/TransactionIdAllocator.h" #include "mozilla/VsyncDispatcher.h" class nsPresContext; class nsIPresShell; class nsIDocument; class imgIRequest; class nsINode; @@ -87,16 +88,20 @@ class nsATimerAdjustmentObserver { class nsAPostRefreshObserver { public: virtual void DidRefresh() = 0; }; class nsRefreshDriver final : public mozilla::layers::TransactionIdAllocator, public nsARefreshObserver { using TransactionId = mozilla::layers::TransactionId; + using VVPResizeEvent = + mozilla::dom::VisualViewport::VisualViewportResizeEvent; + using VVPScrollEvent = + mozilla::dom::VisualViewport::VisualViewportScrollEvent; public: explicit nsRefreshDriver(nsPresContext* aPresContext); ~nsRefreshDriver(); /** * Methods for testing, exposed via nsIDOMWindowUtils. See * nsIDOMWindowUtils.advanceTimeAndRefresh for description. @@ -141,19 +146,25 @@ class nsRefreshDriver final : public moz mozilla::FlushType aFlushType); /** * Add / remove an observer wants to know the time when the refresh driver * updated the most recent refresh time due to its active timer changes. */ void AddTimerAdjustmentObserver(nsATimerAdjustmentObserver* aObserver); void RemoveTimerAdjustmentObserver(nsATimerAdjustmentObserver* aObserver); + void PostVisualViewportResizeEvent(VVPResizeEvent* aResizeEvent); + void DispatchVisualViewportResizeEvents(); + void PostScrollEvent(mozilla::Runnable* aScrollEvent, bool aDelayed = false); void DispatchScrollEvents(); + void PostVisualViewportScrollEvent(VVPScrollEvent* aScrollEvent); + void DispatchVisualViewportScrollEvents(); + /** * Add an observer that will be called after each refresh. The caller * must remove the observer before it is deleted. This does not trigger * refresh driver ticks. */ void AddPostRefreshObserver(nsAPostRefreshObserver* aObserver); void RemovePostRefreshObserver(nsAPostRefreshObserver* aObserver); @@ -406,17 +417,19 @@ class nsRefreshDriver final : public moz void NotifyDOMContentLoaded(); // Schedule a refresh so that any delayed events will run soon. void RunDelayedEventsSoon(); private: typedef nsTObserverArray<nsARefreshObserver*> ObserverArray; + typedef nsTArray<RefPtr<VVPResizeEvent>> VisualViewportResizeEventArray; typedef nsTArray<RefPtr<mozilla::Runnable>> ScrollEventArray; + typedef nsTArray<RefPtr<VVPScrollEvent>> VisualViewportScrollEventArray; typedef nsTHashtable<nsISupportsHashKey> RequestTable; struct ImageStartData { ImageStartData() {} mozilla::Maybe<mozilla::TimeStamp> mStartTime; RequestTable mEntries; }; typedef nsClassHashtable<nsUint32HashKey, ImageStartData> ImageStartTable; @@ -528,17 +541,19 @@ class nsRefreshDriver final : public moz // is used to determine whether or not to stop the timer, or restore it when // thawing the refresh driver. On the other hand these observers are intended // to be called when the timer is re-started and should not influence its // starting or stopping. nsTObserverArray<nsATimerAdjustmentObserver*> mTimerAdjustmentObservers; RequestTable mRequests; ImageStartTable mStartTable; AutoTArray<nsCOMPtr<nsIRunnable>, 16> mEarlyRunners; + VisualViewportResizeEventArray mVisualViewportResizeEvents; ScrollEventArray mScrollEvents; + VisualViewportScrollEventArray mVisualViewportScrollEvents; // Scroll events on documents that might have events suppressed. ScrollEventArray mDelayedScrollEvents; AutoTArray<nsIPresShell*, 16> mResizeEventFlushObservers; AutoTArray<nsIPresShell*, 16> mDelayedResizeEventFlushObservers; AutoTArray<nsIPresShell*, 16> mStyleFlushObservers; AutoTArray<nsIPresShell*, 16> mLayoutFlushObservers;