Bug 1242874 - part2 : window's suspend attribute.
MozReview-Commit-ID: KaUI6oDMgMN
--- a/dom/base/nsDOMWindowUtils.cpp
+++ b/dom/base/nsDOMWindowUtils.cpp
@@ -3669,27 +3669,27 @@ nsDOMWindowUtils::PostRestyleSelfEvent(n
return NS_ERROR_INVALID_ARG;
}
nsLayoutUtils::PostRestyleEvent(element, eRestyle_Self, nsChangeHint(0));
return NS_OK;
}
NS_IMETHODIMP
-nsDOMWindowUtils::GetMediaSuspended(bool* aSuspended)
+nsDOMWindowUtils::GetMediaSuspended(uint32_t* aSuspended)
{
nsCOMPtr<nsPIDOMWindowOuter> window = do_QueryReferent(mWindow);
NS_ENSURE_STATE(window);
*aSuspended = window->GetMediaSuspended();
return NS_OK;
}
NS_IMETHODIMP
-nsDOMWindowUtils::SetMediaSuspended(bool aSuspended)
+nsDOMWindowUtils::SetMediaSuspended(uint32_t aSuspended)
{
nsCOMPtr<nsPIDOMWindowOuter> window = do_QueryReferent(mWindow);
NS_ENSURE_STATE(window);
window->SetMediaSuspended(aSuspended);
return NS_OK;
}
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -609,17 +609,18 @@ nsPIDOMWindow<T>::nsPIDOMWindow(nsPIDOMW
: mFrameElement(nullptr), mDocShell(nullptr), mModalStateDepth(0),
mRunningTimeout(nullptr), mMutationBits(0), mIsDocumentLoaded(false),
mIsHandlingResizeEvent(false), mIsInnerWindow(aOuterWindow != nullptr),
mMayHavePaintEventListener(false), mMayHaveTouchEventListener(false),
mMayHaveMouseEnterLeaveEventListener(false),
mMayHavePointerEnterLeaveEventListener(false),
mInnerObjectsFreed(false),
mIsModalContentWindow(false),
- mIsActive(false), mIsBackground(false), mMediaSuspended(false),
+ mIsActive(false), mIsBackground(false),
+ mMediaSuspended(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)
{}
@@ -3668,40 +3669,39 @@ nsPIDOMWindowInner::CreatePerformanceObj
parentPerformance = parentInnerWindow->GetPerformance();
}
}
mPerformance =
new nsPerformance(this, timing, timedChannel, parentPerformance);
}
}
-bool
+nsSuspendedTypes
nsPIDOMWindowOuter::GetMediaSuspended() const
{
if (IsInnerWindow()) {
return mOuterWindow->GetMediaSuspended();
}
return mMediaSuspended;
}
void
-nsPIDOMWindowOuter::SetMediaSuspended(bool aSuspended)
+nsPIDOMWindowOuter::SetMediaSuspended(nsSuspendedTypes aSuspended)
{
if (IsInnerWindow()) {
mOuterWindow->SetMediaSuspended(aSuspended);
return;
}
- if (mMediaSuspended == aSuspended) {
- return;
- }
-
- mMediaSuspended = aSuspended;
- RefreshMediaElements();
+ if (!IsDisposableSuspended(aSuspended)) {
+ mMediaSuspended = aSuspended;
+ }
+
+ RefreshMediaElementsSuspended(aSuspended);
}
bool
nsPIDOMWindowOuter::GetAudioMuted() const
{
if (IsInnerWindow()) {
return mOuterWindow->GetAudioMuted();
}
@@ -3717,17 +3717,17 @@ nsPIDOMWindowOuter::SetAudioMuted(bool a
return;
}
if (mAudioMuted == aMuted) {
return;
}
mAudioMuted = aMuted;
- RefreshMediaElements();
+ RefreshMediaElementsVolume();
}
float
nsPIDOMWindowOuter::GetAudioVolume() const
{
if (IsInnerWindow()) {
return mOuterWindow->GetAudioVolume();
}
@@ -3746,29 +3746,45 @@ nsPIDOMWindowOuter::SetAudioVolume(float
return NS_ERROR_DOM_INDEX_SIZE_ERR;
}
if (mAudioVolume == aVolume) {
return NS_OK;
}
mAudioVolume = aVolume;
- RefreshMediaElements();
+ RefreshMediaElementsVolume();
return NS_OK;
}
void
-nsPIDOMWindowOuter::RefreshMediaElements()
+nsPIDOMWindowOuter::RefreshMediaElementsVolume()
{
RefPtr<AudioChannelService> service = AudioChannelService::GetOrCreate();
if (service) {
service->RefreshAgentsVolume(GetOuterWindow());
}
}
+void
+nsPIDOMWindowOuter::RefreshMediaElementsSuspended(nsSuspendedTypes aSuspended)
+{
+ RefPtr<AudioChannelService> service = AudioChannelService::GetOrCreate();
+ if (service) {
+ // TODO : Impelement in next patch.
+ }
+}
+
+bool
+nsPIDOMWindowOuter::IsDisposableSuspended(nsSuspendedTypes aSuspended) const
+{
+ return (aSuspended == nsISuspendedTypes::SUSPENDED_PAUSE_DISPOSABLE ||
+ aSuspended == nsISuspendedTypes::SUSPENDED_STOP_DISPOSABLE);
+}
+
bool
nsPIDOMWindowInner::GetAudioCaptured() const
{
MOZ_ASSERT(IsInnerWindow());
return mAudioCaptured;
}
nsresult
--- a/dom/base/nsPIDOMWindow.h
+++ b/dom/base/nsPIDOMWindow.h
@@ -33,16 +33,18 @@ class nsIScriptTimeoutHandler;
class nsIURI;
class nsPerformance;
class nsPIDOMWindowInner;
class nsPIDOMWindowOuter;
class nsPIWindowRoot;
class nsXBLPrototypeHandler;
struct nsTimeout;
+typedef uint32_t nsSuspendedTypes;
+
namespace mozilla {
namespace dom {
class AudioContext;
class Element;
class ServiceWorkerRegistrationMainThread;
} // namespace dom
namespace gfx {
class VRDeviceProxy;
@@ -308,17 +310,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;
@@ -644,17 +646,29 @@ protected:
// Only used on outer windows.
bool mIsActive;
// Tracks whether our docshell is active. If it is, mIsBackground
// is false. Too bad we have so many different concepts of
// "active". Only used on outer windows.
bool mIsBackground;
- bool mMediaSuspended;
+ /**
+ * The suspended types can be "disposable" or "permanent". This varable only
+ * stores the value about permanent suspend.
+ * - disposable
+ * To pause all playing media in that window, but doesn't affect the media
+ * which starts after that.
+ *
+ * - permanent
+ * To pause all media in that window, and also affect the media which starts
+ * after that.
+ */
+ nsSuspendedTypes mMediaSuspended;
+
bool mAudioMuted;
float mAudioVolume;
bool mAudioCaptured;
// current desktop mode flag.
bool mDesktopModeViewport;
@@ -800,17 +814,19 @@ protected:
NS_DEFINE_STATIC_IID_ACCESSOR(nsPIDOMWindowInner, NS_PIDOMWINDOWINNER_IID)
// NB: It's very very important that these two classes have identical vtables
// and memory layout!
class nsPIDOMWindowOuter : public nsPIDOMWindow<mozIDOMWindowProxy>
{
protected:
- void RefreshMediaElements();
+ void RefreshMediaElementsVolume();
+ void RefreshMediaElementsSuspended(nsSuspendedTypes aSuspended);
+ bool IsDisposableSuspended(nsSuspendedTypes aSuspended) const;
public:
NS_DECLARE_STATIC_IID_ACCESSOR(NS_PIDOMWINDOWOUTER_IID)
static nsPIDOMWindowOuter* From(mozIDOMWindowProxy* aFrom) {
return static_cast<nsPIDOMWindowOuter*>(aFrom);
}
@@ -851,18 +867,18 @@ public:
return mDesktopModeViewport;
}
bool IsBackground()
{
return mIsBackground;
}
// Audio API
- bool GetMediaSuspended() const;
- void SetMediaSuspended(bool aSuspended);
+ nsSuspendedTypes GetMediaSuspended() const;
+ void SetMediaSuspended(nsSuspendedTypes aSuspended);
bool GetAudioMuted() const;
void SetAudioMuted(bool aMuted);
float GetAudioVolume() const;
nsresult SetAudioVolume(float aVolume);
void SetServiceWorkersTestingEnabled(bool aEnabled)
--- a/dom/interfaces/base/nsIDOMWindowUtils.idl
+++ b/dom/interfaces/base/nsIDOMWindowUtils.idl
@@ -44,17 +44,17 @@ interface nsIDOMClientRect;
interface nsIURI;
interface nsIDOMEventTarget;
interface nsIRunnable;
interface nsITranslationNodeList;
interface nsIJSRAIIHelper;
interface nsIContentPermissionRequest;
interface nsIObserver;
-[scriptable, uuid(46b44e33-13c2-4eb3-bf80-76a4e0857ccc)]
+[scriptable, uuid(81f63a84-93cf-4510-b346-32926024ecf8)]
interface nsIDOMWindowUtils : nsISupports {
/**
* Image animation mode of the window. When this attribute's value
* is changed, the implementation should set all images in the window
* to the given value. That is, when set to kDontAnimMode, all images
* will stop animating. The attribute's value must be one of the
* animationMode values from imgIContainer.
@@ -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);
@@ -1746,20 +1746,20 @@ interface nsIDOMWindowUtils : nsISupport
[implicit_jscontext] jsval getCompositorAPZTestData();
/**
* Posts an eRestyle_Self restyle event for the given element.
*/
void postRestyleSelfEvent(in nsIDOMElement aElement);
/**
- * Used to pause or resume all MediaElements in this window. Use-cases are
- * audio competing and remote media control.
+ * Used to pause or resume all media in this window. Use-cases are audio
+ * competing, remote media control and to prevent auto-playing media.
*/
- attribute boolean mediaSuspended;
+ attribute uint32_t mediaSuspended;
/**
* With this it's possible to mute all the MediaElements in this window.
* We have audioMuted and audioVolume to preserve the volume across
* mute/umute.
*/
attribute boolean audioMuted;
--- a/mobile/android/installer/package-manifest.in
+++ b/mobile/android/installer/package-manifest.in
@@ -557,8 +557,10 @@
@BINPATH@/components/DataStore.manifest
@BINPATH@/components/DataStoreImpl.js
@BINPATH@/components/dom_datastore.xpt
#ifdef PKG_LOCALE_MANIFEST
#include @PKG_LOCALE_MANIFEST@
#endif
+
+@BINPATH@/components/dom_audiochannel.xpt
\ No newline at end of file
--- a/toolkit/content/browser-content.js
+++ b/toolkit/content/browser-content.js
@@ -716,59 +716,87 @@ addMessageListener("WebChannelMessageToC
var AudioPlaybackListener = {
QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver]),
init() {
Services.obs.addObserver(this, "audio-playback", false);
Services.obs.addObserver(this, "AudioFocusChanged", false);
- addMessageListener("AudioPlaybackMute", this);
+ addMessageListener("AudioPlayback", this);
addEventListener("unload", () => {
AudioPlaybackListener.uninit();
});
},
uninit() {
Services.obs.removeObserver(this, "audio-playback");
Services.obs.removeObserver(this, "AudioFocusChanged");
- removeMessageListener("AudioPlaybackMute", this);
+ removeMessageListener("AudioPlayback", this);
},
observe(subject, topic, data) {
if (topic === "audio-playback") {
if (subject && subject.top == global.content) {
let name = "AudioPlayback:";
name += (data === "active") ? "Start" : "Stop";
sendAsyncMessage(name);
}
} else if (topic == "AudioFocusChanged") {
let utils = global.content.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindowUtils);
+ var suspendTypes = Ci.nsISuspendedTypes;
switch (data) {
// The AudioFocus:LossTransient means the media would be resumed after
// the interruption ended, but AudioFocus:Loss doesn't.
- // TODO : distinguish these types, it would be done in bug1242874.
case "Loss":
+ utils.mediaSuspended = suspendTypes.SUSPENDED_STOP_DISPOSABLE;
+ break;
case "LossTransient":
- utils.mediaSuspended = true;
+ utils.mediaSuspended = suspendTypes.SUSPENDED_PAUSE;
break;
case "Gain":
- utils.mediaSuspended = false;
+ utils.mediaSuspended = suspendTypes.NONE_SUSPENDED;
break;
}
}
},
receiveMessage(msg) {
- if (msg.name == "AudioPlaybackMute") {
+ if (msg.name == "AudioPlayback") {
let utils = global.content.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindowUtils);
- utils.audioMuted = msg.data.type === "mute";
+ var suspendTypes = Ci.nsISuspendedTypes;
+ switch (msg.data.type) {
+ case "mute":
+ utils.audioMuted = true;
+ break;
+ case "unmute":
+ utils.audioMuted = false;
+ break;
+ case "lostAudioFocusTransiently":
+ utils.mediaSuspended = suspendTypes.SUSPENDED_PAUSE;
+ break;
+ case "mediaControlPaused":
+ utils.mediaSuspended = suspendTypes.SUSPENDED_PAUSE_DISPOSABLE;
+ break;
+ case "mediaControlStoppedOrLostAudioFocusPermanently":
+ utils.mediaSuspended = suspendTypes.SUSPENDED_STOP_DISPOSABLE;
+ break;
+ case "blockInactivePageMedia":
+ utils.mediaSuspended = suspendTypes.SUSPENDED_BLOCK;
+ break;
+ case "resumeMedia":
+ utils.mediaSuspended = suspendTypes.NONE_SUSPENDED;
+ break;
+ default:
+ dump("Error : AudioPlaybackListener, wrong suspended type!\n");
+ break;
+ }
}
},
};
AudioPlaybackListener.init();
addMessageListener("Browser:PurgeSessionHistory", function BrowserPurgeHistory() {
let sessionHistory = docShell.QueryInterface(Ci.nsIWebNavigation).sessionHistory;
if (!sessionHistory) {
--- a/toolkit/content/widgets/browser.xml
+++ b/toolkit/content/widgets/browser.xml
@@ -709,32 +709,76 @@
<property name="audioMuted"
onget="return this._audioMuted;"
readonly="true"/>
<method name="mute">
<body>
<![CDATA[
this._audioMuted = true;
- this.messageManager.sendAsyncMessage("AudioPlaybackMute",
+ this.messageManager.sendAsyncMessage("AudioPlayback",
{type: "mute"});
]]>
</body>
</method>
<method name="unmute">
<body>
<![CDATA[
this._audioMuted = false;
- this.messageManager.sendAsyncMessage("AudioPlaybackMute",
+ this.messageManager.sendAsyncMessage("AudioPlayback",
{type: "unmute"});
]]>
</body>
</method>
+ <method name="pauseMedia">
+ <parameter name="disposable"/>
+ <body>
+ <![CDATA[
+ var suspendedReason;
+ if (disposable) {
+ suspendedReason = "mediaControlPaused";
+ } else {
+ suspendedReason = "lostAudioFocusTransiently";
+ }
+
+ this.messageManager.sendAsyncMessage("AudioPlayback",
+ {type: suspendedReason});
+ ]]>
+ </body>
+ </method>
+
+ <method name="stopMedia">
+ <body>
+ <![CDATA[
+ this.messageManager.sendAsyncMessage("AudioPlayback",
+ {type: "mediaControlStoppedOrLostAudioFocusPermanently"});
+ ]]>
+ </body>
+ </method>
+
+ <method name="blockMedia">
+ <body>
+ <![CDATA[
+ this.messageManager.sendAsyncMessage("AudioPlayback",
+ {type: "blockInactivePageMedia"});
+ ]]>
+ </body>
+ </method>
+
+ <method name="resumeMedia">
+ <body>
+ <![CDATA[
+ this.messageManager.sendAsyncMessage("AudioPlayback",
+ {type: "resumeMedia"});
+ ]]>
+ </body>
+ </method>
+
<property name="securityUI">
<getter>
<![CDATA[
if (!this.docShell.securityUI) {
const SECUREBROWSERUI_CONTRACTID = "@mozilla.org/secure_browser_ui;1";
if (!this.hasAttribute("disablesecurity") &&
SECUREBROWSERUI_CONTRACTID in Components.classes) {
var securityUI = Components.classes[SECUREBROWSERUI_CONTRACTID]