michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: /** michael@0: * This component serves as integration between the platform and AddonManager. michael@0: * It is responsible for initializing and shutting down the AddonManager as well michael@0: * as passing new installs from webpages to the AddonManager. michael@0: */ michael@0: michael@0: "use strict"; michael@0: michael@0: const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components; michael@0: michael@0: const PREF_EM_UPDATE_INTERVAL = "extensions.update.interval"; michael@0: michael@0: // The old XPInstall error codes michael@0: const EXECUTION_ERROR = -203; michael@0: const CANT_READ_ARCHIVE = -207; michael@0: const USER_CANCELLED = -210; michael@0: const DOWNLOAD_ERROR = -228; michael@0: const UNSUPPORTED_TYPE = -244; michael@0: const SUCCESS = 0; michael@0: michael@0: const MSG_INSTALL_ENABLED = "WebInstallerIsInstallEnabled"; michael@0: const MSG_INSTALL_ADDONS = "WebInstallerInstallAddonsFromWebpage"; michael@0: const MSG_INSTALL_CALLBACK = "WebInstallerInstallCallback"; michael@0: michael@0: const CHILD_SCRIPT = "resource://gre/modules/addons/Content.js"; michael@0: michael@0: Cu.import("resource://gre/modules/XPCOMUtils.jsm"); michael@0: Cu.import("resource://gre/modules/Services.jsm"); michael@0: michael@0: let gSingleton = null; michael@0: michael@0: let gParentMM = null; michael@0: michael@0: michael@0: function amManager() { michael@0: Cu.import("resource://gre/modules/AddonManager.jsm"); michael@0: michael@0: let globalMM = Cc["@mozilla.org/globalmessagemanager;1"] michael@0: .getService(Ci.nsIMessageListenerManager); michael@0: globalMM.loadFrameScript(CHILD_SCRIPT, true); michael@0: michael@0: gParentMM = Cc["@mozilla.org/parentprocessmessagemanager;1"] michael@0: .getService(Ci.nsIMessageListenerManager); michael@0: gParentMM.addMessageListener(MSG_INSTALL_ENABLED, this); michael@0: gParentMM.addMessageListener(MSG_INSTALL_ADDONS, this); michael@0: } michael@0: michael@0: amManager.prototype = { michael@0: observe: function AMC_observe(aSubject, aTopic, aData) { michael@0: if (aTopic == "addons-startup") michael@0: AddonManagerPrivate.startup(); michael@0: }, michael@0: michael@0: /** michael@0: * @see amIAddonManager.idl michael@0: */ michael@0: mapURIToAddonID: function AMC_mapURIToAddonID(uri, id) { michael@0: id.value = AddonManager.mapURIToAddonID(uri); michael@0: return !!id.value; michael@0: }, michael@0: michael@0: /** michael@0: * @see amIWebInstaller.idl michael@0: */ michael@0: isInstallEnabled: function AMC_isInstallEnabled(aMimetype, aReferer) { michael@0: return AddonManager.isInstallEnabled(aMimetype); michael@0: }, michael@0: michael@0: /** michael@0: * @see amIWebInstaller.idl michael@0: */ michael@0: installAddonsFromWebpage: function AMC_installAddonsFromWebpage(aMimetype, michael@0: aWindow, michael@0: aReferer, aUris, michael@0: aHashes, aNames, michael@0: aIcons, aCallback) { michael@0: if (aUris.length == 0) michael@0: return false; michael@0: michael@0: let retval = true; michael@0: if (!AddonManager.isInstallAllowed(aMimetype, aReferer)) { michael@0: aCallback = null; michael@0: retval = false; michael@0: } michael@0: michael@0: let loadGroup = null; michael@0: michael@0: try { michael@0: loadGroup = aWindow.QueryInterface(Ci.nsIInterfaceRequestor) michael@0: .getInterface(Ci.nsIWebNavigation) michael@0: .QueryInterface(Ci.nsIDocumentLoader).loadGroup; michael@0: } michael@0: catch (e) { michael@0: } michael@0: michael@0: let installs = []; michael@0: function buildNextInstall() { michael@0: if (aUris.length == 0) { michael@0: AddonManager.installAddonsFromWebpage(aMimetype, aWindow, aReferer, installs); michael@0: return; michael@0: } michael@0: let uri = aUris.shift(); michael@0: AddonManager.getInstallForURL(uri, function buildNextInstall_getInstallForURL(aInstall) { michael@0: function callCallback(aUri, aStatus) { michael@0: try { michael@0: aCallback.onInstallEnded(aUri, aStatus); michael@0: } michael@0: catch (e) { michael@0: Components.utils.reportError(e); michael@0: } michael@0: } michael@0: michael@0: if (aInstall) { michael@0: installs.push(aInstall); michael@0: if (aCallback) { michael@0: aInstall.addListener({ michael@0: onDownloadCancelled: function buildNextInstall_onDownloadCancelled(aInstall) { michael@0: callCallback(uri, USER_CANCELLED); michael@0: }, michael@0: michael@0: onDownloadFailed: function buildNextInstall_onDownloadFailed(aInstall) { michael@0: if (aInstall.error == AddonManager.ERROR_CORRUPT_FILE) michael@0: callCallback(uri, CANT_READ_ARCHIVE); michael@0: else michael@0: callCallback(uri, DOWNLOAD_ERROR); michael@0: }, michael@0: michael@0: onInstallFailed: function buildNextInstall_onInstallFailed(aInstall) { michael@0: callCallback(uri, EXECUTION_ERROR); michael@0: }, michael@0: michael@0: onInstallEnded: function buildNextInstall_onInstallEnded(aInstall, aStatus) { michael@0: callCallback(uri, SUCCESS); michael@0: } michael@0: }); michael@0: } michael@0: } michael@0: else if (aCallback) { michael@0: aCallback.onInstallEnded(uri, UNSUPPORTED_TYPE); michael@0: } michael@0: buildNextInstall(); michael@0: }, aMimetype, aHashes.shift(), aNames.shift(), aIcons.shift(), null, loadGroup); michael@0: } michael@0: buildNextInstall(); michael@0: michael@0: return retval; michael@0: }, michael@0: michael@0: notify: function AMC_notify(aTimer) { michael@0: AddonManagerPrivate.backgroundUpdateCheck(); michael@0: }, michael@0: michael@0: /** michael@0: * messageManager callback function. michael@0: * michael@0: * Listens to requests from child processes for InstallTrigger michael@0: * activity, and sends back callbacks. michael@0: */ michael@0: receiveMessage: function AMC_receiveMessage(aMessage) { michael@0: let payload = aMessage.data; michael@0: let referer = Services.io.newURI(payload.referer, null, null); michael@0: michael@0: switch (aMessage.name) { michael@0: case MSG_INSTALL_ENABLED: michael@0: return this.isInstallEnabled(payload.mimetype, referer); michael@0: michael@0: case MSG_INSTALL_ADDONS: michael@0: let callback = null; michael@0: if (payload.callbackID != -1) { michael@0: callback = { michael@0: onInstallEnded: function ITP_callback(url, status) { michael@0: gParentMM.broadcastAsyncMessage(MSG_INSTALL_CALLBACK, { michael@0: callbackID: payload.callbackID, michael@0: url: url, michael@0: status: status michael@0: }); michael@0: }, michael@0: }; michael@0: } michael@0: michael@0: // Should reimplement this properly with Window IDs when possible, michael@0: // see bug 596109. michael@0: let window = aMessage.objects.win; michael@0: michael@0: return this.installAddonsFromWebpage(payload.mimetype, michael@0: window, referer, payload.uris, payload.hashes, payload.names, michael@0: payload.icons, callback); michael@0: } michael@0: }, michael@0: michael@0: classID: Components.ID("{4399533d-08d1-458c-a87a-235f74451cfa}"), michael@0: _xpcom_factory: { michael@0: createInstance: function AMC_createInstance(aOuter, aIid) { michael@0: if (aOuter != null) michael@0: throw Components.Exception("Component does not support aggregation", michael@0: Cr.NS_ERROR_NO_AGGREGATION); michael@0: michael@0: if (!gSingleton) michael@0: gSingleton = new amManager(); michael@0: return gSingleton.QueryInterface(aIid); michael@0: } michael@0: }, michael@0: QueryInterface: XPCOMUtils.generateQI([Ci.amIAddonManager, michael@0: Ci.amIWebInstaller, michael@0: Ci.nsITimerCallback, michael@0: Ci.nsIObserver, michael@0: Ci.nsIMessageListener]) michael@0: }; michael@0: michael@0: this.NSGetFactory = XPCOMUtils.generateNSGetFactory([amManager]);