move cookies to use hashtables instead of a sorted list, and implement a sane notifications

system for cookies and permissions. this will improve perf a bunch, and also sets the stage
for a more performant and flexible cookiemanager UI by providing better notifications.

bugs 143939 and 221185, with r=bsmedberg+mvl, sr=darin and r=mvl, sr=bz respectively.
This commit is contained in:
dwitte%stanford.edu 2003-10-22 06:53:19 +00:00
Родитель fcc14acdea
Коммит 7b125ba0e6
9 изменённых файлов: 714 добавлений и 524 удалений

Просмотреть файл

@ -41,8 +41,24 @@
* used to persistenly store permissions for different object types (cookies,
* images etc) on a site-by-site basis.
*
* This service broadcasts a PERM_CHANGE_NOTIFICATION notification
* when any permission changes. You can add yourself as an observer.
* This service broadcasts the following notification when the permission list
* is changed:
*
* topic : "perm-changed" (PERM_CHANGE_NOTIFICATION)
* broadcast whenever the permission list changes in some way. there
* are four possible data strings for this notification; one
* notification will be broadcast for each change, and will involve
* a single permission.
* subject: an nsIPermission interface pointer representing the permission object
* that changed.
* data : "deleted"
* a permission was deleted. the subject is the deleted permission.
* "added"
* a permission was added. the subject is the added permission.
* "changed"
* a permission was changed. the subject is the new permission.
* "cleared"
* the entire permission list was cleared. the subject is null.
*/
#include "nsISupports.idl"

Просмотреть файл

