1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/extensions/cookie/test/unit/head_cookies.js Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,570 @@ 1.4 +/* Any copyright is dedicated to the Public Domain. 1.5 + * http://creativecommons.org/publicdomain/zero/1.0/ 1.6 + */ 1.7 + 1.8 +Components.utils.import("resource://gre/modules/Services.jsm"); 1.9 +Components.utils.import("resource://gre/modules/NetUtil.jsm"); 1.10 +Components.utils.import("resource://gre/modules/XPCOMUtils.jsm"); 1.11 + 1.12 +const Cc = Components.classes; 1.13 +const Ci = Components.interfaces; 1.14 +const Cr = Components.results; 1.15 + 1.16 +XPCOMUtils.defineLazyServiceGetter(Services, "cookies", 1.17 + "@mozilla.org/cookieService;1", 1.18 + "nsICookieService"); 1.19 +XPCOMUtils.defineLazyServiceGetter(Services, "cookiemgr", 1.20 + "@mozilla.org/cookiemanager;1", 1.21 + "nsICookieManager2"); 1.22 + 1.23 +XPCOMUtils.defineLazyServiceGetter(Services, "etld", 1.24 + "@mozilla.org/network/effective-tld-service;1", 1.25 + "nsIEffectiveTLDService"); 1.26 + 1.27 +function do_check_throws(f, result, stack) 1.28 +{ 1.29 + if (!stack) 1.30 + stack = Components.stack.caller; 1.31 + 1.32 + try { 1.33 + f(); 1.34 + } catch (exc) { 1.35 + if (exc.result == result) 1.36 + return; 1.37 + do_throw("expected result " + result + ", caught " + exc, stack); 1.38 + } 1.39 + do_throw("expected result " + result + ", none thrown", stack); 1.40 +} 1.41 + 1.42 +// Helper to step a generator function and catch a StopIteration exception. 1.43 +function do_run_generator(generator) 1.44 +{ 1.45 + try { 1.46 + generator.next(); 1.47 + } catch (e) { 1.48 + if (e != StopIteration) 1.49 + do_throw("caught exception " + e, Components.stack.caller); 1.50 + } 1.51 +} 1.52 + 1.53 +// Helper to finish a generator function test. 1.54 +function do_finish_generator_test(generator) 1.55 +{ 1.56 + do_execute_soon(function() { 1.57 + generator.close(); 1.58 + do_test_finished(); 1.59 + }); 1.60 +} 1.61 + 1.62 +function _observer(generator, topic) { 1.63 + Services.obs.addObserver(this, topic, false); 1.64 + 1.65 + this.generator = generator; 1.66 + this.topic = topic; 1.67 +} 1.68 + 1.69 +_observer.prototype = { 1.70 + observe: function (subject, topic, data) { 1.71 + do_check_eq(this.topic, topic); 1.72 + 1.73 + Services.obs.removeObserver(this, this.topic); 1.74 + 1.75 + // Continue executing the generator function. 1.76 + if (this.generator) 1.77 + do_run_generator(this.generator); 1.78 + 1.79 + this.generator = null; 1.80 + this.topic = null; 1.81 + } 1.82 +} 1.83 + 1.84 +// Close the cookie database. If a generator is supplied, it will be invoked 1.85 +// once the close is complete. 1.86 +function do_close_profile(generator, cleanse) { 1.87 + // Register an observer for db close. 1.88 + let obs = new _observer(generator, "cookie-db-closed"); 1.89 + 1.90 + // Close the db. 1.91 + let service = Services.cookies.QueryInterface(Ci.nsIObserver); 1.92 + service.observe(null, "profile-before-change", cleanse ? cleanse : ""); 1.93 +} 1.94 + 1.95 +// Load the cookie database. If a generator is supplied, it will be invoked 1.96 +// once the load is complete. 1.97 +function do_load_profile(generator) { 1.98 + // Register an observer for read completion. 1.99 + let obs = new _observer(generator, "cookie-db-read"); 1.100 + 1.101 + // Load the profile. 1.102 + let service = Services.cookies.QueryInterface(Ci.nsIObserver); 1.103 + service.observe(null, "profile-do-change", ""); 1.104 +} 1.105 + 1.106 +// Set a single session cookie using http and test the cookie count 1.107 +// against 'expected' 1.108 +function do_set_single_http_cookie(uri, channel, expected) { 1.109 + Services.cookies.setCookieStringFromHttp(uri, null, null, "foo=bar", null, channel); 1.110 + do_check_eq(Services.cookiemgr.countCookiesFromHost(uri.host), expected); 1.111 +} 1.112 + 1.113 +// Set four cookies; with & without channel, http and non-http; and test 1.114 +// the cookie count against 'expected' after each set. 1.115 +function do_set_cookies(uri, channel, session, expected) { 1.116 + let suffix = session ? "" : "; max-age=1000"; 1.117 + 1.118 + // without channel 1.119 + Services.cookies.setCookieString(uri, null, "oh=hai" + suffix, null); 1.120 + do_check_eq(Services.cookiemgr.countCookiesFromHost(uri.host), expected[0]); 1.121 + // with channel 1.122 + Services.cookies.setCookieString(uri, null, "can=has" + suffix, channel); 1.123 + do_check_eq(Services.cookiemgr.countCookiesFromHost(uri.host), expected[1]); 1.124 + // without channel, from http 1.125 + Services.cookies.setCookieStringFromHttp(uri, null, null, "cheez=burger" + suffix, null, null); 1.126 + do_check_eq(Services.cookiemgr.countCookiesFromHost(uri.host), expected[2]); 1.127 + // with channel, from http 1.128 + Services.cookies.setCookieStringFromHttp(uri, null, null, "hot=dog" + suffix, null, channel); 1.129 + do_check_eq(Services.cookiemgr.countCookiesFromHost(uri.host), expected[3]); 1.130 +} 1.131 + 1.132 +function do_count_enumerator(enumerator) { 1.133 + let i = 0; 1.134 + while (enumerator.hasMoreElements()) { 1.135 + enumerator.getNext(); 1.136 + ++i; 1.137 + } 1.138 + return i; 1.139 +} 1.140 + 1.141 +function do_count_cookies() { 1.142 + return do_count_enumerator(Services.cookiemgr.enumerator); 1.143 +} 1.144 + 1.145 +// Helper object to store cookie data. 1.146 +function Cookie(name, 1.147 + value, 1.148 + host, 1.149 + path, 1.150 + expiry, 1.151 + lastAccessed, 1.152 + creationTime, 1.153 + isSession, 1.154 + isSecure, 1.155 + isHttpOnly) 1.156 +{ 1.157 + this.name = name; 1.158 + this.value = value; 1.159 + this.host = host; 1.160 + this.path = path; 1.161 + this.expiry = expiry; 1.162 + this.lastAccessed = lastAccessed; 1.163 + this.creationTime = creationTime; 1.164 + this.isSession = isSession; 1.165 + this.isSecure = isSecure; 1.166 + this.isHttpOnly = isHttpOnly; 1.167 + 1.168 + let strippedHost = host.charAt(0) == '.' ? host.slice(1) : host; 1.169 + 1.170 + try { 1.171 + this.baseDomain = Services.etld.getBaseDomainFromHost(strippedHost); 1.172 + } catch (e) { 1.173 + if (e.result == Cr.NS_ERROR_HOST_IS_IP_ADDRESS || 1.174 + e.result == Cr.NS_ERROR_INSUFFICIENT_DOMAIN_LEVELS) 1.175 + this.baseDomain = strippedHost; 1.176 + } 1.177 +} 1.178 + 1.179 +// Object representing a database connection and associated statements. The 1.180 +// implementation varies depending on schema version. 1.181 +function CookieDatabaseConnection(file, schema) 1.182 +{ 1.183 + // Manually generate a cookies.sqlite file with appropriate rows, columns, 1.184 + // and schema version. If it already exists, just set up our statements. 1.185 + let exists = file.exists(); 1.186 + 1.187 + this.db = Services.storage.openDatabase(file); 1.188 + this.schema = schema; 1.189 + if (!exists) 1.190 + this.db.schemaVersion = schema; 1.191 + 1.192 + switch (schema) { 1.193 + case 1: 1.194 + { 1.195 + if (!exists) { 1.196 + this.db.executeSimpleSQL( 1.197 + "CREATE TABLE moz_cookies ( \ 1.198 + id INTEGER PRIMARY KEY, \ 1.199 + name TEXT, \ 1.200 + value TEXT, \ 1.201 + host TEXT, \ 1.202 + path TEXT, \ 1.203 + expiry INTEGER, \ 1.204 + isSecure INTEGER, \ 1.205 + isHttpOnly INTEGER)"); 1.206 + } 1.207 + 1.208 + this.stmtInsert = this.db.createStatement( 1.209 + "INSERT INTO moz_cookies ( \ 1.210 + id, \ 1.211 + name, \ 1.212 + value, \ 1.213 + host, \ 1.214 + path, \ 1.215 + expiry, \ 1.216 + isSecure, \ 1.217 + isHttpOnly) \ 1.218 + VALUES ( \ 1.219 + :id, \ 1.220 + :name, \ 1.221 + :value, \ 1.222 + :host, \ 1.223 + :path, \ 1.224 + :expiry, \ 1.225 + :isSecure, \ 1.226 + :isHttpOnly)"); 1.227 + 1.228 + this.stmtDelete = this.db.createStatement( 1.229 + "DELETE FROM moz_cookies WHERE id = :id"); 1.230 + 1.231 + break; 1.232 + } 1.233 + 1.234 + case 2: 1.235 + { 1.236 + if (!exists) { 1.237 + this.db.executeSimpleSQL( 1.238 + "CREATE TABLE moz_cookies ( \ 1.239 + id INTEGER PRIMARY KEY, \ 1.240 + name TEXT, \ 1.241 + value TEXT, \ 1.242 + host TEXT, \ 1.243 + path TEXT, \ 1.244 + expiry INTEGER, \ 1.245 + lastAccessed INTEGER, \ 1.246 + isSecure INTEGER, \ 1.247 + isHttpOnly INTEGER)"); 1.248 + } 1.249 + 1.250 + this.stmtInsert = this.db.createStatement( 1.251 + "INSERT OR REPLACE INTO moz_cookies ( \ 1.252 + id, \ 1.253 + name, \ 1.254 + value, \ 1.255 + host, \ 1.256 + path, \ 1.257 + expiry, \ 1.258 + lastAccessed, \ 1.259 + isSecure, \ 1.260 + isHttpOnly) \ 1.261 + VALUES ( \ 1.262 + :id, \ 1.263 + :name, \ 1.264 + :value, \ 1.265 + :host, \ 1.266 + :path, \ 1.267 + :expiry, \ 1.268 + :lastAccessed, \ 1.269 + :isSecure, \ 1.270 + :isHttpOnly)"); 1.271 + 1.272 + this.stmtDelete = this.db.createStatement( 1.273 + "DELETE FROM moz_cookies WHERE id = :id"); 1.274 + 1.275 + this.stmtUpdate = this.db.createStatement( 1.276 + "UPDATE moz_cookies SET lastAccessed = :lastAccessed WHERE id = :id"); 1.277 + 1.278 + break; 1.279 + } 1.280 + 1.281 + case 3: 1.282 + { 1.283 + if (!exists) { 1.284 + this.db.executeSimpleSQL( 1.285 + "CREATE TABLE moz_cookies ( \ 1.286 + id INTEGER PRIMARY KEY, \ 1.287 + baseDomain TEXT, \ 1.288 + name TEXT, \ 1.289 + value TEXT, \ 1.290 + host TEXT, \ 1.291 + path TEXT, \ 1.292 + expiry INTEGER, \ 1.293 + lastAccessed INTEGER, \ 1.294 + isSecure INTEGER, \ 1.295 + isHttpOnly INTEGER)"); 1.296 + 1.297 + this.db.executeSimpleSQL( 1.298 + "CREATE INDEX moz_basedomain ON moz_cookies (baseDomain)"); 1.299 + } 1.300 + 1.301 + this.stmtInsert = this.db.createStatement( 1.302 + "INSERT INTO moz_cookies ( \ 1.303 + id, \ 1.304 + baseDomain, \ 1.305 + name, \ 1.306 + value, \ 1.307 + host, \ 1.308 + path, \ 1.309 + expiry, \ 1.310 + lastAccessed, \ 1.311 + isSecure, \ 1.312 + isHttpOnly) \ 1.313 + VALUES ( \ 1.314 + :id, \ 1.315 + :baseDomain, \ 1.316 + :name, \ 1.317 + :value, \ 1.318 + :host, \ 1.319 + :path, \ 1.320 + :expiry, \ 1.321 + :lastAccessed, \ 1.322 + :isSecure, \ 1.323 + :isHttpOnly)"); 1.324 + 1.325 + this.stmtDelete = this.db.createStatement( 1.326 + "DELETE FROM moz_cookies WHERE id = :id"); 1.327 + 1.328 + this.stmtUpdate = this.db.createStatement( 1.329 + "UPDATE moz_cookies SET lastAccessed = :lastAccessed WHERE id = :id"); 1.330 + 1.331 + break; 1.332 + } 1.333 + 1.334 + case 4: 1.335 + { 1.336 + if (!exists) { 1.337 + this.db.executeSimpleSQL( 1.338 + "CREATE TABLE moz_cookies ( \ 1.339 + id INTEGER PRIMARY KEY, \ 1.340 + baseDomain TEXT, \ 1.341 + name TEXT, \ 1.342 + value TEXT, \ 1.343 + host TEXT, \ 1.344 + path TEXT, \ 1.345 + expiry INTEGER, \ 1.346 + lastAccessed INTEGER, \ 1.347 + creationTime INTEGER, \ 1.348 + isSecure INTEGER, \ 1.349 + isHttpOnly INTEGER \ 1.350 + CONSTRAINT moz_uniqueid UNIQUE (name, host, path))"); 1.351 + 1.352 + this.db.executeSimpleSQL( 1.353 + "CREATE INDEX moz_basedomain ON moz_cookies (baseDomain)"); 1.354 + 1.355 + this.db.executeSimpleSQL( 1.356 + "PRAGMA journal_mode = WAL"); 1.357 + } 1.358 + 1.359 + this.stmtInsert = this.db.createStatement( 1.360 + "INSERT INTO moz_cookies ( \ 1.361 + baseDomain, \ 1.362 + name, \ 1.363 + value, \ 1.364 + host, \ 1.365 + path, \ 1.366 + expiry, \ 1.367 + lastAccessed, \ 1.368 + creationTime, \ 1.369 + isSecure, \ 1.370 + isHttpOnly) \ 1.371 + VALUES ( \ 1.372 + :baseDomain, \ 1.373 + :name, \ 1.374 + :value, \ 1.375 + :host, \ 1.376 + :path, \ 1.377 + :expiry, \ 1.378 + :lastAccessed, \ 1.379 + :creationTime, \ 1.380 + :isSecure, \ 1.381 + :isHttpOnly)"); 1.382 + 1.383 + this.stmtDelete = this.db.createStatement( 1.384 + "DELETE FROM moz_cookies \ 1.385 + WHERE name = :name AND host = :host AND path = :path"); 1.386 + 1.387 + this.stmtUpdate = this.db.createStatement( 1.388 + "UPDATE moz_cookies SET lastAccessed = :lastAccessed \ 1.389 + WHERE name = :name AND host = :host AND path = :path"); 1.390 + 1.391 + break; 1.392 + } 1.393 + 1.394 + default: 1.395 + do_throw("unrecognized schemaVersion!"); 1.396 + } 1.397 +} 1.398 + 1.399 +CookieDatabaseConnection.prototype = 1.400 +{ 1.401 + insertCookie: function(cookie) 1.402 + { 1.403 + if (!(cookie instanceof Cookie)) 1.404 + do_throw("not a cookie"); 1.405 + 1.406 + switch (this.schema) 1.407 + { 1.408 + case 1: 1.409 + this.stmtInsert.bindByName("id", cookie.creationTime); 1.410 + this.stmtInsert.bindByName("name", cookie.name); 1.411 + this.stmtInsert.bindByName("value", cookie.value); 1.412 + this.stmtInsert.bindByName("host", cookie.host); 1.413 + this.stmtInsert.bindByName("path", cookie.path); 1.414 + this.stmtInsert.bindByName("expiry", cookie.expiry); 1.415 + this.stmtInsert.bindByName("isSecure", cookie.isSecure); 1.416 + this.stmtInsert.bindByName("isHttpOnly", cookie.isHttpOnly); 1.417 + break; 1.418 + 1.419 + case 2: 1.420 + this.stmtInsert.bindByName("id", cookie.creationTime); 1.421 + this.stmtInsert.bindByName("name", cookie.name); 1.422 + this.stmtInsert.bindByName("value", cookie.value); 1.423 + this.stmtInsert.bindByName("host", cookie.host); 1.424 + this.stmtInsert.bindByName("path", cookie.path); 1.425 + this.stmtInsert.bindByName("expiry", cookie.expiry); 1.426 + this.stmtInsert.bindByName("lastAccessed", cookie.lastAccessed); 1.427 + this.stmtInsert.bindByName("isSecure", cookie.isSecure); 1.428 + this.stmtInsert.bindByName("isHttpOnly", cookie.isHttpOnly); 1.429 + break; 1.430 + 1.431 + case 3: 1.432 + this.stmtInsert.bindByName("id", cookie.creationTime); 1.433 + this.stmtInsert.bindByName("baseDomain", cookie.baseDomain); 1.434 + this.stmtInsert.bindByName("name", cookie.name); 1.435 + this.stmtInsert.bindByName("value", cookie.value); 1.436 + this.stmtInsert.bindByName("host", cookie.host); 1.437 + this.stmtInsert.bindByName("path", cookie.path); 1.438 + this.stmtInsert.bindByName("expiry", cookie.expiry); 1.439 + this.stmtInsert.bindByName("lastAccessed", cookie.lastAccessed); 1.440 + this.stmtInsert.bindByName("isSecure", cookie.isSecure); 1.441 + this.stmtInsert.bindByName("isHttpOnly", cookie.isHttpOnly); 1.442 + break; 1.443 + 1.444 + case 4: 1.445 + this.stmtInsert.bindByName("baseDomain", cookie.baseDomain); 1.446 + this.stmtInsert.bindByName("name", cookie.name); 1.447 + this.stmtInsert.bindByName("value", cookie.value); 1.448 + this.stmtInsert.bindByName("host", cookie.host); 1.449 + this.stmtInsert.bindByName("path", cookie.path); 1.450 + this.stmtInsert.bindByName("expiry", cookie.expiry); 1.451 + this.stmtInsert.bindByName("lastAccessed", cookie.lastAccessed); 1.452 + this.stmtInsert.bindByName("creationTime", cookie.creationTime); 1.453 + this.stmtInsert.bindByName("isSecure", cookie.isSecure); 1.454 + this.stmtInsert.bindByName("isHttpOnly", cookie.isHttpOnly); 1.455 + break; 1.456 + 1.457 + default: 1.458 + do_throw("unrecognized schemaVersion!"); 1.459 + } 1.460 + 1.461 + do_execute_stmt(this.stmtInsert); 1.462 + }, 1.463 + 1.464 + deleteCookie: function(cookie) 1.465 + { 1.466 + if (!(cookie instanceof Cookie)) 1.467 + do_throw("not a cookie"); 1.468 + 1.469 + switch (this.db.schemaVersion) 1.470 + { 1.471 + case 1: 1.472 + case 2: 1.473 + case 3: 1.474 + this.stmtDelete.bindByName("id", cookie.creationTime); 1.475 + break; 1.476 + 1.477 + case 4: 1.478 + this.stmtDelete.bindByName("name", cookie.name); 1.479 + this.stmtDelete.bindByName("host", cookie.host); 1.480 + this.stmtDelete.bindByName("path", cookie.path); 1.481 + break; 1.482 + 1.483 + default: 1.484 + do_throw("unrecognized schemaVersion!"); 1.485 + } 1.486 + 1.487 + do_execute_stmt(this.stmtDelete); 1.488 + }, 1.489 + 1.490 + updateCookie: function(cookie) 1.491 + { 1.492 + if (!(cookie instanceof Cookie)) 1.493 + do_throw("not a cookie"); 1.494 + 1.495 + switch (this.db.schemaVersion) 1.496 + { 1.497 + case 1: 1.498 + do_throw("can't update a schema 1 cookie!"); 1.499 + 1.500 + case 2: 1.501 + case 3: 1.502 + this.stmtUpdate.bindByName("id", cookie.creationTime); 1.503 + this.stmtUpdate.bindByName("lastAccessed", cookie.lastAccessed); 1.504 + break; 1.505 + 1.506 + case 4: 1.507 + this.stmtDelete.bindByName("name", cookie.name); 1.508 + this.stmtDelete.bindByName("host", cookie.host); 1.509 + this.stmtDelete.bindByName("path", cookie.path); 1.510 + this.stmtUpdate.bindByName("lastAccessed", cookie.lastAccessed); 1.511 + break; 1.512 + 1.513 + default: 1.514 + do_throw("unrecognized schemaVersion!"); 1.515 + } 1.516 + 1.517 + do_execute_stmt(this.stmtUpdate); 1.518 + }, 1.519 + 1.520 + close: function() 1.521 + { 1.522 + this.stmtInsert.finalize(); 1.523 + this.stmtDelete.finalize(); 1.524 + if (this.stmtUpdate) 1.525 + this.stmtUpdate.finalize(); 1.526 + this.db.close(); 1.527 + 1.528 + this.stmtInsert = null; 1.529 + this.stmtDelete = null; 1.530 + this.stmtUpdate = null; 1.531 + this.db = null; 1.532 + } 1.533 +} 1.534 + 1.535 +function do_get_cookie_file(profile) 1.536 +{ 1.537 + let file = profile.clone(); 1.538 + file.append("cookies.sqlite"); 1.539 + return file; 1.540 +} 1.541 + 1.542 +// Count the cookies from 'host' in a database. If 'host' is null, count all 1.543 +// cookies. 1.544 +function do_count_cookies_in_db(connection, host) 1.545 +{ 1.546 + let select = null; 1.547 + if (host) { 1.548 + select = connection.createStatement( 1.549 + "SELECT COUNT(1) FROM moz_cookies WHERE host = :host"); 1.550 + select.bindByName("host", host); 1.551 + } else { 1.552 + select = connection.createStatement( 1.553 + "SELECT COUNT(1) FROM moz_cookies"); 1.554 + } 1.555 + 1.556 + select.executeStep(); 1.557 + let result = select.getInt32(0); 1.558 + select.reset(); 1.559 + select.finalize(); 1.560 + return result; 1.561 +} 1.562 + 1.563 +// Execute 'stmt', ensuring that we reset it if it throws. 1.564 +function do_execute_stmt(stmt) 1.565 +{ 1.566 + try { 1.567 + stmt.executeStep(); 1.568 + stmt.reset(); 1.569 + } catch (e) { 1.570 + stmt.reset(); 1.571 + throw e; 1.572 + } 1.573 +}