/* Any copyright is dedicated to the Public Domain. http://creativecommons.org/publicdomain/zero/1.0/ */ // Test the various ways opening a cookie database can fail in a synchronous // (i.e. immediate) manner, and that the database is renamed and recreated // under each circumstance. These circumstances are, in no particular order: // // 1) A corrupt database, such that opening the connection fails. // 2) The 'moz_cookies' table doesn't exist. // 3) Not all of the expected columns exist, and statement creation fails when: // a) The schema version is larger than the current version. // b) The schema version is less than or equal to the current version. // 4) Migration fails. This will have different modes depending on the initial // version: // a) Schema 1: the 'lastAccessed' column already exists. // b) Schema 2: the 'baseDomain' column already exists; or 'baseDomain' // cannot be computed for a particular host. // c) Schema 3: the 'creationTime' column already exists; or the // 'moz_uniqueid' index already exists. "use strict"; let profile; let cookieFile; let backupFile; let sub_generator; let now; let futureExpiry; let cookie; var COOKIE_DATABASE_SCHEMA_CURRENT = 13; var test_generator = do_run_test(); function run_test() { do_test_pending(); do_run_generator(test_generator); } function finish_test() { executeSoon(function () { test_generator.return(); do_test_finished(); }); } function* do_run_test() { // Set up a profile. profile = do_get_profile(); // Allow all cookies. Services.prefs.setIntPref("network.cookie.cookieBehavior", 0); Services.prefs.setBoolPref( "network.cookieJarSettings.unblocked_for_testing", true ); // Get the cookie file and the backup file. cookieFile = profile.clone(); cookieFile.append("cookies.sqlite"); backupFile = profile.clone(); backupFile.append("cookies.sqlite.bak"); Assert.ok(!cookieFile.exists()); Assert.ok(!backupFile.exists()); // Create a cookie object for testing. now = Date.now() * 1000; futureExpiry = Math.round(now / 1e6 + 1000); cookie = new Cookie( "oh", "hai", "bar.com", "/", futureExpiry, now, now, false, false, false ); sub_generator = run_test_1(test_generator); sub_generator.next(); yield; sub_generator = run_test_2(test_generator); sub_generator.next(); yield; sub_generator = run_test_3(test_generator, 99); sub_generator.next(); yield; sub_generator = run_test_3(test_generator, COOKIE_DATABASE_SCHEMA_CURRENT); sub_generator.next(); yield; sub_generator = run_test_3(test_generator, 4); sub_generator.next(); yield; sub_generator = run_test_3(test_generator, 3); sub_generator.next(); yield; sub_generator = run_test_4_exists( test_generator, 1, "ALTER TABLE moz_cookies ADD lastAccessed INTEGER" ); sub_generator.next(); yield; sub_generator = run_test_4_exists( test_generator, 2, "ALTER TABLE moz_cookies ADD baseDomain TEXT" ); sub_generator.next(); yield; sub_generator = run_test_4_baseDomain(test_generator); sub_generator.next(); yield; sub_generator = run_test_4_exists( test_generator, 3, "ALTER TABLE moz_cookies ADD creationTime INTEGER" ); sub_generator.next(); yield; sub_generator = run_test_4_exists( test_generator, 3, "CREATE UNIQUE INDEX moz_uniqueid ON moz_cookies (name, host, path)" ); sub_generator.next(); yield; finish_test(); } const garbage = "hello thar!"; function create_garbage_file(file) { // Create an empty database file. file.create(Ci.nsIFile.NORMAL_FILE_TYPE, -1); Assert.ok(file.exists()); Assert.equal(file.fileSize, 0); // Write some garbage to it. let ostream = Cc["@mozilla.org/network/file-output-stream;1"].createInstance( Ci.nsIFileOutputStream ); ostream.init(file, -1, -1, 0); ostream.write(garbage, garbage.length); ostream.flush(); ostream.close(); file = file.clone(); // Windows maintains a stat cache. It's lame. Assert.equal(file.fileSize, garbage.length); } function check_garbage_file(file) { Assert.ok(file.exists()); Assert.equal(file.fileSize, garbage.length); file.remove(false); Assert.ok(!file.exists()); } function* run_test_1(generator) { // Create a garbage database file. create_garbage_file(cookieFile); let uri = NetUtil.newURI("http://foo.com/"); const channel = NetUtil.newChannel({ uri, loadUsingSystemPrincipal: true, contentPolicyType: Ci.nsIContentPolicy.TYPE_DOCUMENT, }); // Load the profile and populate it. Services.cookies.setCookieStringFromHttp( uri, "oh=hai; max-age=1000", channel ); // Fake a profile change. do_close_profile(sub_generator); yield; do_load_profile(); // Check that the new database contains the cookie, and the old file was // renamed. Assert.equal(do_count_cookies(), 1); check_garbage_file(backupFile); // Close the profile. do_close_profile(sub_generator); yield; // Clean up. cookieFile.remove(false); Assert.ok(!cookieFile.exists()); do_run_generator(generator); } function* run_test_2(generator) { // Load the profile and populate it. do_load_profile(); let uri = NetUtil.newURI("http://foo.com/"); const channel = NetUtil.newChannel({ uri, loadUsingSystemPrincipal: true, contentPolicyType: Ci.nsIContentPolicy.TYPE_DOCUMENT, }); Services.cookies.setCookieStringFromHttp( uri, "oh=hai; max-age=1000", channel ); // Fake a profile change. do_close_profile(sub_generator); yield; // Drop the table. let db = Services.storage.openDatabase(cookieFile); db.executeSimpleSQL("DROP TABLE moz_cookies"); db.close(); // Load the profile and check that the table is recreated in-place. do_load_profile(); Assert.equal(do_count_cookies(), 0); Assert.ok(!backupFile.exists()); // Close the profile. do_close_profile(sub_generator); yield; // Clean up. cookieFile.remove(false); Assert.ok(!cookieFile.exists()); do_run_generator(generator); } function* run_test_3(generator, schema) { // Manually create a schema 2 database, populate it, and set the schema // version to the desired number. let schema2db = new CookieDatabaseConnection(do_get_cookie_file(profile), 2); schema2db.insertCookie(cookie); schema2db.db.schemaVersion = schema; schema2db.close(); // Load the profile and check that the column existence test fails. do_load_profile(); Assert.equal(do_count_cookies(), 0); // Close the profile. do_close_profile(sub_generator); yield; // Check that the schema version has been reset. let db = Services.storage.openDatabase(cookieFile); Assert.equal(db.schemaVersion, COOKIE_DATABASE_SCHEMA_CURRENT); db.close(); // Clean up. cookieFile.remove(false); Assert.ok(!cookieFile.exists()); do_run_generator(generator); } function* run_test_4_exists(generator, schema, stmt) { // Manually create a database, populate it, and add the desired column. let db = new CookieDatabaseConnection(do_get_cookie_file(profile), schema); db.insertCookie(cookie); db.db.executeSimpleSQL(stmt); db.close(); // Load the profile and check that migration fails. do_load_profile(); Assert.equal(do_count_cookies(), 0); // Close the profile. do_close_profile(sub_generator); yield; // Check that the schema version has been reset and the backup file exists. db = Services.storage.openDatabase(cookieFile); Assert.equal(db.schemaVersion, COOKIE_DATABASE_SCHEMA_CURRENT); db.close(); Assert.ok(backupFile.exists()); // Clean up. cookieFile.remove(false); backupFile.remove(false); Assert.ok(!cookieFile.exists()); Assert.ok(!backupFile.exists()); do_run_generator(generator); } function* run_test_4_baseDomain(generator) { // Manually create a database and populate it with a bad host. let db = new CookieDatabaseConnection(do_get_cookie_file(profile), 2); let badCookie = new Cookie( "oh", "hai", ".", "/", futureExpiry, now, now, false, false, false ); db.insertCookie(badCookie); db.close(); // Load the profile and check that migration fails. do_load_profile(); Assert.equal(do_count_cookies(), 0); // Close the profile. do_close_profile(sub_generator); yield; // Check that the schema version has been reset and the backup file exists. db = Services.storage.openDatabase(cookieFile); Assert.equal(db.schemaVersion, COOKIE_DATABASE_SCHEMA_CURRENT); db.close(); Assert.ok(backupFile.exists()); // Clean up. cookieFile.remove(false); backupFile.remove(false); Assert.ok(!cookieFile.exists()); Assert.ok(!backupFile.exists()); do_run_generator(generator); }