1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/browser/modules/WebappManager.jsm Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,220 @@ 1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this file, 1.6 + * You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.7 + 1.8 +this.EXPORTED_SYMBOLS = ["WebappManager"]; 1.9 + 1.10 +let Ci = Components.interfaces; 1.11 +let Cc = Components.classes; 1.12 +let Cu = Components.utils; 1.13 + 1.14 +Cu.import("resource://gre/modules/Services.jsm"); 1.15 +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); 1.16 +Cu.import("resource://gre/modules/Webapps.jsm"); 1.17 +Cu.import("resource://gre/modules/AppsUtils.jsm"); 1.18 +Cu.import("resource://gre/modules/Task.jsm"); 1.19 +Cu.import("resource://gre/modules/Promise.jsm"); 1.20 + 1.21 +XPCOMUtils.defineLazyModuleGetter(this, "NativeApp", 1.22 + "resource://gre/modules/NativeApp.jsm"); 1.23 + 1.24 +XPCOMUtils.defineLazyModuleGetter(this, "WebappOSUtils", 1.25 + "resource://gre/modules/WebappOSUtils.jsm"); 1.26 + 1.27 +XPCOMUtils.defineLazyServiceGetter(this, "cpmm", 1.28 + "@mozilla.org/childprocessmessagemanager;1", 1.29 + "nsIMessageSender"); 1.30 + 1.31 +this.WebappManager = { 1.32 + // List of promises for in-progress installations 1.33 + installations: {}, 1.34 + 1.35 + init: function() { 1.36 + Services.obs.addObserver(this, "webapps-ask-install", false); 1.37 + Services.obs.addObserver(this, "webapps-launch", false); 1.38 + Services.obs.addObserver(this, "webapps-uninstall", false); 1.39 + cpmm.addMessageListener("Webapps:Install:Return:OK", this); 1.40 + cpmm.addMessageListener("Webapps:Install:Return:KO", this); 1.41 + cpmm.addMessageListener("Webapps:UpdateState", this); 1.42 + }, 1.43 + 1.44 + uninit: function() { 1.45 + Services.obs.removeObserver(this, "webapps-ask-install"); 1.46 + Services.obs.removeObserver(this, "webapps-launch"); 1.47 + Services.obs.removeObserver(this, "webapps-uninstall"); 1.48 + cpmm.removeMessageListener("Webapps:Install:Return:OK", this); 1.49 + cpmm.removeMessageListener("Webapps:Install:Return:KO", this); 1.50 + cpmm.removeMessageListener("Webapps:UpdateState", this); 1.51 + }, 1.52 + 1.53 + receiveMessage: function(aMessage) { 1.54 + let data = aMessage.data; 1.55 + 1.56 + let manifestURL = data.manifestURL || 1.57 + (data.app && data.app.manifestURL) || 1.58 + data.manifest; 1.59 + 1.60 + if (!this.installations[manifestURL]) { 1.61 + return; 1.62 + } 1.63 + 1.64 + if (aMessage.name == "Webapps:UpdateState") { 1.65 + if (data.error) { 1.66 + this.installations[manifestURL].reject(data.error); 1.67 + } else if (data.app.installState == "installed") { 1.68 + this.installations[manifestURL].resolve(); 1.69 + } 1.70 + } else if (aMessage.name == "Webapps:Install:Return:OK" && 1.71 + !data.isPackage) { 1.72 + let manifest = new ManifestHelper(data.app.manifest, data.app.origin); 1.73 + if (!manifest.appcache_path) { 1.74 + this.installations[manifestURL].resolve(); 1.75 + } 1.76 + } else if (aMessage.name == "Webapps:Install:Return:KO") { 1.77 + this.installations[manifestURL].reject(data.error); 1.78 + } 1.79 + }, 1.80 + 1.81 + observe: function(aSubject, aTopic, aData) { 1.82 + let data = JSON.parse(aData); 1.83 + data.mm = aSubject; 1.84 + 1.85 + switch(aTopic) { 1.86 + case "webapps-ask-install": 1.87 + let win = this._getWindowForId(data.oid); 1.88 + if (win && win.location.href == data.from) { 1.89 + this.doInstall(data, win); 1.90 + } 1.91 + break; 1.92 + case "webapps-launch": 1.93 + WebappOSUtils.launch(data); 1.94 + break; 1.95 + case "webapps-uninstall": 1.96 + WebappOSUtils.uninstall(data); 1.97 + break; 1.98 + } 1.99 + }, 1.100 + 1.101 + _getWindowForId: function(aId) { 1.102 + let someWindow = Services.wm.getMostRecentWindow(null); 1.103 + return someWindow && Services.wm.getOuterWindowWithId(aId); 1.104 + }, 1.105 + 1.106 + doInstall: function(aData, aWindow) { 1.107 + let browser = aWindow.QueryInterface(Ci.nsIInterfaceRequestor) 1.108 + .getInterface(Ci.nsIWebNavigation) 1.109 + .QueryInterface(Ci.nsIDocShell) 1.110 + .chromeEventHandler; 1.111 + let chromeDoc = browser.ownerDocument; 1.112 + let chromeWin = chromeDoc.defaultView; 1.113 + let popupProgressContent = 1.114 + chromeDoc.getElementById("webapps-install-progress-content"); 1.115 + 1.116 + let bundle = chromeWin.gNavigatorBundle; 1.117 + 1.118 + let jsonManifest = aData.isPackage ? aData.app.updateManifest : aData.app.manifest; 1.119 + 1.120 + let notification; 1.121 + 1.122 + let mainAction = { 1.123 + label: bundle.getString("webapps.install"), 1.124 + accessKey: bundle.getString("webapps.install.accesskey"), 1.125 + callback: () => { 1.126 + notification.remove(); 1.127 + 1.128 + notification = chromeWin.PopupNotifications. 1.129 + show(browser, 1.130 + "webapps-install-progress", 1.131 + bundle.getString("webapps.install.inprogress"), 1.132 + "webapps-notification-icon"); 1.133 + 1.134 + let progressMeter = chromeDoc.createElement("progressmeter"); 1.135 + progressMeter.setAttribute("mode", "undetermined"); 1.136 + popupProgressContent.appendChild(progressMeter); 1.137 + 1.138 + let manifestURL = aData.app.manifestURL; 1.139 + 1.140 + let cleanup = () => { 1.141 + popupProgressContent.removeChild(progressMeter); 1.142 + delete this.installations[manifestURL]; 1.143 + if (Object.getOwnPropertyNames(this.installations).length == 0) { 1.144 + notification.remove(); 1.145 + } 1.146 + }; 1.147 + 1.148 + this.installations[manifestURL] = Promise.defer(); 1.149 + this.installations[manifestURL].promise.then(null, (error) => { 1.150 + Cu.reportError("Error installing webapp: " + error); 1.151 + cleanup(); 1.152 + }); 1.153 + 1.154 + let nativeApp = new NativeApp(aData.app, jsonManifest, 1.155 + aData.app.categories); 1.156 + let localDir; 1.157 + try { 1.158 + localDir = nativeApp.createProfile(); 1.159 + } catch (ex) { 1.160 + Cu.reportError("Error installing webapp: " + ex); 1.161 + DOMApplicationRegistry.denyInstall(aData); 1.162 + cleanup(); 1.163 + return; 1.164 + } 1.165 + 1.166 + DOMApplicationRegistry.confirmInstall(aData, localDir, 1.167 + (aManifest, aZipPath) => Task.spawn((function*() { 1.168 + try { 1.169 + yield nativeApp.install(aManifest, aZipPath); 1.170 + yield this.installations[manifestURL].promise; 1.171 + notifyInstallSuccess(aData.app, nativeApp, bundle); 1.172 + } catch (ex) { 1.173 + Cu.reportError("Error installing webapp: " + ex); 1.174 + // TODO: Notify user that the installation has failed 1.175 + } finally { 1.176 + cleanup(); 1.177 + } 1.178 + }).bind(this)) 1.179 + ); 1.180 + } 1.181 + }; 1.182 + 1.183 + let requestingURI = chromeWin.makeURI(aData.from); 1.184 + let manifest = new ManifestHelper(jsonManifest, aData.app.origin); 1.185 + 1.186 + let host; 1.187 + try { 1.188 + host = requestingURI.host; 1.189 + } catch(e) { 1.190 + host = requestingURI.spec; 1.191 + } 1.192 + 1.193 + let message = bundle.getFormattedString("webapps.requestInstall", 1.194 + [manifest.name, host], 2); 1.195 + 1.196 + notification = chromeWin.PopupNotifications.show(browser, 1.197 + "webapps-install", 1.198 + message, 1.199 + "webapps-notification-icon", 1.200 + mainAction); 1.201 + 1.202 + } 1.203 +} 1.204 + 1.205 +function notifyInstallSuccess(aApp, aNativeApp, aBundle) { 1.206 + let launcher = { 1.207 + observe: function(aSubject, aTopic) { 1.208 + if (aTopic == "alertclickcallback") { 1.209 + WebappOSUtils.launch(aApp); 1.210 + } 1.211 + } 1.212 + }; 1.213 + 1.214 + try { 1.215 + let notifier = Cc["@mozilla.org/alerts-service;1"]. 1.216 + getService(Ci.nsIAlertsService); 1.217 + 1.218 + notifier.showAlertNotification(aNativeApp.iconURI.spec, 1.219 + aBundle.getString("webapps.install.success"), 1.220 + aNativeApp.appNameAsFilename, 1.221 + true, null, launcher); 1.222 + } catch (ex) {} 1.223 +}