forked from mirrors/gecko-dev
		
	
		
			
				
	
	
		
			131 lines
		
	
	
	
		
			4.4 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			131 lines
		
	
	
	
		
			4.4 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| const PAGE_NON_AUTOPLAY =
 | |
|   "https://example.com/browser/dom/media/mediacontrol/tests/browser/file_non_autoplay.html";
 | |
| const VIDEO_ID = "video";
 | |
| 
 | |
| add_task(async function setupTestingPref() {
 | |
|   await SpecialPowers.pushPrefEnv({
 | |
|     set: [
 | |
|       ["media.mediacontrol.testingevents.enabled", true],
 | |
|       ["dom.suspend_inactive.enabled", true],
 | |
|       ["dom.audiocontext.testing", true],
 | |
|     ],
 | |
|   });
 | |
| });
 | |
| 
 | |
| /**
 | |
|  * This test to used to test the feature that would suspend the inactive tab,
 | |
|  * which currently is only used on Android.
 | |
|  *
 | |
|  * Normally when tab becomes inactive, we would suspend it and stop its script
 | |
|  * from running. However, if a tab has a main controller, which indicates it
 | |
|  * might have playng media, or waiting media keys to control media, then it
 | |
|  * would not be suspended event if it's inactive.
 | |
|  *
 | |
|  * In addition, Note that, on Android, audio focus management is enabled by
 | |
|  * default, so there is only one tab being able to play at a time, which means
 | |
|  * the tab playing media always has main controller.
 | |
|  */
 | |
| add_task(async function testInactiveTabWouldBeSuspended() {
 | |
|   info(`open a tab`);
 | |
|   const tab = await createTab(PAGE_NON_AUTOPLAY);
 | |
|   await assertIfWindowGetSuspended(tab, { shouldBeSuspended: false });
 | |
| 
 | |
|   info(`tab should be suspended when it becomes inactive`);
 | |
|   setTabActive(tab, false);
 | |
|   await assertIfWindowGetSuspended(tab, { shouldBeSuspended: true });
 | |
| 
 | |
|   info(`remove tab`);
 | |
|   await tab.close();
 | |
| });
 | |
| 
 | |
| add_task(async function testInactiveTabEverStartPlayingWontBeSuspended() {
 | |
|   info(`open tab1 and play media`);
 | |
|   const tab1 = await createTab(PAGE_NON_AUTOPLAY, { needCheck: true });
 | |
|   await assertIfWindowGetSuspended(tab1, { shouldBeSuspended: false });
 | |
|   await playMedia(tab1, VIDEO_ID);
 | |
| 
 | |
|   info(`tab with playing media won't be suspended when it becomes inactive`);
 | |
|   setTabActive(tab1, false);
 | |
|   await assertIfWindowGetSuspended(tab1, { shouldBeSuspended: false });
 | |
| 
 | |
|   info(
 | |
|     `even if media is paused, keep tab running so that it could listen to media keys to control media in the future`
 | |
|   );
 | |
|   await pauseMedia(tab1, VIDEO_ID);
 | |
|   await assertIfWindowGetSuspended(tab1, { shouldBeSuspended: false });
 | |
| 
 | |
|   info(`open tab2 and play media`);
 | |
|   const tab2 = await createTab(PAGE_NON_AUTOPLAY, { needCheck: true });
 | |
|   await assertIfWindowGetSuspended(tab2, { shouldBeSuspended: false });
 | |
|   await playMedia(tab2, VIDEO_ID);
 | |
| 
 | |
|   info(
 | |
|     `as inactive tab1 doesn't own main controller, it should be suspended again`
 | |
|   );
 | |
|   await assertIfWindowGetSuspended(tab1, { shouldBeSuspended: true });
 | |
| 
 | |
|   info(`remove tabs`);
 | |
|   await Promise.all([tab1.close(), tab2.close()]);
 | |
| });
 | |
| 
 | |
| add_task(
 | |
|   async function testInactiveTabWithRunningAudioContextWontBeSuspended() {
 | |
|     info(`open tab and start an audio context (AC)`);
 | |
|     const tab = await createTab("about:blank");
 | |
|     await startAudioContext(tab);
 | |
|     await assertIfWindowGetSuspended(tab, { shouldBeSuspended: false });
 | |
| 
 | |
|     info(`tab with running AC won't be suspended when it becomes inactive`);
 | |
|     setTabActive(tab, false);
 | |
|     await assertIfWindowGetSuspended(tab, { shouldBeSuspended: false });
 | |
| 
 | |
|     info(`if AC has been suspended, then inactive tab should be suspended`);
 | |
|     await suspendAudioContext(tab);
 | |
|     await assertIfWindowGetSuspended(tab, { shouldBeSuspended: true });
 | |
| 
 | |
|     info(`remove tab`);
 | |
|     await tab.close();
 | |
|   }
 | |
| );
 | |
| 
 | |
| /**
 | |
|  * The following are helper functions.
 | |
|  */
 | |
| async function createTab(url, needCheck = false) {
 | |
|   const tab = await createLoadedTabWrapper(url, { needCheck });
 | |
|   return tab;
 | |
| }
 | |
| 
 | |
| function assertIfWindowGetSuspended(tab, { shouldBeSuspended }) {
 | |
|   return SpecialPowers.spawn(
 | |
|     tab.linkedBrowser,
 | |
|     [shouldBeSuspended],
 | |
|     expectedSuspend => {
 | |
|       const isSuspended = content.windowUtils.suspendedByBrowsingContextGroup;
 | |
|       is(
 | |
|         expectedSuspend,
 | |
|         isSuspended,
 | |
|         `window suspended state (${isSuspended}) is equal to the expected`
 | |
|       );
 | |
|     }
 | |
|   );
 | |
| }
 | |
| 
 | |
| function setTabActive(tab, isActive) {
 | |
|   tab.linkedBrowser.docShellIsActive = isActive;
 | |
| }
 | |
| 
 | |
| function startAudioContext(tab) {
 | |
|   return SpecialPowers.spawn(tab.linkedBrowser, [], async _ => {
 | |
|     content.ac = new content.AudioContext();
 | |
|     await new Promise(r => (content.ac.onstatechange = r));
 | |
|     Assert.equal(content.ac.state, "running", `Audio context started running`);
 | |
|   });
 | |
| }
 | |
| 
 | |
| function suspendAudioContext(tab) {
 | |
|   return SpecialPowers.spawn(tab.linkedBrowser, [], async _ => {
 | |
|     await content.ac.suspend();
 | |
|     Assert.equal(content.ac.state, "suspended", `Audio context is suspended`);
 | |
|   });
 | |
| }
 | 
