Bug 1274919 - Part 1: Notify window when mouse hovers over tab. r?mconley,smaug draft
authorDan Glastonbury <dglastonbury@mozilla.com>
Thu, 19 May 2016 10:18:54 +1000
changeset 370570 d59d44f895b9c538b98fda44fe906f44c7232081
parent 370103 dd6f7866cb4aa451e110bc3a5a201d0d704aa329
child 370571 fd85d3450c3310c5e32cd3fddf85089a034accd2
push id19106
push userbmo:dglastonbury@mozilla.com
push dateWed, 25 May 2016 01:25:27 +0000
reviewersmconley, smaug
bugs1274919
milestone49.0a1
Bug 1274919 - Part 1: Notify window when mouse hovers over tab. r?mconley,smaug Notify outer window when the mouse begin/ends hovering over the window's tab. MozReview-Commit-ID: AGLBqUlfqZy
browser/base/content/tab-content.js
browser/base/content/tabbrowser.xml
dom/base/nsDOMWindowUtils.cpp
dom/base/nsGlobalWindow.cpp
dom/base/nsPIDOMWindow.h
dom/interfaces/base/nsIDOMWindowUtils.idl
--- a/browser/base/content/tab-content.js
+++ b/browser/base/content/tab-content.js
@@ -899,8 +899,21 @@ addEventListener("unload", () => {
   ExtensionContent.uninit(this);
   RefreshBlocker.uninit();
 });
 
 addEventListener("MozAfterPaint", function onFirstPaint() {
   removeEventListener("MozAfterPaint", onFirstPaint);
   sendAsyncMessage("Browser:FirstPaint");
 });
+
+addMessageListener("Browser:TabHover", message => {
+  if (!content) {
+    return;
+  }
+  let windowUtils = content.QueryInterface(Ci.nsIInterfaceRequestor)
+                           .getInterface(Ci.nsIDOMWindowUtils);
+  if (!windowUtils) {
+    return;
+  }
+
+  windowUtils.tabHovered = message.data.isHovered;
+});
--- a/browser/base/content/tabbrowser.xml
+++ b/browser/base/content/tabbrowser.xml
@@ -6361,32 +6361,38 @@
             let candidate = visibleTabs[tabIndex + 1];
             if (!candidate.selected) {
               tabContainer._afterHoveredTab = candidate;
               candidate.setAttribute("afterhovered", "true");
             }
           }
 
           tabContainer._hoveredTab = this;
