Thu, 22 Jan 2015 13:21:57 +0100
Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6
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 this.EXPORTED_SYMBOLS = ["WebappManager"];
7 let Ci = Components.interfaces;
8 let Cc = Components.classes;
9 let Cu = Components.utils;
11 Cu.import("resource://gre/modules/Services.jsm");
12 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
13 Cu.import("resource://gre/modules/Webapps.jsm");
14 Cu.import("resource://gre/modules/AppsUtils.jsm");
15 Cu.import("resource://gre/modules/Task.jsm");
16 Cu.import("resource://gre/modules/Promise.jsm");
18 XPCOMUtils.defineLazyModuleGetter(this, "NativeApp",
19 "resource://gre/modules/NativeApp.jsm");
21 XPCOMUtils.defineLazyModuleGetter(this, "WebappOSUtils",
22 "resource://gre/modules/WebappOSUtils.jsm");
24 XPCOMUtils.defineLazyServiceGetter(this, "cpmm",
25 "@mozilla.org/childprocessmessagemanager;1",
26 "nsIMessageSender");
28 this.WebappManager = {
29 // List of promises for in-progress installations
30 installations: {},
32 init: function() {
33 Services.obs.addObserver(this, "webapps-ask-install", false);
34 Services.obs.addObserver(this, "webapps-launch", false);
35 Services.obs.addObserver(this, "webapps-uninstall", false);
36 cpmm.addMessageListener("Webapps:Install:Return:OK", this);
37 cpmm.addMessageListener("Webapps:Install:Return:KO", this);
38 cpmm.addMessageListener("Webapps:UpdateState", this);
39 },
41 uninit: function() {
42 Services.obs.removeObserver(this, "webapps-ask-install");
43 Services.obs.removeObserver(this, "webapps-launch");
44 Services.obs.removeObserver(this, "webapps-uninstall");
45 cpmm.removeMessageListener("Webapps:Install:Return:OK", this);
46 cpmm.removeMessageListener("Webapps:Install:Return:KO", this);
47 cpmm.removeMessageListener("Webapps:UpdateState", this);
48 },
50 receiveMessage: function(aMessage) {
51 let data = aMessage.data;
53 let manifestURL = data.manifestURL ||
54 (data.app && data.app.manifestURL) ||
55 data.manifest;
57 if (!this.installations[manifestURL]) {
58 return;
59 }
61 if (aMessage.name == "Webapps:UpdateState") {
62 if (data.error) {
63 this.installations[manifestURL].reject(data.error);
64 } else if (data.app.installState == "installed") {
65 this.installations[manifestURL].resolve();
66 }
67 } else if (aMessage.name == "Webapps:Install:Return:OK" &&
68 !data.isPackage) {
69 let manifest = new ManifestHelper(data.app.manifest, data.app.origin);
70 if (!manifest.appcache_path) {
71 this.installations[manifestURL].resolve();
72 }
73 } else if (aMessage.name == "Webapps:Install:Return:KO") {
74 this.installations[manifestURL].reject(data.error);
75 }
76 },
78 observe: function(aSubject, aTopic, aData) {
79 let data = JSON.parse(aData);
80 data.mm = aSubject;
82 switch(aTopic) {
83 case "webapps-ask-install":
84 let win = this._getWindowForId(data.oid);
85 if (win && win.location.href == data.from) {
86 this.doInstall(data, win);
87 }
88 break;
89 case "webapps-launch":
90 WebappOSUtils.launch(data);
91 break;
92 case "webapps-uninstall":
93 WebappOSUtils.uninstall(data);
94 break;
95 }
96 },
98 _getWindowForId: function(aId) {
99 let someWindow = Services.wm.getMostRecentWindow(null);
100 return someWindow && Services.wm.getOuterWindowWithId(aId);
101 },
103 doInstall: function(aData, aWindow) {
104 let browser = aWindow.QueryInterface(Ci.nsIInterfaceRequestor)
105 .getInterface(Ci.nsIWebNavigation)
106 .QueryInterface(Ci.nsIDocShell)
107 .chromeEventHandler;
108 let chromeDoc = browser.ownerDocument;
109 let chromeWin = chromeDoc.defaultView;
110 let popupProgressContent =
111 chromeDoc.getElementById("webapps-install-progress-content");
113 let bundle = chromeWin.gNavigatorBundle;
115 let jsonManifest = aData.isPackage ? aData.app.updateManifest : aData.app.manifest;
117 let notification;
119 let mainAction = {
120 label: bundle.getString("webapps.install"),
121 accessKey: bundle.getString("webapps.install.accesskey"),
122 callback: () => {
123 notification.remove();
125 notification = chromeWin.PopupNotifications.
126 show(browser,
127 "webapps-install-progress",
128 bundle.getString("webapps.install.inprogress"),
129 "webapps-notification-icon");
131 let progressMeter = chromeDoc.createElement("progressmeter");
132 progressMeter.setAttribute("mode", "undetermined");
133 popupProgressContent.appendChild(progressMeter);
135 let manifestURL = aData.app.manifestURL;
137 let cleanup = () => {
138 popupProgressContent.removeChild(progressMeter);
139 delete this.installations[manifestURL];
140 if (Object.getOwnPropertyNames(this.installations).length == 0) {
141 notification.remove();
142 }
143 };
145 this.installations[manifestURL] = Promise.defer();
146 this.installations[manifestURL].promise.then(null, (error) => {
147 Cu.reportError("Error installing webapp: " + error);
148 cleanup();
149 });
151 let nativeApp = new NativeApp(aData.app, jsonManifest,
152 aData.app.categories);
153 let localDir;
154 try {
155 localDir = nativeApp.createProfile();
156 } catch (ex) {
157 Cu.reportError("Error installing webapp: " + ex);
158 DOMApplicationRegistry.denyInstall(aData);
159 cleanup();
160 return;
161 }
163 DOMApplicationRegistry.confirmInstall(aData, localDir,
164 (aManifest, aZipPath) => Task.spawn((function*() {
165 try {
166 yield nativeApp.install(aManifest, aZipPath);
167 yield this.installations[manifestURL].promise;
168 notifyInstallSuccess(aData.app, nativeApp, bundle);
169 } catch (ex) {
170 Cu.reportError("Error installing webapp: " + ex);
171 // TODO: Notify user that the installation has failed
172 } finally {
173 cleanup();
174 }
175 }).bind(this))
176 );
177 }
178 };
180 let requestingURI = chromeWin.makeURI(aData.from);
181 let manifest = new ManifestHelper(jsonManifest, aData.app.origin);
183 let host;
184 try {
185 host = requestingURI.host;
186 } catch(e) {
187 host = requestingURI.spec;
188 }
190 let message = bundle.getFormattedString("webapps.requestInstall",
191 [manifest.name, host], 2);
193 notification = chromeWin.PopupNotifications.show(browser,
194 "webapps-install",
195 message,
196 "webapps-notification-icon",
197 mainAction);
199 }
200 }
202 function notifyInstallSuccess(aApp, aNativeApp, aBundle) {
203 let launcher = {
204 observe: function(aSubject, aTopic) {
205 if (aTopic == "alertclickcallback") {
206 WebappOSUtils.launch(aApp);
207 }
208 }
209 };
211 try {
212 let notifier = Cc["@mozilla.org/alerts-service;1"].
213 getService(Ci.nsIAlertsService);
215 notifier.showAlertNotification(aNativeApp.iconURI.spec,
216 aBundle.getString("webapps.install.success"),
217 aNativeApp.appNameAsFilename,
218 true, null, launcher);
219 } catch (ex) {}
220 }