michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this file, michael@0: * You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: this.EXPORTED_SYMBOLS = ["WebappManager"]; michael@0: michael@0: let Ci = Components.interfaces; michael@0: let Cc = Components.classes; michael@0: let Cu = Components.utils; michael@0: michael@0: Cu.import("resource://gre/modules/Services.jsm"); michael@0: Cu.import("resource://gre/modules/XPCOMUtils.jsm"); michael@0: Cu.import("resource://gre/modules/Webapps.jsm"); michael@0: Cu.import("resource://gre/modules/AppsUtils.jsm"); michael@0: Cu.import("resource://gre/modules/Task.jsm"); michael@0: Cu.import("resource://gre/modules/Promise.jsm"); michael@0: michael@0: XPCOMUtils.defineLazyModuleGetter(this, "NativeApp", michael@0: "resource://gre/modules/NativeApp.jsm"); michael@0: michael@0: XPCOMUtils.defineLazyModuleGetter(this, "WebappOSUtils", michael@0: "resource://gre/modules/WebappOSUtils.jsm"); michael@0: michael@0: XPCOMUtils.defineLazyServiceGetter(this, "cpmm", michael@0: "@mozilla.org/childprocessmessagemanager;1", michael@0: "nsIMessageSender"); michael@0: michael@0: this.WebappManager = { michael@0: // List of promises for in-progress installations michael@0: installations: {}, michael@0: michael@0: init: function() { michael@0: Services.obs.addObserver(this, "webapps-ask-install", false); michael@0: Services.obs.addObserver(this, "webapps-launch", false); michael@0: Services.obs.addObserver(this, "webapps-uninstall", false); michael@0: cpmm.addMessageListener("Webapps:Install:Return:OK", this); michael@0: cpmm.addMessageListener("Webapps:Install:Return:KO", this); michael@0: cpmm.addMessageListener("Webapps:UpdateState", this); michael@0: }, michael@0: michael@0: uninit: function() { michael@0: Services.obs.removeObserver(this, "webapps-ask-install"); michael@0: Services.obs.removeObserver(this, "webapps-launch"); michael@0: Services.obs.removeObserver(this, "webapps-uninstall"); michael@0: cpmm.removeMessageListener("Webapps:Install:Return:OK", this); michael@0: cpmm.removeMessageListener("Webapps:Install:Return:KO", this); michael@0: cpmm.removeMessageListener("Webapps:UpdateState", this); michael@0: }, michael@0: michael@0: receiveMessage: function(aMessage) { michael@0: let data = aMessage.data; michael@0: michael@0: let manifestURL = data.manifestURL || michael@0: (data.app && data.app.manifestURL) || michael@0: data.manifest; michael@0: michael@0: if (!this.installations[manifestURL]) { michael@0: return; michael@0: } michael@0: michael@0: if (aMessage.name == "Webapps:UpdateState") { michael@0: if (data.error) { michael@0: this.installations[manifestURL].reject(data.error); michael@0: } else if (data.app.installState == "installed") { michael@0: this.installations[manifestURL].resolve(); michael@0: } michael@0: } else if (aMessage.name == "Webapps:Install:Return:OK" && michael@0: !data.isPackage) { michael@0: let manifest = new ManifestHelper(data.app.manifest, data.app.origin); michael@0: if (!manifest.appcache_path) { michael@0: this.installations[manifestURL].resolve(); michael@0: } michael@0: } else if (aMessage.name == "Webapps:Install:Return:KO") { michael@0: this.installations[manifestURL].reject(data.error); michael@0: } michael@0: }, michael@0: michael@0: observe: function(aSubject, aTopic, aData) { michael@0: let data = JSON.parse(aData); michael@0: data.mm = aSubject; michael@0: michael@0: switch(aTopic) { michael@0: case "webapps-ask-install": michael@0: let win = this._getWindowForId(data.oid); michael@0: if (win && win.location.href == data.from) { michael@0: this.doInstall(data, win); michael@0: } michael@0: break; michael@0: case "webapps-launch": michael@0: WebappOSUtils.launch(data); michael@0: break; michael@0: case "webapps-uninstall": michael@0: WebappOSUtils.uninstall(data); michael@0: break; michael@0: } michael@0: }, michael@0: michael@0: _getWindowForId: function(aId) { michael@0: let someWindow = Services.wm.getMostRecentWindow(null); michael@0: return someWindow && Services.wm.getOuterWindowWithId(aId); michael@0: }, michael@0: michael@0: doInstall: function(aData, aWindow) { michael@0: let browser = aWindow.QueryInterface(Ci.nsIInterfaceRequestor) michael@0: .getInterface(Ci.nsIWebNavigation) michael@0: .QueryInterface(Ci.nsIDocShell) michael@0: .chromeEventHandler; michael@0: let chromeDoc = browser.ownerDocument; michael@0: let chromeWin = chromeDoc.defaultView; michael@0: let popupProgressContent = michael@0: chromeDoc.getElementById("webapps-install-progress-content"); michael@0: michael@0: let bundle = chromeWin.gNavigatorBundle; michael@0: michael@0: let jsonManifest = aData.isPackage ? aData.app.updateManifest : aData.app.manifest; michael@0: michael@0: let notification; michael@0: michael@0: let mainAction = { michael@0: label: bundle.getString("webapps.install"), michael@0: accessKey: bundle.getString("webapps.install.accesskey"), michael@0: callback: () => { michael@0: notification.remove(); michael@0: michael@0: notification = chromeWin.PopupNotifications. michael@0: show(browser, michael@0: "webapps-install-progress", michael@0: bundle.getString("webapps.install.inprogress"), michael@0: "webapps-notification-icon"); michael@0: michael@0: let progressMeter = chromeDoc.createElement("progressmeter"); michael@0: progressMeter.setAttribute("mode", "undetermined"); michael@0: popupProgressContent.appendChild(progressMeter); michael@0: michael@0: let manifestURL = aData.app.manifestURL; michael@0: michael@0: let cleanup = () => { michael@0: popupProgressContent.removeChild(progressMeter); michael@0: delete this.installations[manifestURL]; michael@0: if (Object.getOwnPropertyNames(this.installations).length == 0) { michael@0: notification.remove(); michael@0: } michael@0: }; michael@0: michael@0: this.installations[manifestURL] = Promise.defer(); michael@0: this.installations[manifestURL].promise.then(null, (error) => { michael@0: Cu.reportError("Error installing webapp: " + error); michael@0: cleanup(); michael@0: }); michael@0: michael@0: let nativeApp = new NativeApp(aData.app, jsonManifest, michael@0: aData.app.categories); michael@0: let localDir; michael@0: try { michael@0: localDir = nativeApp.createProfile(); michael@0: } catch (ex) { michael@0: Cu.reportError("Error installing webapp: " + ex); michael@0: DOMApplicationRegistry.denyInstall(aData); michael@0: cleanup(); michael@0: return; michael@0: } michael@0: michael@0: DOMApplicationRegistry.confirmInstall(aData, localDir, michael@0: (aManifest, aZipPath) => Task.spawn((function*() { michael@0: try { michael@0: yield nativeApp.install(aManifest, aZipPath); michael@0: yield this.installations[manifestURL].promise; michael@0: notifyInstallSuccess(aData.app, nativeApp, bundle); michael@0: } catch (ex) { michael@0: Cu.reportError("Error installing webapp: " + ex); michael@0: // TODO: Notify user that the installation has failed michael@0: } finally { michael@0: cleanup(); michael@0: } michael@0: }).bind(this)) michael@0: ); michael@0: } michael@0: }; michael@0: michael@0: let requestingURI = chromeWin.makeURI(aData.from); michael@0: let manifest = new ManifestHelper(jsonManifest, aData.app.origin); michael@0: michael@0: let host; michael@0: try { michael@0: host = requestingURI.host; michael@0: } catch(e) { michael@0: host = requestingURI.spec; michael@0: } michael@0: michael@0: let message = bundle.getFormattedString("webapps.requestInstall", michael@0: [manifest.name, host], 2); michael@0: michael@0: notification = chromeWin.PopupNotifications.show(browser, michael@0: "webapps-install", michael@0: message, michael@0: "webapps-notification-icon", michael@0: mainAction); michael@0: michael@0: } michael@0: } michael@0: michael@0: function notifyInstallSuccess(aApp, aNativeApp, aBundle) { michael@0: let launcher = { michael@0: observe: function(aSubject, aTopic) { michael@0: if (aTopic == "alertclickcallback") { michael@0: WebappOSUtils.launch(aApp); michael@0: } michael@0: } michael@0: }; michael@0: michael@0: try { michael@0: let notifier = Cc["@mozilla.org/alerts-service;1"]. michael@0: getService(Ci.nsIAlertsService); michael@0: michael@0: notifier.showAlertNotification(aNativeApp.iconURI.spec, michael@0: aBundle.getString("webapps.install.success"), michael@0: aNativeApp.appNameAsFilename, michael@0: true, null, launcher); michael@0: } catch (ex) {} michael@0: }