diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
commit | 36d22d82aa202bb199967e9512281e9a53db42c9 (patch) | |
tree | 105e8c98ddea1c1e4784a60a5a6410fa416be2de /dom/media/autoplay/test/browser | |
parent | Initial commit. (diff) | |
download | firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.tar.xz firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.zip |
Adding upstream version 115.7.0esr.upstream/115.7.0esr
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'dom/media/autoplay/test/browser')
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 Binary files differnew file mode 100644 index 0000000000..7f1833508a --- /dev/null +++ b/dom/media/autoplay/test/browser/audio.ogg 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; +} |