Bug 1236512 - Part 1: Send "occlusionstatechange" custom event when occlusion state in Mac is changed; f=spohl; r=mstange
authorEdgar Chen <echen@mozilla.com>
Fri, 26 May 2017 18:09:34 +0800
changeset 597988 683bf7d44d1a257fcb81a6739f58ff1ed6b7396c
parent 597957 93e4115d18670235f950ecaf9ce66559abb6024a
child 597989 c067db7f1d317b16509a3cc94838775adc6747f5
push id65109
push userbmo:dburns@mozilla.com
push dateWed, 21 Jun 2017 09:10:16 +0000
reviewersmstange
bugs1236512
milestone56.0a1
Bug 1236512 - Part 1: Send "occlusionstatechange" custom event when occlusion state in Mac is changed; f=spohl; r=mstange MozReview-Commit-ID: GTBeH4UwZiU
widget/cocoa/nsCocoaWindow.h
widget/cocoa/nsCocoaWindow.mm
widget/nsBaseWidget.cpp
widget/nsBaseWidget.h
widget/nsIWidget.h
widget/nsIWidgetListener.cpp
widget/nsIWidgetListener.h
xpfe/appshell/nsWebShellWindow.cpp
xpfe/appshell/nsWebShellWindow.h
--- a/widget/cocoa/nsCocoaWindow.h
+++ b/widget/cocoa/nsCocoaWindow.h
@@ -322,16 +322,17 @@ public:
     virtual void SetDrawsInTitlebar(bool aState) override;
     virtual void UpdateThemeGeometries(const nsTArray<ThemeGeometry>& aThemeGeometries) override;
     virtual nsresult SynthesizeNativeMouseEvent(LayoutDeviceIntPoint aPoint,
                                                 uint32_t aNativeMessage,
                                                 uint32_t aModifierFlags,
                                                 nsIObserver* aObserver) override;
 
     void DispatchSizeModeEvent();
+    void DispatchOcclusionEvent();
 
     // be notified that a some form of drag event needs to go into Gecko
     virtual bool DragEvent(unsigned int aMessage, mozilla::gfx::Point aMouseGlobal, UInt16 aKeyModifiers);
 
     bool HasModalDescendents() { return mNumModalDescendents > 0; }
     NSWindow *GetCocoaWindow() { return mWindow; }
 
     void SetMenuBar(nsMenuBarX* aMenuBar);
--- a/widget/cocoa/nsCocoaWindow.mm
+++ b/widget/cocoa/nsCocoaWindow.mm
@@ -63,16 +63,29 @@ int32_t gXULModalLevel = 0;
 nsCocoaWindowList *gGeckoAppModalWindowList = NULL;
 
 // defined in nsMenuBarX.mm
 extern NSMenu* sApplicationMenu; // Application menu shared by all menubars
 
 // defined in nsChildView.mm
 extern BOOL                gSomeMenuBarPainted;
 
+#if !defined(MAC_OS_X_VERSION_10_9) || \
+    MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_9
+
+enum NSWindowOcclusionState {
+  NSWindowOcclusionStateVisible = 0x1 << 1
+};
+
+@interface NSWindow(OcclusionState)
+- (NSWindowOcclusionState) occlusionState;
+@end
+
+#endif
+
 #if !defined(MAC_OS_X_VERSION_10_12) || \
     MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_12
 
 @interface NSWindow(AutomaticWindowTabbing)
 + (void)setAllowsAutomaticWindowTabbing:(BOOL)allow;
 @end
 
 #endif
@@ -1967,16 +1980,37 @@ nsCocoaWindow::DispatchSizeModeEvent()
 
   mSizeMode = newMode;
   if (mWidgetListener) {
     mWidgetListener->SizeModeChanged(newMode);
   }
 }
 
 void
