diff options
author | Kris Maglione <maglione.k@gmail.com> | 2011-01-24 03:56:27 -0500 |
---|---|---|
committer | Kris Maglione <maglione.k@gmail.com> | 2011-01-24 03:56:27 -0500 |
commit | fb4aafc21e1057e0614e6c2058dcdf5bbddcf4b2 (patch) | |
tree | aed31e1cdaffc3397cf25e0baded35666d4fe45e /common | |
parent | f23e9321f1f1a73b0456a5fa2e9ee5ba62fd3af8 (diff) | |
download | pentadactyl-fb4aafc21e1057e0614e6c2058dcdf5bbddcf4b2.tar.gz |
Update :extensions with live update and buttons like :downloads. Rename to :addons.
--HG--
extra : transplant_source : %CD%8A%D4L%5D%DD%5E6A%1A%02Gm%22%28%0E3%A6%B4%85
Diffstat (limited to 'common')
-rw-r--r-- | common/content/dactyl.js | 257 | ||||
-rw-r--r-- | common/locale/en-US/gui.xml | 19 | ||||
-rw-r--r-- | common/modules/addons.jsm | 515 | ||||
-rw-r--r-- | common/modules/config.jsm | 17 | ||||
-rw-r--r-- | common/modules/downloads.jsm | 5 | ||||
-rw-r--r-- | common/modules/overlay.jsm | 3 | ||||
-rw-r--r-- | common/modules/services.jsm | 80 |
7 files changed, 538 insertions, 358 deletions
diff --git a/common/content/dactyl.js b/common/content/dactyl.js index a8922422..8c65f810 100644 --- a/common/content/dactyl.js +++ b/common/content/dactyl.js @@ -1527,14 +1527,6 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), { }, commands: function () { - commands.add(["addo[ns]"], - "Manage available Extensions and Themes", - function () { - dactyl.open("chrome://mozapps/content/extensions/extensions.xul", - { from: "addons" }); - }, - { argCount: "0" }); - commands.add(["dia[log]"], "Open a " + config.appName + " dialog", function (args) { @@ -1592,255 +1584,6 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), { literal: 0 }); - /////////////////////////////////////////////////////////////////////////// - - const addonErrors = array.toObject([ - [AddonManager.ERROR_NETWORK_FAILURE, "A network error occurred"], - [AddonManager.ERROR_INCORRECT_HASH, "The downloaded file did not match the expected hash"], - [AddonManager.ERROR_CORRUPT_FILE, "The file appears to be corrupt"], - [AddonManager.ERROR_FILE_ACCESS, "There was an error accessing the filesystem"]]); - - function listener(action, event) - function addonListener(install) { - if (typeof dactyl !== "undefined") - dactyl[install.error ? "echoerr" : "echomsg"]( - "Add-on " + action + " " + event + ": " + (install.name || install.sourceURI.spec) + - (install.error ? ": " + addonErrors[install.error] : "")); - } - const addonListener = { - onNewInstall: function (install) {}, - onExternalInstall: function (addon, existingAddon, needsRestart) {}, - onDownloadStarted: listener("download", "started"), - onDownloadEnded: listener("download", "complete"), - onDownloadCancelled: listener("download", "cancelled"), - onDownloadFailed: listener("download", "failed"), - onDownloadProgress: function (install) {}, - onInstallStarted: function (install) {}, - onInstallEnded: listener("installation", "complete"), - onInstallCancelled: listener("installation", "cancelled"), - onInstallFailed: listener("installation", "failed") - }; - - const updateAddons = Class("UpgradeListener", { - init: function init(addons) { - dactyl.assert(!addons.length || addons[0].findUpdates, - "Not available on " + config.host + " " + services.runtime.version); - this.remaining = addons; - this.upgrade = []; - dactyl.echomsg("Checking updates for addons: " + addons.map(function (a) a.name).join(", ")); - for (let addon in values(addons)) - addon.findUpdates(this, AddonManager.UPDATE_WHEN_USER_REQUESTED, null, null); - }, - addonListener: { - __proto__: addonListener, - onDownloadStarted: function () {}, - onDownloadEnded: function () {} - }, - onUpdateAvailable: function (addon, install) { - this.upgrade.push(addon); - install.addListener(this.addonListener); - install.install(); - }, - onUpdateFinished: function (addon, error) { - this.remaining = this.remaining.filter(function (a) a != addon); - if (!this.remaining.length) - dactyl.echomsg( - this.upgrade.length - ? "Installing updates for addons: " + this.upgrade.map(function (i) i.name).join(", ") - : "No addon updates found"); - } - }); - - /////////////////////////////////////////////////////////////////////////// - - function callResult(method) { - let args = Array.slice(arguments, 1); - return function (result) { result[method].apply(result, args); }; - } - - commands.add(["exta[dd]"], - "Install an extension", - function (args) { - let url = args[0]; - let file = io.File(url); - function install(addonInstall) { - addonInstall.addListener(addonListener); - addonInstall.install(); - } - - if (!file.exists()) - AddonManager.getInstallForURL(url, install, "application/x-xpinstall"); - else if (file.isReadable() && file.isFile()) - AddonManager.getInstallForFile(file, install, "application/x-xpinstall"); - else if (file.isDirectory()) - dactyl.echoerr("Cannot install a directory: " + file.path.quote()); - else - dactyl.echoerr("E484: Can't open file " + file.path); - }, { - argCount: "1", - completer: function (context) { - context.filters.push(function ({ item }) item.isDirectory() || /\.xpi$/.test(item.leafName)); - completion.file(context); - }, - literal: 0 - }); - - // TODO: handle extension dependencies - [ - { - name: "extde[lete]", - description: "Uninstall an extension", - action: callResult("uninstall"), - perm: "uninstall" - }, - { - name: "exte[nable]", - description: "Enable an extension", - action: function (addon) addon.userDisabled = false, - filter: function ({ item }) item.userDisabled, - perm: "enable" - }, - { - name: "extd[isable]", - description: "Disable an extension", - action: function (addon) addon.userDisabled = true, - filter: function ({ item }) !item.userDisabled, - perm: "disable" - }, - { - name: "extr[ehash]", - description: "Reload an extension", - action: function (addon) { - dactyl.assert(dactyl.has("Gecko2"), "This command is not useful in this version of " + config.host); - 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); - }, - perm: "disable" - }, - { - name: "extt[oggle]", - description: "Toggle an extension's enabled status", - action: function (addon) addon.userDisabled = !addon.userDisabled - }, - { - name: "extu[pdate]", - description: "Update an extension", - actions: updateAddons, - perm: "upgrade" - } - ].forEach(function (command) { - let perm = command.perm && AddonManager["PERM_CAN_" + command.perm.toUpperCase()]; - function ok(addon) !perm || addon.permissions & perm; - commands.add([command.name], - command.description, - function (args) { - let name = args[0]; - if (args.bang) - dactyl.assert(!name, "E488: Trailing characters"); - else - dactyl.assert(name, "E471: Argument required"); - - AddonManager.getAddonsByTypes(["extension"], dactyl.wrapCallback(function (list) { - if (!args.bang) { - list = list.filter(function (extension) extension.name == name); - if (list.length == 0) - return void dactyl.echoerr("E475: Invalid argument: " + name); - if (!list.every(ok)) - return void dactyl.echoerr("Permission denied"); - } - if (command.actions) - command.actions(list); - else - list.forEach(command.action); - })); - }, { - argCount: "?", // FIXME: should be "1" - bang: true, - completer: function (context) { - completion.extension(context); - context.filters.push(function ({ item }) ok(item)); - if (command.filter) - context.filters.push(command.filter); - }, - literal: 0 - }); - }); - - commands.add(["exto[ptions]", "extp[references]"], - "Open an extension's preference dialog", - function (args) { - AddonManager.getAddonsByTypes(["extension"], dactyl.wrapCallback(function (list) { - list = list.filter(function (extension) extension.name == args[0]); - if (!list.length || !list[0].optionsURL) - dactyl.echoerr("E474: Invalid argument"); - else if (args.bang) - window.openDialog(list[0].optionsURL, "_blank", "chrome"); - else - dactyl.open(list[0].optionsURL, { from: "extoptions" }); - })); - }, { - argCount: "1", - bang: true, - completer: function (context) { - completion.extension(context); - context.filters.push(function ({ item }) item.isActive && item.optionsURL); - }, - literal: 0 - }); - - commands.add(["extens[ions]", "exts"], - "List available extensions", - function (args) { - function addonExtra(e) { - let extra; - if (e.pendingOperations & AddonManager.PENDING_UNINSTALL) - extra = ["Disabled", "uninstalled"]; - else if (e.pendingOperations & AddonManager.PENDING_DISABLE) - extra = ["Disabled", "disabled"]; - else if (e.pendingOperations & AddonManager.PENDING_INSTALL) - extra = ["Enabled", "installed"]; - else if (e.pendingOperations & AddonManager.PENDING_ENABLE) - extra = ["Enabled", "enabled"]; - else if (e.pendingOperations & AddonManager.PENDING_UPGRADE) - extra = ["Enabled", "upgraded"]; - if (extra) - return <> (<span highlight={extra[0]}>{extra[1]}</span> -  on restart)</>; - return <></>; - } - let waiting = true; - AddonManager.getAddonsByTypes(["extension"], function (extensions) { - if (args[0]) - extensions = extensions.filter(function (extension) extension.name.indexOf(args[0]) >= 0); - extensions.sort(function (a, b) String.localeCompare(a.name, b.name)); - - if (extensions.length > 0) - commandline.commandOutput( - template.tabular(["Name", "Version", "Status", "Description"], [], - ([template.icon({ icon: e.iconURL }, e.name), - e.version, - (e.isActive ? <span highlight="Enabled">enabled</span> - : <span highlight="Disabled">disabled</span>) + - addonExtra(e), - e.description] - for ([, e] in Iterator(extensions))))); - else if (filter) - dactyl.echoerr("Exxx: No extension matching " + filter.quote()); - else - dactyl.echoerr("No extensions installed"); - waiting = false; - }); - if (commandline.savingOutput) - util.waitFor(function () !waiting); - }, - { argCount: "?" }); - [ { name: "h[elp]", diff --git a/common/locale/en-US/gui.xml b/common/locale/en-US/gui.xml index fa1daf5d..e7054858 100644 --- a/common/locale/en-US/gui.xml +++ b/common/locale/en-US/gui.xml @@ -41,16 +41,11 @@ <h2 tag="dialogs">Dialogs</h2> <item> - <tags>:addo :addons</tags> + <tags>:ao :addo :addons</tags> <strut/> <spec>:addo<oa>ns</oa></spec> <description> - <p> - Opens the &dactyl.host; addon manager, where extensions and themes - may be installed, removed, disabled, and configured. See also - <ex>:extensions</ex>, <ex>:extadd</ex>, <ex>:extoptions</ex>, - <ex>:extenable</ex>, <ex>:extdisable</ex>, and <ex>:extdelete</ex>. - </p> + <p>Opens the add-on list.</p> </description> </item> @@ -130,16 +125,6 @@ </item> <item> - <tags>:exts :extens :extensions</tags> - <spec>:extens<oa>ions</oa></spec> - <spec>:exts</spec> - <strut/> - <description> - <p>List all installed extensions.</p> - </description> -</item> - -<item> <tags>:exto :extoptions</tags> <spec>:exto<oa>ptions</oa><oa>!</oa> <a>extension</a></spec> <tags>:extp :extpreferences</tags> diff --git a/common/modules/addons.jsm b/common/modules/addons.jsm new file mode 100644 index 00000000..4964d938 --- /dev/null +++ b/common/modules/addons.jsm @@ -0,0 +1,515 @@ +// Copyright (c) 2011 by Kris Maglione <maglione.k@gmail.com> +// +// This work is licensed for reuse under an MIT license. Details are +// given in the LICENSE.txt file included with this file. +"use strict"; + +try { + +Components.utils.import("resource://dactyl/bootstrap.jsm"); +defineModule("addons", { + exports: ["AddonManager", "Addons", "Addon", "addons"], + require: ["services"], + use: ["config", "io", "prefs", "template", "util"] +}, this); + +var callResult = function callResult(method) { + let args = Array.slice(arguments, 1); + return function (result) { result[method].apply(result, args); }; +} + +var listener = function listener(action, event) + function addonListener(install) { + this.dactyl[install.error ? "echoerr" : "echomsg"]( + "Add-on " + action + " " + event + ": " + (install.name || install.sourceURI.spec) + + (install.error ? ": " + addonErrors[install.error] : "")); + } + +var AddonListener = Class("AddonListener", { + init: function init(modules) { + this.dactyl = modules.dactyl; + }, + + onNewInstall: function (install) {}, + onExternalInstall: function (addon, existingAddon, needsRestart) {}, + onDownloadStarted: listener("download", "started"), + onDownloadEnded: listener("download", "complete"), + onDownloadCancelled: listener("download", "cancelled"), + onDownloadFailed: listener("download", "failed"), + onDownloadProgress: function (install) {}, + onInstallStarted: function (install) {}, + onInstallEnded: listener("installation", "complete"), + onInstallCancelled: listener("installation", "cancelled"), + onInstallFailed: listener("installation", "failed") +}); + +var updateAddons = Class("UpgradeListener", AddonListener, { + init: function init(addons, modules) { + init.supercall(this, modules); + + util.assert(!addons.length || addons[0].findUpdates, + "Not available on " + config.host + " " + services.runtime.version); + + this.remaining = addons; + this.upgrade = []; + this.dactyl.echomsg("Checking updates for addons: " + addons.map(function (a) a.name).join(", ")); + for (let addon in values(addons)) + addon.findUpdates(this, AddonManager.UPDATE_WHEN_USER_REQUESTED, null, null); + + }, + onUpdateAvailable: function (addon, install) { + this.upgrade.push(addon); + install.addListener(this); + install.install(); + }, + onUpdateFinished: function (addon, error) { + this.remaining = this.remaining.filter(function (a) a != addon); + if (!this.remaining.length) + this.dactyl.echomsg( + this.upgrade.length + ? "Installing updates for addons: " + this.upgrade.map(function (i) i.name).join(", ") + : "No addon updates found"); + } +}); + +var actions = { + delete: { + name: "extde[lete]", + description: "Uninstall an extension", + action: callResult("uninstall"), + perm: "uninstall" + }, + enable: { + name: "exte[nable]", + description: "Enable an extension", + action: function (addon) addon.userDisabled = false, + filter: function ({ item }) item.userDisabled, + perm: "enable" + }, + disable: { + name: "extd[isable]", + description: "Disable an extension", + action: function (addon) addon.userDisabled = true, + filter: function ({ item }) !item.userDisabled, + perm: "disable" + }, + rehash: { + name: "extr[ehash]", + description: "Reload an extension", + action: function (addon) { + dactyl.assert(dactyl.has("Gecko2"), "This command is not useful in this version of " + config.host); + 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); + }, + perm: "disable" + }, + toggle: { + name: "extt[oggle]", + description: "Toggle an extension's enabled status", + action: function (addon) addon.userDisabled = !addon.userDisabled + }, + update: { + name: "extu[pdate]", + description: "Update an extension", + actions: updateAddons, + perm: "upgrade" + } +}; + + +var Addon = Class("Addon", { + init: function init(addon, list) { + this.addon = addon; + this.instance = this; + this.list = list; + + this.nodes = { + commandTarget: this + }; + util.xmlToDom( + <li highlight="Addon" key="row" xmlns:dactyl={NS} xmlns={XHTML}> + <span highlight="AddonName" key="name"> + </span> + <span highlight="AddonVersion" key="version"/> + <span highlight="AddonStatus" key="status"/> + <span highlight="AddonButtons Buttons"> + <a highlight="Button" key="enable">On </a> + <a highlight="Button" key="disable">Off</a> + <a highlight="Button" key="delete">Del</a> + <a highlight="Button" key="update">Upd</a> + </span> + <span highlight="AddonDescription" key="description"/> + </li>, + this.list.document, this.nodes); + + this.update(); + }, + + commandAllowed: function commandAllowed(cmd) { + util.assert(set.has(actions, cmd), "Unknown command"); + + 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 })) + return false; + return true; + }, + + command: function command(cmd) { + util.assert(this.commandAllowed(cmd), "Command not allowed"); + + let action = actions[cmd]; + if (action.action) + action.action.call(this.modules, this); + else + action.actions([this], this.list.modules); + }, + + compare: function compare(other) String.localeCompare(this.name, other.name), + + get statusInfo() { + let info = this.isActive ? <span highlight="Enabled">enabled</span> + : <span highlight="Disabled">disabled</span>; + + let pending; + if (this.pendingOperations & AddonManager.PENDING_UNINSTALL) + pending = ["Disabled", "uninstalled"]; + else if (this.pendingOperations & AddonManager.PENDING_DISABLE) + pending = ["Disabled", "disabled"]; + else if (this.pendingOperations & AddonManager.PENDING_INSTALL) + pending = ["Enabled", "installed"]; + else if (this.pendingOperations & AddonManager.PENDING_ENABLE) + pending = ["Enabled", "enabled"]; + else if (this.pendingOperations & AddonManager.PENDING_UPGRADE) + pending = ["Enabled", "upgraded"]; + if (pending) + return <>{info} (<span highlight={pending[0]}>{pending[1]}</span> +  on restart)</>; + return info; + }, + + update: function callee() { + let self = this; + function update(key, xml) { + let node = self.nodes[key]; + while (node.firstChild) + node.removeChild(node.firstChild); + node.appendChild(util.xmlToDom(<>{xml}</>, self.list.document)); + } + + 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; + + for (let node in values(this.nodes)) + if (node.update && node.update !== callee) + node.update(); + } +}); + +["cancelUninstall", "findUpdates", "getResourceURI", "hasResource", + "isCompatibleWith", "uninstall"].forEach(function (prop) { + Addon.prototype[prop] = function proxy() this.addon[prop].apply(this.addon, arguments); +}); + +["aboutURL", "appDisabled", "applyBackgroundUpdates", "blocklistState", + "contributors", "creator", "description", "developers", "homepageURL", + "iconURL", "id", "install", "installDate", "isActive", "isCompatible", + "isPlatformCompatible", "name", "operationsRequiringRestart", + "optionsURL", "pendingOperations", "pendingUpgrade", "permissions", + "providesUpdatesSecurely", "releaseNotesURI", "scope", "screenshots", + "size", "sourceURI", "translators", "type", "updateDate", + "userDisabled", "version"].forEach(function (prop) { + Object.defineProperty(Addon.prototype, prop, { + get: function get_proxy() this.addon[prop], + set: function set_proxy(val) this.addon[prop] = val + }); +}); + +var AddonList = Class("AddonList", { + init: function init(modules, types) { + this.modules = modules; + this.nodes = {}; + this.addons = []; + this.ready = false; + + AddonManager.getAddonsByTypes(types, this.closure(function (addons) { + addons.forEach(this.closure.addAddon); + this.ready = true; + this.update(); + })); + AddonManager.addAddonListener(this); + }, + cleanup: function cleanup() { + AddonManager.removeAddonListener(this); + }, + + message: Class.memoize(function () { + + util.xmlToDom(<ul highlight="Addons" key="list" xmlns={XHTML}> + <li highlight="AddonHead"> + <span>Name</span> + <span>Version</span> + <span>Status</span> + <span/> + <span>Description</span> + </li> + </ul>, this.document, this.nodes); + + return this.nodes.list; + }), + + addAddon: function addAddon(addon) { + if (addon.id in this.addons) + this.update(addon); + else { + addon = Addon(addon, this); + this.addons[addon.id] = addon; + + let index = values(this.addons).sort(function (a, b) a.compare(b)) + .indexOf(addon); + + this.nodes.list.insertBefore(addon.nodes.row, + this.nodes.list.childNodes[index + 1]); + this.update(); + } + }, + removeAddon: function removeAddon(addon) { + if (addon.id in this.addons) { + this.nodes.list.removeChild(this.addons[addon.id].nodes.row); + delete this.addons[addon.id]; + this.update(); + } + }, + + leave: function leave(stack) { + if (stack.pop) + this.cleanup(); + }, + + update: function update(addon) { + if (addon && addon.id in this.addons) + this.addons[addon.id].update(); + if (this.ready) + this.modules.commandline.updateOutputHeight(false); + }, + + onDisabled: function (addon) { this.update(addon); }, + onDisabling: function (addon) { this.update(addon); }, + onEnabled: function (addon) { this.update(addon); }, + onEnabling: function (addon) { this.update(addon); }, + onInstalled: function (addon) { this.addAddon(addon); }, + onInstalling: function (addon) { this.update(addon); }, + onUninstalled: function (addon) { this.removeAddon(addon); }, + onUninstalling: function (addon) { this.update(addon); }, + onOperationCancelled: function (addon) { this.update(addon); }, + onPropertyChanged: function onPropertyChanged(addon, properties) {} +}); + +var Addons = Module("addons", { +}, { +}, { + commands: function (dactyl, modules, window) { + const { commands, completion } = modules; + + let addonListener = AddonListener(modules); + + commands.add(["exta[dd]"], + "Install an extension", + function (args) { + let url = args[0]; + let file = io.File(url); + function install(addonInstall) { + addonInstall.addListener(addonListener); + addonInstall.install(); + } + + if (!file.exists()) + AddonManager.getInstallForURL(url, install, "application/x-xpinstall"); + else if (file.isReadable() && file.isFile()) + AddonManager.getInstallForFile(file, install, "application/x-xpinstall"); + else if (file.isDirectory()) + dactyl.echoerr("Cannot install a directory: " + file.path.quote()); + else + dactyl.echoerr("E484: Can't open file " + file.path); + }, { + argCount: "1", + completer: function (context) { + context.filters.push(function ({ item }) item.isDirectory() || /\.xpi$/.test(item.leafName)); + completion.file(context); + }, + literal: 0 + }); + + // 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; + + commands.add([command.name], + command.description, + function (args) { + let name = args[0]; + if (args.bang) + dactyl.assert(!name, "E488: Trailing characters"); + else + dactyl.assert(name, "E471: Argument required"); + + AddonManager.getAddonsByTypes(["extension"], dactyl.wrapCallback(function (list) { + if (!args.bang) { + list = list.filter(function (extension) extension.name == name); + if (list.length == 0) + return void dactyl.echoerr("E475: Invalid argument: " + name); + if (!list.every(ok)) + return void dactyl.echoerr("Permission denied"); + } + if (command.actions) + command.actions(list, this.modules); + else + list.forEach(command.action, this.modules); + })); + }, { + argCount: "?", // FIXME: should be "1" + bang: true, + completer: function (context) { + completion.extension(context); + context.filters.push(function ({ item }) ok(item)); + if (command.filter) + context.filters.push(command.filter); + }, + literal: 0 + }); + }); + + commands.add(["exto[ptions]", "extp[references]"], + "Open an extension's preference dialog", + function (args) { + AddonManager.getAddonsByTypes(["extension"], dactyl.wrapCallback(function (list) { + list = list.filter(function (extension) extension.name == args[0]); + if (!list.length || !list[0].optionsURL) + dactyl.echoerr("E474: Invalid argument"); + else if (args.bang) + window.openDialog(list[0].optionsURL, "_blank", "chrome"); + else + dactyl.open(list[0].optionsURL, { from: "extoptions" }); + })); + }, { + argCount: "1", + bang: true, + completer: function (context) { + completion.extension(context); + context.filters.push(function ({ item }) item.isActive && item.optionsURL); + }, + literal: 0 + }); + + commands.add(["addo[ns]", "ao"], + "List installed extensions", + function (args) { + let addons = AddonList(modules, ["extension"]); + modules.commandline.echo(addons); + + if (modules.commandline.savingOutput) + util.waitFor(function () addons.ready); + }); + } +}); + +if (!Ci.nsIExtensionManager || !services.extensionManager) + Components.utils.import("resource://gre/modules/AddonManager.jsm"); +else + var AddonManager = { + getAddonByID: function (id, callback) { + callback = callback || util.identity; + let addon = id; + if (!isObject(addon)) + addon = services.extensionManager.getItemForID(id); + if (!addon) + return callback(null); + addon = Object.create(addon); + + function getRdfProperty(item, property) { + let resource = services.rdf.GetResource("urn:mozilla:item:" + item.id); + let value = ""; + + 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) + value = target.Value; + } + + return value; + } + + ["aboutURL", "creator", "description", "developers", + "homepageURL", "installDate", "optionsURL", + "releaseNotesURI", "updateDate"].forEach(function (item) { + memoize(addon, item, function (item) getRdfProperty(this, item)); + }); + + update(addon, { + + appDisabled: false, + + 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); + }, + + isActive: getRdfProperty(addon, "isDisabled") != "true", + + uninstall: function uninstall() { + services.extensionManager.uninstallItem(this.id); + }, + + get userDisabled() getRdfProperty(addon, "userDisabled") === "true", + set userDisabled(val) { + services.extensionManager[val ? "disableItem" : "enableItem"](this.id); + } + }); + + return callback(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.getAddonByID(item)); + return (callback || util.identity)(res); + }, + getInstallForFile: function (file, callback, mimetype) { + callback({ + addListener: function () {}, + install: function () { + services.extensionManager.installItemFromFile(file, "app-profile"); + } + }); + }, + getInstallForURL: function (url, callback, mimetype) { + dactyl.assert(false, "Install by URL not implemented"); + }, + }; + +var addonErrors = array.toObject([ + [AddonManager.ERROR_NETWORK_FAILURE, "A network error occurred"], + [AddonManager.ERROR_INCORRECT_HASH, "The downloaded file did not match the expected hash"], + [AddonManager.ERROR_CORRUPT_FILE, "The file appears to be corrupt"], + [AddonManager.ERROR_FILE_ACCESS, "There was an error accessing the filesystem"]]); + + +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: diff --git a/common/modules/config.jsm b/common/modules/config.jsm index c0ff190b..9b13b7d7 100644 --- a/common/modules/config.jsm +++ b/common/modules/config.jsm @@ -506,6 +506,8 @@ var ConfigBase = Class("ConfigBase", { Download display: table-row; Download:not([active]) color: gray; + Buttons + DownloadCell display: table-cell; padding: 0 1ex; DownloadButtons;;;DownloadCell DownloadPercent;;;DownloadCell @@ -517,6 +519,21 @@ var ConfigBase = Class("ConfigBase", { DownloadTime;;;DownloadCell DownloadTitle;;;DownloadCell,URL + AddonCell display: table-cell; padding: 0 1ex; + + Addons display: table; margin: 0; padding: 0; + AddonHead;;;CompTitle display: table-row; + AddonHead>*;;;AddonCell + + Addon display: table-row; + + Addon>*;;;AddonCell + AddonButtons + AddonDescription + AddonName + AddonStatus + AddonVersion + // </css> ]]></>, /
/g, "\n")), diff --git a/common/modules/downloads.jsm b/common/modules/downloads.jsm index aff156fe..138efaac 100644 --- a/common/modules/downloads.jsm +++ b/common/modules/downloads.jsm @@ -36,7 +36,7 @@ var Download = Class("Download", { </span> </span> <span highlight="DownloadState" key="state"/> - <span highlight="DownloadButtons"> + <span highlight="DownloadButtons Buttons"> <a highlight="Button" key="pause">Pause</a> <a highlight="Button" key="remove">Remove</a> <a highlight="Button" key="resume">Resume</a> @@ -285,9 +285,6 @@ var Downloads = Module("downloads", { function (args) { let downloads = DownloadList(modules); modules.commandline.echo(downloads); - - if (modules.commandline.savingOutput) - util.waitFor(function () downloads.document); }); }, dactyl: function (dactyl, modules, window) { diff --git a/common/modules/overlay.jsm b/common/modules/overlay.jsm index 391553c8..72eb99a9 100644 --- a/common/modules/overlay.jsm +++ b/common/modules/overlay.jsm @@ -148,7 +148,8 @@ var Overlay = Module("Overlay", { let prefix = [BASE, "resource://dactyl-local-content/"]; defineModule.time("load", null, function _load() { - ["base", + ["addons", + "base", "completion", "config", "downloads", diff --git a/common/modules/services.jsm b/common/modules/services.jsm index 71e002e1..48629933 100644 --- a/common/modules/services.jsm +++ b/common/modules/services.jsm @@ -9,7 +9,7 @@ try { var global = this; Components.utils.import("resource://dactyl/bootstrap.jsm"); defineModule("services", { - exports: ["AddonManager", "services"], + exports: ["services"], use: ["util"] }, this); @@ -86,84 +86,6 @@ var Services = Module("Services", { this.addClass("Xmlhttp", "@mozilla.org/xmlextras/xmlhttprequest;1", Ci.nsIXMLHttpRequest); this.addClass("ZipReader", "@mozilla.org/libjar/zip-reader;1", Ci.nsIZipReader, "open"); this.addClass("ZipWriter", "@mozilla.org/zipwriter;1", Ci.nsIZipWriter); - - if (!Ci.nsIExtensionManager || !this.extensionManager) - Components.utils.import("resource://gre/modules/AddonManager.jsm"); - else - global.AddonManager = { - getAddonByID: function (id, callback) { - callback = callback || util.identity; - let addon = id; - if (!isObject(addon)) - addon = services.extensionManager.getItemForID(id); - if (!addon) - return callback(null); - addon = Object.create(addon); - - function getRdfProperty(item, property) { - let resource = services.rdf.GetResource("urn:mozilla:item:" + item.id); - let value = ""; - - 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) - value = target.Value; - } - - return value; - } - - ["aboutURL", "creator", "description", "developers", - "homepageURL", "installDate", "optionsURL", - "releaseNotesURI", "updateDate"].forEach(function (item) { - memoize(addon, item, function (item) getRdfProperty(this, item)); - }); - - update(addon, { - - appDisabled: false, - - 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); - }, - - isActive: getRdfProperty(addon, "isDisabled") != "true", - - uninstall: function uninstall() { - services.extensionManager.uninstallItem(this.id); - }, - - get userDisabled() getRdfProperty(addon, "userDisabled") === "true", - set userDisabled(val) { - services.extensionManager[val ? "disableItem" : "enableItem"](this.id); - } - }); - - return callback(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.getAddonByID(item)); - return (callback || util.identity)(res); - }, - getInstallForFile: function (file, callback, mimetype) { - callback({ - addListener: function () {}, - install: function () { - services.extensionManager.installItemFromFile(file, "app-profile"); - } - }); - }, - getInstallForURL: function (url, callback, mimetype) { - dactyl.assert(false, "Install by URL not implemented"); - }, - }; }, reinit: function () {}, |