diff --git a/caps/tests/mochitest/test_principal_extendedorigin_appid_appstatus.html b/caps/tests/mochitest/test_principal_extendedorigin_appid_appstatus.html index 70dadb71865..ef3a9c35965 100644 --- a/caps/tests/mochitest/test_principal_extendedorigin_appid_appstatus.html +++ b/caps/tests/mochitest/test_principal_extendedorigin_appid_appstatus.html @@ -389,6 +389,8 @@ function runTest() { checkIFrame(this, data); }; iframe.addChild = function() { + SpecialPowers.addPermission("browser", true, iframe.contentDocument); + var childFrame = document.createElement('iframe'); if (data.child.app) { @@ -429,7 +431,6 @@ function runTest() { var gTestRunner = runTest(); -SpecialPowers.addPermission("browser", true, "http://example.org"); SpecialPowers.pushPrefEnv({'set':[["dom.mozBrowserFramesEnabled", true]]}, function() { gTestRunner.next(); }); diff --git a/dom/browser-element/mochitest/browserElement_SetVisibleFrames.js b/dom/browser-element/mochitest/browserElement_SetVisibleFrames.js index 3585928449a..e5a36d2b7df 100644 --- a/dom/browser-element/mochitest/browserElement_SetVisibleFrames.js +++ b/dom/browser-element/mochitest/browserElement_SetVisibleFrames.js @@ -18,6 +18,11 @@ 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; @@ -55,6 +60,12 @@ function finish() { // 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(); } diff --git a/dom/browser-element/mochitest/browserElement_SetVisibleFrames2.js b/dom/browser-element/mochitest/browserElement_SetVisibleFrames2.js index 59b52aa8198..6e7a9b4542c 100644 --- a/dom/browser-element/mochitest/browserElement_SetVisibleFrames2.js +++ b/dom/browser-element/mochitest/browserElement_SetVisibleFrames2.js @@ -12,6 +12,11 @@ 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; @@ -35,7 +40,7 @@ function runTest() { SimpleTest.executeSoon(function() { SimpleTest.executeSoon(function() { SimpleTest.executeSoon(function() { - SimpleTest.finish(); + finish(); }); }); }); @@ -50,4 +55,13 @@ function runTest() { 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(); diff --git a/extensions/cookie/nsPermissionManager.cpp b/extensions/cookie/nsPermissionManager.cpp index 6ef4a1dedfe..21f633b77c1 100644 --- a/extensions/cookie/nsPermissionManager.cpp +++ b/extensions/cookie/nsPermissionManager.cpp @@ -71,28 +71,6 @@ ChildProcess() //////////////////////////////////////////////////////////////////////////////// -#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(mem); -} - namespace { nsresult @@ -109,22 +87,34 @@ GetPrincipalForHost(const nsACString& aHost, nsIPrincipal** aPrincipal) return secMan->GetNoAppCodebasePrincipal(uri, aPrincipal); } +nsresult +GetHostForPrincipal(nsIPrincipal* aPrincipal, nsACString& aHost) +{ + nsCOMPtr uri; + nsresult rv = aPrincipal->GetURI(getter_AddRefs(uri)); + NS_ENSURE_SUCCESS(rv, rv); + + 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; +} + } // anonymous namespace -nsHostEntry::nsHostEntry(const char* aHost) -{ - mHost = ArenaStrDup(aHost, gHostArena); -} - -// XXX this can fail on OOM -nsHostEntry::nsHostEntry(const nsHostEntry& toCopy) - : mHost(toCopy.mHost) - , mPermissions(toCopy.mPermissions) -{ -} - //////////////////////////////////////////////////////////////////////////////// +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 @@ -287,7 +277,7 @@ nsPermissionManager::Init() { nsresult rv; - mHostTable.Init(); + mPermissionTable.Init(); mObserverService = do_GetService("@mozilla.org/observer-service;1", &rv); if (NS_SUCCEEDED(rv)) { @@ -558,12 +548,8 @@ nsPermissionManager::AddInternal(nsIPrincipal* aPrincipal, NotifyOperationType aNotifyOperation, DBOperationType aDBOperation) { - nsCOMPtr 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()) { @@ -580,23 +566,17 @@ nsPermissionManager::AddInternal(nsIPrincipal* aPrincipal, } } - 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 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; } @@ -610,7 +590,7 @@ nsPermissionManager::AddInternal(nsIPrincipal* aPrincipal, 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 @@ -648,7 +628,7 @@ nsPermissionManager::AddInternal(nsIPrincipal* aPrincipal, 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); @@ -667,13 +647,13 @@ nsPermissionManager::AddInternal(nsIPrincipal* aPrincipal, 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, @@ -888,7 +868,7 @@ nsPermissionManager::CommonTestPermission(nsIPrincipal* aPrincipal, 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. @@ -909,38 +889,64 @@ nsPermissionManager::CommonTestPermission(nsIPrincipal* aPrincipal, // 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 "". 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 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 principal; + nsIScriptSecurityManager* secMan = nsContentUtils::GetSecurityManager(); + MOZ_ASSERT(secMan, "No security manager!?"); + + nsCOMPtr 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; @@ -968,14 +974,14 @@ struct nsGetEnumeratorData }; static PLDHashOperator -AddPermissionsToList(nsHostEntry *entry, void *arg) +AddPermissionsToList(nsPermissionManager::PermissionHashKey* entry, void *arg) { nsGetEnumeratorData *data = static_cast(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, @@ -993,7 +999,7 @@ NS_IMETHODIMP nsPermissionManager::GetEnumerator(nsISimpleEnumerator **aEnum) nsCOMArray array; nsGetEnumeratorData data(&array, &mTypeArray); - mHostTable.EnumerateEntries(AddPermissionsToList, &data); + mPermissionTable.EnumerateEntries(AddPermissionsToList, &data); return NS_NewArrayEnumerator(aEnum, array); } @@ -1031,12 +1037,8 @@ nsPermissionManager::RemoveAllFromMemory() { mLargestID = 0; mTypeArray.Clear(); - mHostTable.Clear(); - if (gHostArena) { - PL_FinishArenaPool(gHostArena); - delete gHostArena; - } - gHostArena = nullptr; + mPermissionTable.Clear(); + return NS_OK; } @@ -1257,20 +1259,6 @@ nsPermissionManager::NormalizeToACE(nsCString &aHost) return mIDNService->ConvertUTF8toACE(aHost, aHost); } -nsresult -nsPermissionManager::GetHost(nsIURI *aURI, nsACString &aResult) -{ - nsCOMPtr 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, diff --git a/extensions/cookie/nsPermissionManager.h b/extensions/cookie/nsPermissionManager.h index c6676151666..cd4c40be899 100644 --- a/extensions/cookie/nsPermissionManager.h +++ b/extensions/cookie/nsPermissionManager.h @@ -16,6 +16,8 @@ #include "nsTArray.h" #include "nsString.h" #include "nsPermission.h" +#include "nsHashKeys.h" +#include "nsAutoPtr.h" class nsIPermission; class nsIIDNService; @@ -24,107 +26,132 @@ 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 & 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 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(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 + { + public: + PermissionHashKey(const PermissionKey* aPermissionKey) + : nsRefPtrHashKey(aPermissionKey) + {} + + PermissionHashKey(const PermissionHashKey& toCopy) + : nsRefPtrHashKey(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 & 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 mPermissions; + }; // nsISupports NS_DECL_ISUPPORTS @@ -164,13 +191,14 @@ public: 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, @@ -196,7 +224,6 @@ private: 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, @@ -214,7 +241,7 @@ private: nsCOMPtr mStmtDelete; nsCOMPtr mStmtUpdate; - nsTHashtable mHostTable; + nsTHashtable mPermissionTable; // a unique, monotonically increasing id used to identify each database entry int64_t mLargestID;