Bug 1463919 - Ensure resuming via audio tab indicator bypasses autoplay permission check. r?mconley
If the user opens a tab in the background, and that tab tries to play media,
we'll delay playing that media until the tab is brought to the foreground.
But the user can explicitly start playback of such delayed media by clicking
the "play" icon we show in the tab indicator. Then if autoplay is disabled,
we'll block the play (unless the origin is whitelisted). This is bad, as the
user has clearly indicated intent to play media in this tab.
So this patch adds an override flag that we set when the user starts playback
via the audio tab indicator that short-circuits the autoplay blocking logic,
ensuring we play in this case.
MozReview-Commit-ID: Koir3xhF696
--- a/dom/base/nsDOMWindowUtils.cpp
+++ b/dom/base/nsDOMWindowUtils.cpp
@@ -3948,16 +3948,38 @@ nsDOMWindowUtils::SetMediaSuspend(uint32
nsCOMPtr<nsPIDOMWindowOuter> window = do_QueryReferent(mWindow);
NS_ENSURE_STATE(window);
window->SetMediaSuspend(aSuspend);
return NS_OK;
}
NS_IMETHODIMP
+nsDOMWindowUtils::GetAllowAutoplayOverride(bool* aAllowAutoplayOverride)
+{
+ NS_ENSURE_ARG(aAllowAutoplayOverride);
+
+ nsCOMPtr<nsPIDOMWindowOuter> window = do_QueryReferent(mWindow);
+ NS_ENSURE_STATE(window);
+
+ *aAllowAutoplayOverride = window->GetAllowAutoplayOverride();
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDOMWindowUtils::SetAllowAutoplayOverride(bool aAllowAutoplayOverride)
+{
+ nsCOMPtr<nsPIDOMWindowOuter> window = do_QueryReferent(mWindow);
+ NS_ENSURE_STATE(window);
+
+ window->SetAllowAutoplayOverride(aAllowAutoplayOverride);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
nsDOMWindowUtils::GetAudioMuted(bool* aMuted)
{
nsCOMPtr<nsPIDOMWindowOuter> window = do_QueryReferent(mWindow);
NS_ENSURE_STATE(window);
*aMuted = window->GetAudioMuted();
return NS_OK;
}
--- a/dom/base/nsGlobalWindowOuter.cpp
+++ b/dom/base/nsGlobalWindowOuter.cpp
@@ -2588,16 +2588,28 @@ nsPIDOMWindowOuter::MaybeNotifyMediaResu
RefPtr<AudioChannelService> service = AudioChannelService::GetOrCreate();
if (service) {
service->NotifyMediaResumedFromBlock(GetOuterWindow());
}
}
}
bool
+nsPIDOMWindowOuter::GetAllowAutoplayOverride() const
+{
+ return mAllowAutoplayOverride;
+}
+
+void
+nsPIDOMWindowOuter::SetAllowAutoplayOverride(bool aAllowAutoplayOverride)
+{
+ mAllowAutoplayOverride = aAllowAutoplayOverride;
+}
+
+bool
nsPIDOMWindowOuter::GetAudioMuted() const
{
return mAudioMuted;
}
void
nsPIDOMWindowOuter::SetAudioMuted(bool aMuted)
{
--- a/dom/base/nsPIDOMWindow.h
+++ b/dom/base/nsPIDOMWindow.h
@@ -791,16 +791,19 @@ public:
{
return mIsBackground;
}
// Audio API
SuspendTypes GetMediaSuspend() const;
void SetMediaSuspend(SuspendTypes aSuspend);
+ bool GetAllowAutoplayOverride() const;
+ void SetAllowAutoplayOverride(bool aAllowAutoplayOverride);
+
bool GetAudioMuted() const;
void SetAudioMuted(bool aMuted);
float GetAudioVolume() const;
nsresult SetAudioVolume(float aVolume);
void MaybeActiveMediaComponents();
@@ -1198,16 +1201,18 @@ protected:
* which starts after that.
*
* - permanent
* To pause all media in that window, and also affect the media which starts
* after that.
*/
SuspendTypes mMediaSuspend;
+ bool mAllowAutoplayOverride = false;
+
bool mAudioMuted;
float mAudioVolume;
// current desktop mode flag.
bool mDesktopModeViewport;
bool mIsRootOuterWindow;
--- a/dom/interfaces/base/nsIDOMWindowUtils.idl
+++ b/dom/interfaces/base/nsIDOMWindowUtils.idl
@@ -1730,16 +1730,24 @@ interface nsIDOMWindowUtils : nsISupport
/**
* 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 uint32_t mediaSuspend;
/**
+ * Use to override autoplay blocking logic, so that autoplay is allowed.
+ * This is useful when clear intent has been signaled by the user that media
+ * should be allowed to play. For example, starting media playback via the
+ * audio tab indicator.
+ */
+ attribute bool allowAutoplayOverride;
+
+ /**
* 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;
/**
* range: greater or equal to 0. The real volume level is affected by the
--- a/dom/media/AutoplayPolicy.cpp
+++ b/dom/media/AutoplayPolicy.cpp
@@ -49,16 +49,23 @@ IsAllowedToPlay(nsPIDOMWindowInner* aWin
// Pages which have been granted permission to capture WebRTC camera or
// microphone are assumed to be trusted, and are allowed to autoplay.
MediaManager* manager = MediaManager::GetIfExists();
if (manager &&
manager->IsActivelyCapturingOrHasAPermission(aWindow->WindowID())) {
return true;
}
+ nsPIDOMWindowOuter* top = aWindow->GetScriptableTop();
+ if (top && top->GetAllowAutoplayOverride()) {
+ // Document's tab has been explicitly resumed by the user via the tab audio
+ // indicator. That is clear intent, so allow autoplay.
+ return true;
+ }
+
if (!aWindow->GetExtantDoc()) {
return false;
}
nsIDocument* approver = ApproverDocOf(WrapNotNull(aWindow->GetExtantDoc()));
if (nsContentUtils::IsExactSitePermAllow(approver->NodePrincipal(),
"autoplay-media")) {
// Autoplay permission has been granted already.
--- a/toolkit/content/browser-content.js
+++ b/toolkit/content/browser-content.js
@@ -521,16 +521,20 @@ var AudioPlaybackListener = {
break;
case "mediaControlPaused":
utils.mediaSuspend = suspendTypes.SUSPENDED_PAUSE_DISPOSABLE;
break;
case "mediaControlStopped":
utils.mediaSuspend = suspendTypes.SUSPENDED_STOP_DISPOSABLE;
break;
case "resumeMedia":
+ // Override the autoplay blocking logic, so that resuming media
+ // via the audio tab indicator will bypass the permission check
+ // and allow media to autoplay.
+ utils.allowAutoplayOverride = true;
utils.mediaSuspend = suspendTypes.NONE_SUSPENDED;
break;
default:
dump("Error : wrong media control msg!\n");
break;
}
},