browser/modules/WebappManager.jsm

changeset 0
6474c204b198
     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 +}

mercurial