|
1 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
2 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
4 |
|
5 /** |
|
6 * This component serves as integration between the platform and AddonManager. |
|
7 * It is responsible for initializing and shutting down the AddonManager as well |
|
8 * as passing new installs from webpages to the AddonManager. |
|
9 */ |
|
10 |
|
11 "use strict"; |
|
12 |
|
13 const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components; |
|
14 |
|
15 const PREF_EM_UPDATE_INTERVAL = "extensions.update.interval"; |
|
16 |
|
17 // The old XPInstall error codes |
|
18 const EXECUTION_ERROR = -203; |
|
19 const CANT_READ_ARCHIVE = -207; |
|
20 const USER_CANCELLED = -210; |
|
21 const DOWNLOAD_ERROR = -228; |
|
22 const UNSUPPORTED_TYPE = -244; |
|
23 const SUCCESS = 0; |
|
24 |
|
25 const MSG_INSTALL_ENABLED = "WebInstallerIsInstallEnabled"; |
|
26 const MSG_INSTALL_ADDONS = "WebInstallerInstallAddonsFromWebpage"; |
|
27 const MSG_INSTALL_CALLBACK = "WebInstallerInstallCallback"; |
|
28 |
|
29 const CHILD_SCRIPT = "resource://gre/modules/addons/Content.js"; |
|
30 |
|
31 Cu.import("resource://gre/modules/XPCOMUtils.jsm"); |
|
32 Cu.import("resource://gre/modules/Services.jsm"); |
|
33 |
|
34 let gSingleton = null; |
|
35 |
|
36 let gParentMM = null; |
|
37 |
|
38 |
|
39 function amManager() { |
|
40 Cu.import("resource://gre/modules/AddonManager.jsm"); |
|
41 |
|
42 let globalMM = Cc["@mozilla.org/globalmessagemanager;1"] |
|
43 .getService(Ci.nsIMessageListenerManager); |
|
44 globalMM.loadFrameScript(CHILD_SCRIPT, true); |
|
45 |
|
46 gParentMM = Cc["@mozilla.org/parentprocessmessagemanager;1"] |
|
47 .getService(Ci.nsIMessageListenerManager); |
|
48 gParentMM.addMessageListener(MSG_INSTALL_ENABLED, this); |
|
49 gParentMM.addMessageListener(MSG_INSTALL_ADDONS, this); |
|
50 } |
|
51 |
|
52 amManager.prototype = { |
|
53 observe: function AMC_observe(aSubject, aTopic, aData) { |
|
54 if (aTopic == "addons-startup") |
|
55 AddonManagerPrivate.startup(); |
|
56 }, |
|
57 |
|
58 /** |
|
59 * @see amIAddonManager.idl |
|
60 */ |
|
61 mapURIToAddonID: function AMC_mapURIToAddonID(uri, id) { |
|
62 id.value = AddonManager.mapURIToAddonID(uri); |
|
63 return !!id.value; |
|
64 }, |
|
65 |
|
66 /** |
|
67 * @see amIWebInstaller.idl |
|
68 */ |
|
69 isInstallEnabled: function AMC_isInstallEnabled(aMimetype, aReferer) { |
|
70 return AddonManager.isInstallEnabled(aMimetype); |
|
71 }, |
|
72 |
|
73 /** |
|
74 * @see amIWebInstaller.idl |
|
75 */ |
|
76 installAddonsFromWebpage: function AMC_installAddonsFromWebpage(aMimetype, |
|
77 aWindow, |
|
78 aReferer, aUris, |
|
79 aHashes, aNames, |
|
80 aIcons, aCallback) { |
|
81 if (aUris.length == 0) |
|
82 return false; |
|
83 |
|
84 let retval = true; |
|
85 if (!AddonManager.isInstallAllowed(aMimetype, aReferer)) { |
|
86 aCallback = null; |
|
87 retval = false; |
|
88 } |
|
89 |
|
90 let loadGroup = null; |
|
91 |
|
92 try { |
|
93 loadGroup = aWindow.QueryInterface(Ci.nsIInterfaceRequestor) |
|
94 .getInterface(Ci.nsIWebNavigation) |
|
95 .QueryInterface(Ci.nsIDocumentLoader).loadGroup; |
|
96 } |
|
97 catch (e) { |
|
98 } |
|
99 |
|
100 let installs = []; |
|
101 function buildNextInstall() { |
|
102 if (aUris.length == 0) { |
|
103 AddonManager.installAddonsFromWebpage(aMimetype, aWindow, aReferer, installs); |
|
104 return; |
|
105 } |
|
106 let uri = aUris.shift(); |
|
107 AddonManager.getInstallForURL(uri, function buildNextInstall_getInstallForURL(aInstall) { |
|
108 function callCallback(aUri, aStatus) { |
|
109 try { |
|
110 aCallback.onInstallEnded(aUri, aStatus); |
|
111 } |
|
112 catch (e) { |
|
113 Components.utils.reportError(e); |
|
114 } |
|
115 } |
|
116 |
|
117 if (aInstall) { |
|
118 installs.push(aInstall); |
|
119 if (aCallback) { |
|
120 aInstall.addListener({ |
|
121 onDownloadCancelled: function buildNextInstall_onDownloadCancelled(aInstall) { |
|
122 callCallback(uri, USER_CANCELLED); |
|
123 }, |
|
124 |
|
125 onDownloadFailed: function buildNextInstall_onDownloadFailed(aInstall) { |
|
126 if (aInstall.error == AddonManager.ERROR_CORRUPT_FILE) |
|
127 callCallback(uri, CANT_READ_ARCHIVE); |
|
128 else |
|
129 callCallback(uri, DOWNLOAD_ERROR); |
|
130 }, |
|
131 |
|
132 onInstallFailed: function buildNextInstall_onInstallFailed(aInstall) { |
|
133 callCallback(uri, EXECUTION_ERROR); |
|
134 }, |
|
135 |
|
136 onInstallEnded: function buildNextInstall_onInstallEnded(aInstall, aStatus) { |
|
137 callCallback(uri, SUCCESS); |
|
138 } |
|
139 }); |
|
140 } |
|
141 } |
|
142 else if (aCallback) { |
|
143 aCallback.onInstallEnded(uri, UNSUPPORTED_TYPE); |
|
144 } |
|
145 buildNextInstall(); |
|
146 }, aMimetype, aHashes.shift(), aNames.shift(), aIcons.shift(), null, loadGroup); |
|
147 } |
|
148 buildNextInstall(); |
|
149 |
|
150 return retval; |
|
151 }, |
|
152 |
|
153 notify: function AMC_notify(aTimer) { |
|
154 AddonManagerPrivate.backgroundUpdateCheck(); |
|
155 }, |
|
156 |
|
157 /** |
|
158 * messageManager callback function. |
|
159 * |
|
160 * Listens to requests from child processes for InstallTrigger |
|
161 * activity, and sends back callbacks. |
|
162 */ |
|
163 receiveMessage: function AMC_receiveMessage(aMessage) { |
|
164 let payload = aMessage.data; |
|
165 let referer = Services.io.newURI(payload.referer, null, null); |
|
166 |
|
167 switch (aMessage.name) { |
|
168 case MSG_INSTALL_ENABLED: |
|
169 return this.isInstallEnabled(payload.mimetype, referer); |
|
170 |
|
171 case MSG_INSTALL_ADDONS: |
|
172 let callback = null; |
|
173 if (payload.callbackID != -1) { |
|
174 callback = { |
|
175 onInstallEnded: function ITP_callback(url, status) { |
|
176 gParentMM.broadcastAsyncMessage(MSG_INSTALL_CALLBACK, { |
|
177 callbackID: payload.callbackID, |
|
178 url: url, |
|
179 status: status |
|
180 }); |
|
181 }, |
|
182 }; |
|
183 } |
|
184 |
|
185 // Should reimplement this properly with Window IDs when possible, |
|
186 // see bug 596109. |
|
187 let window = aMessage.objects.win; |
|
188 |
|
189 return this.installAddonsFromWebpage(payload.mimetype, |
|
190 window, referer, payload.uris, payload.hashes, payload.names, |
|
191 payload.icons, callback); |
|
192 } |
|
193 }, |
|
194 |
|
195 classID: Components.ID("{4399533d-08d1-458c-a87a-235f74451cfa}"), |
|
196 _xpcom_factory: { |
|
197 createInstance: function AMC_createInstance(aOuter, aIid) { |
|
198 if (aOuter != null) |
|
199 throw Components.Exception("Component does not support aggregation", |
|
200 Cr.NS_ERROR_NO_AGGREGATION); |
|
201 |
|
202 if (!gSingleton) |
|
203 gSingleton = new amManager(); |
|
204 return gSingleton.QueryInterface(aIid); |
|
205 } |
|
206 }, |
|
207 QueryInterface: XPCOMUtils.generateQI([Ci.amIAddonManager, |
|
208 Ci.amIWebInstaller, |
|
209 Ci.nsITimerCallback, |
|
210 Ci.nsIObserver, |
|
211 Ci.nsIMessageListener]) |
|
212 }; |
|
213 |
|
214 this.NSGetFactory = XPCOMUtils.generateNSGetFactory([amManager]); |