Wed, 31 Dec 2014 06:55:50 +0100
Added tag UPSTREAM_283F7C6 for changeset ca08bd8f51b2
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/. */
5 const Cc = Components.classes;
6 const Ci = Components.interfaces;
7 const Cu = Components.utils;
8 const Cr = Components.results;
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");
16 XPCOMUtils.defineLazyServiceGetter(this, "cpmm",
17 "@mozilla.org/childprocessmessagemanager;1",
18 "nsIMessageSender");
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 }
27 return apps;
28 }
30 function WebappsRegistry() {
31 }
33 WebappsRegistry.prototype = {
34 __proto__: DOMRequestIpcHelper.prototype,
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 },
76 _getOrigin: function(aURL) {
77 let uri = Services.io.newURI(aURL, null, null);
78 return uri.prePath;
79 },
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;
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 }
97 // The scheme is incorrect, fire DOMRequest error.
98 if (!res) {
99 Services.DOMRequest.fireErrorAsync(aRequest, "INVALID_URL");
100 return false;
101 }
103 return uri.spec;
104 },
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 }
116 Services.DOMRequest.fireErrorAsync(aRequest, "BACKGROUND_APP");
117 return false;
118 },
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 : [];
130 let principal = this._window.document.nodePrincipal;
132 return { app: {
133 installOrigin: this._getOrigin(installURL),
134 origin: this._getOrigin(aURL),
135 manifestURL: aURL,
136 receipts: receipts,
137 categories: categories
138 },
140 from: installURL,
141 oid: this._id,
142 requestID: requestID,
143 appId: principal.appId,
144 isBrowser: principal.isInBrowserElement,
145 isPackage: isPackage
146 };
147 },
149 // mozIDOMApplicationRegistry implementation
151 install: function(aURL, aParams) {
152 let request = this.createRequest();
154 let uri = this._validateURL(aURL, request);
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 }
162 return request;
163 },
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 },
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);
179 let request = this.createRequest();
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 },
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 },
198 get mgmt() {
199 if (!this.hasMgmtPrivilege) {
200 return null;
201 }
203 if (!this._mgmt)
204 this._mgmt = new WebappsApplicationMgmt(this._window);
205 return this._mgmt;
206 },
208 uninit: function() {
209 this._mgmt = null;
210 cpmm.sendAsyncMessage("Webapps:UnregisterForMessages",
211 ["Webapps:Install:Return:OK"]);
212 },
214 installPackage: function(aURL, aParams) {
215 let request = this.createRequest();
217 let uri = this._validateURL(aURL, request);
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 }
225 return request;
226 },
228 // nsIDOMGlobalPropertyInitializer implementation
229 init: function(aWindow) {
230 this.initDOMRequestHelper(aWindow, "Webapps:Install:Return:OK");
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"]});
238 let principal = aWindow.document.nodePrincipal;
239 let perm = Services.perms
240 .testExactPermissionFromPrincipal(principal, "webapps-manage");
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 },
247 classID: Components.ID("{fff440b3-fae2-45c1-bf03-3b5a2e432270}"),
249 QueryInterface: XPCOMUtils.generateQI([Ci.nsISupportsWeakReference,
250 Ci.nsIObserver,
251 Ci.mozIDOMApplicationRegistry,
252 Ci.mozIDOMApplicationRegistry2,
253 Ci.nsIDOMGlobalPropertyInitializer]),
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 }
263 /**
264 * mozIDOMApplication object
265 */
267 // A simple cache for the wrapped manifests.
268 let manifestCache = {
269 _cache: { },
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 }
277 let winObjs = this._cache[aManifestURL];
278 if (!(aInnerWindowID in winObjs)) {
279 winObjs[aInnerWindowID] = Cu.cloneInto(aManifest, aWindow);
280 }
282 return winObjs[aInnerWindowID];
283 },
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 }
293 if (Object.keys(winObjs).length == 0) {
294 delete this._cache[aManifestURL];
295 }
296 }
297 },
299 observe: function(aSubject, aTopic, aData) {
300 // Clear the cache on memory pressure.
301 this._cache = { };
302 },
304 init: function() {
305 Services.obs.addObserver(this, "memory-pressure", false);
306 }
307 };
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 }
315 function WebappsApplication() {
316 this.wrappedJSObject = this;
317 }
319 WebappsApplication.prototype = {
320 __proto__: DOMRequestIpcHelper.prototype,
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;
345 this._onprogress = null;
346 this._ondownloadsuccess = null;
347 this._ondownloaderror = null;
348 this._ondownloadavailable = null;
349 this._ondownloadapplied = null;
351 this._downloadError = null;
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 ]);
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 },
374 get manifest() {
375 return manifestCache.get(this.manifestURL,
376 this._manifest,
377 this._window,
378 this.innerWindowID);
379 },
381 get updateManifest() {
382 return this.updateManifest =
383 this._updateManifest ? Cu.cloneInto(this._updateManifest, this._window)
384 : null;
385 },
387 set onprogress(aCallback) {
388 this._onprogress = aCallback;
389 },
391 get onprogress() {
392 return this._onprogress;
393 },
395 set ondownloadsuccess(aCallback) {
396 this._ondownloadsuccess = aCallback;
397 },
399 get ondownloadsuccess() {
400 return this._ondownloadsuccess;
401 },
403 set ondownloaderror(aCallback) {
404 this._ondownloaderror = aCallback;
405 },
407 get ondownloaderror() {
408 return this._ondownloaderror;
409 },
411 set ondownloadavailable(aCallback) {
412 this._ondownloadavailable = aCallback;
413 },
415 get ondownloadavailable() {
416 return this._ondownloadavailable;
417 },
419 set ondownloadapplied(aCallback) {
420 this._ondownloadapplied = aCallback;
421 },
423 get ondownloadapplied() {
424 return this._ondownloadapplied;
425 },
427 get downloadError() {
428 return new this._window.DOMError(this._downloadError || '');
429 },
431 download: function() {
432 cpmm.sendAsyncMessage("Webapps:Download",
433 { manifestURL: this.manifestURL });
434 },
436 cancelDownload: function() {
437 cpmm.sendAsyncMessage("Webapps:CancelDownload",
438 { manifestURL: this.manifestURL });
439 },
441 checkForUpdate: function() {
442 let request = this.createRequest();
444 cpmm.sendAsyncMessage("Webapps:CheckForUpdate",
445 { manifestURL: this.manifestURL,
446 oid: this._id,
447 requestID: this.getRequestId(request) });
448 return request;
449 },
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 },
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 },
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 },
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 },
508 addReceipt: function(receipt) {
509 let request = this.createRequest();
511 this.addMessageListeners(["Webapps:AddReceipt:Return:OK",
512 "Webapps:AddReceipt:Return:KO"]);
514 cpmm.sendAsyncMessage("Webapps:AddReceipt", { manifestURL: this.manifestURL,
515 receipt: receipt,
516 oid: this._id,
517 requestID: this.getRequestId(request) });
519 return request;
520 },
522 removeReceipt: function(receipt) {
523 let request = this.createRequest();
525 this.addMessageListeners(["Webapps:RemoveReceipt:Return:OK",
526 "Webapps:RemoveReceipt:Return:KO"]);
528 cpmm.sendAsyncMessage("Webapps:RemoveReceipt", { manifestURL: this.manifestURL,
529 receipt: receipt,
530 oid: this._id,
531 requestID: this.getRequestId(request) });
533 return request;
534 },
536 replaceReceipt: function(oldReceipt, newReceipt) {
537 let request = this.createRequest();
539 this.addMessageListeners(["Webapps:ReplaceReceipt:Return:OK",
540 "Webapps:ReplaceReceipt:Return:KO"]);
542 cpmm.sendAsyncMessage("Webapps:ReplaceReceipt", { manifestURL: this.manifestURL,
543 newReceipt: newReceipt,
544 oldReceipt: oldReceipt,
545 oid: this._id,
546 requestID: this.getRequestId(request) });
548 return request;
549 },
551 uninit: function() {
552 this._onprogress = null;
553 cpmm.sendAsyncMessage("Webapps:UnregisterForMessages", [
554 "Webapps:FireEvent",
555 "Webapps:UpdateState"
556 ]);
558 manifestCache.evict(this.manifestURL, this.innerWindowID);
559 },
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 },
575 _updateState: function(aMsg) {
576 if (aMsg.app) {
577 for (let prop in aMsg.app) {
578 this[prop] = aMsg.app[prop];
579 }
580 }
582 if (aMsg.error) {
583 this._downloadError = aMsg.error;
584 }
586 if (aMsg.manifest) {
587 this._manifest = aMsg.manifest;
588 manifestCache.evict(this.manifestURL, this.innerWindowID);
589 }
590 },
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 }
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 }
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 }
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 }
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 });
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 }
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 },
717 classID: Components.ID("{723ed303-7757-4fb0-b261-4f78b1f6bd22}"),
719 QueryInterface: XPCOMUtils.generateQI([Ci.mozIDOMApplication,
720 Ci.nsISupportsWeakReference,
721 Ci.nsIObserver]),
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 }
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"]);
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 );
750 this._oninstall = null;
751 this._onuninstall = null;
752 }
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 },
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 },
773 applyDownload: function(aApp) {
774 if (!aApp.readyToApplyDownload) {
775 return;
776 }
778 cpmm.sendAsyncMessage("Webapps:ApplyDownload",
779 { manifestURL: aApp.manifestURL });
780 },
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 },
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 },
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 },
806 get oninstall() {
807 return this._oninstall;
808 },
810 get onuninstall() {
811 return this._onuninstall;
812 },
814 set oninstall(aCallback) {
815 this._oninstall = aCallback;
816 },
818 set onuninstall(aCallback) {
819 this._onuninstall = aCallback;
820 },
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 },
873 classID: Components.ID("{8c1bca96-266f-493a-8d57-ec7a95098c15}"),
875 QueryInterface: XPCOMUtils.generateQI([Ci.mozIDOMApplicationMgmt,
876 Ci.nsISupportsWeakReference,
877 Ci.nsIObserver]),
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 }
886 manifestCache.init();
888 this.NSGetFactory = XPCOMUtils.generateNSGetFactory([WebappsRegistry,
889 WebappsApplication]);