diff options
Diffstat (limited to 'toolkit/mozapps/extensions/test/xpcshell/test_webextension.js')
-rw-r--r-- | toolkit/mozapps/extensions/test/xpcshell/test_webextension.js | 421 |
1 files changed, 421 insertions, 0 deletions
diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_webextension.js b/toolkit/mozapps/extensions/test/xpcshell/test_webextension.js new file mode 100644 index 000000000..b51f47977 --- /dev/null +++ b/toolkit/mozapps/extensions/test/xpcshell/test_webextension.js @@ -0,0 +1,421 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +Components.utils.import("resource://gre/modules/AppConstants.jsm"); + +const ID = "webextension1@tests.mozilla.org"; + +const PREF_SELECTED_LOCALE = "general.useragent.locale"; + +const profileDir = gProfD.clone(); +profileDir.append("extensions"); + +createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "42"); +startupManager(); + +const { GlobalManager, Management } = Components.utils.import("resource://gre/modules/Extension.jsm", {}); + +function promiseAddonStartup() { + return new Promise(resolve => { + let listener = (evt, extension) => { + Management.off("ready", listener); + resolve(extension); + }; + + Management.on("ready", listener); + }); +} + +function promiseInstallWebExtension(aData) { + let addonFile = createTempWebExtensionFile(aData); + + return promiseInstallAllFiles([addonFile]).then(() => { + Services.obs.notifyObservers(addonFile, "flush-cache-entry", null); + return promiseAddonStartup(); + }); +} + +add_task(function*() { + equal(GlobalManager.extensionMap.size, 0); + + yield Promise.all([ + promiseInstallAllFiles([do_get_addon("webextension_1")], true), + promiseAddonStartup() + ]); + + equal(GlobalManager.extensionMap.size, 1); + ok(GlobalManager.extensionMap.has(ID)); + + let chromeReg = AM_Cc["@mozilla.org/chrome/chrome-registry;1"]. + getService(AM_Ci.nsIChromeRegistry); + try { + chromeReg.convertChromeURL(NetUtil.newURI("chrome://webex/content/webex.xul")); + do_throw("Chrome manifest should not have been registered"); + } + catch (e) { + // Expected the chrome url to not be registered + } + + let addon = yield promiseAddonByID(ID); + do_check_neq(addon, null); + do_check_eq(addon.version, "1.0"); + do_check_eq(addon.name, "Web Extension Name"); + do_check_true(addon.isCompatible); + do_check_false(addon.appDisabled); + do_check_true(addon.isActive); + do_check_false(addon.isSystem); + do_check_eq(addon.type, "extension"); + do_check_true(addon.isWebExtension); + do_check_eq(addon.signedState, mozinfo.addon_signing ? AddonManager.SIGNEDSTATE_SIGNED : AddonManager.SIGNEDSTATE_NOT_REQUIRED); + + let uri = do_get_addon_root_uri(profileDir, ID); + + do_check_eq(addon.iconURL, uri + "icon48.png"); + do_check_eq(addon.icon64URL, uri + "icon64.png"); + + // Should persist through a restart + yield promiseShutdownManager(); + + equal(GlobalManager.extensionMap.size, 0); + + startupManager(); + yield promiseAddonStartup(); + + equal(GlobalManager.extensionMap.size, 1); + ok(GlobalManager.extensionMap.has(ID)); + + addon = yield promiseAddonByID(ID); + do_check_neq(addon, null); + do_check_eq(addon.version, "1.0"); + do_check_eq(addon.name, "Web Extension Name"); + do_check_true(addon.isCompatible); + do_check_false(addon.appDisabled); + do_check_true(addon.isActive); + do_check_false(addon.isSystem); + do_check_eq(addon.type, "extension"); + do_check_eq(addon.signedState, mozinfo.addon_signing ? AddonManager.SIGNEDSTATE_SIGNED : AddonManager.SIGNEDSTATE_NOT_REQUIRED); + + let file = getFileForAddon(profileDir, ID); + do_check_true(file.exists()); + + uri = do_get_addon_root_uri(profileDir, ID); + + do_check_eq(addon.iconURL, uri + "icon48.png"); + do_check_eq(addon.icon64URL, uri + "icon64.png"); + + addon.userDisabled = true; + + equal(GlobalManager.extensionMap.size, 0); + + addon.userDisabled = false; + yield promiseAddonStartup(); + + equal(GlobalManager.extensionMap.size, 1); + ok(GlobalManager.extensionMap.has(ID)); + + addon.uninstall(); + + equal(GlobalManager.extensionMap.size, 0); + do_check_false(GlobalManager.extensionMap.has(ID)); + + yield promiseShutdownManager(); +}); + +// Writing the manifest direct to the profile should work +add_task(function*() { + yield promiseWriteWebManifestForExtension({ + name: "Web Extension Name", + version: "1.0", + manifest_version: 2, + applications: { + gecko: { + id: ID + } + } + }, profileDir); + + startupManager(); + yield promiseAddonStartup(); + + let addon = yield promiseAddonByID(ID); + do_check_neq(addon, null); + do_check_eq(addon.version, "1.0"); + do_check_eq(addon.name, "Web Extension Name"); + do_check_true(addon.isCompatible); + do_check_false(addon.appDisabled); + do_check_true(addon.isActive); + do_check_false(addon.isSystem); + do_check_eq(addon.type, "extension"); + do_check_eq(addon.signedState, mozinfo.addon_signing ? AddonManager.SIGNEDSTATE_SIGNED : AddonManager.SIGNEDSTATE_NOT_REQUIRED); + + let file = getFileForAddon(profileDir, ID); + do_check_true(file.exists()); + + addon.uninstall(); + + yield promiseRestartManager(); +}); + +add_task(function* test_manifest_localization() { + const extensionId = "webextension3@tests.mozilla.org"; + + yield promiseInstallAllFiles([do_get_addon("webextension_3")], true); + yield promiseAddonStartup(); + + let addon = yield promiseAddonByID(extensionId); + addon.userDisabled = true; + + equal(addon.name, "Web Extensiøn foo ☹"); + equal(addon.description, "Descriptïon bar ☹ of add-on"); + + Services.prefs.setCharPref(PREF_SELECTED_LOCALE, "fr-FR"); + yield promiseRestartManager(); + + addon = yield promiseAddonByID(extensionId); + + equal(addon.name, "Web Extensiøn le foo ☺"); + equal(addon.description, "Descriptïon le bar ☺ of add-on"); + + Services.prefs.setCharPref(PREF_SELECTED_LOCALE, "de"); + yield promiseRestartManager(); + + addon = yield promiseAddonByID(extensionId); + + equal(addon.name, "Web Extensiøn foo ☹"); + equal(addon.description, "Descriptïon bar ☹ of add-on"); + + addon.uninstall(); +}); + +// Missing version should cause a failure +add_task(function*() { + yield promiseWriteWebManifestForExtension({ + name: "Web Extension Name", + manifest_version: 2, + applications: { + gecko: { + id: ID + } + } + }, profileDir); + + yield promiseRestartManager(); + + let addon = yield promiseAddonByID(ID); + do_check_eq(addon, null); + + let file = getFileForAddon(profileDir, ID); + do_check_false(file.exists()); + + yield promiseRestartManager(); +}); + +// Incorrect manifest version should cause a failure +add_task(function*() { + yield promiseWriteWebManifestForExtension({ + name: "Web Extension Name", + version: "1.0", + manifest_version: 1, + applications: { + gecko: { + id: ID + } + } + }, profileDir); + + yield promiseRestartManager(); + + let addon = yield promiseAddonByID(ID); + do_check_eq(addon, null); + + let file = getFileForAddon(profileDir, ID); + do_check_false(file.exists()); + + yield promiseRestartManager(); +}); + +// install.rdf should be read before manifest.json +add_task(function*() { + + yield Promise.all([ + promiseInstallAllFiles([do_get_addon("webextension_2")], true) + ]); + + yield promiseRestartManager(); + + let installrdf_id = "first-webextension2@tests.mozilla.org"; + let first_addon = yield promiseAddonByID(installrdf_id); + do_check_neq(first_addon, null); + do_check_false(first_addon.appDisabled); + do_check_true(first_addon.isActive); + do_check_false(first_addon.isSystem); + + let manifestjson_id= "last-webextension2@tests.mozilla.org"; + let last_addon = yield promiseAddonByID(manifestjson_id); + do_check_eq(last_addon, null); + + yield promiseRestartManager(); +}); + +// Test that the "options_ui" manifest section is processed correctly. +add_task(function* test_options_ui() { + let OPTIONS_RE = /^moz-extension:\/\/[\da-f]{8}-[\da-f]{4}-[\da-f]{4}-[\da-f]{4}-[\da-f]{12}\/options\.html$/; + + const extensionId = "webextension@tests.mozilla.org"; + yield promiseInstallWebExtension({ + manifest: { + applications: {gecko: {id: extensionId}}, + "options_ui": { + "page": "options.html", + }, + }, + }); + + let addon = yield promiseAddonByID(extensionId); + equal(addon.optionsType, AddonManager.OPTIONS_TYPE_INLINE_BROWSER, + "Addon should have an INLINE_BROWSER options type"); + + ok(OPTIONS_RE.test(addon.optionsURL), + "Addon should have a moz-extension: options URL for /options.html"); + + addon.uninstall(); + + const ID2 = "webextension2@tests.mozilla.org"; + yield promiseInstallWebExtension({ + manifest: { + applications: {gecko: {id: ID2}}, + "options_ui": { + "page": "options.html", + "open_in_tab": true, + }, + }, + }); + + addon = yield promiseAddonByID(ID2); + equal(addon.optionsType, AddonManager.OPTIONS_TYPE_TAB, + "Addon should have a TAB options type"); + + ok(OPTIONS_RE.test(addon.optionsURL), + "Addon should have a moz-extension: options URL for /options.html"); + + addon.uninstall(); +}); + +// Test that experiments permissions add the appropriate dependencies. +add_task(function* test_experiments_dependencies() { + if (AppConstants.RELEASE_OR_BETA) + // Experiments are not enabled on release builds. + return; + + let addonFile = createTempWebExtensionFile({ + manifest: { + applications: {gecko: {id: "meh@experiment"}}, + "permissions": ["experiments.meh"], + }, + }); + + yield promiseInstallAllFiles([addonFile]); + + let addon = yield new Promise(resolve => AddonManager.getAddonByID("meh@experiment", resolve)); + + deepEqual(addon.dependencies, ["meh@experiments.addons.mozilla.org"], + "Addon should have the expected dependencies"); + + equal(addon.appDisabled, true, "Add-on should be app disabled due to missing dependencies"); + + addon.uninstall(); +}); + +// Test that experiments API extensions install correctly. +add_task(function* test_experiments_api() { + if (AppConstants.RELEASE_OR_BETA) + // Experiments are not enabled on release builds. + return; + + const extensionId = "meh@experiments.addons.mozilla.org"; + + let addonFile = createTempXPIFile({ + id: extensionId, + type: 256, + version: "0.1", + name: "Meh API", + }); + + yield promiseInstallAllFiles([addonFile]); + + let addons = yield new Promise(resolve => AddonManager.getAddonsByTypes(["apiextension"], resolve)); + let addon = addons.pop(); + equal(addon.id, extensionId, "Add-on should be installed as an API extension"); + + addons = yield new Promise(resolve => AddonManager.getAddonsByTypes(["extension"], resolve)); + equal(addons.pop().id, extensionId, "Add-on type should be aliased to extension"); + + addon.uninstall(); +}); + +add_task(function* developerShouldOverride() { + let addon = yield promiseInstallWebExtension({ + manifest: { + default_locale: "en", + developer: { + name: "__MSG_name__", + url: "__MSG_url__" + }, + author: "Will be overridden by developer", + homepage_url: "https://will.be.overridden", + }, + files: { + "_locales/en/messages.json": `{ + "name": { + "message": "en name" + }, + "url": { + "message": "https://example.net/en" + } + }` + } + }); + + addon = yield promiseAddonByID(addon.id); + equal(addon.creator, "en name"); + equal(addon.homepageURL, "https://example.net/en"); + addon.uninstall(); +}); + +add_task(function* developerEmpty() { + for (let developer of [{}, null, {name: null, url: null}]) { + let addon = yield promiseInstallWebExtension({ + manifest: { + author: "Some author", + developer: developer, + homepage_url: "https://example.net", + manifest_version: 2, + name: "Web Extension Name", + version: "1.0", + } + }); + + addon = yield promiseAddonByID(addon.id); + equal(addon.creator, "Some author"); + equal(addon.homepageURL, "https://example.net"); + addon.uninstall(); + } +}); + +add_task(function* authorNotString() { + for (let author of [{}, [], 42]) { + let addon = yield promiseInstallWebExtension({ + manifest: { + author: author, + manifest_version: 2, + name: "Web Extension Name", + version: "1.0", + } + }); + + addon = yield promiseAddonByID(addon.id); + equal(addon.creator, null); + addon.uninstall(); + } +}); |