Bug 1463919 - Move ask autoplay permission check into AutoplayPolicy. r=jya

MozReview-Commit-ID: KJcVI6gtGXw

--HG--
extra : rebase_source : a2ccd2da32d77708fdeb6ea6361975a7759cb18d
extra : source : 9b1c40f3e61ab351707f2d320ce8f87951e4868e
This commit is contained in:
Chris Pearce 2018-06-26 14:16:13 +12:00
Родитель b309d364b6
Коммит ea95e39bc9
4 изменённых файлов: 65 добавлений и 73 удалений

Просмотреть файл

@ -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<AutoplayRequest> request =
AutoplayPolicy::RequestFor(WrapNotNull(OwnerDoc()));
RefPtr<AutoplayRequest> 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<nsIContent*>(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*

Просмотреть файл

@ -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();

Просмотреть файл

@ -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<HTMLMediaElement*> 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<AudioContext*> aContext)
return true;
}
if (IsAllowedToPlay(aContext->GetOwner())) {
if (IsWindowAllowedToPlay(aContext->GetOwner())) {
return true;
}

Просмотреть файл

@ -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<HTMLMediaElement*> aElement);
static Authorization IsAllowedToPlay(const HTMLMediaElement& aElement);
// Returns whether a given AudioContext is allowed to play.
static bool IsAudioContextAllowedToPlay(NotNull<AudioContext*> aContext);