gecko-dev/dom/media/AutoplayPolicy.cpp
Chris Pearce 63dbf9d6f7 Bug 1470346 - Gesture activate all documents in tab, even across origins, upon user interaction. r=smaug
Sometimes when video is playing, a preroll ad plays, and that may be in a cross
origin iframe. If autoplay media is disabled, we require a user gesture in a
document before playback in that document is permitted, and we require each
origin to be gesture activated separately. So in the cross origin preroll video
add case, then the user will have to click once to unblock playback for the
cross origin ad, and then once the preroll ad finishes, the user will have to
click again to activate playback of the same origin content video.

This is a bad user experience.

So we should instead make gesture activation propagate up the doc tree
irrespective of crossing origins.  This way, when the user clicks to activate,
all documents in that tab are also also effectively gesture activated, and so
can autoplay.

MozReview-Commit-ID: 1HZQ5zkubR

--HG--
extra : rebase_source : d6b75732548cb1d73b9f82dce60a5e6e97d1da14
2018-06-22 11:52:20 +12:00

113 lines
3.2 KiB
C++

/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "AutoplayPolicy.h"
#include "mozilla/EventStateManager.h"
#include "mozilla/Preferences.h"
#include "mozilla/dom/AudioContext.h"
#include "mozilla/dom/HTMLMediaElement.h"
#include "mozilla/dom/HTMLMediaElementBinding.h"
#include "nsContentUtils.h"
#include "nsIDocument.h"
#include "MediaManager.h"
namespace mozilla {
namespace dom {
/* static */ bool
AutoplayPolicy::IsMediaElementAllowedToPlay(NotNull<HTMLMediaElement*> aElement)
{
if (Preferences::GetBool("media.autoplay.enabled")) {
return true;
}
// 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 elelement is blessed, it would always be allowed to play().
return aElement->IsBlessed() ||
EventStateManager::IsHandlingUserInput();
}
// 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) {
nsCOMPtr<nsPIDOMWindowInner> window = aElement->OwnerDoc()->GetInnerWindow();
if (window && manager->IsActivelyCapturingOrHasAPermission(window->WindowID())) {
return true;
}
}
// Muted content
if (aElement->Volume() == 0.0 || aElement->Muted()) {
return true;
}
// Whitelisted.
if (nsContentUtils::IsExactSitePermAllow(
aElement->NodePrincipal(), "autoplay-media")) {
return true;
}
// Activated by user gesture.
if (aElement->OwnerDoc()->HasBeenUserGestureActivated()) {
return true;
}
return false;
}
/* static */ bool
AutoplayPolicy::IsAudioContextAllowedToPlay(NotNull<AudioContext*> aContext)
{
if (Preferences::GetBool("media.autoplay.enabled")) {
return true;
}
if (!Preferences::GetBool("media.autoplay.enabled.user-gestures-needed", false)) {
return true;
}
// Offline context won't directly output sound to audio devices.
if (aContext->IsOffline()) {
return true;
}
nsPIDOMWindowInner* window = aContext->GetOwner();
if (!window) {
return false;
}
// 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) {
if (manager->IsActivelyCapturingOrHasAPermission(window->WindowID())) {
return true;
}
}
nsCOMPtr<nsIPrincipal> principal = aContext->GetParentObject()->AsGlobal()->PrincipalOrNull();
// Whitelisted.
if (principal &&
nsContentUtils::IsExactSitePermAllow(principal, "autoplay-media")) {
return true;
}
// Activated by user gesture.
if (window->GetExtantDoc()->HasBeenUserGestureActivated()) {
return true;
}
return false;
}
} // namespace dom
} // namespace mozilla