diff --git a/dom/html/HTMLMediaElement.cpp b/dom/html/HTMLMediaElement.cpp
index 3a30ec2a312e..dbf4026dcf89 100644
--- a/dom/html/HTMLMediaElement.cpp
+++ b/dom/html/HTMLMediaElement.cpp
@@ -1998,7 +1998,7 @@ HTMLMediaElement::Load()
HasSourceChildren(this),
EventStateManager::IsHandlingUserInput(),
HasAttr(kNameSpaceID_None, nsGkAtoms::autoplay),
- IsAllowedToPlay(),
+ AutoplayPolicy::IsAllowedToPlay(*this) == Authorization::Allowed,
OwnerDoc(),
DocumentOrigin(OwnerDoc()).get(),
OwnerDoc() ? OwnerDoc()->HasBeenUserGestureActivated() : 0,
@@ -2520,7 +2520,7 @@ HTMLMediaElement::UpdatePreloadAction()
PreloadAction nextAction = PRELOAD_UNDEFINED;
// If autoplay is set, or we're playing, we should always preload data,
// as we'll need it to play.
- if ((AutoplayPolicy::IsMediaElementAllowedToPlay(WrapNotNull(this)) &&
+ if ((AutoplayPolicy::IsAllowedToPlay(*this) == Authorization::Allowed &&
HasAttr(kNameSpaceID_None, nsGkAtoms::autoplay)) ||
!mPaused) {
nextAction = HTMLMediaElement::PRELOAD_ENOUGH;
@@ -3053,7 +3053,7 @@ HTMLMediaElement::PauseIfShouldNotBePlaying()
if (GetPaused()) {
return;
}
- if (!AutoplayPolicy::IsMediaElementAllowedToPlay(WrapNotNull(this))) {
+ if (AutoplayPolicy::IsAllowedToPlay(*this) != Authorization::Allowed) {
ErrorResult rv;
Pause(rv);
}
@@ -4053,26 +4053,32 @@ HTMLMediaElement::Play(ErrorResult& aRv)
return promise.forget();
}
- const bool handlingUserInput = EventStateManager::IsHandlingUserInput();
- if (IsAllowedToPlay()) {
- mPendingPlayPromises.AppendElement(promise);
- PlayInternal(handlingUserInput);
- UpdateCustomPolicyAfterPlayed();
- return promise.forget();
- }
-
- // Otherwise, not allowed to play. We may still be allowed to play if we
- // ask for and are granted permission by the user.
-
- if (!Preferences::GetBool("media.autoplay.ask-permission", false)) {
- LOG(LogLevel::Debug, ("%p play not allowed and prompting disabled.", this));
+ if (AudioChannelAgentBlockedPlay()) {
+ LOG(LogLevel::Debug, ("%p play blocked by AudioChannelAgent.", this));
promise->MaybeReject(NS_ERROR_DOM_MEDIA_NOT_ALLOWED_ERR);
return promise.forget();
}
- // Prompt the user for permission to play.
- mPendingPlayPromises.AppendElement(promise);
- EnsureAutoplayRequested(handlingUserInput);
+ const bool handlingUserInput = EventStateManager::IsHandlingUserInput();
+ switch (AutoplayPolicy::IsAllowedToPlay(*this)) {
+ case Authorization::Allowed: {
+ mPendingPlayPromises.AppendElement(promise);
+ PlayInternal(handlingUserInput);
+ UpdateCustomPolicyAfterPlayed();
+ break;
+ }
+ case Authorization::Blocked: {
+ LOG(LogLevel::Debug, ("%p play not blocked.", this));
+ promise->MaybeReject(NS_ERROR_DOM_MEDIA_NOT_ALLOWED_ERR);
+ break;
+ }
+ case Authorization::Prompt: {
+ // Prompt the user for permission to play.
+ mPendingPlayPromises.AppendElement(promise);
+ EnsureAutoplayRequested(handlingUserInput);
+ break;
+ }
+ }
return promise.forget();
}
@@ -4089,8 +4095,7 @@ HTMLMediaElement::EnsureAutoplayRequested(bool aHandlingUserInput)
return;
}
- RefPtr request =
- AutoplayPolicy::RequestFor(WrapNotNull(OwnerDoc()));
+ RefPtr request = AutoplayPolicy::RequestFor(*OwnerDoc());
if (!request) {
AsyncRejectPendingPlayPromises(NS_ERROR_DOM_INVALID_STATE_ERR);
return;
@@ -6114,7 +6119,8 @@ HTMLMediaElement::ChangeReadyState(nsMediaReadyState aState)
DispatchAsyncEvent(NS_LITERAL_STRING("canplay"));
if (!mPaused) {
if (mDecoder && !mPausedForInactiveDocumentOrChannel) {
- MOZ_ASSERT(IsAllowedToPlay());
+ MOZ_ASSERT(AutoplayPolicy::IsAllowedToPlay(*this) ==
+ Authorization::Allowed);
mDecoder->Play();
}
NotifyAboutPlaying();
@@ -7017,48 +7023,22 @@ HTMLMediaElement::UpdateAudioChannelPlayingState(bool aForcePlaying)
}
bool
-HTMLMediaElement::IsAllowedToPlay()
+HTMLMediaElement::AudioChannelAgentBlockedPlay()
{
- if (!AutoplayPolicy::IsMediaElementAllowedToPlay(WrapNotNull(this))) {
-#if defined(MOZ_WIDGET_ANDROID)
- nsContentUtils::DispatchChromeEvent(
- OwnerDoc(),
- static_cast(this),
- NS_LITERAL_STRING("MozAutoplayMediaBlocked"),
- CanBubble::eNo,
- Cancelable::eNo);
-#endif
+ if (!mAudioChannelWrapper) {
+ // If the mAudioChannelWrapper doesn't exist that means the CC happened.
LOG(LogLevel::Debug,
- ("%p %s AutoplayPolicy blocked autoplay.", this, __func__));
- return false;
- }
-
- LOG(LogLevel::Debug,
- ("%p %s AutoplayPolicy did not block autoplay.", this, __func__));
-
- // Check our custom playback policy.
- if (mAudioChannelWrapper) {
- // Note: SUSPENDED_PAUSE and SUSPENDED_BLOCK will be merged into one single
- // state.
- if (mAudioChannelWrapper->GetSuspendType() ==
- nsISuspendedTypes::SUSPENDED_PAUSE ||
- mAudioChannelWrapper->GetSuspendType() ==
- nsISuspendedTypes::SUSPENDED_BLOCK) {
- LOG(LogLevel::Debug,
- ("%p IsAllowedToPlay() returning false due to AudioChannelAgent.",
- this));
- return false;
- }
-
- LOG(LogLevel::Debug, ("%p IsAllowedToPlay() returning true.", this));
+ ("%p AudioChannelAgentBlockedPlay() returning true due to null "
+ "AudioChannelAgent.",
+ this));
return true;
}
- // If the mAudioChannelWrapper doesn't exist that means the CC happened.
- LOG(LogLevel::Debug,
- ("%p IsAllowedToPlay() returning false due to null AudioChannelAgent.",
- this));
- return false;
+ // Note: SUSPENDED_PAUSE and SUSPENDED_BLOCK will be merged into one single
+ // state.
+ const auto suspendType = mAudioChannelWrapper->GetSuspendType();
+ return suspendType == nsISuspendedTypes::SUSPENDED_PAUSE ||
+ suspendType == nsISuspendedTypes::SUSPENDED_BLOCK;
}
static const char*
diff --git a/dom/html/HTMLMediaElement.h b/dom/html/HTMLMediaElement.h
index 583097aca460..328e3f424b02 100644
--- a/dom/html/HTMLMediaElement.h
+++ b/dom/html/HTMLMediaElement.h
@@ -1284,7 +1284,7 @@ protected:
void AudioCaptureStreamChange(bool aCapture);
// A method to check whether the media element is allowed to start playback.
- bool IsAllowedToPlay();
+ bool AudioChannelAgentBlockedPlay();
// If the network state is empty and then we would trigger DoLoad().
void MaybeDoLoad();
diff --git a/dom/media/AutoplayPolicy.cpp b/dom/media/AutoplayPolicy.cpp
index 3f6421474489..96d293a99bb0 100644
--- a/dom/media/AutoplayPolicy.cpp
+++ b/dom/media/AutoplayPolicy.cpp
@@ -40,7 +40,7 @@ ApproverDocOf(const nsIDocument& aDocument)
}
static bool
-IsAllowedToPlay(nsPIDOMWindowInner* aWindow)
+IsWindowAllowedToPlay(nsPIDOMWindowInner* aWindow)
{
if (!aWindow) {
return false;
@@ -88,31 +88,36 @@ AutoplayPolicy::RequestFor(const nsIDocument& aDocument)
return window->GetAutoplayRequest();
}
-/* static */ bool
-AutoplayPolicy::IsMediaElementAllowedToPlay(NotNull aElement)
+/* static */ Authorization
+AutoplayPolicy::IsAllowedToPlay(const HTMLMediaElement& aElement)
{
if (Preferences::GetBool("media.autoplay.enabled")) {
- return true;
+ return Authorization::Allowed;
}
// TODO : this old way would be removed when user-gestures-needed becomes
// as a default option to block autoplay.
if (!Preferences::GetBool("media.autoplay.enabled.user-gestures-needed", false)) {
// If element is blessed, it would always be allowed to play().
- return aElement->IsBlessed() ||
- EventStateManager::IsHandlingUserInput();
+ return (aElement.IsBlessed() || EventStateManager::IsHandlingUserInput())
+ ? Authorization::Allowed
+ : Authorization::Blocked;
}
// Muted content
- if (aElement->Volume() == 0.0 || aElement->Muted()) {
- return true;
+ if (aElement.Volume() == 0.0 || aElement.Muted()) {
+ return Authorization::Allowed;
}
- if (IsAllowedToPlay(aElement->OwnerDoc()->GetInnerWindow())) {
- return true;
+ if (IsWindowAllowedToPlay(aElement.OwnerDoc()->GetInnerWindow())) {
+ return Authorization::Allowed;
}
- return false;
+ if (Preferences::GetBool("media.autoplay.ask-permission", false)) {
+ return Authorization::Prompt;
+ }
+
+ return Authorization::Blocked;
}
/* static */ bool
@@ -131,7 +136,7 @@ AutoplayPolicy::IsAudioContextAllowedToPlay(NotNull aContext)
return true;
}
- if (IsAllowedToPlay(aContext->GetOwner())) {
+ if (IsWindowAllowedToPlay(aContext->GetOwner())) {
return true;
}
diff --git a/dom/media/AutoplayPolicy.h b/dom/media/AutoplayPolicy.h
index 9fcbaee44ebe..0f765e76c12f 100644
--- a/dom/media/AutoplayPolicy.h
+++ b/dom/media/AutoplayPolicy.h
@@ -20,6 +20,13 @@ namespace dom {
class HTMLMediaElement;
class AudioContext;
+enum class Authorization
+{
+ Allowed,
+ Blocked,
+ Prompt
+};
+
/**
* AutoplayPolicy is used to manage autoplay logic for all kinds of media,
* including MediaElement, Web Audio and Web Speech.
@@ -36,7 +43,7 @@ class AutoplayPolicy
{
public:
// Returns whether a given media element is allowed to play.
- static bool IsMediaElementAllowedToPlay(NotNull aElement);
+ static Authorization IsAllowedToPlay(const HTMLMediaElement& aElement);
// Returns whether a given AudioContext is allowed to play.
static bool IsAudioContextAllowedToPlay(NotNull aContext);