]> git.donarmstrong.com Git - dactyl.git/blobdiff - common/modules/addons.jsm
New upstream version 1.0+hg6948
[dactyl.git] / common / modules / addons.jsm
index 6f43950c46b44649bcb836869c4ecddb1ce5335b..e2c577ad9f704a611207314cf97ffd40881e112b 100644 (file)
@@ -1,4 +1,4 @@
-// Copyright (c) 2009-2011 by Kris Maglione <maglione.k@gmail.com>
+// Copyright (c) 2009-2012 Kris Maglione <maglione.k@gmail.com>
 // Copyright (c) 2009-2010 by Doug Kearns <dougkearns@gmail.com>
 //
 // This work is licensed for reuse under an MIT license. Details are
@@ -7,15 +7,15 @@
 
 try {
 
-Components.utils.import("resource://dactyl/bootstrap.jsm");
 defineModule("addons", {
     exports: ["AddonManager", "Addons", "Addon", "addons"],
-    require: ["services"],
-    use: ["completion", "config", "io", "messages", "prefs", "template", "util"]
-}, this);
+    require: ["services", "util"]
+});
+
+this.lazyRequire("completion", ["completion"]);
+lazyRequire("template", ["template"]);
 
-var callResult = function callResult(method) {
-    let args = Array.slice(arguments, 1);
+var callResult = function callResult(method, ...args) {
     return function (result) { result[method].apply(result, args); };
 }
 
@@ -24,7 +24,7 @@ var listener = function listener(action, event)
         this.dactyl[install.error ? "echoerr" : "echomsg"](
             _("addon.error", action, event, (install.name || install.sourceURI.spec) +
                 (install.error ? ": " + addons.errors[install.error] : "")));
-    }
+    };
 
 var AddonListener = Class("AddonListener", {
     init: function init(modules) {
@@ -59,7 +59,6 @@ var updateAddons = Class("UpgradeListener", AddonListener, {
 
     },
     onUpdateAvailable: function (addon, install) {
-        util.dump("onUpdateAvailable");
         this.upgrade.push(addon);
         install.addListener(this);
         install.install();
@@ -76,7 +75,7 @@ var updateAddons = Class("UpgradeListener", AddonListener, {
 
 var actions = {
     delete: {
-        name: "extde[lete]",
+        name: ["extde[lete]", "extrm"],
         description: "Uninstall an extension",
         action: callResult("uninstall"),
         perm: "uninstall"
@@ -85,14 +84,14 @@ var actions = {
         name: "exte[nable]",
         description: "Enable an extension",
         action: function (addon) { addon.userDisabled = false; },
-        filter: function ({ item }) item.userDisabled,
+        filter: function (addon) addon.userDisabled,
         perm: "enable"
     },
     disable: {
         name: "extd[isable]",
         description: "Disable an extension",
         action: function (addon) { addon.userDisabled = true; },
-        filter: function ({ item }) !item.userDisabled,
+        filter: function (addon) !addon.userDisabled,
         perm: "disable"
     },
     options: {
@@ -105,21 +104,22 @@ var actions = {
             else
                 this.dactyl.open(addon.optionsURL, { from: "extoptions" });
         },
-        filter: function ({ item }) item.isActive && item.optionsURL
+        filter: function (addon) addon.isActive && addon.optionsURL
     },
     rehash: {
         name: "extr[ehash]",
         description: "Reload an extension",
         action: function (addon) {
-            util.assert(util.haveGecko("2b"), _("command.notUseful", config.host));
+            util.assert(config.haveGecko("2b"), _("command.notUseful", config.host));
+            util.flushCache();
             util.timeout(function () {
                 addon.userDisabled = true;
                 addon.userDisabled = false;
             });
         },
         get filter() {
-            let ids = Set(keys(JSON.parse(prefs.get("extensions.bootstrappedAddons", "{}"))));
-            return function ({ item }) !item.userDisabled && Set.has(ids, item.id);
+            return function (addon) !addon.userDisabled &&
+                !(addon.operationsRequiringRestart & (AddonManager.OP_NEEDS_RESTART_ENABLE | AddonManager.OP_NEEDS_RESTART_DISABLE));
         },
         perm: "disable"
     },
@@ -145,21 +145,18 @@ var Addon = Class("Addon", {
         this.nodes = {
             commandTarget: this
         };
-        XML.ignoreWhitespace = true;
-        util.xmlToDom(
-            <tr highlight="Addon" key="row" xmlns:dactyl={NS} xmlns={XHTML}>
-                <td highlight="AddonName" key="name"/>
-                <td highlight="AddonVersion" key="version"/>
-                <td highlight="AddonStatus" key="status"/>
-                <td highlight="AddonButtons Buttons">
-                    <a highlight="Button" href="javascript:0" key="enable">{_("addon.action.On")}</a>
-                    <a highlight="Button" href="javascript:0" key="disable">{_("addon.action.Off")}</a>
-                    <a highlight="Button" href="javascript:0" key="delete">{_("addon.action.Delete")}</a>
-                    <a highlight="Button" href="javascript:0" key="update">{_("addon.action.Update")}</a>
-                    <a highlight="Button" href="javascript:0" key="options">{_("addon.action.Options")}</a>
-                </td>
-                <td highlight="AddonDescription" key="description"/>
-            </tr>,
+        DOM.fromJSON(
+            ["tr", { highlight: "Addon", key: "row" },
+                ["td", { highlight: "AddonName", key: "name" }],
+                ["td", { highlight: "AddonVersion", key: "version" }],
+                ["td", { highlight: "AddonButtons Buttons" },
+                    ["a", { highlight: "Button", href: "javascript:0", key: "enable" }, _("addon.action.On")],
+                    ["a", { highlight: "Button", href: "javascript:0", key: "disable" }, _("addon.action.Off")],
+                    ["a", { highlight: "Button", href: "javascript:0", key: "delete" }, _("addon.action.Delete")],
+                    ["a", { highlight: "Button", href: "javascript:0", key: "update" }, _("addon.action.Update")],
+                    ["a", { highlight: "Button", href: "javascript:0", key: "options" }, _("addon.action.Options")]],
+                ["td", { highlight: "AddonStatus", key: "status" }],
+                ["td", { highlight: "AddonDescription", key: "description" }]],
             this.list.document, this.nodes);
 
         this.update();
@@ -171,7 +168,7 @@ var Addon = Class("Addon", {
         let action = actions[cmd];
         if ("perm" in action && !(this.permissions & AddonManager["PERM_CAN_" + action.perm.toUpperCase()]))
             return false;
-        if ("filter" in action && !action.filter({ item: this }))
+        if ("filter" in action && !action.filter(this))
             return false;
         return true;
     },
@@ -189,11 +186,8 @@ var Addon = Class("Addon", {
     compare: function compare(other) String.localeCompare(this.name, other.name),
 
     get statusInfo() {
-        XML.ignoreWhitespace = XML.prettyPrinting = false;
-        default xml namespace = XHTML;
-
-        let info = this.isActive ? <span highlight="Enabled">enabled</span>
-                                 : <span highlight="Disabled">disabled</span>;
+        let info = this.isActive ? ["span", { highlight: "Enabled" }, "enabled"]
+                                 : ["span", { highlight: "Disabled" }, "disabled"];
 
         let pending;
         if (this.pendingOperations & AddonManager.PENDING_UNINSTALL)
@@ -207,24 +201,28 @@ var Addon = Class("Addon", {
         else if (this.pendingOperations & AddonManager.PENDING_UPGRADE)
             pending = ["Enabled", "upgraded"];
         if (pending)
-            return <>{info}&#xa0;(<span highlight={pending[0]}>{pending[1]}</span>
-                                  &#xa0;on <a href="#" dactyl:command="dactyl.restart" xmlns:dactyl={NS}>restart</a>)</>;
+            return [info, " (",
+                    ["span", { highlight: pending[0] }, pending[1]],
+                    " on ",
+                    ["a", { href: "#", "dactyl:command": "dactyl.restart" }, "restart"],
+                    ")"];
         return info;
     },
 
     update: function callee() {
-        let self = this;
-        function update(key, xml) {
-            let node = self.nodes[key];
+        let update = (key, xml) => {
+            let node = this.nodes[key];
             while (node.firstChild)
                 node.removeChild(node.firstChild);
-            node.appendChild(util.xmlToDom(<>{xml}</>, self.list.document));
+
+            DOM(node).append(isArray(xml) ? xml : DOM.DOMString(xml));
         }
 
         update("name", template.icon({ icon: this.iconURL }, this.name));
         this.nodes.version.textContent = this.version;
         update("status", this.statusInfo);
         this.nodes.description.textContent = this.description;
+        DOM(this.nodes.row).attr("active", this.isActive || null);
 
         for (let node in values(this.nodes))
             if (node.update && node.update !== callee)
@@ -278,18 +276,15 @@ var AddonList = Class("AddonList", {
         this.update();
     },
 
-    message: Class.memoize(function () {
-
-        XML.ignoreWhitespace = true;
-        util.xmlToDom(<table highlight="Addons" key="list" xmlns={XHTML}>
-                        <tr highlight="AddonHead">
-                            <td>{_("title.Name")}</td>
-                            <td>{_("title.Version")}</td>
-                            <td>{_("title.Status")}</td>
-                            <td/>
-                            <td>{_("title.Description")}</td>
-                        </tr>
-                      </table>, this.document, this.nodes);
+    message: Class.Memoize(function () {
+        DOM.fromJSON(["table", { highlight: "Addons", key: "list" },
+                        ["tr", { highlight: "AddonHead" },
+                            ["td", {}, _("title.Name")],
+                            ["td", {}, _("title.Version")],
+                            ["td"],
+                            ["td", {}, _("title.Status")],
+                            ["td", {}, _("title.Description")]]],
+                      this.document, this.nodes);
 
         if (this._addons)
             this._init();
@@ -348,15 +343,15 @@ var AddonList = Class("AddonList", {
 });
 
 var Addons = Module("addons", {
-    errors: Class.memoize(function ()
+    errors: Class.Memoize(function ()
             array(["ERROR_NETWORK_FAILURE", "ERROR_INCORRECT_HASH",
                    "ERROR_CORRUPT_FILE", "ERROR_FILE_ACCESS"])
                 .map(function (e) [AddonManager[e], _("AddonManager." + e)])
                 .toObject())
 }, {
 }, {
-    commands: function (dactyl, modules, window) {
-        const { CommandOption, commands, completion } = modules;
+    commands: function initCommands(dactyl, modules, window) {
+        const { CommandOption, commands, completion, io } = modules;
 
         commands.add(["addo[ns]", "ao"],
             "List installed extensions",
@@ -393,9 +388,9 @@ var Addons = Module("addons", {
                 }
 
                 if (!file.exists())
-                    AddonManager.getInstallForURL(url,   install, "application/x-xpinstall");
+                    AddonManager.getInstallForURL(url,        install, "application/x-xpinstall");
                 else if (file.isReadable() && file.isFile())
-                    AddonManager.getInstallForFile(file, install, "application/x-xpinstall");
+                    AddonManager.getInstallForFile(file.file, install, "application/x-xpinstall");
                 else if (file.isDirectory())
                     dactyl.echoerr(_("addon.cantInstallDir", file.path.quote()));
                 else
@@ -412,7 +407,7 @@ var Addons = Module("addons", {
         // TODO: handle extension dependencies
         values(actions).forEach(function (command) {
             let perm = command.perm && AddonManager["PERM_CAN_" + command.perm.toUpperCase()];
-            function ok(addon) !perm || addon.permissions & perm;
+            function ok(addon) (!perm || addon.permissions & perm) && (!command.filter || command.filter(addon));
 
             commands.add(Array.concat(command.name),
                 command.description,
@@ -425,12 +420,12 @@ var Addons = Module("addons", {
 
                     AddonManager.getAddonsByTypes(args["-types"], dactyl.wrapCallback(function (list) {
                         if (!args.bang || command.bang) {
-                            list = list.filter(function (extension) extension.name == name);
-                            if (list.length == 0)
-                                return void dactyl.echoerr(_("error.invalidArgument", name));
-                            if (!list.every(ok))
-                                return void dactyl.echoerr(_("error.invalidOperation"));
+                            list = list.filter(function (addon) addon.id == name || addon.name == name);
+                            dactyl.assert(list.length, _("error.invalidArgument", name));
+                            dactyl.assert(list.some(ok), _("error.invalidOperation"));
+                            list = list.filter(ok);
                         }
+                        dactyl.assert(list.every(ok));
                         if (command.actions)
                             command.actions(list, this.modules);
                         else
@@ -440,10 +435,8 @@ var Addons = Module("addons", {
                     argCount: "?", // FIXME: should be "1"
                     bang: true,
                     completer: function (context, args) {
-                        completion.extension(context, args["-types"]);
+                        completion.addon(context, args["-types"]);
                         context.filters.push(function ({ item }) ok(item));
-                        if (command.filter)
-                            context.filters.push(command.filter);
                     },
                     literal: 0,
                     options: [
@@ -458,7 +451,7 @@ var Addons = Module("addons", {
                 });
         });
     },
-    completion: function (dactyl, modules, window) {
+    completion: function initCompletion(dactyl, modules, window) {
         completion.addonType = function addonType(context) {
             let base = ["extension", "theme"];
             function update(types) {
@@ -478,10 +471,14 @@ var Addons = Module("addons", {
             };
         };
 
-        completion.extension = function extension(context, types) {
-            context.title = ["Extension"];
+        completion.addon = function addon(context, types) {
+            context.title = ["Add-on"];
             context.anchored = false;
-            context.keys = { text: "name", description: "description", icon: "iconURL" },
+            context.keys = {
+                text: function (addon) [addon.name, addon.id],
+                description: "description",
+                icon: "iconURL"
+            };
             context.generate = function () {
                 context.incomplete = true;
                 AddonManager.getAddonsByTypes(types || ["extension"], function (addons) {
@@ -493,130 +490,10 @@ var Addons = Module("addons", {
     }
 });
 
-if (!services.has("extensionManager"))
-    Components.utils.import("resource://gre/modules/AddonManager.jsm");
-else
-    var AddonManager = {
-        PERM_CAN_UNINSTALL: 1,
-        PERM_CAN_ENABLE: 2,
-        PERM_CAN_DISABLE: 4,
-        PERM_CAN_UPGRADE: 8,
-
-        getAddonByID: function (id, callback) {
-            callback = callback || util.identity;
-            addon = services.extensionManager.getItemForID(id);
-            if (addon)
-                addon = this.wrapAddon(addon);
-            return callback(addon);
-        },
-
-        wrapAddon: function wrapAddon(addon) {
-            addon = Object.create(addon.QueryInterface(Ci.nsIUpdateItem));
-
-            ["aboutURL", "creator", "description", "developers",
-             "homepageURL", "installDate", "optionsURL",
-             "releaseNotesURI", "updateDate"].forEach(function (item) {
-                memoize(addon, item, function (item) this.getProperty(item));
-            });
-
-            update(addon, {
-
-                get permissions() 1 | (this.userDisabled ? 2 : 4),
-
-                appDisabled: false,
-
-                getProperty: function getProperty(property) {
-                    let resource = services.rdf.GetResource("urn:mozilla:item:" + this.id);
-
-                    if (resource) {
-                        let target = services.extensionManager.datasource.GetTarget(resource,
-                            services.rdf.GetResource("http://www.mozilla.org/2004/em-rdf#" + property), true);
-
-                        if (target && target instanceof Ci.nsIRDFLiteral)
-                            return target.Value;
-                    }
-
-                    return "";
-                },
-
-                installLocation: Class.memoize(function () services.extensionManager.getInstallLocation(this.id)),
-                getResourceURI: function getResourceURI(path) {
-                    let file = this.installLocation.getItemFile(this.id, path);
-                    return services.io.newFileURI(file);
-                },
-
-                get isActive() this.getProperty("isDisabled") != "true",
-
-                uninstall: function uninstall() {
-                    services.extensionManager.uninstallItem(this.id);
-                },
-
-                get userDisabled() this.getProperty("userDisabled") === "true",
-                set userDisabled(val) {
-                    services.extensionManager[val ? "disableItem" : "enableItem"](this.id);
-                }
-            });
-
-            return addon;
-        },
-
-        getAddonsByTypes: function (types, callback) {
-            let res = [];
-            for (let [, type] in Iterator(types))
-                for (let [, item] in Iterator(services.extensionManager
-                            .getItemList(Ci.nsIUpdateItem["TYPE_" + type.toUpperCase()], {})))
-                    res.push(this.wrapAddon(item));
-
-            if (callback)
-                util.timeout(function () { callback(res); });
-            return res;
-        },
-
-        getInstallForFile: function (file, callback, mimetype) {
-            callback({
-                addListener: function () {},
-                install: function () {
-                    services.extensionManager.installItemFromFile(file, "app-profile");
-                }
-            });
-        },
-
-        getInstallForURL: function (url, callback, mimetype) {
-            util.assert(false, _("error.unavailable", config.host, services.runtime.version));
-        },
-
-        observers: [],
-        addAddonListener: function (listener) {
-            observer.listener = listener;
-            function observer(subject, topic, data) {
-                if (subject instanceof Ci.nsIUpdateItem)
-                    subject = AddonManager.wrapAddon(subject);
-
-                if (data === "item-installed")
-                    listener.onInstalling(subject, true);
-                else if (data === "item-uninstalled")
-                    listener.onUnistalling(subject, true);
-                else if (data === "item-upgraded")
-                    listener.onInstalling(subject, true);
-                else if (data === "item-enabled")
-                    listener.onEnabling(subject, true);
-                else if (data === "item-disabled")
-                    listener.onDisabling(subject, true);
-            }
-            services.observer.addObserver(observer, "em-action-requested", false);
-            this.observers.push(observer);
-        },
-        removeAddonListener: function (listener) {
-            this.observers = this.observers.filter(function (observer) {
-                if (observer.listener !== listener)
-                    return true;
-                services.observer.removeObserver(observer, "em-action-requested");
-            });
-        }
-    };
+Components.utils.import("resource://gre/modules/AddonManager.jsm", this);
 
 endModule();
 
 } catch(e){ if (isString(e)) e = Error(e); dump(e.fileName+":"+e.lineNumber+": "+e+"\n" + e.stack); }
 
-// vim: set fdm=marker sw=4 ts=4 et ft=javascript:
+// vim: set fdm=marker sw=4 sts=4 ts=8 et ft=javascript: