summaryrefslogtreecommitdiffstats
path: root/dom/media/mediacontrol/tests/browser/browser_suspend_inactive_tab.js
diff options
context:
space:
mode:
Diffstat (limited to 'dom/media/mediacontrol/tests/browser/browser_suspend_inactive_tab.js')
-rw-r--r--dom/media/mediacontrol/tests/browser/browser_suspend_inactive_tab.js131
1 files changed, 131 insertions, 0 deletions
diff --git a/dom/media/mediacontrol/tests/browser/browser_suspend_inactive_tab.js b/dom/media/mediacontrol/tests/browser/browser_suspend_inactive_tab.js
new file mode 100644
index 0000000000..334717a2f2
--- /dev/null
+++ b/dom/media/mediacontrol/tests/browser/browser_suspend_inactive_tab.js
@@ -0,0 +1,131 @@
+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));
+ ok(content.ac.state == "running", `Audio context started running`);
+ });
+}
+
+function suspendAudioContext(tab) {
+ return SpecialPowers.spawn(tab.linkedBrowser, [], async _ => {
+ await content.ac.suspend();
+ ok(content.ac.state == "suspended", `Audio context is suspended`);
+ });
+}