Bug 1773551 - part2 : implement the navigator autoplay policy API. r=media-playback-reviewers,webidl,smaug,padenot

Differential Revision: https://phabricator.services.mozilla.com/D164750
This commit is contained in:
alwu 2022-12-19 20:57:55 +00:00
parent 60bbc8ab3d
commit db2a9a2552
6 changed files with 174 additions and 7 deletions

View File

@ -91,7 +91,6 @@
#include "nsJSUtils.h"
#include "mozilla/dom/NavigatorBinding.h"
#include "mozilla/dom/Promise.h"
#include "nsIUploadChannel2.h"
@ -113,6 +112,9 @@
#include "mozilla/dom/WindowGlobalChild.h"
#include "mozilla/intl/LocaleService.h"
#include "mozilla/dom/AudioContext.h"
#include "mozilla/dom/HTMLMediaElement.h"
#include "AutoplayPolicy.h"
namespace mozilla::dom {
@ -2271,4 +2273,23 @@ bool Navigator::Webdriver() {
return false;
}
AutoplayPolicy Navigator::GetAutoplayPolicy(AutoplayPolicyMediaType aType) {
if (!mWindow) {
return AutoplayPolicy::Disallowed;
}
nsCOMPtr<Document> doc = mWindow->GetExtantDoc();
if (!doc) {
return AutoplayPolicy::Disallowed;
}
return media::AutoplayPolicy::GetAutoplayPolicy(aType, *doc);
}
AutoplayPolicy Navigator::GetAutoplayPolicy(HTMLMediaElement& aElement) {
return media::AutoplayPolicy::GetAutoplayPolicy(aElement);
}
AutoplayPolicy Navigator::GetAutoplayPolicy(AudioContext& aContext) {
return media::AutoplayPolicy::GetAutoplayPolicy(aContext);
}
} // namespace mozilla::dom

View File

