--- a/dom/html/HTMLMediaElement.cpp
+++ b/dom/html/HTMLMediaElement.cpp
@@ -4068,16 +4068,19 @@ HTMLMediaElement::Play(ErrorResult& aRv)
}
// 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));
promise->MaybeReject(NS_ERROR_DOM_MEDIA_NOT_ALLOWED_ERR);
+ if (Preferences::GetBool("media.autoplay.block-event.enabled", false)) {
+ DispatchAsyncEvent(NS_LITERAL_STRING("blocked"));
+ }
return promise.forget();
}
// Prompt the user for permission to play.
mPendingPlayPromises.AppendElement(promise);
EnsureAutoplayRequested(handlingUserInput);
return promise.forget();
}
@@ -7898,16 +7901,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,196 @@
+/* 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/. */
+
+"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],
+ ]});
+});
+
+// Content script that creates an autoplay video.
+async function loadAutoplayVideo(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);
+}
+
+// Content script that checks whether the video created by loadAutoplayVideo()
+// started playing.
+async function checkVideoDidPlay(args) {
+ let video = content.document.getElementById("v1");
+ await video.didPlayPromise;
+ is(video.didPlay, args.shouldPlay,
+ args.test + " mode=" + args.mode + " button=" + args.button +
+ " video should " + (!args.shouldPlay ? "not " : "") + "be able to autoplay");
+ video.src = "";
+ content.document.body.remove(video);
+}
+
+async function testAutoplayExistingPermission(args) {
+ info("- Starting '" + args.name + "' -");
+ info("- open new tab -");
+
+ let tab = await BrowserTestUtils.openNewForegroundTab(window.gBrowser,
+ "about:blank");
+ tab.linkedBrowser.loadURI(VIDEO_PAGE);
+ await BrowserTestUtils.browserLoaded(tab.linkedBrowser);
+
+ let promptShowing = () =>
+ PopupNotifications.getNotification("autoplay-media", tab.linkedBrowser);
+
+ SitePermissions.set(gBrowser.currentURI, "autoplay-media", args.permission);
+ ok(!promptShowing(), "Should not be showing permission prompt yet");
+
+ await ContentTask.spawn(tab.linkedBrowser, args, loadAutoplayVideo);
+ await ContentTask.spawn(tab.linkedBrowser, args, checkVideoDidPlay);
+
+ // Reset permission.
+ SitePermissions.remove(gBrowser.currentURI, "autoplay-media");
+
+ info("- remove tab -");
+ BrowserTestUtils.removeTab(tab);
+ 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 -");
+
+ let tab = await BrowserTestUtils.openNewForegroundTab(window.gBrowser,
+ "about:blank");
+ tab.linkedBrowser.loadURI(VIDEO_PAGE);
+ await BrowserTestUtils.browserLoaded(tab.linkedBrowser);
+
+ let promptShowing = () =>
+ PopupNotifications.getNotification("autoplay-media", tab.linkedBrowser);
+
+ // Set this site to ask permission to autoplay.
+ SitePermissions.set(gBrowser.currentURI, "autoplay-media", SitePermissions.UNKNOWN);
+ ok(!promptShowing(), "Should not be showing permission prompt");
+
+ let popupshown = BrowserTestUtils.waitForEvent(PopupNotifications.panel, "popupshown");
+ await ContentTask.spawn(tab.linkedBrowser, args, loadAutoplayVideo);
+
+ 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 ContentTask.spawn(tab.linkedBrowser, args, checkVideoDidPlay);
+
+ // Reset permission.
+ SitePermissions.remove(gBrowser.currentURI, "autoplay-media");
+
+ info("- remove tab -");
+ BrowserTestUtils.removeTab(tab);
+ 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",
+ });
+});