--- a/dom/html/HTMLMediaElement.cpp
+++ b/dom/html/HTMLMediaElement.cpp
@@ -4051,30 +4051,36 @@ HTMLMediaElement::Play(ErrorResult& aRv)
MaybeDoLoad();
mPendingPlayPromises.AppendElement(promise);
return promise.forget();
}
if (AudioChannelAgentBlockedPlay()) {
LOG(LogLevel::Debug, ("%p play blocked by AudioChannelAgent.", this));
promise->MaybeReject(NS_ERROR_DOM_MEDIA_NOT_ALLOWED_ERR);
+ if (StaticPrefs::MediaBlockEventEnabled()) {
+ DispatchAsyncEvent(NS_LITERAL_STRING("blocked"));
+ }
return promise.forget();
}
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);
+ if (StaticPrefs::MediaBlockEventEnabled()) {
+ DispatchAsyncEvent(NS_LITERAL_STRING("blocked"));
+ }
break;
}
case Authorization::Prompt: {
// Prompt the user for permission to play.
mPendingPlayPromises.AppendElement(promise);
EnsureAutoplayRequested(handlingUserInput);
break;
}
@@ -7878,16 +7884,21 @@ HTMLMediaElement::AsyncRejectPendingPlay
mPaused = true;
DispatchAsyncEvent(NS_LITERAL_STRING("pause"));
}
if (mShuttingDown) {
return;
}
+ if (aError == NS_ERROR_DOM_MEDIA_NOT_ALLOWED_ERR &&
+ Preferences::GetBool("media.autoplay.block-event.enabled", false)) {
+ DispatchAsyncEvent(NS_LITERAL_STRING("blocked"));
+ }
+
nsCOMPtr<nsIRunnable> event = new nsResolveOrRejectPendingPlayPromisesRunner(
this, TakePendingPlayPromises(), aError);
mMainThreadEventTarget->Dispatch(event.forget());
}
void
HTMLMediaElement::GetEMEInfo(nsString& aEMEInfo)
new file mode 100644
--- /dev/null
+++ b/toolkit/content/tests/browser/browser_autoplay_policy_request_permission.js
@@ -0,0 +1,203 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+"use strict";
+
+ChromeUtils.import("resource:///modules/SitePermissions.jsm", this);
+
+const VIDEO_PAGE = "https://example.com/browser/toolkit/content/tests/browser/file_empty.html";
+
+add_task(() => {
+ return SpecialPowers.pushPrefEnv({"set": [
+ ["media.autoplay.enabled", false],
+ ["media.autoplay.enabled.user-gestures-needed", true],
+ ["media.autoplay.ask-permission", true],
+ ["media.autoplay.block-event.enabled", true],
+ ]});
+});
+
+// Runs a content script that creates an autoplay video.
+// browser: the browser to run the script in.
+// args: test case definition, required members {
+// mode: String, "autoplay attribute" or "call play".
+// }
+function loadAutoplayVideo(browser, args) {
+ return ContentTask.spawn(browser, args, async (args) => {
+ info("- create a new autoplay video -");
+ let video = content.document.createElement("video");
+ video.id = "v1";
+ video.didPlayPromise = new Promise((resolve, reject) => {
+ video.addEventListener("play", (e) => {
+ video.didPlay = true;
+ resolve();
+ }, {once: true});
+ video.addEventListener("blocked", (e) => {
+ video.didPlay = false;
+ resolve();
+ }, {once: true});
+ });
+ if (args.mode == "autoplay attribute") {
+ info("autoplay attribute set to true");
+ video.autoplay = true;
+ } else if (args.mode == "call play") {
+ info("will call play() when reached loadedmetadata");
+ video.addEventListener("loadedmetadata", (e) => {
+ video.play().then(
+ () => {
+ info("video play() resolved");
+ },
+ () => {
+ info("video play() rejected");
+ });
+ }, {once: true});
+ } else {
+ ok(false, "Invalid 'mode' arg");
+ }
+ video.src = "gizmo.mp4";
+ content.document.body.appendChild(video);
+ });
+}
+
+// Runs a content script that checks whether the video created by
+// loadAutoplayVideo() started playing.
+// Parameters:
+// browser: the browser to run the script in.
+// args: test case definition, required members {
+// name: String, description of test.
+// mode: String, "autoplay attribute" or "call play".
+// shouldPlay: boolean, whether video should play.
+// }
+function checkVideoDidPlay(browser, args) {
+ return ContentTask.spawn(browser, args, async (args) => {
+ let video = content.document.getElementById("v1");
+ await video.didPlayPromise;
+ is(video.didPlay, args.shouldPlay,
+ args.name + " should " + (!args.shouldPlay ? "not " : "") + "be able to autoplay");
+ video.src = "";
+ content.document.body.remove(video);
+ });
+}
+
+async function testAutoplayExistingPermission(args) {
+ info("- Starting '" + args.name + "' -");
+ await BrowserTestUtils.withNewTab({
+ gBrowser,
+ url: VIDEO_PAGE,
+ }, async (browser) => {
+ let promptShowing = () =>
+ PopupNotifications.getNotification("autoplay-media", browser);
+
+ SitePermissions.set(browser.currentURI, "autoplay-media", args.permission);
+ ok(!promptShowing(), "Should not be showing permission prompt yet");
+
+ await loadAutoplayVideo(browser, args);
+ await checkVideoDidPlay(browser, args);
+
+ // Reset permission.
+ SitePermissions.remove(browser.currentURI, "autoplay-media");
+
+ info("- Finished '" + args.name + "' -");
+ });
+}
+
+// Test the simple ALLOW/BLOCK cases; when permission is already set to ALLOW,
+// we shoud be able to autoplay via calling play(), or via the autoplay attribute,
+// and when it's set to BLOCK, we should not.
+add_task(async () => {
+ await testAutoplayExistingPermission({
+ name: "Prexisting allow permission autoplay attribute",
+ permission: SitePermissions.ALLOW,
+ shouldPlay: true,
+ mode: "autoplay attribute",
+ });
+ await testAutoplayExistingPermission({
+ name: "Prexisting allow permission call play",
+ permission: SitePermissions.ALLOW,
+ shouldPlay: true,
+ mode: "call play",
+ });
+ await testAutoplayExistingPermission({
+ name: "Prexisting block permission autoplay attribute",
+ permission: SitePermissions.BLOCK,
+ shouldPlay: false,
+ mode: "autoplay attribute",
+ });
+ await testAutoplayExistingPermission({
+ name: "Prexisting block permission call play",
+ permission: SitePermissions.BLOCK,
+ shouldPlay: false,
+ mode: "call play",
+ });
+});
+
+async function testAutoplayUnknownPermission(args) {
+ info("- Starting '" + args.name + "' -");
+ info("- open new tab -");
+
+ await BrowserTestUtils.withNewTab({
+ gBrowser,
+ url: VIDEO_PAGE,
+ }, async (browser) => {
+ let promptShowing = () =>
+ PopupNotifications.getNotification("autoplay-media", browser);
+
+ // Set this site to ask permission to autoplay.
+ SitePermissions.set(browser.currentURI, "autoplay-media", SitePermissions.UNKNOWN);
+ ok(!promptShowing(), "Should not be showing permission prompt");
+
+ let popupshown = BrowserTestUtils.waitForEvent(PopupNotifications.panel, "popupshown");
+ await loadAutoplayVideo(browser, args);
+
+ info("Awaiting popupshown");
+ await popupshown;
+ ok(promptShowing(), "Should now be showing permission prompt");
+
+ // Click the appropriate doorhanger button.
+ if (args.button == "allow") {
+ info("Clicking allow button");
+ PopupNotifications.panel.firstElementChild.button.click();
+ } else if (args.button == "block") {
+ info("Clicking block button");
+ PopupNotifications.panel.firstChild.secondaryButton.click();
+ } else {
+ ok(false, "Invalid button field");
+ }
+ // Check that the video started playing.
+ await checkVideoDidPlay(browser, args);
+
+ // Reset permission.
+ SitePermissions.remove(browser.currentURI, "autoplay-media");
+ info("- Finished '" + args.name + "' -");
+ });
+}
+
+// Test the permission UNKNOWN case; we should prompt for permission, and
+// test pressing approve/block in both the autoplay attribute and call
+// play case.
+add_task(async () => {
+ await testAutoplayUnknownPermission({
+ name: "Unknown permission click allow autoplay attribute",
+ button: "allow",
+ shouldPlay: true,
+ mode: "autoplay attribute",
+ });
+ await testAutoplayUnknownPermission({
+ name: "Unknown permission click allow call play",
+ button: "allow",
+ shouldPlay: true,
+ mode: "call play",
+ });
+ await testAutoplayUnknownPermission({
+ name: "Unknown permission click block autoplay attribute",
+ button: "block",
+ shouldPlay: false,
+ mode: "autoplay attribute",
+ });
+ await testAutoplayUnknownPermission({
+ name: "Unknown permission click block call play",
+ button: "block",
+ shouldPlay: false,
+ mode: "call play",
+ });
+});