@ -46,7 +46,6 @@
////////////////////////////////////////////////////////////////////////////////
// XXX how do we choose this number?
#define PL_ARENA_CONST_ALIGN_MASK 3
#include "plarena.h"
@ -237,15 +236,9 @@ nsPermissionManager::Add(nsIURI *aURI,
if (typeIndex == -1 || aPermission >= NUMBER_OF_PERMISSIONS)
return NS_ERROR_FAILURE;
rv = AddInternal(hostPort, typeIndex, aPermission);
rv = AddInternal(hostPort, typeIndex, aPermission, PR_TRUE);
if (NS_FAILED(rv)) return rv;
// Notify permission manager dialog to update its display
//
// This used to be conditional, but now we use AddInternal
// for cases when no notification is needed
NotifyObservers(hostPort);
mChangedList = PR_TRUE;
Write();
return NS_OK;
@ -255,8 +248,9 @@ nsPermissionManager::Add(nsIURI *aURI,
// bounds check aTypeIndex or aPermission. These are up to the caller.
nsresult
nsPermissionManager::AddInternal(const nsAFlatCString &aHost,
PRInt32 aTypeIndex,
PRUint32 aPermission)
PRInt32 aTypeIndex,
PRUint32 aPermission,
PRBool aNotify)
{
if (!gHostArena) {
gHostArena = new PLArenaPool;
@ -265,7 +259,7 @@ nsPermissionManager::AddInternal(const nsAFlatCString &aHost,
PL_INIT_ARENA_POOL(gHostArena, "PermissionHostArena", HOST_ARENA_SIZE);
}
// When an entry already exists, AddEntry will return that, instead
// When an entry already exists, PutEntry will return that, instead
// of adding a new one
nsHostEntry *entry = mHostTable.PutEntry(aHost.get());
if (!entry) return NS_ERROR_FAILURE;
@ -273,8 +267,39 @@ nsPermissionManager::AddInternal(const nsAFlatCString &aHost,
if (entry->PermissionsAreEmpty()) {
++mHostCount;
}
PRUint32 oldPermission = entry->GetPermission(aTypeIndex);
entry->SetPermission(aTypeIndex, aPermission);
// check whether we are deleting, adding, or changing a permission,
// so we can notify observers. this would be neater to do in Add(),
// but we need to do it here because we only know what type of notification
// to send (removal, addition, or change) after we've done the hash
// lookup.
if (aNotify) {
if (aPermission == nsIPermissionManager::UNKNOWN_ACTION) {
if (oldPermission != nsIPermissionManager::UNKNOWN_ACTION)
// deleting
NotifyObserversWithPermission(aHost,
mTypeArray[aTypeIndex],
oldPermission,
NS_LITERAL_STRING("deleted").get());
} else {
if (oldPermission == nsIPermissionManager::UNKNOWN_ACTION)
// adding
NotifyObserversWithPermission(aHost,
mTypeArray[aTypeIndex],
aPermission,
NS_LITERAL_STRING("added").get());
else
// changing
NotifyObserversWithPermission(aHost,
mTypeArray[aTypeIndex],
aPermission,
NS_LITERAL_STRING("changed").get());
}
}
return NS_OK;
}
@ -290,6 +315,9 @@ nsPermissionManager::Remove(const nsACString &aHost,
nsHostEntry* entry = mHostTable.GetEntry(PromiseFlatCString(aHost).get());
if (entry) {
// cache the old permission before we delete it, to notify observers
PRUint32 oldPermission = entry->GetPermission(typeIndex);
entry->SetPermission(typeIndex, nsIPermissionManager::UNKNOWN_ACTION);
// If no more types are present, remove the entry
@ -301,7 +329,11 @@ nsPermissionManager::Remove(const nsACString &aHost,
Write();
// Notify Observers
NotifyObservers(aHost);
if (oldPermission != nsIPermissionManager::UNKNOWN_ACTION)
NotifyObserversWithPermission(PromiseFlatCString(aHost),
aType,
oldPermission,
NS_LITERAL_STRING("deleted").get());
}
return NS_OK;
}
@ -310,6 +342,7 @@ NS_IMETHODIMP
nsPermissionManager::RemoveAll()
{
RemoveAllFromMemory();
NotifyObservers(nsnull, NS_LITERAL_STRING("cleared").get());
Write();
return NS_OK;
}
@ -493,23 +526,34 @@ nsPermissionManager::GetTypeIndex(const char *aType,
return firstEmpty;
}
// broadcast a notification that a permission has changed
nsresult
nsPermissionManager::NotifyObservers(const nsACString &aHost)
// wrapper function for mangling (host,type,perm) triplet into an nsIPermission.
void
nsPermissionManager::NotifyObserversWithPermission(const nsACString &aHost,
const char *aType,
PRUint32 aPermission,
const PRUnichar *aData)
{
if (mObserverService) {
return mObserverService->NotifyObservers(NS_STATIC_CAST(nsIPermissionManager *, this),
kPermissionChangeNotification,
NS_ConvertUTF8toUCS2(aHost).get());
}
return NS_ERROR_FAILURE;
nsCOMPtr<nsIPermission> permission =
new nsPermission(aHost, nsDependentCString(aType), aPermission);
if (permission)
NotifyObservers(permission, aData);
}
// Note:
// We don't do checkbox states here anymore.
// When a consumer wants it back, that is up to the consumer, not this backend
// For cookies, it is now done with a persist in the dialog xul file.
// notify observers that the permission list changed. there are four possible
// values for aData:
// "deleted" means a permission was deleted. aPermission is the deleted permission.
// "added" means a permission was added. aPermission is the added permission.
// "changed" means a permission was altered. aPermission is the new permission.
// "cleared" means the entire permission list was cleared. aPermission is null.
void
nsPermissionManager::NotifyObservers(nsIPermission *aPermission,
const PRUnichar *aData)
{
if (mObserverService)
mObserverService->NotifyObservers(aPermission,
kPermissionChangeNotification,
aData);
}
static const char kTab = '\t';
static const char kNew = '\n';
@ -642,7 +686,7 @@ nsPermissionManager::Read()
// Ignore @@@ as host. Old style checkbox status
if (!permissionString.IsEmpty() && !host.Equals(NS_LITERAL_CSTRING("@@@@"))) {
rv = AddInternal(host, type, permission);
rv = AddInternal(host, type, permission, PR_FALSE);
if (NS_FAILED(rv)) return rv;
}

Просмотреть файл

@ -49,6 +49,8 @@
#include "nsTHashtable.h"
#include "nsString.h"
class nsIPermission;
////////////////////////////////////////////////////////////////////////////////
// This allow us 8 types of permissions, with 256 values for each
@ -160,13 +162,18 @@ private:
nsresult AddInternal(const nsAFlatCString &aHost,
PRInt32 aTypeIndex,
PRUint32 aPermission);
PRUint32 aPermission,
PRBool aNotify);
PRInt32 GetTypeIndex(const char *aTypeString,
PRBool aAdd);
nsresult Read();
nsresult Write();
nsresult NotifyObservers(const nsACString &aHost);
void NotifyObserversWithPermission(const nsACString &aHost,
const char *aType,
PRUint32 aPermission,
const PRUnichar *aData);
void NotifyObservers(nsIPermission *aPermission, const PRUnichar *aData);
nsresult RemoveAllFromMemory();
nsresult GetHostPort(nsIURI *aURI, nsACString &aResult);
void RemoveTypeStrings();

Просмотреть файл

@ -49,6 +49,13 @@
interface nsICookie2 : nsICookie
{
/**
* the host (possibly fully qualified) of the cookie,
* without a leading dot to represent if it is a
* domain cookie.
*/
readonly attribute AUTF8String rawHost;
/**
* true if the cookie is a session cookie
* (note: if true, the expiry time is undefined).

Просмотреть файл

@ -47,6 +47,32 @@ interface nsIChannel;
* Provides methods for setting and getting cookies in the context of a
* page load. See nsICookieManager for methods to manipulate the cookie
* database directly. This separation of interface is mainly historical.
*
* This service broadcasts the following notifications when the cookie
* list is changed, or a cookie is rejected:
*
* topic : "cookie-changed"
* broadcast whenever the cookie list changes in some way. there
* are four possible data strings for this notification; one
* notification will be broadcast for each change, and will involve
* a single cookie.
* subject: an nsICookie2 interface pointer representing the cookie object
* that changed.
* data : "deleted"
* a cookie was deleted. the subject is the deleted cookie.
* "added"
* a cookie was added. the subject is the added cookie.
* "changed"
* a cookie was changed. the subject is the new cookie.
* "cleared"
* the entire cookie list was cleared. the subject is null.
*
* topic : "cookie-rejected"
* broadcast whenever a cookie was rejected from being set as a
* result of user prefs.
* subject: an nsIURI interface pointer representing the URI that attempted
* to set the cookie.
* data : none.
*/
[scriptable, uuid(011C3190-1434-11d6-A618-0010A401EB10)]
interface nsICookieService : nsISupports

Просмотреть файл

@ -94,7 +94,8 @@ nsCookie::nsCookie(const nsACString &aName,
PRBool aIsSecure,
nsCookieStatus aStatus,
nsCookiePolicy aPolicy)
: mExpiry(aExpiry)
: mNext(nsnull)
, mExpiry(aExpiry)
, mLastAccessed(aLastAccessed)
, mRefCnt(0)
, mIsSession(aIsSession != PR_FALSE)
@ -123,6 +124,7 @@ nsCookie::~nsCookie()
NS_IMETHODIMP nsCookie::GetName(nsACString &aName) { aName = Name(); return NS_OK; }
NS_IMETHODIMP nsCookie::GetValue(nsACString &aValue) { aValue = Value(); return NS_OK; }
NS_IMETHODIMP nsCookie::GetHost(nsACString &aHost) { aHost = Host(); return NS_OK; }
NS_IMETHODIMP nsCookie::GetRawHost(nsACString &aHost) { aHost = RawHost(); return NS_OK; }
NS_IMETHODIMP nsCookie::GetPath(nsACString &aPath) { aPath = Path(); return NS_OK; }
NS_IMETHODIMP nsCookie::GetExpiry(PRInt64 *aExpiry) { *aExpiry = Expiry(); return NS_OK; }
NS_IMETHODIMP nsCookie::GetIsSession(PRBool *aIsSession) { *aIsSession = IsSession(); return NS_OK; }

Просмотреть файл

@ -87,6 +87,7 @@ class nsCookie : public nsICookie2
inline const nsDependentCString Name() const { return nsDependentCString(mName, mValue - 1); }
inline const nsDependentCString Value() const { return nsDependentCString(mValue, mHost - 1); }
inline const nsDependentCString Host() const { return nsDependentCString(mHost, mPath - 1); }
inline const nsDependentCString RawHost() const { return nsDependentCString(IsDomain() ? mHost + 1 : mHost, mPath - 1); }
inline const nsDependentCString Path() const { return nsDependentCString(mPath, mEnd); }
inline nsInt64 Expiry() const { NS_ASSERTION(!IsSession(), "can't get expiry time for a session cookie"); return mExpiry; }
inline nsInt64 LastAccessed() const { return mLastAccessed; }
@ -101,6 +102,9 @@ class nsCookie : public nsICookie2
inline void SetExpiry(PRInt64 aExpiry) { mExpiry = aExpiry; }
inline void SetIsSession(PRBool aIsSession) { mIsSession = aIsSession; }
// linked list management helper
inline nsCookie*& Next() { return mNext; }
protected:
// member variables
// we use char* ptrs to store the strings in a contiguous block,
@ -108,7 +112,7 @@ class nsCookie : public nsICookie2
// store a terminating null for each string, so we can hand them
// out as nsAFlatCStrings.
// sizeof(nsCookie) = 44 bytes + Length(strings)
nsCookie *mNext;
char *mName;
char *mValue;
char *mHost;

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -48,9 +48,11 @@
#include "nsCookie.h"
#include "nsString.h"
#include "nsVoidArray.h"
#include "nsTHashtable.h"
struct nsCookieAttributes;
struct nsListIter;
struct nsEnumerationData;
class nsICookieConsent;
class nsICookiePermission;
class nsIPrefBranch;
@ -62,6 +64,85 @@ class nsITimer;
class nsIFile;
class nsInt64;
// hash entry class
class nsCookieEntry : public PLDHashEntryHdr
{
public:
// Hash methods
typedef const char* KeyType;
typedef const char* KeyTypePointer;
// do nothing with aHost - we require mHead to be set before we're live!
nsCookieEntry(KeyTypePointer aHost)
: mHead(nsnull)
{
}
nsCookieEntry(const nsCookieEntry& toCopy)
{
// if we end up here, things will break. nsTHashtable shouldn't
// allow this, since we set ALLOW_MEMMOVE to true.
NS_NOTREACHED("nsCookieEntry copy constructor is forbidden!");
}
~nsCookieEntry()
{
// walk the linked list, and de-link everything by releasing & nulling.
// this allows the parent host entry to be deleted by the hashtable.
// note: we know mHead cannot be null here - we always set mHead to a
// valid nsCookie (if it were null, the hashtable wouldn't be able to find
// this entry, because the key string is provided by mHead).
nsCookie *current = mHead, *next;
do {
next = current->Next();
NS_RELEASE(current);
} while ((current = next));
}
KeyType GetKey() const
{
return HostPtr();
}
KeyTypePointer GetKeyPointer() const
{
return HostPtr();
}
PRBool KeyEquals(KeyTypePointer aKey) const
{
return !strcmp(HostPtr(), 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 nsnull
return PL_DHashStringKey(nsnull, aKey);
}
enum { ALLOW_MEMMOVE = PR_TRUE };
// get methods
inline const nsDependentCString Host() const { return mHead->Host(); }
// linked list management helper
inline nsCookie*& Head() { return mHead; }
inline KeyTypePointer HostPtr() const
{
return mHead->Host().get();
}
private:
nsCookie *mHead;
};
/******************************************************************************
* nsCookieService:
* class declaration
@ -83,6 +164,7 @@ class nsCookieService : public nsICookieService
nsCookieService();
virtual ~nsCookieService();
static nsCookieService* GetSingleton();
nsresult Init();
protected:
void InitPrefObservers();
@ -90,11 +172,12 @@ class nsCookieService : public nsICookieService
nsresult Read();
nsresult Write();
PRBool SetCookieInternal(nsIURI *aHostURI, nsIChannel *aChannel, nsDependentCString &aCookieHeader, nsInt64 aServerTime, nsCookieStatus aStatus, nsCookiePolicy aPolicy);
nsresult AddInternal(nsCookie *aCookie, nsInt64 aCurrentTime, nsIURI *aHostURI, const char *aCookieHeader);
void AddInternal(nsCookie *aCookie, nsInt64 aCurrentTime, nsIURI *aHostURI, const char *aCookieHeader);
void RemoveCookieFromList(nsListIter &aIter);
void AddCookieToList(nsCookie *aCookie);
static PRBool GetTokenValue(nsASingleFragmentCString::const_char_iterator &aIter, nsASingleFragmentCString::const_char_iterator &aEndIter, nsDependentSingleFragmentCSubstring &aTokenString, nsDependentSingleFragmentCSubstring &aTokenValue, PRBool &aEqualsFound);
static PRBool ParseAttributes(nsDependentCString &aCookieHeader, nsCookieAttributes &aCookie);
static PRBool IsIPAddress(const nsAFlatCString &aHost);
static PRBool IsFromMailNews(const nsAFlatCString &aScheme);
static PRBool IsInDomain(const nsACString &aDomain, const nsACString &aHost, PRBool aIsDomain = PR_TRUE);
static PRBool IsForeign(nsIURI *aHostURI, nsIURI *aFirstURI);
static nsCookiePolicy GetP3PPolicy(PRInt32 aPolicy);
@ -105,20 +188,18 @@ class nsCookieService : public nsICookieService
static PRBool CheckPath(nsCookieAttributes &aCookie, nsIURI *aHostURI);
PRBool GetExpiry(nsCookieAttributes &aCookie, nsInt64 aServerTime, nsInt64 aCurrentTime, nsCookieStatus aStatus);
void RemoveAllFromMemory();
void RemoveExpiredCookies(nsInt64 aCurrentTime, PRInt32 &aOldestPosition);
PRBool FindPosition(nsCookie *aCookie, PRInt32 &aInsertPosition, PRInt32 &aDeletePosition, nsInt64 aCurrentTime);
void UpdateCookieIcon();
void RemoveExpiredCookies(nsInt64 aCurrentTime);
PRBool FindCookie(const nsAFlatCString &aHost, const nsAFlatCString &aName, const nsAFlatCString &aPath, nsListIter &aIter);
void FindOldestCookie(nsEnumerationData &aData);
PRUint32 CountCookiesFromHost(nsCookie *aCookie, nsEnumerationData &aData);
void NotifyRejected(nsIURI *aHostURI);
void NotifyChanged(nsICookie2 *aCookie, const PRUnichar *aData);
// Use LazyWrite to save the cookies file on a timer. It will write
// the file only once if repeatedly hammered quickly.
void LazyWrite();
static void DoLazyWrite(nsITimer *aTimer, void *aClosure);
// Use LazyNotify to broadcast the "cookieChanged" notification at
// a reasonable frequency.
void LazyNotify();
static void DoLazyNotify(nsITimer *aTimer, void *aClosure);
protected:
// cached members
nsCOMPtr<nsIPrefBranch> mPrefBranch;
@ -129,8 +210,8 @@ class nsCookieService : public nsICookieService
// impl members
nsCOMPtr<nsITimer> mWriteTimer;
nsCOMPtr<nsITimer> mNotifyTimer;
nsVoidArray mCookieList;
nsTHashtable<nsCookieEntry> mHostTable;
PRUint32 mCookieCount;
PRPackedBool mCookieChanged;
PRPackedBool mCookieIconVisible;
@ -160,6 +241,9 @@ class nsCookieService : public nsICookieService
// private static member, used to cache a ptr to nsCookieService,
// so we can make nsCookieService a singleton xpcom object.
static nsCookieService *gCookieService;
// this callback needs access to member functions
friend PLDHashOperator PR_CALLBACK removeExpiredCallback(nsCookieEntry *aEntry, void *aArg);
};
#define NS_COOKIEMANAGER_CID {0xaaab6710,0xf2c,0x11d5,{0xa5,0x3b,0x0,0x10,0xa4,0x1,0xeb,0x10}}