Bug 777072 - 3/7 - Update nsHostTable and nsHostEntry to make them aware of new security model. r=sicking,jlebar
authorMounir Lamouri <mounir.lamouri@gmail.com>
Thu, 23 Aug 2012 11:47:55 -0700
changeset 105523 ef0744b50b6112938601a17f170292c880dc6943
parent 105522 0e1c51a8387d2298be9a4202301d406c80f22759
child 105524 789055abc89d6f8ec91ac54b4c46c5c836829880
push id55
push usershu@rfrn.org
push dateThu, 30 Aug 2012 01:33:09 +0000
reviewerssicking, jlebar
bugs777072
milestone17.0a1
Bug 777072 - 3/7 - Update nsHostTable and nsHostEntry to make them aware of new security model. r=sicking,jlebar
caps/tests/mochitest/test_principal_extendedorigin_appid_appstatus.html
dom/browser-element/mochitest/browserElement_SetVisibleFrames.js
dom/browser-element/mochitest/browserElement_SetVisibleFrames2.js
extensions/cookie/nsPermissionManager.cpp
extensions/cookie/nsPermissionManager.h
--- a/caps/tests/mochitest/test_principal_extendedorigin_appid_appstatus.html
+++ b/caps/tests/mochitest/test_principal_extendedorigin_appid_appstatus.html
@@ -384,16 +384,18 @@ function runTest() {
   for (var i=0; i<gData.length; ++i) {
     let data = gData[i];
 
     var iframe = document.createElement('iframe');
     iframe.check = function() {
       checkIFrame(this, data);
     };
     iframe.addChild = function() {
+      SpecialPowers.addPermission("browser", true, iframe.contentDocument);
+
       var childFrame = document.createElement('iframe');
 
       if (data.child.app) {
         childFrame.setAttribute('mozapp', data.child.app)
         childFrame.setAttribute('mozbrowser', '');
       } else if (data.child.browser) {
         childFrame.setAttribute('mozbrowser', '');
       }
@@ -424,16 +426,15 @@ function runTest() {
     content.appendChild(iframe);
 
     yield;
   }
 }
 
 var gTestRunner = runTest();
 
-SpecialPowers.addPermission("browser", true, "http://example.org");
 SpecialPowers.pushPrefEnv({'set':[["dom.mozBrowserFramesEnabled", true]]},
                            function() { gTestRunner.next(); });
 
 </script>
 </pre>
 </body>
 </html>
--- a/dom/browser-element/mochitest/browserElement_SetVisibleFrames.js
+++ b/dom/browser-element/mochitest/browserElement_SetVisibleFrames.js
@@ -13,16 +13,21 @@
 SimpleTest.waitForExplicitFinish();
 
 var iframe;
 
 function runTest() {
   browserElementTestHelpers.setEnabledPref(true);
   browserElementTestHelpers.addPermission();
 
+  var principal = SpecialPowers.wrap(SpecialPowers.getNodePrincipal(document));
+  SpecialPowers.addPermission("browser", true, { url: SpecialPowers.wrap(principal.URI).spec,
+                                                 appId: principal.appId,
+                                                 isInBrowserElement: true });
+
   iframe = document.createElement('iframe');
   iframe.mozbrowser = true;
 
   // Our test involves three <iframe mozbrowser>'s, parent, child1, and child2.
   // child1 and child2 are contained inside parent.  child1 is visibile, and
   // child2 is not.
   //
   // For the purposes of this test, we want there to be a process barrier
@@ -50,16 +55,22 @@ function test2() {
 
 function finish() {
   // We need to remove this listener because when this test finishes and the
   // iframe containing this document is navigated, we'll fire a
   // visibilitychange(false) event on all child iframes.  That's OK and
   // expected, but if we don't remove our listener, then we'll end up causing
   // the /next/ test to fail!
   iframe.removeEventListener('mozbrowsershowmodalprompt', checkMessage);
+
+  var principal = SpecialPowers.wrap(SpecialPowers.getNodePrincipal(document));
+  SpecialPowers.removePermission("browser", { url: SpecialPowers.wrap(principal.URI).spec,
+                                              appId: principal.appId,
+                                              isInBrowserElement: true });
+
   SimpleTest.finish();
 }
 
 var expectedMsg = null;
 var expectedMsgCallback = null;
 function expectMessage(msg, next) {
   expectedMsg = msg;
   expectedMsgCallback = next;
--- a/dom/browser-element/mochitest/browserElement_SetVisibleFrames2.js
+++ b/dom/browser-element/mochitest/browserElement_SetVisibleFrames2.js
@@ -7,16 +7,21 @@
 "use strict";
 
 SimpleTest.waitForExplicitFinish();
 
 function runTest() {
   browserElementTestHelpers.setEnabledPref(true);
   browserElementTestHelpers.addPermission();
 
+  var principal = SpecialPowers.wrap(SpecialPowers.getNodePrincipal(document));
+  SpecialPowers.addPermission("browser", true, { url: SpecialPowers.wrap(principal.URI).spec,
+                                                 appId: principal.appId,
+                                                 isInBrowserElement: true });
+
   var iframe = document.createElement('iframe');
   iframe.mozbrowser = true;
 
   // We need remote = false here until bug 761935 is fixed; see
   // SetVisibleFrames.js for an explanation.
   iframe.remote = false;
 
   iframe.addEventListener('mozbrowserloadend', function loadEnd(e) {
@@ -30,24 +35,33 @@ function runTest() {
       ok(true, "Got parent:finish");
 
       // Give any extra events a chance to fire, then end the test.
       SimpleTest.executeSoon(function() {
         SimpleTest.executeSoon(function() {
           SimpleTest.executeSoon(function() {
             SimpleTest.executeSoon(function() {
               SimpleTest.executeSoon(function() {
-                SimpleTest.finish();
+                finish();
               });
             });
           });
         });
       });
     }
     else {
       ok(false, "Got unexpected message: " + e.detail.message);
     }
   });
 
   document.body.appendChild(iframe);
 }
 
+function finish() {
+  var principal = SpecialPowers.wrap(SpecialPowers.getNodePrincipal(document));
+  SpecialPowers.removePermission("browser", { url: SpecialPowers.wrap(principal.URI).spec,
+                                              appId: principal.appId,
+                                              isInBrowserElement: true });
+
+  SimpleTest.finish();
+}
+
 runTest();
--- a/extensions/cookie/nsPermissionManager.cpp
+++ b/extensions/cookie/nsPermissionManager.cpp
@@ -66,70 +66,60 @@ ChildProcess()
 #define ENSURE_NOT_CHILD_PROCESS \
   ENSURE_NOT_CHILD_PROCESS_({ return NS_ERROR_NOT_AVAILABLE; })
 
 #define ENSURE_NOT_CHILD_PROCESS_NORET \
   ENSURE_NOT_CHILD_PROCESS_()
 
 ////////////////////////////////////////////////////////////////////////////////
 
-#define PL_ARENA_CONST_ALIGN_MASK 3
-#include "plarena.h"
-
-static PLArenaPool *gHostArena = nullptr;
-
-// making sHostArena 512b for nice allocation
-// growing is quite cheap
-#define HOST_ARENA_SIZE 512
-
-// equivalent to strdup() - does no error checking,
-// we're assuming we're only called with a valid pointer
-static char *
-ArenaStrDup(const char* str, PLArenaPool* aArena)
-{
-  void* mem;
-  const uint32_t size = strlen(str) + 1;
-  PL_ARENA_ALLOCATE(mem, aArena, size);
-  if (mem)
-    memcpy(mem, str, size);
-  return static_cast<char*>(mem);
-}
-
 namespace {
 
 nsresult
 GetPrincipalForHost(const nsACString& aHost, nsIPrincipal** aPrincipal)
 {
   nsIScriptSecurityManager* secMan = nsContentUtils::GetSecurityManager();
   NS_ENSURE_TRUE(secMan, NS_ERROR_FAILURE);
 
   nsCOMPtr<nsIURI> uri;
   // NOTE: we use "http://" as a protocal but we will just use the host so it
   // doesn't really matter.
   NS_NewURI(getter_AddRefs(uri), NS_LITERAL_CSTRING("http://") + aHost);
 
   return secMan->GetNoAppCodebasePrincipal(uri, aPrincipal);
 }
 
-} // anonymous namespace
+nsresult
+GetHostForPrincipal(nsIPrincipal* aPrincipal, nsACString& aHost)
+{
+  nsCOMPtr<nsIURI> uri;
+  nsresult rv = aPrincipal->GetURI(getter_AddRefs(uri));
+  NS_ENSURE_SUCCESS(rv, rv);
 
-nsHostEntry::nsHostEntry(const char* aHost)
-{
-  mHost = ArenaStrDup(aHost, gHostArena);
+  uri = NS_GetInnermostURI(uri);
+  NS_ENSURE_TRUE(uri, NS_ERROR_FAILURE);
+
+  rv = uri->GetAsciiHost(aHost);
+  if (NS_FAILED(rv) || aHost.IsEmpty()) {
+    return NS_ERROR_UNEXPECTED;
+  }
+
+  return NS_OK;
 }
 
-// XXX this can fail on OOM
-nsHostEntry::nsHostEntry(const nsHostEntry& toCopy)
- : mHost(toCopy.mHost)
- , mPermissions(toCopy.mPermissions)
-{
-}
+} // anonymous namespace
 
 ////////////////////////////////////////////////////////////////////////////////
 
+nsPermissionManager::PermissionKey::PermissionKey(nsIPrincipal* aPrincipal)
+{
+  MOZ_ALWAYS_TRUE(NS_SUCCEEDED(GetHostForPrincipal(aPrincipal, mHost)));
+  MOZ_ALWAYS_TRUE(NS_SUCCEEDED(aPrincipal->GetAppId(&mAppId)));
+  MOZ_ALWAYS_TRUE(NS_SUCCEEDED(aPrincipal->GetIsInBrowserElement(&mIsInBrowserElement)));
+}
 
 /**
  * Simple callback used by |AsyncClose| to trigger a treatment once
  * the database is closed.
  *
  * Note: Beware that, if you hold onto a |CloseDatabaseListener| from a
  * |nsPermissionManager|, this will create a cycle.
  *
@@ -282,17 +272,17 @@ nsPermissionManager::GetXPCOMSingleton()
   return gPermissionManager;
 }
 
 nsresult
 nsPermissionManager::Init()
 {
   nsresult rv;
 
-  mHostTable.Init();
+  mPermissionTable.Init();
 
   mObserverService = do_GetService("@mozilla.org/observer-service;1", &rv);
   if (NS_SUCCEEDED(rv)) {
     mObserverService->AddObserver(this, "profile-before-change", true);
     mObserverService->AddObserver(this, "profile-do-change", true);
   }
 
   if (IsChildProcess()) {
@@ -553,69 +543,59 @@ nsPermissionManager::AddInternal(nsIPrin
                                  const nsAFlatCString &aType,
                                  uint32_t              aPermission,
                                  int64_t               aID,
                                  uint32_t              aExpireType,
                                  int64_t               aExpireTime,
                                  NotifyOperationType   aNotifyOperation,
                                  DBOperationType       aDBOperation)
 {
-  nsCOMPtr<nsIURI> uri;
-  nsresult rv = aPrincipal->GetURI(getter_AddRefs(uri));
-  NS_ENSURE_SUCCESS(rv, rv);
-
   nsCAutoString host;
-  rv = GetHost(uri, host);
+  nsresult rv = GetHostForPrincipal(aPrincipal, host);
   NS_ENSURE_SUCCESS(rv, rv);
 
   if (!IsChildProcess()) {
     IPC::Permission permission((host),
                                (aType),
                                aPermission, aExpireType, aExpireTime);
 
     nsTArray<ContentParent*> cplist;
     ContentParent::GetAll(cplist);
     for (uint32_t i = 0; i < cplist.Length(); ++i) {
       ContentParent* cp = cplist[i];
       if (cp->NeedsPermissionsUpdate())
         unused << cp->SendAddPermission(permission);
     }
   }
 
-  if (!gHostArena) {
-    gHostArena = new PLArenaPool;
-    if (!gHostArena)
-      return NS_ERROR_OUT_OF_MEMORY;    
-    PL_INIT_ARENA_POOL(gHostArena, "PermissionHostArena", HOST_ARENA_SIZE);
-  }
-
   // look up the type index
   int32_t typeIndex = GetTypeIndex(aType.get(), true);
   NS_ENSURE_TRUE(typeIndex != -1, NS_ERROR_OUT_OF_MEMORY);
 
   // When an entry already exists, PutEntry will return that, instead
   // of adding a new one
-  nsHostEntry *entry = mHostTable.PutEntry(host.get());
+  nsRefPtr<PermissionKey> key = new PermissionKey(aPrincipal);
+  PermissionHashKey* entry = mPermissionTable.PutEntry(key);
   if (!entry) return NS_ERROR_FAILURE;
   if (!entry->GetKey()) {
-    mHostTable.RawRemoveEntry(entry);
+    mPermissionTable.RawRemoveEntry(entry);
     return NS_ERROR_OUT_OF_MEMORY;
   }
 
   // figure out the transaction type, and get any existing permission value
   OperationType op;
   int32_t index = entry->GetPermissionIndex(typeIndex);
   if (index == -1) {
     if (aPermission == nsIPermissionManager::UNKNOWN_ACTION)
       op = eOperationNone;
     else
       op = eOperationAdding;
 
   } else {
-    nsPermissionEntry oldPermissionEntry = entry->GetPermissions()[index];
+    PermissionEntry oldPermissionEntry = entry->GetPermissions()[index];
 
     // remove the permission if the permission is UNKNOWN, update the
     // permission if its value or expire type have changed OR if the time has
     // changed and the expire type is time, otherwise, don't modify.  There's
     // no need to modify a permission that doesn't expire with time when the
     // only thing changed is the expire time.
     if (aPermission == oldPermissionEntry.mPermission && 
         aExpireType == oldPermissionEntry.mExpireType &&
@@ -643,17 +623,17 @@ nsPermissionManager::AddInternal(nsIPrin
       if (aDBOperation == eWriteToDB) {
         // we'll be writing to the database - generate a known unique id
         id = ++mLargestID;
       } else {
         // we're reading from the database - use the id already assigned
         id = aID;
       }
 
-      entry->GetPermissions().AppendElement(nsPermissionEntry(typeIndex, aPermission, id, aExpireType, aExpireTime));
+      entry->GetPermissions().AppendElement(PermissionEntry(id, typeIndex, aPermission, aExpireType, aExpireTime));
 
       if (aDBOperation == eWriteToDB && aExpireType != nsIPermissionManager::EXPIRE_SESSION)
         UpdateDB(op, mStmtInsert, id, host, aType, aPermission, aExpireType, aExpireTime);
 
       if (aNotifyOperation == eNotify) {
         NotifyObserversWithPermission(host,
                                       mTypeArray[typeIndex],
                                       aPermission,
@@ -662,23 +642,23 @@ nsPermissionManager::AddInternal(nsIPrin
                                       NS_LITERAL_STRING("added").get());
       }
 
       break;
     }
 
   case eOperationRemoving:
     {
-      nsPermissionEntry oldPermissionEntry = entry->GetPermissions()[index];
+      PermissionEntry oldPermissionEntry = entry->GetPermissions()[index];
       id = oldPermissionEntry.mID;
       entry->GetPermissions().RemoveElementAt(index);
 
       // If no more types are present, remove the entry
       if (entry->GetPermissions().IsEmpty())
-        mHostTable.RawRemoveEntry(entry);
+        mPermissionTable.RawRemoveEntry(entry);
 
       if (aDBOperation == eWriteToDB)
         UpdateDB(op, mStmtDelete, id, EmptyCString(), EmptyCString(), 0, 
                  nsIPermissionManager::EXPIRE_NEVER, 0);
 
       if (aNotifyOperation == eNotify) {
         NotifyObserversWithPermission(host,
                                       mTypeArray[typeIndex],
@@ -883,17 +863,17 @@ nsPermissionManager::CommonTestPermissio
   // set the default
   *aPermission = nsIPermissionManager::UNKNOWN_ACTION;
 
   nsCOMPtr<nsIURI> uri;
   nsresult rv = aPrincipal->GetURI(getter_AddRefs(uri));
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsCAutoString host;
-  rv = GetHost(uri, host);
+  rv = GetHostForPrincipal(aPrincipal, host);
 
   // No host doesn't mean an error. Just return the default. Unless this is
   // a file uri. In that case use a magic host.
   if (NS_FAILED(rv)) {
     bool isFile;
     rv = uri->SchemeIs("file", &isFile);
     NS_ENSURE_SUCCESS(rv, rv);
     if (isFile) {
@@ -904,48 +884,74 @@ nsPermissionManager::CommonTestPermissio
     }
   }
 
   int32_t typeIndex = GetTypeIndex(aType, false);
   // If type == -1, the type isn't known,
   // so just return NS_OK
   if (typeIndex == -1) return NS_OK;
 
-  nsHostEntry *entry = GetHostEntry(host, typeIndex, aExactHostMatch);
-  if (entry)
+  uint32_t appId;
+  rv = aPrincipal->GetAppId(&appId);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  bool isInBrowserElement;
+  rv = aPrincipal->GetIsInBrowserElement(&isInBrowserElement);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  PermissionHashKey* entry = GetPermissionHashKey(host, appId, isInBrowserElement,
+                                                  typeIndex, aExactHostMatch);
+  if (entry) {
     *aPermission = entry->GetPermission(typeIndex).mPermission;
+  }
 
   return NS_OK;
 }
 
-// Get hostentry for given host string and permission type.
-// walk up the domain if needed.
-// return null if nothing found.
+// Returns PermissionHashKey for a given { host, appId, isInBrowserElement } tuple.
+// This is not simply using PermissionKey because we will walk-up domains in
+// case of |host| contains sub-domains.
+// Returns null if nothing found.
 // Also accepts host on the format "<foo>". This will perform an exact match
 // lookup as the string doesn't contain any dots.
-nsHostEntry *
-nsPermissionManager::GetHostEntry(const nsAFlatCString &aHost,
-                                  uint32_t              aType,
-                                  bool                  aExactHostMatch)
+nsPermissionManager::PermissionHashKey*
+nsPermissionManager::GetPermissionHashKey(const nsACString& aHost,
+                                          uint32_t aAppId,
+                                          bool aIsInBrowserElement,
+                                          uint32_t aType,
+                                          bool aExactHostMatch)
 {
   uint32_t offset = 0;
-  nsHostEntry *entry;
+  PermissionHashKey* entry;
   int64_t now = PR_Now() / 1000;
 
   do {
-    entry = mHostTable.GetEntry(aHost.get() + offset);
+    nsRefPtr<PermissionKey> key = new PermissionKey(Substring(aHost, offset), aAppId, aIsInBrowserElement);
+    entry = mPermissionTable.GetEntry(key);
     if (entry) {
-      nsPermissionEntry permEntry = entry->GetPermission(aType);
+      PermissionEntry permEntry = entry->GetPermission(aType);
 
       // if the entry is expired, remove and keep looking for others.
       if (permEntry.mExpireType == nsIPermissionManager::EXPIRE_TIME &&
-          permEntry.mExpireTime <= now)
-        Remove(aHost, mTypeArray[aType].get());
-      else if (permEntry.mPermission != nsIPermissionManager::UNKNOWN_ACTION)
+          permEntry.mExpireTime <= now) {
+        nsCOMPtr<nsIPrincipal> principal;
+        nsIScriptSecurityManager* secMan = nsContentUtils::GetSecurityManager();
+        MOZ_ASSERT(secMan, "No security manager!?");
+
+        nsCOMPtr<nsIURI> uri;
+        NS_NewURI(getter_AddRefs(uri), NS_LITERAL_CSTRING("http://") + aHost);
+
+        if (NS_FAILED(secMan->GetAppCodebasePrincipal(uri, aAppId, aIsInBrowserElement, getter_AddRefs(principal)))) {
+          return nullptr;
+        }
+
+        RemoveFromPrincipal(principal, mTypeArray[aType].get());
+      } else if (permEntry.mPermission != nsIPermissionManager::UNKNOWN_ACTION) {
         break;
+      }
 
       // reset entry, to be able to return null on failure
       entry = nullptr;
     }
     if (aExactHostMatch)
       break; // do not try super domains
 
     offset = aHost.FindChar('.', offset) + 1;
@@ -963,24 +969,24 @@ struct nsGetEnumeratorData
    : array(aArray)
    , types(aTypes) {}
 
   nsCOMArray<nsIPermission> *array;
   const nsTArray<nsCString> *types;
 };
 
 static PLDHashOperator
-AddPermissionsToList(nsHostEntry *entry, void *arg)
+AddPermissionsToList(nsPermissionManager::PermissionHashKey* entry, void *arg)
 {
   nsGetEnumeratorData *data = static_cast<nsGetEnumeratorData *>(arg);
 
   for (uint32_t i = 0; i < entry->GetPermissions().Length(); ++i) {
-    nsPermissionEntry &permEntry = entry->GetPermissions()[i];
+    nsPermissionManager::PermissionEntry& permEntry = entry->GetPermissions()[i];
 
-    nsPermission *perm = new nsPermission(entry->GetHost(), 
+    nsPermission *perm = new nsPermission(entry->GetKey()->mHost,
                                           data->types->ElementAt(permEntry.mType),
                                           permEntry.mPermission,
                                           permEntry.mExpireType,
                                           permEntry.mExpireTime);
 
     data->array->AppendObject(perm);
   }
 
@@ -988,17 +994,17 @@ AddPermissionsToList(nsHostEntry *entry,
 }
 
 NS_IMETHODIMP nsPermissionManager::GetEnumerator(nsISimpleEnumerator **aEnum)
 {
   // roll an nsCOMArray of all our permissions, then hand out an enumerator
   nsCOMArray<nsIPermission> array;
   nsGetEnumeratorData data(&array, &mTypeArray);
 
-  mHostTable.EnumerateEntries(AddPermissionsToList, &data);
+  mPermissionTable.EnumerateEntries(AddPermissionsToList, &data);
 
   return NS_NewArrayEnumerator(aEnum, array);
 }
 
 NS_IMETHODIMP nsPermissionManager::Observe(nsISupports *aSubject, const char *aTopic, const PRUnichar *someData)
 {
   ENSURE_NOT_CHILD_PROCESS;
 
@@ -1026,22 +1032,18 @@ NS_IMETHODIMP nsPermissionManager::Obser
 //*** nsPermissionManager private methods
 //*****************************************************************************
 
 nsresult
 nsPermissionManager::RemoveAllFromMemory()
 {
   mLargestID = 0;
   mTypeArray.Clear();
-  mHostTable.Clear();
-  if (gHostArena) {
-    PL_FinishArenaPool(gHostArena);
-    delete gHostArena;
-  }
-  gHostArena = nullptr;
+  mPermissionTable.Clear();
+
   return NS_OK;
 }
 
 // Returns -1 on failure
 int32_t
 nsPermissionManager::GetTypeIndex(const char *aType,
                                   bool        aAdd)
 {
@@ -1252,30 +1254,16 @@ nsPermissionManager::NormalizeToACE(nsCS
     nsresult rv;
     mIDNService = do_GetService(NS_IDNSERVICE_CONTRACTID, &rv);
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   return mIDNService->ConvertUTF8toACE(aHost, aHost);
 }
 
-nsresult
-nsPermissionManager::GetHost(nsIURI *aURI, nsACString &aResult)
-{
-  nsCOMPtr<nsIURI> innerURI = NS_GetInnermostURI(aURI);
-  if (!innerURI) return NS_ERROR_FAILURE;
-
-  nsresult rv = innerURI->GetAsciiHost(aResult);
-
-  if (NS_FAILED(rv) || aResult.IsEmpty())
-    return NS_ERROR_UNEXPECTED;
-
-  return NS_OK;
-}
-
 void
 nsPermissionManager::UpdateDB(OperationType         aOp,
                               mozIStorageStatement* aStmt,
                               int64_t               aID,
                               const nsACString     &aHost,
                               const nsACString     &aType,
                               uint32_t              aPermission,
                               uint32_t              aExpireType,
--- a/extensions/cookie/nsPermissionManager.h
+++ b/extensions/cookie/nsPermissionManager.h
@@ -11,125 +11,152 @@
 #include "nsIObserverService.h"
 #include "nsWeakReference.h"
 #include "nsCOMPtr.h"
 #include "nsIFile.h"
 #include "nsTHashtable.h"
 #include "nsTArray.h"
 #include "nsString.h"
 #include "nsPermission.h"
+#include "nsHashKeys.h"
+#include "nsAutoPtr.h"
 
 class nsIPermission;
 class nsIIDNService;
 class mozIStorageConnection;
 class mozIStorageStatement;
 
 ////////////////////////////////////////////////////////////////////////////////
 
-class nsPermissionEntry
-{
-public:
-  nsPermissionEntry(uint32_t aType, uint32_t aPermission, int64_t aID, 
-                    uint32_t aExpireType, int64_t aExpireTime)
-   : mType(aType)
-   , mPermission(aPermission)
-   , mID(aID)
-   , mExpireType(aExpireType)
-   , mExpireTime(aExpireTime) {}
-
-  uint32_t mType;
-  uint32_t mPermission;
-  int64_t  mID;
-  uint32_t mExpireType;
-  int64_t  mExpireTime;
-};
-
-class nsHostEntry : public PLDHashEntryHdr
-{
-public:
-  // Hash methods
-  typedef const char* KeyType;
-  typedef const char* KeyTypePointer;
-
-  nsHostEntry(const char* aHost);
-  nsHostEntry(const nsHostEntry& toCopy);
-
-  ~nsHostEntry()
-  {
-  }
-
-  KeyType GetKey() const
-  {
-    return mHost;
-  }
-
-  bool KeyEquals(KeyTypePointer aKey) const
-  {
-    return !strcmp(mHost, aKey);
-  }
-
-  static KeyTypePointer KeyToPointer(KeyType aKey)
-  {
-    return aKey;
-  }
-
-  static PLDHashNumber HashKey(KeyTypePointer aKey)
-  {
-    // PL_DHashStringKey doesn't use the table parameter, so we can safely
-    // pass nullptr
-    return PL_DHashStringKey(nullptr, aKey);
-  }
-
-  // force the hashtable to use the copy constructor when shuffling entries
-  // around, otherwise the Auto part of our nsAutoTArray won't be happy!
-  enum { ALLOW_MEMMOVE = false };
-
-  // Permissions methods
-  inline const nsDependentCString GetHost() const
-  {
-    return nsDependentCString(mHost);
-  }
-
-  inline nsTArray<nsPermissionEntry> & GetPermissions()
-  {
-    return mPermissions;
-  }
-
-  inline int32_t GetPermissionIndex(uint32_t aType) const
-  {
-    for (uint32_t i = 0; i < mPermissions.Length(); ++i)
-      if (mPermissions[i].mType == aType)
-        return i;
-
-    return -1;
-  }
-
-  inline nsPermissionEntry GetPermission(uint32_t aType) const
-  {
-    for (uint32_t i = 0; i < mPermissions.Length(); ++i)
-      if (mPermissions[i].mType == aType)
-        return mPermissions[i];
-
-    // unknown permission... return relevant data 
-    nsPermissionEntry unk = nsPermissionEntry(aType, nsIPermissionManager::UNKNOWN_ACTION,
-                                              -1, nsIPermissionManager::EXPIRE_NEVER, 0);
-    return unk;
-  }
-
-private:
-  const char *mHost;
-  nsAutoTArray<nsPermissionEntry, 1> mPermissions;
-};
-
-
 class nsPermissionManager : public nsIPermissionManager,
                             public nsIObserver,
                             public nsSupportsWeakReference
 {
 public:
+  class PermissionEntry
+  {
+  public:
+    PermissionEntry(int64_t aID, uint32_t aType, uint32_t aPermission,
+                    uint32_t aExpireType, int64_t aExpireTime)
+     : mID(aID)
+     , mType(aType)
+     , mPermission(aPermission)
+     , mExpireType(aExpireType)
+     , mExpireTime(aExpireTime)
+    {}
+
+    int64_t  mID;
+    uint32_t mType;
+    uint32_t mPermission;
+    uint32_t mExpireType;
+    int64_t  mExpireTime;
+  };
+
+  /**
+   * PermissionKey is the key used by PermissionHashKey hash table.
+   *
+   * NOTE: It could be implementing nsIHashable but there is no reason to worry
+   * with XPCOM interfaces while we don't need to.
+   */
+  class PermissionKey
+  {
+  public:
+    PermissionKey(nsIPrincipal* aPrincipal);
+    PermissionKey(const nsACString& aHost,
+                  uint32_t aAppId,
+                  bool aIsInBrowserElement)
+      : mHost(aHost)
+      , mAppId(aAppId)
+      , mIsInBrowserElement(aIsInBrowserElement)
+    {
+    }
+
+    bool operator==(const PermissionKey& aKey) const {
+      return mHost.Equals(aKey.mHost) &&
+             mAppId == aKey.mAppId &&
+             mIsInBrowserElement == aKey.mIsInBrowserElement;
+    }
+
+    PLDHashNumber GetHashCode() const {
+      nsCAutoString str;
+      str.Assign(mHost);
+      str.AppendInt(mAppId);
+      str.AppendInt(static_cast<int32_t>(mIsInBrowserElement));
+
+      return mozilla::HashString(str);
+    }
+
+    NS_INLINE_DECL_THREADSAFE_REFCOUNTING(PermissionKey);
+
+    nsCString mHost;
+    uint32_t  mAppId;
+    bool      mIsInBrowserElement;
+
+  private:
+    // Default ctor shouldn't be used.
+    PermissionKey() MOZ_DELETE;
+
+    // Dtor shouldn't be used outside of the class.
+    ~PermissionKey() {};
+  };
+
+  class PermissionHashKey : public nsRefPtrHashKey<PermissionKey>
+  {
+  public:
+    PermissionHashKey(const PermissionKey* aPermissionKey)
+      : nsRefPtrHashKey<PermissionKey>(aPermissionKey)
+    {}
+
+    PermissionHashKey(const PermissionHashKey& toCopy)
+      : nsRefPtrHashKey<PermissionKey>(toCopy)
+      , mPermissions(toCopy.mPermissions)
+    {}
+
+    bool KeyEquals(const PermissionKey* aKey) const
+    {
+      return *aKey == *GetKey();
+    }
+
+    static PLDHashNumber HashKey(const PermissionKey* aKey)
+    {
+      return aKey->GetHashCode();
+    }
+
+    // Force the hashtable to use the copy constructor when shuffling entries
+    // around, otherwise the Auto part of our nsAutoTArray won't be happy!
+    enum { ALLOW_MEMMOVE = false };
+
+    inline nsTArray<PermissionEntry> & GetPermissions()
+    {
+      return mPermissions;
+    }
+
+    inline int32_t GetPermissionIndex(uint32_t aType) const
+    {
+      for (uint32_t i = 0; i < mPermissions.Length(); ++i)
+        if (mPermissions[i].mType == aType)
+          return i;
+
+      return -1;
+    }
+
+    inline PermissionEntry GetPermission(uint32_t aType) const
+    {
+      for (uint32_t i = 0; i < mPermissions.Length(); ++i)
+        if (mPermissions[i].mType == aType)
+          return mPermissions[i];
+
+      // unknown permission... return relevant data 
+      return PermissionEntry(-1, aType, nsIPermissionManager::UNKNOWN_ACTION,
+                             nsIPermissionManager::EXPIRE_NEVER, 0);
+    }
+
+  private:
+    nsAutoTArray<PermissionEntry, 1> mPermissions;
+  };
 
   // nsISupports
   NS_DECL_ISUPPORTS
   NS_DECL_NSIPERMISSIONMANAGER
   NS_DECL_NSIOBSERVER
 
   nsPermissionManager();
   virtual ~nsPermissionManager();
@@ -159,23 +186,24 @@ public:
                        uint32_t aPermission,
                        int64_t aID,
                        uint32_t aExpireType,
                        int64_t  aExpireTime,
                        NotifyOperationType aNotifyOperation,
                        DBOperationType aDBOperation);
 
 private:
-
   int32_t GetTypeIndex(const char *aTypeString,
                        bool        aAdd);
 
-  nsHostEntry *GetHostEntry(const nsAFlatCString &aHost,
-                            uint32_t              aType,
-                            bool                  aExactHostMatch);
+  PermissionHashKey* GetPermissionHashKey(const nsACString& aHost,
+                                          uint32_t aAppId,
+                                          bool aIsInBrowserElement,
+                                          uint32_t          aType,
+                                          bool              aExactHostMatch);
 
   nsresult CommonTestPermission(nsIPrincipal* aPrincipal,
                                 const char *aType,
                                 uint32_t   *aPermission,
                                 bool        aExactHostMatch);
 
   nsresult InitDB(bool aRemoveFile);
   nsresult CreateTable();
@@ -191,17 +219,16 @@ private:
 
   // Finalize all statements, close the DB and null it.
   // if aRebuildOnSuccess, reinitialize database
   void     CloseDB(bool aRebuildOnSuccess = false);
 
   nsresult RemoveAllInternal(bool aNotifyObservers);
   nsresult RemoveAllFromMemory();
   nsresult NormalizeToACE(nsCString &aHost);
-  nsresult GetHost(nsIURI *aURI, nsACString &aResult);
   static void UpdateDB(OperationType         aOp,
                        mozIStorageStatement* aStmt,
                        int64_t               aID,
                        const nsACString     &aHost,
                        const nsACString     &aType,
                        uint32_t              aPermission,
                        uint32_t              aExpireType,
                        int64_t               aExpireTime);
@@ -209,17 +236,17 @@ private:
   nsCOMPtr<nsIObserverService> mObserverService;
   nsCOMPtr<nsIIDNService>      mIDNService;
 
   nsCOMPtr<mozIStorageConnection> mDBConn;
   nsCOMPtr<mozIStorageStatement> mStmtInsert;
   nsCOMPtr<mozIStorageStatement> mStmtDelete;
   nsCOMPtr<mozIStorageStatement> mStmtUpdate;
 
-  nsTHashtable<nsHostEntry>    mHostTable;
+  nsTHashtable<PermissionHashKey> mPermissionTable;
   // a unique, monotonically increasing id used to identify each database entry
   int64_t                      mLargestID;
 
   // An array to store the strings identifying the different types.
   nsTArray<nsCString>          mTypeArray;
 
   // Initially, |false|. Set to |true| once shutdown has started, to avoid
   // reopening the database.