@ -11,6 +11,7 @@
#include "mozilla/dom/AddonManagerBinding.h"
#include "mozilla/dom/BindingDeclarations.h"
#include "mozilla/dom/Fetch.h"
#include "mozilla/dom/NavigatorBinding.h"
#include "mozilla/dom/Nullable.h"
#include "nsWrapperCache.h"
#include "nsHashKeys.h"
@ -43,6 +44,8 @@ class DOMRequest;
class CredentialsContainer;
class Clipboard;
class LockManager;
class HTMLMediaElement;
class AudioContext;
} // namespace dom
namespace webgpu {
class Instance;
@ -244,6 +247,12 @@ class Navigator final : public nsISupports, public nsWrapperCache {
bool HasCreatedMediaSession() const;
// Following methods are for the Autoplay Policy Detection API.
// https://w3c.github.io/autoplay/#autoplay-detection-methods
AutoplayPolicy GetAutoplayPolicy(AutoplayPolicyMediaType aType);
AutoplayPolicy GetAutoplayPolicy(HTMLMediaElement& aElement);
AutoplayPolicy GetAutoplayPolicy(AudioContext& aContext);
private:
void ValidateShareData(const ShareData& aData, ErrorResult& aRv);
RefPtr<MediaKeySystemAccessManager> mMediaKeySystemAccessManager;

View File

@ -11,6 +11,7 @@
#include "mozilla/dom/FeaturePolicyUtils.h"
#include "mozilla/dom/HTMLMediaElement.h"
#include "mozilla/dom/HTMLMediaElementBinding.h"
#include "mozilla/dom/NavigatorBinding.h"
#include "mozilla/dom/UserActivation.h"
#include "mozilla/dom/WindowContext.h"
#include "mozilla/Logging.h"
@ -192,7 +193,8 @@ static bool IsGVAutoplayRequestAllowed(nsPIDOMWindowInner* aWindow,
return status == GVAutoplayRequestStatus::eALLOWED;
}
static bool IsGVAutoplayRequestAllowed(const HTMLMediaElement& aElement) {
static bool IsGVAutoplayRequestAllowed(const HTMLMediaElement& aElement,
RType aType) {
// On GV, blocking model is the first thing we would check inside Gecko, and
// if the media is not allowed by that, then we would check the response from
// the embedding app to decide the final result.
@ -204,17 +206,16 @@ static bool IsGVAutoplayRequestAllowed(const HTMLMediaElement& aElement) {
if (!window) {
return false;
}
const RType type =
IsMediaElementInaudible(aElement) ? RType::eINAUDIBLE : RType::eAUDIBLE;
return IsGVAutoplayRequestAllowed(window, type);
return IsGVAutoplayRequestAllowed(window, aType);
}
#endif
static bool IsAllowedToPlayInternal(const HTMLMediaElement& aElement) {
#if defined(MOZ_WIDGET_ANDROID)
if (StaticPrefs::media_geckoview_autoplay_request()) {
return IsGVAutoplayRequestAllowed(aElement);
return IsGVAutoplayRequestAllowed(
aElement, IsMediaElementInaudible(aElement) ? RType::eINAUDIBLE
: RType::eAUDIBLE);
}
#endif
bool isInaudible = IsMediaElementInaudible(aElement);
@ -359,4 +360,96 @@ bool AutoplayPolicyTelemetryUtils::WouldBeAllowedToPlayIfAutoplayDisabled(
return IsAudioContextAllowedToPlay(aContext);
}
/* static */
dom::AutoplayPolicy AutoplayPolicy::GetAutoplayPolicy(
const dom::HTMLMediaElement& aElement) {
// Note, the site permission can contain following values :
// - UNKNOWN_ACTION : no permission set for this site
// - ALLOW_ACTION : allowed to autoplay
// - DENY_ACTION : allowed inaudible autoplay, disallowed inaudible autoplay
// - nsIAutoplay::BLOCKED_ALL : autoplay disallowed
// and the global permissions would be nsIAutoplay::{BLOCKED, ALLOWED,
// BLOCKED_ALL}
const uint32_t sitePermission =
SiteAutoplayPerm(aElement.OwnerDoc()->GetInnerWindow());
const uint32_t globalPermission = DefaultAutoplayBehaviour();
const bool isAllowedToPlayByBlockingModel =
IsAllowedToPlayByBlockingModel(aElement);
AUTOPLAY_LOG(
"IsAllowedToPlay(element), sitePermission=%d, globalPermission=%d, "
"isAllowedToPlayByBlockingModel=%d",
sitePermission, globalPermission, isAllowedToPlayByBlockingModel);
#if defined(MOZ_WIDGET_ANDROID)
if (StaticPrefs::media_geckoview_autoplay_request()) {
if (IsGVAutoplayRequestAllowed(aElement, RType::eAUDIBLE)) {
return dom::AutoplayPolicy::Allowed;
} else if (IsGVAutoplayRequestAllowed(aElement, RType::eINAUDIBLE)) {
return isAllowedToPlayByBlockingModel
? dom::AutoplayPolicy::Allowed
: dom::AutoplayPolicy::Allowed_muted;
} else {
return isAllowedToPlayByBlockingModel ? dom::AutoplayPolicy::Allowed
: dom::AutoplayPolicy::Disallowed;
}
}
#endif
// These are situations when an element is allowed to autoplay
// 1. The site permission is explicitly allowed
// 2. The global permission is allowed, and the site isn't explicitly
// disallowed
// 3. The blocking model is explicitly allowed this element
if (sitePermission == nsIPermissionManager::ALLOW_ACTION ||
(globalPermission == nsIAutoplay::ALLOWED &&
(sitePermission != nsIPermissionManager::DENY_ACTION &&
sitePermission != nsIAutoplay::BLOCKED_ALL)) ||
isAllowedToPlayByBlockingModel) {
return dom::AutoplayPolicy::Allowed;
}
// These are situations when a element is allowed to autoplay only when it's
// inaudible.
// 1. The site permission is block-audible-autoplay
// 2. The global permission is block-audible-autoplay, and the site permission
// isn't block-all-autoplay
if (sitePermission == nsIPermissionManager::DENY_ACTION ||
(globalPermission == nsIAutoplay::BLOCKED &&
sitePermission != nsIAutoplay::BLOCKED_ALL)) {
return dom::AutoplayPolicy::Allowed_muted;
}
return dom::AutoplayPolicy::Disallowed;
}
/* static */
dom::AutoplayPolicy AutoplayPolicy::GetAutoplayPolicy(
const dom::AudioContext& aContext) {
if (AutoplayPolicy::IsAllowedToPlay(aContext)) {
return dom::AutoplayPolicy::Allowed;
}
return dom::AutoplayPolicy::Disallowed;
}
/* static */
dom::AutoplayPolicy AutoplayPolicy::GetAutoplayPolicy(
const dom::AutoplayPolicyMediaType& aType, const dom::Document& aDoc) {
dom::DocumentAutoplayPolicy policy = AutoplayPolicy::IsAllowedToPlay(aDoc);
// https://w3c.github.io/autoplay/#query-by-a-media-type
if (aType == dom::AutoplayPolicyMediaType::Audiocontext) {
return policy == dom::DocumentAutoplayPolicy::Allowed
? dom::AutoplayPolicy::Allowed
: dom::AutoplayPolicy::Disallowed;
}
MOZ_ASSERT(aType == dom::AutoplayPolicyMediaType::Mediaelement);
if (policy == dom::DocumentAutoplayPolicy::Allowed) {
return dom::AutoplayPolicy::Allowed;
}
if (policy == dom::DocumentAutoplayPolicy::Allowed_muted) {
return dom::AutoplayPolicy::Allowed_muted;
}
return dom::AutoplayPolicy::Disallowed;
}
} // namespace mozilla::media

View File

@ -16,6 +16,8 @@ namespace mozilla::dom {
class HTMLMediaElement;
class AudioContext;
class Document;
enum class AutoplayPolicy : uint8_t;
enum class AutoplayPolicyMediaType : uint8_t;
enum class DocumentAutoplayPolicy : uint8_t;
} // namespace mozilla::dom
@ -48,6 +50,18 @@ class AutoplayPolicy {
// Return the value of the autoplay permission for given principal. The return
// value can be 0=unknown, 1=allow, 2=block audio, 5=block audio and video.
static uint32_t GetSiteAutoplayPermission(nsIPrincipal* aPrincipal);
// Following methods are used for the internal implementation for the Autoplay
// Policy Detection API, the public JS interfaces are in exposed on Navigator.
// https://w3c.github.io/autoplay/#autoplay-detection-methods
static dom::AutoplayPolicy GetAutoplayPolicy(
const dom::HTMLMediaElement& aElement);
static dom::AutoplayPolicy GetAutoplayPolicy(
const dom::AudioContext& aContext);
static dom::AutoplayPolicy GetAutoplayPolicy(
const dom::AutoplayPolicyMediaType& aType, const dom::Document& aDoc);
};
/**

View File

@ -344,3 +344,27 @@ interface mixin NavigatorLocks {
readonly attribute LockManager locks;
};
Navigator includes NavigatorLocks;
// https://w3c.github.io/autoplay/#autoplay-policy
enum AutoplayPolicy {
"allowed",
"allowed-muted",
"disallowed"
};
enum AutoplayPolicyMediaType {
"mediaelement",
"audiocontext"
};
// https://w3c.github.io/autoplay/#autoplay-detection-methods
partial interface Navigator {
[Pref="dom.media.autoplay-policy-detection.enabled"]
AutoplayPolicy getAutoplayPolicy(AutoplayPolicyMediaType type);
[Pref="dom.media.autoplay-policy-detection.enabled"]
AutoplayPolicy getAutoplayPolicy(HTMLMediaElement element);
[Pref="dom.media.autoplay-policy-detection.enabled"]
AutoplayPolicy getAutoplayPolicy(AudioContext context);
};

View File

@ -3000,6 +3000,12 @@
value: false
mirror: always
# Autoplay Policy Detection https://w3c.github.io/autoplay/
- name: dom.media.autoplay-policy-detection.enabled
type: RelaxedAtomicBool
value: @IS_NIGHTLY_BUILD@
mirror: always
# Media Session API
- name: dom.media.mediasession.enabled
type: bool