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
--- 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