Wed, 31 Dec 2014 07:22:50 +0100
Correct previous dual key logic pending first delivery installment.
michael@0 | 1 | /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
michael@0 | 2 | /* This Source Code Form is subject to the terms of the Mozilla Public |
michael@0 | 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this |
michael@0 | 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
michael@0 | 5 | |
michael@0 | 6 | #ifndef nsCookieService_h__ |
michael@0 | 7 | #define nsCookieService_h__ |
michael@0 | 8 | |
michael@0 | 9 | #include "nsICookieService.h" |
michael@0 | 10 | #include "nsICookieManager.h" |
michael@0 | 11 | #include "nsICookieManager2.h" |
michael@0 | 12 | #include "nsIObserver.h" |
michael@0 | 13 | #include "nsWeakReference.h" |
michael@0 | 14 | |
michael@0 | 15 | #include "nsCookie.h" |
michael@0 | 16 | #include "nsString.h" |
michael@0 | 17 | #include "nsAutoPtr.h" |
michael@0 | 18 | #include "nsHashKeys.h" |
michael@0 | 19 | #include "nsIMemoryReporter.h" |
michael@0 | 20 | #include "nsTHashtable.h" |
michael@0 | 21 | #include "mozIStorageStatement.h" |
michael@0 | 22 | #include "mozIStorageAsyncStatement.h" |
michael@0 | 23 | #include "mozIStoragePendingStatement.h" |
michael@0 | 24 | #include "mozIStorageConnection.h" |
michael@0 | 25 | #include "mozIStorageRow.h" |
michael@0 | 26 | #include "mozIStorageCompletionCallback.h" |
michael@0 | 27 | #include "mozIStorageStatementCallback.h" |
michael@0 | 28 | |
michael@0 | 29 | #include "mozilla/MemoryReporting.h" |
michael@0 | 30 | |
michael@0 | 31 | class nsICookiePermission; |
michael@0 | 32 | class nsIEffectiveTLDService; |
michael@0 | 33 | class nsIIDNService; |
michael@0 | 34 | class nsIPrefBranch; |
michael@0 | 35 | class nsIObserverService; |
michael@0 | 36 | class nsIURI; |
michael@0 | 37 | class nsIChannel; |
michael@0 | 38 | class nsIArray; |
michael@0 | 39 | class mozIStorageService; |
michael@0 | 40 | class mozIThirdPartyUtil; |
michael@0 | 41 | class ReadCookieDBListener; |
michael@0 | 42 | |
michael@0 | 43 | struct nsCookieAttributes; |
michael@0 | 44 | struct nsListIter; |
michael@0 | 45 | struct nsEnumerationData; |
michael@0 | 46 | |
michael@0 | 47 | namespace mozilla { |
michael@0 | 48 | namespace net { |
michael@0 | 49 | class CookieServiceParent; |
michael@0 | 50 | } |
michael@0 | 51 | } |
michael@0 | 52 | |
michael@0 | 53 | // hash key class |
michael@0 | 54 | class nsCookieKey : public PLDHashEntryHdr |
michael@0 | 55 | { |
michael@0 | 56 | public: |
michael@0 | 57 | typedef const nsCookieKey& KeyType; |
michael@0 | 58 | typedef const nsCookieKey* KeyTypePointer; |
michael@0 | 59 | |
michael@0 | 60 | nsCookieKey() |
michael@0 | 61 | {} |
michael@0 | 62 | |
michael@0 | 63 | nsCookieKey(const nsCString &baseDomain, uint32_t appId, bool inBrowser) |
michael@0 | 64 | : mBaseDomain(baseDomain) |
michael@0 | 65 | , mAppId(appId) |
michael@0 | 66 | , mInBrowserElement(inBrowser) |
michael@0 | 67 | {} |
michael@0 | 68 | |
michael@0 | 69 | nsCookieKey(KeyTypePointer other) |
michael@0 | 70 | : mBaseDomain(other->mBaseDomain) |
michael@0 | 71 | , mAppId(other->mAppId) |
michael@0 | 72 | , mInBrowserElement(other->mInBrowserElement) |
michael@0 | 73 | {} |
michael@0 | 74 | |
michael@0 | 75 | nsCookieKey(KeyType other) |
michael@0 | 76 | : mBaseDomain(other.mBaseDomain) |
michael@0 | 77 | , mAppId(other.mAppId) |
michael@0 | 78 | , mInBrowserElement(other.mInBrowserElement) |
michael@0 | 79 | {} |
michael@0 | 80 | |
michael@0 | 81 | ~nsCookieKey() |
michael@0 | 82 | {} |
michael@0 | 83 | |
michael@0 | 84 | bool KeyEquals(KeyTypePointer other) const |
michael@0 | 85 | { |
michael@0 | 86 | return mBaseDomain == other->mBaseDomain && |
michael@0 | 87 | mAppId == other->mAppId && |
michael@0 | 88 | mInBrowserElement == other->mInBrowserElement; |
michael@0 | 89 | } |
michael@0 | 90 | |
michael@0 | 91 | static KeyTypePointer KeyToPointer(KeyType aKey) |
michael@0 | 92 | { |
michael@0 | 93 | return &aKey; |
michael@0 | 94 | } |
michael@0 | 95 | |
michael@0 | 96 | static PLDHashNumber HashKey(KeyTypePointer aKey) |
michael@0 | 97 | { |
michael@0 | 98 | // TODO: more efficient way to generate hash? |
michael@0 | 99 | nsAutoCString temp(aKey->mBaseDomain); |
michael@0 | 100 | temp.Append("#"); |
michael@0 | 101 | temp.Append(aKey->mAppId); |
michael@0 | 102 | temp.Append("#"); |
michael@0 | 103 | temp.Append(aKey->mInBrowserElement ? 1 : 0); |
michael@0 | 104 | return mozilla::HashString(temp); |
michael@0 | 105 | } |
michael@0 | 106 | |
michael@0 | 107 | size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const; |
michael@0 | 108 | |
michael@0 | 109 | enum { ALLOW_MEMMOVE = true }; |
michael@0 | 110 | |
michael@0 | 111 | nsCString mBaseDomain; |
michael@0 | 112 | uint32_t mAppId; |
michael@0 | 113 | bool mInBrowserElement; |
michael@0 | 114 | }; |
michael@0 | 115 | |
michael@0 | 116 | // Inherit from nsCookieKey so this can be stored in nsTHashTable |
michael@0 | 117 | // TODO: why aren't we using nsClassHashTable<nsCookieKey, ArrayType>? |
michael@0 | 118 | class nsCookieEntry : public nsCookieKey |
michael@0 | 119 | { |
michael@0 | 120 | public: |
michael@0 | 121 | // Hash methods |
michael@0 | 122 | typedef nsTArray< nsRefPtr<nsCookie> > ArrayType; |
michael@0 | 123 | typedef ArrayType::index_type IndexType; |
michael@0 | 124 | |
michael@0 | 125 | nsCookieEntry(KeyTypePointer aKey) |
michael@0 | 126 | : nsCookieKey(aKey) |
michael@0 | 127 | {} |
michael@0 | 128 | |
michael@0 | 129 | nsCookieEntry(const nsCookieEntry& toCopy) |
michael@0 | 130 | { |
michael@0 | 131 | // if we end up here, things will break. nsTHashtable shouldn't |
michael@0 | 132 | // allow this, since we set ALLOW_MEMMOVE to true. |
michael@0 | 133 | NS_NOTREACHED("nsCookieEntry copy constructor is forbidden!"); |
michael@0 | 134 | } |
michael@0 | 135 | |
michael@0 | 136 | ~nsCookieEntry() |
michael@0 | 137 | {} |
michael@0 | 138 | |
michael@0 | 139 | inline ArrayType& GetCookies() { return mCookies; } |
michael@0 | 140 | |
michael@0 | 141 | size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const; |
michael@0 | 142 | |
michael@0 | 143 | private: |
michael@0 | 144 | ArrayType mCookies; |
michael@0 | 145 | }; |
michael@0 | 146 | |
michael@0 | 147 | // encapsulates a (key, nsCookie) tuple for temporary storage purposes. |
michael@0 | 148 | struct CookieDomainTuple |
michael@0 | 149 | { |
michael@0 | 150 | nsCookieKey key; |
michael@0 | 151 | nsRefPtr<nsCookie> cookie; |
michael@0 | 152 | |
michael@0 | 153 | size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const; |
michael@0 | 154 | }; |
michael@0 | 155 | |
michael@0 | 156 | // encapsulates in-memory and on-disk DB states, so we can |
michael@0 | 157 | // conveniently switch state when entering or exiting private browsing. |
michael@0 | 158 | struct DBState MOZ_FINAL |
michael@0 | 159 | { |
michael@0 | 160 | DBState() : cookieCount(0), cookieOldestTime(INT64_MAX), corruptFlag(OK) |
michael@0 | 161 | { |
michael@0 | 162 | } |
michael@0 | 163 | |
michael@0 | 164 | private: |
michael@0 | 165 | // Private destructor, to discourage deletion outside of Release(): |
michael@0 | 166 | ~DBState() |
michael@0 | 167 | { |
michael@0 | 168 | } |
michael@0 | 169 | |
michael@0 | 170 | public: |
michael@0 | 171 | NS_INLINE_DECL_REFCOUNTING(DBState) |
michael@0 | 172 | |
michael@0 | 173 | size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const; |
michael@0 | 174 | |
michael@0 | 175 | // State of the database connection. |
michael@0 | 176 | enum CorruptFlag { |
michael@0 | 177 | OK, // normal |
michael@0 | 178 | CLOSING_FOR_REBUILD, // corruption detected, connection closing |
michael@0 | 179 | REBUILDING // close complete, rebuilding database from memory |
michael@0 | 180 | }; |
michael@0 | 181 | |
michael@0 | 182 | nsTHashtable<nsCookieEntry> hostTable; |
michael@0 | 183 | uint32_t cookieCount; |
michael@0 | 184 | int64_t cookieOldestTime; |
michael@0 | 185 | nsCOMPtr<nsIFile> cookieFile; |
michael@0 | 186 | nsCOMPtr<mozIStorageConnection> dbConn; |
michael@0 | 187 | nsCOMPtr<mozIStorageAsyncStatement> stmtInsert; |
michael@0 | 188 | nsCOMPtr<mozIStorageAsyncStatement> stmtDelete; |
michael@0 | 189 | nsCOMPtr<mozIStorageAsyncStatement> stmtUpdate; |
michael@0 | 190 | CorruptFlag corruptFlag; |
michael@0 | 191 | |
michael@0 | 192 | // Various parts representing asynchronous read state. These are useful |
michael@0 | 193 | // while the background read is taking place. |
michael@0 | 194 | nsCOMPtr<mozIStorageConnection> syncConn; |
michael@0 | 195 | nsCOMPtr<mozIStorageStatement> stmtReadDomain; |
michael@0 | 196 | nsCOMPtr<mozIStoragePendingStatement> pendingRead; |
michael@0 | 197 | // The asynchronous read listener. This is a weak ref (storage has ownership) |
michael@0 | 198 | // since it may need to outlive the DBState's database connection. |
michael@0 | 199 | ReadCookieDBListener* readListener; |
michael@0 | 200 | // An array of (baseDomain, cookie) tuples representing data read in |
michael@0 | 201 | // asynchronously. This is merged into hostTable once read is complete. |
michael@0 | 202 | nsTArray<CookieDomainTuple> hostArray; |
michael@0 | 203 | // A hashset of baseDomains read in synchronously, while the async read is |
michael@0 | 204 | // in flight. This is used to keep track of which data in hostArray is stale |
michael@0 | 205 | // when the time comes to merge. |
michael@0 | 206 | nsTHashtable<nsCookieKey> readSet; |
michael@0 | 207 | |
michael@0 | 208 | // DB completion handlers. |
michael@0 | 209 | nsCOMPtr<mozIStorageStatementCallback> insertListener; |
michael@0 | 210 | nsCOMPtr<mozIStorageStatementCallback> updateListener; |
michael@0 | 211 | nsCOMPtr<mozIStorageStatementCallback> removeListener; |
michael@0 | 212 | nsCOMPtr<mozIStorageCompletionCallback> closeListener; |
michael@0 | 213 | }; |
michael@0 | 214 | |
michael@0 | 215 | // these constants represent a decision about a cookie based on user prefs. |
michael@0 | 216 | enum CookieStatus |
michael@0 | 217 | { |
michael@0 | 218 | STATUS_ACCEPTED, |
michael@0 | 219 | STATUS_ACCEPT_SESSION, |
michael@0 | 220 | STATUS_REJECTED, |
michael@0 | 221 | // STATUS_REJECTED_WITH_ERROR indicates the cookie should be rejected because |
michael@0 | 222 | // of an error (rather than something the user can control). this is used for |
michael@0 | 223 | // notification purposes, since we only want to notify of rejections where |
michael@0 | 224 | // the user can do something about it (e.g. whitelist the site). |
michael@0 | 225 | STATUS_REJECTED_WITH_ERROR |
michael@0 | 226 | }; |
michael@0 | 227 | |
michael@0 | 228 | // Result codes for TryInitDB() and Read(). |
michael@0 | 229 | enum OpenDBResult |
michael@0 | 230 | { |
michael@0 | 231 | RESULT_OK, |
michael@0 | 232 | RESULT_RETRY, |
michael@0 | 233 | RESULT_FAILURE |
michael@0 | 234 | }; |
michael@0 | 235 | |
michael@0 | 236 | /****************************************************************************** |
michael@0 | 237 | * nsCookieService: |
michael@0 | 238 | * class declaration |
michael@0 | 239 | ******************************************************************************/ |
michael@0 | 240 | |
michael@0 | 241 | class nsCookieService : public nsICookieService |
michael@0 | 242 | , public nsICookieManager2 |
michael@0 | 243 | , public nsIObserver |
michael@0 | 244 | , public nsSupportsWeakReference |
michael@0 | 245 | , public nsIMemoryReporter |
michael@0 | 246 | { |
michael@0 | 247 | private: |
michael@0 | 248 | size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const; |
michael@0 | 249 | |
michael@0 | 250 | public: |
michael@0 | 251 | NS_DECL_ISUPPORTS |
michael@0 | 252 | NS_DECL_NSIOBSERVER |
michael@0 | 253 | NS_DECL_NSICOOKIESERVICE |
michael@0 | 254 | NS_DECL_NSICOOKIEMANAGER |
michael@0 | 255 | NS_DECL_NSICOOKIEMANAGER2 |
michael@0 | 256 | NS_DECL_NSIMEMORYREPORTER |
michael@0 | 257 | |
michael@0 | 258 | nsCookieService(); |
michael@0 | 259 | virtual ~nsCookieService(); |
michael@0 | 260 | static nsICookieService* GetXPCOMSingleton(); |
michael@0 | 261 | nsresult Init(); |
michael@0 | 262 | |
michael@0 | 263 | /** |
michael@0 | 264 | * Start watching the observer service for messages indicating that an app has |
michael@0 | 265 | * been uninstalled. When an app is uninstalled, we get the cookie service |
michael@0 | 266 | * (thus instantiating it, if necessary) and clear all the cookies for that |
michael@0 | 267 | * app. |
michael@0 | 268 | */ |
michael@0 | 269 | static void AppClearDataObserverInit(); |
michael@0 | 270 | |
michael@0 | 271 | protected: |
michael@0 | 272 | void PrefChanged(nsIPrefBranch *aPrefBranch); |
michael@0 | 273 | void InitDBStates(); |
michael@0 | 274 | OpenDBResult TryInitDB(bool aDeleteExistingDB); |
michael@0 | 275 | nsresult CreateTable(); |
michael@0 | 276 | void CloseDBStates(); |
michael@0 | 277 | void CleanupCachedStatements(); |
michael@0 | 278 | void CleanupDefaultDBConnection(); |
michael@0 | 279 | void HandleDBClosed(DBState* aDBState); |
michael@0 | 280 | void HandleCorruptDB(DBState* aDBState); |
michael@0 | 281 | void RebuildCorruptDB(DBState* aDBState); |
michael@0 | 282 | OpenDBResult Read(); |
michael@0 | 283 | template<class T> nsCookie* GetCookieFromRow(T &aRow); |
michael@0 | 284 | void AsyncReadComplete(); |
michael@0 | 285 | void CancelAsyncRead(bool aPurgeReadSet); |
michael@0 | 286 | void EnsureReadDomain(const nsCookieKey &aKey); |
michael@0 | 287 | void EnsureReadComplete(); |
michael@0 | 288 | nsresult NormalizeHost(nsCString &aHost); |
michael@0 | 289 | nsresult GetBaseDomain(nsIURI *aHostURI, nsCString &aBaseDomain, bool &aRequireHostMatch); |
michael@0 | 290 | nsresult GetBaseDomainFromHost(const nsACString &aHost, nsCString &aBaseDomain); |
michael@0 | 291 | nsresult GetCookieStringCommon(nsIURI *aHostURI, nsIChannel *aChannel, bool aHttpBound, char** aCookie); |
michael@4 | 292 | void GetCookieStringInternal(nsIURI *aHostURI, bool aIsForeign, bool aHttpBound, uint32_t aAppId, bool aInBrowserElement, bool aIsPrivate, nsCString &aOrigin, nsCString &aCookie); |
michael@0 | 293 | nsresult SetCookieStringCommon(nsIURI *aHostURI, const char *aCookieHeader, const char *aServerTime, nsIChannel *aChannel, bool aFromHttp); |
michael@0 | 294 | void SetCookieStringInternal(nsIURI *aHostURI, bool aIsForeign, nsDependentCString &aCookieHeader, const nsCString &aServerTime, bool aFromHttp, uint32_t aAppId, bool aInBrowserElement, bool aIsPrivate, nsIChannel* aChannel); |
michael@4 | 295 | bool SetCookieInternal(nsIURI *aHostURI, const nsCookieKey& aKey, bool aRequireHostMatch, const nsCString &aOrigin, CookieStatus aStatus, nsDependentCString &aCookieHeader, int64_t aServerTime, bool aFromHttp, nsIChannel* aChannel); |
michael@0 | 296 | void AddInternal(const nsCookieKey& aKey, nsCookie *aCookie, int64_t aCurrentTimeInUsec, nsIURI *aHostURI, const char *aCookieHeader, bool aFromHttp); |
michael@0 | 297 | void RemoveCookieFromList(const nsListIter &aIter, mozIStorageBindingParamsArray *aParamsArray = nullptr); |
michael@0 | 298 | void AddCookieToList(const nsCookieKey& aKey, nsCookie *aCookie, DBState *aDBState, mozIStorageBindingParamsArray *aParamsArray, bool aWriteToDB = true); |
michael@0 | 299 | void UpdateCookieInList(nsCookie *aCookie, int64_t aLastAccessed, mozIStorageBindingParamsArray *aParamsArray); |
michael@0 | 300 | static bool GetTokenValue(nsASingleFragmentCString::const_char_iterator &aIter, nsASingleFragmentCString::const_char_iterator &aEndIter, nsDependentCSubstring &aTokenString, nsDependentCSubstring &aTokenValue, bool &aEqualsFound); |
michael@0 | 301 | static bool ParseAttributes(nsDependentCString &aCookieHeader, nsCookieAttributes &aCookie); |
michael@0 | 302 | bool RequireThirdPartyCheck(); |
michael@0 | 303 | CookieStatus CheckPrefs(nsIURI *aHostURI, bool aIsForeign, bool aRequireHostMatch, const char *aCookieHeader); |
michael@0 | 304 | bool CheckDomain(nsCookieAttributes &aCookie, nsIURI *aHostURI, const nsCString &aBaseDomain, bool aRequireHostMatch); |
michael@0 | 305 | static bool CheckPath(nsCookieAttributes &aCookie, nsIURI *aHostURI); |
michael@0 | 306 | static bool GetExpiry(nsCookieAttributes &aCookie, int64_t aServerTime, int64_t aCurrentTime); |
michael@0 | 307 | void RemoveAllFromMemory(); |
michael@0 | 308 | already_AddRefed<nsIArray> PurgeCookies(int64_t aCurrentTimeInUsec); |
michael@4 | 309 | bool FindCookie(const nsCookieKey& aKey, const nsAFlatCString &aOrigin, const nsAFlatCString &aHost, const nsAFlatCString &aName, const nsAFlatCString &aPath, nsListIter &aIter); |
michael@0 | 310 | static void FindStaleCookie(nsCookieEntry *aEntry, int64_t aCurrentTime, nsListIter &aIter); |
michael@0 | 311 | void NotifyRejected(nsIURI *aHostURI); |
michael@0 | 312 | void NotifyThirdParty(nsIURI *aHostURI, bool aAccepted, nsIChannel *aChannel); |
michael@0 | 313 | void NotifyChanged(nsISupports *aSubject, const char16_t *aData); |
michael@0 | 314 | void NotifyPurged(nsICookie2* aCookie); |
michael@0 | 315 | already_AddRefed<nsIArray> CreatePurgeList(nsICookie2* aCookie); |
michael@0 | 316 | |
michael@0 | 317 | /** |
michael@0 | 318 | * This method is used to iterate the cookie hash table and select the ones |
michael@0 | 319 | * that are part of a specific app. |
michael@0 | 320 | */ |
michael@0 | 321 | static PLDHashOperator GetCookiesForApp(nsCookieEntry* entry, void* arg); |
michael@0 | 322 | |
michael@0 | 323 | /** |
michael@0 | 324 | * This method is a helper that allows calling nsICookieManager::Remove() |
michael@0 | 325 | * with appId/inBrowserElement parameters. |
michael@0 | 326 | * NOTE: this could be added to a public interface if we happen to need it. |
michael@0 | 327 | */ |
michael@0 | 328 | nsresult Remove(const nsACString& aHost, uint32_t aAppId, |
michael@0 | 329 | bool aInBrowserElement, const nsACString& aName, |
michael@0 | 330 | const nsACString& aPath, bool aBlocked); |
michael@0 | 331 | |
michael@0 | 332 | protected: |
michael@0 | 333 | // cached members. |
michael@0 | 334 | nsCOMPtr<nsIObserverService> mObserverService; |
michael@0 | 335 | nsCOMPtr<nsICookiePermission> mPermissionService; |
michael@0 | 336 | nsCOMPtr<mozIThirdPartyUtil> mThirdPartyUtil; |
michael@0 | 337 | nsCOMPtr<nsIEffectiveTLDService> mTLDService; |
michael@0 | 338 | nsCOMPtr<nsIIDNService> mIDNService; |
michael@0 | 339 | nsCOMPtr<mozIStorageService> mStorageService; |
michael@0 | 340 | |
michael@0 | 341 | // we have two separate DB states: one for normal browsing and one for |
michael@0 | 342 | // private browsing, switching between them on a per-cookie-request basis. |
michael@0 | 343 | // this state encapsulates both the in-memory table and the on-disk DB. |
michael@0 | 344 | // note that the private states' dbConn should always be null - we never |
michael@0 | 345 | // want to be dealing with the on-disk DB when in private browsing. |
michael@0 | 346 | DBState *mDBState; |
michael@0 | 347 | nsRefPtr<DBState> mDefaultDBState; |
michael@0 | 348 | nsRefPtr<DBState> mPrivateDBState; |
michael@0 | 349 | |
michael@0 | 350 | // cached prefs |
michael@0 | 351 | uint8_t mCookieBehavior; // BEHAVIOR_{ACCEPT, REJECTFOREIGN, REJECT, LIMITFOREIGN} |
michael@0 | 352 | bool mThirdPartySession; |
michael@0 | 353 | uint16_t mMaxNumberOfCookies; |
michael@0 | 354 | uint16_t mMaxCookiesPerHost; |
michael@0 | 355 | int64_t mCookiePurgeAge; |
michael@0 | 356 | |
michael@0 | 357 | // friends! |
michael@0 | 358 | friend PLDHashOperator purgeCookiesCallback(nsCookieEntry *aEntry, void *aArg); |
michael@0 | 359 | friend class DBListenerErrorHandler; |
michael@0 | 360 | friend class ReadCookieDBListener; |
michael@0 | 361 | friend class CloseCookieDBListener; |
michael@0 | 362 | |
michael@0 | 363 | static nsCookieService* GetSingleton(); |
michael@0 | 364 | friend class mozilla::net::CookieServiceParent; |
michael@0 | 365 | }; |
michael@0 | 366 | |
michael@0 | 367 | #endif // nsCookieService_h__ |