Bug 768868 - App manifest should support application type [r=vingtetun]
☠☠ backed out by 118cc431d56f ☠ ☠
authorFabrice Desré <fabrice@mozilla.com>
Mon, 27 Aug 2012 19:43:57 -0700
changeset 105669 a08d28e8a86b66f90f61bb684b33ba12d2c262ab
parent 105668 9b96029a9cf0aa09a1aee772005ef7ac53c7d0ff
child 105670 09ecfa4faa7612a4b8950083bc8a03d90521ccab
push id55
push usershu@rfrn.org
push dateThu, 30 Aug 2012 01:33:09 +0000
reviewersvingtetun
bugs768868
milestone18.0a1
Bug 768868 - App manifest should support application type [r=vingtetun]
dom/apps/src/Webapps.js
dom/apps/src/Webapps.jsm
modules/libpref/src/init/all.js
--- a/dom/apps/src/Webapps.js
+++ b/dom/apps/src/Webapps.js
@@ -15,17 +15,17 @@ Cu.import("resource://gre/modules/Object
 XPCOMUtils.defineLazyServiceGetter(this, "cpmm",
                                    "@mozilla.org/childprocessmessagemanager;1",
                                    "nsIMessageSender");
 
 function convertAppsArray(aApps, aWindow) {
   let apps = Cu.createArrayIn(aWindow);
   for (let i = 0; i < aApps.length; i++) {
     let app = aApps[i];
-    apps.push(createApplicationObject(aWindow, app.origin, app.manifest, app.manifestURL, 
+    apps.push(createApplicationObject(aWindow, app.origin, app.manifest, app.manifestURL,
                                       app.receipts, app.installOrigin, app.installTime));
   }
 
   return apps;
 }
 
 function WebappsRegistry() {
 }
@@ -50,16 +50,24 @@ WebappsRegistry.prototype = {
     if (aManifest.installs_allowed_from) {
       return aManifest.installs_allowed_from.some(function(aOrigin) {
         return aOrigin == "*" || aOrigin == aInstallOrigin;
       });
     }
     return true;
   },
 
+  // Hosted apps can't be trusted or certified, so just check that the
+  // manifest doesn't ask for those.
+  checkAppStatus: function(aManifest) {
+    let manifestStatus = aManifest.type || "web";
+    return (Services.prefs.getBoolPref("dom.mozApps.dev_mode") ||
+            manifestStatus === "web");
+  },
+
   receiveMessage: function(aMessage) {
     let msg = aMessage.json;
     if (msg.oid != this._id)
       return
     let req = this.getRequest(msg.requestID);
     if (!req)
       return;
     let app = msg.app;
@@ -87,56 +95,60 @@ WebappsRegistry.prototype = {
         Services.DOMRequest.fireError(req, "ERROR");
         break;
     }
     this.removeRequest(msg.requestID);
   },
 
   _getOrigin: function(aURL) {
     let uri = Services.io.newURI(aURL, null, null);
-    return uri.prePath; 
+    return uri.prePath;
   },
 
   // mozIDOMApplicationRegistry implementation
-  
+
   install: function(aURL, aParams) {
     let installURL = this._window.location.href;
     let installOrigin = this._getOrigin(installURL);
     let request = this.createRequest();
     let requestID = this.getRequestId(request);
     let xhr = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"].createInstance(Ci.nsIXMLHttpRequest);
     xhr.open("GET", aURL, true);
     xhr.channel.loadFlags |= Ci.nsIRequest.VALIDATE_ALWAYS;
 
     xhr.addEventListener("load", (function() {
       if (xhr.status == 200) {
         try {
           let manifest = JSON.parse(xhr.responseText, installOrigin);
           if (!this.checkManifest(manifest, installOrigin)) {
             Services.DOMRequest.fireError(request, "INVALID_MANIFEST");
           } else {
-            let receipts = (aParams && aParams.receipts && Array.isArray(aParams.receipts)) ? aParams.receipts : [];
-            let categories = (aParams && aParams.categories && Array.isArray(aParams.categories)) ? aParams.categories : [];
-            cpmm.sendAsyncMessage("Webapps:Install", { app: { installOrigin: installOrigin,
-                                                              origin: this._getOrigin(aURL),
-                                                              manifestURL: aURL,
-                                                              manifest: manifest,
-                                                              receipts: receipts,
-                                                              categories: categories },
-                                                              from: installURL,
-                                                              oid: this._id,
-                                                              requestID: requestID });
+            if (!this.checkAppStatus(manifest)) {
+              Services.DOMRequest.fireError(request, "INVALID_SECURITY_LEVEL");
+            } else {
+              let receipts = (aParams && aParams.receipts && Array.isArray(aParams.receipts)) ? aParams.receipts : [];
+              let categories = (aParams && aParams.categories && Array.isArray(aParams.categories)) ? aParams.categories : [];
+              cpmm.sendAsyncMessage("Webapps:Install", { app: { installOrigin: installOrigin,
+                                                                origin: this._getOrigin(aURL),
+                                                                manifestURL: aURL,
+                                                                manifest: manifest,
+                                                                receipts: receipts,
+                                                                categories: categories },
+                                                                from: installURL,
+                                                                oid: this._id,
+                                                                requestID: requestID });
+            }
           }
         } catch(e) {
           Services.DOMRequest.fireError(request, "MANIFEST_PARSE_ERROR");
         }
       }
       else {
         Services.DOMRequest.fireError(request, "MANIFEST_URL_ERROR");
-      }      
+      }
     }).bind(this), false);
 
     xhr.addEventListener("error", (function() {
       Services.DOMRequest.fireError(request, "NETWORK_ERROR");
     }).bind(this), false);
 
     xhr.send(null);
     return request;
@@ -190,21 +202,21 @@ WebappsRegistry.prototype = {
   init: function(aWindow) {
     this.initHelper(aWindow, ["Webapps:Install:Return:OK", "Webapps:Install:Return:KO",
                               "Webapps:GetInstalled:Return:OK",
                               "Webapps:GetSelf:Return:OK", "Webapps:GetSelf:Return:KO"]);
 
     let util = this._window.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils);
     this._id = util.outerWindowID;
   },
-  
+
   classID: Components.ID("{fff440b3-fae2-45c1-bf03-3b5a2e432270}"),
 
   QueryInterface: XPCOMUtils.generateQI([Ci.mozIDOMApplicationRegistry, Ci.nsIDOMGlobalPropertyInitializer]),
-  
+
   classInfo: XPCOMUtils.generateCI({classID: Components.ID("{fff440b3-fae2-45c1-bf03-3b5a2e432270}"),
                                     contractID: "@mozilla.org/webapps;1",
                                     interfaces: [Ci.mozIDOMApplicationRegistry],
                                     flags: Ci.nsIClassInfo.DOM_OBJECT,
                                     classDescription: "Webapps Registry"})
 }
 
 /**
@@ -289,17 +301,17 @@ WebappsApplication.prototype = {
         Services.DOMRequest.fireSuccess(req, msg.origin);
         break;
       case "Webapps:Uninstall:Return:KO":
         Services.DOMRequest.fireError(req, "NOT_INSTALLED");
         break;
       case "Webapps:OfflineCache":
         if (msg.manifest != this.manifestURL)
           return;
-        
+
         this.status = msg.status;
         if (this._onprogress) {
           let event = new this._window.MozApplicationEvent("applicationinstall", { application: this });
           this._onprogress.handleEvent(event);
         }
         break;
     }
   },
@@ -388,41 +400,41 @@ WebappsApplicationMgmt.prototype = {
       throw new Components.Exception("Denied", Cr.NS_ERROR_FAILURE);
   },
 
   receiveMessage: function(aMessage) {
     var msg = aMessage.json;
     let req = this.getRequest(msg.requestID);
     // We want Webapps:Install:Return:OK and Webapps:Uninstall:Return:OK to be boradcasted
     // to all instances of mozApps.mgmt
-    if (!((msg.oid == this._id && req) 
+    if (!((msg.oid == this._id && req)
        || aMessage.name == "Webapps:Install:Return:OK" || aMessage.name == "Webapps:Uninstall:Return:OK"))
       return;
     switch (aMessage.name) {
       case "Webapps:GetAll:Return:OK":
         Services.DOMRequest.fireSuccess(req, convertAppsArray(msg.apps, this._window));
         break;
       case "Webapps:GetAll:Return:KO":
         Services.DOMRequest.fireError(req, "DENIED");
         break;
       case "Webapps:GetNotInstalled:Return:OK":
         Services.DOMRequest.fireSuccess(req, convertAppsArray(msg.apps, this._window));
         break;
       case "Webapps:Install:Return:OK":
         if (this._oninstall) {
           let app = msg.app;
-          let event = new this._window.MozApplicationEvent("applicationinstall", 
+          let event = new this._window.MozApplicationEvent("applicationinstall",
                            { application : createApplicationObject(this._window, app.origin, app.manifest, app.manifestURL, app.receipts,
                                                                   app.installOrigin, app.installTime) });
           this._oninstall.handleEvent(event);
         }
         break;
       case "Webapps:Uninstall:Return:OK":
         if (this._onuninstall) {
-          let event = new this._window.MozApplicationEvent("applicationuninstall", 
+          let event = new this._window.MozApplicationEvent("applicationuninstall",
                            { application : createApplicationObject(this._window, msg.origin, null, null, null, null, 0) });
           this._onuninstall.handleEvent(event);
         }
         break;
     }
     this.removeRequest(msg.requestID);
   },
 
--- a/dom/apps/src/Webapps.jsm
+++ b/dom/apps/src/Webapps.jsm
@@ -479,16 +479,57 @@ let DOMApplicationRegistry = {
         dir.remove(true);
       } catch (e) { }
       ppmm.broadcastAsyncMessage("Webapps:Install:Return:KO",
                             { oid: aData.oid,
                               requestID: aData.requestID,
                               error: aError });
     }
 
+    function getInferedStatus() {
+      // XXX Update once we have digital signatures (bug 772365)
+      return Ci.nsIPrincipal.APP_STATUS_INSTALLED;
+    }
+
+    function getAppManifestStatus(aManifest) {
+      let type = aManifest.type || "web";
+      let manifestStatus = Ci.nsIPrincipal.APP_STATUS_INSTALLED;
+
+      switch(type) {
+        case "web":
+          manifestStatus = Ci.nsIPrincipal.APP_STATUS_INSTALLED;
+          break;
+        case "privileged":
+          manifestStatus = Ci.nsIPrincipal.APP_STATUS_PRIVILEGED;
+          break
+        case "certified":
+          manifestStatus = Ci.nsIPrincipal.APP_STATUS_CERTIFIED;
+          break;
+      }
+
+      return manifestStatus;
+    }
+
+    function getAppStatus(aManifest) {
+      let manifestStatus = getAppManifestStatus(aManifest);
+      let inferedStatus = getInferedStatus();
+
+      return (Services.prefs.getBoolPref("dom.mozApps.dev_mode") ? manifestStatus
+                                                                : inferedStatus);
+    }
+    // Returns true if the privilege level from the manifest
+    // is lower or equal to the one we infered for the app.
+    function checkAppStatus(aManifest) {
+      if (Services.prefs.getBoolPref("dom.mozApps.dev_mode")) {
+        return true;
+      }
+
+      return (getAppManifestStatus(aManifest) <= getInferedStatus());
+    }
+
     NetUtil.asyncFetch(aData.url, function(aInput, aResult, aRequest) {
       if (!Components.isSuccessCode(aResult)) {
         // We failed to fetch the zip.
         cleanup("NETWORK_ERROR");
         return;
       }
       // Copy the zip on disk.
       let zipFile = FileUtils.getFile("TmpD",
@@ -524,24 +565,29 @@ let DOMApplicationRegistry = {
           if (!zipReader.hasEntry("manifest.webapp")) {
             throw "No manifest.webapp found.";
           }
 
           let istream = zipReader.getInputStream("manifest.webapp");
           msg.app.manifest = JSON.parse(NetUtil.readInputStreamToString(istream,
                                         istream.available()) || "");
           if (!checkManifest(msg.app.manifest)) {
-            throw "Invalid manifest";
+            throw "INVALID_MANIFEST";
           }
 
+          if (!checkAppStatus(msg.app.manifest)) {
+            throw "INVALID_SECURITY_LEVEL";
+          }
+
+          msg.appStatus = getAppStatus(msg.app.manifest);
           Services.obs.notifyObservers(this, "webapps-ask-install",
                                              JSON.stringify(msg));
         } catch (e) {
           // XXX we may need new error messages.
-          cleanup("INVALID_MANIFEST");
+          cleanup(e);
         } finally {
           zipReader.close();
         }
       });
     });
   },
 
   uninstall: function(aData) {
--- a/modules/libpref/src/init/all.js
+++ b/modules/libpref/src/init/all.js
@@ -3684,8 +3684,12 @@ pref("memory.low_memory_notification_int
 // window to be collected via the GC/CC.
 pref("memory.ghost_window_timeout_seconds", 60);
 
 pref("social.enabled", false);
 
 // Disable idle observer fuzz, because only privileged content can access idle
 // observers (bug 780507).
 pref("dom.idle-observers-api.fuzz_time.disabled", true);
+
+// Setting that to true grant elevated privileges to apps that ask
+// for them in their manifest.
+pref("dom.mozApps.dev_mode", false);