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