/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */ /* vim: set sts=2 sw=2 et tw=80: */ "use strict"; /** * Tests that we apply dark theme variants to PBM windows where applicable. */ const { BuiltInThemes } = ChromeUtils.importESModule( "resource:///modules/BuiltInThemes.sys.mjs" ); const { PromptTestUtils } = ChromeUtils.importESModule( "resource://testing-common/PromptTestUtils.sys.mjs" ); const LIGHT_THEME_ID = "firefox-compact-light@mozilla.org"; const DARK_THEME_ID = "firefox-compact-dark@mozilla.org"; // This tests opens many chrome windows which is slow on debug builds. requestLongerTimeout(2); async function testIsDark(win, expectDark) { let mql = win.matchMedia("(prefers-color-scheme: dark)"); if (mql.matches != expectDark) { // The color scheme change might not have been processed yet, since that // happens on a refresh driver tick. await new Promise(r => mql.addEventListener("change", r, { once: true })); } is( mql.matches, expectDark, `Window should${expectDark ? "" : " not"} be dark.` ); } /** * Test a window's theme color scheme. * * @param {*} options - Test options. * @param {Window} options.win - Window object to test. * @param {boolean} options.colorScheme - Whether expected chrome color scheme * is dark (true) or light (false). * @param {boolean} options.expectLWTAttributes - Whether the window should * have the LWT attributes set matching the color scheme. */ async function testWindowColorScheme({ win, expectDark, expectLWTAttributes }) { let docEl = win.document.documentElement; await testIsDark(win, expectDark); if (expectLWTAttributes) { ok(docEl.hasAttribute("lwtheme"), "Window should have LWT attribute."); is( docEl.getAttribute("lwtheme-brighttext"), expectDark ? "true" : null, "LWT text color attribute should be set." ); } else { ok(!docEl.hasAttribute("lwtheme"), "Window should not have LWT attribute."); ok( !docEl.hasAttribute("lwtheme-brighttext"), "LWT text color attribute should not be set." ); } } /** * Match the prefers-color-scheme media query and return the results. * * @param {object} options * @param {Window} options.win - If chrome=true, window to test, otherwise * parent window of the content window to test. * @param {boolean} options.chrome - If true the media queries will be matched * against the supplied chrome window. Otherwise they will be matched against * the content window. * @returns {Promise<{light: boolean, dark: boolean}>} - Resolves with an * object of the media query results. */ function getPrefersColorSchemeInfo({ win, chrome = false }) { let fn = async windowObj => { // If called in the parent, we use the supplied win object. Otherwise use // the content window global. let win = windowObj || content; // LookAndFeel updates are async. await new Promise(resolve => { win.requestAnimationFrame(() => win.requestAnimationFrame(resolve)); }); return { light: win.matchMedia("(prefers-color-scheme: light)").matches, dark: win.matchMedia("(prefers-color-scheme: dark)").matches, }; }; if (chrome) { return fn(win); } return SpecialPowers.spawn(win.gBrowser.selectedBrowser, [], fn); } add_setup(async function () { // Set system theme to light to ensure consistency across test machines. await SpecialPowers.pushPrefEnv({ set: [ ["browser.theme.dark-private-windows", true], ["ui.systemUsesDarkTheme", 0], ], }); // Ensure the built-in themes are initialized. await BuiltInThemes.ensureBuiltInThemes(); // The previous test, browser_ext_themes_ntp_colors.js has side effects. // Switch to a theme, then switch back to the default theme to reach a // consistent themeData state. Without this, themeData in // LightWeightConsumer#_update does not contain darkTheme data and PBM windows // don't get themed correctly. let lightTheme = await AddonManager.getAddonByID(LIGHT_THEME_ID); await lightTheme.enable(); await lightTheme.disable(); }); // For the default theme with light color scheme, private browsing windows // should be themed dark. // The PBM window's content should not be themed dark. add_task(async function test_default_theme_light() { info("Normal browsing window should not be in dark mode."); await testWindowColorScheme({ win: window, expectDark: false, expectLWTAttributes: false, }); let windowB = await BrowserTestUtils.openNewBrowserWindow(); info("Additional normal browsing window should not be in dark mode."); await testWindowColorScheme({ win: windowB, expectDark: false, expectLWTAttributes: false, }); let pbmWindowA = await BrowserTestUtils.openNewBrowserWindow({ private: true, }); info("Private browsing window should be in dark mode."); await testWindowColorScheme({ win: pbmWindowA, expectDark: true, expectLWTAttributes: true, }); let prefersColorScheme = await getPrefersColorSchemeInfo({ win: pbmWindowA }); ok( prefersColorScheme.light && !prefersColorScheme.dark, "Content of dark themed PBM window should still be themed light" ); let pbmWindowB = await BrowserTestUtils.openNewBrowserWindow({ private: true, }); info("Additional private browsing window should be in dark mode."); await testWindowColorScheme({ win: pbmWindowB, expectDark: true, expectLWTAttributes: true, }); await BrowserTestUtils.closeWindow(windowB); await BrowserTestUtils.closeWindow(pbmWindowA); await BrowserTestUtils.closeWindow(pbmWindowB); }); // For the default theme with dark color scheme, normal and private browsing // windows should be themed dark. add_task(async function test_default_theme_dark() { // Set the system theme to dark. The default theme will follow this color // scheme. await SpecialPowers.pushPrefEnv({ set: [["ui.systemUsesDarkTheme", 1]] }); info("Normal browsing window should be in dark mode."); await testWindowColorScheme({ win: window, expectDark: true, expectLWTAttributes: false, }); let pbmWindow = await BrowserTestUtils.openNewBrowserWindow({ private: true, }); info("Private browsing window should be in dark mode."); await testWindowColorScheme({ win: pbmWindow, expectDark: true, expectLWTAttributes: false, }); await BrowserTestUtils.closeWindow(pbmWindow); await SpecialPowers.popPrefEnv(); }); // For the light theme both normal and private browsing windows should have a // bright color scheme applied. add_task(async function test_light_theme_builtin() { let lightTheme = await AddonManager.getAddonByID(LIGHT_THEME_ID); await lightTheme.enable(); info("Normal browsing window should not be in dark mode."); await testWindowColorScheme({ win: window, expectDark: false, expectLWTAttributes: true, }); let pbmWindow = await BrowserTestUtils.openNewBrowserWindow({ private: true, }); info("Private browsing window should not be in dark mode."); await testWindowColorScheme({ win: pbmWindow, expectDark: false, expectLWTAttributes: true, }); await BrowserTestUtils.closeWindow(pbmWindow); await lightTheme.disable(); }); // For the dark theme both normal and private browsing should have a dark color // scheme applied. add_task(async function test_dark_theme_builtin() { let darkTheme = await AddonManager.getAddonByID(DARK_THEME_ID); await darkTheme.enable(); info("Normal browsing window should be in dark mode."); await testWindowColorScheme({ win: window, expectDark: true, expectLWTAttributes: true, }); let pbmWindow = await BrowserTestUtils.openNewBrowserWindow({ private: true, }); info("Private browsing window should be in dark mode."); await testWindowColorScheme({ win: pbmWindow, expectDark: true, expectLWTAttributes: true, }); await BrowserTestUtils.closeWindow(pbmWindow); await darkTheme.disable(); }); // When switching between default, light and dark theme the private browsing // window color scheme should update accordingly. add_task(async function test_theme_switch_updates_existing_pbm_win() { let windowB = await BrowserTestUtils.openNewBrowserWindow(); let pbmWindow = await BrowserTestUtils.openNewBrowserWindow({ private: true, }); info("Normal browsing window should not be in dark mode."); await testWindowColorScheme({ win: window, expectDark: false, expectLWTAttributes: false, }); info("Private browsing window should be in dark mode."); await testWindowColorScheme({ win: pbmWindow, expectDark: true, expectLWTAttributes: true, }); info("Enabling light theme."); let lightTheme = await AddonManager.getAddonByID(LIGHT_THEME_ID); await lightTheme.enable(); info("Normal browsing window should not be in dark mode."); await testWindowColorScheme({ win: window, expectDark: false, expectLWTAttributes: true, }); info("Private browsing window should not be in dark mode."); await testWindowColorScheme({ win: pbmWindow, expectDark: false, expectLWTAttributes: true, }); await lightTheme.disable(); info("Enabling dark theme."); let darkTheme = await AddonManager.getAddonByID(DARK_THEME_ID); await darkTheme.enable(); info("Normal browsing window should be in dark mode."); await testWindowColorScheme({ win: window, expectDark: true, expectLWTAttributes: true, }); info("Private browsing window should be in dark mode."); await testWindowColorScheme({ win: pbmWindow, expectDark: true, expectLWTAttributes: true, }); await darkTheme.disable(); await BrowserTestUtils.closeWindow(windowB); await BrowserTestUtils.closeWindow(pbmWindow); }); // pageInfo windows should inherit the PBM window dark theme. add_task(async function test_pbm_dark_page_info() { for (let isPBM of [false, true]) { let win = await BrowserTestUtils.openNewBrowserWindow({ private: isPBM, }); let windowTypeStr = isPBM ? "private" : "normal"; info(`Opening pageInfo from ${windowTypeStr} browsing.`); await BrowserTestUtils.withNewTab( { gBrowser: win.gBrowser, url: "https://example.com" }, async () => { let pageInfo = win.BrowserCommands.pageInfo(null, "securityTab"); await BrowserTestUtils.waitForEvent(pageInfo, "page-info-init"); let prefersColorScheme = await getPrefersColorSchemeInfo({ win: pageInfo, chrome: true, }); if (isPBM) { ok( !prefersColorScheme.light && prefersColorScheme.dark, "pageInfo from private window should be themed dark." ); } else { ok( prefersColorScheme.light && !prefersColorScheme.dark, "pageInfo from normal window should be themed light." ); } pageInfo.close(); } ); await BrowserTestUtils.closeWindow(win); } }); // Prompts should inherit the PBM window dark theme. add_task(async function test_pbm_dark_prompts() { const { MODAL_TYPE_TAB, MODAL_TYPE_CONTENT } = Services.prompt; for (let isPBM of [false, true]) { let win = await BrowserTestUtils.openNewBrowserWindow({ private: isPBM, }); // TODO: Once Bug 1751953 has been fixed, we can also test MODAL_TYPE_WINDOW // here. for (let modalType of [MODAL_TYPE_TAB, MODAL_TYPE_CONTENT]) { let windowTypeStr = isPBM ? "private" : "normal"; let modalTypeStr = modalType == MODAL_TYPE_TAB ? "tab" : "content"; info(`Opening ${modalTypeStr} prompt from ${windowTypeStr} browsing.`); let openPromise = PromptTestUtils.waitForPrompt( win.gBrowser.selectedBrowser, { modalType, promptType: "alert", } ); let promptPromise = Services.prompt.asyncAlert( win.gBrowser.selectedBrowser.browsingContext, modalType, "Hello", "Hello, world!" ); let dialog = await openPromise; let prefersColorScheme = await getPrefersColorSchemeInfo({ win: dialog.ui.prompt, chrome: true, }); if (isPBM) { ok( !prefersColorScheme.light && prefersColorScheme.dark, "Prompt from private window should be themed dark." ); } else { ok( prefersColorScheme.light && !prefersColorScheme.dark, "Prompt from normal window should be themed light." ); } await PromptTestUtils.handlePrompt(dialog); await promptPromise; } await BrowserTestUtils.closeWindow(win); } });