Bug 1373581 - Make transition into and out of native fullscreen smoother on macOS. r=mstange, a=ritu
authorStephen A Pohl <spohl.mozilla.bugs@gmail.com>
Wed, 18 Oct 2017 21:50:47 -0400
changeset 432655 fe950d3f8ad3928a111e0a3e4bacd0d12a50715b
parent 432654 8dbd91b516700e2b13f1c774dbc9325a0c41a764
child 432656 dfee079363e1ba600ae3d15c02b23bc8a9b35900
push id8020
push userryanvm@gmail.com
push dateFri, 20 Oct 2017 21:59:23 +0000
treeherdermozilla-beta@add4a60517c0 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmstange, ritu
bugs1373581
milestone57.0
Bug 1373581 - Make transition into and out of native fullscreen smoother on macOS. r=mstange, a=ritu
browser/base/content/browser-fullScreenAndPointerLock.js
dom/base/nsGlobalWindow.cpp
dom/base/nsGlobalWindow.h
dom/base/nsPIDOMWindow.h
widget/cocoa/nsCocoaWindow.h
widget/cocoa/nsCocoaWindow.mm
widget/nsIWidgetListener.cpp
widget/nsIWidgetListener.h
xpfe/appshell/nsWebShellWindow.cpp
xpfe/appshell/nsWebShellWindow.h
--- a/browser/base/content/browser-fullScreenAndPointerLock.js
+++ b/browser/base/content/browser-fullScreenAndPointerLock.js
@@ -251,16 +251,18 @@ var FullScreen = {
     "DOMFullscreen:NewOrigin",
     "DOMFullscreen:Exit",
     "DOMFullscreen:Painted",
   ],
 
   init() {
     // called when we go into full screen, even if initiated by a web page script
     window.addEventListener("fullscreen", this, true);
+    window.addEventListener("willenterfullscreen", this, true);
+    window.addEventListener("willexitfullscreen", this, true);
     window.addEventListener("MozDOMFullscreen:Entered", this,
                             /* useCapture */ true,
                             /* wantsUntrusted */ false);
     window.addEventListener("MozDOMFullscreen:Exited", this,
                             /* useCapture */ true,
                             /* wantsUntrusted */ false);
     for (let type of this._MESSAGES) {
       window.messageManager.addMessageListener(type, this);
@@ -272,16 +274,24 @@ var FullScreen = {
 
   uninit() {
     for (let type of this._MESSAGES) {
       window.messageManager.removeMessageListener(type, this);
     }
     this.cleanup();
   },
 
+  willToggle(aWillEnterFullscreen) {
+    if (aWillEnterFullscreen) {
+      document.documentElement.setAttribute("inFullscreen", true);
+    } else {
+      document.documentElement.removeAttribute("inFullscreen");
+    }
+  },
+
   toggle() {
     var enterFS = window.fullScreen;
 
     // Toggle the View:FullScreen command, which controls elements like the
     // fullscreen menuitem, and menubars.
     let fullscreenCommand = document.getElementById("View:FullScreen");
     if (enterFS) {
       fullscreenCommand.setAttribute("checked", enterFS);
@@ -343,16 +353,22 @@ var FullScreen = {
   },
 
   exitDomFullScreen() {
     document.exitFullscreen();
   },
 
   handleEvent(event) {
     switch (event.type) {
+      case "willenterfullscreen":
+        this.willToggle(true);
+        break;
+      case "willexitfullscreen":
+        this.willToggle(false);
+        break;
       case "fullscreen":
         this.toggle();
         break;
       case "MozDOMFullscreen:Entered": {
         // The event target is the element which requested the DOM
         // fullscreen. If we were entering DOM fullscreen for a remote
         // browser, the target would be `gBrowser` and the original
         // target would be the browser which was the parameter of
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -7333,16 +7333,26 @@ nsGlobalWindow::SetWidgetFullscreen(Full
     // If we enter fullscreen for fullscreen mode, we want
     // the native system behavior.
     aWidget->MakeFullScreenWithNativeTransition(aIsFullscreen, aScreen) :
     aWidget->MakeFullScreen(aIsFullscreen, aScreen);
   return NS_SUCCEEDED(rv);
 }
 
 /* virtual */ void
+nsGlobalWindow::FullscreenWillChange(bool aIsFullscreen)
+{
+  if (aIsFullscreen) {
+    DispatchCustomEvent(NS_LITERAL_STRING("willenterfullscreen"));
+  } else {
+    DispatchCustomEvent(NS_LITERAL_STRING("willexitfullscreen"));
+  }
+}
+
+/* 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.
     // Stop further changes and restore the state.
--- a/dom/base/nsGlobalWindow.h
+++ b/dom/base/nsGlobalWindow.h
@@ -443,16 +443,17 @@ public:
   void RefreshCompartmentPrincipal();
 
   // For accessing protected field mFullScreen
   friend class FullscreenTransitionTask;
 
   // Outer windows only.
   virtual nsresult SetFullscreenInternal(
     FullscreenReason aReason, bool aIsFullscreen) override final;
+  virtual void FullscreenWillChange(bool aIsFullscreen) override final;
   virtual void FinishFullscreenChange(bool aIsFullscreen) override final;
   bool SetWidgetFullscreen(FullscreenReason aReason, bool aIsFullscreen,
                            nsIWidget* aWidget, nsIScreen* aScreen);
   bool FullScreen() const;
 
   // Inner windows only.
   virtual void SetHasGamepadEventListener(bool aHasGamepad = true) override;
   void NotifyVREventListenerAdded();
--- a/dom/base/nsPIDOMWindow.h
+++ b/dom/base/nsPIDOMWindow.h
@@ -383,16 +383,17 @@ public:
    * Moves the top-level window into fullscreen mode if aIsFullScreen is true,
    * otherwise exits fullscreen.
    *
    * Outer windows only.
    */
   virtual nsresult SetFullscreenInternal(
     FullscreenReason aReason, bool aIsFullscreen) = 0;
 
+  virtual void FullscreenWillChange(bool aIsFullscreen) = 0;
   /**
    * This function should be called when the fullscreen state is flipped.
    * If no widget is involved the fullscreen change, this method is called
    * by SetFullscreenInternal, otherwise, it is called when the widget
    * finishes its change to or from fullscreen.
    *
    * @param aIsFullscreen indicates whether the widget is in fullscreen.
    *
--- a/widget/cocoa/nsCocoaWindow.h
+++ b/widget/cocoa/nsCocoaWindow.h
@@ -245,16 +245,17 @@ public:
     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 WillEnterFullScreen(bool aFullScreen);
     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;
     virtual nsresult MakeFullScreen(
       bool aFullScreen, nsIScreen* aTargetScreen = nullptr) override final;
--- a/widget/cocoa/nsCocoaWindow.mm
+++ b/widget/cocoa/nsCocoaWindow.mm
@@ -1486,16 +1486,23 @@ nsCocoaWindow::PerformFullscreenTransiti
   };
   mFullscreenTransitionAnimation =
     [[NSViewAnimation alloc] initWithViewAnimations:@[dict]];
   [mFullscreenTransitionAnimation setDelegate:delegate];
   [mFullscreenTransitionAnimation setDuration:aDuration / 1000.0];
   [mFullscreenTransitionAnimation startAnimation];
 }
 
+void nsCocoaWindow::WillEnterFullScreen(bool aFullScreen)
+{
+  if (mWidgetListener) {
+    mWidgetListener->FullscreenWillChange(aFullScreen);
+  }
+}
+
 void nsCocoaWindow::EnteredFullScreen(bool aFullScreen, bool aNativeMode)
 {
   mInFullScreenTransition = false;
   bool wasInFullscreen = mInFullScreenMode;
   mInFullScreenMode = aFullScreen;
   if (aNativeMode || mInNativeFullScreenMode) {
     mInNativeFullScreenMode = aFullScreen;
   }
@@ -2637,16 +2644,25 @@ nsCocoaWindow::GetEditCommands(NativeKey
     if (GetBackingScaleFactor(window) != mGeckoWindow->BackingScaleFactor()) {
       mGeckoWindow->BackingScaleFactorChanged();
     }
   }
 
   mGeckoWindow->ReportMoveEvent();
 }
 
+- (void)windowWillEnterFullScreen:(NSNotification *)notification
+{
+  if (!mGeckoWindow) {
+    return;
+  }
+
+  mGeckoWindow->WillEnterFullScreen(true);
+}
+
 // Lion's full screen mode will bypass our internal fullscreen tracking, so
 // we need to catch it when we transition and call our own methods, which in
 // turn will fire "fullscreen" events.
 - (void)windowDidEnterFullScreen:(NSNotification *)notification
 {
   if (!mGeckoWindow) {
     return;
   }
@@ -2672,16 +2688,25 @@ nsCocoaWindow::GetEditCommands(NativeKey
   if ([titlebarView respondsToSelector:@selector(setTransparent:)]) {
     [titlebarView setTransparent:NO];
   }
   if ([titlebarContainerView respondsToSelector:@selector(setTransparent:)]) {
     [titlebarContainerView setTransparent:NO];
   }
 }
 
+- (void)windowWillExitFullScreen:(NSNotification *)notification
+{
+  if (!mGeckoWindow) {
+    return;
+  }
+
+  mGeckoWindow->WillEnterFullScreen(false);
+}
+
 - (void)windowDidExitFullScreen:(NSNotification *)notification
 {
   if (!mGeckoWindow) {
     return;
   }
 
   mGeckoWindow->EnteredFullScreen(false);
 }
--- a/widget/nsIWidgetListener.cpp
+++ b/widget/nsIWidgetListener.cpp
@@ -55,16 +55,21 @@ nsIWidgetListener::SizeModeChanged(nsSiz
 }
 
 void
 nsIWidgetListener::UIResolutionChanged()
 {
 }
 
 void
+nsIWidgetListener::FullscreenWillChange(bool aInFullscreen)
+{
+}
+
+void
 nsIWidgetListener::FullscreenChanged(bool aInFullscreen)
 {
 }
 
 bool
 nsIWidgetListener::ZLevelChanged(bool aImmediate,
                                  nsWindowZ* aPlacement,
                                  nsIWidget* aRequestBelow,
--- a/widget/nsIWidgetListener.h
+++ b/widget/nsIWidgetListener.h
@@ -92,16 +92,21 @@ public:
    * window to place below. On return, aActualBelow will be set to the
    * window actually behind. This generally only applies to Windows.
    */
   virtual bool ZLevelChanged(bool aImmediate, nsWindowZ* aPlacement,
                              nsIWidget* aRequestBelow,
                              nsIWidget** aActualBelow);
 
   /**
+   * Called when the window will enter or leave the fullscreen state.
+   */
+  virtual void FullscreenWillChange(bool aInFullscreen);
+
+  /**
    * Called when the window entered or left the fullscreen state.
    */
   virtual void FullscreenChanged(bool aInFullscreen);
 
   /**
    * Called when the occlusion state is changed.
    */
   virtual void OcclusionStateChanged(bool aIsFullyOccluded);
--- a/xpfe/appshell/nsWebShellWindow.cpp
+++ b/xpfe/appshell/nsWebShellWindow.cpp
@@ -409,16 +409,26 @@ nsWebShellWindow::UIResolutionChanged()
     mDocShell ? mDocShell->GetWindow() : nullptr;
   if (ourWindow) {
     MOZ_ASSERT(ourWindow->IsOuterWindow());
     ourWindow->DispatchCustomEvent(NS_LITERAL_STRING("resolutionchange"));
   }
 }
 
 void
+nsWebShellWindow::FullscreenWillChange(bool aInFullscreen)
+{
+  if (mDocShell) {
+    if (nsCOMPtr<nsPIDOMWindowOuter> ourWindow = mDocShell->GetWindow()) {
+      ourWindow->FullscreenWillChange(aInFullscreen);
+    }
+  }
+}
+
+void
 nsWebShellWindow::FullscreenChanged(bool aInFullscreen)
 {
   if (mDocShell) {
     if (nsCOMPtr<nsPIDOMWindowOuter> ourWindow = mDocShell->GetWindow()) {
       ourWindow->FinishFullscreenChange(aInFullscreen);
     }
   }
 }
--- a/xpfe/appshell/nsWebShellWindow.h
+++ b/xpfe/appshell/nsWebShellWindow.h
@@ -53,16 +53,17 @@ public:
   // nsIWidgetListener
   virtual nsIXULWindow* GetXULWindow() override { return this; }
   virtual nsIPresShell* GetPresShell() override;
   virtual bool WindowMoved(nsIWidget* aWidget, int32_t x, int32_t y) override;
   virtual bool WindowResized(nsIWidget* aWidget, int32_t aWidth, int32_t aHeight) override;
   virtual bool RequestWindowClose(nsIWidget* aWidget) override;
   virtual void SizeModeChanged(nsSizeMode sizeMode) override;
   virtual void UIResolutionChanged() override;
+  virtual void FullscreenWillChange(bool aInFullscreen) override;
   virtual void FullscreenChanged(bool aInFullscreen) override;
   virtual void OcclusionStateChanged(bool aIsFullyOccluded) override;
   virtual void OSToolbarButtonPressed() override;
   virtual bool ZLevelChanged(bool aImmediate, nsWindowZ *aPlacement,
                              nsIWidget* aRequestBelow, nsIWidget** aActualBelow) override;
   virtual void WindowActivated() override;
   virtual void WindowDeactivated() override;