+nsCocoaWindow::DispatchOcclusionEvent()
+{
+  if (!mWindow) {
+    return;
+  }
+
+  bool newOcclusionState =
+    !([mWindow occlusionState] & NSWindowOcclusionStateVisible);
+
+  // Don't dispatch if the new occlustion state is the same as the current state.
+  if (mIsFullyOccluded == newOcclusionState) {
+    return;
+  }
+
+  mIsFullyOccluded = newOcclusionState;
+  if (mWidgetListener) {
+    mWidgetListener->OcclusionStateChanged(mIsFullyOccluded);
+  }
+}
+
+void
 nsCocoaWindow::ReportSizeEvent()
 {
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
 
   UpdateBounds();
 
   if (mWidgetListener) {
     LayoutDeviceIntRect innerBounds = GetClientBounds();
@@ -2784,16 +2818,24 @@ nsCocoaWindow::GetEditCommands(NativeKey
     if ([window backingScaleFactor] != oldFactor) {
       mGeckoWindow->BackingScaleFactorChanged();
     }
   }
 
   NS_OBJC_END_TRY_ABORT_BLOCK;
 }
 
+// This method is on NSWindowDelegate starting with 10.9
+- (void)windowDidChangeOcclusionState:(NSNotification*)aNotification
+{
+  if (mGeckoWindow) {
+    mGeckoWindow->DispatchOcclusionEvent();
+  }
+}
+
 - (nsCocoaWindow*)geckoWidget
 {
   return mGeckoWindow;
 }
 
 - (bool)toplevelActiveState
 {
   return mToplevelActiveState;
--- a/widget/nsBaseWidget.cpp
+++ b/widget/nsBaseWidget.cpp
@@ -163,16 +163,17 @@ nsBaseWidget::nsBaseWidget()
 , mSizeMode(nsSizeMode_Normal)
 , mPopupLevel(ePopupLevelTop)
 , mPopupType(ePopupTypeAny)
 , mHasRemoteContent(false)
 , mCompositorWidgetDelegate(nullptr)
 , mUpdateCursor(true)
 , mUseAttachedEvents(false)
 , mIMEHasFocus(false)
+, mIsFullyOccluded(false)
 #if defined(XP_WIN) || defined(XP_MACOSX) || defined(MOZ_WIDGET_GTK)
 , mAccessibilityInUseFlag(false)
 #endif
 {
 #ifdef NOISY_WIDGET_LEAKS
   gNumWidgets++;
   printf("WIDGETS+ = %d\n", gNumWidgets);
 #endif
--- a/widget/nsBaseWidget.h
+++ b/widget/nsBaseWidget.h
@@ -163,16 +163,21 @@ public:
                                       override {}
 
   virtual void            SetSizeMode(nsSizeMode aMode) override;
   virtual nsSizeMode      SizeMode() override
   {
     return mSizeMode;
   }
 
+  virtual bool            IsFullyOccluded() const override
+  {
+    return mIsFullyOccluded;
+  }
+
   virtual nsCursor        GetCursor() override;
   virtual void            SetCursor(nsCursor aCursor) override;
   virtual nsresult        SetCursor(imgIContainer* aCursor,
                                     uint32_t aHotspotX, uint32_t aHotspotY) override;
   virtual void            ClearCachedCursor() override { mUpdateCursor = true; }
   virtual void            SetTransparencyMode(nsTransparencyMode aMode) override;
   virtual nsTransparencyMode GetTransparencyMode() override;
   virtual void            GetWindowClipRegion(nsTArray<LayoutDeviceIntRect>* aRects) override;
@@ -682,16 +687,17 @@ protected:
   SizeConstraints   mSizeConstraints;
   bool              mHasRemoteContent;
 
   CompositorWidgetDelegate* mCompositorWidgetDelegate;
 
   bool              mUpdateCursor;
   bool              mUseAttachedEvents;
   bool              mIMEHasFocus;
+  bool              mIsFullyOccluded;
 #if defined(XP_WIN) || defined(XP_MACOSX) || defined(MOZ_WIDGET_GTK)
   bool              mAccessibilityInUseFlag;
 #endif
   static nsIRollupListener* gRollupListener;
 
   struct InitialZoomConstraints {
     InitialZoomConstraints(const uint32_t& aPresShellID,
                            const FrameMetrics::ViewID& aViewID,
--- a/widget/nsIWidget.h
+++ b/widget/nsIWidget.h
@@ -832,16 +832,21 @@ class nsIWidget : public nsISupports
 
     /**
      * 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
+     */
+    virtual bool IsFullyOccluded() const = 0;
+
+    /**
      * Enable or disable this Widget
      *
      * @param aState true to enable the Widget, false to disable it.
      */
     virtual void Enable(bool aState) = 0;
 
     /**
      * Ask whether the widget is enabled
--- a/widget/nsIWidgetListener.cpp
+++ b/widget/nsIWidgetListener.cpp
@@ -69,16 +69,21 @@ nsIWidgetListener::ZLevelChanged(bool aI
                                  nsWindowZ* aPlacement,
                                  nsIWidget* aRequestBelow,
                                  nsIWidget** aActualBelow)
 {
   return false;
 }
 
 void
+nsIWidgetListener::OcclusionStateChanged(bool aIsFullyOccluded)
+{
+}
+
+void
 nsIWidgetListener::WindowActivated()
 {
 }
 
 void
 nsIWidgetListener::WindowDeactivated()
 {
 }
--- a/widget/nsIWidgetListener.h
+++ b/widget/nsIWidgetListener.h
@@ -97,16 +97,21 @@ public:
                              nsIWidget** aActualBelow);
 
   /**
    * 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);
+
+  /**
    * Called when the window is activated and focused.
    */
   virtual void WindowActivated();
 
   /**
    * Called when the window is deactivated and no longer focused.
    */
   virtual void WindowDeactivated();
--- a/xpfe/appshell/nsWebShellWindow.cpp
+++ b/xpfe/appshell/nsWebShellWindow.cpp
@@ -417,16 +417,28 @@ nsWebShellWindow::FullscreenChanged(bool
   if (mDocShell) {
     if (nsCOMPtr<nsPIDOMWindowOuter> ourWindow = mDocShell->GetWindow()) {
       ourWindow->FinishFullscreenChange(aInFullscreen);
     }
   }
 }
 
 void
+nsWebShellWindow::OcclusionStateChanged(bool aIsFullyOccluded)
+{
+  nsCOMPtr<nsPIDOMWindowOuter> ourWindow =
+    mDocShell ? mDocShell->GetWindow() : nullptr;
+  if (ourWindow) {
+    MOZ_ASSERT(ourWindow->IsOuterWindow());
+    // And always fire a user-defined occlusionstatechange event on the window
+    ourWindow->DispatchCustomEvent(NS_LITERAL_STRING("occlusionstatechange"));
+  }
+}
+
+void
 nsWebShellWindow::OSToolbarButtonPressed()
 {
   // Keep a reference as setting the chrome flags can fire events.
   nsCOMPtr<nsIXULWindow> xulWindow(this);
 
   // rjc: don't use "nsIWebBrowserChrome::CHROME_EXTRA"
   //      due to components with multiple sidebar components
   //      (such as Mail/News, Addressbook, etc)... and frankly,
--- a/xpfe/appshell/nsWebShellWindow.h
+++ b/xpfe/appshell/nsWebShellWindow.h
@@ -54,16 +54,17 @@ public:
   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 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;
 
 protected:
   friend class mozilla::WebShellWindowTimerCallback;