browser_autoplay_policy_user_gestures.js (7600B)
1 /* eslint-disable mozilla/no-arbitrary-setTimeout */ 2 3 const VIDEO_PAGE = GetTestWebBasedURL("file_video.html"); 4 5 const UserGestures = { 6 MOUSE_CLICK: "mouse-click", 7 MOUSE_MOVE: "mouse-move", 8 KEYBOARD_PRESS: "keyboard-press", 9 }; 10 11 const UserGestureTests = [ 12 { type: UserGestures.MOUSE_CLICK, isActivationGesture: true }, 13 { type: UserGestures.MOUSE_MOVE, isActivationGesture: false }, 14 // test different keycode here. printable key, non-printable key and other 15 // special keys. 16 { 17 type: UserGestures.KEYBOARD_PRESS, 18 isActivationGesture: true, 19 keyCode: "a", 20 }, 21 { 22 type: UserGestures.KEYBOARD_PRESS, 23 isActivationGesture: false, 24 keyCode: "VK_ESCAPE", 25 }, 26 { 27 type: UserGestures.KEYBOARD_PRESS, 28 isActivationGesture: true, 29 keyCode: "VK_RETURN", 30 }, 31 { 32 type: UserGestures.KEYBOARD_PRESS, 33 isActivationGesture: true, 34 keyCode: "VK_SPACE", 35 }, 36 ]; 37 38 /** 39 * This test is used to ensure we would stop blocking autoplay after document 40 * has been activated by user gestures. We would treat mouse clicking, key board 41 * pressing (printable keys or carriage return) as valid user gesture input. 42 */ 43 add_task(async function startTestUserGestureInput() { 44 info("- setup test preference -"); 45 await setupTestPreferences(); 46 47 info("- test play when page doesn't be activated -"); 48 await testPlayWithoutUserGesture(); 49 50 info("- test play after page got user gesture -"); 51 for (let idx = 0; idx < UserGestureTests.length; idx++) { 52 info("- test play after page got user gesture -"); 53 await testPlayWithUserGesture(UserGestureTests[idx]); 54 55 info("- test web audio with user gesture -"); 56 await testWebAudioWithUserGesture(UserGestureTests[idx]); 57 } 58 }); 59 60 /** 61 * testing helper functions 62 */ 63 function setupTestPreferences() { 64 return SpecialPowers.pushPrefEnv({ 65 set: [ 66 ["media.autoplay.default", SpecialPowers.Ci.nsIAutoplay.BLOCKED], 67 ["media.autoplay.blocking_policy", 0], 68 ["media.autoplay.block-event.enabled", true], 69 ], 70 }); 71 } 72 73 function simulateUserGesture(gesture, targetBrowser) { 74 info(`- simulate ${gesture.type} event -`); 75 switch (gesture.type) { 76 case UserGestures.MOUSE_CLICK: 77 return BrowserTestUtils.synthesizeMouseAtCenter( 78 "body", 79 { button: 0 }, 80 targetBrowser 81 ); 82 case UserGestures.MOUSE_MOVE: 83 return BrowserTestUtils.synthesizeMouseAtCenter( 84 "body", 85 { type: "mousemove" }, 86 targetBrowser 87 ); 88 case UserGestures.KEYBOARD_PRESS: 89 info(`- keycode=${gesture.keyCode} -`); 90 return BrowserTestUtils.synthesizeKey(gesture.keyCode, {}, targetBrowser); 91 default: 92 ok(false, "undefined user gesture"); 93 return false; 94 } 95 } 96 97 async function testPlayWithoutUserGesture() { 98 info("- open new tab -"); 99 let tab = await BrowserTestUtils.openNewForegroundTab( 100 window.gBrowser, 101 "about:blank" 102 ); 103 BrowserTestUtils.startLoadingURIString(tab.linkedBrowser, VIDEO_PAGE); 104 await BrowserTestUtils.browserLoaded(tab.linkedBrowser); 105 106 async function checkAutoplayKeyword() { 107 info("- create an new autoplay video -"); 108 let video = content.document.createElement("video"); 109 video.src = "gizmo.mp4"; 110 video.autoplay = true; 111 let canplayPromise = new Promise(function (resolve) { 112 video.addEventListener( 113 "canplaythrough", 114 function () { 115 resolve(); 116 }, 117 { once: true } 118 ); 119 }); 120 content.document.body.appendChild(video); 121 122 info("- can't autoplay without user activation -"); 123 await canplayPromise; 124 ok(video.paused, "video can't start without user input."); 125 } 126 await SpecialPowers.spawn(tab.linkedBrowser, [], checkAutoplayKeyword); 127 128 async function playVideo() { 129 let video = content.document.getElementById("v"); 130 info("- call play() without user activation -"); 131 await video.play().catch(function () { 132 ok(video.paused, "video can't start play without user input."); 133 }); 134 } 135 await SpecialPowers.spawn(tab.linkedBrowser, [], playVideo); 136 137 info("- remove tab -"); 138 BrowserTestUtils.removeTab(tab); 139 } 140 141 async function testPlayWithUserGesture(gesture) { 142 info("- open new tab -"); 143 let tab = await BrowserTestUtils.openNewForegroundTab( 144 window.gBrowser, 145 "about:blank" 146 ); 147 BrowserTestUtils.startLoadingURIString(tab.linkedBrowser, VIDEO_PAGE); 148 await BrowserTestUtils.browserLoaded(tab.linkedBrowser); 149 150 info("- simulate user gesture -"); 151 await simulateUserGesture(gesture, tab.linkedBrowser); 152 153 info("- call play() -"); 154 async function playVideo(gesture) { 155 let video = content.document.getElementById("v"); 156 try { 157 await video.play(); 158 ok(gesture.isActivationGesture, "user gesture can activate the page"); 159 ok(!video.paused, "video starts playing."); 160 } catch (e) { 161 ok( 162 !gesture.isActivationGesture, 163 "user gesture can not activate the page" 164 ); 165 ok(video.paused, "video can not start playing."); 166 } 167 } 168 169 await SpecialPowers.spawn(tab.linkedBrowser, [gesture], playVideo); 170 171 info("- remove tab -"); 172 BrowserTestUtils.removeTab(tab); 173 } 174 175 function createAudioContext() { 176 content.ac = new content.AudioContext(); 177 let ac = content.ac; 178 ac.resumePromises = []; 179 ac.stateChangePromise = new Promise(resolve => { 180 ac.addEventListener( 181 "statechange", 182 function () { 183 resolve(); 184 }, 185 { once: true } 186 ); 187 }); 188 ac.notAllowedToStart = new Promise(resolve => { 189 ac.addEventListener( 190 "blocked", 191 function () { 192 resolve(); 193 }, 194 { once: true } 195 ); 196 }); 197 } 198 199 function resumeWithoutExpectedSuccess() { 200 let ac = content.ac; 201 let promise = ac.resume(); 202 ac.resumePromises.push(promise); 203 return new Promise((resolve, reject) => { 204 content.setTimeout(() => { 205 if (ac.state == "suspended") { 206 ok(true, "audio context is still suspended"); 207 resolve(); 208 } else { 209 reject("audio context should not be allowed to start"); 210 } 211 }, 2000); 212 }); 213 } 214 215 function resumeWithExpectedSuccess() { 216 let ac = content.ac; 217 ac.resumePromises.push(ac.resume()); 218 return Promise.all(ac.resumePromises).then(() => { 219 Assert.equal(ac.state, "running", "audio context starts running"); 220 }); 221 } 222 223 async function testWebAudioWithUserGesture(gesture) { 224 info("- open new tab -"); 225 let tab = await BrowserTestUtils.openNewForegroundTab( 226 window.gBrowser, 227 "about:blank" 228 ); 229 info("- create audio context -"); 230 await SpecialPowers.spawn(tab.linkedBrowser, [], () => { 231 content.ac = new content.AudioContext(); 232 let ac = content.ac; 233 ac.resumePromises = []; 234 return new Promise(resolve => { 235 ac.addEventListener( 236 "blocked", 237 function () { 238 Assert.equal( 239 ac.state, 240 "suspended", 241 `AudioContext is not started yet.` 242 ); 243 resolve(); 244 }, 245 { once: true } 246 ); 247 }); 248 }); 249 250 info("- calling resume() -"); 251 try { 252 await SpecialPowers.spawn( 253 tab.linkedBrowser, 254 [], 255 resumeWithoutExpectedSuccess 256 ); 257 } catch (error) { 258 ok(false, error.toString()); 259 } 260 261 info("- simulate user gesture -"); 262 await simulateUserGesture(gesture, tab.linkedBrowser); 263 264 info("- calling resume() again"); 265 try { 266 let resumeFunc = gesture.isActivationGesture 267 ? resumeWithExpectedSuccess 268 : resumeWithoutExpectedSuccess; 269 await SpecialPowers.spawn(tab.linkedBrowser, [], resumeFunc); 270 } catch (error) { 271 ok(false, error.toString()); 272 } 273 274 info("- remove tab -"); 275 await BrowserTestUtils.removeTab(tab); 276 }