summaryrefslogtreecommitdiffstats
path: root/toolkit/mozapps/extensions/test/xpcshell/test_webextension_theme.js
diff options
context:
space:
mode:
Diffstat (limited to 'toolkit/mozapps/extensions/test/xpcshell/test_webextension_theme.js')
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/test_webextension_theme.js365
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();
+});