+
+          let browser = tabContainer.tabbrowser.getBrowserForTab(this);
+          browser.messageManager.sendAsyncMessage("Browser:TabHover", { isHovered: true })
         ]]></body>
       </method>
 
       <method name="_mouseleave">
         <body><![CDATA[
           let tabContainer = this.parentNode;
           if (tabContainer._beforeHoveredTab) {
             tabContainer._beforeHoveredTab.removeAttribute("beforehovered");
             tabContainer._beforeHoveredTab = null;
           }
           if (tabContainer._afterHoveredTab) {
             tabContainer._afterHoveredTab.removeAttribute("afterhovered");
             tabContainer._afterHoveredTab = null;
           }
 
           tabContainer._hoveredTab = null;
+
+          let browser = tabContainer.tabbrowser.getBrowserForTab(this);
+          browser.messageManager.sendAsyncMessage("Browser:TabHover", { isHovered: false })
         ]]></body>
       </method>
 
       <method name="toggleMuteAudio">
         <parameter name="aMuteReason"/>
         <body>
         <![CDATA[
           let tabContainer = this.parentNode;
--- a/dom/base/nsDOMWindowUtils.cpp
+++ b/dom/base/nsDOMWindowUtils.cpp
@@ -3911,16 +3911,36 @@ nsDOMWindowUtils::SetNextPaintSyncId(int
 NS_IMETHODIMP
 nsDOMWindowUtils::RespectDisplayPortSuppression(bool aEnabled)
 {
   nsCOMPtr<nsIPresShell> shell(GetPresShell());
   APZCCallbackHelper::RespectDisplayPortSuppression(aEnabled, shell);
   return NS_OK;
 }
 
+NS_IMETHODIMP
+nsDOMWindowUtils::GetTabHovered(bool* aHovered)
+{
+  nsCOMPtr<nsPIDOMWindowOuter> window = do_QueryReferent(mWindow);
+  NS_ENSURE_STATE(window);
+
+  *aHovered = window->GetTabHovered();
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDOMWindowUtils::SetTabHovered(bool aHovered)
+{
+  nsCOMPtr<nsPIDOMWindowOuter> window = do_QueryReferent(mWindow);
+  NS_ENSURE_STATE(window);
+
+  window->SetTabHovered(aHovered);
+  return NS_OK;
+}
+
 NS_INTERFACE_MAP_BEGIN(nsTranslationNodeList)
   NS_INTERFACE_MAP_ENTRY(nsISupports)
   NS_INTERFACE_MAP_ENTRY(nsITranslationNodeList)
 NS_INTERFACE_MAP_END
 
 NS_IMPL_ADDREF(nsTranslationNodeList)
 NS_IMPL_RELEASE(nsTranslationNodeList)
 
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -616,17 +616,18 @@ nsPIDOMWindow<T>::nsPIDOMWindow(nsPIDOMW
   mIsModalContentWindow(false),
   mIsActive(false), mIsBackground(false),
   mMediaSuspend(nsISuspendedTypes::NONE_SUSPENDED),
   mAudioMuted(false), mAudioVolume(1.0), mAudioCaptured(false),
   mDesktopModeViewport(false), mInnerWindow(nullptr),
   mOuterWindow(aOuterWindow),
   // Make sure no actual window ends up with mWindowID == 0
   mWindowID(NextWindowID()), mHasNotifiedGlobalCreated(false),
-  mMarkedCCGeneration(0), mServiceWorkersTestingEnabled(false)
+  mMarkedCCGeneration(0), mServiceWorkersTestingEnabled(false),
+  mTabHovered(false)
  {}
 
 template<class T>
 nsPIDOMWindow<T>::~nsPIDOMWindow() {}
 
 /* static */
 nsPIDOMWindowOuter*
 nsPIDOMWindowOuter::GetFromCurrentInner(nsPIDOMWindowInner* aInner)
@@ -4005,16 +4006,53 @@ nsPIDOMWindowOuter::GetServiceWorkersTes
   nsCOMPtr<nsPIDOMWindowOuter> topWindow = GetScriptableTop();
   if (!topWindow) {
     return false;
   }
   return topWindow->mServiceWorkersTestingEnabled;
 }
 
 bool
+nsPIDOMWindowOuter::GetTabHovered() const
+{
+  if (IsInnerWindow()) {
+    return mOuterWindow->GetTabHovered();
+  }
+
+  // Automatically get this setting from the top level window so that
+  // nested iframes get the correct setting, since the value is only
+  // ever set on the top-most, outer window owned by tab-content.
+  nsCOMPtr<nsPIDOMWindowOuter> topWindow = GetScriptableTop();
+  if (!topWindow) {
+    return false;
+  }
+
+  return topWindow->mTabHovered;
+}
+
+void
+nsPIDOMWindowOuter::SetTabHovered(bool aHovered)
+{
+  // Tab hovered should only be set on the top level window (via
+  // Browser:TabHovered message)
+#ifdef DEBUG
+  nsCOMPtr<nsPIDOMWindowOuter> topWindow = GetScriptableTop();
+  MOZ_ASSERT_IF(aEnabled, this == topWindow);
+#endif
+
+  if (mTabHovered == aHovered) {
+    return;
+  }
+
+  mTabHovered = aHovered;
+
+  // TODO: Notify media elements to become ready (exit suspend/dormant, spin
+}
+
+bool
 nsPIDOMWindowInner::GetAudioCaptured() const
 {
   MOZ_ASSERT(IsInnerWindow());
   return mAudioCaptured;
 }
 
 nsresult
 nsPIDOMWindowInner::SetAudioCapture(bool aCapture)
--- a/dom/base/nsPIDOMWindow.h
+++ b/dom/base/nsPIDOMWindow.h
@@ -316,17 +316,17 @@ public:
   /**
    * Call this to check whether some node (this window, its document,
    * or content in that document) has a paint event listener.
    */
   bool HasPaintEventListeners()
   {
     return mMayHavePaintEventListener;
   }
-  
+
   /**
    * Call this to indicate that some node (this window, its document,
    * or content in that document) has a touch event listener.
    */
   void SetHasTouchEventListeners()
   {
     if (!mMayHaveTouchEventListener) {
       mMayHaveTouchEventListener = true;
@@ -697,16 +697,19 @@ protected:
   // the (chrome|content)-document-global-created notification.
   bool mHasNotifiedGlobalCreated;
 
   uint32_t mMarkedCCGeneration;
 
   // Let the service workers plumbing know that some feature are enabled while
   // testing.
   bool mServiceWorkersTestingEnabled;
+
+  // True if the tab associated with the window is hovered by mouse pointer.
+  bool mTabHovered;
 };
 
 #define NS_PIDOMWINDOWINNER_IID \
 { 0x775dabc9, 0x8f43, 0x4277, \
   { 0x9a, 0xdb, 0xf1, 0x99, 0x0d, 0x77, 0xcf, 0xfb } }
 
 #define NS_PIDOMWINDOWOUTER_IID \
   { 0x769693d4, 0xb009, 0x4fe2, \
@@ -884,16 +887,19 @@ public:
   bool GetAudioMuted() const;
   void SetAudioMuted(bool aMuted);
 
   float GetAudioVolume() const;
   nsresult SetAudioVolume(float aVolume);
 
   void SetServiceWorkersTestingEnabled(bool aEnabled);
   bool GetServiceWorkersTestingEnabled();
+
+  bool GetTabHovered() const;
+  void SetTabHovered(bool aHovered);
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(nsPIDOMWindowOuter, NS_PIDOMWINDOWOUTER_IID)
 
 #include "nsPIDOMWindowInlines.h"
 
 #ifdef MOZILLA_INTERNAL_API
 PopupControlState
--- a/dom/interfaces/base/nsIDOMWindowUtils.idl
+++ b/dom/interfaces/base/nsIDOMWindowUtils.idl
@@ -872,17 +872,17 @@ interface nsIDOMWindowUtils : nsISupport
    * @param aLeftSize How much to expand left the rectangle
    * @param aIgnoreRootScrollFrame whether or not to ignore the root scroll
    *        frame when retrieving the element. If false, this method returns
    *        null for coordinates outside of the viewport.
    * @param aFlushLayout flushes layout if true. Otherwise, no flush occurs.
    */
   nsIDOMNodeList nodesFromRect(in float aX,
                                in float aY,
-                               in float aTopSize, 
+                               in float aTopSize,
                                in float aRightSize,
                                in float aBottomSize,
                                in float aLeftSize,
                                in boolean aIgnoreRootScrollFrame,
                                in boolean aFlushLayout);
 
 
   /**
@@ -1325,17 +1325,17 @@ interface nsIDOMWindowUtils : nsISupport
   void resumeTimeouts();
 
   /**
    * What type of layer manager the widget associated with this window is
    * using. "Basic" is unaccelerated; other types are accelerated. Throws an
    * error if there is no widget associated with this window.
    */
   readonly attribute AString layerManagerType;
-  
+
   /**
    * True if the layer manager for the widget associated with this window is
    * forwarding layers to a remote compositor, false otherwise. Throws an
    * error if there is no widget associated with this window.
    */
   readonly attribute boolean layerManagerRemote;
 
   /**
@@ -1500,17 +1500,17 @@ interface nsIDOMWindowUtils : nsISupport
 
   /**
    * Checks the layer tree for this window and returns true
    * if all layers have transforms that are translations by integers,
    * no leaf layers overlap, and the union of the leaf layers is exactly
    * the bounds of the window. Always returns true in non-DEBUG builds.
    */
   boolean leafLayersPartitionWindow();
- 
+
   /**
    * Check if any PaintedLayer painting has been done for this element,
    * clears the painted flags if they have.
    */
   boolean checkAndClearPaintedState(in nsIDOMElement aElement);
 
   /**
    * Check whether all display items of the primary frame of aElement have been
@@ -1635,17 +1635,17 @@ interface nsIDOMWindowUtils : nsISupport
 
   const unsigned long AGENT_SHEET = 0;
   const unsigned long USER_SHEET = 1;
   const unsigned long AUTHOR_SHEET = 2;
   /**
    * Synchronously loads a style sheet from |sheetURI| and adds it to the list
    * of additional style sheets of the document.
    *
-   * These additional style sheets are very much like user/agent sheets loaded 
+   * These additional style sheets are very much like user/agent sheets loaded
    * with loadAndRegisterSheet. The only difference is that they are applied only
    * on the document owned by this window.
    *
    * Sheets added via this API take effect immediately on the document.
    */
   void loadSheet(in nsIURI sheetURI, in unsigned long type);
 
   /**
@@ -1658,17 +1658,17 @@ interface nsIDOMWindowUtils : nsISupport
    *
    * Style sheets can be preloaded with nsIStyleSheetService.preloadSheet.
    *
    * Sheets added via this API take effect immediately on the document.
    */
   void addSheet(in nsIDOMStyleSheet sheet, in unsigned long type);
 
   /**
-   * Remove the document style sheet at |sheetURI| from the list of additional 
+   * Remove the document style sheet at |sheetURI| from the list of additional
    * style sheets of the document.  The removal takes effect immediately.
    */
   void removeSheet(in nsIURI sheetURI, in unsigned long type);
 
   /**
    * Same as the above method but allows passing the URI as a string.
    */
   void removeSheetUsingURIString(in ACString sheetURI, in unsigned long type);
@@ -1867,16 +1867,22 @@ interface nsIDOMWindowUtils : nsISupport
 
   /**
    * Enable or disable displayport suppression. This is intended to be used by
    * testing code, to provide more deterministic behaviour over the displayport
    * suppression during tests. Note that this updates a flag, so whatever value
    * was last provided is what will be used.
    */
   void respectDisplayPortSuppression(in boolean aEnabled);
+
+  /**
+   * Signal to the window that its associated tab has been "hovered"
+   * by the mouse pointer.
+   */
+  attribute boolean tabHovered;
 };
 
 [scriptable, uuid(c694e359-7227-4392-a138-33c0cc1f15a6)]
 interface nsITranslationNodeList : nsISupports {
   readonly attribute unsigned long length;
   nsIDOMNode item(in unsigned long index);
 
   // A translation root is a block element, or an inline element