Bug 1331932 - Suppress window animations while we're restoring window dimension. r=mikedeboer,jimm,mstange
authorBeekill95 <nnn_bikiu0707@yahoo.com>
Thu, 22 Jun 2017 14:27:05 +0700
changeset 416782 afad368bc897f3463e9d654ae7829cd17fd8ce3a
parent 416781 f7a83b03c3dabcaf95dbbfe0180ee60263a6f5a4
child 416783 3a8ffdd054153b66b5fa519214ac2429743b803d
push id7566
push usermtabara@mozilla.com
push dateWed, 02 Aug 2017 08:25:16 +0000
treeherdermozilla-beta@86913f512c3c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmikedeboer, jimm, mstange
bugs1331932
milestone56.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 1331932 - Suppress window animations while we're restoring window dimension. r=mikedeboer,jimm,mstange
browser/components/sessionstore/SessionStore.jsm
dom/base/nsDOMWindowUtils.cpp
dom/interfaces/base/nsIDOMWindowUtils.idl
widget/cocoa/nsCocoaWindow.h
widget/cocoa/nsCocoaWindow.mm
widget/nsIWidget.h
widget/windows/nsWindow.cpp
widget/windows/nsWindow.h
--- a/browser/components/sessionstore/SessionStore.jsm
+++ b/browser/components/sessionstore/SessionStore.jsm
@@ -3893,16 +3893,18 @@ var SessionStoreInternal = {
    * @param aSidebar
    *        Sidebar command
    */
   restoreDimensions: function ssi_restoreDimensions(aWindow, aWidth, aHeight, aLeft, aTop, aSizeMode, aSidebar) {
     var win = aWindow;
     var _this = this;
     function win_(aName) { return _this._getWindowDimension(win, aName); }
 
+    const dwu = win.QueryInterface(Ci.nsIInterfaceRequestor)
+                   .getInterface(Ci.nsIDOMWindowUtils);
     // find available space on the screen where this window is being placed
     let screen = gScreenManager.screenForRect(aLeft, aTop, aWidth, aHeight);
     if (screen) {
       let screenLeft = {}, screenTop = {}, screenWidth = {}, screenHeight = {};
       screen.GetAvailRectDisplayPix(screenLeft, screenTop, screenWidth, screenHeight);
       // screenX/Y are based on the origin of the screen's desktop-pixel coordinate space
       let screenLeftCss = screenLeft.value;
       let screenTopCss = screenTop.value;
@@ -3940,48 +3942,57 @@ var SessionStoreInternal = {
         bottom = screenBottomCss;
         if (aTop > screenTopCss) {
           aTop = Math.max(bottom - aHeight, screenTopCss);
         }
       }
       aHeight = bottom - aTop;
     }
 
-    // only modify those aspects which aren't correct yet
-    if (!isNaN(aLeft) && !isNaN(aTop) && (aLeft != win_("screenX") || aTop != win_("screenY"))) {
-      aWindow.moveTo(aLeft, aTop);
-    }
-    if (aWidth && aHeight && (aWidth != win_("width") || aHeight != win_("height")) && !gResistFingerprintingEnabled) {
-      // Don't resize the window if it's currently maximized and we would
-      // maximize it again shortly after.
-      if (aSizeMode != "maximized" || win_("sizemode") != "maximized") {
-        aWindow.resizeTo(aWidth, aHeight);
+    // Suppress animations.
+    dwu.suppressAnimation(true);
+
+    // We want to make sure users will get their animations back in case an exception is thrown.
+    try {
+      // only modify those aspects which aren't correct yet
+      if (!isNaN(aLeft) && !isNaN(aTop) && (aLeft != win_("screenX") || aTop != win_("screenY"))) {
+        aWindow.moveTo(aLeft, aTop);
+      }
+      if (aWidth && aHeight && (aWidth != win_("width") || aHeight != win_("height")) && !gResistFingerprintingEnabled) {
+        // Don't resize the window if it's currently maximized and we would
+        // maximize it again shortly after.
+        if (aSizeMode != "maximized" || win_("sizemode") != "maximized") {
+          aWindow.resizeTo(aWidth, aHeight);
+        }
       }
-    }
-    if (aSizeMode && win_("sizemode") != aSizeMode && !gResistFingerprintingEnabled) {
-      switch (aSizeMode) {
-      case "maximized":
-        aWindow.maximize();
-        break;
-      case "minimized":
-        aWindow.minimize();
-        break;
-      case "normal":
-        aWindow.restore();
-        break;
+      if (aSizeMode && win_("sizemode") != aSizeMode && !gResistFingerprintingEnabled) {
+        switch (aSizeMode) {
+        case "maximized":
+          aWindow.maximize();
+          break;
+        case "minimized":
+          aWindow.minimize();
+          break;
+        case "normal":
+          aWindow.restore();
+          break;
+        }
       }
-    }
-    var sidebar = aWindow.document.getElementById("sidebar-box");
-    if (sidebar.getAttribute("sidebarcommand") != aSidebar) {
-      aWindow.SidebarUI.show(aSidebar);
-    }
-    // since resizing/moving a window brings it to the foreground,
-    // we might want to re-focus the last focused window
-    if (this.windowToFocus) {
-      this.windowToFocus.focus();
+      var sidebar = aWindow.document.getElementById("sidebar-box");
+      if (sidebar.getAttribute("sidebarcommand") != aSidebar) {
+        aWindow.SidebarUI.show(aSidebar);
+      }
+      // since resizing/moving a window brings it to the foreground,
+      // we might want to re-focus the last focused window
+      if (this.windowToFocus) {
+        this.windowToFocus.focus();
+      }
+    } finally {
+      // Enable animations.
+      dwu.suppressAnimation(false);
     }
   },
 
   /* ........ Disk Access .............. */
 
   /**
    * Save the current session state to disk, after a delay.
    *
--- a/dom/base/nsDOMWindowUtils.cpp
+++ b/dom/base/nsDOMWindowUtils.cpp
@@ -1276,16 +1276,26 @@ nsDOMWindowUtils::SendNativeTouchTap(int
       &nsIWidget::SynthesizeNativeTouchTap,
       LayoutDeviceIntPoint(aScreenX, aScreenY),
       aLongTap,
       aObserver));
   return NS_OK;
 }
 
 NS_IMETHODIMP
+nsDOMWindowUtils::SuppressAnimation(bool aSuppress)
+{
+  nsIWidget* widget = GetWidget();
+  if (widget) {
+    widget->SuppressAnimation(aSuppress);
+  }
+  return NS_OK;
+}
+
+NS_IMETHODIMP
 nsDOMWindowUtils::ClearNativeTouchSequence(nsIObserver* aObserver)
 {
   nsCOMPtr<nsIWidget> widget = GetWidget();
   if (!widget) {
     return NS_ERROR_FAILURE;
   }
 
   NS_DispatchToMainThread(
--- a/dom/interfaces/base/nsIDOMWindowUtils.idl
+++ b/dom/interfaces/base/nsIDOMWindowUtils.idl
@@ -633,16 +633,22 @@ interface nsIDOMWindowUtils : nsISupport
    * See nsIWidget::SynthesizeNativeMouseMove and sendNativeMouseEvent
    */
   void sendNativeMouseMove(in long aScreenX,
                            in long aScreenY,
                            in nsIDOMElement aElement,
                            [optional] in nsIObserver aObserver);
 
   /**
+   * Suppress animations that are applied to a window by OS when
+   * resizing, moving, changing size mode, ...
+   */
+  void suppressAnimation(in boolean aSuppress);
+
+  /**
    * The values for sendNativeMouseScrollEvent's aAdditionalFlags.
    */
 
   /**
    * If MOUSESCROLL_PREFER_WIDGET_AT_POINT is set, widget will dispatch
    * the event to a widget which is under the cursor.  Otherwise, dispatch to
    * a default target on the platform.  E.g., on Windows, it's focused window.
    */
--- a/widget/cocoa/nsCocoaWindow.h
+++ b/widget/cocoa/nsCocoaWindow.h
@@ -248,16 +248,17 @@ public:
 
     virtual void* GetNativeData(uint32_t aDataType) override;
 
     virtual void            ConstrainPosition(bool aAllowSlop,
                                               int32_t *aX, int32_t *aY) override;
     virtual void            SetSizeConstraints(const SizeConstraints& aConstraints) override;
     virtual void            Move(double aX, double aY) override;
     virtual void            SetSizeMode(nsSizeMode aMode) override;
+    virtual void            SuppressAnimation(bool aSuppress) override;
     virtual void            HideWindowChrome(bool aShouldHide) override;
 
     void EnteredFullScreen(bool aFullScreen, bool aNativeMode = true);
     virtual bool PrepareForFullscreenTransition(nsISupports** aData) override;
     virtual void PerformFullscreenTransition(FullscreenTransitionStage aStage,
                                              uint16_t aDuration,
                                              nsISupports* aData,
                                              nsIRunnable* aCallback) override;
@@ -415,11 +416,12 @@ protected:
   bool                 mIsAnimationSuppressed;
 
   bool                 mInReportMoveEvent; // true if in a call to ReportMoveEvent().
   bool                 mInResize; // true if in a call to DoResize().
   bool                 mWindowTransformIsIdentity;
 
   int32_t              mNumModalDescendents;
   InputContext         mInputContext;
+  NSWindowAnimationBehavior mWindowAnimationBehavior;
 };
 
 #endif // nsCocoaWindow_h_
--- a/widget/cocoa/nsCocoaWindow.mm
+++ b/widget/cocoa/nsCocoaWindow.mm
@@ -890,16 +890,17 @@ nsCocoaWindow::Show(bool bState)
               NS_NOTREACHED("unexpected mAnimationType value");
               // fall through
             case nsIWidget::eGenericWindowAnimation:
               behavior = NSWindowAnimationBehaviorDefault;
               break;
           }
         }
         [mWindow setAnimationBehavior:behavior];
+        mWindowAnimationBehavior = behavior;
       }
       [mWindow makeKeyAndOrderFront:nil];
       NS_OBJC_END_TRY_ABORT_BLOCK;
       SendSetZLevelEvent();
     }
   }
   else {
     // roll up any popups if a top-level window is going away
@@ -1295,16 +1296,28 @@ nsCocoaWindow::SetSizeMode(nsSizeMode aM
   else if (aMode == nsSizeMode_Fullscreen) {
     if (!mInFullScreenMode)
       MakeFullScreen(true);
   }
 
   NS_OBJC_END_TRY_ABORT_BLOCK;
 }
 
+void
+nsCocoaWindow::SuppressAnimation(bool aSuppress)
+{
+  if ([mWindow respondsToSelector:@selector(setAnimationBehavior:)]) {
+    if (aSuppress) {
+      [mWindow setAnimationBehavior:NSWindowAnimationBehaviorNone];
+    } else {
+      [mWindow setAnimationBehavior:mWindowAnimationBehavior];
+    }
+  }
+}
+
 // This has to preserve the window's frame bounds.
 // This method requires (as does the Windows impl.) that you call Resize shortly
 // after calling HideWindowChrome. See bug 498835 for fixing this.
 void
 nsCocoaWindow::HideWindowChrome(bool aShouldHide)
 {
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
 
--- a/widget/nsIWidget.h
+++ b/widget/nsIWidget.h
@@ -830,16 +830,21 @@ class nsIWidget : public nsISupports
 
     /**
      * Minimize, maximize or normalize the window size.
      * Takes a value from nsSizeMode (see nsIWidgetListener.h)
      */
     virtual void SetSizeMode(nsSizeMode aMode) = 0;
 
     /**
+     * Suppress animations that are applied to a window by OS.
+     */
+    virtual void SuppressAnimation(bool aSuppress) {}
+
+    /**
      * Return size mode (minimized, maximized, normalized).
      * Returns a value from nsSizeMode (see nsIWidgetListener.h)
      */
     virtual nsSizeMode SizeMode() = 0;
 
     /**
      * Ask wether the widget is fully occluded
      */
--- a/widget/windows/nsWindow.cpp
+++ b/widget/windows/nsWindow.cpp
@@ -855,19 +855,17 @@ nsWindow::Create(nsIWidget* aParent,
   }
 
   if (mIsRTL) {
     DWORD dwAttribute = TRUE;    
     DwmSetWindowAttribute(mWnd, DWMWA_NONCLIENT_RTL_LAYOUT, &dwAttribute, sizeof dwAttribute);
   }
 
   if (mOpeningAnimationSuppressed) {
-    DWORD dwAttribute = TRUE;
-    DwmSetWindowAttribute(mWnd, DWMWA_TRANSITIONS_FORCEDISABLED,
-                          &dwAttribute, sizeof dwAttribute);
+    SuppressAnimation(true);
   }
 
   if (!IsPlugin() &&
       mWindowType != eWindowType_invisible &&
       MouseScrollHandler::Device::IsFakeScrollableWindowNeeded()) {
     // Ugly Thinkpad Driver Hack (Bugs 507222 and 594977)
     //
     // We create two zero-sized windows as descendants of the top-level window,
@@ -1651,19 +1649,17 @@ nsWindow::Show(bool bState)
     Invalidate();
     if (syncInvalidate && !mInDtor && !mOnDestroyCalled) {
       ::UpdateWindow(mWnd);
     }
   }
 #endif
 
   if (mOpeningAnimationSuppressed) {
-    DWORD dwAttribute = FALSE;
-    DwmSetWindowAttribute(mWnd, DWMWA_TRANSITIONS_FORCEDISABLED,
-                          &dwAttribute, sizeof dwAttribute);
+    SuppressAnimation(false);
   }
 }
 
 /**************************************************************
  *
  * SECTION: nsIWidget::IsVisible
  *
  * Returns the visibility state.
@@ -2135,16 +2131,24 @@ nsWindow::SetSizeMode(nsSizeMode aMode)
       ::ShowWindow(mWnd, mode);
     }
     // we activate here to ensure that the right child window is focused
     if (mode == SW_MAXIMIZE || mode == SW_SHOW)
       DispatchFocusToTopLevelWindow(true);
   }
 }
 
+void
+nsWindow::SuppressAnimation(bool aSuppress)
+{
+  DWORD dwAttribute = aSuppress ? TRUE : FALSE;
+  DwmSetWindowAttribute(mWnd, DWMWA_TRANSITIONS_FORCEDISABLED,
+                        &dwAttribute, sizeof dwAttribute);
+}
+
 // Constrain a potential move to fit onscreen
 // Position (aX, aY) is specified in Windows screen (logical) pixels,
 // except when using per-monitor DPI, in which case it's device pixels.
 void
 nsWindow::ConstrainPosition(bool aAllowSlop, int32_t *aX, int32_t *aY)
 {
   if (!mIsTopWidgetWindow) // only a problem for top-level windows
     return;
--- a/widget/windows/nsWindow.h
+++ b/widget/windows/nsWindow.h
@@ -128,16 +128,17 @@ public:
   virtual void            Resize(double aWidth, double aHeight, bool aRepaint) override;
   virtual void            Resize(double aX, double aY, double aWidth, double aHeight, bool aRepaint) override;
   virtual MOZ_MUST_USE nsresult
                           BeginResizeDrag(mozilla::WidgetGUIEvent* aEvent,
                                           int32_t aHorizontal,
                                           int32_t aVertical) override;
   virtual void            PlaceBehind(nsTopLevelWidgetZPlacement aPlacement, nsIWidget *aWidget, bool aActivate) override;
   virtual void            SetSizeMode(nsSizeMode aMode) override;
+  virtual void            SuppressAnimation(bool aSuppress) override;
   virtual void            Enable(bool aState) override;
   virtual bool            IsEnabled() const override;
   virtual nsresult        SetFocus(bool aRaise) override;
   virtual LayoutDeviceIntRect GetBounds() override;
   virtual LayoutDeviceIntRect GetScreenBounds() override;
   virtual MOZ_MUST_USE nsresult GetRestoredBounds(LayoutDeviceIntRect& aRect) override;
   virtual LayoutDeviceIntRect GetClientBounds() override;
   virtual LayoutDeviceIntPoint GetClientOffset() override;