Bug 1177155 part 1 - Defer resize reflow and freeze refresh driver during window fullscreen change. r=smaug
authorXidorn Quan <quanxunzhen@gmail.com>
Thu, 27 Aug 2015 23:14:49 +1000
changeset 293964 3e3948004ac1732cfe444d7d3a90a2a93111ed0d
parent 293963 c546668be9b0e45c0266dfbe498ea4e6561de3f1
child 293965 ef599e365a7b598a4286b64b544f3badec43e124
push id962
push userjlund@mozilla.com
push dateFri, 04 Dec 2015 23:28:54 +0000
treeherdermozilla-release@23a2d286e80f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssmaug
bugs1177155
milestone43.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
Bug 1177155 part 1 - Defer resize reflow and freeze refresh driver during window fullscreen change. r=smaug
dom/base/nsGlobalWindow.cpp
dom/base/nsGlobalWindow.h
layout/base/nsIPresShell.h
layout/base/nsPresShell.cpp
view/nsViewManager.cpp
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -6378,17 +6378,18 @@ FullscreenTransitionTask::Run()
       // different direction happen continuously in a short time. We
       // need to ensure the fullscreen state matches our target here,
       // otherwise the widget would change the window state as if we
       // toggle for Fullscreen Mode instead of Fullscreen API.
       NS_WARNING("The fullscreen state of the window does not match");
       mWindow->mFullScreen = mFullscreen;
     }
     // Toggle the fullscreen state on the widget
-    mWidget->MakeFullScreen(mFullscreen, mScreen);
+    mWindow->SetWidgetFullscreen(nsPIDOMWindow::eForFullscreenAPI,
+                                 mFullscreen, mWidget, mScreen);
     // Set observer for the next content paint.
     nsCOMPtr<nsIObserver> observer = new Observer(this);
     nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
     obs->AddObserver(observer, kPaintedTopic, false);
     // There are several edge cases where we may never get the paint
     // notification, including:
     // 1. the window/tab is closed before the next paint;
     // 2. the user has switched to another tab before we get here.
@@ -6457,23 +6458,17 @@ MakeWidgetFullscreen(nsGlobalWindow* aWi
     GetFullscreenTransitionDuration(aFullscreen, &duration);
     if (!duration.IsSuppressed()) {
       performTransition = widget->
         PrepareForFullscreenTransition(getter_AddRefs(transitionData));
     }
   }
   nsCOMPtr<nsIScreen> screen = aHMD ? aHMD->GetScreen() : nullptr;
   if (!performTransition) {
-    if (aReason == nsPIDOMWindow::eForFullscreenMode) {
-      // If we enter fullscreen for fullscreen mode, we want
-      // the native system behavior.
-      widget->MakeFullScreenWithNativeTransition(aFullscreen, screen);
-    } else {
-      widget->MakeFullScreen(aFullscreen, screen);
-    }
+    aWindow->SetWidgetFullscreen(aReason, aFullscreen, widget, screen);
   } else {
     nsCOMPtr<nsIRunnable> task =
       new FullscreenTransitionTask(duration, aWindow, aFullscreen,
                                    widget, screen, transitionData);
     task->Run();
   }
   return true;
 }
@@ -6567,16 +6562,37 @@ nsGlobalWindow::SetFullscreenInternal(Fu
       return NS_OK;
     }
   }
 
   FinishFullscreenChange(aFullScreen);
   return NS_OK;
 }
 
