test_autoplay_policy_web_audio_AudioParamStream.html (5670B)
1 <!DOCTYPE HTML> 2 <html> 3 <head> 4 <title>Autoplay policy test : suspend/resume the AudioParam's stream</title> 5 <script src="/tests/SimpleTest/SimpleTest.js"></script> 6 <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> 7 <script type="text/javascript" src="manifest.js"></script> 8 </head> 9 <body> 10 <script> 11 12 /** 13 * This test is used to ensure the AudioParam's stream can be suspended/resumed 14 * by AudioContext. 15 */ 16 17 SimpleTest.waitForExplicitFinish(); 18 19 (async function testSuspendAndResumeAudioParamStreams() { 20 await setupTestPreferences(); 21 22 info(`- create the AudioContext -`); 23 createAudioContext(); 24 25 info(`- the AudioContext is not allowed to start in beginning -`); 26 await audioContextShouldBeBlocked(); 27 28 info(`- connect AudioScheduledSourceNode to the AudioParam and start AudioScheduledSourceNode, the AudioParam's stream should be suspended in the beginning -`) 29 let audioParamsArr = await connectAudioScheduledSourceNodeToAudioParams(); 30 31 info(`- the AudioContext and the AudioParam's stream should be resumed -`); 32 await audioContextAndAudioParamStreamsShouldBeResumed(audioParamsArr); 33 34 info(`- suspend the AudioContext which should also suspend the AudioParam's stream -`); 35 await suspendAudioContextAndAudioParamStreams(audioParamsArr); 36 37 endTest(); 38 })(); 39 40 /** 41 * Test utility functions 42 */ 43 44 function setupTestPreferences() { 45 return SpecialPowers.pushPrefEnv({"set": [ 46 ["media.autoplay.default", SpecialPowers.Ci.nsIAutoplay.BLOCKED], 47 ["media.autoplay.blocking_policy", 0], 48 ["media.autoplay.block-event.enabled", true], 49 ]}); 50 } 51 52 function createAudioContext() { 53 /* global ac */ 54 window.ac = new AudioContext(); 55 56 ac.allowedToStart = new Promise(resolve => { 57 ac.addEventListener("statechange", function() { 58 if (ac.state === "running") { 59 resolve(); 60 } 61 }, {once: true}); 62 }); 63 64 ac.notAllowedToStart = new Promise(resolve => { 65 ac.addEventListener("blocked", async function() { 66 resolve(); 67 }, {once: true}); 68 }); 69 } 70 71 async function audioContextShouldBeBlocked() { 72 await ac.notAllowedToStart; 73 is(ac.state, "suspended", `AudioContext is blocked.`); 74 } 75 76 function createAudioParams(nodeType) { 77 switch (nodeType) { 78 case "audioBufferSource": { 79 let buffer = ac.createBufferSource(); 80 return [buffer.playbackRate, buffer.detune]; 81 } 82 case "biquadFilter": { 83 let bf = ac.createBiquadFilter(); 84 return [bf.frequency, bf.detune, bf.Q, bf.gain]; 85 } 86 case "constantSource": { 87 return [ac.createConstantSource().offset]; 88 } 89 case "dynamicsCompressor": { 90 let dc = ac.createDynamicsCompressor(); 91 return [dc.threshold, dc.knee, dc.ratio, dc.attack, dc.release]; 92 } 93 case "delay": { 94 return [ac.createDelay(5.0).delayTime]; 95 } 96 case "gain": { 97 return [ac.createGain().gain]; 98 } 99 case "oscillator": { 100 let osc = ac.createOscillator(); 101 return [osc.frequency, osc.detune]; 102 } 103 case "panner": { 104 let panner = ac.createPanner(); 105 return [panner.positionX, panner.positionY, panner.positionZ, 106 panner.orientationX, panner.orientationY, panner.orientationZ]; 107 } 108 case "stereoPanner": { 109 return [ac.createStereoPanner().pan]; 110 } 111 default: { 112 ok(false, `non-defined node type ${nodeType}.`); 113 return []; 114 } 115 } 116 } 117 118 function createAudioParamArrWithName(nodeType) { 119 let audioParamsArr = createAudioParams(nodeType); 120 for (let audioParam of audioParamsArr) { 121 audioParam.name = nodeType; 122 } 123 return audioParamsArr; 124 } 125 126 function createAllAudioParamsFromDifferentAudioNode() { 127 const NodesWithAudioParam = 128 ["audioBufferSource", "biquadFilter", "constantSource", "delay", 129 "dynamicsCompressor", "gain", "oscillator", "panner", "stereoPanner"]; 130 let audioParamsArr = []; 131 for (let nodeType of NodesWithAudioParam) { 132 audioParamsArr = audioParamsArr.concat(createAudioParamArrWithName(nodeType)); 133 } 134 ok(audioParamsArr.length >= NodesWithAudioParam.length, 135 `Length of AudioParam array (${audioParamsArr.length}) is longer than the " 136 "length of node type array (${NodesWithAudioParam.length}).`); 137 return audioParamsArr; 138 } 139 140 function connectAudioScheduledSourceNodeToAudioParams() { 141 let osc = ac.createOscillator(); 142 let audioParamsArr = createAllAudioParamsFromDifferentAudioNode(); 143 for (let audioParam of audioParamsArr) { 144 osc.connect(audioParam); 145 ok(SpecialPowers.wrap(audioParam).isTrackSuspended, 146 `(${audioParam.name}) audioParam's stream has been suspended.`); 147 } 148 149 // simulate user gesture in order to start video. 150 SpecialPowers.wrap(document).notifyUserGestureActivation(); 151 osc.start(); 152 return audioParamsArr; 153 } 154 155 async function audioContextAndAudioParamStreamsShouldBeResumed(audioParamsArr) { 156 await ac.allowedToStart; 157 is(ac.state, "running", `AudioContext is allowed to start.`); 158 for (let audioParam of audioParamsArr) { 159 ok(!SpecialPowers.wrap(audioParam).isTrackSuspended, 160 `(${audioParam.name}) audioParam's stream has been resumed.`);; 161 } 162 } 163 164 async function suspendAudioContextAndAudioParamStreams(audioParamsArr) { 165 await ac.suspend(); 166 is(ac.state, "suspended", `AudioContext is suspended.`); 167 for (let audioParam of audioParamsArr) { 168 ok(SpecialPowers.wrap(audioParam).isTrackSuspended, 169 `(${audioParam.name}) audioParam's stream has been suspended.`);; 170 } 171 } 172 173 function endTest() { 174 // reset the activation flag in order not to interfere following test in the 175 // verify mode which would run the test using same document couple times. 176 SpecialPowers.wrap(document).clearUserGestureActivation(); 177 SimpleTest.finish(); 178 } 179 180 </script>