/* Any copyright is dedicated to the Public Domain. http://creativecommons.org/publicdomain/zero/1.0/ */ "use strict"; async function getVisibility(tab) { return SpecialPowers.spawn( tab.linkedBrowser, [], () => content.document.visibilityState ); } async function isThrottled(tab) { return SpecialPowers.spawn( tab.linkedBrowser, [], () => SpecialPowers.DOMWindowUtils.effectivelyThrottlesFrameRequests ); } // Wait for the tab to be active / inactive async function ensureActivity(tab, activity = true) { await TestUtils.waitForCondition( () => tab.linkedBrowser.browsingContext.isActive == activity, `Waiting for tab isActive = ${activity}` ); } add_task(async function test_pip_opener_not_throttled() { const [tab, chromePiP] = await newTabWithPiP(); // Baseline: Active tab with PiP ok(tab.linkedBrowser.browsingContext.isActive, "Foreground opener is active"); ok(!(await isThrottled(tab)), "Foreground opener not throttled"); // Background tab with PiP, i.e. switch away from opener info("Move opener to background by switching tabs"); const tab2 = await BrowserTestUtils.openNewForegroundTab({ gBrowser, opening: "https://example.org", waitForLoad: true, }); ok(tab.linkedBrowser.browsingContext.isActive, "Background opener is active"); ok(!(await isThrottled(tab)), "Background opener not throttled"); is(await getVisibility(tab), "visible", "Background opener is visible"); // Verify switching back keeps opener visible, active etc. info("Switch back to opener to ensure nothing breaks"); await BrowserTestUtils.switchTab(gBrowser, tab); ok( tab.linkedBrowser.browsingContext.isActive, "Foreground opener stays active" ); ok(!(await isThrottled(tab)), "Foreground opener stays unthrottled"); is(await getVisibility(tab), "visible", "Foreground opener stays visible"); // Move opener to background again and close PiP info("Move back to other tab, close PiP, ensure throttling"); await BrowserTestUtils.switchTab(gBrowser, tab2); ok(tab.linkedBrowser.browsingContext.isActive, "Background opener is active"); await BrowserTestUtils.closeWindow(chromePiP); await ensureActivity(tab, false); ok(!tab.linkedBrowser.browsingContext.isActive, "Background tab is inactive"); ok(await isThrottled(tab), "Background tab throttled"); is(await getVisibility(tab), "hidden", "Background tab is hidden"); // Verify switching back makes tab visible, active etc. info("Move back to opener tab, ensure it becomes active"); await BrowserTestUtils.switchTab(gBrowser, tab); await ensureActivity(tab); ok(tab.linkedBrowser.browsingContext.isActive, "Tab becomes active again"); ok(!(await isThrottled(tab)), "Tab stopes being throttled"); is(await getVisibility(tab), "visible", "Tab becomes visible"); // Cleanup BrowserTestUtils.removeTab(tab); BrowserTestUtils.removeTab(tab2); }); // Minimize / Restore app window async function setMinimized(window, minimized = true) { const promiseSizeModeChange = BrowserTestUtils.waitForEvent( window, "sizemodechange" ); if (minimized) { window.minimize(); } else { window.restore(); } await promiseSizeModeChange; } add_task(async function test_pip_throttling_window_hidden() { const [tab, chromePiP] = await newTabWithPiP(); // Hide app window await setMinimized(tab.ownerGlobal); ok(tab.linkedBrowser.browsingContext.isActive, "Hidden opener is active"); ok(!(await isThrottled(tab)), "Hidden opener not throttled"); is(await getVisibility(tab), "visible", "Hidden opener is visible"); // Restore visibility await setMinimized(tab.ownerGlobal, false); ok(tab.linkedBrowser.browsingContext.isActive, "Visible opener is active"); ok(!(await isThrottled(tab)), "Visible opener not throttled"); is(await getVisibility(tab), "visible", "Visible opener is visible"); // Hide and close PiP await setMinimized(tab.ownerGlobal); ok(tab.linkedBrowser.browsingContext.isActive, "Hidden opener is active"); await BrowserTestUtils.closeWindow(chromePiP); await ensureActivity(tab, false); ok(!tab.linkedBrowser.browsingContext.isActive, "Hidden tab is inactive"); ok(await isThrottled(tab), "Hidden tab throttled"); is(await getVisibility(tab), "hidden", "Hidden tab is hidden"); // Restore visibility await setMinimized(tab.ownerGlobal, false); // Tab isn't immediately unthrottled after window is restored await TestUtils.waitForCondition( async () => !(await isThrottled(tab)), "Wait for tab not throttled" ); ok(tab.linkedBrowser.browsingContext.isActive, "Visible tab is active"); ok(!(await isThrottled(tab)), "Visible tab not throttled"); is(await getVisibility(tab), "visible", "Visible tab is visible"); // Cleanup BrowserTestUtils.removeTab(tab); }); add_task(async function test_pip_throttling_opener_tab_adoption() { // Open a PiP and adopt the opener into a new window // Request a small window to avoid timeouts from the other window not getting visible. const [originalTab, chromePiP] = await newTabWithPiP({ width: 100, height: 100, }); const tab2win1 = await BrowserTestUtils.openNewForegroundTab({ gBrowser, opening: "https://example.org", waitForLoad: true, }); const win2 = await BrowserTestUtils.openNewBrowserWindow(); const adoptedTab = win2.gBrowser.adoptTab(originalTab, 0, true); // Minimize old window to ensure it's being throttled as the PiP was adopted away await setMinimized(tab2win1.ownerGlobal); ok( !tab2win1.linkedBrowser.browsingContext.isActive, "Hidden tab is inactive" ); ok(await isThrottled(tab2win1), "Hidden tab throttled"); is(await getVisibility(tab2win1), "hidden", "Hidden tab is hidden"); // Adoping the opener currently closes the PiP (bug 2009192). // So we cannot test that the new window isn't throttled. // Cleanup await setMinimized(tab2win1.ownerGlobal, false); if (chromePiP && !chromePiP.closed) { // See bug 2009192 await BrowserTestUtils.closeWindow(chromePiP); } BrowserTestUtils.removeTab(adoptedTab); await BrowserTestUtils.closeWindow(win2); BrowserTestUtils.removeTab(tab2win1); });