2017-11-21 19:33:16 +03:00
|
|
|
/* -*- 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"
|
2018-08-02 03:30:59 +03:00
|
|
|
#include "mozilla/Logging.h"
|
2017-11-21 19:33:16 +03:00
|
|
|
#include "mozilla/Preferences.h"
|
2017-12-13 23:05:35 +03:00
|
|
|
#include "mozilla/dom/AudioContext.h"
|
2018-10-09 15:22:19 +03:00
|
|
|
#include "mozilla/dom/FeaturePolicyUtils.h"
|
2017-11-21 19:33:16 +03:00
|
|
|
#include "mozilla/dom/HTMLMediaElement.h"
|
2018-02-15 06:23:32 +03:00
|
|
|
#include "mozilla/dom/HTMLMediaElementBinding.h"
|
2018-09-24 01:11:06 +03:00
|
|
|
#include "nsGlobalWindowInner.h"
|
2018-06-29 16:14:33 +03:00
|
|
|
#include "nsIAutoplay.h"
|
2018-04-30 08:40:50 +03:00
|
|
|
#include "nsContentUtils.h"
|
2019-01-02 16:05:23 +03:00
|
|
|
#include "mozilla/dom/Document.h"
|
2018-02-08 06:05:46 +03:00
|
|
|
#include "MediaManager.h"
|
2018-06-22 02:57:24 +03:00
|
|
|
#include "nsIDocShell.h"
|
|
|
|
#include "nsIDocShellTreeItem.h"
|
|
|
|
#include "nsPIDOMWindow.h"
|
2017-11-21 19:33:16 +03:00
|
|
|
|
2018-08-02 03:30:59 +03:00
|
|
|
mozilla::LazyLogModule gAutoplayPermissionLog("Autoplay");
|
|
|
|
|
|
|
|
#define AUTOPLAY_LOG(msg, ...) \
|
|
|
|
MOZ_LOG(gAutoplayPermissionLog, LogLevel::Debug, (msg, ##__VA_ARGS__))
|
|
|
|
|
2017-11-21 19:33:16 +03:00
|
|
|
namespace mozilla {
|
|
|
|
namespace dom {
|
|
|
|
|
2019-01-02 16:05:23 +03:00
|
|
|
static Document* ApproverDocOf(const Document& aDocument) {
|
2018-06-22 02:57:24 +03:00
|
|
|
nsCOMPtr<nsIDocShell> ds = aDocument.GetDocShell();
|
|
|
|
if (!ds) {
|
|
|
|
return nullptr;
|
2017-11-21 19:33:16 +03:00
|
|
|
}
|
|
|
|
|
2018-06-22 02:57:24 +03:00
|
|
|
nsCOMPtr<nsIDocShellTreeItem> rootTreeItem;
|
|
|
|
ds->GetSameTypeRootTreeItem(getter_AddRefs(rootTreeItem));
|
|
|
|
if (!rootTreeItem) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
return rootTreeItem->GetDocument();
|
|
|
|
}
|
|
|
|
|
2018-09-24 01:11:06 +03:00
|
|
|
static bool IsActivelyCapturingOrHasAPermission(nsPIDOMWindowInner* aWindow) {
|
|
|
|
// Pages which have been granted permission to capture WebRTC camera or
|
|
|
|
// microphone or screen are assumed to be trusted, and are allowed to
|
|
|
|
// autoplay.
|
|
|
|
if (MediaManager::GetIfExists()) {
|
|
|
|
return MediaManager::GetIfExists()->IsActivelyCapturingOrHasAPermission(
|
|
|
|
aWindow->WindowID());
|
|
|
|
}
|
|
|
|
|
|
|
|
auto principal = nsGlobalWindowInner::Cast(aWindow)->GetPrincipal();
|
|
|
|
return (nsContentUtils::IsExactSitePermAllow(principal, "camera") ||
|
|
|
|
nsContentUtils::IsExactSitePermAllow(principal, "microphone") ||
|
|
|
|
nsContentUtils::IsExactSitePermAllow(principal, "screen"));
|
|
|
|
}
|
|
|
|
|
2018-06-26 05:16:13 +03:00
|
|
|
static bool IsWindowAllowedToPlay(nsPIDOMWindowInner* aWindow) {
|
2018-06-22 02:57:24 +03:00
|
|
|
if (!aWindow) {
|
|
|
|
return false;
|
2018-05-18 04:43:00 +03:00
|
|
|
}
|
|
|
|
|
2018-09-24 01:11:06 +03:00
|
|
|
if (IsActivelyCapturingOrHasAPermission(aWindow)) {
|
|
|
|
AUTOPLAY_LOG(
|
|
|
|
"Allow autoplay as document has camera or microphone or screen"
|
|
|
|
" permission.");
|
2018-06-22 02:57:24 +03:00
|
|
|
return true;
|
2018-02-08 06:05:46 +03:00
|
|
|
}
|
|
|
|
|
2018-06-22 02:57:24 +03:00
|
|
|
if (!aWindow->GetExtantDoc()) {
|
|
|
|
return false;
|
2017-11-30 05:50:21 +03:00
|
|
|
}
|
|
|
|
|
2018-10-09 15:22:19 +03:00
|
|
|
// Here we are checking whether the current document is blocked via
|
|
|
|
// feature-policy, and further down we walk up the doc tree to the top level
|
|
|
|
// content document and check permissions etc on the top level content
|
|
|
|
// document. FeaturePolicy propagates the permission to any sub-documents if
|
|
|
|
// they don't have special directives.
|
|
|
|
if (!FeaturePolicyUtils::IsFeatureAllowed(aWindow->GetExtantDoc(),
|
|
|
|
NS_LITERAL_STRING("autoplay"))) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2019-01-02 16:05:23 +03:00
|
|
|
Document* approver = ApproverDocOf(*aWindow->GetExtantDoc());
|
2018-11-12 20:39:51 +03:00
|
|
|
if (!approver) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2018-06-22 02:57:24 +03:00
|
|
|
if (nsContentUtils::IsExactSitePermAllow(approver->NodePrincipal(),
|
|
|
|
"autoplay-media")) {
|
2018-11-16 21:27:00 +03:00
|
|
|
AUTOPLAY_LOG(
|
|
|
|
"Allow autoplay as document has permanent autoplay permission.");
|
2018-04-30 08:40:50 +03:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2018-06-22 02:57:24 +03:00
|
|
|
if (approver->HasBeenUserGestureActivated()) {
|
2018-08-02 03:30:59 +03:00
|
|
|
AUTOPLAY_LOG("Allow autoplay as document activated by user gesture.");
|
2018-04-30 08:40:50 +03:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2018-07-10 20:37:33 +03:00
|
|
|
if (approver->IsExtensionPage()) {
|
2018-08-02 03:30:59 +03:00
|
|
|
AUTOPLAY_LOG("Allow autoplay as in extension document.");
|
2018-07-10 20:37:33 +03:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2018-04-30 08:40:50 +03:00
|
|
|
return false;
|
2017-11-21 19:33:16 +03:00
|
|
|
}
|
|
|
|
|
2018-06-29 16:14:33 +03:00
|
|
|
static uint32_t DefaultAutoplayBehaviour() {
|
|
|
|
int prefValue =
|
|
|
|
Preferences::GetInt("media.autoplay.default", nsIAutoplay::ALLOWED);
|
2019-01-07 21:41:03 +03:00
|
|
|
if (prefValue < nsIAutoplay::ALLOWED || prefValue > nsIAutoplay::BLOCKED) {
|
|
|
|
// Invalid pref values are just converted to BLOCKED.
|
|
|
|
return nsIAutoplay::BLOCKED;
|
2017-12-13 23:05:35 +03:00
|
|
|
}
|
2018-06-29 16:14:33 +03:00
|
|
|
return prefValue;
|
|
|
|
}
|
2017-12-13 23:05:35 +03:00
|
|
|
|
2018-07-18 06:34:04 +03:00
|
|
|
static bool IsMediaElementAllowedToPlay(const HTMLMediaElement& aElement) {
|
2018-12-14 00:31:42 +03:00
|
|
|
const bool isAllowedMuted =
|
|
|
|
Preferences::GetBool("media.autoplay.allow-muted", true);
|
|
|
|
if ((aElement.Volume() == 0.0 || aElement.Muted()) && isAllowedMuted) {
|
2018-08-02 03:30:59 +03:00
|
|
|
AUTOPLAY_LOG("Allow muted media %p to autoplay.", &aElement);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (IsWindowAllowedToPlay(aElement.OwnerDoc()->GetInnerWindow())) {
|
2018-11-16 21:27:00 +03:00
|
|
|
AUTOPLAY_LOG("Autoplay allowed as window is allowed to play, media %p.",
|
|
|
|
&aElement);
|
2018-08-02 03:30:59 +03:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2019-01-02 16:05:23 +03:00
|
|
|
Document* topDocument = ApproverDocOf(*aElement.OwnerDoc());
|
|
|
|
if (topDocument &&
|
|
|
|
topDocument->MediaDocumentKind() == Document::MediaDocumentKind::Video) {
|
2018-08-23 02:31:58 +03:00
|
|
|
AUTOPLAY_LOG("Allow video document %p to autoplay", &aElement);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!aElement.HasAudio() &&
|
2018-12-14 00:31:42 +03:00
|
|
|
aElement.ReadyState() >= HTMLMediaElement_Binding::HAVE_METADATA &&
|
|
|
|
isAllowedMuted) {
|
2018-08-23 02:31:58 +03:00
|
|
|
AUTOPLAY_LOG("Allow media %p without audio track to autoplay", &aElement);
|
2018-08-02 03:30:59 +03:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
2018-07-18 06:34:04 +03:00
|
|
|
}
|
|
|
|
|
2019-01-09 00:52:31 +03:00
|
|
|
static bool IsAudioContextAllowedToPlay(const AudioContext& aContext) {
|
|
|
|
// Offline context won't directly output sound to audio devices.
|
|
|
|
return aContext.IsOffline() ||
|
|
|
|
IsWindowAllowedToPlay(aContext.GetParentObject());
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool IsEnableBlockingWebAudioByUserGesturePolicy() {
|
|
|
|
return DefaultAutoplayBehaviour() != nsIAutoplay::ALLOWED &&
|
|
|
|
Preferences::GetBool("media.autoplay.block-webaudio", false) &&
|
|
|
|
Preferences::GetBool("media.autoplay.enabled.user-gestures-needed",
|
|
|
|
false);
|
|
|
|
}
|
|
|
|
|
2018-07-18 06:34:04 +03:00
|
|
|
/* static */ bool AutoplayPolicy::WouldBeAllowedToPlayIfAutoplayDisabled(
|
|
|
|
const HTMLMediaElement& aElement) {
|
|
|
|
return IsMediaElementAllowedToPlay(aElement);
|
|
|
|
}
|
|
|
|
|
2019-01-09 00:52:31 +03:00
|
|
|
/* static */ bool AutoplayPolicy::WouldBeAllowedToPlayIfAutoplayDisabled(
|
|
|
|
const AudioContext& aContext) {
|
|
|
|
return IsAudioContextAllowedToPlay(aContext);
|
|
|
|
}
|
|
|
|
|
2018-06-29 16:14:33 +03:00
|
|
|
/* static */ bool AutoplayPolicy::IsAllowedToPlay(
|
|
|
|
const HTMLMediaElement& aElement) {
|
|
|
|
const uint32_t autoplayDefault = DefaultAutoplayBehaviour();
|
2018-06-22 02:57:24 +03:00
|
|
|
// TODO : this old way would be removed when user-gestures-needed becomes
|
|
|
|
// as a default option to block autoplay.
|
2017-12-13 23:05:35 +03:00
|
|
|
if (!Preferences::GetBool("media.autoplay.enabled.user-gestures-needed",
|
|
|
|
false)) {
|
2018-06-22 02:57:24 +03:00
|
|
|
// If element is blessed, it would always be allowed to play().
|
2018-06-29 16:14:33 +03:00
|
|
|
return (autoplayDefault == nsIAutoplay::ALLOWED || aElement.IsBlessed() ||
|
2018-07-23 18:43:08 +03:00
|
|
|
EventStateManager::IsHandlingUserInput());
|
2017-12-13 23:05:35 +03:00
|
|
|
}
|
|
|
|
|
2018-07-23 18:43:08 +03:00
|
|
|
if (IsMediaElementAllowedToPlay(aElement)) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
const bool result = IsMediaElementAllowedToPlay(aElement) ||
|
|
|
|
autoplayDefault == nsIAutoplay::ALLOWED;
|
2018-06-26 05:16:13 +03:00
|
|
|
|
2018-08-02 03:30:59 +03:00
|
|
|
AUTOPLAY_LOG("IsAllowedToPlay, mediaElement=%p, isAllowToPlay=%s", &aElement,
|
2018-09-26 03:49:54 +03:00
|
|
|
result ? "allowed" : "blocked");
|
2018-07-23 18:43:08 +03:00
|
|
|
|
2018-08-02 03:30:59 +03:00
|
|
|
return result;
|
2018-06-22 02:57:24 +03:00
|
|
|
}
|
|
|
|
|
2018-09-13 19:51:07 +03:00
|
|
|
/* static */ bool AutoplayPolicy::IsAllowedToPlay(
|
|
|
|
const AudioContext& aContext) {
|
2019-01-09 00:52:31 +03:00
|
|
|
if (!IsEnableBlockingWebAudioByUserGesturePolicy()) {
|
2018-06-22 02:57:24 +03:00
|
|
|
return true;
|
2018-04-27 20:13:40 +03:00
|
|
|
}
|
|
|
|
|
2019-01-09 00:52:31 +03:00
|
|
|
return IsAudioContextAllowedToPlay(aContext);
|
2017-12-13 23:05:35 +03:00
|
|
|
}
|
|
|
|
|
2019-01-15 03:13:34 +03:00
|
|
|
/* static */ DocumentAutoplayPolicy AutoplayPolicy::IsAllowedToPlay(
|
|
|
|
const Document& aDocument) {
|
|
|
|
if (DefaultAutoplayBehaviour() == nsIAutoplay::ALLOWED ||
|
|
|
|
IsWindowAllowedToPlay(aDocument.GetInnerWindow())) {
|
|
|
|
return DocumentAutoplayPolicy::Allowed;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (StaticPrefs::MediaAutoplayAllowMuted()) {
|
|
|
|
return DocumentAutoplayPolicy::Allowed_muted;
|
|
|
|
}
|
|
|
|
|
|
|
|
return DocumentAutoplayPolicy::Disallowed;
|
|
|
|
}
|
|
|
|
|
2017-11-21 19:33:16 +03:00
|
|
|
} // namespace dom
|
2018-02-15 06:23:32 +03:00
|
|
|
} // namespace mozilla
|