toolkit/devtools/gcli/commands/addon.js

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/toolkit/devtools/gcli/commands/addon.js	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,256 @@
     1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.6 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.7 +
     1.8 +"use strict";
     1.9 +
    1.10 +const { Cc, Ci, Cu } = require("chrome");
    1.11 +const { AddonManager } = Cu.import("resource://gre/modules/AddonManager.jsm", {});
    1.12 +const gcli = require("gcli/index");
    1.13 +const { Promise: promise } = require("resource://gre/modules/Promise.jsm");
    1.14 +
    1.15 +const BRAND_SHORT_NAME = Cc["@mozilla.org/intl/stringbundle;1"]
    1.16 +                           .getService(Ci.nsIStringBundleService)
    1.17 +                           .createBundle("chrome://branding/locale/brand.properties")
    1.18 +                           .GetStringFromName("brandShortName");
    1.19 +
    1.20 +/**
    1.21 + * Takes a function that uses a callback as its last parameter, and returns a
    1.22 + * new function that returns a promise instead.
    1.23 + * This should probably live in async-util
    1.24 + */
    1.25 +const promiseify = function(scope, functionWithLastParamCallback) {
    1.26 +  return (...args) => {
    1.27 +    return new Promise(resolve => {
    1.28 +      args.push((...results) => {
    1.29 +        resolve(results.length > 1 ? results : results[0]);
    1.30 +      });
    1.31 +      functionWithLastParamCallback.apply(scope, args);
    1.32 +    });
    1.33 +  }
    1.34 +};
    1.35 +
    1.36 +// Convert callback based functions to promise based ones
    1.37 +const getAllAddons = promiseify(AddonManager, AddonManager.getAllAddons);
    1.38 +const getAddonsByTypes = promiseify(AddonManager, AddonManager.getAddonsByTypes);
    1.39 +
    1.40 +/**
    1.41 + * Return a string array containing the pending operations on an addon
    1.42 + */
    1.43 +function pendingOperations(addon) {
    1.44 +  let allOperations = [
    1.45 +    "PENDING_ENABLE", "PENDING_DISABLE", "PENDING_UNINSTALL",
    1.46 +    "PENDING_INSTALL", "PENDING_UPGRADE"
    1.47 +  ];
    1.48 +  return allOperations.reduce(function(operations, opName) {
    1.49 +    return addon.pendingOperations & AddonManager[opName] ?
    1.50 +      operations.concat(opName) :
    1.51 +      operations;
    1.52 +  }, []);
    1.53 +}
    1.54 +
    1.55 +exports.items = [
    1.56 +  {
    1.57 +    item: "type",
    1.58 +    name: "addon",
    1.59 +    parent: "selection",
    1.60 +    stringifyProperty: "name",
    1.61 +    cacheable: true,
    1.62 +    constructor: function() {
    1.63 +      // Tell GCLI to clear the cache of addons when one is added or removed
    1.64 +      let listener = {
    1.65 +        onInstalled: addon => { this.clearCache(); },
    1.66 +        onUninstalled: addon => { this.clearCache(); },
    1.67 +      };
    1.68 +      AddonManager.addAddonListener(listener);
    1.69 +    },
    1.70 +    lookup: function() {
    1.71 +      return getAllAddons().then(addons => {
    1.72 +        return addons.map(addon => {
    1.73 +          let name = addon.name + " " + addon.version;
    1.74 +          name = name.trim().replace(/\s/g, "_");
    1.75 +          return { name: name, value: addon };
    1.76 +        });
    1.77 +      });
    1.78 +    }
    1.79 +  },
    1.80 +  {
    1.81 +    name: "addon",
    1.82 +    description: gcli.lookup("addonDesc")
    1.83 +  },
    1.84 +  {
    1.85 +    name: "addon list",
    1.86 +    description: gcli.lookup("addonListDesc"),
    1.87 +    returnType: "addonsInfo",
    1.88 +    params: [{
    1.89 +      name: "type",
    1.90 +      type: {
    1.91 +        name: "selection",
    1.92 +        data: [ "dictionary", "extension", "locale", "plugin", "theme", "all" ]
    1.93 +      },
    1.94 +      defaultValue: "all",
    1.95 +      description: gcli.lookup("addonListTypeDesc")
    1.96 +    }],
    1.97 +    exec: function(args, context) {
    1.98 +      let types = (args.type === "all") ? null : [ args.type ];
    1.99 +      return getAddonsByTypes(types).then(addons => {
   1.100 +        addons = addons.map(function(addon) {
   1.101 +          return {
   1.102 +            name: addon.name,
   1.103 +            version: addon.version,
   1.104 +            isActive: addon.isActive,
   1.105 +            pendingOperations: pendingOperations(addon)
   1.106 +          };
   1.107 +        });
   1.108 +        return { addons: addons, type: args.type };
   1.109 +      });
   1.110 +    }
   1.111 +  },
   1.112 +  {
   1.113 +    item: "converter",
   1.114 +    from: "addonsInfo",
   1.115 +    to: "view",
   1.116 +    exec: function(addonsInfo, context) {
   1.117 +      if (!addonsInfo.addons.length) {
   1.118 +        return context.createView({
   1.119 +          html: "<p>${message}</p>",
   1.120 +          data: { message: gcli.lookup("addonNoneOfType") }
   1.121 +        });
   1.122 +      }
   1.123 +
   1.124 +      let headerLookups = {
   1.125 +        "dictionary": "addonListDictionaryHeading",
   1.126 +        "extension": "addonListExtensionHeading",
   1.127 +        "locale": "addonListLocaleHeading",
   1.128 +        "plugin": "addonListPluginHeading",
   1.129 +        "theme": "addonListThemeHeading",
   1.130 +        "all": "addonListAllHeading"
   1.131 +      };
   1.132 +      let header = gcli.lookup(headerLookups[addonsInfo.type] ||
   1.133 +                               "addonListUnknownHeading");
   1.134 +
   1.135 +      let operationLookups = {
   1.136 +        "PENDING_ENABLE": "addonPendingEnable",
   1.137 +        "PENDING_DISABLE": "addonPendingDisable",
   1.138 +        "PENDING_UNINSTALL": "addonPendingUninstall",
   1.139 +        "PENDING_INSTALL": "addonPendingInstall",
   1.140 +        "PENDING_UPGRADE": "addonPendingUpgrade"
   1.141 +      };
   1.142 +      function lookupOperation(opName) {
   1.143 +        let lookupName = operationLookups[opName];
   1.144 +        return lookupName ? gcli.lookup(lookupName) : opName;
   1.145 +      }
   1.146 +
   1.147 +      function arrangeAddons(addons) {
   1.148 +        let enabledAddons = [];
   1.149 +        let disabledAddons = [];
   1.150 +        addons.forEach(function(addon) {
   1.151 +          if (addon.isActive) {
   1.152 +            enabledAddons.push(addon);
   1.153 +          } else {
   1.154 +            disabledAddons.push(addon);
   1.155 +          }
   1.156 +        });
   1.157 +
   1.158 +        function compareAddonNames(nameA, nameB) {
   1.159 +          return String.localeCompare(nameA.name, nameB.name);
   1.160 +        }
   1.161 +        enabledAddons.sort(compareAddonNames);
   1.162 +        disabledAddons.sort(compareAddonNames);
   1.163 +
   1.164 +        return enabledAddons.concat(disabledAddons);
   1.165 +      }
   1.166 +
   1.167 +      function isActiveForToggle(addon) {
   1.168 +        return (addon.isActive && ~~addon.pendingOperations.indexOf("PENDING_DISABLE"));
   1.169 +      }
   1.170 +
   1.171 +      return context.createView({
   1.172 +        html:
   1.173 +          "<table>" +
   1.174 +          " <caption>${header}</caption>" +
   1.175 +          " <tbody>" +
   1.176 +          "  <tr foreach='addon in ${addons}'" +
   1.177 +          "      class=\"gcli-addon-${addon.status}\">" +
   1.178 +          "    <td>${addon.name} ${addon.version}</td>" +
   1.179 +          "    <td>${addon.pendingOperations}</td>" +
   1.180 +          "    <td>" +
   1.181 +          "      <span class='gcli-out-shortcut'" +
   1.182 +          "          data-command='addon ${addon.toggleActionName} ${addon.label}'" +
   1.183 +          "          onclick='${onclick}' ondblclick='${ondblclick}'" +
   1.184 +          "      >${addon.toggleActionMessage}</span>" +
   1.185 +          "    </td>" +
   1.186 +          "  </tr>" +
   1.187 +          " </tbody>" +
   1.188 +          "</table>",
   1.189 +        data: {
   1.190 +          header: header,
   1.191 +          addons: arrangeAddons(addonsInfo.addons).map(function(addon) {
   1.192 +            return {
   1.193 +              name: addon.name,
   1.194 +              label: addon.name.replace(/\s/g, "_") +
   1.195 +                    (addon.version ? "_" + addon.version : ""),
   1.196 +              status: addon.isActive ? "enabled" : "disabled",
   1.197 +              version: addon.version,
   1.198 +              pendingOperations: addon.pendingOperations.length ?
   1.199 +                (" (" + gcli.lookup("addonPending") + ": "
   1.200 +                 + addon.pendingOperations.map(lookupOperation).join(", ")
   1.201 +                 + ")") :
   1.202 +                "",
   1.203 +              toggleActionName: isActiveForToggle(addon) ? "disable": "enable",
   1.204 +              toggleActionMessage: isActiveForToggle(addon) ?
   1.205 +                gcli.lookup("addonListOutDisable") :
   1.206 +                gcli.lookup("addonListOutEnable")
   1.207 +            };
   1.208 +          }),
   1.209 +          onclick: context.update,
   1.210 +          ondblclick: context.updateExec
   1.211 +        }
   1.212 +      });
   1.213 +    }
   1.214 +  },
   1.215 +  {
   1.216 +    name: "addon enable",
   1.217 +    description: gcli.lookup("addonEnableDesc"),
   1.218 +    params: [
   1.219 +      {
   1.220 +        name: "addon",
   1.221 +        type: "addon",
   1.222 +        description: gcli.lookup("addonNameDesc")
   1.223 +      }
   1.224 +    ],
   1.225 +    exec: function(args, context) {
   1.226 +      let name = (args.addon.name + " " + args.addon.version).trim();
   1.227 +      if (args.addon.userDisabled) {
   1.228 +        args.addon.userDisabled = false;
   1.229 +        return gcli.lookupFormat("addonEnabled", [ name ]);
   1.230 +      }
   1.231 +
   1.232 +      return gcli.lookupFormat("addonAlreadyEnabled", [ name ]);
   1.233 +    }
   1.234 +  },
   1.235 +  {
   1.236 +    name: "addon disable",
   1.237 +    description: gcli.lookup("addonDisableDesc"),
   1.238 +    params: [
   1.239 +      {
   1.240 +        name: "addon",
   1.241 +        type: "addon",
   1.242 +        description: gcli.lookup("addonNameDesc")
   1.243 +      }
   1.244 +    ],
   1.245 +    exec: function(args, context) {
   1.246 +      // If the addon is not disabled or is set to "click to play" then
   1.247 +      // disable it. Otherwise display the message "Add-on is already
   1.248 +      // disabled."
   1.249 +      let name = (args.addon.name + " " + args.addon.version).trim();
   1.250 +      if (!args.addon.userDisabled ||
   1.251 +          args.addon.userDisabled === AddonManager.STATE_ASK_TO_ACTIVATE) {
   1.252 +        args.addon.userDisabled = true;
   1.253 +        return gcli.lookupFormat("addonDisabled", [ name ]);
   1.254 +      }
   1.255 +
   1.256 +      return gcli.lookupFormat("addonAlreadyDisabled", [ name ]);
   1.257 +    }
   1.258 +  }
   1.259 +];

mercurial