Bumping manifests a=b2g-bump
[gecko.git] / browser / modules / WebappManager.jsm
blobb33eadf10f023b4e3a0e68a3aaac252988c5ff2e
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",
27                                    "nsIMessageSender");
29 this.WebappManager = {
30   // List of promises for in-progress installations
31   installations: {},
33   init: function() {
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);
41   },
43   uninit: function() {
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);
51   },
53   receiveMessage: function(aMessage) {
54     let data = aMessage.data;
56     let manifestURL = data.manifestURL ||
57                       (data.app && data.app.manifestURL) ||
58                       data.manifest;
60     if (!this.installations[manifestURL]) {
61       return;
62     }
64     if (aMessage.name == "Webapps:UpdateState") {
65       if (data.error) {
66         this.installations[manifestURL].reject(data.error);
67       } else if (data.app.installState == "installed") {
68         this.installations[manifestURL].resolve();
69       }
70     } else if (aMessage.name == "Webapps:Install:Return:OK" &&
71                !data.isPackage) {
72       let manifest = new ManifestHelper(data.app.manifest,
73                                         data.app.origin,
74                                         data.app.manifestURL);
75       if (!manifest.appcache_path) {
76         this.installations[manifestURL].resolve();
77       }
78     } else if (aMessage.name == "Webapps:Install:Return:KO") {
79       this.installations[manifestURL].reject(data.error);
80     }
81   },
83   observe: function(aSubject, aTopic, aData) {
84     let data = JSON.parse(aData);
85     data.mm = aSubject;
87     let win;
88     switch(aTopic) {
89       case "webapps-ask-install":
90         win = this._getWindowForId(data.oid);
91         if (win && win.location.href == data.from) {
92           this.doInstall(data, win);
93         }
94         break;
95       case "webapps-ask-uninstall":
96         win = this._getWindowForId(data.windowId);
97         if (win && win.location.href == data.from) {
98           this.doUninstall(data, win);
99         }
100         break;
101       case "webapps-launch":
102         WebappOSUtils.launch(data);
103         break;
104       case "webapps-uninstall":
105         WebappOSUtils.uninstall(data);
106         break;
107     }
108   },
110   _getWindowForId: function(aId) {
111     let someWindow = Services.wm.getMostRecentWindow(null);
112     return someWindow && Services.wm.getOuterWindowWithId(aId);
113   },
115   doInstall: function(aData, aWindow) {
116     let browser = aWindow.QueryInterface(Ci.nsIInterfaceRequestor)
117                          .getInterface(Ci.nsIWebNavigation)
118                          .QueryInterface(Ci.nsIDocShell)
119                          .chromeEventHandler;
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;
129     let notification;
131     let mainAction = {
132       label: bundle.getString("webapps.install"),
133       accessKey: bundle.getString("webapps.install.accesskey"),
134       callback: () => {
135         notification.remove();
137         notification = chromeWin.PopupNotifications.
138                         show(browser,
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));
156         }, (error) => {
157           Cu.reportError("Error installing webapp: " + error);
158         }).then(() => {
159           popupProgressContent.removeChild(progressMeter);
160           delete this.installations[manifestURL];
161           if (Object.getOwnPropertyNames(this.installations).length == 0) {
162             notification.remove();
163           }
164         });
166         let localDir;
167         try {
168           localDir = nativeApp.createProfile();
169         } catch (ex) {
170           DOMApplicationRegistry.denyInstall(aData);
171           return;
172         }
174         DOMApplicationRegistry.confirmInstall(aData, localDir,
175           Task.async(function*(aApp, aManifest, aZipPath) {
176             try {
177               yield nativeApp.install(aApp, aManifest, aZipPath);
178             } catch (ex) {
179               Cu.reportError("Error installing webapp: " + ex);
180               throw ex;
181             }
182           })
183         );
184       }
185     };
187     let requestingURI = chromeWin.makeURI(aData.from);
188     let app = aData.app;
189     let manifest = new ManifestHelper(jsonManifest, app.origin, app.manifestURL);
191     let host;
192     try {
193       host = requestingURI.host;
194     } catch(e) {
195       host = requestingURI.spec;
196     }
198     let message = bundle.getFormattedString("webapps.requestInstall",
199                                             [manifest.name, host], 2);
201     notification = chromeWin.PopupNotifications.show(browser,
202                                                      "webapps-install",
203                                                      message,
204                                                      "webapps-notification-icon",
205                                                      mainAction);
207   },
209   doUninstall: function(aData, aWindow) {
210     let browser = aWindow.QueryInterface(Ci.nsIInterfaceRequestor)
211                          .getInterface(Ci.nsIWebNavigation)
212                          .QueryInterface(Ci.nsIDocShell)
213                          .chromeEventHandler;
214     let chromeDoc = browser.ownerDocument;
215     let chromeWin = chromeDoc.defaultView;
217     let bundle = chromeWin.gNavigatorBundle;
218     let jsonManifest = aData.app.manifest;
220     let notification;
222     let mainAction = {
223       label: bundle.getString("webapps.uninstall"),
224       accessKey: bundle.getString("webapps.uninstall.accesskey"),
225       callback: () => {
226         notification.remove();
227         DOMApplicationRegistry.confirmUninstall(aData);
228       }
229     };
231     let secondaryAction = {
232       label: bundle.getString("webapps.doNotUninstall"),
233       accessKey: bundle.getString("webapps.doNotUninstall.accesskey"),
234       callback: () => {
235         notification.remove();
236         DOMApplicationRegistry.denyUninstall(aData, "USER_DECLINED");
237       }
238     };
240     let manifest = new ManifestHelper(jsonManifest, aData.app.origin,
241                                       aData.app.manifestURL);
243     let message = bundle.getFormattedString("webapps.requestUninstall",
244                                             [manifest.name]);
246     notification = chromeWin.PopupNotifications.show(
247                      browser, "webapps-uninstall", message,
248                      "webapps-notification-icon",
249                      mainAction, [secondaryAction]);
250   }
253 function notifyInstallSuccess(aApp, aNativeApp, aBundle, aInPrivateBrowsing) {
254   let launcher = {
255     observe: function(aSubject, aTopic) {
256       if (aTopic == "alertclickcallback") {
257         WebappOSUtils.launch(aApp);
258       }
259     }
260   };
262   try {
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,
270                                    aInPrivateBrowsing);
271   } catch (ex) {}