diff --git a/browser/base/content/tabbrowser.xml b/browser/base/content/tabbrowser.xml index ecc852166570..fde36c6ee001 100644 --- a/browser/base/content/tabbrowser.xml +++ b/browser/base/content/tabbrowser.xml @@ -3077,6 +3077,11 @@ // Map from tabs to STATE_* (below). tabState: new Map(), + // Keep an exact list of content processes (tabParent) in which + // we're actively suppressing the display port. This gives a robust + // way to make sure we don't forget to un-suppress. + activeSuppressDisplayport: new Set(), + // Set of tabs that might be visible right now. We maintain // this set because we can't be sure when a tab is actually // drawn. A tab is added to this set when we ask to make it @@ -3140,6 +3145,11 @@ window.removeEventListener("TabRemotenessChange", this); this.tabbrowser._switcher = null; + + this.activeSuppressDisplayport.forEach(function(tabParent) { + tabParent.suppressDisplayport(false); + }); + this.activeSuppressDisplayport.clear(); }, finish: function() { @@ -3438,6 +3448,13 @@ this.requestedTab = tab; + let browser = this.requestedTab.linkedBrowser; + let fl = browser.QueryInterface(Ci.nsIFrameLoaderOwner).frameLoader; + if (fl && fl.tabParent && !this.activeSuppressDisplayport.has(fl.tabParent)) { + fl.tabParent.suppressDisplayport(true); + this.activeSuppressDisplayport.add(fl.tabParent); + } + this.preActions(); clearTimeout(this.unloadTimer); diff --git a/dom/interfaces/base/nsITabParent.idl b/dom/interfaces/base/nsITabParent.idl index c09fba5d5524..a53a46f8850f 100644 --- a/dom/interfaces/base/nsITabParent.idl +++ b/dom/interfaces/base/nsITabParent.idl @@ -5,7 +5,7 @@ #include "domstubs.idl" -[scriptable, uuid(CE6F563B-BD77-4EF2-9D7C-A94C587353E4)] +[scriptable, uuid(531b902b-b551-4faa-9814-1a73e8299ac4)] interface nsITabParent : nsISupports { void injectTouchEvent(in AString aType, @@ -25,6 +25,14 @@ interface nsITabParent : nsISupports void setIsDocShellActive(in bool aIsActive); + /** + * During interactions where painting performance + * is more important than scrolling, we may temporarily + * suppress the displayport. Each enable called must be matched + * with a disable call. + */ + void suppressDisplayport(in bool aEnabled); + readonly attribute uint64_t tabId; /** diff --git a/dom/ipc/PBrowser.ipdl b/dom/ipc/PBrowser.ipdl index 2ba56babdc3e..70017a953783 100644 --- a/dom/ipc/PBrowser.ipdl +++ b/dom/ipc/PBrowser.ipdl @@ -709,6 +709,17 @@ child: */ SetIsDocShellActive(bool aIsActive); + /** + * Notify the child that it shouldn't paint the offscreen displayport. + * This is useful to speed up interactive operations over async + * scrolling performance like resize, tabswitch, pageload. + * + * Each enable call must be matched with a disable call. The child + * will remain in the suppress mode as long as there's + * a single unmatched call. + */ + async SuppressDisplayport(bool aEnabled); + /** * Navigate by key (Tab/Shift+Tab/F6/Shift+f6). */ diff --git a/dom/ipc/TabChild.cpp b/dom/ipc/TabChild.cpp index c20fa1bec7da..5c94d7c9713d 100644 --- a/dom/ipc/TabChild.cpp +++ b/dom/ipc/TabChild.cpp @@ -554,6 +554,7 @@ TabChild::TabChild(nsIContentChild* aManager, , mRemoteFrame(nullptr) , mManager(aManager) , mChromeFlags(aChromeFlags) + , mActiveSuppressDisplayport(0) , mLayersId(0) , mActivePointerId(-1) , mAppPackageFileDescriptorRecved(false) @@ -1693,6 +1694,20 @@ TabChild::RecvUpdateFrame(const FrameMetrics& aFrameMetrics) return TabChildBase::UpdateFrameHandler(aFrameMetrics); } +bool +TabChild::RecvSuppressDisplayport(const bool& aEnabled) +{ + if (aEnabled) { + mActiveSuppressDisplayport++; + } else { + mActiveSuppressDisplayport--; + } + + MOZ_ASSERT(mActiveSuppressDisplayport >= 0); + APZCCallbackHelper::SuppressDisplayport(aEnabled); + return true; +} + bool TabChild::RecvRequestFlingSnap(const FrameMetrics::ViewID& aScrollId, const mozilla::CSSPoint& aDestination) @@ -2413,6 +2428,11 @@ TabChild::RecvDestroy() MOZ_ASSERT(mDestroyed == false); mDestroyed = true; + while (mActiveSuppressDisplayport > 0) { + APZCCallbackHelper::SuppressDisplayport(false); + mActiveSuppressDisplayport--; + } + if (mTabChildGlobal) { // Message handlers are called from the event loop, so it better be safe to // run script. diff --git a/dom/ipc/TabChild.h b/dom/ipc/TabChild.h index 0520562df94a..00d85ee4a393 100644 --- a/dom/ipc/TabChild.h +++ b/dom/ipc/TabChild.h @@ -512,6 +512,8 @@ protected: virtual bool RecvRequestNotifyAfterRemotePaint() override; + virtual bool RecvSuppressDisplayport(const bool& aEnabled) override; + virtual bool RecvParentActivated(const bool& aActivated) override; virtual bool RecvStopIMEStateManagement() override; @@ -608,6 +610,7 @@ private: RenderFrameChild* mRemoteFrame; nsRefPtr mManager; uint32_t mChromeFlags; + int32_t mActiveSuppressDisplayport; uint64_t mLayersId; CSSRect mUnscaledOuterRect; // When we're tracking a possible tap gesture, this is the "down" diff --git a/dom/ipc/TabParent.cpp b/dom/ipc/TabParent.cpp index e79b3e1d6420..3265ec35b83f 100644 --- a/dom/ipc/TabParent.cpp +++ b/dom/ipc/TabParent.cpp @@ -287,6 +287,7 @@ TabParent::TabParent(nsIContentParent* aManager, , mCursor(nsCursor(-1)) , mTabSetsCursor(false) , mHasContentOpener(false) + , mActiveSupressDisplayportCount(0) { MOZ_ASSERT(aManager); } @@ -2955,6 +2956,25 @@ TabParent::SetIsDocShellActive(bool isActive) return NS_OK; } +NS_IMETHODIMP +TabParent::SuppressDisplayport(bool aEnabled) +{ + if (IsDestroyed()) { + return NS_OK; + } + + if (aEnabled) { + mActiveSupressDisplayportCount++; + } else { + mActiveSupressDisplayportCount--; + } + + MOZ_ASSERT(mActiveSupressDisplayportCount >= 0); + + unused << SendSuppressDisplayport(aEnabled); + return NS_OK; +} + NS_IMETHODIMP TabParent::GetTabId(uint64_t* aId) { diff --git a/dom/ipc/TabParent.h b/dom/ipc/TabParent.h index 3c53f0251d1d..f0a04b0d8ed1 100644 --- a/dom/ipc/TabParent.h +++ b/dom/ipc/TabParent.h @@ -609,6 +609,8 @@ private: nsRefPtr mPresShellWithRefreshListener; bool mHasContentOpener; + + DebugOnly mActiveSupressDisplayportCount; private: // This is used when APZ needs to find the TabParent associated with a layer // to dispatch events. diff --git a/gfx/layers/apz/util/APZCCallbackHelper.cpp b/gfx/layers/apz/util/APZCCallbackHelper.cpp index da4377ff25fb..dbb4e81d4afb 100644 --- a/gfx/layers/apz/util/APZCCallbackHelper.cpp +++ b/gfx/layers/apz/util/APZCCallbackHelper.cpp @@ -810,6 +810,27 @@ APZCCallbackHelper::NotifyFlushComplete() observerService->NotifyObservers(nullptr, "apz-repaints-flushed", nullptr); } +static int32_t sActiveSuppressDisplayport = 0; + +void +APZCCallbackHelper::SuppressDisplayport(const bool& aEnabled) +{ + if (aEnabled) { + sActiveSuppressDisplayport++; + } else { + sActiveSuppressDisplayport--; + } + + MOZ_ASSERT(sActiveSuppressDisplayport >= 0); +} + +bool +APZCCallbackHelper::IsDisplayportSuppressed() +{ + return sActiveSuppressDisplayport > 0; +} + + } // namespace layers } // namespace mozilla diff --git a/gfx/layers/apz/util/APZCCallbackHelper.h b/gfx/layers/apz/util/APZCCallbackHelper.h index a2d8f49f668b..a655f08a6e75 100644 --- a/gfx/layers/apz/util/APZCCallbackHelper.h +++ b/gfx/layers/apz/util/APZCCallbackHelper.h @@ -169,6 +169,10 @@ public: /* Notify content that the repaint flush is complete. */ static void NotifyFlushComplete(); + + /* Temporarily ignore the Displayport for better paint performance. */ + static void SuppressDisplayport(const bool& aEnabled); + static bool IsDisplayportSuppressed(); }; } // namespace layers diff --git a/layout/base/nsLayoutUtils.cpp b/layout/base/nsLayoutUtils.cpp index 612115e3e3c2..ae9aa41e5142 100644 --- a/layout/base/nsLayoutUtils.cpp +++ b/layout/base/nsLayoutUtils.cpp @@ -62,6 +62,7 @@ #include "mozilla/dom/HTMLImageElement.h" #include "mozilla/dom/DOMRect.h" #include "mozilla/dom/KeyframeEffect.h" +#include "mozilla/layers/APZCCallbackHelper.h" #include "imgIRequest.h" #include "nsIImageLoadingContent.h" #include "nsCOMPtr.h" @@ -1038,7 +1039,10 @@ GetDisplayPortImpl(nsIContent* aContent, nsRect *aResult, float aMultiplier) "Only one of rectData or marginsData should be set!"); nsRect result; - if (rectData) { + if (APZCCallbackHelper::IsDisplayportSuppressed()) { + DisplayPortMarginsPropertyData noMargins(ScreenMargin(), 1); + result = GetDisplayPortFromMarginsData(aContent, &noMargins, aMultiplier); + } else if (rectData) { result = GetDisplayPortFromRectData(aContent, rectData, aMultiplier); } else { result = GetDisplayPortFromMarginsData(aContent, marginsData, aMultiplier);