diff options
Diffstat (limited to 'toolkit/mozapps/extensions/test/xpcshell/test_webextension_theme.js')
-rw-r--r-- | toolkit/mozapps/extensions/test/xpcshell/test_webextension_theme.js | 365 |
1 files changed, 365 insertions, 0 deletions
diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_webextension_theme.js b/toolkit/mozapps/extensions/test/xpcshell/test_webextension_theme.js new file mode 100644 index 0000000000..0b1b0ee8d9 --- /dev/null +++ b/toolkit/mozapps/extensions/test/xpcshell/test_webextension_theme.js @@ -0,0 +1,365 @@ +"use strict"; + +/** + * This file contains test for 'theme' type WebExtension addons. Tests focus mostly + * on interoperability between the different theme formats (XUL and LWT) and + * Addon Manager integration. + * + * Coverage may overlap with other tests in this folder. + */ + +const THEME_IDS = [ + "theme3@tests.mozilla.org", + "theme2@personas.mozilla.org", // Unused. Legacy. Evil. + "default-theme@mozilla.org", +]; +const REAL_THEME_IDS = [THEME_IDS[0], THEME_IDS[2]]; +const DEFAULT_THEME = THEME_IDS[2]; + +const profileDir = gProfD.clone(); +profileDir.append("extensions"); + +Services.prefs.setIntPref( + "extensions.enabledScopes", + AddonManager.SCOPE_PROFILE | AddonManager.SCOPE_APPLICATION +); + +// We remember the last/ currently active theme for tracking events. +var gActiveTheme = null; + +add_task(async function setup_to_default_browserish_state() { + createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2"); + + await promiseWriteWebManifestForExtension( + { + author: "Some author", + manifest_version: 2, + name: "Web Extension Name", + version: "1.0", + theme: { images: { theme_frame: "example.png" } }, + applications: { + gecko: { + id: THEME_IDS[0], + }, + }, + }, + profileDir + ); + + await promiseStartupManager(); + + if (AppConstants.MOZ_DEV_EDITION) { + // Developer Edition selects the wrong theme by default. + let defaultTheme = await AddonManager.getAddonByID(DEFAULT_THEME); + await defaultTheme.enable(); + } + + let [t1, t2, d] = await promiseAddonsByIDs(THEME_IDS); + Assert.ok(t1, "Theme addon should exist"); + Assert.equal(t2, null, "Theme addon is not a thing anymore"); + Assert.ok(d, "Theme addon should exist"); + + await t1.disable(); + await new Promise(executeSoon); + Assert.ok(!t1.isActive, "Theme should be disabled"); + Assert.ok(d.isActive, "Default theme should be active"); + + await promiseRestartManager(); + + [t1, t2, d] = await promiseAddonsByIDs(THEME_IDS); + Assert.ok(!t1.isActive, "Theme should still be disabled"); + Assert.ok(d.isActive, "Default theme should still be active"); + + gActiveTheme = d.id; +}); + +/** + * Set the `userDisabled` property of one specific theme and check if the theme + * switching works as expected by checking the state of all installed themes. + * + * @param {String} which ID of the addon to set the `userDisabled` property on + * @param {Boolean} disabled Flag value to switch to + */ +async function setDisabledStateAndCheck(which, disabled = false) { + if (disabled) { + Assert.equal(which, gActiveTheme, "Only the active theme can be disabled"); + } + + let themeToDisable = disabled ? which : gActiveTheme; + let themeToEnable = disabled ? DEFAULT_THEME : which; + + let expectedStates = { + [themeToDisable]: true, + [themeToEnable]: false, + }; + let addonEvents = { + [themeToDisable]: [{ event: "onDisabling" }, { event: "onDisabled" }], + [themeToEnable]: [{ event: "onEnabling" }, { event: "onEnabled" }], + }; + + // Set the state of the theme to change. + let theme = await promiseAddonByID(which); + await expectEvents({ addonEvents }, () => { + if (disabled) { + theme.disable(); + } else { + theme.enable(); + } + }); + + let isDisabled; + for (theme of await promiseAddonsByIDs(REAL_THEME_IDS)) { + isDisabled = theme.id in expectedStates ? expectedStates[theme.id] : true; + Assert.equal( + theme.userDisabled, + isDisabled, + `Theme '${theme.id}' should be ${isDisabled ? "dis" : "en"}abled` + ); + Assert.equal( + theme.pendingOperations, + AddonManager.PENDING_NONE, + "There should be no pending operations when no restart is expected" + ); + Assert.equal( + theme.isActive, + !isDisabled, + `Theme '${theme.id} should be ${isDisabled ? "in" : ""}active` + ); + } + + await promiseRestartManager(); + + // All should still be good after a restart of the Addon Manager. + for (theme of await promiseAddonsByIDs(REAL_THEME_IDS)) { + isDisabled = theme.id in expectedStates ? expectedStates[theme.id] : true; + Assert.equal( + theme.userDisabled, + isDisabled, + `Theme '${theme.id}' should be ${isDisabled ? "dis" : "en"}abled` + ); + Assert.equal( + theme.isActive, + !isDisabled, + `Theme '${theme.id}' should be ${isDisabled ? "in" : ""}active` + ); + Assert.equal( + theme.pendingOperations, + AddonManager.PENDING_NONE, + "There should be no pending operations left" + ); + if (!isDisabled) { + gActiveTheme = theme.id; + } + } +} + +add_task(async function test_WebExtension_themes() { + // Enable the WebExtension theme. + await setDisabledStateAndCheck(THEME_IDS[0]); + + // Disabling WebExtension should revert to the default theme. + await setDisabledStateAndCheck(THEME_IDS[0], true); + + // Enable it again. + await setDisabledStateAndCheck(THEME_IDS[0]); +}); + +add_task(async function test_default_theme() { + // Explicitly enable the default theme. + await setDisabledStateAndCheck(DEFAULT_THEME); + + // Swith to the WebExtension theme. + await setDisabledStateAndCheck(THEME_IDS[0]); + + // Enable it again. + await setDisabledStateAndCheck(DEFAULT_THEME); +}); + +add_task(async function uninstall_offers_undo() { + let defaultTheme = await AddonManager.getAddonByID(DEFAULT_THEME); + const ID = THEME_IDS[0]; + let theme = await promiseAddonByID(ID); + + Assert.ok(theme, "Webextension theme is present"); + + async function promiseAddonEvent(event, id) { + let [addon] = await AddonTestUtils.promiseAddonEvent(event); + if (id) { + Assert.equal(addon.id, id, `Got event for expected addon (${event})`); + } + } + + async function uninstallTheme() { + let uninstallingPromise = promiseAddonEvent("onUninstalling", ID); + await theme.uninstall(true); + await uninstallingPromise; + + Assert.ok( + hasFlag(theme.pendingOperations, AddonManager.PENDING_UNINSTALL), + "Theme being uninstalled has PENDING_UNINSTALL flag" + ); + } + + async function cancelUninstallTheme() { + let cancelPromise = promiseAddonEvent("onOperationCancelled", ID); + theme.cancelUninstall(); + await cancelPromise; + + Assert.equal( + theme.pendingOperations, + AddonManager.PENDING_NONE, + "PENDING_UNINSTALL flag is cleared when uninstall is canceled" + ); + } + + // A theme should still be disabled if the uninstallation of a disabled theme + // is undone. + Assert.ok(!theme.isActive, "Webextension theme is not active"); + Assert.ok(defaultTheme.isActive, "Default theme is active"); + await uninstallTheme(); + await cancelUninstallTheme(); + Assert.ok(!theme.isActive, "Webextension theme is still not active"); + Assert.ok(defaultTheme.isActive, "Default theme is still active"); + + // Enable theme, the previously active theme should be disabled. + await Promise.all([ + promiseAddonEvent("onDisabled", DEFAULT_THEME), + promiseAddonEvent("onEnabled", ID), + theme.enable(), + ]); + Assert.ok(theme.isActive, "Webextension theme is active after enabling"); + Assert.ok(!defaultTheme.isActive, "Default theme is not active any more"); + + // Uninstall active theme, default theme should become active. + await Promise.all([ + // Note: no listener for onDisabled & ID because the uninstall is pending. + promiseAddonEvent("onEnabled", DEFAULT_THEME), + uninstallTheme(), + ]); + Assert.ok(!theme.isActive, "Webextension theme is not active upon uninstall"); + Assert.ok(defaultTheme.isActive, "Default theme is active again"); + + // Undo uninstall, default theme should be deactivated. + await Promise.all([ + // Note: no listener for onEnabled & ID because the uninstall was pending. + promiseAddonEvent("onDisabled", DEFAULT_THEME), + cancelUninstallTheme(), + ]); + Assert.ok(theme.isActive, "Webextension theme is active upon undo uninstall"); + Assert.ok(!defaultTheme.isActive, "Default theme is not active again"); + + // Immediately remove the theme. Default theme should be activated. + await Promise.all([ + promiseAddonEvent("onEnabled", DEFAULT_THEME), + theme.uninstall(), + ]); + + await promiseRestartManager(); +}); + +// Test that default_locale works with WE themes +add_task(async function default_locale_themes() { + let addon = await promiseInstallWebExtension({ + manifest: { + default_locale: "en", + name: "__MSG_name__", + description: "__MSG_description__", + theme: { + colors: { + frame: "black", + tab_background_text: "white", + }, + }, + }, + files: { + "_locales/en/messages.json": `{ + "name": { + "message": "the name" + }, + "description": { + "message": "the description" + } + }`, + }, + }); + + addon = await promiseAddonByID(addon.id); + equal(addon.name, "the name"); + equal(addon.description, "the description"); + equal(addon.type, "theme"); + await addon.uninstall(); +}); + +add_task(async function test_theme_update() { + let addon = await AddonManager.getAddonByID(DEFAULT_THEME); + ok(!addon.userDisabled, "default theme is enabled"); + + await AddonTestUtils.promiseRestartManager("2"); + + addon = await AddonManager.getAddonByID(DEFAULT_THEME); + ok(!addon.userDisabled, "default theme is enabled after upgrade"); +}); + +add_task(async function test_builtin_theme_permissions() { + const ADDON_ID = "mytheme@mozilla.org"; + + let themeDef = { + manifest: { + applications: { gecko: { id: ADDON_ID } }, + version: "1.0", + theme: {}, + }, + }; + + function checkPerms(addon) { + // builtin themes enable or disable based on disabled state + Assert.equal( + addon.userDisabled, + hasFlag(addon.permissions, AddonManager.PERM_CAN_ENABLE), + "enable permission is correct" + ); + Assert.equal( + !addon.userDisabled, + hasFlag(addon.permissions, AddonManager.PERM_CAN_DISABLE), + "disable permission is correct" + ); + // builtin themes do not get any other permission + Assert.ok( + !hasFlag(addon.permissions, AddonManager.PERM_CAN_INSTALL), + "cannot install by user" + ); + Assert.ok( + !hasFlag(addon.permissions, AddonManager.PERM_CAN_UPGRADE), + "cannot upgrade" + ); + Assert.ok( + !hasFlag(addon.permissions, AddonManager.PERM_CAN_UNINSTALL), + "cannot uninstall" + ); + Assert.ok( + !hasFlag( + addon.permissions, + AddonManager.PERM_CAN_CHANGE_PRIVATEBROWSING_ACCESS + ), + "can change private browsing access" + ); + Assert.ok( + hasFlag(addon.permissions, AddonManager.PERM_API_CAN_UNINSTALL), + "can uninstall via API" + ); + } + + await setupBuiltinExtension(themeDef, "first-loc", false); + await AddonManager.maybeInstallBuiltinAddon( + ADDON_ID, + "1.0", + "resource://first-loc/" + ); + + let addon = await AddonManager.getAddonByID(ADDON_ID); + checkPerms(addon); + await addon.enable(); + checkPerms(addon); + + await addon.uninstall(); +}); |