browser_media_control_non_eligible_media.js (6759B)
1 const PAGE_NON_ELIGIBLE_MEDIA = 2 "https://example.com/browser/dom/media/mediacontrol/tests/browser/file_non_eligible_media.html"; 3 4 // Import this in order to use `triggerPictureInPicture()`. 5 Services.scriptloader.loadSubScript( 6 "chrome://mochitests/content/browser/toolkit/components/pictureinpicture/tests/head.js", 7 this 8 ); 9 10 // Bug 1673509 - This test requests a lot of fullscreen for media elements, 11 // which sometime Gecko would take longer time to fulfill. 12 requestLongerTimeout(2); 13 14 // This array contains the elements' id in `file_non_eligible_media.html`. 15 const gNonEligibleElementIds = [ 16 "muted", 17 "volume-0", 18 "silent-audio-track", 19 "no-audio-track", 20 "short-duration", 21 "inaudible-captured-media", 22 ]; 23 24 /** 25 * This test is used to test couples of things about what kinds of media is 26 * eligible for being controlled by media control keys. 27 * (1) If media is inaudible all the time, then we would not control it. 28 * (2) If media starts inaudibly, we would not try to control it. But once it 29 * becomes audible later, we would keep controlling it until it's destroyed. 30 * (3) If media's duration is too short (<3s), then we would not control it. 31 */ 32 add_setup(async function setupTestingPref() { 33 await SpecialPowers.pushPrefEnv({ 34 set: [ 35 ["media.mediacontrol.testingevents.enabled", true], 36 ["test.wait300msAfterTabSwitch", true], 37 ], 38 }); 39 }); 40 41 add_task( 42 async function testNonAudibleMediaCantActivateControllerButAudibleMediaCan() { 43 for (const elementId of gNonEligibleElementIds) { 44 info(`open new tab with non eligible media elements`); 45 const tab = await createLoadedTabWrapper(PAGE_NON_ELIGIBLE_MEDIA, { 46 needCheck: couldElementBecomeEligible(elementId), 47 }); 48 49 info(`although media is playing but it won't activate controller`); 50 await Promise.all([ 51 startNonEligibleMedia(tab, elementId), 52 checkIfMediaIsStillPlaying(tab, elementId), 53 ]); 54 ok(!tab.controller.isActive, "controller is still inactive"); 55 56 if (couldElementBecomeEligible(elementId)) { 57 info(`make element ${elementId} audible would activate controller`); 58 await Promise.all([ 59 makeElementEligible(tab, elementId), 60 checkOrWaitUntilControllerBecomeActive(tab), 61 ]); 62 } 63 64 info(`remove tab`); 65 await tab.close(); 66 } 67 } 68 ); 69 70 /** 71 * Normally those media are not able to being controlled, however, once they 72 * enter fullsceen or Picture-in-Picture mode, then they can be controlled. 73 */ 74 add_task(async function testNonEligibleMediaEnterFullscreen() { 75 info(`open new tab with non eligible media elements`); 76 const tab = await createLoadedTabWrapper(PAGE_NON_ELIGIBLE_MEDIA); 77 78 for (const elementId of gNonEligibleElementIds) { 79 await startNonEligibleMedia(tab, elementId); 80 81 info(`entering fullscreen should activate the media controller`); 82 await enterFullScreen(tab, elementId); 83 await checkOrWaitUntilControllerBecomeActive(tab); 84 ok(true, `fullscreen ${elementId} media is able to being controlled`); 85 86 info(`leave fullscreen`); 87 await leaveFullScreen(tab); 88 } 89 info(`remove tab`); 90 await tab.close(); 91 }); 92 93 add_task(async function testNonEligibleMediaEnterPIPMode() { 94 info(`open new tab with non eligible media elements`); 95 const tab = await createLoadedTabWrapper(PAGE_NON_ELIGIBLE_MEDIA); 96 97 for (const elementId of gNonEligibleElementIds) { 98 await startNonEligibleMedia(tab, elementId); 99 100 info(`media entering PIP mode should activate the media controller`); 101 const winPIP = await triggerPictureInPicture(tab.linkedBrowser, elementId); 102 await checkOrWaitUntilControllerBecomeActive(tab); 103 ok(true, `PIP ${elementId} media is able to being controlled`); 104 105 info(`stop PIP mode`); 106 await BrowserTestUtils.closeWindow(winPIP); 107 } 108 info(`remove tab`); 109 await tab.close(); 110 }); 111 112 /** 113 * The following are helper functions. 114 */ 115 function startNonEligibleMedia(tab, elementId) { 116 return SpecialPowers.spawn(tab.linkedBrowser, [elementId], Id => { 117 const video = content.document.getElementById(Id); 118 if (!video) { 119 ok(false, `can't get the media element!`); 120 } 121 if (Id == "volume-0") { 122 video.volume = 0.0; 123 } 124 if (Id == "inaudible-captured-media") { 125 const context = new content.AudioContext(); 126 context.createMediaElementSource(video); 127 } 128 info(`start non eligible media ${Id}`); 129 return video.play(); 130 }); 131 } 132 133 function checkIfMediaIsStillPlaying(tab, elementId) { 134 return SpecialPowers.spawn(tab.linkedBrowser, [elementId], Id => { 135 const video = content.document.getElementById(Id); 136 if (!video) { 137 ok(false, `can't get the media element!`); 138 } 139 return new Promise(r => { 140 // In order to test "media isn't affected by media control", we would not 141 // only check `mPaused`, we would also oberve "timeupdate" event multiple 142 // times to ensure that video is still playing continually. 143 let timeUpdateCount = 0; 144 ok(!video.paused); 145 video.ontimeupdate = () => { 146 if (++timeUpdateCount == 3) { 147 video.ontimeupdate = null; 148 r(); 149 } 150 }; 151 }); 152 }); 153 } 154 155 function couldElementBecomeEligible(elementId) { 156 return elementId == "muted" || elementId == "volume-0"; 157 } 158 159 function makeElementEligible(tab, elementId) { 160 return SpecialPowers.spawn(tab.linkedBrowser, [elementId], Id => { 161 const video = content.document.getElementById(Id); 162 if (!video) { 163 ok(false, `can't get the media element!`); 164 } 165 // to turn inaudible media become audible in order to be controlled. 166 video.volume = 1.0; 167 video.muted = false; 168 }); 169 } 170 171 function waitUntilMediaPaused(tab, elementId) { 172 return SpecialPowers.spawn(tab.linkedBrowser, [elementId], Id => { 173 const video = content.document.getElementById(Id); 174 if (!video) { 175 ok(false, `can't get the media element!`); 176 } 177 if (video.paused) { 178 ok(true, "media has been paused"); 179 return Promise.resolve(); 180 } 181 return new Promise(r => (video.onpaused = r)); 182 }); 183 } 184 185 function enterFullScreen(tab, elementId) { 186 return SpecialPowers.spawn(tab.linkedBrowser, [elementId], Id => { 187 return new Promise(r => { 188 const element = content.document.getElementById(Id); 189 element.requestFullscreen(); 190 element.onfullscreenchange = () => { 191 element.onfullscreenchange = null; 192 element.onfullscreenerror = null; 193 r(); 194 }; 195 element.onfullscreenerror = () => { 196 // Retry until the element successfully enters fullscreen. 197 element.requestFullscreen(); 198 }; 199 }); 200 }); 201 } 202 203 function leaveFullScreen(tab) { 204 return SpecialPowers.spawn(tab.linkedBrowser, [], _ => { 205 return content.document.exitFullscreen(); 206 }); 207 }