forked from mirrors/gecko-dev
Check whether web audio starts when calling calling resume() or AudioScheduledNode.start() after granting user activation. Differential Revision: https://phabricator.services.mozilla.com/D14332 --HG-- extra : moz-landing-system : lando
178 lines
5.8 KiB
JavaScript
178 lines
5.8 KiB
JavaScript
/**
|
|
* 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";
|
|
|
|
ChromeUtils.import("resource:///modules/SitePermissions.jsm", this);
|
|
const PAGE = "https://example.com/browser/toolkit/content/tests/browser/file_empty.html";
|
|
|
|
function setup_test_preference() {
|
|
return SpecialPowers.pushPrefEnv({"set": [
|
|
["media.autoplay.default", SpecialPowers.Ci.nsIAutoplay.BLOCKED],
|
|
["media.autoplay.enabled.user-gestures-needed", true],
|
|
["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);
|
|
SitePermissions.set(browser.currentURI, "autoplay-media", permission);
|
|
ok(!promptShow(), `should not be showing permission prompt yet`);
|
|
|
|
info(`- create audio context -`);
|
|
loadFrameScript(browser, createAudioContext);
|
|
|
|
info(`- check AudioContext status -`);
|
|
const isAllowedToStart = permission === SitePermissions.ALLOW;
|
|
await ContentTask.spawn(browser, isAllowedToStart,
|
|
checkIfAudioContextIsAllowedToStart);
|
|
await ContentTask.spawn(browser, isAllowedToStart,
|
|
resumeAudioContext);
|
|
|
|
info(`- remove tab -`);
|
|
SitePermissions.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);
|
|
SitePermissions.set(browser.currentURI, "autoplay-media", SitePermissions.UNKNOWN);
|
|
ok(!promptShow(), `should not be showing permission prompt yet`);
|
|
|
|
info(`- create AudioContext which should not start -`);
|
|
loadFrameScript(browser, createAudioContext);
|
|
await ContentTask.spawn(browser, false, checkIfAudioContextIsAllowedToStart);
|
|
|
|
info(`- simulate user activate the page -`);
|
|
await ContentTask.spawn(browser, null, () => {
|
|
content.document.notifyUserGestureActivation();
|
|
});
|
|
|
|
info(`- try to start AudioContext -`);
|
|
await ContentTask.spawn(browser, method, startAudioContext);
|
|
|
|
info(`- check AudioContext status -`);
|
|
await ContentTask.spawn(browser, true /* allow to start */,
|
|
checkIfAudioContextIsAllowedToStart);
|
|
await ContentTask.spawn(browser, true /* allow to start */,
|
|
resumeAudioContext);
|
|
|
|
info(`- remove tab -`);
|
|
SitePermissions.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: SitePermissions.ALLOW,
|
|
});
|
|
await testAutoplayExistingPermission({
|
|
name: "Prexisting block permission",
|
|
permission: SitePermissions.BLOCK,
|
|
});
|
|
const startMethods = ["AudioContext", "AudioBufferSourceNode",
|
|
"ConstantSourceNode", "OscillatorNode"];
|
|
for (let method of startMethods) {
|
|
await testAutoplayUnknownPermission({
|
|
name: "Unknown permission and start AudioContext after granting user activation",
|
|
method,
|
|
});
|
|
}
|
|
});
|