CLOSED TREE: TraceMonkey merge head. (a=blockers)
[mozilla-central.git] / toolkit / mozapps / extensions / addonManager.js
blobbcea9e0c4cc9f92bead65c5b2c19e3d635a1abc9
1 /*
2 # ***** BEGIN LICENSE BLOCK *****
3 # Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 # The contents of this file are subject to the Mozilla Public License Version
6 # 1.1 (the "License"); you may not use this file except in compliance with
7 # the License. You may obtain a copy of the License at
8 # http://www.mozilla.org/MPL/
10 # Software distributed under the License is distributed on an "AS IS" basis,
11 # WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 # for the specific language governing rights and limitations under the
13 # License.
15 # The Original Code is the Extension Manager.
17 # The Initial Developer of the Original Code is
18 # the Mozilla Foundation.
19 # Portions created by the Initial Developer are Copyright (C) 2009
20 # the Initial Developer. All Rights Reserved.
22 # Contributor(s):
23 #   Dave Townsend <dtownsend@oxymoronical.com>
25 # Alternatively, the contents of this file may be used under the terms of
26 # either the GNU General Public License Version 2 or later (the "GPL"), or
27 # the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
28 # in which case the provisions of the GPL or the LGPL are applicable instead
29 # of those above. If you wish to allow use of your version of this file only
30 # under the terms of either the GPL or the LGPL, and not to allow others to
31 # use your version of this file under the terms of the MPL, indicate your
32 # decision by deleting the provisions above and replace them with the notice
33 # and other provisions required by the GPL or the LGPL. If you do not delete
34 # the provisions above, a recipient may use your version of this file under
35 # the terms of any one of the MPL, the GPL or the LGPL.
37 # ***** END LICENSE BLOCK *****
40 /**
41  * This component serves as integration between the platform and AddonManager.
42  * It is responsible for initializing and shutting down the AddonManager as well
43  * as passing new installs from webpages to the AddonManager.
44  */
46 const Cc = Components.classes;
47 const Ci = Components.interfaces;
48 const Cr = Components.results;
50 const PREF_EM_UPDATE_INTERVAL = "extensions.update.interval";
52 // The old XPInstall error codes
53 const EXECUTION_ERROR   = -203;
54 const CANT_READ_ARCHIVE = -207;
55 const USER_CANCELLED    = -210;
56 const DOWNLOAD_ERROR    = -228;
57 const UNSUPPORTED_TYPE  = -244;
58 const SUCCESS = 0;
60 const MSG_INSTALL_ENABLED  = "WebInstallerIsInstallEnabled";
61 const MSG_INSTALL_ADDONS   = "WebInstallerInstallAddonsFromWebpage";
62 const MSG_INSTALL_CALLBACK = "WebInstallerInstallCallback";
64 const CHILD_SCRIPT =
65   "chrome://mozapps/content/extensions/extensions-content.js";
67 Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
68 Components.utils.import("resource://gre/modules/Services.jsm");
70 var gSingleton = null;
72 function amManager() {
73   Components.utils.import("resource://gre/modules/AddonManager.jsm");
75   var messageManager = Cc["@mozilla.org/globalmessagemanager;1"].
76                        getService(Ci.nsIChromeFrameMessageManager);
78   messageManager.addMessageListener(MSG_INSTALL_ENABLED, this);
79   messageManager.addMessageListener(MSG_INSTALL_ADDONS, this);
80   messageManager.loadFrameScript(CHILD_SCRIPT, true, true);
83 amManager.prototype = {
84   observe: function AMC_observe(aSubject, aTopic, aData) {
85     let os = Cc["@mozilla.org/observer-service;1"].
86              getService(Ci.nsIObserverService);
88     switch (aTopic) {
89     case "addons-startup":
90       os.addObserver(this, "xpcom-shutdown", false);
91       AddonManagerPrivate.startup();
92       break;
93     case "xpcom-shutdown":
94       os.removeObserver(this, "xpcom-shutdown");
95       AddonManagerPrivate.shutdown();
96       break;
97     }
98   },
100   /**
101    * @see amIWebInstaller.idl
102    */
103   isInstallEnabled: function AMC_isInstallEnabled(aMimetype, aReferer) {
104     return AddonManager.isInstallEnabled(aMimetype);
105   },
107   /**
108    * @see amIWebInstaller.idl
109    */
110   installAddonsFromWebpage: function AMC_installAddonsFromWebpage(aMimetype,
111                                                                   aWindow,
112                                                                   aReferer, aUris,
113                                                                   aHashes, aNames,
114                                                                   aIcons, aCallback) {
115     if (aUris.length == 0)
116       return false;
118     let retval = true;
119     if (!AddonManager.isInstallAllowed(aMimetype, aReferer)) {
120       aCallback = null;
121       retval = false;
122     }
124     let loadGroup = null;
126     try {
127       loadGroup = aWindow.QueryInterface(Ci.nsIInterfaceRequestor)
128                          .getInterface(Ci.nsIWebNavigation)
129                          .QueryInterface(Ci.nsIDocumentLoader).loadGroup;
130     }
131     catch (e) {
132     }
134     let installs = [];
135     function buildNextInstall() {
136       if (aUris.length == 0) {
137         AddonManager.installAddonsFromWebpage(aMimetype, aWindow, aReferer, installs);
138         return;
139       }
140       let uri = aUris.shift();
141       AddonManager.getInstallForURL(uri, function(aInstall) {
142         if (aInstall) {
143           installs.push(aInstall);
144           if (aCallback) {
145             function callCallback(aUri, aStatus) {
146               try {
147                 aCallback.onInstallEnded(aUri, aStatus);
148               }
149               catch (e) {
150                 Components.utils.reportError(e);
151               }
152             }
154             aInstall.addListener({
155               onDownloadCancelled: function(aInstall) {
156                 callCallback(uri, USER_CANCELLED);
157               },
159               onDownloadFailed: function(aInstall) {
160                 if (aInstall.error == AddonManager.ERROR_CORRUPT_FILE)
161                   callCallback(uri, CANT_READ_ARCHIVE);
162                 else
163                   callCallback(uri, DOWNLOAD_ERROR);
164               },
166               onInstallFailed: function(aInstall) {
167                 callCallback(uri, EXECUTION_ERROR);
168               },
170               onInstallEnded: function(aInstall, aStatus) {
171                 callCallback(uri, SUCCESS);
172               }
173             });
174           }
175         }
176         else if (aCallback) {
177           aCallback.onInstallEnded(uri, UNSUPPORTED_TYPE);
178         }
179         buildNextInstall();
180       }, aMimetype, aHashes.shift(), aNames.shift(), aIcons.shift(), null, loadGroup);
181     }
182     buildNextInstall();
184     return retval;
185   },
187   notify: function AMC_notify(aTimer) {
188     AddonManagerPrivate.backgroundUpdateCheck();
189   },
191   /**
192    * messageManager callback function.
193    *
194    * Listens to requests from child processes for InstallTrigger
195    * activity, and sends back callbacks.
196    */
197   receiveMessage: function(aMessage) {
198     var payload = aMessage.json;
199     var referer = Services.io.newURI(payload.referer, null, null);
200     switch (aMessage.name) {
201       case MSG_INSTALL_ENABLED:
202         return this.isInstallEnabled(payload.mimetype, referer);
204       case MSG_INSTALL_ADDONS:
205         var callback = null;
206         if (payload.callbackId != -1) {
207           callback = {
208             onInstallEnded: function ITP_callback(url, status) {
209               // Doing it this way, instead of aMessage.target.messageManager,
210               // ensures it works in Firefox and not only Fennec. See bug
211               // 578172. TODO: Clean up this code once that bug is fixed
212               var flo = aMessage.target.QueryInterface(Ci.nsIFrameLoaderOwner);
213               var returnMessageManager = flo.frameLoader.messageManager;
214               returnMessageManager.sendAsyncMessage(MSG_INSTALL_CALLBACK,
215                 { installerId: payload.installerId,
216                   callbackId: payload.callbackId, url: url, status: status }
217               );
218             },
219           };
220         }
221         var window = null;
222         try {
223           // Normal approach for single-process mode
224           window = aMessage.target.contentWindow;
225         } catch (e) {
226           // Fallback for multiprocess (e10s) mode. Should reimplement this
227           // properly with Window IDs when possible, see bug 596109.
228           window = aMessage.target.ownerDocument.defaultView;
229         }
230         return this.installAddonsFromWebpage(payload.mimetype,
231           window, referer, payload.uris, payload.hashes, payload.names,
232           payload.icons, callback, payload.uris.length);
233     }
234   },
236   classID: Components.ID("{4399533d-08d1-458c-a87a-235f74451cfa}"),
237   _xpcom_factory: {
238     createInstance: function(aOuter, aIid) {
239       if (aOuter != null)
240         throw Cr.NS_ERROR_NO_AGGREGATION;
241   
242       if (!gSingleton)
243         gSingleton = new amManager();
244       return gSingleton.QueryInterface(aIid);
245     }
246   },
247   QueryInterface: XPCOMUtils.generateQI([Ci.amIWebInstaller,
248                                          Ci.nsITimerCallback,
249                                          Ci.nsIObserver])
252 var NSGetFactory = XPCOMUtils.generateNSGetFactory([amManager]);