browser_autoplay_policy_web_audio.js (6107B)
1 /** 2 * This test is used for testing whether WebAudio can be started correctly in 3 * different scenarios, such as 4 * 1) site has existing 'autoplay-media' permission for allowing autoplay 5 * 2) site has existing 'autoplay-media' permission for blocking autoplay 6 * 3) site doesn't have permission, start audio context by calling resume() or 7 * AudioScheduledNode.start() after granting user activation. 8 */ 9 "use strict"; 10 11 const { PermissionTestUtils } = ChromeUtils.importESModule( 12 "resource://testing-common/PermissionTestUtils.sys.mjs" 13 ); 14 15 const PAGE = GetTestWebBasedURL("file_empty.html"); 16 17 function setup_test_preference() { 18 return SpecialPowers.pushPrefEnv({ 19 set: [ 20 ["media.autoplay.default", SpecialPowers.Ci.nsIAutoplay.BLOCKED], 21 ["media.autoplay.blocking_policy", 0], 22 ["media.autoplay.block-event.enabled", true], 23 ], 24 }); 25 } 26 27 function createAudioContext() { 28 content.ac = new content.AudioContext(); 29 const ac = content.ac; 30 31 ac.allowedToStart = new Promise(resolve => { 32 ac.addEventListener( 33 "statechange", 34 function () { 35 if (ac.state === "running") { 36 resolve(); 37 } 38 }, 39 { once: true } 40 ); 41 }); 42 43 ac.notAllowedToStart = new Promise(resolve => { 44 ac.addEventListener( 45 "blocked", 46 function () { 47 resolve(); 48 }, 49 { once: true } 50 ); 51 }); 52 } 53 54 async function checkIfAudioContextIsAllowedToStart(isAllowedToStart) { 55 const ac = content.ac; 56 if (isAllowedToStart) { 57 await ac.allowedToStart; 58 Assert.strictEqual(ac.state, "running", `AudioContext is running.`); 59 } else { 60 await ac.notAllowedToStart; 61 Assert.strictEqual( 62 ac.state, 63 "suspended", 64 `AudioContext is not started yet.` 65 ); 66 } 67 } 68 69 async function resumeAudioContext(isAllowedToStart) { 70 const ac = content.ac; 71 const resumePromise = ac.resume(); 72 const blockedPromise = new Promise(resolve => { 73 ac.addEventListener( 74 "blocked", 75 function () { 76 resolve(); 77 }, 78 { once: true } 79 ); 80 }); 81 82 if (isAllowedToStart) { 83 await resumePromise; 84 ok(true, `successfully resume AudioContext.`); 85 } else { 86 await blockedPromise; 87 ok(true, `resume is blocked because AudioContext is not allowed to start.`); 88 } 89 } 90 91 function startAudioContext(method) { 92 const ac = content.ac; 93 if (method == "AudioContext") { 94 info(`using AudioContext.resume() to start AudioContext`); 95 ac.resume(); 96 return; 97 } 98 info(`using ${method}.start() to start AudioContext`); 99 let node; 100 switch (method) { 101 case "AudioBufferSourceNode": 102 node = ac.createBufferSource(); 103 break; 104 case "ConstantSourceNode": 105 node = ac.createConstantSource(); 106 break; 107 case "OscillatorNode": 108 node = ac.createOscillator(); 109 break; 110 default: 111 ok(false, "undefined AudioScheduledSourceNode type"); 112 return; 113 } 114 node.connect(ac.destination); 115 node.start(); 116 } 117 118 async function testAutoplayExistingPermission({ name, permission }) { 119 info(`- starting \"${name}\" -`); 120 const tab = await BrowserTestUtils.openNewForegroundTab( 121 window.gBrowser, 122 PAGE 123 ); 124 const browser = tab.linkedBrowser; 125 126 info(`- set the 'autoplay-media' permission -`); 127 const promptShow = () => 128 PopupNotifications.getNotification("autoplay-media", browser); 129 PermissionTestUtils.add(browser.currentURI, "autoplay-media", permission); 130 ok(!promptShow(), `should not be showing permission prompt yet`); 131 132 info(`- create audio context -`); 133 await SpecialPowers.spawn(browser, [], createAudioContext); 134 135 info(`- check AudioContext status -`); 136 const isAllowedToStart = permission === Services.perms.ALLOW_ACTION; 137 await SpecialPowers.spawn( 138 browser, 139 [isAllowedToStart], 140 checkIfAudioContextIsAllowedToStart 141 ); 142 await SpecialPowers.spawn(browser, [isAllowedToStart], resumeAudioContext); 143 144 info(`- remove tab -`); 145 PermissionTestUtils.remove(browser.currentURI, "autoplay-media"); 146 await BrowserTestUtils.removeTab(tab); 147 } 148 149 async function testAutoplayUnknownPermission({ name, method }) { 150 info(`- starting \"${name}\" -`); 151 const tab = await BrowserTestUtils.openNewForegroundTab( 152 window.gBrowser, 153 PAGE 154 ); 155 const browser = tab.linkedBrowser; 156 157 info(`- set the 'autoplay-media' permission to UNKNOWN -`); 158 const promptShow = () => 159 PopupNotifications.getNotification("autoplay-media", browser); 160 PermissionTestUtils.add( 161 browser.currentURI, 162 "autoplay-media", 163 Services.perms.UNKNOWN_ACTION 164 ); 165 ok(!promptShow(), `should not be showing permission prompt yet`); 166 167 info(`- create AudioContext which should not start -`); 168 await SpecialPowers.spawn(browser, [], createAudioContext); 169 await SpecialPowers.spawn( 170 browser, 171 [false], 172 checkIfAudioContextIsAllowedToStart 173 ); 174 175 info(`- simulate user activate the page -`); 176 await SpecialPowers.spawn(browser, [], () => { 177 content.document.notifyUserGestureActivation(); 178 }); 179 180 info(`- try to start AudioContext -`); 181 await SpecialPowers.spawn(browser, [method], startAudioContext); 182 183 info(`- check AudioContext status -`); 184 await SpecialPowers.spawn( 185 browser, 186 [true], 187 checkIfAudioContextIsAllowedToStart 188 ); 189 await SpecialPowers.spawn(browser, [true], resumeAudioContext); 190 191 info(`- remove tab -`); 192 PermissionTestUtils.remove(browser.currentURI, "autoplay-media"); 193 await BrowserTestUtils.removeTab(tab); 194 } 195 196 add_task(async function start_tests() { 197 info("- setup test preference -"); 198 await setup_test_preference(); 199 200 await testAutoplayExistingPermission({ 201 name: "Prexisting allow permission", 202 permission: Services.perms.ALLOW_ACTION, 203 }); 204 await testAutoplayExistingPermission({ 205 name: "Prexisting block permission", 206 permission: Services.perms.DENY_ACTION, 207 }); 208 const startMethods = [ 209 "AudioContext", 210 "AudioBufferSourceNode", 211 "ConstantSourceNode", 212 "OscillatorNode", 213 ]; 214 for (let method of startMethods) { 215 await testAutoplayUnknownPermission({ 216 name: "Unknown permission and start AudioContext after granting user activation", 217 method, 218 }); 219 } 220 });