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 103228 8700d3b9054d191a2a71cf45534969860081ac7d
parent 103227 13c586b3c010fc9ddfce4e19a7b26e07e73eccc9
child 103229 0e6ed173961e3d1faf0dd8c64247689e45c72dc0
push id13890
push userryanvm@gmail.com
push dateThu, 23 Aug 2012 23:50:55 +0000
treeherdermozilla-inbound@e137f28dfe70 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssicking, jlebar
bugs777072
milestone17.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
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.