/* Any copyright is dedicated to the Public Domain. http://creativecommons.org/publicdomain/zero/1.0/ */ "use strict"; // This test tends to trigger a race in the fullscreen time telemetry, // where the fullscreen enter and fullscreen exit events (which use the // same histogram ID) overlap. That causes TelemetryStopwatch to log an // error. SimpleTest.ignoreAllUncaughtExceptions(true); const { PromiseTestUtils } = ChromeUtils.importESModule( "resource://testing-common/PromiseTestUtils.sys.mjs" ); PromiseTestUtils.allowMatchingRejectionsGlobally(/Not in fullscreen mode/); SimpleTest.requestCompleteLog(); async function requestNotificationPermission(browser) { return SpecialPowers.spawn(browser, [], () => { return content.Notification.requestPermission(); }); } async function requestCameraPermission(browser) { return SpecialPowers.spawn(browser, [], () => content.navigator.mediaDevices .getUserMedia({ video: true, fake: true }) .then( () => true, () => false ) ); } add_task(async function test_fullscreen_closes_permissionui_prompt() { await SpecialPowers.pushPrefEnv({ set: [ ["dom.webnotifications.requireuserinteraction", false], ["permissions.fullscreen.allowed", false], ], }); let tab = await BrowserTestUtils.openNewForegroundTab( gBrowser, "https://example.com" ); let browser = tab.linkedBrowser; let popupShown, requestResult, popupHidden; popupShown = BrowserTestUtils.waitForEvent( window.PopupNotifications.panel, "popupshown" ); info("Requesting notification permission"); requestResult = requestNotificationPermission(browser); await popupShown; info("Entering DOM full-screen"); popupHidden = BrowserTestUtils.waitForEvent( window.PopupNotifications.panel, "popuphidden" ); await changeFullscreen(browser, true); await popupHidden; is( await requestResult, "default", "Expect permission request to be cancelled" ); await changeFullscreen(browser, false); BrowserTestUtils.removeTab(tab); await SpecialPowers.popPrefEnv(); }); add_task(async function test_fullscreen_closes_webrtc_permission_prompt() { await SpecialPowers.pushPrefEnv({ set: [ ["media.navigator.permission.fake", true], ["media.navigator.permission.force", true], ["permissions.fullscreen.allowed", false], ], }); let tab = await BrowserTestUtils.openNewForegroundTab( gBrowser, "https://example.com" ); let browser = tab.linkedBrowser; let popupShown, requestResult, popupHidden; popupShown = BrowserTestUtils.waitForEvent( window.PopupNotifications.panel, "popupshown" ); info("Requesting camera permission"); requestResult = requestCameraPermission(browser); await popupShown; info("Entering DOM full-screen"); popupHidden = BrowserTestUtils.waitForEvent( window.PopupNotifications.panel, "popuphidden" ); await changeFullscreen(browser, true); await popupHidden; is( await requestResult, false, "Expect webrtc permission request to be cancelled" ); await changeFullscreen(browser, false); BrowserTestUtils.removeTab(tab); await SpecialPowers.popPrefEnv(); }); add_task(async function test_permission_prompt_closes_fullscreen() { await SpecialPowers.pushPrefEnv({ set: [ ["dom.webnotifications.requireuserinteraction", false], ["permissions.fullscreen.allowed", false], ], }); let tab = await BrowserTestUtils.openNewForegroundTab( gBrowser, "https://example.com" ); let browser = tab.linkedBrowser; info("Entering DOM full-screen"); await changeFullscreen(browser, true); let popupShown = BrowserTestUtils.waitForEvent( window.PopupNotifications.panel, "popupshown" ); let fullScreenExit = waitForFullScreenState(browser, false); info("Requesting notification permission"); requestNotificationPermission(browser).catch(() => {}); await popupShown; info("Waiting for full-screen exit"); await fullScreenExit; BrowserTestUtils.removeTab(tab); await SpecialPowers.popPrefEnv(); }); function triggerMainCommand(popup) { let notifications = popup.childNodes; ok(!!notifications.length, "at least one notification displayed"); let notification = notifications[0]; info("Triggering main command for notification " + notification.id); EventUtils.synthesizeMouseAtCenter(notification.button, {}); } add_task( async function test_permission_prompt_closes_fullscreen_and_extends_security_delay() { const TEST_SECURITY_DELAY = 500; await SpecialPowers.pushPrefEnv({ set: [ ["dom.webnotifications.requireuserinteraction", false], ["permissions.fullscreen.allowed", false], ["security.notification_enable_delay", TEST_SECURITY_DELAY], // macOS is not affected by the sec delay bug because it uses the native // macOS full screen API. Revert back to legacy behavior so we can also // test on macOS. If this pref is removed in the future we can consider // skipping the testcase for macOS altogether. ["full-screen-api.macos-native-full-screen", false], ], }); let tab = await BrowserTestUtils.openNewForegroundTab( gBrowser, "https://example.com" ); let browser = tab.linkedBrowser; info("Entering DOM full-screen"); await changeFullscreen(browser, true); let popupShown = BrowserTestUtils.waitForPopupEvent( window.PopupNotifications.panel, "shown" ); let fullScreenExit = waitForFullScreenState(browser, false); info("Requesting notification permission"); requestNotificationPermission(browser).catch(() => {}); await popupShown; let notificationHiddenPromise = BrowserTestUtils.waitForPopupEvent( window.PopupNotifications.panel, "hidden" ); info("Waiting for full-screen exit"); await fullScreenExit; info("Wait for original security delay to expire."); SimpleTest.requestFlakyTimeout( "Wait for original security delay to expire." ); // eslint-disable-next-line mozilla/no-arbitrary-setTimeout await new Promise(resolve => setTimeout(resolve, TEST_SECURITY_DELAY)); info( "Trigger main action via button click during the extended security delay" ); triggerMainCommand(PopupNotifications.panel); let notification = PopupNotifications.getNotification( "web-notifications", gBrowser.selectedBrowser ); // Linux in CI seems to skip the full screen animation, which means its not // affected by the bug and we can't test extension of the sec delay here. if (Services.appinfo.OS == "Linux") { todo( notification && !notification.dismissed && BrowserTestUtils.is_visible(PopupNotifications.panel.firstChild), "Notification should still be open because we clicked during the security delay." ); } else { ok( notification && !notification.dismissed && BrowserTestUtils.is_visible(PopupNotifications.panel.firstChild), "Notification should still be open because we clicked during the security delay." ); } // If the notification is no longer shown (test failure) skip the remaining // checks. if (!notification) { // Cleanup BrowserTestUtils.removeTab(tab); await SpecialPowers.popPrefEnv(); // Remove the granted notification permission. Services.perms.removeAll(); return; } Assert.greater( notification.timeShown, performance.now(), "Notification timeShown property should be in the future, because the security delay was extended." ); // Ensure that once the security delay has passed the notification can be // closed again. let fakeTimeShown = TEST_SECURITY_DELAY + 500; info(`Manually set timeShown to ${fakeTimeShown}ms in the past.`); notification.timeShown = performance.now() - fakeTimeShown; info("Trigger main action via button click outside security delay"); triggerMainCommand(PopupNotifications.panel); info("Wait for panel to be hidden."); await notificationHiddenPromise; ok( !PopupNotifications.getNotification( "web-notifications", gBrowser.selectedBrowser ), "Should not longer see the notification." ); // Cleanup BrowserTestUtils.removeTab(tab); await SpecialPowers.popPrefEnv(); // Remove the granted notification permission. Services.perms.removeAll(); } );