|
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 file, |
|
3 * You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
4 |
|
5 const Cc = Components.classes; |
|
6 const Ci = Components.interfaces; |
|
7 const Cu = Components.utils; |
|
8 const Cr = Components.results; |
|
9 |
|
10 Cu.import("resource://gre/modules/XPCOMUtils.jsm"); |
|
11 Cu.import("resource://gre/modules/Services.jsm"); |
|
12 Cu.import("resource://gre/modules/DOMRequestHelper.jsm"); |
|
13 Cu.import("resource://gre/modules/AppsUtils.jsm"); |
|
14 Cu.import("resource://gre/modules/BrowserElementPromptService.jsm"); |
|
15 |
|
16 XPCOMUtils.defineLazyServiceGetter(this, "cpmm", |
|
17 "@mozilla.org/childprocessmessagemanager;1", |
|
18 "nsIMessageSender"); |
|
19 |
|
20 function convertAppsArray(aApps, aWindow) { |
|
21 let apps = new aWindow.Array(); |
|
22 for (let i = 0; i < aApps.length; i++) { |
|
23 let app = aApps[i]; |
|
24 apps.push(createApplicationObject(aWindow, app)); |
|
25 } |
|
26 |
|
27 return apps; |
|
28 } |
|
29 |
|
30 function WebappsRegistry() { |
|
31 } |
|
32 |
|
33 WebappsRegistry.prototype = { |
|
34 __proto__: DOMRequestIpcHelper.prototype, |
|
35 |
|
36 receiveMessage: function(aMessage) { |
|
37 let msg = aMessage.json; |
|
38 if (msg.oid != this._id) |
|
39 return |
|
40 let req = this.getRequest(msg.requestID); |
|
41 if (!req) |
|
42 return; |
|
43 let app = msg.app; |
|
44 switch (aMessage.name) { |
|
45 case "Webapps:Install:Return:OK": |
|
46 this.removeMessageListeners("Webapps:Install:Return:KO"); |
|
47 Services.DOMRequest.fireSuccess(req, createApplicationObject(this._window, app)); |
|
48 cpmm.sendAsyncMessage("Webapps:Install:Return:Ack", |
|
49 { manifestURL : app.manifestURL }); |
|
50 break; |
|
51 case "Webapps:Install:Return:KO": |
|
52 this.removeMessageListeners(aMessage.name); |
|
53 Services.DOMRequest.fireError(req, msg.error || "DENIED"); |
|
54 break; |
|
55 case "Webapps:GetSelf:Return:OK": |
|
56 this.removeMessageListeners(aMessage.name); |
|
57 if (msg.apps.length) { |
|
58 app = msg.apps[0]; |
|
59 Services.DOMRequest.fireSuccess(req, createApplicationObject(this._window, app)); |
|
60 } else { |
|
61 Services.DOMRequest.fireSuccess(req, null); |
|
62 } |
|
63 break; |
|
64 case "Webapps:CheckInstalled:Return:OK": |
|
65 this.removeMessageListeners(aMessage.name); |
|
66 Services.DOMRequest.fireSuccess(req, msg.app); |
|
67 break; |
|
68 case "Webapps:GetInstalled:Return:OK": |
|
69 this.removeMessageListeners(aMessage.name); |
|
70 Services.DOMRequest.fireSuccess(req, convertAppsArray(msg.apps, this._window)); |
|
71 break; |
|
72 } |
|
73 this.removeRequest(msg.requestID); |
|
74 }, |
|
75 |
|
76 _getOrigin: function(aURL) { |
|
77 let uri = Services.io.newURI(aURL, null, null); |
|
78 return uri.prePath; |
|
79 }, |
|
80 |
|
81 // Checks that the URL scheme is appropriate (http or https) and |
|
82 // asynchronously fire an error on the DOM Request if it isn't. |
|
83 _validateURL: function(aURL, aRequest) { |
|
84 let uri; |
|
85 let res; |
|
86 |
|
87 try { |
|
88 uri = Services.io.newURI(aURL, null, null); |
|
89 if (uri.schemeIs("http") || uri.schemeIs("https")) { |
|
90 res = uri.spec; |
|
91 } |
|
92 } catch(e) { |
|
93 Services.DOMRequest.fireErrorAsync(aRequest, "INVALID_URL"); |
|
94 return false; |
|
95 } |
|
96 |
|
97 // The scheme is incorrect, fire DOMRequest error. |
|
98 if (!res) { |
|
99 Services.DOMRequest.fireErrorAsync(aRequest, "INVALID_URL"); |
|
100 return false; |
|
101 } |
|
102 |
|
103 return uri.spec; |
|
104 }, |
|
105 |
|
106 // Checks that we run as a foreground page, and fire an error on the |
|
107 // DOM Request if we aren't. |
|
108 _ensureForeground: function(aRequest) { |
|
109 let docShell = this._window.QueryInterface(Ci.nsIInterfaceRequestor) |
|
110 .getInterface(Ci.nsIWebNavigation) |
|
111 .QueryInterface(Ci.nsIDocShell); |
|
112 if (docShell.isActive) { |
|
113 return true; |
|
114 } |
|
115 |
|
116 Services.DOMRequest.fireErrorAsync(aRequest, "BACKGROUND_APP"); |
|
117 return false; |
|
118 }, |
|
119 |
|
120 _prepareInstall: function(aURL, aRequest, aParams, isPackage) { |
|
121 let installURL = this._window.location.href; |
|
122 let requestID = this.getRequestId(aRequest); |
|
123 let receipts = (aParams && aParams.receipts && |
|
124 Array.isArray(aParams.receipts)) ? aParams.receipts |
|
125 : []; |
|
126 let categories = (aParams && aParams.categories && |
|
127 Array.isArray(aParams.categories)) ? aParams.categories |
|
128 : []; |
|
129 |
|
130 let principal = this._window.document.nodePrincipal; |
|
131 |
|
132 return { app: { |
|
133 installOrigin: this._getOrigin(installURL), |
|
134 origin: this._getOrigin(aURL), |
|
135 manifestURL: aURL, |
|
136 receipts: receipts, |
|
137 categories: categories |
|
138 }, |
|
139 |
|
140 from: installURL, |
|
141 oid: this._id, |
|
142 requestID: requestID, |
|
143 appId: principal.appId, |
|
144 isBrowser: principal.isInBrowserElement, |
|
145 isPackage: isPackage |
|
146 }; |
|
147 }, |
|
148 |
|
149 // mozIDOMApplicationRegistry implementation |
|
150 |
|
151 install: function(aURL, aParams) { |
|
152 let request = this.createRequest(); |
|
153 |
|
154 let uri = this._validateURL(aURL, request); |
|
155 |
|
156 if (uri && this._ensureForeground(request)) { |
|
157 this.addMessageListeners("Webapps:Install:Return:KO"); |
|
158 cpmm.sendAsyncMessage("Webapps:Install", |
|
159 this._prepareInstall(uri, request, aParams, false)); |
|
160 } |
|
161 |
|
162 return request; |
|
163 }, |
|
164 |
|
165 getSelf: function() { |
|
166 let request = this.createRequest(); |
|
167 this.addMessageListeners("Webapps:GetSelf:Return:OK"); |
|
168 cpmm.sendAsyncMessage("Webapps:GetSelf", { origin: this._getOrigin(this._window.location.href), |
|
169 appId: this._window.document.nodePrincipal.appId, |
|
170 oid: this._id, |
|
171 requestID: this.getRequestId(request) }); |
|
172 return request; |
|
173 }, |
|
174 |
|
175 checkInstalled: function(aManifestURL) { |
|
176 let manifestURL = Services.io.newURI(aManifestURL, null, this._window.document.baseURIObject); |
|
177 this._window.document.nodePrincipal.checkMayLoad(manifestURL, true, false); |
|
178 |
|
179 let request = this.createRequest(); |
|
180 |
|
181 this.addMessageListeners("Webapps:CheckInstalled:Return:OK"); |
|
182 cpmm.sendAsyncMessage("Webapps:CheckInstalled", { origin: this._getOrigin(this._window.location.href), |
|
183 manifestURL: manifestURL.spec, |
|
184 oid: this._id, |
|
185 requestID: this.getRequestId(request) }); |
|
186 return request; |
|
187 }, |
|
188 |
|
189 getInstalled: function() { |
|
190 let request = this.createRequest(); |
|
191 this.addMessageListeners("Webapps:GetInstalled:Return:OK"); |
|
192 cpmm.sendAsyncMessage("Webapps:GetInstalled", { origin: this._getOrigin(this._window.location.href), |
|
193 oid: this._id, |
|
194 requestID: this.getRequestId(request) }); |
|
195 return request; |
|
196 }, |
|
197 |
|
198 get mgmt() { |
|
199 if (!this.hasMgmtPrivilege) { |
|
200 return null; |
|
201 } |
|
202 |
|
203 if (!this._mgmt) |
|
204 this._mgmt = new WebappsApplicationMgmt(this._window); |
|
205 return this._mgmt; |
|
206 }, |
|
207 |
|
208 uninit: function() { |
|
209 this._mgmt = null; |
|
210 cpmm.sendAsyncMessage("Webapps:UnregisterForMessages", |
|
211 ["Webapps:Install:Return:OK"]); |
|
212 }, |
|
213 |
|
214 installPackage: function(aURL, aParams) { |
|
215 let request = this.createRequest(); |
|
216 |
|
217 let uri = this._validateURL(aURL, request); |
|
218 |
|
219 if (uri && this._ensureForeground(request)) { |
|
220 this.addMessageListeners("Webapps:Install:Return:KO"); |
|
221 cpmm.sendAsyncMessage("Webapps:InstallPackage", |
|
222 this._prepareInstall(uri, request, aParams, true)); |
|
223 } |
|
224 |
|
225 return request; |
|
226 }, |
|
227 |
|
228 // nsIDOMGlobalPropertyInitializer implementation |
|
229 init: function(aWindow) { |
|
230 this.initDOMRequestHelper(aWindow, "Webapps:Install:Return:OK"); |
|
231 |
|
232 let util = this._window.QueryInterface(Ci.nsIInterfaceRequestor) |
|
233 .getInterface(Ci.nsIDOMWindowUtils); |
|
234 this._id = util.outerWindowID; |
|
235 cpmm.sendAsyncMessage("Webapps:RegisterForMessages", |
|
236 { messages: ["Webapps:Install:Return:OK"]}); |
|
237 |
|
238 let principal = aWindow.document.nodePrincipal; |
|
239 let perm = Services.perms |
|
240 .testExactPermissionFromPrincipal(principal, "webapps-manage"); |
|
241 |
|
242 // Only pages with the webapps-manage permission set can get access to |
|
243 // the mgmt object. |
|
244 this.hasMgmtPrivilege = perm == Ci.nsIPermissionManager.ALLOW_ACTION; |
|
245 }, |
|
246 |
|
247 classID: Components.ID("{fff440b3-fae2-45c1-bf03-3b5a2e432270}"), |
|
248 |
|
249 QueryInterface: XPCOMUtils.generateQI([Ci.nsISupportsWeakReference, |
|
250 Ci.nsIObserver, |
|
251 Ci.mozIDOMApplicationRegistry, |
|
252 Ci.mozIDOMApplicationRegistry2, |
|
253 Ci.nsIDOMGlobalPropertyInitializer]), |
|
254 |
|
255 classInfo: XPCOMUtils.generateCI({classID: Components.ID("{fff440b3-fae2-45c1-bf03-3b5a2e432270}"), |
|
256 contractID: "@mozilla.org/webapps;1", |
|
257 interfaces: [Ci.mozIDOMApplicationRegistry, |
|
258 Ci.mozIDOMApplicationRegistry2], |
|
259 flags: Ci.nsIClassInfo.DOM_OBJECT, |
|
260 classDescription: "Webapps Registry"}) |
|
261 } |
|
262 |
|
263 /** |
|
264 * mozIDOMApplication object |
|
265 */ |
|
266 |
|
267 // A simple cache for the wrapped manifests. |
|
268 let manifestCache = { |
|
269 _cache: { }, |
|
270 |
|
271 // Gets an entry from the cache, and populates the cache if needed. |
|
272 get: function mcache_get(aManifestURL, aManifest, aWindow, aInnerWindowID) { |
|
273 if (!(aManifestURL in this._cache)) { |
|
274 this._cache[aManifestURL] = { }; |
|
275 } |
|
276 |
|
277 let winObjs = this._cache[aManifestURL]; |
|
278 if (!(aInnerWindowID in winObjs)) { |
|
279 winObjs[aInnerWindowID] = Cu.cloneInto(aManifest, aWindow); |
|
280 } |
|
281 |
|
282 return winObjs[aInnerWindowID]; |
|
283 }, |
|
284 |
|
285 // Invalidates an entry in the cache. |
|
286 evict: function mcache_evict(aManifestURL, aInnerWindowID) { |
|
287 if (aManifestURL in this._cache) { |
|
288 let winObjs = this._cache[aManifestURL]; |
|
289 if (aInnerWindowID in winObjs) { |
|
290 delete winObjs[aInnerWindowID]; |
|
291 } |
|
292 |
|
293 if (Object.keys(winObjs).length == 0) { |
|
294 delete this._cache[aManifestURL]; |
|
295 } |
|
296 } |
|
297 }, |
|
298 |
|
299 observe: function(aSubject, aTopic, aData) { |
|
300 // Clear the cache on memory pressure. |
|
301 this._cache = { }; |
|
302 }, |
|
303 |
|
304 init: function() { |
|
305 Services.obs.addObserver(this, "memory-pressure", false); |
|
306 } |
|
307 }; |
|
308 |
|
309 function createApplicationObject(aWindow, aApp) { |
|
310 let app = Cc["@mozilla.org/webapps/application;1"].createInstance(Ci.mozIDOMApplication); |
|
311 app.wrappedJSObject.init(aWindow, aApp); |
|
312 return app; |
|
313 } |
|
314 |
|
315 function WebappsApplication() { |
|
316 this.wrappedJSObject = this; |
|
317 } |
|
318 |
|
319 WebappsApplication.prototype = { |
|
320 __proto__: DOMRequestIpcHelper.prototype, |
|
321 |
|
322 init: function(aWindow, aApp) { |
|
323 this._window = aWindow; |
|
324 let principal = this._window.document.nodePrincipal; |
|
325 this._appStatus = principal.appStatus; |
|
326 this.origin = aApp.origin; |
|
327 this._manifest = aApp.manifest; |
|
328 this._updateManifest = aApp.updateManifest; |
|
329 this.manifestURL = aApp.manifestURL; |
|
330 this.receipts = aApp.receipts; |
|
331 this.installOrigin = aApp.installOrigin; |
|
332 this.installTime = aApp.installTime; |
|
333 this.installState = aApp.installState || "installed"; |
|
334 this.removable = aApp.removable; |
|
335 this.lastUpdateCheck = aApp.lastUpdateCheck ? aApp.lastUpdateCheck |
|
336 : Date.now(); |
|
337 this.updateTime = aApp.updateTime ? aApp.updateTime |
|
338 : aApp.installTime; |
|
339 this.progress = NaN; |
|
340 this.downloadAvailable = aApp.downloadAvailable; |
|
341 this.downloading = aApp.downloading; |
|
342 this.readyToApplyDownload = aApp.readyToApplyDownload; |
|
343 this.downloadSize = aApp.downloadSize || 0; |
|
344 |
|
345 this._onprogress = null; |
|
346 this._ondownloadsuccess = null; |
|
347 this._ondownloaderror = null; |
|
348 this._ondownloadavailable = null; |
|
349 this._ondownloadapplied = null; |
|
350 |
|
351 this._downloadError = null; |
|
352 |
|
353 this.initDOMRequestHelper(aWindow, [ |
|
354 { name: "Webapps:CheckForUpdate:Return:KO", weakRef: true }, |
|
355 { name: "Webapps:Connect:Return:OK", weakRef: true }, |
|
356 { name: "Webapps:Connect:Return:KO", weakRef: true }, |
|
357 { name: "Webapps:FireEvent", weakRef: true }, |
|
358 { name: "Webapps:GetConnections:Return:OK", weakRef: true }, |
|
359 { name: "Webapps:UpdateState", weakRef: true } |
|
360 ]); |
|
361 |
|
362 cpmm.sendAsyncMessage("Webapps:RegisterForMessages", { |
|
363 messages: ["Webapps:FireEvent", |
|
364 "Webapps:UpdateState"], |
|
365 app: { |
|
366 id: this.id, |
|
367 manifestURL: this.manifestURL, |
|
368 installState: this.installState, |
|
369 downloading: this.downloading |
|
370 } |
|
371 }); |
|
372 }, |
|
373 |
|
374 get manifest() { |
|
375 return manifestCache.get(this.manifestURL, |
|
376 this._manifest, |
|
377 this._window, |
|
378 this.innerWindowID); |
|
379 }, |
|
380 |
|
381 get updateManifest() { |
|
382 return this.updateManifest = |
|
383 this._updateManifest ? Cu.cloneInto(this._updateManifest, this._window) |
|
384 : null; |
|
385 }, |
|
386 |
|
387 set onprogress(aCallback) { |
|
388 this._onprogress = aCallback; |
|
389 }, |
|
390 |
|
391 get onprogress() { |
|
392 return this._onprogress; |
|
393 }, |
|
394 |
|
395 set ondownloadsuccess(aCallback) { |
|
396 this._ondownloadsuccess = aCallback; |
|
397 }, |
|
398 |
|
399 get ondownloadsuccess() { |
|
400 return this._ondownloadsuccess; |
|
401 }, |
|
402 |
|
403 set ondownloaderror(aCallback) { |
|
404 this._ondownloaderror = aCallback; |
|
405 }, |
|
406 |
|
407 get ondownloaderror() { |
|
408 return this._ondownloaderror; |
|
409 }, |
|
410 |
|
411 set ondownloadavailable(aCallback) { |
|
412 this._ondownloadavailable = aCallback; |
|
413 }, |
|
414 |
|
415 get ondownloadavailable() { |
|
416 return this._ondownloadavailable; |
|
417 }, |
|
418 |
|
419 set ondownloadapplied(aCallback) { |
|
420 this._ondownloadapplied = aCallback; |
|
421 }, |
|
422 |
|
423 get ondownloadapplied() { |
|
424 return this._ondownloadapplied; |
|
425 }, |
|
426 |
|
427 get downloadError() { |
|
428 return new this._window.DOMError(this._downloadError || ''); |
|
429 }, |
|
430 |
|
431 download: function() { |
|
432 cpmm.sendAsyncMessage("Webapps:Download", |
|
433 { manifestURL: this.manifestURL }); |
|
434 }, |
|
435 |
|
436 cancelDownload: function() { |
|
437 cpmm.sendAsyncMessage("Webapps:CancelDownload", |
|
438 { manifestURL: this.manifestURL }); |
|
439 }, |
|
440 |
|
441 checkForUpdate: function() { |
|
442 let request = this.createRequest(); |
|
443 |
|
444 cpmm.sendAsyncMessage("Webapps:CheckForUpdate", |
|
445 { manifestURL: this.manifestURL, |
|
446 oid: this._id, |
|
447 requestID: this.getRequestId(request) }); |
|
448 return request; |
|
449 }, |
|
450 |
|
451 launch: function(aStartPoint) { |
|
452 let request = this.createRequest(); |
|
453 this.addMessageListeners(["Webapps:Launch:Return:OK", |
|
454 "Webapps:Launch:Return:KO"]); |
|
455 cpmm.sendAsyncMessage("Webapps:Launch", { origin: this.origin, |
|
456 manifestURL: this.manifestURL, |
|
457 startPoint: aStartPoint || "", |
|
458 oid: this._id, |
|
459 timestamp: Date.now(), |
|
460 requestID: this.getRequestId(request) }); |
|
461 return request; |
|
462 }, |
|
463 |
|
464 clearBrowserData: function() { |
|
465 let request = this.createRequest(); |
|
466 let browserChild = |
|
467 BrowserElementPromptService.getBrowserElementChildForWindow(this._window); |
|
468 if (browserChild) { |
|
469 this.addMessageListeners("Webapps:ClearBrowserData:Return"); |
|
470 browserChild.messageManager.sendAsyncMessage( |
|
471 "Webapps:ClearBrowserData", |
|
472 { manifestURL: this.manifestURL, |
|
473 oid: this._id, |
|
474 requestID: this.getRequestId(request) } |
|
475 ); |
|
476 } else { |
|
477 Services.DOMRequest.fireErrorAsync(request, "NO_CLEARABLE_BROWSER"); |
|
478 } |
|
479 return request; |
|
480 }, |
|
481 |
|
482 connect: function(aKeyword, aRules) { |
|
483 return this.createPromise(function (aResolve, aReject) { |
|
484 cpmm.sendAsyncMessage("Webapps:Connect", |
|
485 { keyword: aKeyword, |
|
486 rules: aRules, |
|
487 manifestURL: this.manifestURL, |
|
488 outerWindowID: this._id, |
|
489 requestID: this.getPromiseResolverId({ |
|
490 resolve: aResolve, |
|
491 reject: aReject |
|
492 })}); |
|
493 }.bind(this)); |
|
494 }, |
|
495 |
|
496 getConnections: function() { |
|
497 return this.createPromise(function (aResolve, aReject) { |
|
498 cpmm.sendAsyncMessage("Webapps:GetConnections", |
|
499 { manifestURL: this.manifestURL, |
|
500 outerWindowID: this._id, |
|
501 requestID: this.getPromiseResolverId({ |
|
502 resolve: aResolve, |
|
503 reject: aReject |
|
504 })}); |
|
505 }.bind(this)); |
|
506 }, |
|
507 |
|
508 addReceipt: function(receipt) { |
|
509 let request = this.createRequest(); |
|
510 |
|
511 this.addMessageListeners(["Webapps:AddReceipt:Return:OK", |
|
512 "Webapps:AddReceipt:Return:KO"]); |
|
513 |
|
514 cpmm.sendAsyncMessage("Webapps:AddReceipt", { manifestURL: this.manifestURL, |
|
515 receipt: receipt, |
|
516 oid: this._id, |
|
517 requestID: this.getRequestId(request) }); |
|
518 |
|
519 return request; |
|
520 }, |
|
521 |
|
522 removeReceipt: function(receipt) { |
|
523 let request = this.createRequest(); |
|
524 |
|
525 this.addMessageListeners(["Webapps:RemoveReceipt:Return:OK", |
|
526 "Webapps:RemoveReceipt:Return:KO"]); |
|
527 |
|
528 cpmm.sendAsyncMessage("Webapps:RemoveReceipt", { manifestURL: this.manifestURL, |
|
529 receipt: receipt, |
|
530 oid: this._id, |
|
531 requestID: this.getRequestId(request) }); |
|
532 |
|
533 return request; |
|
534 }, |
|
535 |
|
536 replaceReceipt: function(oldReceipt, newReceipt) { |
|
537 let request = this.createRequest(); |
|
538 |
|
539 this.addMessageListeners(["Webapps:ReplaceReceipt:Return:OK", |
|
540 "Webapps:ReplaceReceipt:Return:KO"]); |
|
541 |
|
542 cpmm.sendAsyncMessage("Webapps:ReplaceReceipt", { manifestURL: this.manifestURL, |
|
543 newReceipt: newReceipt, |
|
544 oldReceipt: oldReceipt, |
|
545 oid: this._id, |
|
546 requestID: this.getRequestId(request) }); |
|
547 |
|
548 return request; |
|
549 }, |
|
550 |
|
551 uninit: function() { |
|
552 this._onprogress = null; |
|
553 cpmm.sendAsyncMessage("Webapps:UnregisterForMessages", [ |
|
554 "Webapps:FireEvent", |
|
555 "Webapps:UpdateState" |
|
556 ]); |
|
557 |
|
558 manifestCache.evict(this.manifestURL, this.innerWindowID); |
|
559 }, |
|
560 |
|
561 _fireEvent: function(aName) { |
|
562 let handler = this["_on" + aName]; |
|
563 if (handler) { |
|
564 let event = new this._window.MozApplicationEvent(aName, { |
|
565 application: this |
|
566 }); |
|
567 try { |
|
568 handler.handleEvent(event); |
|
569 } catch (ex) { |
|
570 dump("Event handler expection " + ex + "\n"); |
|
571 } |
|
572 } |
|
573 }, |
|
574 |
|
575 _updateState: function(aMsg) { |
|
576 if (aMsg.app) { |
|
577 for (let prop in aMsg.app) { |
|
578 this[prop] = aMsg.app[prop]; |
|
579 } |
|
580 } |
|
581 |
|
582 if (aMsg.error) { |
|
583 this._downloadError = aMsg.error; |
|
584 } |
|
585 |
|
586 if (aMsg.manifest) { |
|
587 this._manifest = aMsg.manifest; |
|
588 manifestCache.evict(this.manifestURL, this.innerWindowID); |
|
589 } |
|
590 }, |
|
591 |
|
592 receiveMessage: function(aMessage) { |
|
593 let msg = aMessage.json; |
|
594 let req; |
|
595 if (aMessage.name == "Webapps:Connect:Return:OK" || |
|
596 aMessage.name == "Webapps:Connect:Return:KO" || |
|
597 aMessage.name == "Webapps:GetConnections:Return:OK") { |
|
598 req = this.takePromiseResolver(msg.requestID); |
|
599 } else { |
|
600 req = this.takeRequest(msg.requestID); |
|
601 } |
|
602 |
|
603 // ondownload* callbacks should be triggered on all app instances |
|
604 if ((msg.oid != this._id || !req) && |
|
605 aMessage.name !== "Webapps:FireEvent" && |
|
606 aMessage.name !== "Webapps:UpdateState") { |
|
607 return; |
|
608 } |
|
609 |
|
610 switch (aMessage.name) { |
|
611 case "Webapps:Launch:Return:KO": |
|
612 this.removeMessageListeners(["Webapps:Launch:Return:OK", |
|
613 "Webapps:Launch:Return:KO"]); |
|
614 Services.DOMRequest.fireError(req, "APP_INSTALL_PENDING"); |
|
615 break; |
|
616 case "Webapps:Launch:Return:OK": |
|
617 this.removeMessageListeners(["Webapps:Launch:Return:OK", |
|
618 "Webapps:Launch:Return:KO"]); |
|
619 Services.DOMRequest.fireSuccess(req, null); |
|
620 break; |
|
621 case "Webapps:CheckForUpdate:Return:KO": |
|
622 Services.DOMRequest.fireError(req, msg.error); |
|
623 break; |
|
624 case "Webapps:FireEvent": |
|
625 if (msg.manifestURL != this.manifestURL) { |
|
626 return; |
|
627 } |
|
628 |
|
629 // The parent might ask childs to trigger more than one event in one |
|
630 // shot, so in order to avoid needless IPC we allow an array for the |
|
631 // 'eventType' IPC message field. |
|
632 if (!Array.isArray(msg.eventType)) { |
|
633 msg.eventType = [msg.eventType]; |
|
634 } |
|
635 |
|
636 msg.eventType.forEach((aEventType) => { |
|
637 if ("_on" + aEventType in this) { |
|
638 this._fireEvent(aEventType); |
|
639 } else { |
|
640 dump("Unsupported event type " + aEventType + "\n"); |
|
641 } |
|
642 }); |
|
643 |
|
644 if (req) { |
|
645 Services.DOMRequest.fireSuccess(req, this.manifestURL); |
|
646 } |
|
647 break; |
|
648 case "Webapps:UpdateState": |
|
649 if (msg.manifestURL != this.manifestURL) { |
|
650 return; |
|
651 } |
|
652 |
|
653 this._updateState(msg); |
|
654 break; |
|
655 case "Webapps:ClearBrowserData:Return": |
|
656 this.removeMessageListeners(aMessage.name); |
|
657 Services.DOMRequest.fireSuccess(req, null); |
|
658 break; |
|
659 case "Webapps:Connect:Return:OK": |
|
660 let messagePorts = []; |
|
661 msg.messagePortIDs.forEach((aPortID) => { |
|
662 let port = new this._window.MozInterAppMessagePort(aPortID); |
|
663 messagePorts.push(port); |
|
664 }); |
|
665 req.resolve(messagePorts); |
|
666 break; |
|
667 case "Webapps:Connect:Return:KO": |
|
668 req.reject("No connections registered"); |
|
669 break; |
|
670 case "Webapps:GetConnections:Return:OK": |
|
671 let connections = []; |
|
672 msg.connections.forEach((aConnection) => { |
|
673 let connection = |
|
674 new this._window.MozInterAppConnection(aConnection.keyword, |
|
675 aConnection.pubAppManifestURL, |
|
676 aConnection.subAppManifestURL); |
|
677 connections.push(connection); |
|
678 }); |
|
679 req.resolve(connections); |
|
680 break; |
|
681 case "Webapps:AddReceipt:Return:OK": |
|
682 this.removeMessageListeners(["Webapps:AddReceipt:Return:OK", |
|
683 "Webapps:AddReceipt:Return:KO"]); |
|
684 this.receipts = msg.receipts; |
|
685 Services.DOMRequest.fireSuccess(req, null); |
|
686 break; |
|
687 case "Webapps:AddReceipt:Return:KO": |
|
688 this.removeMessageListeners(["Webapps:AddReceipt:Return:OK", |
|
689 "Webapps:AddReceipt:Return:KO"]); |
|
690 Services.DOMRequest.fireError(req, msg.error); |
|
691 break; |
|
692 case "Webapps:RemoveReceipt:Return:OK": |
|
693 this.removeMessageListeners(["Webapps:RemoveReceipt:Return:OK", |
|
694 "Webapps:RemoveReceipt:Return:KO"]); |
|
695 this.receipts = msg.receipts; |
|
696 Services.DOMRequest.fireSuccess(req, null); |
|
697 break; |
|
698 case "Webapps:RemoveReceipt:Return:KO": |
|
699 this.removeMessageListeners(["Webapps:RemoveReceipt:Return:OK", |
|
700 "Webapps:RemoveReceipt:Return:KO"]); |
|
701 Services.DOMRequest.fireError(req, msg.error); |
|
702 break; |
|
703 case "Webapps:ReplaceReceipt:Return:OK": |
|
704 this.removeMessageListeners(["Webapps:ReplaceReceipt:Return:OK", |
|
705 "Webapps:ReplaceReceipt:Return:KO"]); |
|
706 this.receipts = msg.receipts; |
|
707 Services.DOMRequest.fireSuccess(req, null); |
|
708 break; |
|
709 case "Webapps:ReplaceReceipt:Return:KO": |
|
710 this.removeMessageListeners(["Webapps:ReplaceReceipt:Return:OK", |
|
711 "Webapps:ReplaceReceipt:Return:KO"]); |
|
712 Services.DOMRequest.fireError(req, msg.error); |
|
713 break; |
|
714 } |
|
715 }, |
|
716 |
|
717 classID: Components.ID("{723ed303-7757-4fb0-b261-4f78b1f6bd22}"), |
|
718 |
|
719 QueryInterface: XPCOMUtils.generateQI([Ci.mozIDOMApplication, |
|
720 Ci.nsISupportsWeakReference, |
|
721 Ci.nsIObserver]), |
|
722 |
|
723 classInfo: XPCOMUtils.generateCI({classID: Components.ID("{723ed303-7757-4fb0-b261-4f78b1f6bd22}"), |
|
724 contractID: "@mozilla.org/webapps/application;1", |
|
725 interfaces: [Ci.mozIDOMApplication], |
|
726 flags: Ci.nsIClassInfo.DOM_OBJECT, |
|
727 classDescription: "Webapps Application"}) |
|
728 } |
|
729 |
|
730 /** |
|
731 * mozIDOMApplicationMgmt object |
|
732 */ |
|
733 function WebappsApplicationMgmt(aWindow) { |
|
734 this.initDOMRequestHelper(aWindow, ["Webapps:GetAll:Return:OK", |
|
735 "Webapps:GetAll:Return:KO", |
|
736 "Webapps:Uninstall:Return:OK", |
|
737 "Webapps:Uninstall:Broadcast:Return:OK", |
|
738 "Webapps:Uninstall:Return:KO", |
|
739 "Webapps:Install:Return:OK", |
|
740 "Webapps:GetNotInstalled:Return:OK"]); |
|
741 |
|
742 cpmm.sendAsyncMessage("Webapps:RegisterForMessages", |
|
743 { |
|
744 messages: ["Webapps:Install:Return:OK", |
|
745 "Webapps:Uninstall:Return:OK", |
|
746 "Webapps:Uninstall:Broadcast:Return:OK"] |
|
747 } |
|
748 ); |
|
749 |
|
750 this._oninstall = null; |
|
751 this._onuninstall = null; |
|
752 } |
|
753 |
|
754 WebappsApplicationMgmt.prototype = { |
|
755 __proto__: DOMRequestIpcHelper.prototype, |
|
756 __exposedProps__: { |
|
757 applyDownload: "r", |
|
758 getAll: "r", |
|
759 getNotInstalled: "r", |
|
760 oninstall: "rw", |
|
761 onuninstall: "rw" |
|
762 }, |
|
763 |
|
764 uninit: function() { |
|
765 this._oninstall = null; |
|
766 this._onuninstall = null; |
|
767 cpmm.sendAsyncMessage("Webapps:UnregisterForMessages", |
|
768 ["Webapps:Install:Return:OK", |
|
769 "Webapps:Uninstall:Return:OK", |
|
770 "Webapps:Uninstall:Broadcast:Return:OK"]); |
|
771 }, |
|
772 |
|
773 applyDownload: function(aApp) { |
|
774 if (!aApp.readyToApplyDownload) { |
|
775 return; |
|
776 } |
|
777 |
|
778 cpmm.sendAsyncMessage("Webapps:ApplyDownload", |
|
779 { manifestURL: aApp.manifestURL }); |
|
780 }, |
|
781 |
|
782 uninstall: function(aApp) { |
|
783 dump("-- webapps.js uninstall " + aApp.manifestURL + "\n"); |
|
784 let request = this.createRequest(); |
|
785 cpmm.sendAsyncMessage("Webapps:Uninstall", { origin: aApp.origin, |
|
786 manifestURL: aApp.manifestURL, |
|
787 oid: this._id, |
|
788 requestID: this.getRequestId(request) }); |
|
789 return request; |
|
790 }, |
|
791 |
|
792 getAll: function() { |
|
793 let request = this.createRequest(); |
|
794 cpmm.sendAsyncMessage("Webapps:GetAll", { oid: this._id, |
|
795 requestID: this.getRequestId(request) }); |
|
796 return request; |
|
797 }, |
|
798 |
|
799 getNotInstalled: function() { |
|
800 let request = this.createRequest(); |
|
801 cpmm.sendAsyncMessage("Webapps:GetNotInstalled", { oid: this._id, |
|
802 requestID: this.getRequestId(request) }); |
|
803 return request; |
|
804 }, |
|
805 |
|
806 get oninstall() { |
|
807 return this._oninstall; |
|
808 }, |
|
809 |
|
810 get onuninstall() { |
|
811 return this._onuninstall; |
|
812 }, |
|
813 |
|
814 set oninstall(aCallback) { |
|
815 this._oninstall = aCallback; |
|
816 }, |
|
817 |
|
818 set onuninstall(aCallback) { |
|
819 this._onuninstall = aCallback; |
|
820 }, |
|
821 |
|
822 receiveMessage: function(aMessage) { |
|
823 var msg = aMessage.json; |
|
824 let req = this.getRequest(msg.requestID); |
|
825 // We want Webapps:Install:Return:OK and Webapps:Uninstall:Broadcast:Return:OK |
|
826 // to be broadcasted to all instances of mozApps.mgmt. |
|
827 if (!((msg.oid == this._id && req) || |
|
828 aMessage.name == "Webapps:Install:Return:OK" || |
|
829 aMessage.name == "Webapps:Uninstall:Broadcast:Return:OK")) { |
|
830 return; |
|
831 } |
|
832 switch (aMessage.name) { |
|
833 case "Webapps:GetAll:Return:OK": |
|
834 Services.DOMRequest.fireSuccess(req, convertAppsArray(msg.apps, this._window)); |
|
835 break; |
|
836 case "Webapps:GetAll:Return:KO": |
|
837 Services.DOMRequest.fireError(req, "DENIED"); |
|
838 break; |
|
839 case "Webapps:GetNotInstalled:Return:OK": |
|
840 Services.DOMRequest.fireSuccess(req, convertAppsArray(msg.apps, this._window)); |
|
841 break; |
|
842 case "Webapps:Install:Return:OK": |
|
843 if (this._oninstall) { |
|
844 let app = msg.app; |
|
845 let event = new this._window.MozApplicationEvent("applicationinstall", |
|
846 { application : createApplicationObject(this._window, app) }); |
|
847 this._oninstall.handleEvent(event); |
|
848 } |
|
849 break; |
|
850 case "Webapps:Uninstall:Broadcast:Return:OK": |
|
851 if (this._onuninstall) { |
|
852 let detail = { |
|
853 manifestURL: msg.manifestURL, |
|
854 origin: msg.origin |
|
855 }; |
|
856 let event = new this._window.MozApplicationEvent("applicationuninstall", |
|
857 { application : createApplicationObject(this._window, detail) }); |
|
858 this._onuninstall.handleEvent(event); |
|
859 } |
|
860 break; |
|
861 case "Webapps:Uninstall:Return:OK": |
|
862 Services.DOMRequest.fireSuccess(req, msg.origin); |
|
863 break; |
|
864 case "Webapps:Uninstall:Return:KO": |
|
865 Services.DOMRequest.fireError(req, "NOT_INSTALLED"); |
|
866 break; |
|
867 } |
|
868 if (aMessage.name !== "Webapps:Uninstall:Broadcast:Return:OK") { |
|
869 this.removeRequest(msg.requestID); |
|
870 } |
|
871 }, |
|
872 |
|
873 classID: Components.ID("{8c1bca96-266f-493a-8d57-ec7a95098c15}"), |
|
874 |
|
875 QueryInterface: XPCOMUtils.generateQI([Ci.mozIDOMApplicationMgmt, |
|
876 Ci.nsISupportsWeakReference, |
|
877 Ci.nsIObserver]), |
|
878 |
|
879 classInfo: XPCOMUtils.generateCI({classID: Components.ID("{8c1bca96-266f-493a-8d57-ec7a95098c15}"), |
|
880 contractID: "@mozilla.org/webapps/application-mgmt;1", |
|
881 interfaces: [Ci.mozIDOMApplicationMgmt], |
|
882 flags: Ci.nsIClassInfo.DOM_OBJECT, |
|
883 classDescription: "Webapps Application Mgmt"}) |
|
884 } |
|
885 |
|
886 manifestCache.init(); |
|
887 |
|
888 this.NSGetFactory = XPCOMUtils.generateNSGetFactory([WebappsRegistry, |
|
889 WebappsApplication]); |