extensions/cookie/test/unit/head_cookies.js

branch
TOR_BUG_9701
changeset 15
b8a032363ba2
equal deleted inserted replaced
-1:000000000000 0:1c2bd1372788
1 /* Any copyright is dedicated to the Public Domain.
2 * http://creativecommons.org/publicdomain/zero/1.0/
3 */
4
5 Components.utils.import("resource://gre/modules/Services.jsm");
6 Components.utils.import("resource://gre/modules/NetUtil.jsm");
7 Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
8
9 const Cc = Components.classes;
10 const Ci = Components.interfaces;
11 const Cr = Components.results;
12
13 XPCOMUtils.defineLazyServiceGetter(Services, "cookies",
14 "@mozilla.org/cookieService;1",
15 "nsICookieService");
16 XPCOMUtils.defineLazyServiceGetter(Services, "cookiemgr",
17 "@mozilla.org/cookiemanager;1",
18 "nsICookieManager2");
19
20 XPCOMUtils.defineLazyServiceGetter(Services, "etld",
21 "@mozilla.org/network/effective-tld-service;1",
22 "nsIEffectiveTLDService");
23
24 function do_check_throws(f, result, stack)
25 {
26 if (!stack)
27 stack = Components.stack.caller;
28
29 try {
30 f();
31 } catch (exc) {
32 if (exc.result == result)
33 return;
34 do_throw("expected result " + result + ", caught " + exc, stack);
35 }
36 do_throw("expected result " + result + ", none thrown", stack);
37 }
38
39 // Helper to step a generator function and catch a StopIteration exception.
40 function do_run_generator(generator)
41 {
42 try {
43 generator.next();
44 } catch (e) {
45 if (e != StopIteration)
46 do_throw("caught exception " + e, Components.stack.caller);
47 }
48 }
49
50 // Helper to finish a generator function test.
51 function do_finish_generator_test(generator)
52 {
53 do_execute_soon(function() {
54 generator.close();
55 do_test_finished();
56 });
57 }
58
59 function _observer(generator, topic) {
60 Services.obs.addObserver(this, topic, false);
61
62 this.generator = generator;
63 this.topic = topic;
64 }
65
66 _observer.prototype = {
67 observe: function (subject, topic, data) {
68 do_check_eq(this.topic, topic);
69
70 Services.obs.removeObserver(this, this.topic);
71
72 // Continue executing the generator function.
73 if (this.generator)
74 do_run_generator(this.generator);
75
76 this.generator = null;
77 this.topic = null;
78 }
79 }
80
81 // Close the cookie database. If a generator is supplied, it will be invoked
82 // once the close is complete.
83 function do_close_profile(generator, cleanse) {
84 // Register an observer for db close.
85 let obs = new _observer(generator, "cookie-db-closed");
86
87 // Close the db.
88 let service = Services.cookies.QueryInterface(Ci.nsIObserver);
89 service.observe(null, "profile-before-change", cleanse ? cleanse : "");
90 }
91
92 // Load the cookie database. If a generator is supplied, it will be invoked
93 // once the load is complete.
94 function do_load_profile(generator) {
95 // Register an observer for read completion.
96 let obs = new _observer(generator, "cookie-db-read");
97
98 // Load the profile.
99 let service = Services.cookies.QueryInterface(Ci.nsIObserver);
100 service.observe(null, "profile-do-change", "");
101 }
102
103 // Set a single session cookie using http and test the cookie count
104 // against 'expected'
105 function do_set_single_http_cookie(uri, channel, expected) {
106 Services.cookies.setCookieStringFromHttp(uri, null, null, "foo=bar", null, channel);
107 do_check_eq(Services.cookiemgr.countCookiesFromHost(uri.host), expected);
108 }
109
110 // Set four cookies; with & without channel, http and non-http; and test
111 // the cookie count against 'expected' after each set.
112 function do_set_cookies(uri, channel, session, expected) {
113 let suffix = session ? "" : "; max-age=1000";
114
115 // without channel
116 Services.cookies.setCookieString(uri, null, "oh=hai" + suffix, null);
117 do_check_eq(Services.cookiemgr.countCookiesFromHost(uri.host), expected[0]);
118 // with channel
119 Services.cookies.setCookieString(uri, null, "can=has" + suffix, channel);
120 do_check_eq(Services.cookiemgr.countCookiesFromHost(uri.host), expected[1]);
121 // without channel, from http
122 Services.cookies.setCookieStringFromHttp(uri, null, null, "cheez=burger" + suffix, null, null);
123 do_check_eq(Services.cookiemgr.countCookiesFromHost(uri.host), expected[2]);
124 // with channel, from http
125 Services.cookies.setCookieStringFromHttp(uri, null, null, "hot=dog" + suffix, null, channel);
126 do_check_eq(Services.cookiemgr.countCookiesFromHost(uri.host), expected[3]);
127 }
128
129 function do_count_enumerator(enumerator) {
130 let i = 0;
131 while (enumerator.hasMoreElements()) {
132 enumerator.getNext();
133 ++i;
134 }
135 return i;
136 }
137
138 function do_count_cookies() {
139 return do_count_enumerator(Services.cookiemgr.enumerator);
140 }
141
142 // Helper object to store cookie data.
143 function Cookie(name,
144 value,
145 host,
146 path,
147 expiry,
148 lastAccessed,
149 creationTime,
150 isSession,
151 isSecure,
152 isHttpOnly)
153 {
154 this.name = name;
155 this.value = value;
156 this.host = host;
157 this.path = path;
158 this.expiry = expiry;
159 this.lastAccessed = lastAccessed;
160 this.creationTime = creationTime;
161 this.isSession = isSession;
162 this.isSecure = isSecure;
163 this.isHttpOnly = isHttpOnly;
164
165 let strippedHost = host.charAt(0) == '.' ? host.slice(1) : host;
166
167 try {
168 this.baseDomain = Services.etld.getBaseDomainFromHost(strippedHost);
169 } catch (e) {
170 if (e.result == Cr.NS_ERROR_HOST_IS_IP_ADDRESS ||
171 e.result == Cr.NS_ERROR_INSUFFICIENT_DOMAIN_LEVELS)
172 this.baseDomain = strippedHost;
173 }
174 }
175
176 // Object representing a database connection and associated statements. The
177 // implementation varies depending on schema version.
178 function CookieDatabaseConnection(file, schema)
179 {
180 // Manually generate a cookies.sqlite file with appropriate rows, columns,
181 // and schema version. If it already exists, just set up our statements.
182 let exists = file.exists();
183
184 this.db = Services.storage.openDatabase(file);
185 this.schema = schema;
186 if (!exists)
187 this.db.schemaVersion = schema;
188
189 switch (schema) {
190 case 1:
191 {
192 if (!exists) {
193 this.db.executeSimpleSQL(
194 "CREATE TABLE moz_cookies ( \
195 id INTEGER PRIMARY KEY, \
196 name TEXT, \
197 value TEXT, \
198 host TEXT, \
199 path TEXT, \
200 expiry INTEGER, \
201 isSecure INTEGER, \
202 isHttpOnly INTEGER)");
203 }
204
205 this.stmtInsert = this.db.createStatement(
206 "INSERT INTO moz_cookies ( \
207 id, \
208 name, \
209 value, \
210 host, \
211 path, \
212 expiry, \
213 isSecure, \
214 isHttpOnly) \
215 VALUES ( \
216 :id, \
217 :name, \
218 :value, \
219 :host, \
220 :path, \
221 :expiry, \
222 :isSecure, \
223 :isHttpOnly)");
224
225 this.stmtDelete = this.db.createStatement(
226 "DELETE FROM moz_cookies WHERE id = :id");
227
228 break;
229 }
230
231 case 2:
232 {
233 if (!exists) {
234 this.db.executeSimpleSQL(
235 "CREATE TABLE moz_cookies ( \
236 id INTEGER PRIMARY KEY, \
237 name TEXT, \
238 value TEXT, \
239 host TEXT, \
240 path TEXT, \
241 expiry INTEGER, \
242 lastAccessed INTEGER, \
243 isSecure INTEGER, \
244 isHttpOnly INTEGER)");
245 }
246
247 this.stmtInsert = this.db.createStatement(
248 "INSERT OR REPLACE INTO moz_cookies ( \
249 id, \
250 name, \
251 value, \
252 host, \
253 path, \
254 expiry, \
255 lastAccessed, \
256 isSecure, \
257 isHttpOnly) \
258 VALUES ( \
259 :id, \
260 :name, \
261 :value, \
262 :host, \
263 :path, \
264 :expiry, \
265 :lastAccessed, \
266 :isSecure, \
267 :isHttpOnly)");
268
269 this.stmtDelete = this.db.createStatement(
270 "DELETE FROM moz_cookies WHERE id = :id");
271
272 this.stmtUpdate = this.db.createStatement(
273 "UPDATE moz_cookies SET lastAccessed = :lastAccessed WHERE id = :id");
274
275 break;
276 }
277
278 case 3:
279 {
280 if (!exists) {
281 this.db.executeSimpleSQL(
282 "CREATE TABLE moz_cookies ( \
283 id INTEGER PRIMARY KEY, \
284 baseDomain TEXT, \
285 name TEXT, \
286 value TEXT, \
287 host TEXT, \
288 path TEXT, \
289 expiry INTEGER, \
290 lastAccessed INTEGER, \
291 isSecure INTEGER, \
292 isHttpOnly INTEGER)");
293
294 this.db.executeSimpleSQL(
295 "CREATE INDEX moz_basedomain ON moz_cookies (baseDomain)");
296 }
297
298 this.stmtInsert = this.db.createStatement(
299 "INSERT INTO moz_cookies ( \
300 id, \
301 baseDomain, \
302 name, \
303 value, \
304 host, \
305 path, \
306 expiry, \
307 lastAccessed, \
308 isSecure, \
309 isHttpOnly) \
310 VALUES ( \
311 :id, \
312 :baseDomain, \
313 :name, \
314 :value, \
315 :host, \
316 :path, \
317 :expiry, \
318 :lastAccessed, \
319 :isSecure, \
320 :isHttpOnly)");
321
322 this.stmtDelete = this.db.createStatement(
323 "DELETE FROM moz_cookies WHERE id = :id");
324
325 this.stmtUpdate = this.db.createStatement(
326 "UPDATE moz_cookies SET lastAccessed = :lastAccessed WHERE id = :id");
327
328 break;
329 }
330
331 case 4:
332 {
333 if (!exists) {
334 this.db.executeSimpleSQL(
335 "CREATE TABLE moz_cookies ( \
336 id INTEGER PRIMARY KEY, \
337 baseDomain TEXT, \
338 name TEXT, \
339 value TEXT, \
340 host TEXT, \
341 path TEXT, \
342 expiry INTEGER, \
343 lastAccessed INTEGER, \
344 creationTime INTEGER, \
345 isSecure INTEGER, \
346 isHttpOnly INTEGER \
347 CONSTRAINT moz_uniqueid UNIQUE (name, host, path))");
348
349 this.db.executeSimpleSQL(
350 "CREATE INDEX moz_basedomain ON moz_cookies (baseDomain)");
351
352 this.db.executeSimpleSQL(
353 "PRAGMA journal_mode = WAL");
354 }
355
356 this.stmtInsert = this.db.createStatement(
357 "INSERT INTO moz_cookies ( \
358 baseDomain, \
359 name, \
360 value, \
361 host, \
362 path, \
363 expiry, \
364 lastAccessed, \
365 creationTime, \
366 isSecure, \
367 isHttpOnly) \
368 VALUES ( \
369 :baseDomain, \
370 :name, \
371 :value, \
372 :host, \
373 :path, \
374 :expiry, \
375 :lastAccessed, \
376 :creationTime, \
377 :isSecure, \
378 :isHttpOnly)");
379
380 this.stmtDelete = this.db.createStatement(
381 "DELETE FROM moz_cookies \
382 WHERE name = :name AND host = :host AND path = :path");
383
384 this.stmtUpdate = this.db.createStatement(
385 "UPDATE moz_cookies SET lastAccessed = :lastAccessed \
386 WHERE name = :name AND host = :host AND path = :path");
387
388 break;
389 }
390
391 default:
392 do_throw("unrecognized schemaVersion!");
393 }
394 }
395
396 CookieDatabaseConnection.prototype =
397 {
398 insertCookie: function(cookie)
399 {
400 if (!(cookie instanceof Cookie))
401 do_throw("not a cookie");
402
403 switch (this.schema)
404 {
405 case 1:
406 this.stmtInsert.bindByName("id", cookie.creationTime);
407 this.stmtInsert.bindByName("name", cookie.name);
408 this.stmtInsert.bindByName("value", cookie.value);
409 this.stmtInsert.bindByName("host", cookie.host);
410 this.stmtInsert.bindByName("path", cookie.path);
411 this.stmtInsert.bindByName("expiry", cookie.expiry);
412 this.stmtInsert.bindByName("isSecure", cookie.isSecure);
413 this.stmtInsert.bindByName("isHttpOnly", cookie.isHttpOnly);
414 break;
415
416 case 2:
417 this.stmtInsert.bindByName("id", cookie.creationTime);
418 this.stmtInsert.bindByName("name", cookie.name);
419 this.stmtInsert.bindByName("value", cookie.value);
420 this.stmtInsert.bindByName("host", cookie.host);
421 this.stmtInsert.bindByName("path", cookie.path);
422 this.stmtInsert.bindByName("expiry", cookie.expiry);
423 this.stmtInsert.bindByName("lastAccessed", cookie.lastAccessed);
424 this.stmtInsert.bindByName("isSecure", cookie.isSecure);
425 this.stmtInsert.bindByName("isHttpOnly", cookie.isHttpOnly);
426 break;
427
428 case 3:
429 this.stmtInsert.bindByName("id", cookie.creationTime);
430 this.stmtInsert.bindByName("baseDomain", cookie.baseDomain);
431 this.stmtInsert.bindByName("name", cookie.name);
432 this.stmtInsert.bindByName("value", cookie.value);
433 this.stmtInsert.bindByName("host", cookie.host);
434 this.stmtInsert.bindByName("path", cookie.path);
435 this.stmtInsert.bindByName("expiry", cookie.expiry);
436 this.stmtInsert.bindByName("lastAccessed", cookie.lastAccessed);
437 this.stmtInsert.bindByName("isSecure", cookie.isSecure);
438 this.stmtInsert.bindByName("isHttpOnly", cookie.isHttpOnly);
439 break;
440
441 case 4:
442 this.stmtInsert.bindByName("baseDomain", cookie.baseDomain);
443 this.stmtInsert.bindByName("name", cookie.name);
444 this.stmtInsert.bindByName("value", cookie.value);
445 this.stmtInsert.bindByName("host", cookie.host);
446 this.stmtInsert.bindByName("path", cookie.path);
447 this.stmtInsert.bindByName("expiry", cookie.expiry);
448 this.stmtInsert.bindByName("lastAccessed", cookie.lastAccessed);
449 this.stmtInsert.bindByName("creationTime", cookie.creationTime);
450 this.stmtInsert.bindByName("isSecure", cookie.isSecure);
451 this.stmtInsert.bindByName("isHttpOnly", cookie.isHttpOnly);
452 break;
453
454 default:
455 do_throw("unrecognized schemaVersion!");
456 }
457
458 do_execute_stmt(this.stmtInsert);
459 },
460
461 deleteCookie: function(cookie)
462 {
463 if (!(cookie instanceof Cookie))
464 do_throw("not a cookie");
465
466 switch (this.db.schemaVersion)
467 {
468 case 1:
469 case 2:
470 case 3:
471 this.stmtDelete.bindByName("id", cookie.creationTime);
472 break;
473
474 case 4:
475 this.stmtDelete.bindByName("name", cookie.name);
476 this.stmtDelete.bindByName("host", cookie.host);
477 this.stmtDelete.bindByName("path", cookie.path);
478 break;
479
480 default:
481 do_throw("unrecognized schemaVersion!");
482 }
483
484 do_execute_stmt(this.stmtDelete);
485 },
486
487 updateCookie: function(cookie)
488 {
489 if (!(cookie instanceof Cookie))
490 do_throw("not a cookie");
491
492 switch (this.db.schemaVersion)
493 {
494 case 1:
495 do_throw("can't update a schema 1 cookie!");
496
497 case 2:
498 case 3:
499 this.stmtUpdate.bindByName("id", cookie.creationTime);
500 this.stmtUpdate.bindByName("lastAccessed", cookie.lastAccessed);
501 break;
502
503 case 4:
504 this.stmtDelete.bindByName("name", cookie.name);
505 this.stmtDelete.bindByName("host", cookie.host);
506 this.stmtDelete.bindByName("path", cookie.path);
507 this.stmtUpdate.bindByName("lastAccessed", cookie.lastAccessed);
508 break;
509
510 default:
511 do_throw("unrecognized schemaVersion!");
512 }
513
514 do_execute_stmt(this.stmtUpdate);
515 },
516
517 close: function()
518 {
519 this.stmtInsert.finalize();
520 this.stmtDelete.finalize();
521 if (this.stmtUpdate)
522 this.stmtUpdate.finalize();
523 this.db.close();
524
525 this.stmtInsert = null;
526 this.stmtDelete = null;
527 this.stmtUpdate = null;
528 this.db = null;
529 }
530 }
531
532 function do_get_cookie_file(profile)
533 {
534 let file = profile.clone();
535 file.append("cookies.sqlite");
536 return file;
537 }
538
539 // Count the cookies from 'host' in a database. If 'host' is null, count all
540 // cookies.
541 function do_count_cookies_in_db(connection, host)
542 {
543 let select = null;
544 if (host) {
545 select = connection.createStatement(
546 "SELECT COUNT(1) FROM moz_cookies WHERE host = :host");
547 select.bindByName("host", host);
548 } else {
549 select = connection.createStatement(
550 "SELECT COUNT(1) FROM moz_cookies");
551 }
552
553 select.executeStep();
554 let result = select.getInt32(0);
555 select.reset();
556 select.finalize();
557 return result;
558 }
559
560 // Execute 'stmt', ensuring that we reset it if it throws.
561 function do_execute_stmt(stmt)
562 {
563 try {
564 stmt.executeStep();
565 stmt.reset();
566 } catch (e) {
567 stmt.reset();
568 throw e;
569 }
570 }

mercurial