Bumping gaia.json for 1 gaia revision(s) a=gaia-bump
[gecko.git] / browser / modules / WebappManager.jsm
blob51a806ee380151813d661cc8bfe79a66d73f6ef9
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-ask-uninstall", false);
35     Services.obs.addObserver(this, "webapps-launch", false);
36     Services.obs.addObserver(this, "webapps-uninstall", false);
37     cpmm.addMessageListener("Webapps:Install:Return:OK", this);
38     cpmm.addMessageListener("Webapps:Install:Return:KO", this);
39     cpmm.addMessageListener("Webapps:UpdateState", this);
40   },
42   uninit: function() {
43     Services.obs.removeObserver(this, "webapps-ask-install");
44     Services.obs.removeObserver(this, "webapps-ask-uninstall");
45     Services.obs.removeObserver(this, "webapps-launch");
46     Services.obs.removeObserver(this, "webapps-uninstall");
47     cpmm.removeMessageListener("Webapps:Install:Return:OK", this);
48     cpmm.removeMessageListener("Webapps:Install:Return:KO", this);
49     cpmm.removeMessageListener("Webapps:UpdateState", this);
50   },
52   receiveMessage: function(aMessage) {
53     let data = aMessage.data;
55     let manifestURL = data.manifestURL ||
56                       (data.app && data.app.manifestURL) ||
57                       data.manifest;
59     if (!this.installations[manifestURL]) {
60       return;
61     }
63     if (aMessage.name == "Webapps:UpdateState") {
64       if (data.error) {
65         this.installations[manifestURL].reject(data.error);
66       } else if (data.app.installState == "installed") {
67         this.installations[manifestURL].resolve();
68       }
69     } else if (aMessage.name == "Webapps:Install:Return:OK" &&
70                !data.isPackage) {
71       let manifest = new ManifestHelper(data.app.manifest,
72                                         data.app.origin,
73                                         data.app.manifestURL);
74       if (!manifest.appcache_path) {
75         this.installations[manifestURL].resolve();
76       }
77     } else if (aMessage.name == "Webapps:Install:Return:KO") {
78       this.installations[manifestURL].reject(data.error);
79     }
80   },
82   observe: function(aSubject, aTopic, aData) {
83     let data = JSON.parse(aData);
84     data.mm = aSubject;
86     let win;
87     switch(aTopic) {
88       case "webapps-ask-install":
89         win = this._getWindowForId(data.oid);
90         if (win && win.location.href == data.from) {
91           this.doInstall(data, win);
92         }
93         break;
94       case "webapps-ask-uninstall":
95         win = this._getWindowForId(data.windowId);
96         if (win && win.location.href == data.from) {
97           this.doUninstall(data, win);
98         }
99         break;
100       case "webapps-launch":
101         WebappOSUtils.launch(data);
102         break;
103       case "webapps-uninstall":
104         WebappOSUtils.uninstall(data);
105         break;
106     }
107   },
109   _getWindowForId: function(aId) {
110     let someWindow = Services.wm.getMostRecentWindow(null);
111     return someWindow && Services.wm.getOuterWindowWithId(aId);
112   },
114   doInstall: function(aData, aWindow) {
115     let browser = aWindow.QueryInterface(Ci.nsIInterfaceRequestor)
116                          .getInterface(Ci.nsIWebNavigation)
117                          .QueryInterface(Ci.nsIDocShell)
118                          .chromeEventHandler;
119     let chromeDoc = browser.ownerDocument;
120     let chromeWin = chromeDoc.defaultView;
121     let popupProgressContent =
122       chromeDoc.getElementById("webapps-install-progress-content");
124     let bundle = chromeWin.gNavigatorBundle;
126     let jsonManifest = aData.isPackage ? aData.app.updateManifest : aData.app.manifest;
128     let notification;
130     let mainAction = {
131       label: bundle.getString("webapps.install"),
132       accessKey: bundle.getString("webapps.install.accesskey"),
133       callback: () => {
134         notification.remove();
136         notification = chromeWin.PopupNotifications.
137                         show(browser,
138                              "webapps-install-progress",
139                              bundle.getString("webapps.install.inprogress"),
140                              "webapps-notification-icon");
142         let progressMeter = chromeDoc.createElement("progressmeter");
143         progressMeter.setAttribute("mode", "undetermined");
144         popupProgressContent.appendChild(progressMeter);
146         let manifestURL = aData.app.manifestURL;
148         let nativeApp = new NativeApp(aData.app, jsonManifest,
149                                       aData.app.categories);
151         this.installations[manifestURL] = Promise.defer();
152         this.installations[manifestURL].promise.then(() => {
153           notifyInstallSuccess(aData.app, nativeApp, bundle);
154         }, (error) => {
155           Cu.reportError("Error installing webapp: " + error);
156         }).then(() => {
157           popupProgressContent.removeChild(progressMeter);
158           delete this.installations[manifestURL];
159           if (Object.getOwnPropertyNames(this.installations).length == 0) {
160             notification.remove();
161           }
162         });
164         let localDir;
165         try {
166           localDir = nativeApp.createProfile();
167         } catch (ex) {
168           DOMApplicationRegistry.denyInstall(aData);
169           return;
170         }
172         DOMApplicationRegistry.confirmInstall(aData, localDir,
173           Task.async(function*(aApp, aManifest, aZipPath) {
174             try {
175               yield nativeApp.install(aApp, aManifest, aZipPath);
176             } catch (ex) {
177               Cu.reportError("Error installing webapp: " + ex);
178               throw ex;
179             }
180           })
181         );
182       }
183     };
185     let requestingURI = chromeWin.makeURI(aData.from);
186     let app = aData.app;
187     let manifest = new ManifestHelper(jsonManifest, app.origin, app.manifestURL);
189     let host;
190     try {
191       host = requestingURI.host;
192     } catch(e) {
193       host = requestingURI.spec;
194     }
196     let message = bundle.getFormattedString("webapps.requestInstall",
197                                             [manifest.name, host], 2);
199     notification = chromeWin.PopupNotifications.show(browser,
200                                                      "webapps-install",
201                                                      message,
202                                                      "webapps-notification-icon",
203                                                      mainAction);
205   },
207   doUninstall: function(aData, aWindow) {
208     let browser = aWindow.QueryInterface(Ci.nsIInterfaceRequestor)
209                          .getInterface(Ci.nsIWebNavigation)
210                          .QueryInterface(Ci.nsIDocShell)
211                          .chromeEventHandler;
212     let chromeDoc = browser.ownerDocument;
213     let chromeWin = chromeDoc.defaultView;
215     let bundle = chromeWin.gNavigatorBundle;
216     let jsonManifest = aData.app.manifest;
218     let notification;
220     let mainAction = {
221       label: bundle.getString("webapps.uninstall"),
222       accessKey: bundle.getString("webapps.uninstall.accesskey"),
223       callback: () => {
224         notification.remove();
225         DOMApplicationRegistry.confirmUninstall(aData);
226       }
227     };
229     let secondaryAction = {
230       label: bundle.getString("webapps.doNotUninstall"),
231       accessKey: bundle.getString("webapps.doNotUninstall.accesskey"),
232       callback: () => {
233         notification.remove();
234         DOMApplicationRegistry.denyUninstall(aData, "USER_DECLINED");
235       }
236     };
238     let manifest = new ManifestHelper(jsonManifest, aData.app.origin,
239                                       aData.app.manifestURL);
241     let message = bundle.getFormattedString("webapps.requestUninstall",
242                                             [manifest.name]);
244     notification = chromeWin.PopupNotifications.show(
245                      browser, "webapps-uninstall", message,
246                      "webapps-notification-icon",
247                      mainAction, [secondaryAction]);
248   }
251 function notifyInstallSuccess(aApp, aNativeApp, aBundle) {
252   let launcher = {
253     observe: function(aSubject, aTopic) {
254       if (aTopic == "alertclickcallback") {
255         WebappOSUtils.launch(aApp);
256       }
257     }
258   };
260   try {
261     let notifier = Cc["@mozilla.org/alerts-service;1"].
262                    getService(Ci.nsIAlertsService);
264     notifier.showAlertNotification(aNativeApp.iconURI.spec,
265                                    aBundle.getString("webapps.install.success"),
266                                    aNativeApp.appNameAsFilename,
267                                    true, null, launcher);
268   } catch (ex) {}