dom/apps/src/Webapps.js

Wed, 31 Dec 2014 06:55:50 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:55:50 +0100
changeset 2
7e26c7da4463
permissions
-rw-r--r--

Added tag UPSTREAM_283F7C6 for changeset ca08bd8f51b2

     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 const Cc = Components.classes;
     6 const Ci = Components.interfaces;
     7 const Cu = Components.utils;
     8 const Cr = Components.results;
    10 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
    11 Cu.import("resource://gre/modules/Services.jsm");
    12 Cu.import("resource://gre/modules/DOMRequestHelper.jsm");
    13 Cu.import("resource://gre/modules/AppsUtils.jsm");
    14 Cu.import("resource://gre/modules/BrowserElementPromptService.jsm");
    16 XPCOMUtils.defineLazyServiceGetter(this, "cpmm",
    17                                    "@mozilla.org/childprocessmessagemanager;1",
    18                                    "nsIMessageSender");
    20 function convertAppsArray(aApps, aWindow) {
    21   let apps = new aWindow.Array();
    22   for (let i = 0; i < aApps.length; i++) {
    23     let app = aApps[i];
    24     apps.push(createApplicationObject(aWindow, app));
    25   }
    27   return apps;
    28 }
    30 function WebappsRegistry() {
    31 }
    33 WebappsRegistry.prototype = {
    34   __proto__: DOMRequestIpcHelper.prototype,
    36   receiveMessage: function(aMessage) {
    37     let msg = aMessage.json;
    38     if (msg.oid != this._id)
    39       return
    40     let req = this.getRequest(msg.requestID);
    41     if (!req)
    42       return;
    43     let app = msg.app;
    44     switch (aMessage.name) {
    45       case "Webapps:Install:Return:OK":
    46         this.removeMessageListeners("Webapps:Install:Return:KO");
    47         Services.DOMRequest.fireSuccess(req, createApplicationObject(this._window, app));
    48         cpmm.sendAsyncMessage("Webapps:Install:Return:Ack",
    49                               { manifestURL : app.manifestURL });
    50         break;
    51       case "Webapps:Install:Return:KO":
    52         this.removeMessageListeners(aMessage.name);
    53         Services.DOMRequest.fireError(req, msg.error || "DENIED");
    54         break;
    55       case "Webapps:GetSelf:Return:OK":
    56         this.removeMessageListeners(aMessage.name);
    57         if (msg.apps.length) {
    58           app = msg.apps[0];
    59           Services.DOMRequest.fireSuccess(req, createApplicationObject(this._window, app));
    60         } else {
    61           Services.DOMRequest.fireSuccess(req, null);
    62         }
    63         break;
    64       case "Webapps:CheckInstalled:Return:OK":
    65         this.removeMessageListeners(aMessage.name);
    66         Services.DOMRequest.fireSuccess(req, msg.app);
    67         break;
    68       case "Webapps:GetInstalled:Return:OK":
    69         this.removeMessageListeners(aMessage.name);
    70         Services.DOMRequest.fireSuccess(req, convertAppsArray(msg.apps, this._window));
    71         break;
    72     }
    73     this.removeRequest(msg.requestID);
    74   },
    76   _getOrigin: function(aURL) {
    77     let uri = Services.io.newURI(aURL, null, null);
    78     return uri.prePath;
    79   },
    81   // Checks that the URL scheme is appropriate (http or https) and
    82   // asynchronously fire an error on the DOM Request if it isn't.
    83   _validateURL: function(aURL, aRequest) {
    84     let uri;
    85     let res;
    87     try {
    88       uri = Services.io.newURI(aURL, null, null);
    89       if (uri.schemeIs("http") || uri.schemeIs("https")) {
    90         res = uri.spec;
    91       }
    92     } catch(e) {
    93       Services.DOMRequest.fireErrorAsync(aRequest, "INVALID_URL");
    94       return false;
    95     }
    97     // The scheme is incorrect, fire DOMRequest error.
    98     if (!res) {
    99       Services.DOMRequest.fireErrorAsync(aRequest, "INVALID_URL");
   100       return false;
   101     }
   103     return uri.spec;
   104   },
   106   // Checks that we run as a foreground page, and fire an error on the
   107   // DOM Request if we aren't.
   108   _ensureForeground: function(aRequest) {
   109     let docShell = this._window.QueryInterface(Ci.nsIInterfaceRequestor)
   110                                .getInterface(Ci.nsIWebNavigation)
   111                                .QueryInterface(Ci.nsIDocShell);
   112     if (docShell.isActive) {
   113       return true;
   114     }
   116     Services.DOMRequest.fireErrorAsync(aRequest, "BACKGROUND_APP");
   117     return false;
   118   },
   120   _prepareInstall: function(aURL, aRequest, aParams, isPackage) {
   121     let installURL = this._window.location.href;
   122     let requestID = this.getRequestId(aRequest);
   123     let receipts = (aParams && aParams.receipts &&
   124                     Array.isArray(aParams.receipts)) ? aParams.receipts
   125                                                      : [];
   126     let categories = (aParams && aParams.categories &&
   127                       Array.isArray(aParams.categories)) ? aParams.categories
   128                                                          : [];
   130     let principal = this._window.document.nodePrincipal;
   132     return { app: {
   133                     installOrigin: this._getOrigin(installURL),
   134                     origin: this._getOrigin(aURL),
   135                     manifestURL: aURL,
   136                     receipts: receipts,
   137                     categories: categories
   138                   },
   140              from: installURL,
   141              oid: this._id,
   142              requestID: requestID,
   143              appId: principal.appId,
   144              isBrowser: principal.isInBrowserElement,
   145              isPackage: isPackage
   146            };
   147   },
   149   // mozIDOMApplicationRegistry implementation
   151   install: function(aURL, aParams) {
   152     let request = this.createRequest();
   154     let uri = this._validateURL(aURL, request);
   156     if (uri && this._ensureForeground(request)) {
   157       this.addMessageListeners("Webapps:Install:Return:KO");
   158       cpmm.sendAsyncMessage("Webapps:Install",
   159                             this._prepareInstall(uri, request, aParams, false));
   160     }
   162     return request;
   163   },
   165   getSelf: function() {
   166     let request = this.createRequest();
   167     this.addMessageListeners("Webapps:GetSelf:Return:OK");
   168     cpmm.sendAsyncMessage("Webapps:GetSelf", { origin: this._getOrigin(this._window.location.href),
   169                                                appId: this._window.document.nodePrincipal.appId,
   170                                                oid: this._id,
   171                                                requestID: this.getRequestId(request) });
   172     return request;
   173   },
   175   checkInstalled: function(aManifestURL) {
   176     let manifestURL = Services.io.newURI(aManifestURL, null, this._window.document.baseURIObject);
   177     this._window.document.nodePrincipal.checkMayLoad(manifestURL, true, false);
   179     let request = this.createRequest();
   181     this.addMessageListeners("Webapps:CheckInstalled:Return:OK");
   182     cpmm.sendAsyncMessage("Webapps:CheckInstalled", { origin: this._getOrigin(this._window.location.href),
   183                                                       manifestURL: manifestURL.spec,
   184                                                       oid: this._id,
   185                                                       requestID: this.getRequestId(request) });
   186     return request;
   187   },
   189   getInstalled: function() {
   190     let request = this.createRequest();
   191     this.addMessageListeners("Webapps:GetInstalled:Return:OK");
   192     cpmm.sendAsyncMessage("Webapps:GetInstalled", { origin: this._getOrigin(this._window.location.href),
   193                                                     oid: this._id,
   194                                                     requestID: this.getRequestId(request) });
   195     return request;
   196   },
   198   get mgmt() {
   199     if (!this.hasMgmtPrivilege) {
   200       return null;
   201     }
   203     if (!this._mgmt)
   204       this._mgmt = new WebappsApplicationMgmt(this._window);
   205     return this._mgmt;
   206   },
   208   uninit: function() {
   209     this._mgmt = null;
   210     cpmm.sendAsyncMessage("Webapps:UnregisterForMessages",
   211                           ["Webapps:Install:Return:OK"]);
   212   },
   214   installPackage: function(aURL, aParams) {
   215     let request = this.createRequest();
   217     let uri = this._validateURL(aURL, request);
   219     if (uri && this._ensureForeground(request)) {
   220       this.addMessageListeners("Webapps:Install:Return:KO");
   221       cpmm.sendAsyncMessage("Webapps:InstallPackage",
   222                             this._prepareInstall(uri, request, aParams, true));
   223     }
   225     return request;
   226   },
   228   // nsIDOMGlobalPropertyInitializer implementation
   229   init: function(aWindow) {
   230     this.initDOMRequestHelper(aWindow, "Webapps:Install:Return:OK");
   232     let util = this._window.QueryInterface(Ci.nsIInterfaceRequestor)
   233                            .getInterface(Ci.nsIDOMWindowUtils);
   234     this._id = util.outerWindowID;
   235     cpmm.sendAsyncMessage("Webapps:RegisterForMessages",
   236                           { messages: ["Webapps:Install:Return:OK"]});
   238     let principal = aWindow.document.nodePrincipal;
   239     let perm = Services.perms
   240                .testExactPermissionFromPrincipal(principal, "webapps-manage");
   242     // Only pages with the webapps-manage permission set can get access to
   243     // the mgmt object.
   244     this.hasMgmtPrivilege = perm == Ci.nsIPermissionManager.ALLOW_ACTION;
   245   },
   247   classID: Components.ID("{fff440b3-fae2-45c1-bf03-3b5a2e432270}"),
   249   QueryInterface: XPCOMUtils.generateQI([Ci.nsISupportsWeakReference,
   250                                          Ci.nsIObserver,
   251                                          Ci.mozIDOMApplicationRegistry,
   252                                          Ci.mozIDOMApplicationRegistry2,
   253                                          Ci.nsIDOMGlobalPropertyInitializer]),
   255   classInfo: XPCOMUtils.generateCI({classID: Components.ID("{fff440b3-fae2-45c1-bf03-3b5a2e432270}"),
   256                                     contractID: "@mozilla.org/webapps;1",
   257                                     interfaces: [Ci.mozIDOMApplicationRegistry,
   258                                                  Ci.mozIDOMApplicationRegistry2],
   259                                     flags: Ci.nsIClassInfo.DOM_OBJECT,
   260                                     classDescription: "Webapps Registry"})
   261 }
   263 /**
   264   * mozIDOMApplication object
   265   */
   267 // A simple cache for the wrapped manifests.
   268 let manifestCache = {
   269   _cache: { },
   271   // Gets an entry from the cache, and populates the cache if needed.
   272   get: function mcache_get(aManifestURL, aManifest, aWindow, aInnerWindowID) {
   273     if (!(aManifestURL in this._cache)) {
   274       this._cache[aManifestURL] = { };
   275     }
   277     let winObjs = this._cache[aManifestURL];
   278     if (!(aInnerWindowID in winObjs)) {
   279       winObjs[aInnerWindowID] = Cu.cloneInto(aManifest, aWindow);
   280     }
   282     return winObjs[aInnerWindowID];
   283   },
   285   // Invalidates an entry in the cache.
   286   evict: function mcache_evict(aManifestURL, aInnerWindowID) {
   287     if (aManifestURL in this._cache) {
   288       let winObjs = this._cache[aManifestURL];
   289       if (aInnerWindowID in winObjs) {
   290         delete winObjs[aInnerWindowID];
   291       }
   293       if (Object.keys(winObjs).length == 0) {
   294         delete this._cache[aManifestURL];
   295       }
   296     }
   297   },
   299   observe: function(aSubject, aTopic, aData) {
   300     // Clear the cache on memory pressure.
   301     this._cache = { };
   302   },
   304   init: function() {
   305     Services.obs.addObserver(this, "memory-pressure", false);
   306   }
   307 };
   309 function createApplicationObject(aWindow, aApp) {
   310   let app = Cc["@mozilla.org/webapps/application;1"].createInstance(Ci.mozIDOMApplication);
   311   app.wrappedJSObject.init(aWindow, aApp);
   312   return app;
   313 }
   315 function WebappsApplication() {
   316   this.wrappedJSObject = this;
   317 }
   319 WebappsApplication.prototype = {
   320   __proto__: DOMRequestIpcHelper.prototype,
   322   init: function(aWindow, aApp) {
   323     this._window = aWindow;
   324     let principal = this._window.document.nodePrincipal;
   325     this._appStatus = principal.appStatus;
   326     this.origin = aApp.origin;
   327     this._manifest = aApp.manifest;
   328     this._updateManifest = aApp.updateManifest;
   329     this.manifestURL = aApp.manifestURL;
   330     this.receipts = aApp.receipts;
   331     this.installOrigin = aApp.installOrigin;
   332     this.installTime = aApp.installTime;
   333     this.installState = aApp.installState || "installed";
   334     this.removable = aApp.removable;
   335     this.lastUpdateCheck = aApp.lastUpdateCheck ? aApp.lastUpdateCheck
   336                                                 : Date.now();
   337     this.updateTime = aApp.updateTime ? aApp.updateTime
   338                                       : aApp.installTime;
   339     this.progress = NaN;
   340     this.downloadAvailable = aApp.downloadAvailable;
   341     this.downloading = aApp.downloading;
   342     this.readyToApplyDownload = aApp.readyToApplyDownload;
   343     this.downloadSize = aApp.downloadSize || 0;
   345     this._onprogress = null;
   346     this._ondownloadsuccess = null;
   347     this._ondownloaderror = null;
   348     this._ondownloadavailable = null;
   349     this._ondownloadapplied = null;
   351     this._downloadError = null;
   353     this.initDOMRequestHelper(aWindow, [
   354       { name: "Webapps:CheckForUpdate:Return:KO", weakRef: true },
   355       { name: "Webapps:Connect:Return:OK", weakRef: true },
   356       { name: "Webapps:Connect:Return:KO", weakRef: true },
   357       { name: "Webapps:FireEvent", weakRef: true },
   358       { name: "Webapps:GetConnections:Return:OK", weakRef: true },
   359       { name: "Webapps:UpdateState", weakRef: true }
   360     ]);
   362     cpmm.sendAsyncMessage("Webapps:RegisterForMessages", {
   363       messages: ["Webapps:FireEvent",
   364                  "Webapps:UpdateState"],
   365       app: {
   366         id: this.id,
   367         manifestURL: this.manifestURL,
   368         installState: this.installState,
   369         downloading: this.downloading
   370       }
   371     });
   372   },
   374   get manifest() {
   375     return manifestCache.get(this.manifestURL,
   376                              this._manifest,
   377                              this._window,
   378                              this.innerWindowID);
   379   },
   381   get updateManifest() {
   382     return this.updateManifest =
   383       this._updateManifest ? Cu.cloneInto(this._updateManifest, this._window)
   384                            : null;
   385   },
   387   set onprogress(aCallback) {
   388     this._onprogress = aCallback;
   389   },
   391   get onprogress() {
   392     return this._onprogress;
   393   },
   395   set ondownloadsuccess(aCallback) {
   396     this._ondownloadsuccess = aCallback;
   397   },
   399   get ondownloadsuccess() {
   400     return this._ondownloadsuccess;
   401   },
   403   set ondownloaderror(aCallback) {
   404     this._ondownloaderror = aCallback;
   405   },
   407   get ondownloaderror() {
   408     return this._ondownloaderror;
   409   },
   411   set ondownloadavailable(aCallback) {
   412     this._ondownloadavailable = aCallback;
   413   },
   415   get ondownloadavailable() {
   416     return this._ondownloadavailable;
   417   },
   419   set ondownloadapplied(aCallback) {
   420     this._ondownloadapplied = aCallback;
   421   },
   423   get ondownloadapplied() {
   424     return this._ondownloadapplied;
   425   },
   427   get downloadError() {
   428     return new this._window.DOMError(this._downloadError || '');
   429   },
   431   download: function() {
   432     cpmm.sendAsyncMessage("Webapps:Download",
   433                           { manifestURL: this.manifestURL });
   434   },
   436   cancelDownload: function() {
   437     cpmm.sendAsyncMessage("Webapps:CancelDownload",
   438                           { manifestURL: this.manifestURL });
   439   },
   441   checkForUpdate: function() {
   442     let request = this.createRequest();
   444     cpmm.sendAsyncMessage("Webapps:CheckForUpdate",
   445                           { manifestURL: this.manifestURL,
   446                             oid: this._id,
   447                             requestID: this.getRequestId(request) });
   448     return request;
   449   },
   451   launch: function(aStartPoint) {
   452     let request = this.createRequest();
   453     this.addMessageListeners(["Webapps:Launch:Return:OK",
   454                               "Webapps:Launch:Return:KO"]);
   455     cpmm.sendAsyncMessage("Webapps:Launch", { origin: this.origin,
   456                                               manifestURL: this.manifestURL,
   457                                               startPoint: aStartPoint || "",
   458                                               oid: this._id,
   459                                               timestamp: Date.now(),
   460                                               requestID: this.getRequestId(request) });
   461     return request;
   462   },
   464   clearBrowserData: function() {
   465     let request = this.createRequest();
   466     let browserChild =
   467       BrowserElementPromptService.getBrowserElementChildForWindow(this._window);
   468     if (browserChild) {
   469       this.addMessageListeners("Webapps:ClearBrowserData:Return");
   470       browserChild.messageManager.sendAsyncMessage(
   471         "Webapps:ClearBrowserData",
   472         { manifestURL: this.manifestURL,
   473           oid: this._id,
   474           requestID: this.getRequestId(request) }
   475       );
   476     } else {
   477       Services.DOMRequest.fireErrorAsync(request, "NO_CLEARABLE_BROWSER");
   478     }
   479     return request;
   480   },
   482   connect: function(aKeyword, aRules) {
   483     return this.createPromise(function (aResolve, aReject) {
   484       cpmm.sendAsyncMessage("Webapps:Connect",
   485                             { keyword: aKeyword,
   486                               rules: aRules,
   487                               manifestURL: this.manifestURL,
   488                               outerWindowID: this._id,
   489                               requestID: this.getPromiseResolverId({
   490                                 resolve: aResolve,
   491                                 reject: aReject
   492                               })});
   493     }.bind(this));
   494   },
   496   getConnections: function() {
   497     return this.createPromise(function (aResolve, aReject) {
   498       cpmm.sendAsyncMessage("Webapps:GetConnections",
   499                             { manifestURL: this.manifestURL,
   500                               outerWindowID: this._id,
   501                               requestID: this.getPromiseResolverId({
   502                                 resolve: aResolve,
   503                                 reject: aReject
   504                               })});
   505     }.bind(this));
   506   },
   508   addReceipt: function(receipt) {
   509     let request = this.createRequest();
   511     this.addMessageListeners(["Webapps:AddReceipt:Return:OK",
   512                               "Webapps:AddReceipt:Return:KO"]);
   514     cpmm.sendAsyncMessage("Webapps:AddReceipt", { manifestURL: this.manifestURL,
   515                                                   receipt: receipt,
   516                                                   oid: this._id,
   517                                                   requestID: this.getRequestId(request) });
   519     return request;
   520   },
   522   removeReceipt: function(receipt) {
   523     let request = this.createRequest();
   525     this.addMessageListeners(["Webapps:RemoveReceipt:Return:OK",
   526                               "Webapps:RemoveReceipt:Return:KO"]);
   528     cpmm.sendAsyncMessage("Webapps:RemoveReceipt", { manifestURL: this.manifestURL,
   529                                                      receipt: receipt,
   530                                                      oid: this._id,
   531                                                      requestID: this.getRequestId(request) });
   533     return request;
   534   },
   536   replaceReceipt: function(oldReceipt, newReceipt) {
   537     let request = this.createRequest();
   539     this.addMessageListeners(["Webapps:ReplaceReceipt:Return:OK",
   540                               "Webapps:ReplaceReceipt:Return:KO"]);
   542     cpmm.sendAsyncMessage("Webapps:ReplaceReceipt", { manifestURL: this.manifestURL,
   543                                                       newReceipt: newReceipt,
   544                                                       oldReceipt: oldReceipt,
   545                                                       oid: this._id,
   546                                                       requestID: this.getRequestId(request) });
   548     return request;
   549   },
   551   uninit: function() {
   552     this._onprogress = null;
   553     cpmm.sendAsyncMessage("Webapps:UnregisterForMessages", [
   554       "Webapps:FireEvent",
   555       "Webapps:UpdateState"
   556     ]);
   558     manifestCache.evict(this.manifestURL, this.innerWindowID);
   559   },
   561   _fireEvent: function(aName) {
   562     let handler = this["_on" + aName];
   563     if (handler) {
   564       let event = new this._window.MozApplicationEvent(aName, {
   565         application: this
   566       });
   567       try {
   568         handler.handleEvent(event);
   569       } catch (ex) {
   570         dump("Event handler expection " + ex + "\n");
   571       }
   572     }
   573   },
   575   _updateState: function(aMsg) {
   576     if (aMsg.app) {
   577       for (let prop in aMsg.app) {
   578         this[prop] = aMsg.app[prop];
   579       }
   580     }
   582     if (aMsg.error) {
   583       this._downloadError = aMsg.error;
   584     }
   586     if (aMsg.manifest) {
   587       this._manifest = aMsg.manifest;
   588       manifestCache.evict(this.manifestURL, this.innerWindowID);
   589     }
   590   },
   592   receiveMessage: function(aMessage) {
   593     let msg = aMessage.json;
   594     let req;
   595     if (aMessage.name == "Webapps:Connect:Return:OK" ||
   596         aMessage.name == "Webapps:Connect:Return:KO" ||
   597         aMessage.name == "Webapps:GetConnections:Return:OK") {
   598       req = this.takePromiseResolver(msg.requestID);
   599     } else {
   600       req = this.takeRequest(msg.requestID);
   601     }
   603     // ondownload* callbacks should be triggered on all app instances
   604     if ((msg.oid != this._id || !req) &&
   605         aMessage.name !== "Webapps:FireEvent" &&
   606         aMessage.name !== "Webapps:UpdateState") {
   607       return;
   608     }
   610     switch (aMessage.name) {
   611       case "Webapps:Launch:Return:KO":
   612         this.removeMessageListeners(["Webapps:Launch:Return:OK",
   613                                      "Webapps:Launch:Return:KO"]);
   614         Services.DOMRequest.fireError(req, "APP_INSTALL_PENDING");
   615         break;
   616       case "Webapps:Launch:Return:OK":
   617         this.removeMessageListeners(["Webapps:Launch:Return:OK",
   618                                      "Webapps:Launch:Return:KO"]);
   619         Services.DOMRequest.fireSuccess(req, null);
   620         break;
   621       case "Webapps:CheckForUpdate:Return:KO":
   622         Services.DOMRequest.fireError(req, msg.error);
   623         break;
   624       case "Webapps:FireEvent":
   625         if (msg.manifestURL != this.manifestURL) {
   626            return;
   627         }
   629         // The parent might ask childs to trigger more than one event in one
   630         // shot, so in order to avoid needless IPC we allow an array for the
   631         // 'eventType' IPC message field.
   632         if (!Array.isArray(msg.eventType)) {
   633           msg.eventType = [msg.eventType];
   634         }
   636         msg.eventType.forEach((aEventType) => {
   637           if ("_on" + aEventType in this) {
   638             this._fireEvent(aEventType);
   639           } else {
   640             dump("Unsupported event type " + aEventType + "\n");
   641           }
   642         });
   644         if (req) {
   645           Services.DOMRequest.fireSuccess(req, this.manifestURL);
   646         }
   647         break;
   648       case "Webapps:UpdateState":
   649         if (msg.manifestURL != this.manifestURL) {
   650           return;
   651         }
   653         this._updateState(msg);
   654         break;
   655       case "Webapps:ClearBrowserData:Return":
   656         this.removeMessageListeners(aMessage.name);
   657         Services.DOMRequest.fireSuccess(req, null);
   658         break;
   659       case "Webapps:Connect:Return:OK":
   660         let messagePorts = [];
   661         msg.messagePortIDs.forEach((aPortID) => {
   662           let port = new this._window.MozInterAppMessagePort(aPortID);
   663           messagePorts.push(port);
   664         });
   665         req.resolve(messagePorts);
   666         break;
   667       case "Webapps:Connect:Return:KO":
   668         req.reject("No connections registered");
   669         break;
   670       case "Webapps:GetConnections:Return:OK":
   671         let connections = [];
   672         msg.connections.forEach((aConnection) => {
   673           let connection =
   674             new this._window.MozInterAppConnection(aConnection.keyword,
   675                                                    aConnection.pubAppManifestURL,
   676                                                    aConnection.subAppManifestURL);
   677           connections.push(connection);
   678         });
   679         req.resolve(connections);
   680         break;
   681       case "Webapps:AddReceipt:Return:OK":
   682         this.removeMessageListeners(["Webapps:AddReceipt:Return:OK",
   683                                      "Webapps:AddReceipt:Return:KO"]);
   684         this.receipts = msg.receipts;
   685         Services.DOMRequest.fireSuccess(req, null);
   686         break;
   687       case "Webapps:AddReceipt:Return:KO":
   688         this.removeMessageListeners(["Webapps:AddReceipt:Return:OK",
   689                                      "Webapps:AddReceipt:Return:KO"]);
   690         Services.DOMRequest.fireError(req, msg.error);
   691         break;
   692       case "Webapps:RemoveReceipt:Return:OK":
   693         this.removeMessageListeners(["Webapps:RemoveReceipt:Return:OK",
   694                                      "Webapps:RemoveReceipt:Return:KO"]);
   695         this.receipts = msg.receipts;
   696         Services.DOMRequest.fireSuccess(req, null);
   697         break;
   698       case "Webapps:RemoveReceipt:Return:KO":
   699         this.removeMessageListeners(["Webapps:RemoveReceipt:Return:OK",
   700                                      "Webapps:RemoveReceipt:Return:KO"]);
   701         Services.DOMRequest.fireError(req, msg.error);
   702         break;
   703       case "Webapps:ReplaceReceipt:Return:OK":
   704         this.removeMessageListeners(["Webapps:ReplaceReceipt:Return:OK",
   705                                      "Webapps:ReplaceReceipt:Return:KO"]);
   706         this.receipts = msg.receipts;
   707         Services.DOMRequest.fireSuccess(req, null);
   708         break;
   709       case "Webapps:ReplaceReceipt:Return:KO":
   710         this.removeMessageListeners(["Webapps:ReplaceReceipt:Return:OK",
   711                                      "Webapps:ReplaceReceipt:Return:KO"]);
   712         Services.DOMRequest.fireError(req, msg.error);
   713         break;
   714     }
   715   },
   717   classID: Components.ID("{723ed303-7757-4fb0-b261-4f78b1f6bd22}"),
   719   QueryInterface: XPCOMUtils.generateQI([Ci.mozIDOMApplication,
   720                                          Ci.nsISupportsWeakReference,
   721                                          Ci.nsIObserver]),
   723   classInfo: XPCOMUtils.generateCI({classID: Components.ID("{723ed303-7757-4fb0-b261-4f78b1f6bd22}"),
   724                                     contractID: "@mozilla.org/webapps/application;1",
   725                                     interfaces: [Ci.mozIDOMApplication],
   726                                     flags: Ci.nsIClassInfo.DOM_OBJECT,
   727                                     classDescription: "Webapps Application"})
   728 }
   730 /**
   731   * mozIDOMApplicationMgmt object
   732   */
   733 function WebappsApplicationMgmt(aWindow) {
   734   this.initDOMRequestHelper(aWindow, ["Webapps:GetAll:Return:OK",
   735                                       "Webapps:GetAll:Return:KO",
   736                                       "Webapps:Uninstall:Return:OK",
   737                                       "Webapps:Uninstall:Broadcast:Return:OK",
   738                                       "Webapps:Uninstall:Return:KO",
   739                                       "Webapps:Install:Return:OK",
   740                                       "Webapps:GetNotInstalled:Return:OK"]);
   742   cpmm.sendAsyncMessage("Webapps:RegisterForMessages",
   743                         {
   744                           messages: ["Webapps:Install:Return:OK",
   745                                      "Webapps:Uninstall:Return:OK",
   746                                      "Webapps:Uninstall:Broadcast:Return:OK"]
   747                         }
   748                        );
   750   this._oninstall = null;
   751   this._onuninstall = null;
   752 }
   754 WebappsApplicationMgmt.prototype = {
   755   __proto__: DOMRequestIpcHelper.prototype,
   756   __exposedProps__: {
   757                       applyDownload: "r",
   758                       getAll: "r",
   759                       getNotInstalled: "r",
   760                       oninstall: "rw",
   761                       onuninstall: "rw"
   762                      },
   764   uninit: function() {
   765     this._oninstall = null;
   766     this._onuninstall = null;
   767     cpmm.sendAsyncMessage("Webapps:UnregisterForMessages",
   768                           ["Webapps:Install:Return:OK",
   769                            "Webapps:Uninstall:Return:OK",
   770                            "Webapps:Uninstall:Broadcast:Return:OK"]);
   771   },
   773   applyDownload: function(aApp) {
   774     if (!aApp.readyToApplyDownload) {
   775       return;
   776     }
   778     cpmm.sendAsyncMessage("Webapps:ApplyDownload",
   779                           { manifestURL: aApp.manifestURL });
   780   },
   782   uninstall: function(aApp) {
   783     dump("-- webapps.js uninstall " + aApp.manifestURL + "\n");
   784     let request = this.createRequest();
   785     cpmm.sendAsyncMessage("Webapps:Uninstall", { origin: aApp.origin,
   786                                                  manifestURL: aApp.manifestURL,
   787                                                  oid: this._id,
   788                                                  requestID: this.getRequestId(request) });
   789     return request;
   790   },
   792   getAll: function() {
   793     let request = this.createRequest();
   794     cpmm.sendAsyncMessage("Webapps:GetAll", { oid: this._id,
   795                                               requestID: this.getRequestId(request) });
   796     return request;
   797   },
   799   getNotInstalled: function() {
   800     let request = this.createRequest();
   801     cpmm.sendAsyncMessage("Webapps:GetNotInstalled", { oid: this._id,
   802                                                        requestID: this.getRequestId(request) });
   803     return request;
   804   },
   806   get oninstall() {
   807     return this._oninstall;
   808   },
   810   get onuninstall() {
   811     return this._onuninstall;
   812   },
   814   set oninstall(aCallback) {
   815     this._oninstall = aCallback;
   816   },
   818   set onuninstall(aCallback) {
   819     this._onuninstall = aCallback;
   820   },
   822   receiveMessage: function(aMessage) {
   823     var msg = aMessage.json;
   824     let req = this.getRequest(msg.requestID);
   825     // We want Webapps:Install:Return:OK and Webapps:Uninstall:Broadcast:Return:OK
   826     // to be broadcasted to all instances of mozApps.mgmt.
   827     if (!((msg.oid == this._id && req) ||
   828           aMessage.name == "Webapps:Install:Return:OK" ||
   829           aMessage.name == "Webapps:Uninstall:Broadcast:Return:OK")) {
   830       return;
   831     }
   832     switch (aMessage.name) {
   833       case "Webapps:GetAll:Return:OK":
   834         Services.DOMRequest.fireSuccess(req, convertAppsArray(msg.apps, this._window));
   835         break;
   836       case "Webapps:GetAll:Return:KO":
   837         Services.DOMRequest.fireError(req, "DENIED");
   838         break;
   839       case "Webapps:GetNotInstalled:Return:OK":
   840         Services.DOMRequest.fireSuccess(req, convertAppsArray(msg.apps, this._window));
   841         break;
   842       case "Webapps:Install:Return:OK":
   843         if (this._oninstall) {
   844           let app = msg.app;
   845           let event = new this._window.MozApplicationEvent("applicationinstall",
   846                            { application : createApplicationObject(this._window, app) });
   847           this._oninstall.handleEvent(event);
   848         }
   849         break;
   850       case "Webapps:Uninstall:Broadcast:Return:OK":
   851         if (this._onuninstall) {
   852           let detail = {
   853             manifestURL: msg.manifestURL,
   854             origin: msg.origin
   855           };
   856           let event = new this._window.MozApplicationEvent("applicationuninstall",
   857                            { application : createApplicationObject(this._window, detail) });
   858           this._onuninstall.handleEvent(event);
   859         }
   860         break;
   861       case "Webapps:Uninstall:Return:OK":
   862         Services.DOMRequest.fireSuccess(req, msg.origin);
   863         break;
   864       case "Webapps:Uninstall:Return:KO":
   865         Services.DOMRequest.fireError(req, "NOT_INSTALLED");
   866         break;
   867     }
   868     if (aMessage.name !== "Webapps:Uninstall:Broadcast:Return:OK") {
   869       this.removeRequest(msg.requestID);
   870     }
   871   },
   873   classID: Components.ID("{8c1bca96-266f-493a-8d57-ec7a95098c15}"),
   875   QueryInterface: XPCOMUtils.generateQI([Ci.mozIDOMApplicationMgmt,
   876                                          Ci.nsISupportsWeakReference,
   877                                          Ci.nsIObserver]),
   879   classInfo: XPCOMUtils.generateCI({classID: Components.ID("{8c1bca96-266f-493a-8d57-ec7a95098c15}"),
   880                                     contractID: "@mozilla.org/webapps/application-mgmt;1",
   881                                     interfaces: [Ci.mozIDOMApplicationMgmt],
   882                                     flags: Ci.nsIClassInfo.DOM_OBJECT,
   883                                     classDescription: "Webapps Application Mgmt"})
   884 }
   886 manifestCache.init();
   888 this.NSGetFactory = XPCOMUtils.generateNSGetFactory([WebappsRegistry,
   889                                                      WebappsApplication]);

mercurial