diff options
Diffstat (limited to 'toolkit/jetpack/sdk/addon/installer.js')
-rw-r--r-- | toolkit/jetpack/sdk/addon/installer.js | 121 |
1 files changed, 121 insertions, 0 deletions
diff --git a/toolkit/jetpack/sdk/addon/installer.js b/toolkit/jetpack/sdk/addon/installer.js new file mode 100644 index 000000000..bb8cf8d16 --- /dev/null +++ b/toolkit/jetpack/sdk/addon/installer.js @@ -0,0 +1,121 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +module.metadata = { + "stability": "experimental" +}; + +const { Cc, Ci, Cu } = require("chrome"); +const { AddonManager } = Cu.import("resource://gre/modules/AddonManager.jsm"); +const { defer } = require("../core/promise"); +const { setTimeout } = require("../timers"); + +/** + * `install` method error codes: + * + * https://developer.mozilla.org/en/Addons/Add-on_Manager/AddonManager#AddonInstall_errors + */ +exports.ERROR_NETWORK_FAILURE = AddonManager.ERROR_NETWORK_FAILURE; +exports.ERROR_INCORRECT_HASH = AddonManager.ERROR_INCORRECT_HASH; +exports.ERROR_CORRUPT_FILE = AddonManager.ERROR_CORRUPT_FILE; +exports.ERROR_FILE_ACCESS = AddonManager.ERROR_FILE_ACCESS; + +/** + * Immediatly install an addon. + * + * @param {String} xpiPath + * file path to an xpi file to install + * @return {Promise} + * A promise resolved when the addon is finally installed. + * Resolved with addon id as value or rejected with an error code. + */ +exports.install = function install(xpiPath) { + let { promise, resolve, reject } = defer(); + + // Create nsIFile for the xpi file + let file = Cc['@mozilla.org/file/local;1'].createInstance(Ci.nsILocalFile); + try { + file.initWithPath(xpiPath); + } + catch(e) { + reject(exports.ERROR_FILE_ACCESS); + return promise; + } + + // Listen for installation end + let listener = { + onInstallEnded: function(aInstall, aAddon) { + aInstall.removeListener(listener); + // Bug 749745: on FF14+, onInstallEnded is called just before `startup()` + // is called, but we expect to resolve the promise only after it. + // As startup is called synchronously just after onInstallEnded, + // a simple setTimeout(0) is enough + setTimeout(resolve, 0, aAddon.id); + }, + onInstallFailed: function (aInstall) { + aInstall.removeListener(listener); + reject(aInstall.error); + }, + onDownloadFailed: function(aInstall) { + this.onInstallFailed(aInstall); + } + }; + + // Order AddonManager to install the addon + AddonManager.getInstallForFile(file, function(install) { + if (install.error == 0) { + install.addListener(listener); + install.install(); + } else { + reject(install.error); + } + }); + + return promise; +}; + +exports.uninstall = function uninstall(addonId) { + let { promise, resolve, reject } = defer(); + + // Listen for uninstallation end + let listener = { + onUninstalled: function onUninstalled(aAddon) { + if (aAddon.id != addonId) + return; + AddonManager.removeAddonListener(listener); + resolve(); + } + }; + AddonManager.addAddonListener(listener); + + // Order Addonmanager to uninstall the addon + getAddon(addonId).then(addon => addon.uninstall(), reject); + + return promise; +}; + +exports.disable = function disable(addonId) { + return getAddon(addonId).then(addon => { + addon.userDisabled = true; + return addonId; + }); +}; + +exports.enable = function enabled(addonId) { + return getAddon(addonId).then(addon => { + addon.userDisabled = false; + return addonId; + }); +}; + +exports.isActive = function isActive(addonId) { + return getAddon(addonId).then(addon => addon.isActive && !addon.appDisabled); +}; + +const getAddon = function getAddon (id) { + let { promise, resolve, reject } = defer(); + AddonManager.getAddonByID(id, addon => addon ? resolve(addon) : reject()); + return promise; +} +exports.getAddon = getAddon; |