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");
17 Cu.import("resource://gre/modules/PrivateBrowsingUtils.jsm");
19 XPCOMUtils.defineLazyModuleGetter(this, "NativeApp",
20 "resource://gre/modules/NativeApp.jsm");
22 XPCOMUtils.defineLazyModuleGetter(this, "WebappOSUtils",
23 "resource://gre/modules/WebappOSUtils.jsm");
25 XPCOMUtils.defineLazyServiceGetter(this, "cpmm",
26 "@mozilla.org/childprocessmessagemanager;1",
29 this.WebappManager = {
30 // List of promises for in-progress installations
34 Services.obs.addObserver(this, "webapps-ask-install", false);
35 Services.obs.addObserver(this, "webapps-ask-uninstall", false);
36 Services.obs.addObserver(this, "webapps-launch", false);
37 Services.obs.addObserver(this, "webapps-uninstall", false);
38 cpmm.addMessageListener("Webapps:Install:Return:OK", this);
39 cpmm.addMessageListener("Webapps:Install:Return:KO", this);
40 cpmm.addMessageListener("Webapps:UpdateState", this);
44 Services.obs.removeObserver(this, "webapps-ask-install");
45 Services.obs.removeObserver(this, "webapps-ask-uninstall");
46 Services.obs.removeObserver(this, "webapps-launch");
47 Services.obs.removeObserver(this, "webapps-uninstall");
48 cpmm.removeMessageListener("Webapps:Install:Return:OK", this);
49 cpmm.removeMessageListener("Webapps:Install:Return:KO", this);
50 cpmm.removeMessageListener("Webapps:UpdateState", this);
53 receiveMessage: function(aMessage) {
54 let data = aMessage.data;
56 let manifestURL = data.manifestURL ||
57 (data.app && data.app.manifestURL) ||
60 if (!this.installations[manifestURL]) {
64 if (aMessage.name == "Webapps:UpdateState") {
66 this.installations[manifestURL].reject(data.error);
67 } else if (data.app.installState == "installed") {
68 this.installations[manifestURL].resolve();
70 } else if (aMessage.name == "Webapps:Install:Return:OK" &&
72 let manifest = new ManifestHelper(data.app.manifest,
74 data.app.manifestURL);
75 if (!manifest.appcache_path) {
76 this.installations[manifestURL].resolve();
78 } else if (aMessage.name == "Webapps:Install:Return:KO") {
79 this.installations[manifestURL].reject(data.error);
83 observe: function(aSubject, aTopic, aData) {
84 let data = JSON.parse(aData);
89 case "webapps-ask-install":
90 win = this._getWindowForId(data.oid);
91 if (win && win.location.href == data.from) {
92 this.doInstall(data, win);
95 case "webapps-ask-uninstall":
96 win = this._getWindowForId(data.windowId);
97 if (win && win.location.href == data.from) {
98 this.doUninstall(data, win);
101 case "webapps-launch":
102 WebappOSUtils.launch(data);
104 case "webapps-uninstall":
105 WebappOSUtils.uninstall(data);
110 _getWindowForId: function(aId) {
111 let someWindow = Services.wm.getMostRecentWindow(null);
112 return someWindow && Services.wm.getOuterWindowWithId(aId);
115 doInstall: function(aData, aWindow) {
116 let browser = aWindow.QueryInterface(Ci.nsIInterfaceRequestor)
117 .getInterface(Ci.nsIWebNavigation)
118 .QueryInterface(Ci.nsIDocShell)
120 let chromeDoc = browser.ownerDocument;
121 let chromeWin = chromeDoc.defaultView;
122 let popupProgressContent =
123 chromeDoc.getElementById("webapps-install-progress-content");
125 let bundle = chromeWin.gNavigatorBundle;
127 let jsonManifest = aData.isPackage ? aData.app.updateManifest : aData.app.manifest;
132 label: bundle.getString("webapps.install"),
133 accessKey: bundle.getString("webapps.install.accesskey"),
135 notification.remove();
137 notification = chromeWin.PopupNotifications.
139 "webapps-install-progress",
140 bundle.getString("webapps.install.inprogress"),
141 "webapps-notification-icon");
143 let progressMeter = chromeDoc.createElement("progressmeter");
144 progressMeter.setAttribute("mode", "undetermined");
145 popupProgressContent.appendChild(progressMeter);
147 let manifestURL = aData.app.manifestURL;
149 let nativeApp = new NativeApp(aData.app, jsonManifest,
150 aData.app.categories);
152 this.installations[manifestURL] = Promise.defer();
153 this.installations[manifestURL].promise.then(() => {
154 notifyInstallSuccess(aData.app, nativeApp, bundle,
155 PrivateBrowsingUtils.isWindowPrivate(aWindow));
157 Cu.reportError("Error installing webapp: " + error);
159 popupProgressContent.removeChild(progressMeter);
160 delete this.installations[manifestURL];
161 if (Object.getOwnPropertyNames(this.installations).length == 0) {
162 notification.remove();
168 localDir = nativeApp.createProfile();
170 DOMApplicationRegistry.denyInstall(aData);
174 DOMApplicationRegistry.confirmInstall(aData, localDir,
175 Task.async(function*(aApp, aManifest, aZipPath) {
177 yield nativeApp.install(aApp, aManifest, aZipPath);
179 Cu.reportError("Error installing webapp: " + ex);
187 let requestingURI = chromeWin.makeURI(aData.from);
189 let manifest = new ManifestHelper(jsonManifest, app.origin, app.manifestURL);
193 host = requestingURI.host;
195 host = requestingURI.spec;
198 let message = bundle.getFormattedString("webapps.requestInstall",
199 [manifest.name, host], 2);
201 notification = chromeWin.PopupNotifications.show(browser,
204 "webapps-notification-icon",
209 doUninstall: function(aData, aWindow) {
210 let browser = aWindow.QueryInterface(Ci.nsIInterfaceRequestor)
211 .getInterface(Ci.nsIWebNavigation)
212 .QueryInterface(Ci.nsIDocShell)
214 let chromeDoc = browser.ownerDocument;
215 let chromeWin = chromeDoc.defaultView;
217 let bundle = chromeWin.gNavigatorBundle;
218 let jsonManifest = aData.app.manifest;
223 label: bundle.getString("webapps.uninstall"),
224 accessKey: bundle.getString("webapps.uninstall.accesskey"),
226 notification.remove();
227 DOMApplicationRegistry.confirmUninstall(aData);
231 let secondaryAction = {
232 label: bundle.getString("webapps.doNotUninstall"),
233 accessKey: bundle.getString("webapps.doNotUninstall.accesskey"),
235 notification.remove();
236 DOMApplicationRegistry.denyUninstall(aData, "USER_DECLINED");
240 let manifest = new ManifestHelper(jsonManifest, aData.app.origin,
241 aData.app.manifestURL);
243 let message = bundle.getFormattedString("webapps.requestUninstall",
246 notification = chromeWin.PopupNotifications.show(
247 browser, "webapps-uninstall", message,
248 "webapps-notification-icon",
249 mainAction, [secondaryAction]);
253 function notifyInstallSuccess(aApp, aNativeApp, aBundle, aInPrivateBrowsing) {
255 observe: function(aSubject, aTopic) {
256 if (aTopic == "alertclickcallback") {
257 WebappOSUtils.launch(aApp);
263 let notifier = Cc["@mozilla.org/alerts-service;1"].
264 getService(Ci.nsIAlertsService);
266 notifier.showAlertNotification(aNativeApp.iconURI.spec,
267 aBundle.getString("webapps.install.success"),
268 aNativeApp.appNameAsFilename,
269 true, null, launcher, "", "", "", "", null,