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