summaryrefslogtreecommitdiffstats
path: root/dom/media/autoplay/test/browser
diff options
context:
space:
mode:
Diffstat (limited to 'dom/media/autoplay/test/browser')
-rw-r--r--dom/media/autoplay/test/browser/audio.oggbin0 -> 14290 bytes
-rw-r--r--dom/media/autoplay/test/browser/browser.ini27
-rw-r--r--dom/media/autoplay/test/browser/browser_autoplay_policy_detection_click_to_play.js120
-rw-r--r--dom/media/autoplay/test/browser/browser_autoplay_policy_detection_global_and_site_sticky.js168
-rw-r--r--dom/media/autoplay/test/browser/browser_autoplay_policy_detection_global_sticky.js105
-rw-r--r--dom/media/autoplay/test/browser/browser_autoplay_policy_play_twice.js54
-rw-r--r--dom/media/autoplay/test/browser/browser_autoplay_policy_request_permission.js269
-rw-r--r--dom/media/autoplay/test/browser/browser_autoplay_policy_touchScroll.js103
-rw-r--r--dom/media/autoplay/test/browser/browser_autoplay_policy_user_gestures.js277
-rw-r--r--dom/media/autoplay/test/browser/browser_autoplay_policy_webRTC_permission.js67
-rw-r--r--dom/media/autoplay/test/browser/browser_autoplay_policy_web_audio.js217
-rw-r--r--dom/media/autoplay/test/browser/browser_autoplay_policy_web_audio_with_gum.js174
-rw-r--r--dom/media/autoplay/test/browser/browser_autoplay_videoDocument.js80
-rw-r--r--dom/media/autoplay/test/browser/file_empty.html8
-rw-r--r--dom/media/autoplay/test/browser/file_mediaplayback_frame.html21
-rw-r--r--dom/media/autoplay/test/browser/file_nonAutoplayAudio.html7
-rw-r--r--dom/media/autoplay/test/browser/file_video.html9
-rw-r--r--dom/media/autoplay/test/browser/head.js149
18 files changed, 1855 insertions, 0 deletions
diff --git a/dom/media/autoplay/test/browser/audio.ogg b/dom/media/autoplay/test/browser/audio.ogg
new file mode 100644
index 0000000000..7f1833508a
--- /dev/null
+++ b/dom/media/autoplay/test/browser/audio.ogg
Binary files differ
diff --git a/dom/media/autoplay/test/browser/browser.ini b/dom/media/autoplay/test/browser/browser.ini
new file mode 100644
index 0000000000..8bdf83859e
--- /dev/null
+++ b/dom/media/autoplay/test/browser/browser.ini
@@ -0,0 +1,27 @@
+[DEFAULT]
+subsuite = media-bc
+skip-if = (os == "win" && processor == "aarch64") # aarch64 due to 1536573
+tags = autoplay
+support-files =
+ ../../../test/gizmo.mp4
+ audio.ogg
+ file_empty.html
+ file_mediaplayback_frame.html
+ file_nonAutoplayAudio.html
+ file_video.html
+ head.js
+
+[browser_autoplay_policy_detection_global_sticky.js]
+[browser_autoplay_policy_detection_global_and_site_sticky.js]
+[browser_autoplay_policy_detection_click_to_play.js]
+[browser_autoplay_policy_play_twice.js]
+[browser_autoplay_policy_user_gestures.js]
+https_first_disabled = true
+[browser_autoplay_policy_request_permission.js]
+https_first_disabled = true
+[browser_autoplay_policy_touchScroll.js]
+https_first_disabled = true
+[browser_autoplay_policy_web_audio.js]
+[browser_autoplay_policy_web_audio_with_gum.js]
+[browser_autoplay_policy_webRTC_permission.js]
+[browser_autoplay_videoDocument.js]
diff --git a/dom/media/autoplay/test/browser/browser_autoplay_policy_detection_click_to_play.js b/dom/media/autoplay/test/browser/browser_autoplay_policy_detection_click_to_play.js
new file mode 100644
index 0000000000..576f01b1cf
--- /dev/null
+++ b/dom/media/autoplay/test/browser/browser_autoplay_policy_detection_click_to_play.js
@@ -0,0 +1,120 @@
+/**
+ * This test will check the Autoplay Policy Detection API for click-to-play
+ * blocking policy (media.autoplay.blocking_policy=2) and the blocked value set
+ * to BLOCKED (block audible) and BLOCKED_ALL (block audible & inaudible).
+ *
+ * We will create two video elements in the test page, and then click one of
+ * them. After doing that, only the element has been clicked can be allowed to
+ * autoplay, other elements should remain blocked depend on the default blocking
+ * value.
+ */
+"use strict";
+
+// TODO : remove this when it's enabled by default in bug 1812189.
+add_setup(async function setSharedPrefs() {
+ await SpecialPowers.pushPrefEnv({
+ set: [["dom.media.autoplay-policy-detection.enabled", true]],
+ });
+});
+
+async function testAutoplayPolicy(defaultPolicy) {
+ await setupTestPref(defaultPolicy);
+ let tab = await BrowserTestUtils.openNewForegroundTab(
+ window.gBrowser,
+ "about:blank"
+ );
+ await createVideoElements(tab);
+ await SpecialPowers.spawn(
+ tab.linkedBrowser,
+ [defaultPolicy],
+ defaultPolicy => {
+ is(
+ content.navigator.getAutoplayPolicy("mediaelement"),
+ defaultPolicy,
+ "Check autoplay policy by media element type is correct"
+ );
+ let videos = content.document.getElementsByTagName("video");
+ for (let video of videos) {
+ is(
+ content.navigator.getAutoplayPolicy(video),
+ defaultPolicy,
+ "Check autoplay policy by element is correct"
+ );
+ }
+ }
+ );
+
+ info("click on one video to make it play");
+ await BrowserTestUtils.synthesizeMouseAtCenter(
+ "#will-be-clicked",
+ { button: 0 },
+ tab.linkedBrowser
+ );
+
+ info("only the element has been clicked can be allowed to autoplay");
+ await SpecialPowers.spawn(
+ tab.linkedBrowser,
+ [defaultPolicy],
+ defaultPolicy => {
+ is(
+ content.navigator.getAutoplayPolicy("mediaelement"),
+ defaultPolicy,
+ "Check autoplay policy by media element type is correct"
+ );
+ let videos = content.document.getElementsByTagName("video");
+ for (let video of videos) {
+ is(
+ content.navigator.getAutoplayPolicy(video),
+ video.id === "will-be-clicked" ? "allowed" : defaultPolicy,
+ "Check autoplay policy by element is correct"
+ );
+ }
+ }
+ );
+
+ BrowserTestUtils.removeTab(tab);
+}
+
+add_task(async function testAutoplayPolicyDetectionForClickToPlay() {
+ await testAutoplayPolicy("allowed-muted");
+ await testAutoplayPolicy("disallowed");
+});
+
+// Following are helper functions
+async function setupTestPref(defaultPolicy) {
+ function policyToBlockedValue(defaultPolicy) {
+ // Value for media.autoplay.default
+ if (defaultPolicy === "allowed") {
+ return 0 /* Allowed */;
+ } else if (defaultPolicy === "allowed-muted") {
+ return 1 /* Blocked */;
+ }
+ return 5 /* Blocked All */;
+ }
+ const defaultBlocked = policyToBlockedValue(defaultPolicy);
+ info(`Set 'media.autoplay.default' to ${defaultBlocked}`);
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ ["media.autoplay.default", defaultBlocked],
+ ["media.autoplay.blocking_policy", 2 /* click-to-play */],
+ ],
+ });
+}
+
+function createVideoElements(tab) {
+ info("create two video elements in the page");
+ let url = GetTestWebBasedURL("gizmo.mp4");
+ return SpecialPowers.spawn(tab.linkedBrowser, [url], url => {
+ let video1 = content.document.createElement("video");
+ video1.id = "will-be-clicked";
+ video1.controls = true;
+ video1.src = url;
+
+ let video2 = content.document.createElement("video");
+ video2.controls = true;
+ video2.src = url;
+
+ content.document.body.appendChild(video1);
+ content.document.body.appendChild(video2);
+ });
+}
diff --git a/dom/media/autoplay/test/browser/browser_autoplay_policy_detection_global_and_site_sticky.js b/dom/media/autoplay/test/browser/browser_autoplay_policy_detection_global_and_site_sticky.js
new file mode 100644
index 0000000000..828b3e0986
--- /dev/null
+++ b/dom/media/autoplay/test/browser/browser_autoplay_policy_detection_global_and_site_sticky.js
@@ -0,0 +1,168 @@
+/**
+ * This test checks whether Autoplay Policy Detection API works correctly under
+ * different situations of having global permission set for block autoplay
+ * along with different site permission setting. This test only checks the
+ * sticky user gesture blocking model.
+ */
+"use strict";
+
+const { PermissionTestUtils } = ChromeUtils.importESModule(
+ "resource://testing-common/PermissionTestUtils.sys.mjs"
+);
+
+// We can't set site permission on 'about:blank' so we use an empty page.
+const PAGE_URL = GetTestWebBasedURL("file_empty.html");
+
+add_setup(async function setSharedPrefs() {
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ ["dom.media.autoplay-policy-detection.enabled", true],
+ ["media.autoplay.blocking_policy", 0],
+ ["media.autoplay.block-webaudio", true],
+ ],
+ });
+});
+
+add_task(async function testGlobalPermissionIsAllowed() {
+ await SpecialPowers.pushPrefEnv({
+ set: [["media.autoplay.default", SpecialPowers.Ci.nsIAutoplay.ALLOWED]],
+ });
+ let tab = await createTabAndSetupPolicyAssertFunc(PAGE_URL);
+ PermissionTestUtils.add(
+ tab.linkedBrowser.currentURI,
+ "autoplay-media",
+ Services.perms.DENY_ACTION
+ );
+ await SpecialPowers.spawn(tab.linkedBrowser, [], _ => {
+ info("site permission blocks audible autoplay");
+ content.assertAutoplayPolicy({
+ resultForElementType: "allowed-muted",
+ resultForElement: "allowed-muted",
+ resultForContextType: "disallowed",
+ resultForContext: "disallowed",
+ });
+ });
+ PermissionTestUtils.add(
+ tab.linkedBrowser.currentURI,
+ "autoplay-media",
+ Ci.nsIAutoplay.BLOCKED_ALL
+ );
+ await SpecialPowers.spawn(tab.linkedBrowser, [], _ => {
+ info("site permission blocks all autoplay");
+ content.assertAutoplayPolicy({
+ resultForElementType: "disallowed",
+ resultForElement: "disallowed",
+ resultForContextType: "disallowed",
+ resultForContext: "disallowed",
+ });
+
+ info(
+ "activate document by using user gesture, all autoplay will be allowed"
+ );
+ content.document.notifyUserGestureActivation();
+ content.assertAutoplayPolicy({
+ resultForElementType: "allowed",
+ resultForElement: "allowed",
+ resultForContextType: "allowed",
+ resultForContext: "allowed",
+ });
+ });
+ PermissionTestUtils.remove(tab.linkedBrowser.currentURI, "autoplay-media");
+ BrowserTestUtils.removeTab(tab);
+});
+
+add_task(async function testGlobalPermissionIsBlocked() {
+ await SpecialPowers.pushPrefEnv({
+ set: [["media.autoplay.default", SpecialPowers.Ci.nsIAutoplay.BLOCKED]],
+ });
+ let tab = await createTabAndSetupPolicyAssertFunc(PAGE_URL);
+ PermissionTestUtils.add(
+ tab.linkedBrowser.currentURI,
+ "autoplay-media",
+ Services.perms.ALLOW_ACTION
+ );
+ await SpecialPowers.spawn(tab.linkedBrowser, [], _ => {
+ info("site permission allows all autoplay");
+ content.assertAutoplayPolicy({
+ resultForElementType: "allowed",
+ resultForElement: "allowed",
+ resultForContextType: "allowed",
+ resultForContext: "allowed",
+ });
+ });
+ PermissionTestUtils.add(
+ tab.linkedBrowser.currentURI,
+ "autoplay-media",
+ Ci.nsIAutoplay.BLOCKED_ALL
+ );
+ await SpecialPowers.spawn(tab.linkedBrowser, [], _ => {
+ info("site permission blocks all autoplay");
+ content.assertAutoplayPolicy({
+ resultForElementType: "disallowed",
+ resultForElement: "disallowed",
+ resultForContextType: "disallowed",
+ resultForContext: "disallowed",
+ });
+
+ info(
+ "activate document by using user gesture, all autoplay will be allowed"
+ );
+ content.document.notifyUserGestureActivation();
+ content.assertAutoplayPolicy({
+ resultForElementType: "allowed",
+ resultForElement: "allowed",
+ resultForContextType: "allowed",
+ resultForContext: "allowed",
+ });
+ });
+ PermissionTestUtils.remove(tab.linkedBrowser.currentURI, "autoplay-media");
+ BrowserTestUtils.removeTab(tab);
+});
+
+add_task(async function testGlobalPermissionIsBlockedAll() {
+ await SpecialPowers.pushPrefEnv({
+ set: [["media.autoplay.default", SpecialPowers.Ci.nsIAutoplay.BLOCKED_ALL]],
+ });
+ let tab = await createTabAndSetupPolicyAssertFunc(PAGE_URL);
+ PermissionTestUtils.add(
+ tab.linkedBrowser.currentURI,
+ "autoplay-media",
+ Services.perms.ALLOW_ACTION
+ );
+ await SpecialPowers.spawn(tab.linkedBrowser, [], _ => {
+ info("site permission allows all autoplay");
+ content.assertAutoplayPolicy({
+ resultForElementType: "allowed",
+ resultForElement: "allowed",
+ resultForContextType: "allowed",
+ resultForContext: "allowed",
+ });
+ });
+ PermissionTestUtils.add(
+ tab.linkedBrowser.currentURI,
+ "autoplay-media",
+ Services.perms.DENY_ACTION
+ );
+ await SpecialPowers.spawn(tab.linkedBrowser, [], _ => {
+ info("site permission blocks audible autoplay");
+ content.assertAutoplayPolicy({
+ resultForElementType: "allowed-muted",
+ resultForElement: "allowed-muted",
+ resultForContextType: "disallowed",
+ resultForContext: "disallowed",
+ });
+
+ info(
+ "activate document by using user gesture, all autoplay will be allowed"
+ );
+ content.document.notifyUserGestureActivation();
+ content.assertAutoplayPolicy({
+ resultForElementType: "allowed",
+ resultForElement: "allowed",
+ resultForContextType: "allowed",
+ resultForContext: "allowed",
+ });
+ });
+ PermissionTestUtils.remove(tab.linkedBrowser.currentURI, "autoplay-media");
+ BrowserTestUtils.removeTab(tab);
+});
diff --git a/dom/media/autoplay/test/browser/browser_autoplay_policy_detection_global_sticky.js b/dom/media/autoplay/test/browser/browser_autoplay_policy_detection_global_sticky.js
new file mode 100644
index 0000000000..ab9fe6b418
--- /dev/null
+++ b/dom/media/autoplay/test/browser/browser_autoplay_policy_detection_global_sticky.js
@@ -0,0 +1,105 @@
+/**
+ * This test checks whether Autoplay Policy Detection API works correctly under
+ * different situations of having global permission set for block autoplay.
+ * This test only checks the sticky user gesture blocking model.
+ */
+"use strict";
+
+add_setup(async function setSharedPrefs() {
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ ["dom.media.autoplay-policy-detection.enabled", true],
+ ["media.autoplay.blocking_policy", 0],
+ ["media.autoplay.block-webaudio", true],
+ ],
+ });
+});
+
+add_task(async function testGlobalPermissionIsAllowed() {
+ await SpecialPowers.pushPrefEnv({
+ set: [["media.autoplay.default", SpecialPowers.Ci.nsIAutoplay.ALLOWED]],
+ });
+ let tab = await createTabAndSetupPolicyAssertFunc("about:blank");
+ await SpecialPowers.spawn(tab.linkedBrowser, [], _ => {
+ info("global setting allows any autoplay");
+ content.assertAutoplayPolicy({
+ resultForElementType: "allowed",
+ resultForElement: "allowed",
+ resultForContextType: "allowed",
+ resultForContext: "allowed",
+ });
+ });
+ BrowserTestUtils.removeTab(tab);
+});
+
+add_task(async function testGlobalPermissionIsBlocked() {
+ await SpecialPowers.pushPrefEnv({
+ set: [["media.autoplay.default", SpecialPowers.Ci.nsIAutoplay.BLOCKED]],
+ });
+ let tab = await createTabAndSetupPolicyAssertFunc("about:blank");
+ await SpecialPowers.spawn(tab.linkedBrowser, [], _ => {
+ info(
+ "global setting allows inaudible autoplay but audible autoplay is still not allowed"
+ );
+ content.assertAutoplayPolicy({
+ resultForElementType: "allowed-muted",
+ resultForElement: "allowed-muted",
+ resultForContextType: "disallowed",
+ resultForContext: "disallowed",
+ });
+
+ info("tweaking video's muted attribute won't change the result");
+ content.video.muted = true;
+ is(
+ "allowed-muted",
+ content.navigator.getAutoplayPolicy(content.video),
+ "getAutoplayPolicy(video) returns correct value"
+ );
+ content.video.muted = false;
+ is(
+ "allowed-muted",
+ content.navigator.getAutoplayPolicy(content.video),
+ "getAutoplayPolicy(video) returns correct value"
+ );
+
+ info(
+ "activate document by using user gesture, all autoplay will be allowed"
+ );
+ content.document.notifyUserGestureActivation();
+ content.assertAutoplayPolicy({
+ resultForElementType: "allowed",
+ resultForElement: "allowed",
+ resultForContextType: "allowed",
+ resultForContext: "allowed",
+ });
+ });
+ BrowserTestUtils.removeTab(tab);
+});
+
+add_task(async function testGlobalPermissionIsBlockedAll() {
+ await SpecialPowers.pushPrefEnv({
+ set: [["media.autoplay.default", SpecialPowers.Ci.nsIAutoplay.BLOCKED_ALL]],
+ });
+ let tab = await createTabAndSetupPolicyAssertFunc("about:blank");
+ await SpecialPowers.spawn(tab.linkedBrowser, [], _ => {
+ info("global setting doesn't allow any autoplay");
+ content.assertAutoplayPolicy({
+ resultForElementType: "disallowed",
+ resultForElement: "disallowed",
+ resultForContextType: "disallowed",
+ resultForContext: "disallowed",
+ });
+
+ info(
+ "activate document by using user gesture, all autoplay will be allowed"
+ );
+ content.document.notifyUserGestureActivation();
+ content.assertAutoplayPolicy({
+ resultForElementType: "allowed",
+ resultForElement: "allowed",
+ resultForContextType: "allowed",
+ resultForContext: "allowed",
+ });
+ });
+ BrowserTestUtils.removeTab(tab);
+});
diff --git a/dom/media/autoplay/test/browser/browser_autoplay_policy_play_twice.js b/dom/media/autoplay/test/browser/browser_autoplay_policy_play_twice.js
new file mode 100644
index 0000000000..7130e6e781
--- /dev/null
+++ b/dom/media/autoplay/test/browser/browser_autoplay_policy_play_twice.js
@@ -0,0 +1,54 @@
+const VIDEO_PAGE = GetTestWebBasedURL("file_video.html");
+
+function setup_test_preference(enableUserGesture) {
+ let state = enableUserGesture ? "enable" : "disable";
+ info(`- set pref : ${state} user gesture -`);
+ return SpecialPowers.pushPrefEnv({
+ set: [
+ ["media.autoplay.default", SpecialPowers.Ci.nsIAutoplay.BLOCKED],
+ ["media.autoplay.blocking_policy", enableUserGesture ? 0 : 1],
+ ],
+ });
+}
+
+async function allow_play_for_played_video() {
+ info("- open new tab -");
+ let tab = await BrowserTestUtils.openNewForegroundTab(
+ window.gBrowser,
+ "about:blank"
+ );
+ BrowserTestUtils.loadURIString(tab.linkedBrowser, VIDEO_PAGE);
+ await BrowserTestUtils.browserLoaded(tab.linkedBrowser);
+
+ info("- simulate user-click to start video -");
+ await BrowserTestUtils.synthesizeMouseAtCenter(
+ "#v",
+ { button: 0 },
+ tab.linkedBrowser
+ );
+
+ async function play_video_again() {
+ let video = content.document.getElementById("v");
+ ok(!video.paused, "video is playing");
+
+ info("- call video play() again -");
+ try {
+ await video.play();
+ ok(true, "success to resolve play promise");
+ } catch (e) {
+ ok(false, "promise should not be rejected");
+ }
+ }
+ await SpecialPowers.spawn(tab.linkedBrowser, [], play_video_again);
+
+ info("- remove tab -");
+ BrowserTestUtils.removeTab(tab);
+}
+
+add_task(async function start_test() {
+ await setup_test_preference(true);
+ await allow_play_for_played_video();
+
+ await setup_test_preference(false);
+ await allow_play_for_played_video();
+});
diff --git a/dom/media/autoplay/test/browser/browser_autoplay_policy_request_permission.js b/dom/media/autoplay/test/browser/browser_autoplay_policy_request_permission.js
new file mode 100644
index 0000000000..5cdb3ffe6c
--- /dev/null
+++ b/dom/media/autoplay/test/browser/browser_autoplay_policy_request_permission.js
@@ -0,0 +1,269 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+"use strict";
+
+const { PermissionTestUtils } = ChromeUtils.importESModule(
+ "resource://testing-common/PermissionTestUtils.sys.mjs"
+);
+
+const VIDEO_PAGE_URI = GetTestWebBasedURL("file_empty.html");
+const SAME_ORIGIN_FRAME_URI = GetTestWebBasedURL(
+ "file_mediaplayback_frame.html"
+);
+const DIFFERENT_ORIGIN_FRAME_URI = GetTestWebBasedURL(
+ "file_mediaplayback_frame.html",
+ { crossOrigin: true }
+);
+
+const gPermissionName = "autoplay-media";
+
+function setTestingPreferences(defaultSetting) {
+ info(`set default autoplay setting to '${defaultSetting}'`);
+ let defaultValue =
+ defaultSetting == "blocked"
+ ? SpecialPowers.Ci.nsIAutoplay.BLOCKED
+ : SpecialPowers.Ci.nsIAutoplay.ALLOWED;
+ return SpecialPowers.pushPrefEnv({
+ set: [
+ ["media.autoplay.default", defaultValue],
+ ["media.autoplay.blocking_policy", 0],
+ ["media.autoplay.block-event.enabled", true],
+ ],
+ });
+}
+
+async function testAutoplayExistingPermission(args) {
+ info("- Starting '" + args.name + "' -");
+ await BrowserTestUtils.withNewTab(
+ {
+ gBrowser,
+ url: VIDEO_PAGE_URI,
+ },
+ async browser => {
+ let promptShowing = () =>
+ PopupNotifications.getNotification("autoplay-media", browser);
+
+ PermissionTestUtils.add(
+ 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.
+ PermissionTestUtils.remove(browser.currentURI, "autoplay-media");
+
+ info("- Finished '" + args.name + "' -");
+ }
+ );
+}
+
+async function testAutoplayExistingPermissionAgainstDefaultSetting(args) {
+ await setTestingPreferences(args.defaultSetting);
+ await testAutoplayExistingPermission(args);
+}
+
+// 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 setTestingPreferences("blocked" /* default setting */);
+ await testAutoplayExistingPermission({
+ name: "Prexisting allow permission autoplay attribute",
+ permission: Services.perms.ALLOW_ACTION,
+ shouldPlay: true,
+ mode: "autoplay attribute",
+ });
+ await testAutoplayExistingPermission({
+ name: "Prexisting allow permission call play",
+ permission: Services.perms.ALLOW_ACTION,
+ shouldPlay: true,
+ mode: "call play",
+ });
+ await testAutoplayExistingPermission({
+ name: "Prexisting block permission autoplay attribute",
+ permission: Services.perms.DENY_ACTION,
+ shouldPlay: false,
+ mode: "autoplay attribute",
+ });
+ await testAutoplayExistingPermission({
+ name: "Prexisting block permission call play",
+ permission: Services.perms.DENY_ACTION,
+ shouldPlay: false,
+ mode: "call play",
+ });
+});
+
+/**
+ * These tests are used to ensure the autoplay setting for specific site can
+ * always override the default autoplay setting.
+ */
+add_task(async () => {
+ await testAutoplayExistingPermissionAgainstDefaultSetting({
+ name: "Site has prexisting allow permission but default setting is 'blocked'",
+ permission: Services.perms.ALLOW_ACTION,
+ defaultSetting: "blocked",
+ shouldPlay: true,
+ mode: "autoplay attribute",
+ });
+ await testAutoplayExistingPermissionAgainstDefaultSetting({
+ name: "Site has prexisting block permission but default setting is 'allowed'",
+ permission: Services.perms.DENY_ACTION,
+ defaultSetting: "allowed",
+ shouldPlay: false,
+ mode: "autoplay attribute",
+ });
+});
+
+/**
+ * The permission of the main page's domain would determine the final autoplay
+ * result when a page contains multiple iframes which are in the different
+ * domain from the main pages's.
+ * That means we would not check the permission of iframe's domain, even if it
+ * has been set.
+ */
+add_task(async function testExistingPermissionForIframe() {
+ await setTestingPreferences("blocked" /* default setting */);
+ await testAutoplayExistingPermissionForIframe({
+ name: "Prexisting ALLOW for main page with same origin iframe",
+ permissionForParent: Services.perms.ALLOW_ACTION,
+ isIframeDifferentOrgin: true,
+ shouldPlay: true,
+ });
+
+ await testAutoplayExistingPermissionForIframe({
+ name: "Prexisting ALLOW for main page with different origin iframe",
+ permissionForParent: Services.perms.ALLOW_ACTION,
+ isIframeDifferentOrgin: false,
+ shouldPlay: true,
+ });
+
+ await testAutoplayExistingPermissionForIframe({
+ name: "Prexisting ALLOW for main page, prexisting DENY for different origin iframe",
+ permissionForParent: Services.perms.ALLOW_ACTION,
+ permissionForChild: Services.perms.DENY_ACTION,
+ isIframeDifferentOrgin: false,
+ shouldPlay: true,
+ });
+
+ await testAutoplayExistingPermissionForIframe({
+ name: "Prexisting DENY for main page, prexisting ALLOW for different origin iframe",
+ permissionForParent: Services.perms.DENY_ACTION,
+ permissionForChild: Services.perms.ALLOW_ACTION,
+ isIframeDifferentOrgin: false,
+ shouldPlay: false,
+ });
+});
+
+/**
+ * The following are helper functions.
+ */
+async function testAutoplayExistingPermissionForIframe(args) {
+ info(`Start test : ${args.name}`);
+ await BrowserTestUtils.withNewTab(
+ {
+ gBrowser,
+ url: VIDEO_PAGE_URI,
+ },
+ async browser => {
+ setupSitesPermission(browser, args);
+
+ await createIframe(browser, args);
+ await checkAutplayInIframe(browser, args);
+
+ clearSitesPermission(browser, args);
+ }
+ );
+ info(`Finish test : ${args.name}`);
+}
+
+function setupSitesPermission(
+ browser,
+ {
+ isIframeDifferentOrgin,
+ permissionForParent,
+ permissionForChild = Services.perms.UNKNOWN_ACTION,
+ }
+) {
+ info(`setupSitesPermission`);
+ // Set permission for the main page's domain
+ setPermissionForBrowser(browser, browser.currentURI, permissionForParent);
+ if (isIframeDifferentOrgin) {
+ // Set permission for different domain of the iframe
+ setPermissionForBrowser(
+ browser,
+ DIFFERENT_ORIGIN_FRAME_URI,
+ permissionForChild
+ );
+ }
+}
+
+function clearSitesPermission(browser, { isIframeDifferentOrgin }) {
+ info(`clearSitesPermission`);
+ // Clear permission for the main page's domain
+ setPermissionForBrowser(
+ browser,
+ browser.currentURI,
+ Services.perms.UNKNOWN_ACTION
+ );
+ if (isIframeDifferentOrgin) {
+ // Clear permission for different domain of the iframe
+ setPermissionForBrowser(
+ browser,
+ DIFFERENT_ORIGIN_FRAME_URI,
+ Services.perms.UNKNOWN_ACTION
+ );
+ }
+}
+
+function setPermissionForBrowser(browser, uri, permValue) {
+ const promptShowing = () =>
+ PopupNotifications.getNotification(gPermissionName, browser);
+ PermissionTestUtils.add(uri, gPermissionName, permValue);
+ ok(!promptShowing(), "Should not be showing permission prompt yet");
+ is(
+ PermissionTestUtils.testExactPermission(uri, gPermissionName),
+ permValue,
+ "Set permission correctly"
+ );
+}
+
+function createIframe(browser, { isIframeDifferentOrgin }) {
+ const iframeURL = isIframeDifferentOrgin
+ ? DIFFERENT_ORIGIN_FRAME_URI
+ : SAME_ORIGIN_FRAME_URI;
+ return SpecialPowers.spawn(browser, [iframeURL], async url => {
+ info(`Create iframe and wait until it finsihes loading`);
+ const iframe = content.document.createElement("iframe");
+ iframe.src = url;
+ content.document.body.appendChild(iframe);
+ await new Promise(r => (iframe.onload = r));
+ });
+}
+
+function checkAutplayInIframe(browser, args) {
+ return SpecialPowers.spawn(browser, [args], async ({ shouldPlay }) => {
+ info(`check if media in iframe can start playing`);
+ const iframe = content.document.getElementsByTagName("iframe")[0];
+ if (!iframe) {
+ ok(false, `can not get the iframe!`);
+ return;
+ }
+ iframe.contentWindow.postMessage("play", "*");
+ await new Promise(r => {
+ content.onmessage = event => {
+ if (shouldPlay) {
+ is(event.data, "played", `played media in iframe`);
+ } else {
+ is(event.data, "blocked", `blocked media in iframe`);
+ }
+ r();
+ };
+ });
+ });
+}
diff --git a/dom/media/autoplay/test/browser/browser_autoplay_policy_touchScroll.js b/dom/media/autoplay/test/browser/browser_autoplay_policy_touchScroll.js
new file mode 100644
index 0000000000..fa28bf2943
--- /dev/null
+++ b/dom/media/autoplay/test/browser/browser_autoplay_policy_touchScroll.js
@@ -0,0 +1,103 @@
+/**
+ * This test is used to ensure that touch in point can activate document and
+ * allow autoplay, but touch scroll can't activate document.
+ */
+/* eslint-disable mozilla/no-arbitrary-setTimeout */
+"use strict";
+
+const PAGE = GetTestWebBasedURL("file_nonAutoplayAudio.html");
+
+function checkMediaPlayingState(isPlaying) {
+ let audio = content.document.getElementById("testAudio");
+ if (!audio) {
+ ok(false, "can't get the audio element!");
+ }
+
+ is(!audio.paused, isPlaying, "media playing state is correct.");
+}
+
+async function callMediaPlay(shouldStartPlaying) {
+ let audio = content.document.getElementById("testAudio");
+ if (!audio) {
+ ok(false, "can't get the audio element!");
+ }
+
+ info(`call media.play().`);
+ let playPromise = new Promise((resolve, reject) => {
+ audio.play().then(() => {
+ audio.isPlayStarted = true;
+ resolve();
+ });
+ content.setTimeout(() => {
+ if (audio.isPlayStarted) {
+ return;
+ }
+ reject();
+ }, 3000);
+ });
+
+ let isStartPlaying = await playPromise.then(
+ () => true,
+ () => false
+ );
+ is(
+ isStartPlaying,
+ shouldStartPlaying,
+ "media is " + (isStartPlaying ? "" : "not ") + "playing."
+ );
+}
+
+async function synthesizeTouchScroll(target, browser) {
+ const offset = 100;
+ await BrowserTestUtils.synthesizeTouch(
+ target,
+ 0,
+ 0,
+ { type: "touchstart" },
+ browser
+ );
+ await BrowserTestUtils.synthesizeTouch(
+ target,
+ offset / 2,
+ offset / 2,
+ { type: "touchmove" },
+ browser
+ );
+ await BrowserTestUtils.synthesizeTouch(
+ target,
+ offset,
+ offset,
+ { type: "touchend" },
+ browser
+ );
+}
+
+add_task(async function setup_test_preference() {
+ return SpecialPowers.pushPrefEnv({
+ set: [
+ ["media.autoplay.default", SpecialPowers.Ci.nsIAutoplay.BLOCKED],
+ ["media.autoplay.blocking_policy", 0],
+ ],
+ });
+});
+
+add_task(async function testTouchScroll() {
+ await BrowserTestUtils.withNewTab(
+ {
+ gBrowser,
+ url: PAGE,
+ },
+ async browser => {
+ info(`- media should not start playing -`);
+ await SpecialPowers.spawn(browser, [false], checkMediaPlayingState);
+
+ info(`- simulate touch scroll which should not activate document -`);
+ await synthesizeTouchScroll("#testAudio", browser);
+ await SpecialPowers.spawn(browser, [false], callMediaPlay);
+
+ info(`- simulate touch at a point which should activate document -`);
+ await BrowserTestUtils.synthesizeTouch("#testAudio", 0, 0, {}, browser);
+ await SpecialPowers.spawn(browser, [true], callMediaPlay);
+ }
+ );
+});
diff --git a/dom/media/autoplay/test/browser/browser_autoplay_policy_user_gestures.js b/dom/media/autoplay/test/browser/browser_autoplay_policy_user_gestures.js
new file mode 100644
index 0000000000..2b9d8c1158
--- /dev/null
+++ b/dom/media/autoplay/test/browser/browser_autoplay_policy_user_gestures.js
@@ -0,0 +1,277 @@
+/* eslint-disable mozilla/no-arbitrary-setTimeout */
+
+const VIDEO_PAGE = GetTestWebBasedURL("file_video.html");
+
+const UserGestures = {
+ MOUSE_CLICK: "mouse-click",
+ MOUSE_MOVE: "mouse-move",
+ KEYBOARD_PRESS: "keyboard-press",
+};
+
+const UserGestureTests = [
+ { type: UserGestures.MOUSE_CLICK, isActivationGesture: true },
+ { type: UserGestures.MOUSE_MOVE, isActivationGesture: false },
+ // test different keycode here. printable key, non-printable key and other
+ // special keys.
+ {
+ type: UserGestures.KEYBOARD_PRESS,
+ isActivationGesture: true,
+ keyCode: "a",
+ },
+ {
+ type: UserGestures.KEYBOARD_PRESS,
+ isActivationGesture: false,
+ keyCode: "VK_ESCAPE",
+ },
+ {
+ type: UserGestures.KEYBOARD_PRESS,
+ isActivationGesture: true,
+ keyCode: "VK_RETURN",
+ },
+ {
+ type: UserGestures.KEYBOARD_PRESS,
+ isActivationGesture: true,
+ keyCode: "VK_SPACE",
+ },
+];
+
+/**
+ * This test is used to ensure we would stop blocking autoplay after document
+ * has been activated by user gestures. We would treat mouse clicking, key board
+ * pressing (printable keys or carriage return) as valid user gesture input.
+ */
+add_task(async function startTestUserGestureInput() {
+ info("- setup test preference -");
+ await setupTestPreferences();
+
+ info("- test play when page doesn't be activated -");
+ await testPlayWithoutUserGesture();
+
+ info("- test play after page got user gesture -");
+ for (let idx = 0; idx < UserGestureTests.length; idx++) {
+ info("- test play after page got user gesture -");
+ await testPlayWithUserGesture(UserGestureTests[idx]);
+
+ info("- test web audio with user gesture -");
+ await testWebAudioWithUserGesture(UserGestureTests[idx]);
+ }
+});
+
+/**
+ * testing helper functions
+ */
+function setupTestPreferences() {
+ return SpecialPowers.pushPrefEnv({
+ set: [
+ ["media.autoplay.default", SpecialPowers.Ci.nsIAutoplay.BLOCKED],
+ ["media.autoplay.blocking_policy", 0],
+ ["media.autoplay.block-event.enabled", true],
+ ["media.autoplay.block-webaudio", true],
+ ],
+ });
+}
+
+function simulateUserGesture(gesture, targetBrowser) {
+ info(`- simulate ${gesture.type} event -`);
+ switch (gesture.type) {
+ case UserGestures.MOUSE_CLICK:
+ return BrowserTestUtils.synthesizeMouseAtCenter(
+ "body",
+ { button: 0 },
+ targetBrowser
+ );
+ case UserGestures.MOUSE_MOVE:
+ return BrowserTestUtils.synthesizeMouseAtCenter(
+ "body",
+ { type: "mousemove" },
+ targetBrowser
+ );
+ case UserGestures.KEYBOARD_PRESS:
+ info(`- keycode=${gesture.keyCode} -`);
+ return BrowserTestUtils.synthesizeKey(gesture.keyCode, {}, targetBrowser);
+ default:
+ ok(false, "undefined user gesture");
+ return false;
+ }
+}
+
+async function testPlayWithoutUserGesture() {
+ info("- open new tab -");
+ let tab = await BrowserTestUtils.openNewForegroundTab(
+ window.gBrowser,
+ "about:blank"
+ );
+ BrowserTestUtils.loadURIString(tab.linkedBrowser, VIDEO_PAGE);
+ await BrowserTestUtils.browserLoaded(tab.linkedBrowser);
+
+ async function checkAutoplayKeyword() {
+ info("- create an new autoplay video -");
+ let video = content.document.createElement("video");
+ video.src = "gizmo.mp4";
+ video.autoplay = true;
+ let canplayPromise = new Promise(function (resolve) {
+ video.addEventListener(
+ "canplaythrough",
+ function () {
+ resolve();
+ },
+ { once: true }
+ );
+ });
+ content.document.body.appendChild(video);
+
+ info("- can't autoplay without user activation -");
+ await canplayPromise;
+ ok(video.paused, "video can't start without user input.");
+ }
+ await SpecialPowers.spawn(tab.linkedBrowser, [], checkAutoplayKeyword);
+
+ async function playVideo() {
+ let video = content.document.getElementById("v");
+ info("- call play() without user activation -");
+ await video.play().catch(function () {
+ ok(video.paused, "video can't start play without user input.");
+ });
+ }
+ await SpecialPowers.spawn(tab.linkedBrowser, [], playVideo);
+
+ info("- remove tab -");
+ BrowserTestUtils.removeTab(tab);
+}
+
+async function testPlayWithUserGesture(gesture) {
+ info("- open new tab -");
+ let tab = await BrowserTestUtils.openNewForegroundTab(
+ window.gBrowser,
+ "about:blank"
+ );
+ BrowserTestUtils.loadURIString(tab.linkedBrowser, VIDEO_PAGE);
+ await BrowserTestUtils.browserLoaded(tab.linkedBrowser);
+
+ info("- simulate user gesture -");
+ await simulateUserGesture(gesture, tab.linkedBrowser);
+
+ info("- call play() -");
+ async function playVideo(gesture) {
+ let video = content.document.getElementById("v");
+ try {
+ await video.play();
+ ok(gesture.isActivationGesture, "user gesture can activate the page");
+ ok(!video.paused, "video starts playing.");
+ } catch (e) {
+ ok(
+ !gesture.isActivationGesture,
+ "user gesture can not activate the page"
+ );
+ ok(video.paused, "video can not start playing.");
+ }
+ }
+
+ await SpecialPowers.spawn(tab.linkedBrowser, [gesture], playVideo);
+
+ info("- remove tab -");
+ BrowserTestUtils.removeTab(tab);
+}
+
+function createAudioContext() {
+ content.ac = new content.AudioContext();
+ let ac = content.ac;
+ ac.resumePromises = [];
+ ac.stateChangePromise = new Promise(resolve => {
+ ac.addEventListener(
+ "statechange",
+ function () {
+ resolve();
+ },
+ { once: true }
+ );
+ });
+ ac.notAllowedToStart = new Promise(resolve => {
+ ac.addEventListener(
+ "blocked",
+ function () {
+ resolve();
+ },
+ { once: true }
+ );
+ });
+}
+
+function resumeWithoutExpectedSuccess() {
+ let ac = content.ac;
+ let promise = ac.resume();
+ ac.resumePromises.push(promise);
+ return new Promise((resolve, reject) => {
+ content.setTimeout(() => {
+ if (ac.state == "suspended") {
+ ok(true, "audio context is still suspended");
+ resolve();
+ } else {
+ reject("audio context should not be allowed to start");
+ }
+ }, 2000);
+ });
+}
+
+function resumeWithExpectedSuccess() {
+ let ac = content.ac;
+ ac.resumePromises.push(ac.resume());
+ return Promise.all(ac.resumePromises).then(() => {
+ ok(ac.state == "running", "audio context starts running");
+ });
+}
+
+async function testWebAudioWithUserGesture(gesture) {
+ info("- open new tab -");
+ let tab = await BrowserTestUtils.openNewForegroundTab(
+ window.gBrowser,
+ "about:blank"
+ );
+ info("- create audio context -");
+ await SpecialPowers.spawn(tab.linkedBrowser, [], () => {
+ content.ac = new content.AudioContext();
+ let ac = content.ac;
+ ac.resumePromises = [];
+ return new Promise(resolve => {
+ ac.addEventListener(
+ "blocked",
+ function () {
+ Assert.equal(
+ ac.state,
+ "suspended",
+ `AudioContext is not started yet.`
+ );
+ resolve();
+ },
+ { once: true }
+ );
+ });
+ });
+
+ info("- calling resume() -");
+ try {
+ await SpecialPowers.spawn(
+ tab.linkedBrowser,
+ [],
+ resumeWithoutExpectedSuccess
+ );
+ } catch (error) {
+ ok(false, error.toString());
+ }
+
+ info("- simulate user gesture -");
+ await simulateUserGesture(gesture, tab.linkedBrowser);
+
+ info("- calling resume() again");
+ try {
+ let resumeFunc = gesture.isActivationGesture
+ ? resumeWithExpectedSuccess
+ : resumeWithoutExpectedSuccess;
+ await SpecialPowers.spawn(tab.linkedBrowser, [], resumeFunc);
+ } catch (error) {
+ ok(false, error.toString());
+ }
+
+ info("- remove tab -");
+ await BrowserTestUtils.removeTab(tab);
+}
diff --git a/dom/media/autoplay/test/browser/browser_autoplay_policy_webRTC_permission.js b/dom/media/autoplay/test/browser/browser_autoplay_policy_webRTC_permission.js
new file mode 100644
index 0000000000..8afae4d08e
--- /dev/null
+++ b/dom/media/autoplay/test/browser/browser_autoplay_policy_webRTC_permission.js
@@ -0,0 +1,67 @@
+/**
+ * This test is used to ensure site which has granted 'camera' or 'microphone'
+ * or 'screen' permission could be allowed to autoplay.
+ */
+"use strict";
+
+const { PermissionTestUtils } = ChromeUtils.importESModule(
+ "resource://testing-common/PermissionTestUtils.sys.mjs"
+);
+
+const VIDEO_PAGE = GetTestWebBasedURL("file_empty.html");
+
+add_task(() => {
+ return SpecialPowers.pushPrefEnv({
+ set: [
+ ["media.autoplay.default", SpecialPowers.Ci.nsIAutoplay.BLOCKED],
+ ["media.autoplay.blocking_policy", 0],
+ ["media.autoplay.block-event.enabled", true],
+ ],
+ });
+});
+
+async function testAutoplayWebRTCPermission(args) {
+ info(`- Starting ${args.name} -`);
+ await BrowserTestUtils.withNewTab(
+ {
+ gBrowser,
+ url: VIDEO_PAGE,
+ },
+ async browser => {
+ PermissionTestUtils.add(
+ browser.currentURI,
+ args.permission,
+ Services.perms.ALLOW_ACTION
+ );
+
+ await loadAutoplayVideo(browser, args);
+ await checkVideoDidPlay(browser, args);
+
+ // Reset permission.
+ PermissionTestUtils.remove(browser.currentURI, args.permission);
+
+ info(`- Finished ${args.name} -`);
+ }
+ );
+}
+
+add_task(async function start_test() {
+ await testAutoplayWebRTCPermission({
+ name: "Site with camera permission",
+ permission: "camera",
+ shouldPlay: true,
+ mode: "call play",
+ });
+ await testAutoplayWebRTCPermission({
+ name: "Site with microphone permission",
+ permission: "microphone",
+ shouldPlay: true,
+ mode: "call play",
+ });
+ await testAutoplayWebRTCPermission({
+ name: "Site with screen permission",
+ permission: "screen",
+ shouldPlay: true,
+ mode: "call play",
+ });
+});
diff --git a/dom/media/autoplay/test/browser/browser_autoplay_policy_web_audio.js b/dom/media/autoplay/test/browser/browser_autoplay_policy_web_audio.js
new file mode 100644
index 0000000000..b6e1133dd1
--- /dev/null
+++ b/dom/media/autoplay/test/browser/browser_autoplay_policy_web_audio.js
@@ -0,0 +1,217 @@
+/**
+ * This test is used for testing whether WebAudio can be started correctly in
+ * different scenarios, such as
+ * 1) site has existing 'autoplay-media' permission for allowing autoplay
+ * 2) site has existing 'autoplay-media' permission for blocking autoplay
+ * 3) site doesn't have permission, start audio context by calling resume() or
+ * AudioScheduledNode.start() after granting user activation.
+ */
+"use strict";
+
+const { PermissionTestUtils } = ChromeUtils.importESModule(
+ "resource://testing-common/PermissionTestUtils.sys.mjs"
+);
+
+const PAGE = GetTestWebBasedURL("file_empty.html");
+
+function setup_test_preference() {
+ return SpecialPowers.pushPrefEnv({
+ set: [
+ ["media.autoplay.default", SpecialPowers.Ci.nsIAutoplay.BLOCKED],
+ ["media.autoplay.blocking_policy", 0],
+ ["media.autoplay.block-webaudio", true],
+ ["media.autoplay.block-event.enabled", true],
+ ],
+ });
+}
+
+function createAudioContext() {
+ content.ac = new content.AudioContext();
+ const ac = content.ac;
+
+ ac.allowedToStart = new Promise(resolve => {
+ ac.addEventListener(
+ "statechange",
+ function () {
+ if (ac.state === "running") {
+ resolve();
+ }
+ },
+ { once: true }
+ );
+ });
+
+ ac.notAllowedToStart = new Promise(resolve => {
+ ac.addEventListener(
+ "blocked",
+ function () {
+ resolve();
+ },
+ { once: true }
+ );
+ });
+}
+
+async function checkIfAudioContextIsAllowedToStart(isAllowedToStart) {
+ const ac = content.ac;
+ if (isAllowedToStart) {
+ await ac.allowedToStart;
+ ok(ac.state === "running", `AudioContext is running.`);
+ } else {
+ await ac.notAllowedToStart;
+ ok(ac.state === "suspended", `AudioContext is not started yet.`);
+ }
+}
+
+async function resumeAudioContext(isAllowedToStart) {
+ const ac = content.ac;
+ const resumePromise = ac.resume();
+ const blockedPromise = new Promise(resolve => {
+ ac.addEventListener(
+ "blocked",
+ function () {
+ resolve();
+ },
+ { once: true }
+ );
+ });
+
+ if (isAllowedToStart) {
+ await resumePromise;
+ ok(true, `successfully resume AudioContext.`);
+ } else {
+ await blockedPromise;
+ ok(true, `resume is blocked because AudioContext is not allowed to start.`);
+ }
+}
+
+function startAudioContext(method) {
+ const ac = content.ac;
+ if (method == "AudioContext") {
+ info(`using AudioContext.resume() to start AudioContext`);
+ ac.resume();
+ return;
+ }
+ info(`using ${method}.start() to start AudioContext`);
+ let node;
+ switch (method) {
+ case "AudioBufferSourceNode":
+ node = ac.createBufferSource();
+ break;
+ case "ConstantSourceNode":
+ node = ac.createConstantSource();
+ break;
+ case "OscillatorNode":
+ node = ac.createOscillator();
+ break;
+ default:
+ ok(false, "undefined AudioScheduledSourceNode type");
+ return;
+ }
+ node.connect(ac.destination);
+ node.start();
+}
+
+async function testAutoplayExistingPermission({ name, permission }) {
+ info(`- starting \"${name}\" -`);
+ const tab = await BrowserTestUtils.openNewForegroundTab(
+ window.gBrowser,
+ PAGE
+ );
+ const browser = tab.linkedBrowser;
+
+ info(`- set the 'autoplay-media' permission -`);
+ const promptShow = () =>
+ PopupNotifications.getNotification("autoplay-media", browser);
+ PermissionTestUtils.add(browser.currentURI, "autoplay-media", permission);
+ ok(!promptShow(), `should not be showing permission prompt yet`);
+
+ info(`- create audio context -`);
+ await SpecialPowers.spawn(browser, [], createAudioContext);
+
+ info(`- check AudioContext status -`);
+ const isAllowedToStart = permission === Services.perms.ALLOW_ACTION;
+ await SpecialPowers.spawn(
+ browser,
+ [isAllowedToStart],
+ checkIfAudioContextIsAllowedToStart
+ );
+ await SpecialPowers.spawn(browser, [isAllowedToStart], resumeAudioContext);
+
+ info(`- remove tab -`);
+ PermissionTestUtils.remove(browser.currentURI, "autoplay-media");
+ await BrowserTestUtils.removeTab(tab);
+}
+
+async function testAutoplayUnknownPermission({ name, method }) {
+ info(`- starting \"${name}\" -`);
+ const tab = await BrowserTestUtils.openNewForegroundTab(
+ window.gBrowser,
+ PAGE
+ );
+ const browser = tab.linkedBrowser;
+
+ info(`- set the 'autoplay-media' permission to UNKNOWN -`);
+ const promptShow = () =>
+ PopupNotifications.getNotification("autoplay-media", browser);
+ PermissionTestUtils.add(
+ browser.currentURI,
+ "autoplay-media",
+ Services.perms.UNKNOWN_ACTION
+ );
+ ok(!promptShow(), `should not be showing permission prompt yet`);
+
+ info(`- create AudioContext which should not start -`);
+ await SpecialPowers.spawn(browser, [], createAudioContext);
+ await SpecialPowers.spawn(
+ browser,
+ [false],
+ checkIfAudioContextIsAllowedToStart
+ );
+
+ info(`- simulate user activate the page -`);
+ await SpecialPowers.spawn(browser, [], () => {
+ content.document.notifyUserGestureActivation();
+ });
+
+ info(`- try to start AudioContext -`);
+ await SpecialPowers.spawn(browser, [method], startAudioContext);
+
+ info(`- check AudioContext status -`);
+ await SpecialPowers.spawn(
+ browser,
+ [true],
+ checkIfAudioContextIsAllowedToStart
+ );
+ await SpecialPowers.spawn(browser, [true], resumeAudioContext);
+
+ info(`- remove tab -`);
+ PermissionTestUtils.remove(browser.currentURI, "autoplay-media");
+ await BrowserTestUtils.removeTab(tab);
+}
+
+add_task(async function start_tests() {
+ info("- setup test preference -");
+ await setup_test_preference();
+
+ await testAutoplayExistingPermission({
+ name: "Prexisting allow permission",
+ permission: Services.perms.ALLOW_ACTION,
+ });
+ await testAutoplayExistingPermission({
+ name: "Prexisting block permission",
+ permission: Services.perms.DENY_ACTION,
+ });
+ const startMethods = [
+ "AudioContext",
+ "AudioBufferSourceNode",
+ "ConstantSourceNode",
+ "OscillatorNode",
+ ];
+ for (let method of startMethods) {
+ await testAutoplayUnknownPermission({
+ name: "Unknown permission and start AudioContext after granting user activation",
+ method,
+ });
+ }
+});
diff --git a/dom/media/autoplay/test/browser/browser_autoplay_policy_web_audio_with_gum.js b/dom/media/autoplay/test/browser/browser_autoplay_policy_web_audio_with_gum.js
new file mode 100644
index 0000000000..45d5c71a52
--- /dev/null
+++ b/dom/media/autoplay/test/browser/browser_autoplay_policy_web_audio_with_gum.js
@@ -0,0 +1,174 @@
+/* eslint-disable mozilla/no-arbitrary-setTimeout */
+
+/**
+ * This test is used to ensure web audio can be allowed to start when we have
+ * GUM permission.
+ */
+add_task(async function startTestingWebAudioWithGUM() {
+ info("- setup test preference -");
+ await setupTestPreferences();
+
+ info("- test web audio with gUM success -");
+ await testWebAudioWithGUM({
+ constraints: { audio: true },
+ shouldAllowStartingContext: true,
+ });
+ await testWebAudioWithGUM({
+ constraints: { video: true },
+ shouldAllowStartingContext: true,
+ });
+ await testWebAudioWithGUM({
+ constraints: { video: true, audio: true },
+ shouldAllowStartingContext: true,
+ });
+
+ await SpecialPowers.pushPrefEnv({
+ set: [["media.navigator.permission.force", true]],
+ }).then(async function () {
+ info("- test web audio with gUM denied -");
+ await testWebAudioWithGUM({
+ constraints: { video: true },
+ shouldAllowStartingContext: false,
+ });
+ await testWebAudioWithGUM({
+ constraints: { audio: true },
+ shouldAllowStartingContext: false,
+ });
+ await testWebAudioWithGUM({
+ constraints: { video: true, audio: true },
+ shouldAllowStartingContext: false,
+ });
+ });
+});
+
+/**
+ * testing helper functions
+ */
+function setupTestPreferences() {
+ return SpecialPowers.pushPrefEnv({
+ set: [
+ ["media.autoplay.default", SpecialPowers.Ci.nsIAutoplay.BLOCKED],
+ ["media.autoplay.blocking_policy", 0],
+ ["media.autoplay.block-event.enabled", true],
+ ["media.autoplay.block-webaudio", true],
+ ["media.navigator.permission.fake", true],
+ ],
+ });
+}
+
+function createAudioContext() {
+ content.ac = new content.AudioContext();
+ let ac = content.ac;
+ ac.resumePromises = [];
+ ac.stateChangePromise = new Promise(resolve => {
+ ac.addEventListener(
+ "statechange",
+ function () {
+ resolve();
+ },
+ { once: true }
+ );
+ });
+ ac.notAllowedToStart = new Promise(resolve => {
+ ac.addEventListener(
+ "blocked",
+ function () {
+ resolve();
+ },
+ { once: true }
+ );
+ });
+}
+
+async function checkingAudioContextRunningState() {
+ let ac = content.ac;
+ await ac.notAllowedToStart;
+ ok(ac.state === "suspended", `AudioContext is not started yet.`);
+}
+
+function resumeWithoutExpectedSuccess() {
+ let ac = content.ac;
+ let promise = ac.resume();
+ ac.resumePromises.push(promise);
+ return new Promise((resolve, reject) => {
+ content.setTimeout(() => {
+ if (ac.state == "suspended") {
+ ok(true, "audio context is still suspended");
+ resolve();
+ } else {
+ reject("audio context should not be allowed to start");
+ }
+ }, 2000);
+ });
+}
+
+function resumeWithExpectedSuccess() {
+ let ac = content.ac;
+ ac.resumePromises.push(ac.resume());
+ return Promise.all(ac.resumePromises).then(() => {
+ ok(ac.state == "running", "audio context starts running");
+ });
+}
+
+async function callGUM(testParameters) {
+ info("- calling gum with " + JSON.stringify(testParameters.constraints));
+ if (testParameters.shouldAllowStartingContext) {
+ // Because of the prefs we've set and passed, this is going to allow the
+ // window to start an AudioContext synchronously.
+ testParameters.constraints.fake = true;
+ await content.navigator.mediaDevices.getUserMedia(
+ testParameters.constraints
+ );
+ return;
+ }
+
+ // Call gUM, without sucess: we've made it so that only fake requests
+ // succeed without permission, and this is requesting non-fake-devices. Return
+ // a resolved promise so that the test continues, but the getUserMedia Promise
+ // will never be resolved.
+ // We do this to check that it's not merely calling gUM that allows starting
+ // an AudioContext, it's having the Promise it return resolved successfuly,
+ // because of saved permissions for an origin or explicit user consent using
+ // the prompt.
+ content.navigator.mediaDevices.getUserMedia(testParameters.constraints);
+}
+
+async function testWebAudioWithGUM(testParameters) {
+ info("- open new tab -");
+ let tab = await BrowserTestUtils.openNewForegroundTab(
+ window.gBrowser,
+ "https://example.com"
+ );
+ info("- create audio context -");
+ await SpecialPowers.spawn(tab.linkedBrowser, [], createAudioContext);
+
+ info("- check whether audio context starts running -");
+ try {
+ await SpecialPowers.spawn(
+ tab.linkedBrowser,
+ [],
+ checkingAudioContextRunningState
+ );
+ } catch (error) {
+ ok(false, error.toString());
+ }
+
+ try {
+ await SpecialPowers.spawn(tab.linkedBrowser, [testParameters], callGUM);
+ } catch (error) {
+ ok(false, error.toString());
+ }
+
+ info("- calling resume() again");
+ try {
+ let resumeFunc = testParameters.shouldAllowStartingContext
+ ? resumeWithExpectedSuccess
+ : resumeWithoutExpectedSuccess;
+ await SpecialPowers.spawn(tab.linkedBrowser, [], resumeFunc);
+ } catch (error) {
+ ok(false, error.toString());
+ }
+
+ info("- remove tab -");
+ await BrowserTestUtils.removeTab(tab);
+}
diff --git a/dom/media/autoplay/test/browser/browser_autoplay_videoDocument.js b/dom/media/autoplay/test/browser/browser_autoplay_videoDocument.js
new file mode 100644
index 0000000000..77ce4ddbc1
--- /dev/null
+++ b/dom/media/autoplay/test/browser/browser_autoplay_videoDocument.js
@@ -0,0 +1,80 @@
+"use strict";
+
+const PAGE = GetTestWebBasedURL("audio.ogg");
+
+function setup_test_preference() {
+ return SpecialPowers.pushPrefEnv({
+ set: [
+ ["media.autoplay.default", SpecialPowers.Ci.nsIAutoplay.BLOCKED],
+ ["media.autoplay.blocking_policy", 0],
+ ],
+ });
+}
+
+async function checkIsVideoDocumentAutoplay(browser) {
+ const played = await SpecialPowers.spawn(browser, [], async () => {
+ const video = content.document.getElementsByTagName("video")[0];
+ const played =
+ video &&
+ (await video.play().then(
+ () => true,
+ () => false
+ ));
+ return played;
+ });
+ ok(played, "Should be able to play in video document.");
+}
+
+async function checkIsIframeVideoDocumentAutoplay(browser) {
+ info("- create iframe video document -");
+ const iframeBC = await SpecialPowers.spawn(browser, [PAGE], async pageURL => {
+ const iframe = content.document.createElement("iframe");
+ iframe.src = pageURL;
+ content.document.body.appendChild(iframe);
+ const iframeLoaded = new Promise((resolve, reject) => {
+ iframe.addEventListener("load", e => resolve(), { once: true });
+ });
+ await iframeLoaded;
+ return iframe.browsingContext;
+ });
+
+ info("- check whether iframe video document starts playing -");
+ const [paused, playedLength] = await SpecialPowers.spawn(iframeBC, [], () => {
+ const video = content.document.querySelector("video");
+ return [video.paused, video.played.length];
+ });
+ ok(paused, "Subdoc video should not have played");
+ is(playedLength, 0, "Should have empty played ranges");
+}
+
+add_task(async () => {
+ await BrowserTestUtils.withNewTab(
+ {
+ gBrowser,
+ url: PAGE,
+ },
+ async browser => {
+ info("- setup test preference -");
+ await setup_test_preference();
+
+ info(`- check whether video document is autoplay -`);
+ await checkIsVideoDocumentAutoplay(browser);
+ }
+ );
+});
+
+add_task(async () => {
+ await BrowserTestUtils.withNewTab(
+ {
+ gBrowser,
+ url: "about:blank",
+ },
+ async browser => {
+ info("- setup test preference -");
+ await setup_test_preference();
+
+ info(`- check whether video document in iframe is autoplay -`);
+ await checkIsIframeVideoDocumentAutoplay(browser);
+ }
+ );
+});
diff --git a/dom/media/autoplay/test/browser/file_empty.html b/dom/media/autoplay/test/browser/file_empty.html
new file mode 100644
index 0000000000..d2b0361f09
--- /dev/null
+++ b/dom/media/autoplay/test/browser/file_empty.html
@@ -0,0 +1,8 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title>Page left intentionally blank...</title>
+ </head>
+ <body>
+ </body>
+</html>
diff --git a/dom/media/autoplay/test/browser/file_mediaplayback_frame.html b/dom/media/autoplay/test/browser/file_mediaplayback_frame.html
new file mode 100644
index 0000000000..b5685c07b3
--- /dev/null
+++ b/dom/media/autoplay/test/browser/file_mediaplayback_frame.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>Non-Autoplay page being used in Iframe</title>
+</head>
+<body>
+<video id="video" src="gizmo.mp4" loop></video>
+<script type="text/javascript">
+
+const video = document.getElementById("video");
+const w = window.opener || window.parent;
+
+window.onmessage = async event => {
+ if (event.data == "play") {
+ let rv = await video.play().then(() => true, () => false);
+ w.postMessage(rv ? "played" : "blocked", "*");
+ }
+}
+</script>
+</body>
+</html>
diff --git a/dom/media/autoplay/test/browser/file_nonAutoplayAudio.html b/dom/media/autoplay/test/browser/file_nonAutoplayAudio.html
new file mode 100644
index 0000000000..4d2641021a
--- /dev/null
+++ b/dom/media/autoplay/test/browser/file_nonAutoplayAudio.html
@@ -0,0 +1,7 @@
+<!DOCTYPE html>
+<head>
+ <meta content="text/html;charset=utf-8" http-equiv="Content-Type">
+ <meta content="utf-8" http-equiv="encoding">
+</head>
+<body>
+<audio id="testAudio" src="audio.ogg" loop></audio>
diff --git a/dom/media/autoplay/test/browser/file_video.html b/dom/media/autoplay/test/browser/file_video.html
new file mode 100644
index 0000000000..3c70268fbb
--- /dev/null
+++ b/dom/media/autoplay/test/browser/file_video.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>video</title>
+</head>
+<body>
+<video id="v" src="gizmo.mp4" controls loop></video>
+</body>
+</html>
diff --git a/dom/media/autoplay/test/browser/head.js b/dom/media/autoplay/test/browser/head.js
new file mode 100644
index 0000000000..c84850900a
--- /dev/null
+++ b/dom/media/autoplay/test/browser/head.js
@@ -0,0 +1,149 @@
+/**
+ * Return a web-based URL for a given file based on the testing directory.
+ * @param {String} fileName
+ * file that caller wants its web-based url
+ * @param {Boolean} crossOrigin [optional]
+ * if set, then return a url with different origin. The default value is
+ * false.
+ */
+function GetTestWebBasedURL(fileName, { crossOrigin = false } = {}) {
+ const origin = crossOrigin ? "http://example.org" : "http://example.com";
+ return (
+ getRootDirectory(gTestPath).replace("chrome://mochitests/content", origin) +
+ fileName
+ );
+}
+
+/**
+ * Runs a content script that creates an autoplay video.
+ * @param {browserElement} browser
+ * the browser to run the script in
+ * @param {object} args
+ * test case definition, required members
+ * {
+ * mode: String, "autoplay attribute" or "call play".
+ * }
+ */
+function loadAutoplayVideo(browser, args) {
+ return SpecialPowers.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(
+ "playing",
+ 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.
+ * @param {browserElement} browser
+ * the browser to run the script in
+ * @param {object} 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 SpecialPowers.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);
+ });
+}
+
+/**
+ * Create a tab that will load the given url, and define an autoplay policy
+ * check function inside the content window in that tab. This function should
+ * only be used when `dom.media.autoplay-policy-detection.enabled` is true.
+ * @param {url} url
+ * the url which the created tab should load
+ */
+async function createTabAndSetupPolicyAssertFunc(url) {
+ let tab = await BrowserTestUtils.openNewForegroundTab(window.gBrowser, url);
+ await SpecialPowers.spawn(tab.linkedBrowser, [], _ => {
+ content.video = content.document.createElement("video");
+ content.ac = new content.AudioContext();
+ content.assertAutoplayPolicy = ({
+ resultForElementType,
+ resultForElement,
+ resultForContextType,
+ resultForContext,
+ }) => {
+ is(
+ content.navigator.getAutoplayPolicy("mediaelement"),
+ resultForElementType,
+ "getAutoplayPolicy('mediaelement') returns correct value"
+ );
+ is(
+ content.navigator.getAutoplayPolicy(content.video),
+ resultForElement,
+ "getAutoplayPolicy(content.video) returns correct value"
+ );
+ // note, per spec "allowed-muted" won't be used for audio context.
+ is(
+ content.navigator.getAutoplayPolicy("audiocontext"),
+ resultForContextType,
+ "getAutoplayPolicy('audiocontext') returns correct value"
+ );
+ is(
+ content.navigator.getAutoplayPolicy(content.ac),
+ resultForContext,
+ "getAutoplayPolicy(content.ac) returns correct value"
+ );
+ };
+ });
+ return tab;
+}