Bug 781620 - Bridge the DOM webapps registry with nsIPrincipal::GetStatus() - Part 1 : nsPrincipal changes [r=mounir]
authorFabrice Desré <fabrice@mozilla.com>
Mon, 27 Aug 2012 19:43:57 -0700
changeset 105667 010b5cb3576da7c7c0f38d81b5bbe27a03c1ff22
parent 105666 e3da600d76429306e000d1f1dc4733ea71493ec1
child 105668 9b96029a9cf0aa09a1aee772005ef7ac53c7d0ff
push id55
push usershu@rfrn.org
push dateThu, 30 Aug 2012 01:33:09 +0000
reviewersmounir
bugs781620
milestone18.0a1
Bug 781620 - Bridge the DOM webapps registry with nsIPrincipal::GetStatus() - Part 1 : nsPrincipal changes [r=mounir]
caps/src/nsPrincipal.cpp
dom/apps/src/Webapps.jsm
dom/interfaces/apps/mozIApplication.idl
--- a/caps/src/nsPrincipal.cpp
+++ b/caps/src/nsPrincipal.cpp
@@ -25,16 +25,19 @@
 #include "nsContentUtils.h"
 #include "jswrapper.h"
 
 #include "nsPrincipal.h"
 
 #include "mozilla/Preferences.h"
 #include "mozilla/HashFunctions.h"
 
+#include "nsIAppsService.h"
+#include "mozIApplication.h"
+
 using namespace mozilla;
 
 static bool gCodeBasePrincipalSupport = false;
 static bool gIsObservingCodeBasePrincipalSupport = false;
 
 static bool URIIsImmutable(nsIURI* aURI)
 {
   nsCOMPtr<nsIMutable> mutableObj(do_QueryInterface(aURI));
@@ -1294,20 +1297,55 @@ nsPrincipal::Write(nsIObjectOutputStream
 
 uint16_t
 nsPrincipal::GetAppStatus()
 {
   MOZ_ASSERT(mAppId != nsIScriptSecurityManager::UNKNOWN_APP_ID);
 
   // Installed apps have a valid app id (not NO_APP_ID or UNKNOWN_APP_ID)
   // and they are not inside a mozbrowser.
-  return mAppId != nsIScriptSecurityManager::NO_APP_ID &&
-         mAppId != nsIScriptSecurityManager::UNKNOWN_APP_ID && !mInMozBrowser
-          ? nsIPrincipal::APP_STATUS_INSTALLED
-          : nsIPrincipal::APP_STATUS_NOT_INSTALLED;
+  if (mAppId == nsIScriptSecurityManager::NO_APP_ID ||
+      mAppId == nsIScriptSecurityManager::UNKNOWN_APP_ID || mInMozBrowser) {
+    return nsIPrincipal::APP_STATUS_NOT_INSTALLED;
+  }
+
+  nsCOMPtr<nsIAppsService> appsService = do_GetService(APPS_SERVICE_CONTRACTID);
+  NS_ENSURE_TRUE(appsService, nsIPrincipal::APP_STATUS_NOT_INSTALLED);
+
+  nsCOMPtr<mozIDOMApplication> domApp;
+  appsService->GetAppByLocalId(mAppId, getter_AddRefs(domApp));
+  nsCOMPtr<mozIApplication> app = do_QueryInterface(domApp);
+  NS_ENSURE_TRUE(app, nsIPrincipal::APP_STATUS_NOT_INSTALLED);
+
+  uint16_t status = nsIPrincipal::APP_STATUS_INSTALLED;
+  NS_ENSURE_SUCCESS(app->GetAppStatus(&status),
+                    nsIPrincipal::APP_STATUS_NOT_INSTALLED);
+
+  nsCAutoString origin;
+  NS_ENSURE_SUCCESS(GetOrigin(getter_Copies(origin)),
+                    nsIPrincipal::APP_STATUS_NOT_INSTALLED);
+  nsString appOrigin;
+  NS_ENSURE_SUCCESS(app->GetOrigin(appOrigin),
+                    nsIPrincipal::APP_STATUS_NOT_INSTALLED);
+
+  // We go from string -> nsIURI -> origin to be sure we
+  // compare two punny-encoded origins.
+  nsCOMPtr<nsIURI> appURI;
+  NS_ENSURE_SUCCESS(NS_NewURI(getter_AddRefs(appURI), appOrigin),
+                    nsIPrincipal::APP_STATUS_NOT_INSTALLED);
+
+  nsCAutoString appOriginPunned;
+  NS_ENSURE_SUCCESS(GetOriginForURI(appURI, getter_Copies(appOriginPunned)),
+                    nsIPrincipal::APP_STATUS_NOT_INSTALLED);
+
+  if (!appOriginPunned.Equals(origin)) {
+    return nsIPrincipal::APP_STATUS_NOT_INSTALLED;
+  }
+
+  return status;
 }
 
 /************************************************************************************************************************/
 
 static const char EXPANDED_PRINCIPAL_SPEC[] = "[Expanded Principal]";
 
 NS_IMPL_CLASSINFO(nsExpandedPrincipal, NULL, nsIClassInfo::MAIN_THREAD_ONLY,
                   NS_EXPANDEDPRINCIPAL_CID)
--- a/dom/apps/src/Webapps.jsm
+++ b/dom/apps/src/Webapps.jsm
@@ -75,16 +75,21 @@ let DOMApplicationRegistry = {
         this.webapps = aData;
         for (let id in this.webapps) {
 #ifdef MOZ_SYS_MSG
           this._processManifestForId(id);
 #endif
           if (!this.webapps[id].localId) {
             this.webapps[id].localId = this._nextLocalId();
           }
+
+          // Default to a non privileged status.
+          if (this.webapps[id].appStatus === undefined) {
+            this.webapps[id].appStatus = Ci.nsIPrincipal.APP_STATUS_INSTALLED;
+          }
         };
       }).bind(this));
     }
 
     try {
       let hosts = Services.prefs.getCharPref("dom.mozApps.whitelist");
       hosts.split(",").forEach(function(aHost) {
         Services.perms.add(Services.io.newURI(aHost, null, null),
@@ -285,16 +290,18 @@ let DOMApplicationRegistry = {
   // clones a app object, without the manifest
   _cloneAppObject: function(aApp) {
     let clone = {
       installOrigin: aApp.installOrigin,
       origin: aApp.origin,
       receipts: aApp.receipts ? JSON.parse(JSON.stringify(aApp.receipts)) : null,
       installTime: aApp.installTime,
       manifestURL: aApp.manifestURL,
+      appStatus: aApp.appStatus,
+      localId: aApp.localId,
       progress: aApp.progress || 0.0,
       status: aApp.status || "installed"
     };
     return clone;
   },
 
   denyInstall: function(aData) {
     let packageId = aData.app.packageId;
@@ -327,16 +334,17 @@ let DOMApplicationRegistry = {
     }
 
     if (app.packageId) {
       // Override the origin with the correct id.
       app.origin = "app://" + id;
     }
 
     let appObject = this._cloneAppObject(app);
+    appObject.appStatus = app.appStatus || Ci.nsIPrincipal.APP_STATUS_INSTALLED;
     appObject.installTime = app.installTime = Date.now();
     let appNote = JSON.stringify(appObject);
     appNote.id = id;
 
     appObject.localId = localId;
 
     let dir = FileUtils.getDir(DIRECTORY_NAME, ["webapps", id], true, true);
     let manFile = dir.clone();
@@ -691,53 +699,57 @@ let DOMApplicationRegistry = {
   getAppById: function(aId) {
     if (!this.webapps[aId])
       return null;
 
     let app = this._cloneAppObject(this.webapps[aId]);
     return app;
   },
 
+  _cloneAsMozIApplication: function(aApp) {
+    let res = this._cloneAppObject(aApp);
+    res.hasPermission = function(permission) {
+      let localId = DOMApplicationRegistry.getAppLocalIdByManifestURL(
+        this.manifestURL);
+      let uri = Services.io.newURI(this.origin, null, null);
+      let secMan = Cc["@mozilla.org/scriptsecuritymanager;1"]
+                   .getService(Ci.nsIScriptSecurityManager);
+      // XXX for the purposes of permissions checking, this helper
+      // should never been called with isBrowser=true, so we
+      // assume false here.
+      let principal = secMan.getAppCodebasePrincipal(uri, localId,
+                                                     /*mozbrowser*/false);
+      let perm = Services.perms.testExactPermissionFromPrincipal(principal,
+                                                                 permission);
+      return (perm === Ci.nsIPermissionManager.ALLOW_ACTION);
+    };
+    res.QueryInterface = XPCOMUtils.generateQI([Ci.mozIDOMApplication,
+                                                Ci.mozIApplication]);
+    return res;
+  },
+
   getAppByManifestURL: function(aManifestURL) {
     // This could be O(1) if |webapps| was a dictionary indexed on manifestURL
     // which should be the unique app identifier.
     // It's currently O(n).
     for (let id in this.webapps) {
       let app = this.webapps[id];
       if (app.manifestURL == aManifestURL) {
-        let res = this._cloneAppObject(app);
-        res.hasPermission = function(permission) {
-          let localId = DOMApplicationRegistry.getAppLocalIdByManifestURL(
-            this.manifestURL);
-          let uri = Services.io.newURI(this.manifestURL, null, null);
-          let secMan = Cc["@mozilla.org/scriptsecuritymanager;1"]
-                       .getService(Ci.nsIScriptSecurityManager);
-          // XXX for the purposes of permissions checking, this helper
-          // should always be called on !isBrowser frames, so we
-          // assume false here.
-          let principal = secMan.getAppCodebasePrincipal(uri, localId,
-                                                         /*mozbrowser*/false);
-          let perm = Services.perms.testExactPermissionFromPrincipal(principal,
-                                                                     permission);
-          return (perm === Ci.nsIPermissionManager.ALLOW_ACTION);
-        };
-        res.QueryInterface = XPCOMUtils.generateQI([Ci.mozIDOMApplication,
-                                                    Ci.mozIApplication]);
-        return res;
+        return this._cloneAsMozIApplication(app);
       }
     }
 
     return null;
   },
 
   getAppByLocalId: function(aLocalId) {
     for (let id in this.webapps) {
       let app = this.webapps[id];
       if (app.localId == aLocalId) {
-        return this._cloneAppObject(app);
+        return this._cloneAsMozIApplication(app);
       }
     }
 
     return null;
   },
 
   getManifestURLByLocalId: function(aLocalId) {
     for (let id in this.webapps) {
--- a/dom/interfaces/apps/mozIApplication.idl
+++ b/dom/interfaces/apps/mozIApplication.idl
@@ -6,14 +6,17 @@
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsIDOMApplicationRegistry.idl"
 
 /**
  * We expose Gecko-internal helpers related to "web apps" through this
  * sub-interface.
  */
-[scriptable, uuid(8de25e36-b4cb-4e89-9310-a199dce4e5f4)]
+[scriptable, uuid(acf46a46-729a-4ab4-9da3-8d59ecfd103d)]
 interface mozIApplication: mozIDOMApplication
 {
   /* Return true if this app has |permission|. */
   boolean hasPermission(in string permission);
+
+  /* Application status as defined in nsIPrincipal. */
+  readonly attribute unsigned short appStatus;
 };