+void
+nsGlobalWindow::SetWidgetFullscreen(FullscreenReason aReason, bool aIsFullscreen,
+                                    nsIWidget* aWidget, nsIScreen* aScreen)
+{
+  MOZ_ASSERT(IsOuterWindow());
+  MOZ_ASSERT(this == GetTop(), "Only topmost window should call this");
+  MOZ_ASSERT(!GetFrameElementInternal(), "Content window should not call this");
+  MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
+
+  if (nsCOMPtr<nsIPresShell> presShell = mDocShell->GetPresShell()) {
+    presShell->SetIsInFullscreenChange(true);
+  }
+  if (aReason == nsPIDOMWindow::eForFullscreenMode) {
+    // If we enter fullscreen for fullscreen mode, we want
+    // the native system behavior.
+    aWidget->MakeFullScreenWithNativeTransition(aIsFullscreen, aScreen);
+  } else {
+    aWidget->MakeFullScreen(aIsFullscreen, aScreen);
+  }
+}
+
 /* virtual */ void
 nsGlobalWindow::FinishFullscreenChange(bool aIsFullscreen)
 {
   MOZ_ASSERT(IsOuterWindow());
 
   if (aIsFullscreen != mFullScreen) {
     NS_WARNING("Failed to toggle fullscreen state of the widget");
     // We failed to make the widget enter fullscreen.
@@ -6599,16 +6615,21 @@ nsGlobalWindow::FinishFullscreenChange(b
   // that the chrome can distinguish between browser fullscreen mode
   // and DOM fullscreen.
   FinishDOMFullscreenChange(mDoc, mFullScreen);
 
   // dispatch a "fullscreen" DOM event so that XUL apps can
   // respond visually if we are kicked into full screen mode
   DispatchCustomEvent(NS_LITERAL_STRING("fullscreen"));
 
+  if (nsCOMPtr<nsIPresShell> presShell = mDocShell->GetPresShell()) {
+    MOZ_ASSERT(presShell->IsInFullscreenChange());
+    presShell->SetIsInFullscreenChange(false);
+  }
+
   if (!mWakeLock && mFullScreen) {
     nsRefPtr<power::PowerManagerService> pmService =
       power::PowerManagerService::GetInstance();
     if (!pmService) {
       return;
     }
 
     ErrorResult rv;
--- a/dom/base/nsGlobalWindow.h
+++ b/dom/base/nsGlobalWindow.h
@@ -493,16 +493,18 @@ public:
   // For accessing protected field mFullScreen
   friend class FullscreenTransitionTask;
 
   // Outer windows only.
   virtual nsresult SetFullscreenInternal(
     FullscreenReason aReason, bool aIsFullscreen,
     mozilla::gfx::VRHMDInfo *aHMD = nullptr) override final;
   virtual void FinishFullscreenChange(bool aIsFullscreen) override final;
+  void SetWidgetFullscreen(FullscreenReason aReason, bool aIsFullscreen,
+                           nsIWidget* aWidget, nsIScreen* aScreen);
   bool FullScreen() const;
 
   // Inner windows only.
   virtual void SetHasGamepadEventListener(bool aHasGamepad = true) override;
 
   // nsIInterfaceRequestor
   NS_DECL_NSIINTERFACEREQUESTOR
 
--- a/layout/base/nsIPresShell.h
+++ b/layout/base/nsIPresShell.h
@@ -1584,16 +1584,19 @@ public:
    * Returns whether the document's style set's rule processor for the
    * specified level of the cascade is shared by multiple style sets.
    *
    * @param aSheetType One of the nsIStyleSheetService.*_SHEET constants.
    */
   nsresult HasRuleProcessorUsedByMultipleStyleSets(uint32_t aSheetType,
                                                    bool* aRetVal);
 
+  bool IsInFullscreenChange() const { return mIsInFullscreenChange; }
+  void SetIsInFullscreenChange(bool aValue);
+
   /**
    * Refresh observer management.
    */
 protected:
   virtual bool AddRefreshObserverExternal(nsARefreshObserver* aObserver,
                                           mozFlushType aFlushType);
   bool AddRefreshObserverInternal(nsARefreshObserver* aObserver,
                                   mozFlushType aFlushType);
@@ -1765,16 +1768,21 @@ protected:
   // style sheets are recorded in mChangedScopeStyleRoots rather than here
   // in mStylesHaveChanged.
   bool                      mStylesHaveChanged : 1;
   bool                      mDidInitialize : 1;
   bool                      mIsDestroying : 1;
   bool                      mIsZombie : 1;
   bool                      mIsReflowing : 1;
 
+  // Indicates that the whole document is performing fullscreen change,
+  // in which case, we need to defer dispatching resize event and freeze
+  // the refresh driver to avoid unnecessary reflow.
+  bool                      mIsInFullscreenChange : 1;
+
   // For all documents we initially lock down painting.
   bool                      mPaintingSuppressed : 1;
 
   // Whether or not form controls should use nsITheme in this shell.
   bool                      mIsThemeSupportDisabled : 1;
 
   bool                      mIsActive : 1;
   bool                      mFrozen : 1;
--- a/layout/base/nsPresShell.cpp
+++ b/layout/base/nsPresShell.cpp
@@ -10951,8 +10951,26 @@ nsIPresShell::HasRuleProcessorUsedByMult
     default:
       MOZ_ASSERT(false, "unexpected aSheetType value");
       return NS_ERROR_ILLEGAL_VALUE;
   }
 
   *aRetVal = mStyleSet->HasRuleProcessorUsedByMultipleStyleSets(type);
   return NS_OK;
 }
+
+void
+nsIPresShell::SetIsInFullscreenChange(bool aValue)
+{
+  if (mIsInFullscreenChange == aValue) {
+    NS_WARNING(aValue ? "Pres shell has been in fullscreen change?" :
+               "Pres shell is not in fullscreen change?");
+    return;
+  }
+  mIsInFullscreenChange = aValue;
+  if (nsRefreshDriver* rd = mPresContext->RefreshDriver()) {
+    if (aValue) {
+      rd->Freeze();
+    } else {
+      rd->Thaw();
+    }
+  }
+}
--- a/view/nsViewManager.cpp
+++ b/view/nsViewManager.cpp
@@ -192,17 +192,18 @@ void nsViewManager::DoSetWindowDimension
       mPresShell->ResizeReflow(aWidth, aHeight);
   }
 }
 
 void
 nsViewManager::SetWindowDimensions(nscoord aWidth, nscoord aHeight)
 {
   if (mRootView) {
-    if (mRootView->IsEffectivelyVisible() && mPresShell && mPresShell->IsVisible()) {
+    if (mRootView->IsEffectivelyVisible() && mPresShell &&
+        mPresShell->IsVisible() && !mPresShell->IsInFullscreenChange()) {
       if (mDelayedResize != nsSize(NSCOORD_NONE, NSCOORD_NONE) &&
           mDelayedResize != nsSize(aWidth, aHeight)) {
         // We have a delayed resize; that now obsolete size may already have
         // been flushed to the PresContext so we need to update the PresContext
         // with the new size because if the new size is exactly the same as the
         // root view's current size then DoSetWindowDimensions will not
         // request a resize reflow (which would correct it). See bug 617076.
         mDelayedResize = nsSize(aWidth, aHeight);