new file mode 100644
--- /dev/null
+++ b/toolkit/mozapps/extensions/AddonManager.jsm
@@ -0,0 +1,877 @@
+/*
+# ***** BEGIN LICENSE BLOCK *****
+# Version: MPL 1.1/GPL 2.0/LGPL 2.1
+#
+# The contents of this file are subject to the Mozilla Public License Version
+# 1.1 (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+# http://www.mozilla.org/MPL/
+#
+# Software distributed under the License is distributed on an "AS IS" basis,
+# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+# for the specific language governing rights and limitations under the
+# License.
+#
+# The Original Code is the Extension Manager.
+#
+# The Initial Developer of the Original Code is
+# the Mozilla Foundation.
+# Portions created by the Initial Developer are Copyright (C) 2009
+# the Initial Developer. All Rights Reserved.
+#
+# Contributor(s):
+# Dave Townsend <dtownsend@oxymoronical.com>
+#
+# Alternatively, the contents of this file may be used under the terms of
+# either the GNU General Public License Version 2 or later (the "GPL"), or
+# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+# in which case the provisions of the GPL or the LGPL are applicable instead
+# of those above. If you wish to allow use of your version of this file only
+# under the terms of either the GPL or the LGPL, and not to allow others to
+# use your version of this file under the terms of the MPL, indicate your
+# decision by deleting the provisions above and replace them with the notice
+# and other provisions required by the GPL or the LGPL. If you do not delete
+# the provisions above, a recipient may use your version of this file under
+# the terms of any one of the MPL, the GPL or the LGPL.
+#
+# ***** END LICENSE BLOCK *****
+*/
+
+const Cc = Components.classes;
+const Ci = Components.interfaces;
+const Cr = Components.results;
+
+const PREF_EM_UPDATE_ENABLED = "extensions.update.enabled";
+
+Components.utils.import("resource://gre/modules/Services.jsm");
+
+var EXPORTED_SYMBOLS = [ "AddonManager", "AddonManagerPrivate" ];
+
+// A list of providers to load by default
+const PROVIDERS = [
+ "resource://gre/modules/XPIProvider.jsm",
+ "resource://gre/modules/LightweightThemeManager.jsm"
+];
+
+/**
+ * Logs a debugging message.
+ *
+ * @param str
+ * The string to log
+ */
+function LOG(str) {
+ dump("*** addons.manager: " + str + "\n");
+}
+
+/**
+ * Logs a warning message.
+ *
+ * @param str
+ * The string to log
+ */
+function WARN(str) {
+ LOG(str);
+}
+
+/**
+ * Logs an error message.
+ *
+ * @param str
+ * The string to log
+ */
+function ERROR(str) {
+ LOG(str);
+}
+
+/**
+ * Calls a callback method consuming any thrown exception. Any parameters after
+ * the callback parameter will be passed to the callback.
+ *
+ * @param callback
+ * The callback method to call
+ */
+function safeCall(callback) {
+ var args = Array.slice(arguments, 1);
+
+ try {
+ callback.apply(null, args);
+ }
+ catch (e) {
+ WARN("Exception calling callback: " + e);
+ }
+}
+
+/**
+ * Calls a method on a provider if it exists and consumes any thrown exception.
+ * Any parameters after the dflt parameter are passed to the provider's method.
+ *
+ * @param provider
+ * The provider to call
+ * @param method
+ * The method name to call
+ * @param dflt
+ * A default return value if the provider does not implement the named
+ * method or throws an error.
+ * @return the return value from the provider or dflt if the provider does not
+ * implement method or throws an error
+ */
+function callProvider(provider, method, dflt) {
+ if (!(method in provider))
+ return dflt;
+
+ var args = Array.slice(arguments, 3);
+
+ try {
+ return provider[method].apply(provider, args);
+ }
+ catch (e) {
+ ERROR("Exception calling provider." + method + ": " + e);
+ return dflt;
+ }
+}
+
+/**
+ * A helper class to repeatedly call a listener with each object in an array
+ * optionally checking whether the object has a method in it.
+ *
+ * @param objects
+ * The array of objects to iterate through
+ * @param method
+ * An optional method name, if not null any objects without this method
+ * will not be passed to the listener
+ * @param listener
+ * A listener implementing nextObject and noMoreObjects methods. The
+ * former will be called with the AsyncObjectCaller as the first
+ * parameter and the object as the second. noMoreObjects will be passed
+ * just the AsyncObjectCaller
+ */
+function AsyncObjectCaller(objects, method, listener) {
+ this.objects = objects.slice(0);
+ this.method = method;
+ this.listener = listener;
+
+ this.callNext();
+}
+
+AsyncObjectCaller.prototype = {
+ objects: null,
+ method: null,
+ listener: null,
+
+ /**
+ * Passes the next object to the listener or calls noMoreObjects if there
+ * are none left.
+ */
+ callNext: function AOC_callNext() {
+ if (this.objects.length == 0) {
+ this.listener.noMoreObjects(this);
+ return;
+ }
+
+ let object = this.objects.shift();
+ if (!this.method || this.method in object)
+ this.listener.nextObject(this, object);
+ else
+ this.callNext();
+ }
+};
+
+/**
+ * This is the real manager, kept here rather than in AddonManager to keep its
+ * contents hidden from API users.
+ */
+var AddonManagerInternal = {
+ installListeners: null,
+ addonListeners: null,
+ providers: [],
+ started: false,
+
+ /**
+ * Initializes the AddonManager, loading any known providers and initializing
+ * them.
+ */
+ startup: function AMI_startup() {
+ if (this.started)
+ return;
+
+ this.installListeners = [];
+ this.addonListeners = [];
+
+ let appChanged = true;
+
+ try {
+ appChanged = Services.appinfo.version !=
+ Services.prefs.getCharPref("extensions.lastAppVersion");
+ }
+ catch (e) { }
+
+ if (appChanged) {
+ LOG("Application has been upgraded");
+ Services.prefs.setCharPref("extensions.lastAppVersion",
+ Services.appinfo.version);
+ }
+
+ // Ensure all default providers have had a chance to register themselves
+ PROVIDERS.forEach(function(url) {
+ Components.utils.import(url, {});
+ });
+
+ let needsRestart = false;
+ this.providers.forEach(function(provider) {
+ callProvider(provider, "startup");
+ if (callProvider(provider, "checkForChanges", false, appChanged))
+ needsRestart = true;
+ });
+ this.started = true;
+
+ // Flag to the platform that a restart is necessary
+ if (needsRestart) {
+ let appStartup = Cc["@mozilla.org/toolkit/app-startup;1"].
+ getService(Ci.nsIAppStartup2);
+ appStartup.needsRestart = needsRestart;
+ }
+ },
+
+ /**
+ * Registers a new AddonProvider.
+ *
+ * @param provider
+ * The provider to register
+ */
+ registerProvider: function AMI_registerProvider(provider) {
+ this.providers.push(provider);
+
+ // If we're registering after startup call this provider's startup.
+ if (this.started)
+ callProvider(provider, "startup");
+ },
+
+ /**
+ * Shuts down the addon manager and all registered providers, this must clean
+ * up everything in order for automated tests to fake restarts.
+ */
+ shutdown: function AM_shutdown() {
+ this.providers.forEach(function(provider) {
+ callProvider(provider, "shutdown");
+ });
+
+ this.installListeners = null;
+ this.addonListeners = null;
+ this.started = false;
+ },
+
+ /**
+ * Performs a background update check by starting an update for all add-ons
+ * that can be updated.
+ */
+ backgroundUpdateCheck: function AMI_backgroundUpdateCheck() {
+ if (!Services.prefs.getBoolPref(PREF_EM_UPDATE_ENABLED))
+ return;
+
+ this.getAddonsByTypes(null, function getAddonsCallback(addons) {
+ addons.forEach(function BUC_forEachCallback(addon) {
+ if (addon.permissions & AddonManager.PERM_CAN_UPGRADE) {
+ addon.findUpdates({
+ onUpdateAvailable: function BUC_onUpdateAvailable(addon, install) {
+ install.install();
+ }
+ }, AddonManager.UPDATE_WHEN_PERIODIC_UPDATE);
+ }
+ });
+ });
+ },
+
+ /**
+ * Calls all registered InstallListeners with an event. Any parameters after
+ * the extraListeners parameter are passed to the listener.
+ *
+ * @param method
+ * The method on the listeners to call
+ * @param extraListeners
+ * An array of extra InstallListeners to also call
+ * @return false if any of the listeners returned false, true otherwise
+ */
+ callInstallListeners: function AMI_callInstallListeners(method, extraListeners) {
+ let result = true;
+ let listeners = this.installListeners;
+ if (extraListeners)
+ listeners = extraListeners.concat(listeners);
+ let args = Array.slice(arguments, 2);
+
+ listeners.forEach(function(listener) {
+ try {
+ if (method in listener) {
+ if (listener[method].apply(listener, args) === false)
+ result = false;
+ }
+ }
+ catch (e) {
+ WARN("InstallListener threw exception when calling " + method + ": " + e);
+ }
+ });
+ return result;
+ },
+
+ /**
+ * Calls all registered AddonListeners with an event. Any parameters after
+ * the method parameter are passed to the listener.
+ *
+ * @param method
+ * The method on the listeners to call
+ */
+ callAddonListeners: function AMI_callAddonListeners(method) {
+ var args = Array.slice(arguments, 1);
+ this.addonListeners.forEach(function(listener) {
+ try {
+ if (method in listener)
+ listener[method].apply(listener, args);
+ }
+ catch (e) {
+ WARN("AddonListener threw exception when calling " + method + ": " + e);
+ }
+ });
+ },
+
+ /**
+ * Notifies all providers that an add-on has been enabled when that type of
+ * add-on only supports a single add-on being enabled at a time. This allows
+ * the providers to disable theirs if necessary.
+ *
+ * @param id
+ * The id of the enabled add-on
+ * @param type
+ * The type of the enabled add-on
+ * @param pendingRestart
+ * A boolean indicating if the change will only take place the next
+ * time the application is restarted
+ */
+ notifyAddonChanged: function AMI_notifyAddonChanged(id, type, pendingRestart) {
+ this.providers.forEach(function(provider) {
+ callProvider(provider, "addonChanged", null, id, type, pendingRestart);
+ });
+ },
+
+ /**
+ * Asynchronously gets an AddonInstall for a URL.
+ *
+ * @param url
+ * The url the add-on is located at
+ * @param callback
+ * A callback to pass the AddonInstall to
+ * @param mimetype
+ * The mimetype of the add-on
+ * @param hash
+ * An optional hash of the add-on
+ * @param name
+ * An optional placeholder name while the add-on is being downloaded
+ * @param iconURL
+ * An optional placeholder icon URL while the add-on is being downloaded
+ * @param version
+ * An optional placeholder version while the add-on is being downloaded
+ * @param loadgroup
+ * An optional nsILoadGroup to associate any network requests with
+ * @throws if the url, callback or mimetype arguments are not specified
+ */
+ getInstallForURL: function AMI_getInstallForURL(url, callback, mimetype, hash,
+ name, iconURL, version,
+ loadgroup) {
+ if (!url || !mimetype || !callback)
+ throw new TypeError("Invalid arguments");
+
+ for (let i = 0; i < this.providers.length; i++) {
+ if (callProvider(this.providers[i], "supportsMimetype", false, mimetype)) {
+ callProvider(this.providers[i], "getInstallForURL", null,
+ url, hash, name, iconURL, version, loadgroup,
+ function(install) {
+ safeCall(callback, install);
+ });
+ return;
+ }
+ }
+ safeCall(callback, null);
+ },
+
+ /**
+ * Asynchronously gets an AddonInstall for an nsIFile.
+ *
+ * @param file
+ * the nsIFile where the add-on is located
+ * @param callback
+ * A callback to pass the AddonInstall to
+ * @param mimetype
+ * An optional mimetype hint for the add-on
+ * @throws if the file or callback arguments are not specified
+ */
+ getInstallForFile: function AMI_getInstallForFile(file, callback, mimetype) {
+ if (!file || !callback)
+ throw Cr.NS_ERROR_INVALID_ARG;
+
+ new AsyncObjectCaller(this.providers, "getInstallForFile", {
+ nextObject: function(caller, provider) {
+ callProvider(provider, "getInstallForFile", null, file,
+ function(install) {
+ if (install)
+ safeCall(callback, install);
+ else
+ caller.callNext();
+ });
+ },
+
+ noMoreObjects: function(caller) {
+ safeCall(callback, null);
+ }
+ });
+ },
+
+ /**
+ * Asynchronously gets all current AddonInstalls optionally limiting to a list
+ * of types.
+ *
+ * @param types
+ * An optional array of types to retrieve. Each type is a string name
+ * @param callback
+ * A callback which will be passed an array of AddonInstalls
+ * @throws if the callback argument is not specified
+ */
+ getInstalls: function AMI_getInstalls(types, callback) {
+ if (!callback)
+ throw Cr.NS_ERROR_INVALID_ARG;
+
+ let installs = [];
+
+ new AsyncObjectCaller(this.providers, "getInstalls", {
+ nextObject: function(caller, provider) {
+ callProvider(provider, "getInstalls", null, types,
+ function(providerInstalls) {
+ installs = installs.concat(providerInstalls);
+ caller.callNext();
+ });
+ },
+
+ noMoreObjects: function(caller) {
+ safeCall(callback, installs);
+ }
+ });
+ },
+
+ /**
+ * Checks whether installation is enabled for a particular mimetype.
+ *
+ * @param mimetype
+ * The mimetype to check
+ * @return true if installation is enabled for the mimetype
+ */
+ isInstallEnabled: function AMI_isInstallEnabled(mimetype) {
+ for (let i = 0; i < this.providers.length; i++) {
+ if (callProvider(this.providers[i], "supportsMimetype", false, mimetype) &&
+ callProvider(this.providers[i], "isInstallEnabled"))
+ return true;
+ }
+ return false;
+ },
+
+ /**
+ * Checks whether a particular source is allowed to install add-ons of a
+ * given mimetype.
+ *
+ * @param mimetype
+ * The mimetype of the add-on
+ * @param uri
+ * The uri of the source, may be null
+ * @return true if the source is allowed to install this mimetype
+ */
+ isInstallAllowed: function AMI_isInstallAllowed(mimetype, uri) {
+ for (let i = 0; i < this.providers.length; i++) {
+ if (callProvider(this.providers[i], "supportsMimetype", false, mimetype) &&
+ callProvider(this.providers[i], "isInstallAllowed", null, uri))
+ return true;
+ }
+ },
+
+ /**
+ * Starts installation of an array of AddonInstalls notifying the registered
+ * web install listener of blocked or started installs.
+ *
+ * @param mimetype
+ * The mimetype of add-ons being installed
+ * @param source
+ * The nsIDOMWindowInternal that started the installs
+ * @param uri
+ * the nsIURI that started the installs
+ * @param installs
+ * The array of AddonInstalls to be installed
+ */
+ installAddonsFromWebpage: function AMI_installAddonsFromWebpage(mimetype,
+ source,
+ uri,
+ installs) {
+ if (!("@mozilla.org/addons/web-install-listener;1" in Cc)) {
+ WARN("No web installer available, cancelling all installs");
+ installs.forEach(function(install) {
+ install.cancel();
+ });
+ return;
+ }
+
+ try {
+ let weblistener = Cc["@mozilla.org/addons/web-install-listener;1"].
+ getService(Ci.amIWebInstallListener);
+
+ if (!this.isInstallAllowed(mimetype, uri)) {
+ if (weblistener.onWebInstallBlocked(source, uri, installs,
+ installs.length)) {
+ installs.forEach(function(install) {
+ install.install();
+ });
+ }
+ }
+ else if (weblistener.onWebInstallRequested(source, uri, installs,
+ installs.length)) {
+ installs.forEach(function(install) {
+ install.install();
+ });
+ }
+ }
+ catch (e) {
+ // In the event that the weblistener throws during instatiation or when
+ // calling onWebInstallBlocked or onWebInstallRequested all of the
+ // installs should get cancelled.
+ WARN("Failure calling web installer: " + e);
+ installs.forEach(function(install) {
+ install.cancel();
+ });
+ }
+ },
+
+ /**
+ * Adds a new InstallListener if the listener is not already registered.
+ *
+ * @param listener
+ * The InstallListener to add
+ */
+ addInstallListener: function AMI_addInstallListener(listener) {
+ if (!this.installListeners.some(function(i) { return i == listener; }))
+ this.installListeners.push(listener);
+ },
+
+ /**
+ * Removes an InstallListener if the listener is registered.
+ *
+ * @param listener
+ * The InstallListener to remove
+ */
+ removeInstallListener: function AMI_removeInstallListener(listener) {
+ this.installListeners = this.installListeners.filter(function(i) {
+ return i != listener;
+ });
+ },
+
+ /**
+ * Asynchronously gets an add-on with a specific ID.
+ *
+ * @param id
+ * The ID of the add-on to retrieve
+ * @param callback
+ * The callback to pass the retrieved add-on to
+ * @throws if the id or callback arguments are not specified
+ */
+ getAddon: function AMI_getAddon(id, callback) {
+ if (!id || !callback)
+ throw Cr.NS_ERROR_INVALID_ARG;
+
+ new AsyncObjectCaller(this.providers, "getAddon", {
+ nextObject: function(caller, provider) {
+ callProvider(provider, "getAddon", null, id, function(addon) {
+ if (addon)
+ safeCall(callback, addon);
+ else
+ caller.callNext();
+ });
+ },
+
+ noMoreObjects: function(caller) {
+ safeCall(callback, null);
+ }
+ });
+ },
+
+ /**
+ * Asynchronously gets an array of add-ons.
+ *
+ * @param ids
+ * The array of IDs to retrieve
+ * @param callback
+ * The callback to pass an array of Addons to
+ * @throws if the id or callback arguments are not specified
+ */
+ getAddons: function AMI_getAddons(ids, callback) {
+ if (!ids || !callback)
+ throw Cr.NS_ERROR_INVALID_ARG;
+
+ let addons = [];
+
+ new AsyncObjectCaller(ids, null, {
+ nextObject: function(caller, id) {
+ AddonManagerInternal.getAddon(id, function(addon) {
+ addons.push(addon);
+ caller.callNext();
+ });
+ },
+
+ noMoreObjects: function(caller) {
+ safeCall(callback, addons);
+ }
+ });
+ },
+
+ /**
+ * Asynchronously gets add-ons of specific types.
+ *
+ * @param types
+ * An optional array of types to retrieve. Each type is a string name
+ * @param callback
+ * The callback to pass an array of Addons to.
+ * @throws if the callback argument is not specified
+ */
+ getAddonsByTypes: function AMI_getAddonsByTypes(types, callback) {
+ if (!callback)
+ throw Cr.NS_ERROR_INVALID_ARG;
+
+ let addons = [];
+
+ new AsyncObjectCaller(this.providers, "getAddonsByTypes", {
+ nextObject: function(caller, provider) {
+ callProvider(provider, "getAddonsByTypes", null, types,
+ function(providerAddons) {
+ addons = addons.concat(providerAddons);
+ caller.callNext();
+ });
+ },
+
+ noMoreObjects: function(caller) {
+ safeCall(callback, addons);
+ }
+ });
+ },
+
+ /**
+ * Asynchronously gets add-ons that have operations waiting for an application
+ * restart to complete.
+ *
+ * @param types
+ * An optional array of types to retrieve. Each type is a string name
+ * @param callback
+ * The callback to pass the array of Addons to
+ * @throws if the callback argument is not specified
+ */
+ getAddonsWithPendingOperations:
+ function AMI_getAddonsWithPendingOperations(types, callback) {
+ if (!callback)
+ throw Cr.NS_ERROR_INVALID_ARG;
+
+ let addons = [];
+
+ new AsyncObjectCaller(this.providers, "getAddonsWithPendingOperations", {
+ nextObject: function(caller, provider) {
+ callProvider(provider, "getAddonsWithPendingOperations", null, types,
+ function(providerAddons) {
+ addons = addons.concat(providerAddons);
+ caller.callNext();
+ });
+ },
+
+ noMoreObjects: function(caller) {
+ safeCall(callback, addons);
+ }
+ });
+ },
+
+ /**
+ * Adds a new AddonListener if the listener is not already registered.
+ *
+ * @param listener
+ * The listener to add
+ */
+ addAddonListener: function AMI_addAddonListener(listener) {
+ if (!this.addonListeners.some(function(i) { return i == listener; }))
+ this.addonListeners.push(listener);
+ },
+
+ /**
+ * Removes an AddonListener if the listener is registered.
+ *
+ * @param listener
+ * The listener to remove
+ */
+ removeAddonListener: function AMI_removeAddonListener(listener) {
+ this.addonListeners = this.addonListeners.filter(function(i) {
+ return i != listener;
+ });
+ }
+};
+
+/**
+ * Should not be used outside of core Mozilla code. This is a private API for
+ * the startup and platform integration code to use. Refer to the methods on
+ * AddonManagerInternal for documentation however note that these methods are
+ * subject to change at any time.
+ */
+var AddonManagerPrivate = {
+ startup: function AMP_startup() {
+ AddonManagerInternal.startup();
+ },
+
+ registerProvider: function AMP_registerProvider(provider) {
+ AddonManagerInternal.registerProvider(provider);
+ },
+
+ shutdown: function AMP_shutdown() {
+ AddonManagerInternal.shutdown();
+ },
+
+ backgroundUpdateCheck: function AMP_backgroundUpdateCheck() {
+ AddonManagerInternal.backgroundUpdateCheck();
+ },
+
+ notifyAddonChanged: function AMP_notifyAddonChanged(id, type, pendingRestart) {
+ AddonManagerInternal.notifyAddonChanged(id, type, pendingRestart);
+ },
+
+ callInstallListeners: function AMP_callInstallListeners(method) {
+ return AddonManagerInternal.callInstallListeners.apply(AddonManagerInternal,
+ arguments);
+ },
+
+ callAddonListeners: function AMP_callAddonListeners(method) {
+ AddonManagerInternal.callAddonListeners.apply(AddonManagerInternal, arguments);
+ }
+};
+
+/**
+ * This is the public API that UI and developers should be calling. All methods
+ * just forward to AddonManagerInternal.
+ */
+var AddonManager = {
+ // Constants for the AddonInstall.state property
+ // The install is available for download.
+ STATE_AVAILABLE: 0,
+ // The install is being downloaded.
+ STATE_DOWNLOADING: 1,
+ // The install is checking for compatibility information.
+ STATE_CHECKING: 2,
+ // The install is downloaded and ready to install.
+ STATE_DOWNLOADED: 3,
+ // The download failed.
+ STATE_DOWNLOAD_FAILED: 4,
+ // The add-on is being installed.
+ STATE_INSTALLING: 5,
+ // The add-on has been installed.
+ STATE_INSTALLED: 6,
+ // The install failed.
+ STATE_INSTALL_FAILED: 7,
+ // The install has been cancelled.
+ STATE_CANCELLED: 8,
+
+ // Constants representing different types of errors while downloading an
+ // add-on.
+ // The download failed due to network problems.
+ ERROR_NETWORK_FAILURE: -1,
+ // The downloaded file did not match the provided hash.
+ ERROR_INCORRECT_HASH: -2,
+ // The downloaded file seems to be corrupted in some way.
+ ERROR_CORRUPT_FILE: -3,
+
+ // Constants to indicate why an update check is being performed
+ // Update check has been requested by the user.
+ UPDATE_WHEN_USER_REQUESTED: 1,
+ // Update check is necessary to see if the Addon is compatibile with a new
+ // version of the application.
+ UPDATE_WHEN_NEW_APP_DETECTED: 2,
+ // Update check is necessary because a new application has been installed.
+ UPDATE_WHEN_NEW_APP_INSTALLED: 3,
+ // Update check is a regular background update check.
+ UPDATE_WHEN_PERIODIC_UPDATE: 16,
+ // Update check is needed to check an Addon that is being installed.
+ UPDATE_WHEN_ADDON_INSTALLED: 17,
+
+ // Constants for operations in Addon.pendingOperations
+ // Indicates that the Addon will be enabled after the application restarts.
+ PENDING_ENABLE: 1,
+ // Indicates that the Addon will be disabled after the application restarts.
+ PENDING_DISABLE: 2,
+ // Indicates that the Addon will be uninstalled after the application restarts.
+ PENDING_UNINSTALL: 4,
+ // Indicates that the Addon will be installed after the application restarts.
+ PENDING_INSTALL: 8,
+
+ // Constants for permissions in Addon.permissions.
+ // Indicates that the Addon can be uninstalled.
+ PERM_CAN_UNINSTALL: 1,
+ // Indicates that the Addon can be enabled by the user.
+ PERM_CAN_ENABLE: 2,
+ // Indicates that the Addon can be disabled by the user.
+ PERM_CAN_DISABLE: 4,
+ // Indicates that the Addon can be upgraded.
+ PERM_CAN_UPGRADE: 8,
+
+ getInstallForURL: function AM_getInstallForURL(url, callback, mimetype, hash,
+ name, iconURL, version,
+ loadgroup) {
+ AddonManagerInternal.getInstallForURL(url, callback, mimetype, hash, name,
+ iconURL, version, loadgroup);
+ },
+
+ getInstallForFile: function AM_getInstallForFile(file, callback, mimetype) {
+ AddonManagerInternal.getInstallForFile(file, callback, mimetype);
+ },
+
+ getAddon: function AM_getAdon(id, callback) {
+ AddonManagerInternal.getAddon(id, callback);
+ },
+
+ getAddons: function AM_getAddons(ids, callback) {
+ AddonManagerInternal.getAddons(ids, callback);
+ },
+
+ getAddonsWithPendingOperations:
+ function AM_getAddonsWithPendingOperations(types, callback) {
+ AddonManagerInternal.getAddonsWithPendingOperations(types, callback);
+ },
+
+ getAddonsByTypes: function AM_getAddonsByTypes(types, callback) {
+ AddonManagerInternal.getAddonsByTypes(types, callback);
+ },
+
+ getInstalls: function AM_getInstalls(types, callback) {
+ AddonManagerInternal.getInstalls(types, callback);
+ },
+
+ isInstallEnabled: function AM_isInstallEnabled(type) {
+ return AddonManagerInternal.isInstallEnabled(type);
+ },
+
+ isInstallAllowed: function AM_isInstallAllowed(type, uri) {
+ return AddonManagerInternal.isInstallAllowed(type, uri);
+ },
+
+ installAddonsFromWebpage: function AM_installAddonsFromWebpage(type, source,
+ uri, installs) {
+ AddonManagerInternal.installAddonsFromWebpage(type, source, uri, installs);
+ },
+
+ addInstallListener: function AM_addInstallListener(listener) {
+ AddonManagerInternal.addInstallListener(listener);
+ },
+
+ removeInstallListener: function AM_removeInstallListener(listener) {
+ AddonManagerInternal.removeInstallListener(listener);
+ },
+
+ addAddonListener: function AM_addAddonListener(listener) {
+ AddonManagerInternal.addAddonListener(listener);
+ },
+
+ removeAddonListener: function AM_removeAddonListener(listener) {
+ AddonManagerInternal.removeAddonListener(listener);
+ }
+};
new file mode 100644
--- /dev/null
+++ b/toolkit/mozapps/extensions/addonManager.js
@@ -0,0 +1,191 @@
+/*
+# ***** BEGIN LICENSE BLOCK *****
+# Version: MPL 1.1/GPL 2.0/LGPL 2.1
+#
+# The contents of this file are subject to the Mozilla Public License Version
+# 1.1 (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+# http://www.mozilla.org/MPL/
+#
+# Software distributed under the License is distributed on an "AS IS" basis,
+# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+# for the specific language governing rights and limitations under the
+# License.
+#
+# The Original Code is the Extension Manager.
+#
+# The Initial Developer of the Original Code is
+# the Mozilla Foundation.
+# Portions created by the Initial Developer are Copyright (C) 2009
+# the Initial Developer. All Rights Reserved.
+#
+# Contributor(s):
+# Dave Townsend <dtownsend@oxymoronical.com>
+#
+# Alternatively, the contents of this file may be used under the terms of
+# either the GNU General Public License Version 2 or later (the "GPL"), or
+# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+# in which case the provisions of the GPL or the LGPL are applicable instead
+# of those above. If you wish to allow use of your version of this file only
+# under the terms of either the GPL or the LGPL, and not to allow others to
+# use your version of this file under the terms of the MPL, indicate your
+# decision by deleting the provisions above and replace them with the notice
+# and other provisions required by the GPL or the LGPL. If you do not delete
+# the provisions above, a recipient may use your version of this file under
+# the terms of any one of the MPL, the GPL or the LGPL.
+#
+# ***** END LICENSE BLOCK *****
+*/
+
+/**
+ * This component serves as integration between the platform and AddonManager.
+ * It is responsible for initializing and shutting down the AddonManager as well
+ * as passing new installs from webpages to the AddonManager.
+ */
+
+const Cc = Components.classes;
+const Ci = Components.interfaces;
+const Cr = Components.results;
+
+const PREF_EM_UPDATE_INTERVAL = "extensions.update.interval";
+
+// The old XPInstall error codes
+const EXECUTION_ERROR = -203;
+const CANT_READ_ARCHIVE = -207;
+const USER_CANCELLED = -210;
+const DOWNLOAD_ERROR = -228;
+const UNSUPPORTED_TYPE = -244;
+const SUCCESS = 0;
+
+Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
+
+var gSingleton = null;
+
+function amManager() {
+ Components.utils.import("resource://gre/modules/AddonManager.jsm");
+}
+
+amManager.prototype = {
+ observe: function AMC_observe(subject, topic, data) {
+ let os = Cc["@mozilla.org/observer-service;1"].
+ getService(Ci.nsIObserverService);
+
+ switch (topic) {
+ case "profile-after-change":
+ os.addObserver(this, "xpcom-shutdown", false);
+ AddonManagerPrivate.startup();
+ break;
+ case "xpcom-shutdown":
+ os.removeObserver(this, "xpcom-shutdown");
+ AddonManagerPrivate.shutdown();
+ break;
+ }
+ },
+
+ /**
+ * @see amIWebInstaller.idl
+ */
+ isInstallEnabled: function AMC_isInstallEnabled(mimetype, referer) {
+ return AddonManager.isInstallEnabled(mimetype);
+ },
+
+ /**
+ * @see amIWebInstaller.idl
+ */
+ installAddonsFromWebpage: function AMC_installAddonsFromWebpage(mimetype,
+ window,
+ referer, uris,
+ hashes, names,
+ icons, callback) {
+ if (uris.length == 0)
+ return false;
+
+ let retval = true;
+ if (!AddonManager.isInstallAllowed(mimetype, referer)) {
+ callback = null;
+ retval = false;
+ }
+
+ let loadgroup = null;
+
+ try {
+ loadgroup = window.QueryInterface(Ci.nsIInterfaceRequestor)
+ .getInterface(Ci.nsIWebNavigation)
+ .QueryInterface(Ci.nsIDocumentLoader).loadGroup;
+ }
+ catch (e) {
+ }
+
+ let installs = [];
+ function buildNextInstall() {
+ if (uris.length == 0) {
+ AddonManager.installAddonsFromWebpage(mimetype, window, referer, installs);
+ return;
+ }
+ let uri = uris.shift();
+ AddonManager.getInstallForURL(uri, function(install) {
+ if (install) {
+ installs.push(install);
+ if (callback) {
+ install.addListener({
+ onDownloadCancelled: function(install) {
+ callback.onInstallEnded(uri, USER_CANCELLED);
+ },
+
+ onDownloadFailed: function(install, error) {
+ if (error == AddonManager.ERROR_CORRUPT_FILE)
+ callback.onInstallEnded(uri, CANT_READ_ARCHIVE);
+ else
+ callback.onInstallEnded(uri, DOWNLOAD_ERROR);
+ },
+
+ onInstallFailed: function(install, error) {
+ callback.onInstallEnded(uri, EXECUTION_ERROR);
+ },
+
+ onInstallEnded: function(install, status) {
+ callback.onInstallEnded(uri, SUCCESS);
+ }
+ });
+ }
+ }
+ else if (callback) {
+ callback.callback(uri, UNSUPPORTED_TYPE);
+ }
+ buildNextInstall();
+ }, mimetype, hashes.shift(), names.shift(), icons.shift(), null, loadgroup);
+ }
+ buildNextInstall();
+
+ return retval;
+ },
+
+ notify: function AMC_notify(timer) {
+ AddonManagerPrivate.backgroundUpdateCheck();
+ },
+
+ classDescription: "Addons Manager",
+ contractID: "@mozilla.org/addons/integration;1",
+ classID: Components.ID("{4399533d-08d1-458c-a87a-235f74451cfa}"),
+ _xpcom_categories: [{ category: "profile-after-change" },
+ { category: "update-timer",
+ value: "@mozilla.org/addons/integration;1," +
+ "getService,addon-background-update-timer," +
+ PREF_EM_UPDATE_INTERVAL + ",86400" }],
+ _xpcom_factory: {
+ createInstance: function(outer, iid) {
+ if (outer != null)
+ throw Cr.NS_ERROR_NO_AGGREGATION;
+
+ if (!gSingleton)
+ gSingleton = new amManager();
+ return gSingleton.QueryInterface(iid);
+ }
+ },
+ QueryInterface: XPCOMUtils.generateQI([Ci.amIWebInstaller,
+ Ci.nsITimerCallback,
+ Ci.nsIObserver])
+};
+
+function NSGetModule(compMgr, fileSpec)
+ XPCOMUtils.generateModule([amManager]);