Sat, 03 Jan 2015 20:18:00 +0100
Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 * vim: sw=2 ts=2 et lcs=trail\:.,tab\:>~ :
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "TestHarness.h"
8 #include "nsMemory.h"
9 #include "nsThreadUtils.h"
10 #include "nsNetUtil.h"
11 #include "nsDocShellCID.h"
13 #include "nsToolkitCompsCID.h"
14 #include "nsINavHistoryService.h"
15 #include "nsIObserverService.h"
16 #include "mozilla/IHistory.h"
17 #include "mozIStorageConnection.h"
18 #include "mozIStorageStatement.h"
19 #include "mozIStorageAsyncStatement.h"
20 #include "mozIStorageStatementCallback.h"
21 #include "mozIStoragePendingStatement.h"
22 #include "nsPIPlacesDatabase.h"
23 #include "nsIObserver.h"
24 #include "prinrval.h"
25 #include "mozilla/Attributes.h"
27 #define WAITFORTOPIC_TIMEOUT_SECONDS 5
30 static size_t gTotalTests = 0;
31 static size_t gPassedTests = 0;
33 #define do_check_true(aCondition) \
34 PR_BEGIN_MACRO \
35 gTotalTests++; \
36 if (aCondition) { \
37 gPassedTests++; \
38 } else { \
39 fail("%s | Expected true, got false at line %d", __FILE__, __LINE__); \
40 } \
41 PR_END_MACRO
43 #define do_check_false(aCondition) \
44 PR_BEGIN_MACRO \
45 gTotalTests++; \
46 if (!aCondition) { \
47 gPassedTests++; \
48 } else { \
49 fail("%s | Expected false, got true at line %d", __FILE__, __LINE__); \
50 } \
51 PR_END_MACRO
53 #define do_check_success(aResult) \
54 do_check_true(NS_SUCCEEDED(aResult))
56 #ifdef LINUX
57 // XXX Linux opt builds on tinderbox are orange due to linking with stdlib.
58 // This is sad and annoying, but it's a workaround that works.
59 #define do_check_eq(aExpected, aActual) \
60 do_check_true(aExpected == aActual)
61 #else
62 #include <sstream>
64 #define do_check_eq(aActual, aExpected) \
65 PR_BEGIN_MACRO \
66 gTotalTests++; \
67 if (aExpected == aActual) { \
68 gPassedTests++; \
69 } else { \
70 std::ostringstream temp; \
71 temp << __FILE__ << " | Expected '" << aExpected << "', got '"; \
72 temp << aActual <<"' at line " << __LINE__; \
73 fail(temp.str().c_str()); \
74 } \
75 PR_END_MACRO
76 #endif
78 struct Test
79 {
80 void (*func)(void);
81 const char* const name;
82 };
83 #define TEST(aName) \
84 {aName, #aName}
86 /**
87 * Runs the next text.
88 */
89 void run_next_test();
91 /**
92 * To be used around asynchronous work.
93 */
94 void do_test_pending();
95 void do_test_finished();
97 /**
98 * Spins current thread until a topic is received.
99 */
100 class WaitForTopicSpinner MOZ_FINAL : public nsIObserver
101 {
102 public:
103 NS_DECL_ISUPPORTS
105 WaitForTopicSpinner(const char* const aTopic)
106 : mTopicReceived(false)
107 , mStartTime(PR_IntervalNow())
108 {
109 nsCOMPtr<nsIObserverService> observerService =
110 do_GetService(NS_OBSERVERSERVICE_CONTRACTID);
111 do_check_true(observerService);
112 (void)observerService->AddObserver(this, aTopic, false);
113 }
115 void Spin() {
116 while (!mTopicReceived) {
117 if ((PR_IntervalNow() - mStartTime) > (WAITFORTOPIC_TIMEOUT_SECONDS * PR_USEC_PER_SEC)) {
118 // Timed out waiting for the topic.
119 do_check_true(false);
120 break;
121 }
122 (void)NS_ProcessNextEvent();
123 }
124 }
126 NS_IMETHOD Observe(nsISupports* aSubject,
127 const char* aTopic,
128 const char16_t* aData)
129 {
130 mTopicReceived = true;
131 nsCOMPtr<nsIObserverService> observerService =
132 do_GetService(NS_OBSERVERSERVICE_CONTRACTID);
133 do_check_true(observerService);
134 (void)observerService->RemoveObserver(this, aTopic);
135 return NS_OK;
136 }
138 private:
139 bool mTopicReceived;
140 PRIntervalTime mStartTime;
141 };
142 NS_IMPL_ISUPPORTS(
143 WaitForTopicSpinner,
144 nsIObserver
145 )
147 /**
148 * Spins current thread until an async statement is executed.
149 */
150 class AsyncStatementSpinner MOZ_FINAL : public mozIStorageStatementCallback
151 {
152 public:
153 NS_DECL_ISUPPORTS
154 NS_DECL_MOZISTORAGESTATEMENTCALLBACK
156 AsyncStatementSpinner();
157 void SpinUntilCompleted();
158 uint16_t completionReason;
160 protected:
161 volatile bool mCompleted;
162 };
164 NS_IMPL_ISUPPORTS(AsyncStatementSpinner,
165 mozIStorageStatementCallback)
167 AsyncStatementSpinner::AsyncStatementSpinner()
168 : completionReason(0)
169 , mCompleted(false)
170 {
171 }
173 NS_IMETHODIMP
174 AsyncStatementSpinner::HandleResult(mozIStorageResultSet *aResultSet)
175 {
176 return NS_OK;
177 }
179 NS_IMETHODIMP
180 AsyncStatementSpinner::HandleError(mozIStorageError *aError)
181 {
182 return NS_OK;
183 }
185 NS_IMETHODIMP
186 AsyncStatementSpinner::HandleCompletion(uint16_t aReason)
187 {
188 completionReason = aReason;
189 mCompleted = true;
190 return NS_OK;
191 }
193 void AsyncStatementSpinner::SpinUntilCompleted()
194 {
195 nsCOMPtr<nsIThread> thread(::do_GetCurrentThread());
196 nsresult rv = NS_OK;
197 bool processed = true;
198 while (!mCompleted && NS_SUCCEEDED(rv)) {
199 rv = thread->ProcessNextEvent(true, &processed);
200 }
201 }
203 struct PlaceRecord
204 {
205 int64_t id;
206 int32_t hidden;
207 int32_t typed;
208 int32_t visitCount;
209 nsCString guid;
210 };
212 struct VisitRecord
213 {
214 int64_t id;
215 int64_t lastVisitId;
216 int32_t transitionType;
217 };
219 already_AddRefed<mozilla::IHistory>
220 do_get_IHistory()
221 {
222 nsCOMPtr<mozilla::IHistory> history = do_GetService(NS_IHISTORY_CONTRACTID);
223 do_check_true(history);
224 return history.forget();
225 }
227 already_AddRefed<nsINavHistoryService>
228 do_get_NavHistory()
229 {
230 nsCOMPtr<nsINavHistoryService> serv =
231 do_GetService(NS_NAVHISTORYSERVICE_CONTRACTID);
232 do_check_true(serv);
233 return serv.forget();
234 }
236 already_AddRefed<mozIStorageConnection>
237 do_get_db()
238 {
239 nsCOMPtr<nsINavHistoryService> history = do_get_NavHistory();
240 nsCOMPtr<nsPIPlacesDatabase> database = do_QueryInterface(history);
241 do_check_true(database);
243 nsCOMPtr<mozIStorageConnection> dbConn;
244 nsresult rv = database->GetDBConnection(getter_AddRefs(dbConn));
245 do_check_success(rv);
246 return dbConn.forget();
247 }
249 /**
250 * Get the place record from the database.
251 *
252 * @param aURI The unique URI of the place we are looking up
253 * @param result Out parameter where the result is stored
254 */
255 void
256 do_get_place(nsIURI* aURI, PlaceRecord& result)
257 {
258 nsCOMPtr<mozIStorageConnection> dbConn = do_get_db();
259 nsCOMPtr<mozIStorageStatement> stmt;
261 nsCString spec;
262 nsresult rv = aURI->GetSpec(spec);
263 do_check_success(rv);
265 rv = dbConn->CreateStatement(NS_LITERAL_CSTRING(
266 "SELECT id, hidden, typed, visit_count, guid FROM moz_places "
267 "WHERE url=?1 "
268 ), getter_AddRefs(stmt));
269 do_check_success(rv);
271 rv = stmt->BindUTF8StringByIndex(0, spec);
272 do_check_success(rv);
274 bool hasResults;
275 rv = stmt->ExecuteStep(&hasResults);
276 do_check_success(rv);
277 if (!hasResults) {
278 result.id = 0;
279 return;
280 }
282 rv = stmt->GetInt64(0, &result.id);
283 do_check_success(rv);
284 rv = stmt->GetInt32(1, &result.hidden);
285 do_check_success(rv);
286 rv = stmt->GetInt32(2, &result.typed);
287 do_check_success(rv);
288 rv = stmt->GetInt32(3, &result.visitCount);
289 do_check_success(rv);
290 rv = stmt->GetUTF8String(4, result.guid);
291 do_check_success(rv);
292 }
294 /**
295 * Gets the most recent visit to a place.
296 *
297 * @param placeID ID from the moz_places table
298 * @param result Out parameter where visit is stored
299 */
300 void
301 do_get_lastVisit(int64_t placeId, VisitRecord& result)
302 {
303 nsCOMPtr<mozIStorageConnection> dbConn = do_get_db();
304 nsCOMPtr<mozIStorageStatement> stmt;
306 nsresult rv = dbConn->CreateStatement(NS_LITERAL_CSTRING(
307 "SELECT id, from_visit, visit_type FROM moz_historyvisits "
308 "WHERE place_id=?1 "
309 "LIMIT 1"
310 ), getter_AddRefs(stmt));
311 do_check_success(rv);
313 rv = stmt->BindInt64ByIndex(0, placeId);
314 do_check_success(rv);
316 bool hasResults;
317 rv = stmt->ExecuteStep(&hasResults);
318 do_check_success(rv);
320 if (!hasResults) {
321 result.id = 0;
322 return;
323 }
325 rv = stmt->GetInt64(0, &result.id);
326 do_check_success(rv);
327 rv = stmt->GetInt64(1, &result.lastVisitId);
328 do_check_success(rv);
329 rv = stmt->GetInt32(2, &result.transitionType);
330 do_check_success(rv);
331 }
333 void
334 do_wait_async_updates() {
335 nsCOMPtr<mozIStorageConnection> db = do_get_db();
336 nsCOMPtr<mozIStorageAsyncStatement> stmt;
338 db->CreateAsyncStatement(NS_LITERAL_CSTRING("BEGIN EXCLUSIVE"),
339 getter_AddRefs(stmt));
340 nsCOMPtr<mozIStoragePendingStatement> pending;
341 (void)stmt->ExecuteAsync(nullptr, getter_AddRefs(pending));
343 db->CreateAsyncStatement(NS_LITERAL_CSTRING("COMMIT"),
344 getter_AddRefs(stmt));
345 nsRefPtr<AsyncStatementSpinner> spinner = new AsyncStatementSpinner();
346 (void)stmt->ExecuteAsync(spinner, getter_AddRefs(pending));
348 spinner->SpinUntilCompleted();
349 }
351 /**
352 * Adds a URI to the database.
353 *
354 * @param aURI
355 * The URI to add to the database.
356 */
357 void
358 addURI(nsIURI* aURI)
359 {
360 nsCOMPtr<mozilla::IHistory> history = do_GetService(NS_IHISTORY_CONTRACTID);
361 do_check_true(history);
362 nsresult rv = history->VisitURI(aURI, nullptr, mozilla::IHistory::TOP_LEVEL);
363 do_check_success(rv);
365 do_wait_async_updates();
366 }
368 static const char TOPIC_PROFILE_CHANGE[] = "profile-before-change";
369 static const char TOPIC_PLACES_CONNECTION_CLOSED[] = "places-connection-closed";
371 class WaitForConnectionClosed MOZ_FINAL : public nsIObserver
372 {
373 nsRefPtr<WaitForTopicSpinner> mSpinner;
374 public:
375 NS_DECL_ISUPPORTS
377 WaitForConnectionClosed()
378 {
379 nsCOMPtr<nsIObserverService> os =
380 do_GetService(NS_OBSERVERSERVICE_CONTRACTID);
381 MOZ_ASSERT(os);
382 if (os) {
383 MOZ_ALWAYS_TRUE(NS_SUCCEEDED(os->AddObserver(this, TOPIC_PROFILE_CHANGE, false)));
384 }
385 mSpinner = new WaitForTopicSpinner(TOPIC_PLACES_CONNECTION_CLOSED);
386 }
388 NS_IMETHOD Observe(nsISupports* aSubject,
389 const char* aTopic,
390 const char16_t* aData)
391 {
392 nsCOMPtr<nsIObserverService> os =
393 do_GetService(NS_OBSERVERSERVICE_CONTRACTID);
394 MOZ_ASSERT(os);
395 if (os) {
396 MOZ_ALWAYS_TRUE(NS_SUCCEEDED(os->RemoveObserver(this, aTopic)));
397 }
399 mSpinner->Spin();
401 return NS_OK;
402 }
403 };
405 NS_IMPL_ISUPPORTS(WaitForConnectionClosed, nsIObserver)