/* Any copyright is dedicated to the Public Domain. http://creativecommons.org/publicdomain/zero/1.0/ */ "use strict"; const { TelemetryTestUtils } = ChromeUtils.importESModule( "resource://testing-common/TelemetryTestUtils.sys.mjs" ); const { ASRouter } = ChromeUtils.importESModule( "resource:///modules/asrouter/ASRouter.sys.mjs" ); const PIP_URLBAR_EVENTS = [ { category: "pictureinpicture", method: "opened_method", object: "urlBar", }, ]; const PIP_DISABLED_EVENTS = [ { category: "pictureinpicture", method: "opened_method", object: "urlBar", extra: { disableDialog: "true" }, }, { category: "pictureinpicture", method: "disrespect_disable", object: "urlBar", }, ]; const FIRST_TOGGLE_AFTER_CALLOUT_EXPECTED_EVENTS = [ { category: "pictureinpicture", method: "opened_method", object: "urlBar", extra: { firstTimeToggle: "true", callout: "true" }, }, ]; add_task(async function test_urlbar_toggle_multiple_contexts() { await BrowserTestUtils.withNewTab( { url: TEST_PAGE_MULTIPLE_CONTEXTS, gBrowser, }, async browser => { Services.telemetry.clearEvents(); await ensureVideosReady(browser); await ensureVideosReady(browser.browsingContext.children[0]); await TestUtils.waitForCondition( () => PictureInPicture.getEligiblePipVideoCount(browser).totalPipCount === 2, "Waiting for videos to register" ); let { totalPipCount } = PictureInPicture.getEligiblePipVideoCount(browser); is(totalPipCount, 2, "Total PiP count is 2"); let pipUrlbarToggle = document.getElementById( "picture-in-picture-button" ); ok( BrowserTestUtils.isHidden(pipUrlbarToggle), "PiP urlbar toggle is hidden because there is more than 1 video" ); // Remove one video from page so urlbar toggle will show await SpecialPowers.spawn(browser, [], async () => { let video = content.document.getElementById("with-controls"); video.remove(); }); await BrowserTestUtils.waitForMutationCondition( pipUrlbarToggle, { attributeFilter: ["hidden"] }, () => BrowserTestUtils.isVisible(pipUrlbarToggle) ); ok( BrowserTestUtils.isVisible(pipUrlbarToggle), "PiP urlbar toggle is visible" ); ({ totalPipCount } = PictureInPicture.getEligiblePipVideoCount(browser)); is(totalPipCount, 1, "Total PiP count is 1"); let domWindowOpened = BrowserTestUtils.domWindowOpenedAndLoaded(null); pipUrlbarToggle.click(); let win = await domWindowOpened; ok(win, "A Picture-in-Picture window opened."); await assertVideoIsBeingCloned( browser.browsingContext.children[0], "video" ); let filter = { category: "pictureinpicture", method: "opened_method", object: "urlBar", }; await waitForTelemeryEvents(filter, PIP_URLBAR_EVENTS.length, "content"); TelemetryTestUtils.assertEvents(PIP_URLBAR_EVENTS, filter, { clear: true, process: "content", }); let domWindowClosed = BrowserTestUtils.domWindowClosed(win); pipUrlbarToggle.click(); await domWindowClosed; await SpecialPowers.spawn(browser, [], async () => { let iframe = content.document.getElementById("iframe"); iframe.remove(); }); await BrowserTestUtils.waitForMutationCondition( pipUrlbarToggle, { attributeFilter: ["hidden"] }, () => BrowserTestUtils.isHidden(pipUrlbarToggle) ); ok( BrowserTestUtils.isHidden(pipUrlbarToggle), "PiP urlbar toggle is hidden because there are no videos on the page" ); ({ totalPipCount } = PictureInPicture.getEligiblePipVideoCount(browser)); is(totalPipCount, 0, "Total PiP count is 0"); } ); }); add_task(async function test_urlbar_toggle_switch_tabs() { await BrowserTestUtils.withNewTab( { url: TEST_PAGE_TRANSPARENT_NESTED_IFRAMES, gBrowser, }, async browser => { await ensureVideosReady(browser); await TestUtils.waitForCondition( () => PictureInPicture.getEligiblePipVideoCount(browser).totalPipCount === 1, "Waiting for video to register" ); let { totalPipCount } = PictureInPicture.getEligiblePipVideoCount(browser); is(totalPipCount, 1, "Total PiP count is 1"); let pipUrlbarToggle = document.getElementById( "picture-in-picture-button" ); ok( BrowserTestUtils.isVisible(pipUrlbarToggle), "PiP urlbar toggle is visible because there is 1 video" ); let pipActivePromise = BrowserTestUtils.waitForMutationCondition( pipUrlbarToggle, { attributeFilter: ["pipactive"] }, () => pipUrlbarToggle.hasAttribute("pipactive") ); let domWindowOpened = BrowserTestUtils.domWindowOpenedAndLoaded(null); pipUrlbarToggle.click(); let win = await domWindowOpened; ok(win, "A Picture-in-Picture window opened."); await assertVideoIsBeingCloned(browser, "video"); await pipActivePromise; ok( pipUrlbarToggle.hasAttribute("pipactive"), "We are PiP'd in this tab so the icon is active" ); let newTab = BrowserTestUtils.addTab( gBrowser, TEST_PAGE_TRANSPARENT_NESTED_IFRAMES ); await BrowserTestUtils.browserLoaded(newTab.linkedBrowser); await BrowserTestUtils.switchTab(gBrowser, newTab); await BrowserTestUtils.waitForMutationCondition( pipUrlbarToggle, { attributeFilter: ["pipactive"] }, () => !pipUrlbarToggle.hasAttribute("pipactive") ); ok( !pipUrlbarToggle.hasAttribute("pipactive"), "After switching tabs the pip icon is not active" ); BrowserTestUtils.removeTab(newTab); await ensureMessageAndClosePiP( browser, "video-transparent-background", win, false ); } ); }); add_task(async function test_pipDisabled() { await BrowserTestUtils.withNewTab( { url: TEST_PAGE_PIP_DISABLED, gBrowser, }, async browser => { Services.telemetry.clearEvents(); await SpecialPowers.pushPrefEnv({ set: [ [ "media.videocontrols.picture-in-picture.respect-disablePictureInPicture", true, ], ], }); const VIDEO_ID = "with-controls"; await ensureVideosReady(browser); await TestUtils.waitForCondition( () => PictureInPicture.getEligiblePipVideoCount(browser).totalPipCount === 1, "Waiting for video to register" ); let { totalPipCount, totalPipDisabled } = PictureInPicture.getEligiblePipVideoCount(browser); is(totalPipCount, 1, "Total PiP count is 1"); is(totalPipDisabled, 1, "PiP is disabled on 1 video"); // Confirm that the toggle is hidden because we are respecting disablePictureInPicture await testToggleHelper(browser, VIDEO_ID, false); let pipUrlbarToggle = document.getElementById( "picture-in-picture-button" ); ok( BrowserTestUtils.isVisible(pipUrlbarToggle), "PiP urlbar toggle is visible because PiP is disabled" ); pipUrlbarToggle.click(); let panel = browser.ownerDocument.querySelector("#PictureInPicturePanel"); await BrowserTestUtils.waitForCondition(async () => { if (!panel) { panel = browser.ownerDocument.querySelector("#PictureInPicturePanel"); } return BrowserTestUtils.isVisible(panel); }); let respectPipDisableSwitch = panel.querySelector( "#respect-pipDisabled-switch" ); ok( !respectPipDisableSwitch.pressed, "Respect PiP disabled is not pressed" ); EventUtils.synthesizeMouseAtCenter(respectPipDisableSwitch.buttonEl, {}); await BrowserTestUtils.waitForEvent(respectPipDisableSwitch, "toggle"); ok(respectPipDisableSwitch.pressed, "Respect PiP disabled is pressed"); pipUrlbarToggle.click(); await BrowserTestUtils.waitForCondition(async () => { return BrowserTestUtils.isHidden(panel); }); let filter = { category: "pictureinpicture", object: "urlBar", }; await waitForTelemeryEvents(filter, PIP_DISABLED_EVENTS.length, "parent"); TelemetryTestUtils.assertEvents(PIP_DISABLED_EVENTS, filter, { clear: true, process: "parent", }); // Confirm that the toggle is now visible because we no longer respect disablePictureInPicture await testToggleHelper(browser, VIDEO_ID, true); let pipActivePromise = BrowserTestUtils.waitForMutationCondition( pipUrlbarToggle, { attributeFilter: ["pipactive"] }, () => pipUrlbarToggle.hasAttribute("pipactive") ); let domWindowOpened = BrowserTestUtils.domWindowOpenedAndLoaded(null); pipUrlbarToggle.click(); let win = await domWindowOpened; ok(win, "A Picture-in-Picture window opened."); await assertVideoIsBeingCloned(browser, "video"); await pipActivePromise; ok( pipUrlbarToggle.hasAttribute("pipactive"), "We are PiP'd in this tab so the icon is active" ); let domWindowClosed = BrowserTestUtils.domWindowClosed(win); pipUrlbarToggle.click(); await domWindowClosed; await BrowserTestUtils.waitForMutationCondition( pipUrlbarToggle, { attributeFilter: ["pipactive"] }, () => !pipUrlbarToggle.hasAttribute("pipactive") ); ok( !pipUrlbarToggle.hasAttribute("pipactive"), "We closed the PiP window so the urlbar button is no longer active" ); await SpecialPowers.popPrefEnv(); } ); }); add_task(async function test_urlbar_toggle_telemetry() { await BrowserTestUtils.withNewTab( { url: TEST_PAGE_MULTIPLE_CONTEXTS, gBrowser, }, async browser => { Services.telemetry.clearEvents(); await ensureVideosReady(browser); await ensureVideosReady(browser.browsingContext.children[0]); await TestUtils.waitForCondition( () => PictureInPicture.getEligiblePipVideoCount(browser).totalPipCount === 2, "Waiting for videos to register" ); let { totalPipCount } = PictureInPicture.getEligiblePipVideoCount(browser); is(totalPipCount, 2, "Total PiP count is 2"); let pipUrlbarToggle = document.getElementById( "picture-in-picture-button" ); ok( BrowserTestUtils.isHidden(pipUrlbarToggle), "PiP urlbar toggle is hidden because there is more than 1 video" ); // Remove one video from page so urlbar toggle will show await SpecialPowers.spawn(browser, [], async () => { let video = content.document.getElementById("with-controls"); video.remove(); }); await BrowserTestUtils.waitForMutationCondition( pipUrlbarToggle, { attributeFilter: ["hidden"] }, () => BrowserTestUtils.isVisible(pipUrlbarToggle) ); ok( BrowserTestUtils.isVisible(pipUrlbarToggle), "PiP urlbar toggle is visible" ); // open for first time after seeing feature callout message await SpecialPowers.pushPrefEnv({ set: [ [ "media.videocontrols.picture-in-picture.video-toggle.has-used", false, ], ], }); let fakeMessage = { template: "feature_callout", id: "FAKE_PIP_FEATURE_CALLOUT", content: { screens: [{ anchors: [{ selector: "#picture-in-picture-button" }] }], }, }; await ASRouter.waitForInitialized; let originalASRouterState; await ASRouter.setState(state => { originalASRouterState = state; return { messages: [...state.messages, fakeMessage], messageImpressions: { ...state.messageImpressions, [fakeMessage.id]: [Date.now()], }, }; }); ({ totalPipCount } = PictureInPicture.getEligiblePipVideoCount(browser)); is(totalPipCount, 1, "Total PiP count is 1"); let domWindowOpened = BrowserTestUtils.domWindowOpenedAndLoaded(null); pipUrlbarToggle.click(); let win = await domWindowOpened; ok(win, "A Picture-in-Picture window opened."); await assertVideoIsBeingCloned( browser.browsingContext.children[0], "video" ); let filter = { category: "pictureinpicture", method: "opened_method", object: "urlBar", }; await waitForTelemeryEvents( filter, FIRST_TOGGLE_AFTER_CALLOUT_EXPECTED_EVENTS.length, "content" ); TelemetryTestUtils.assertEvents( FIRST_TOGGLE_AFTER_CALLOUT_EXPECTED_EVENTS, filter, { clear: true, process: "content" } ); let domWindowClosed = BrowserTestUtils.domWindowClosed(win); pipUrlbarToggle.click(); await domWindowClosed; await ASRouter.setState(originalASRouterState); } ); });