/* 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/. */ "use strict"; /** * You can't require the AddonManager in a child process, but GCLI wants to * check for 'items' in all processes, so we return empty array if the * AddonManager is not available */ function getAddonManager() { try { return { AddonManager: require("resource://gre/modules/AddonManager.jsm").AddonManager, addonManagerActive: true }; } catch (ex) { // Fake up an AddonManager just enough to let the file load return { AddonManager: { getAllAddons() {}, getAddonsByTypes() {} }, addonManagerActive: false }; } } const { Cc, Ci, Cu } = require("chrome"); const { AddonManager, addonManagerActive } = getAddonManager(); const l10n = require("gcli/l10n"); const BRAND_SHORT_NAME = Cc["@mozilla.org/intl/stringbundle;1"] .getService(Ci.nsIStringBundleService) .createBundle("chrome://branding/locale/brand.properties") .GetStringFromName("brandShortName"); /** * Takes a function that uses a callback as its last parameter, and returns a * new function that returns a promise instead. * This should probably live in async-util */ const promiseify = function(scope, functionWithLastParamCallback) { return (...args) => { return new Promise(resolve => { args.push((...results) => { resolve(results.length > 1 ? results : results[0]); }); functionWithLastParamCallback.apply(scope, args); }); } }; // Convert callback based functions to promise based ones const getAllAddons = promiseify(AddonManager, AddonManager.getAllAddons); const getAddonsByTypes = promiseify(AddonManager, AddonManager.getAddonsByTypes); /** * Return a string array containing the pending operations on an addon */ function pendingOperations(addon) { let allOperations = [ "PENDING_ENABLE", "PENDING_DISABLE", "PENDING_UNINSTALL", "PENDING_INSTALL", "PENDING_UPGRADE" ]; return allOperations.reduce(function(operations, opName) { return addon.pendingOperations & AddonManager[opName] ? operations.concat(opName) : operations; }, []); } var items = [ { item: "type", name: "addon", parent: "selection", stringifyProperty: "name", cacheable: true, constructor: function() { // Tell GCLI to clear the cache of addons when one is added or removed let listener = { onInstalled: addon => { this.clearCache(); }, onUninstalled: addon => { this.clearCache(); }, }; AddonManager.addAddonListener(listener); }, lookup: function() { return getAllAddons().then(addons => { return addons.map(addon => { let name = addon.name + " " + addon.version; name = name.trim().replace(/\s/g, "_"); return { name: name, value: addon }; }); }); } }, { name: "addon", description: l10n.lookup("addonDesc") }, { name: "addon list", description: l10n.lookup("addonListDesc"), returnType: "addonsInfo", params: [{ name: "type", type: { name: "selection", data: [ "dictionary", "extension", "locale", "plugin", "theme", "all" ] }, defaultValue: "all", description: l10n.lookup("addonListTypeDesc") }], exec: function(args, context) { let types = (args.type === "all") ? null : [ args.type ]; return getAddonsByTypes(types).then(addons => { addons = addons.map(function(addon) { return { name: addon.name, version: addon.version, isActive: addon.isActive, pendingOperations: pendingOperations(addon) }; }); return { addons: addons, type: args.type }; }); } }, { item: "converter", from: "addonsInfo", to: "view", exec: function(addonsInfo, context) { if (!addonsInfo.addons.length) { return context.createView({ html: "
${message}
", data: { message: l10n.lookup("addonNoneOfType") } }); } let headerLookups = { "dictionary": "addonListDictionaryHeading", "extension": "addonListExtensionHeading", "locale": "addonListLocaleHeading", "plugin": "addonListPluginHeading", "theme": "addonListThemeHeading", "all": "addonListAllHeading" }; let header = l10n.lookup(headerLookups[addonsInfo.type] || "addonListUnknownHeading"); let operationLookups = { "PENDING_ENABLE": "addonPendingEnable", "PENDING_DISABLE": "addonPendingDisable", "PENDING_UNINSTALL": "addonPendingUninstall", "PENDING_INSTALL": "addonPendingInstall", "PENDING_UPGRADE": "addonPendingUpgrade" }; function lookupOperation(opName) { let lookupName = operationLookups[opName]; return lookupName ? l10n.lookup(lookupName) : opName; } function arrangeAddons(addons) { let enabledAddons = []; let disabledAddons = []; addons.forEach(function(addon) { if (addon.isActive) { enabledAddons.push(addon); } else { disabledAddons.push(addon); } }); function compareAddonNames(nameA, nameB) { return String.localeCompare(nameA.name, nameB.name); } enabledAddons.sort(compareAddonNames); disabledAddons.sort(compareAddonNames); return enabledAddons.concat(disabledAddons); } function isActiveForToggle(addon) { return (addon.isActive && ~~addon.pendingOperations.indexOf("PENDING_DISABLE")); } return context.createView({ html: "${addon.name} ${addon.version} | " + "${addon.pendingOperations} | " + "" + " ${addon.toggleActionMessage}" + " | " + "