diff options
Diffstat (limited to 'extensions/cookie/test/unit')
42 files changed, 6395 insertions, 0 deletions
diff --git a/extensions/cookie/test/unit/cookieprompt.js b/extensions/cookie/test/unit/cookieprompt.js new file mode 100644 index 000000000..c0a17e006 --- /dev/null +++ b/extensions/cookie/test/unit/cookieprompt.js @@ -0,0 +1,22 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +Components.utils.import("resource://gre/modules/XPCOMUtils.jsm"); + +var Ci = Components.interfaces; + +function CookiePromptService() { +} + +CookiePromptService.prototype = { + classID: Components.ID("{509b5540-c87c-11dd-ad8b-0800200c9a66}"), + QueryInterface: XPCOMUtils.generateQI([Ci.nsICookiePromptService]), + + cookieDialog: function(parent, cookie, hostname, + cookiesFromHost, changingCookie, + rememberDecision) { + return 0; + } +}; + +this.NSGetFactory = XPCOMUtils.generateNSGetFactory([CookiePromptService]); diff --git a/extensions/cookie/test/unit/cookieprompt.manifest b/extensions/cookie/test/unit/cookieprompt.manifest new file mode 100644 index 000000000..5a3bcad40 --- /dev/null +++ b/extensions/cookie/test/unit/cookieprompt.manifest @@ -0,0 +1,2 @@ +component {509b5540-c87c-11dd-ad8b-0800200c9a66} cookieprompt.js
+contract @mozilla.org/embedcomp/cookieprompt-service;1 {509b5540-c87c-11dd-ad8b-0800200c9a66}
diff --git a/extensions/cookie/test/unit/head_cookies.js b/extensions/cookie/test/unit/head_cookies.js new file mode 100644 index 000000000..fe4c0a53d --- /dev/null +++ b/extensions/cookie/test/unit/head_cookies.js @@ -0,0 +1,570 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +Components.utils.import("resource://gre/modules/Services.jsm"); +Components.utils.import("resource://gre/modules/NetUtil.jsm"); +Components.utils.import("resource://gre/modules/XPCOMUtils.jsm"); + +var Cc = Components.classes; +var Ci = Components.interfaces; +var Cr = Components.results; + +XPCOMUtils.defineLazyServiceGetter(Services, "cookies", + "@mozilla.org/cookieService;1", + "nsICookieService"); +XPCOMUtils.defineLazyServiceGetter(Services, "cookiemgr", + "@mozilla.org/cookiemanager;1", + "nsICookieManager2"); + +XPCOMUtils.defineLazyServiceGetter(Services, "etld", + "@mozilla.org/network/effective-tld-service;1", + "nsIEffectiveTLDService"); + +function do_check_throws(f, result, stack) +{ + if (!stack) + stack = Components.stack.caller; + + try { + f(); + } catch (exc) { + if (exc.result == result) + return; + do_throw("expected result " + result + ", caught " + exc, stack); + } + do_throw("expected result " + result + ", none thrown", stack); +} + +// Helper to step a generator function and catch a StopIteration exception. +function do_run_generator(generator) +{ + try { + generator.next(); + } catch (e) { + if (e != StopIteration) + do_throw("caught exception " + e, Components.stack.caller); + } +} + +// Helper to finish a generator function test. +function do_finish_generator_test(generator) +{ + do_execute_soon(function() { + generator.close(); + do_test_finished(); + }); +} + +function _observer(generator, topic) { + Services.obs.addObserver(this, topic, false); + + this.generator = generator; + this.topic = topic; +} + +_observer.prototype = { + observe: function (subject, topic, data) { + do_check_eq(this.topic, topic); + + Services.obs.removeObserver(this, this.topic); + + // Continue executing the generator function. + if (this.generator) + do_run_generator(this.generator); + + this.generator = null; + this.topic = null; + } +} + +// Close the cookie database. If a generator is supplied, it will be invoked +// once the close is complete. +function do_close_profile(generator) { + // Register an observer for db close. + let obs = new _observer(generator, "cookie-db-closed"); + + // Close the db. + let service = Services.cookies.QueryInterface(Ci.nsIObserver); + service.observe(null, "profile-before-change", "shutdown-persist"); +} + +// Load the cookie database. If a generator is supplied, it will be invoked +// once the load is complete. +function do_load_profile(generator) { + // Register an observer for read completion. + let obs = new _observer(generator, "cookie-db-read"); + + // Load the profile. + let service = Services.cookies.QueryInterface(Ci.nsIObserver); + service.observe(null, "profile-do-change", ""); +} + +// Set a single session cookie using http and test the cookie count +// against 'expected' +function do_set_single_http_cookie(uri, channel, expected) { + Services.cookies.setCookieStringFromHttp(uri, null, null, "foo=bar", null, channel); + do_check_eq(Services.cookiemgr.countCookiesFromHost(uri.host), expected); +} + +// Set four cookies; with & without channel, http and non-http; and test +// the cookie count against 'expected' after each set. +function do_set_cookies(uri, channel, session, expected) { + let suffix = session ? "" : "; max-age=1000"; + + // without channel + Services.cookies.setCookieString(uri, null, "oh=hai" + suffix, null); + do_check_eq(Services.cookiemgr.countCookiesFromHost(uri.host), expected[0]); + // with channel + Services.cookies.setCookieString(uri, null, "can=has" + suffix, channel); + do_check_eq(Services.cookiemgr.countCookiesFromHost(uri.host), expected[1]); + // without channel, from http + Services.cookies.setCookieStringFromHttp(uri, null, null, "cheez=burger" + suffix, null, null); + do_check_eq(Services.cookiemgr.countCookiesFromHost(uri.host), expected[2]); + // with channel, from http + Services.cookies.setCookieStringFromHttp(uri, null, null, "hot=dog" + suffix, null, channel); + do_check_eq(Services.cookiemgr.countCookiesFromHost(uri.host), expected[3]); +} + +function do_count_enumerator(enumerator) { + let i = 0; + while (enumerator.hasMoreElements()) { + enumerator.getNext(); + ++i; + } + return i; +} + +function do_count_cookies() { + return do_count_enumerator(Services.cookiemgr.enumerator); +} + +// Helper object to store cookie data. +function Cookie(name, + value, + host, + path, + expiry, + lastAccessed, + creationTime, + isSession, + isSecure, + isHttpOnly) +{ + this.name = name; + this.value = value; + this.host = host; + this.path = path; + this.expiry = expiry; + this.lastAccessed = lastAccessed; + this.creationTime = creationTime; + this.isSession = isSession; + this.isSecure = isSecure; + this.isHttpOnly = isHttpOnly; + + let strippedHost = host.charAt(0) == '.' ? host.slice(1) : host; + + try { + this.baseDomain = Services.etld.getBaseDomainFromHost(strippedHost); + } catch (e) { + if (e.result == Cr.NS_ERROR_HOST_IS_IP_ADDRESS || + e.result == Cr.NS_ERROR_INSUFFICIENT_DOMAIN_LEVELS) + this.baseDomain = strippedHost; + } +} + +// Object representing a database connection and associated statements. The +// implementation varies depending on schema version. +function CookieDatabaseConnection(file, schema) +{ + // Manually generate a cookies.sqlite file with appropriate rows, columns, + // and schema version. If it already exists, just set up our statements. + let exists = file.exists(); + + this.db = Services.storage.openDatabase(file); + this.schema = schema; + if (!exists) + this.db.schemaVersion = schema; + + switch (schema) { + case 1: + { + if (!exists) { + this.db.executeSimpleSQL( + "CREATE TABLE moz_cookies ( \ + id INTEGER PRIMARY KEY, \ + name TEXT, \ + value TEXT, \ + host TEXT, \ + path TEXT, \ + expiry INTEGER, \ + isSecure INTEGER, \ + isHttpOnly INTEGER)"); + } + + this.stmtInsert = this.db.createStatement( + "INSERT INTO moz_cookies ( \ + id, \ + name, \ + value, \ + host, \ + path, \ + expiry, \ + isSecure, \ + isHttpOnly) \ + VALUES ( \ + :id, \ + :name, \ + :value, \ + :host, \ + :path, \ + :expiry, \ + :isSecure, \ + :isHttpOnly)"); + + this.stmtDelete = this.db.createStatement( + "DELETE FROM moz_cookies WHERE id = :id"); + + break; + } + + case 2: + { + if (!exists) { + this.db.executeSimpleSQL( + "CREATE TABLE moz_cookies ( \ + id INTEGER PRIMARY KEY, \ + name TEXT, \ + value TEXT, \ + host TEXT, \ + path TEXT, \ + expiry INTEGER, \ + lastAccessed INTEGER, \ + isSecure INTEGER, \ + isHttpOnly INTEGER)"); + } + + this.stmtInsert = this.db.createStatement( + "INSERT OR REPLACE INTO moz_cookies ( \ + id, \ + name, \ + value, \ + host, \ + path, \ + expiry, \ + lastAccessed, \ + isSecure, \ + isHttpOnly) \ + VALUES ( \ + :id, \ + :name, \ + :value, \ + :host, \ + :path, \ + :expiry, \ + :lastAccessed, \ + :isSecure, \ + :isHttpOnly)"); + + this.stmtDelete = this.db.createStatement( + "DELETE FROM moz_cookies WHERE id = :id"); + + this.stmtUpdate = this.db.createStatement( + "UPDATE moz_cookies SET lastAccessed = :lastAccessed WHERE id = :id"); + + break; + } + + case 3: + { + if (!exists) { + this.db.executeSimpleSQL( + "CREATE TABLE moz_cookies ( \ + id INTEGER PRIMARY KEY, \ + baseDomain TEXT, \ + name TEXT, \ + value TEXT, \ + host TEXT, \ + path TEXT, \ + expiry INTEGER, \ + lastAccessed INTEGER, \ + isSecure INTEGER, \ + isHttpOnly INTEGER)"); + + this.db.executeSimpleSQL( + "CREATE INDEX moz_basedomain ON moz_cookies (baseDomain)"); + } + + this.stmtInsert = this.db.createStatement( + "INSERT INTO moz_cookies ( \ + id, \ + baseDomain, \ + name, \ + value, \ + host, \ + path, \ + expiry, \ + lastAccessed, \ + isSecure, \ + isHttpOnly) \ + VALUES ( \ + :id, \ + :baseDomain, \ + :name, \ + :value, \ + :host, \ + :path, \ + :expiry, \ + :lastAccessed, \ + :isSecure, \ + :isHttpOnly)"); + + this.stmtDelete = this.db.createStatement( + "DELETE FROM moz_cookies WHERE id = :id"); + + this.stmtUpdate = this.db.createStatement( + "UPDATE moz_cookies SET lastAccessed = :lastAccessed WHERE id = :id"); + + break; + } + + case 4: + { + if (!exists) { + this.db.executeSimpleSQL( + "CREATE TABLE moz_cookies ( \ + id INTEGER PRIMARY KEY, \ + baseDomain TEXT, \ + name TEXT, \ + value TEXT, \ + host TEXT, \ + path TEXT, \ + expiry INTEGER, \ + lastAccessed INTEGER, \ + creationTime INTEGER, \ + isSecure INTEGER, \ + isHttpOnly INTEGER \ + CONSTRAINT moz_uniqueid UNIQUE (name, host, path))"); + + this.db.executeSimpleSQL( + "CREATE INDEX moz_basedomain ON moz_cookies (baseDomain)"); + + this.db.executeSimpleSQL( + "PRAGMA journal_mode = WAL"); + } + + this.stmtInsert = this.db.createStatement( + "INSERT INTO moz_cookies ( \ + baseDomain, \ + name, \ + value, \ + host, \ + path, \ + expiry, \ + lastAccessed, \ + creationTime, \ + isSecure, \ + isHttpOnly) \ + VALUES ( \ + :baseDomain, \ + :name, \ + :value, \ + :host, \ + :path, \ + :expiry, \ + :lastAccessed, \ + :creationTime, \ + :isSecure, \ + :isHttpOnly)"); + + this.stmtDelete = this.db.createStatement( + "DELETE FROM moz_cookies \ + WHERE name = :name AND host = :host AND path = :path"); + + this.stmtUpdate = this.db.createStatement( + "UPDATE moz_cookies SET lastAccessed = :lastAccessed \ + WHERE name = :name AND host = :host AND path = :path"); + + break; + } + + default: + do_throw("unrecognized schemaVersion!"); + } +} + +CookieDatabaseConnection.prototype = +{ + insertCookie: function(cookie) + { + if (!(cookie instanceof Cookie)) + do_throw("not a cookie"); + + switch (this.schema) + { + case 1: + this.stmtInsert.bindByName("id", cookie.creationTime); + this.stmtInsert.bindByName("name", cookie.name); + this.stmtInsert.bindByName("value", cookie.value); + this.stmtInsert.bindByName("host", cookie.host); + this.stmtInsert.bindByName("path", cookie.path); + this.stmtInsert.bindByName("expiry", cookie.expiry); + this.stmtInsert.bindByName("isSecure", cookie.isSecure); + this.stmtInsert.bindByName("isHttpOnly", cookie.isHttpOnly); + break; + + case 2: + this.stmtInsert.bindByName("id", cookie.creationTime); + this.stmtInsert.bindByName("name", cookie.name); + this.stmtInsert.bindByName("value", cookie.value); + this.stmtInsert.bindByName("host", cookie.host); + this.stmtInsert.bindByName("path", cookie.path); + this.stmtInsert.bindByName("expiry", cookie.expiry); + this.stmtInsert.bindByName("lastAccessed", cookie.lastAccessed); + this.stmtInsert.bindByName("isSecure", cookie.isSecure); + this.stmtInsert.bindByName("isHttpOnly", cookie.isHttpOnly); + break; + + case 3: + this.stmtInsert.bindByName("id", cookie.creationTime); + this.stmtInsert.bindByName("baseDomain", cookie.baseDomain); + this.stmtInsert.bindByName("name", cookie.name); + this.stmtInsert.bindByName("value", cookie.value); + this.stmtInsert.bindByName("host", cookie.host); + this.stmtInsert.bindByName("path", cookie.path); + this.stmtInsert.bindByName("expiry", cookie.expiry); + this.stmtInsert.bindByName("lastAccessed", cookie.lastAccessed); + this.stmtInsert.bindByName("isSecure", cookie.isSecure); + this.stmtInsert.bindByName("isHttpOnly", cookie.isHttpOnly); + break; + + case 4: + this.stmtInsert.bindByName("baseDomain", cookie.baseDomain); + this.stmtInsert.bindByName("name", cookie.name); + this.stmtInsert.bindByName("value", cookie.value); + this.stmtInsert.bindByName("host", cookie.host); + this.stmtInsert.bindByName("path", cookie.path); + this.stmtInsert.bindByName("expiry", cookie.expiry); + this.stmtInsert.bindByName("lastAccessed", cookie.lastAccessed); + this.stmtInsert.bindByName("creationTime", cookie.creationTime); + this.stmtInsert.bindByName("isSecure", cookie.isSecure); + this.stmtInsert.bindByName("isHttpOnly", cookie.isHttpOnly); + break; + + default: + do_throw("unrecognized schemaVersion!"); + } + + do_execute_stmt(this.stmtInsert); + }, + + deleteCookie: function(cookie) + { + if (!(cookie instanceof Cookie)) + do_throw("not a cookie"); + + switch (this.db.schemaVersion) + { + case 1: + case 2: + case 3: + this.stmtDelete.bindByName("id", cookie.creationTime); + break; + + case 4: + this.stmtDelete.bindByName("name", cookie.name); + this.stmtDelete.bindByName("host", cookie.host); + this.stmtDelete.bindByName("path", cookie.path); + break; + + default: + do_throw("unrecognized schemaVersion!"); + } + + do_execute_stmt(this.stmtDelete); + }, + + updateCookie: function(cookie) + { + if (!(cookie instanceof Cookie)) + do_throw("not a cookie"); + + switch (this.db.schemaVersion) + { + case 1: + do_throw("can't update a schema 1 cookie!"); + + case 2: + case 3: + this.stmtUpdate.bindByName("id", cookie.creationTime); + this.stmtUpdate.bindByName("lastAccessed", cookie.lastAccessed); + break; + + case 4: + this.stmtDelete.bindByName("name", cookie.name); + this.stmtDelete.bindByName("host", cookie.host); + this.stmtDelete.bindByName("path", cookie.path); + this.stmtUpdate.bindByName("lastAccessed", cookie.lastAccessed); + break; + + default: + do_throw("unrecognized schemaVersion!"); + } + + do_execute_stmt(this.stmtUpdate); + }, + + close: function() + { + this.stmtInsert.finalize(); + this.stmtDelete.finalize(); + if (this.stmtUpdate) + this.stmtUpdate.finalize(); + this.db.close(); + + this.stmtInsert = null; + this.stmtDelete = null; + this.stmtUpdate = null; + this.db = null; + } +} + +function do_get_cookie_file(profile) +{ + let file = profile.clone(); + file.append("cookies.sqlite"); + return file; +} + +// Count the cookies from 'host' in a database. If 'host' is null, count all +// cookies. +function do_count_cookies_in_db(connection, host) +{ + let select = null; + if (host) { + select = connection.createStatement( + "SELECT COUNT(1) FROM moz_cookies WHERE host = :host"); + select.bindByName("host", host); + } else { + select = connection.createStatement( + "SELECT COUNT(1) FROM moz_cookies"); + } + + select.executeStep(); + let result = select.getInt32(0); + select.reset(); + select.finalize(); + return result; +} + +// Execute 'stmt', ensuring that we reset it if it throws. +function do_execute_stmt(stmt) +{ + try { + stmt.executeStep(); + stmt.reset(); + } catch (e) { + stmt.reset(); + throw e; + } +} diff --git a/extensions/cookie/test/unit/test_bug526789.js b/extensions/cookie/test/unit/test_bug526789.js new file mode 100644 index 000000000..0eac1d492 --- /dev/null +++ b/extensions/cookie/test/unit/test_bug526789.js @@ -0,0 +1,248 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +function run_test() { + var cs = Cc["@mozilla.org/cookieService;1"].getService(Ci.nsICookieService); + var cm = Cc["@mozilla.org/cookiemanager;1"].getService(Ci.nsICookieManager2); + var expiry = (Date.now() + 1000) * 1000; + + cm.removeAll(); + + // Allow all cookies. + Services.prefs.setIntPref("network.cookie.cookieBehavior", 0); + + // test that variants of 'baz.com' get normalized appropriately, but that + // malformed hosts are rejected + cm.add("baz.com", "/", "foo", "bar", false, false, true, expiry, {}); + do_check_eq(cm.countCookiesFromHost("baz.com"), 1); + do_check_eq(cm.countCookiesFromHost("BAZ.com"), 1); + do_check_eq(cm.countCookiesFromHost(".baz.com"), 1); + do_check_eq(cm.countCookiesFromHost("baz.com."), 0); + do_check_eq(cm.countCookiesFromHost(".baz.com."), 0); + do_check_throws(function() { + cm.countCookiesFromHost("baz.com.."); + }, Cr.NS_ERROR_ILLEGAL_VALUE); + do_check_throws(function() { + cm.countCookiesFromHost("baz..com"); + }, Cr.NS_ERROR_ILLEGAL_VALUE); + do_check_throws(function() { + cm.countCookiesFromHost("..baz.com"); + }, Cr.NS_ERROR_ILLEGAL_VALUE); + cm.remove("BAZ.com.", "foo", "/", false, {}); + do_check_eq(cm.countCookiesFromHost("baz.com"), 1); + cm.remove("baz.com", "foo", "/", false, {}); + do_check_eq(cm.countCookiesFromHost("baz.com"), 0); + + // Test that 'baz.com' and 'baz.com.' are treated differently + cm.add("baz.com.", "/", "foo", "bar", false, false, true, expiry, {}); + do_check_eq(cm.countCookiesFromHost("baz.com"), 0); + do_check_eq(cm.countCookiesFromHost("BAZ.com"), 0); + do_check_eq(cm.countCookiesFromHost(".baz.com"), 0); + do_check_eq(cm.countCookiesFromHost("baz.com."), 1); + do_check_eq(cm.countCookiesFromHost(".baz.com."), 1); + cm.remove("baz.com", "foo", "/", false, {}); + do_check_eq(cm.countCookiesFromHost("baz.com."), 1); + cm.remove("baz.com.", "foo", "/", false, {}); + do_check_eq(cm.countCookiesFromHost("baz.com."), 0); + + // test that domain cookies are illegal for IP addresses, aliases such as + // 'localhost', and eTLD's such as 'co.uk' + cm.add("192.168.0.1", "/", "foo", "bar", false, false, true, expiry, {}); + do_check_eq(cm.countCookiesFromHost("192.168.0.1"), 1); + do_check_eq(cm.countCookiesFromHost("192.168.0.1."), 0); + do_check_throws(function() { + cm.countCookiesFromHost(".192.168.0.1"); + }, Cr.NS_ERROR_ILLEGAL_VALUE); + do_check_throws(function() { + cm.countCookiesFromHost(".192.168.0.1."); + }, Cr.NS_ERROR_ILLEGAL_VALUE); + + cm.add("localhost", "/", "foo", "bar", false, false, true, expiry, {}); + do_check_eq(cm.countCookiesFromHost("localhost"), 1); + do_check_eq(cm.countCookiesFromHost("localhost."), 0); + do_check_throws(function() { + cm.countCookiesFromHost(".localhost"); + }, Cr.NS_ERROR_ILLEGAL_VALUE); + do_check_throws(function() { + cm.countCookiesFromHost(".localhost."); + }, Cr.NS_ERROR_ILLEGAL_VALUE); + + cm.add("co.uk", "/", "foo", "bar", false, false, true, expiry, {}); + do_check_eq(cm.countCookiesFromHost("co.uk"), 1); + do_check_eq(cm.countCookiesFromHost("co.uk."), 0); + do_check_throws(function() { + cm.countCookiesFromHost(".co.uk"); + }, Cr.NS_ERROR_ILLEGAL_VALUE); + do_check_throws(function() { + cm.countCookiesFromHost(".co.uk."); + }, Cr.NS_ERROR_ILLEGAL_VALUE); + + cm.removeAll(); + + // test that setting an empty or '.' http:// host results in a no-op + var uri = NetUtil.newURI("http://baz.com/"); + var emptyuri = NetUtil.newURI("http:///"); + var doturi = NetUtil.newURI("http://./"); + do_check_eq(uri.asciiHost, "baz.com"); + do_check_eq(emptyuri.asciiHost, ""); + do_check_eq(doturi.asciiHost, "."); + cs.setCookieString(emptyuri, null, "foo2=bar", null); + do_check_eq(getCookieCount(), 0); + cs.setCookieString(doturi, null, "foo3=bar", null); + do_check_eq(getCookieCount(), 0); + cs.setCookieString(uri, null, "foo=bar", null); + do_check_eq(getCookieCount(), 1); + + do_check_eq(cs.getCookieString(uri, null), "foo=bar"); + do_check_eq(cs.getCookieString(emptyuri, null), null); + do_check_eq(cs.getCookieString(doturi, null), null); + + do_check_eq(cm.countCookiesFromHost(""), 0); + do_check_throws(function() { + cm.countCookiesFromHost("."); + }, Cr.NS_ERROR_ILLEGAL_VALUE); + do_check_throws(function() { + cm.countCookiesFromHost(".."); + }, Cr.NS_ERROR_ILLEGAL_VALUE); + + var e = cm.getCookiesFromHost("", {}); + do_check_false(e.hasMoreElements()); + do_check_throws(function() { + cm.getCookiesFromHost(".", {}); + }, Cr.NS_ERROR_ILLEGAL_VALUE); + do_check_throws(function() { + cm.getCookiesFromHost("..", {}); + }, Cr.NS_ERROR_ILLEGAL_VALUE); + + e = cm.getCookiesFromHost("baz.com", {}); + do_check_true(e.hasMoreElements()); + do_check_eq(e.getNext().QueryInterface(Ci.nsICookie2).name, "foo"); + do_check_false(e.hasMoreElements()); + e = cm.getCookiesFromHost("", {}); + do_check_false(e.hasMoreElements()); + do_check_throws(function() { + cm.getCookiesFromHost(".", {}); + }, Cr.NS_ERROR_ILLEGAL_VALUE); + do_check_throws(function() { + cm.getCookiesFromHost("..", {}); + }, Cr.NS_ERROR_ILLEGAL_VALUE); + + cm.removeAll(); + + // test that an empty file:// host works + emptyuri = NetUtil.newURI("file:///"); + do_check_eq(emptyuri.asciiHost, ""); + do_check_eq(NetUtil.newURI("file://./").asciiHost, ""); + do_check_eq(NetUtil.newURI("file://foo.bar/").asciiHost, ""); + cs.setCookieString(emptyuri, null, "foo2=bar", null); + do_check_eq(getCookieCount(), 1); + cs.setCookieString(emptyuri, null, "foo3=bar; domain=", null); + do_check_eq(getCookieCount(), 2); + cs.setCookieString(emptyuri, null, "foo4=bar; domain=.", null); + do_check_eq(getCookieCount(), 2); + cs.setCookieString(emptyuri, null, "foo5=bar; domain=bar.com", null); + do_check_eq(getCookieCount(), 2); + + do_check_eq(cs.getCookieString(emptyuri, null), "foo2=bar; foo3=bar"); + + do_check_eq(cm.countCookiesFromHost("baz.com"), 0); + do_check_eq(cm.countCookiesFromHost(""), 2); + do_check_throws(function() { + cm.countCookiesFromHost("."); + }, Cr.NS_ERROR_ILLEGAL_VALUE); + + e = cm.getCookiesFromHost("baz.com", {}); + do_check_false(e.hasMoreElements()); + e = cm.getCookiesFromHost("", {}); + do_check_true(e.hasMoreElements()); + e.getNext(); + do_check_true(e.hasMoreElements()); + e.getNext(); + do_check_false(e.hasMoreElements()); + do_check_throws(function() { + cm.getCookiesFromHost(".", {}); + }, Cr.NS_ERROR_ILLEGAL_VALUE); + + cm.removeAll(); + + // test that an empty host to add() or remove() works, + // but a host of '.' doesn't + cm.add("", "/", "foo2", "bar", false, false, true, expiry, {}); + do_check_eq(getCookieCount(), 1); + do_check_throws(function() { + cm.add(".", "/", "foo3", "bar", false, false, true, expiry, {}); + }, Cr.NS_ERROR_ILLEGAL_VALUE); + do_check_eq(getCookieCount(), 1); + + cm.remove("", "foo2", "/", false, {}); + do_check_eq(getCookieCount(), 0); + do_check_throws(function() { + cm.remove(".", "foo3", "/", false, {}); + }, Cr.NS_ERROR_ILLEGAL_VALUE); + + // test that the 'domain' attribute accepts a leading dot for IP addresses, + // aliases such as 'localhost', and eTLD's such as 'co.uk'; but that the + // resulting cookie is for the exact host only. + testDomainCookie("http://192.168.0.1/", "192.168.0.1"); + testDomainCookie("http://localhost/", "localhost"); + testDomainCookie("http://co.uk/", "co.uk"); + + // Test that trailing dots are treated differently for purposes of the + // 'domain' attribute when using setCookieString. + testTrailingDotCookie("http://localhost", "localhost"); + testTrailingDotCookie("http://foo.com", "foo.com"); + + cm.removeAll(); +} + +function getCookieCount() { + var count = 0; + var cm = Cc["@mozilla.org/cookiemanager;1"].getService(Ci.nsICookieManager2); + var enumerator = cm.enumerator; + while (enumerator.hasMoreElements()) { + if (!(enumerator.getNext() instanceof Ci.nsICookie2)) + throw new Error("not a cookie"); + ++count; + } + return count; +} + +function testDomainCookie(uriString, domain) { + var cs = Cc["@mozilla.org/cookieService;1"].getService(Ci.nsICookieService); + var cm = Cc["@mozilla.org/cookiemanager;1"].getService(Ci.nsICookieManager2); + + cm.removeAll(); + + var uri = NetUtil.newURI(uriString); + cs.setCookieString(uri, null, "foo=bar; domain=" + domain, null); + var e = cm.getCookiesFromHost(domain, {}); + do_check_true(e.hasMoreElements()); + do_check_eq(e.getNext().QueryInterface(Ci.nsICookie2).host, domain); + cm.removeAll(); + + cs.setCookieString(uri, null, "foo=bar; domain=." + domain, null); + e = cm.getCookiesFromHost(domain, {}); + do_check_true(e.hasMoreElements()); + do_check_eq(e.getNext().QueryInterface(Ci.nsICookie2).host, domain); + cm.removeAll(); +} + +function testTrailingDotCookie(uriString, domain) { + var cs = Cc["@mozilla.org/cookieService;1"].getService(Ci.nsICookieService); + var cm = Cc["@mozilla.org/cookiemanager;1"].getService(Ci.nsICookieManager2); + + cm.removeAll(); + + var uri = NetUtil.newURI(uriString); + cs.setCookieString(uri, null, "foo=bar; domain=" + domain + ".", null); + do_check_eq(cm.countCookiesFromHost(domain), 0); + do_check_eq(cm.countCookiesFromHost(domain + "."), 0); + cm.removeAll(); + + uri = NetUtil.newURI(uriString + "."); + cs.setCookieString(uri, null, "foo=bar; domain=" + domain, null); + do_check_eq(cm.countCookiesFromHost(domain), 0); + do_check_eq(cm.countCookiesFromHost(domain + "."), 0); + cm.removeAll(); +} + diff --git a/extensions/cookie/test/unit/test_bug650522.js b/extensions/cookie/test/unit/test_bug650522.js new file mode 100644 index 000000000..1d99cb233 --- /dev/null +++ b/extensions/cookie/test/unit/test_bug650522.js @@ -0,0 +1,16 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +Components.utils.import("resource://gre/modules/NetUtil.jsm"); + +function run_test() { + var cs = Cc["@mozilla.org/cookieService;1"].getService(Ci.nsICookieService); + var cm = Cc["@mozilla.org/cookiemanager;1"].getService(Ci.nsICookieManager2); + var expiry = (Date.now() + 1000) * 1000; + + // Test our handling of host names with a single character at the beginning + // followed by a dot. + cm.add("e.mail.com", "/", "foo", "bar", false, false, true, expiry, {}); + do_check_eq(cm.countCookiesFromHost("e.mail.com"), 1); + do_check_eq(cs.getCookieString(NetUtil.newURI("http://e.mail.com"), null), "foo=bar"); +} diff --git a/extensions/cookie/test/unit/test_bug667087.js b/extensions/cookie/test/unit/test_bug667087.js new file mode 100644 index 000000000..2400dea51 --- /dev/null +++ b/extensions/cookie/test/unit/test_bug667087.js @@ -0,0 +1,16 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +Components.utils.import("resource://gre/modules/NetUtil.jsm"); + +function run_test() { + var cs = Cc["@mozilla.org/cookieService;1"].getService(Ci.nsICookieService); + var cm = Cc["@mozilla.org/cookiemanager;1"].getService(Ci.nsICookieManager2); + var expiry = (Date.now() + 1000) * 1000; + + // Test our handling of host names with a single character consisting only + // of a single character + cm.add("a", "/", "foo", "bar", false, false, true, expiry, {}); + do_check_eq(cm.countCookiesFromHost("a"), 1); + do_check_eq(cs.getCookieString(NetUtil.newURI("http://a"), null), "foo=bar"); +} diff --git a/extensions/cookie/test/unit/test_cookies_async_failure.js b/extensions/cookie/test/unit/test_cookies_async_failure.js new file mode 100644 index 000000000..628553a4e --- /dev/null +++ b/extensions/cookie/test/unit/test_cookies_async_failure.js @@ -0,0 +1,600 @@ +/* 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 an asynchronous +// (i.e. after synchronous initialization) manner, and that the database is +// renamed and recreated under each circumstance. These circumstances are, in no +// particular order: +// +// 1) A write operation failing after the database has been read in. +// 2) Asynchronous read failure due to a corrupt database. +// 3) Synchronous read failure due to a corrupt database, when reading: +// a) a single base domain; +// b) the entire database. +// 4) Asynchronous read failure, followed by another failure during INSERT but +// before the database closes for rebuilding. (The additional error should be +// ignored.) +// 5) Asynchronous read failure, followed by an INSERT failure during rebuild. +// This should result in an abort of the database rebuild; the partially- +// built database should be moved to 'cookies.sqlite.bak-rebuild'. + +var test_generator = do_run_test(); + +function run_test() { + do_test_pending(); + do_run_generator(test_generator); +} + +function finish_test() { + do_execute_soon(function() { + test_generator.close(); + do_test_finished(); + }); +} + +function do_run_test() { + // Set up a profile. + this.profile = do_get_profile(); + + // Allow all cookies. + Services.prefs.setIntPref("network.cookie.cookieBehavior", 0); + + // Get the cookie file and the backup file. + do_check_false(do_get_cookie_file(profile).exists()); + do_check_false(do_get_backup_file(profile).exists()); + + // Create a cookie object for testing. + this.now = Date.now() * 1000; + this.futureExpiry = Math.round(this.now / 1e6 + 1000); + this.cookie = new Cookie("oh", "hai", "bar.com", "/", this.futureExpiry, + this.now, this.now, false, false, false); + + this.sub_generator = run_test_1(test_generator); + sub_generator.next(); + yield; + + this.sub_generator = run_test_2(test_generator); + sub_generator.next(); + yield; + + this.sub_generator = run_test_3(test_generator); + sub_generator.next(); + yield; + + this.sub_generator = run_test_4(test_generator); + sub_generator.next(); + yield; + + this.sub_generator = run_test_5(test_generator); + sub_generator.next(); + yield; + + finish_test(); + return; +} + +function do_get_backup_file(profile) +{ + let file = profile.clone(); + file.append("cookies.sqlite.bak"); + return file; +} + +function do_get_rebuild_backup_file(profile) +{ + let file = profile.clone(); + file.append("cookies.sqlite.bak-rebuild"); + return file; +} + +function do_corrupt_db(file) +{ + // Sanity check: the database size should be larger than 450k, since we've + // written about 460k of data. If it's not, let's make it obvious now. + let size = file.fileSize; + do_check_true(size > 450e3); + + // Corrupt the database by writing bad data to the end of the file. We + // assume that the important metadata -- table structure etc -- is stored + // elsewhere, and that doing this will not cause synchronous failure when + // initializing the database connection. This is totally empirical -- + // overwriting between 1k and 100k of live data seems to work. (Note that the + // database file will be larger than the actual content requires, since the + // cookie service uses a large growth increment. So we calculate the offset + // based on the expected size of the content, not just the file size.) + let ostream = Cc["@mozilla.org/network/file-output-stream;1"]. + createInstance(Ci.nsIFileOutputStream); + ostream.init(file, 2, -1, 0); + let sstream = ostream.QueryInterface(Ci.nsISeekableStream); + let n = size - 450e3 + 20e3; + sstream.seek(Ci.nsISeekableStream.NS_SEEK_SET, size - n); + for (let i = 0; i < n; ++i) { + ostream.write("a", 1); + } + ostream.flush(); + ostream.close(); + + do_check_eq(file.clone().fileSize, size); + return size; +} + +function run_test_1(generator) +{ + // Load the profile and populate it. + let uri = NetUtil.newURI("http://foo.com/"); + Services.cookies.setCookieString(uri, null, "oh=hai; max-age=1000", null); + + // Close the profile. + do_close_profile(sub_generator); + yield; + + // Open a database connection now, before we load the profile and begin + // asynchronous write operations. In order to tell when the async delete + // statement has completed, we do something tricky: open a schema 2 connection + // and add a cookie with null baseDomain. We can then wait until we see it + // deleted in the new database. + let db2 = new CookieDatabaseConnection(do_get_cookie_file(profile), 2); + db2.db.executeSimpleSQL("INSERT INTO moz_cookies (baseDomain) VALUES (NULL)"); + db2.close(); + let db = new CookieDatabaseConnection(do_get_cookie_file(profile), 4); + do_check_eq(do_count_cookies_in_db(db.db), 2); + + // Load the profile, and wait for async read completion... + do_load_profile(sub_generator); + yield; + + // ... and the DELETE statement to finish. + while (do_count_cookies_in_db(db.db) == 2) { + do_execute_soon(function() { + do_run_generator(sub_generator); + }); + yield; + } + do_check_eq(do_count_cookies_in_db(db.db), 1); + + // Insert a row. + db.insertCookie(cookie); + db.close(); + + // Attempt to insert a cookie with the same (name, host, path) triplet. + Services.cookiemgr.add(cookie.host, cookie.path, cookie.name, "hallo", + cookie.isSecure, cookie.isHttpOnly, cookie.isSession, cookie.expiry, {}); + + // Check that the cookie service accepted the new cookie. + do_check_eq(Services.cookiemgr.countCookiesFromHost(cookie.host), 1); + + // Wait for the cookie service to rename the old database and rebuild. + new _observer(sub_generator, "cookie-db-rebuilding"); + yield; + do_execute_soon(function() { do_run_generator(sub_generator); }); + yield; + + // At this point, the cookies should still be in memory. + do_check_eq(Services.cookiemgr.countCookiesFromHost("foo.com"), 1); + do_check_eq(Services.cookiemgr.countCookiesFromHost(cookie.host), 1); + do_check_eq(do_count_cookies(), 2); + + // Close the profile. + do_close_profile(sub_generator); + yield; + + // Check that the original database was renamed, and that it contains the + // original cookie. + do_check_true(do_get_backup_file(profile).exists()); + let backupdb = Services.storage.openDatabase(do_get_backup_file(profile)); + do_check_eq(do_count_cookies_in_db(backupdb, "foo.com"), 1); + backupdb.close(); + + // Load the profile, and check that it contains the new cookie. + do_load_profile(); + + do_check_eq(Services.cookiemgr.countCookiesFromHost("foo.com"), 1); + let enumerator = Services.cookiemgr.getCookiesFromHost(cookie.host, {}); + do_check_true(enumerator.hasMoreElements()); + let dbcookie = enumerator.getNext().QueryInterface(Ci.nsICookie2); + do_check_eq(dbcookie.value, "hallo"); + do_check_false(enumerator.hasMoreElements()); + + // Close the profile. + do_close_profile(sub_generator); + yield; + + // Clean up. + do_get_cookie_file(profile).remove(false); + do_get_backup_file(profile).remove(false); + do_check_false(do_get_cookie_file(profile).exists()); + do_check_false(do_get_backup_file(profile).exists()); + do_run_generator(generator); +} + +function run_test_2(generator) +{ + // Load the profile and populate it. + do_load_profile(); + for (let i = 0; i < 3000; ++i) { + let uri = NetUtil.newURI("http://" + i + ".com/"); + Services.cookies.setCookieString(uri, null, "oh=hai; max-age=1000", null); + } + + // Close the profile. + do_close_profile(sub_generator); + yield; + + // Corrupt the database file. + let size = do_corrupt_db(do_get_cookie_file(profile)); + + // Load the profile. + do_load_profile(); + + // At this point, the database connection should be open. Ensure that it + // succeeded. + do_check_false(do_get_backup_file(profile).exists()); + + // Synchronously read in the first cookie. This will cause it to go into the + // cookie table, whereupon it will be written out during database rebuild. + do_check_eq(Services.cookiemgr.countCookiesFromHost("0.com"), 1); + + // Wait for the asynchronous read to choke, at which point the backup file + // will be created and the database rebuilt. + new _observer(sub_generator, "cookie-db-rebuilding"); + yield; + do_execute_soon(function() { do_run_generator(sub_generator); }); + yield; + + // At this point, the cookies should still be in memory. + do_check_eq(Services.cookiemgr.countCookiesFromHost("0.com"), 1); + do_check_eq(do_count_cookies(), 1); + + // Close the profile. + do_close_profile(sub_generator); + yield; + + // Check that the original database was renamed. + do_check_true(do_get_backup_file(profile).exists()); + do_check_eq(do_get_backup_file(profile).fileSize, size); + let db = Services.storage.openDatabase(do_get_cookie_file(profile)); + do_check_eq(do_count_cookies_in_db(db, "0.com"), 1); + db.close(); + + // Load the profile, and check that it contains the new cookie. + do_load_profile(); + do_check_eq(Services.cookiemgr.countCookiesFromHost("0.com"), 1); + do_check_eq(do_count_cookies(), 1); + + // Close the profile. + do_close_profile(sub_generator); + yield; + + // Clean up. + do_get_cookie_file(profile).remove(false); + do_get_backup_file(profile).remove(false); + do_check_false(do_get_cookie_file(profile).exists()); + do_check_false(do_get_backup_file(profile).exists()); + do_run_generator(generator); +} + +function run_test_3(generator) +{ + // Set the maximum cookies per base domain limit to a large value, so that + // corrupting the database is easier. + Services.prefs.setIntPref("network.cookie.maxPerHost", 3000); + + // Load the profile and populate it. + do_load_profile(); + for (let i = 0; i < 10; ++i) { + let uri = NetUtil.newURI("http://hither.com/"); + Services.cookies.setCookieString(uri, null, "oh" + i + "=hai; max-age=1000", + null); + } + for (let i = 10; i < 3000; ++i) { + let uri = NetUtil.newURI("http://haithur.com/"); + Services.cookies.setCookieString(uri, null, "oh" + i + "=hai; max-age=1000", + null); + } + + // Close the profile. + do_close_profile(sub_generator); + yield; + + // Corrupt the database file. + let size = do_corrupt_db(do_get_cookie_file(profile)); + + // Load the profile. + do_load_profile(); + + // At this point, the database connection should be open. Ensure that it + // succeeded. + do_check_false(do_get_backup_file(profile).exists()); + + // Synchronously read in the cookies for our two domains. The first should + // succeed, but the second should fail midway through, resulting in none of + // those cookies being present. + do_check_eq(Services.cookiemgr.countCookiesFromHost("hither.com"), 10); + do_check_eq(Services.cookiemgr.countCookiesFromHost("haithur.com"), 0); + + // Wait for the backup file to be created and the database rebuilt. + do_check_false(do_get_backup_file(profile).exists()); + new _observer(sub_generator, "cookie-db-rebuilding"); + yield; + do_execute_soon(function() { do_run_generator(sub_generator); }); + yield; + + // Close the profile. + do_close_profile(sub_generator); + yield; + let db = Services.storage.openDatabase(do_get_cookie_file(profile)); + do_check_eq(do_count_cookies_in_db(db, "hither.com"), 10); + do_check_eq(do_count_cookies_in_db(db), 10); + db.close(); + + // Check that the original database was renamed. + do_check_true(do_get_backup_file(profile).exists()); + do_check_eq(do_get_backup_file(profile).fileSize, size); + + // Rename it back, and try loading the entire database synchronously. + do_get_backup_file(profile).moveTo(null, "cookies.sqlite"); + do_load_profile(); + + // At this point, the database connection should be open. Ensure that it + // succeeded. + do_check_false(do_get_backup_file(profile).exists()); + + // Synchronously read in everything. + do_check_eq(do_count_cookies(), 0); + + // Wait for the backup file to be created and the database rebuilt. + do_check_false(do_get_backup_file(profile).exists()); + new _observer(sub_generator, "cookie-db-rebuilding"); + yield; + do_execute_soon(function() { do_run_generator(sub_generator); }); + yield; + + // Close the profile. + do_close_profile(sub_generator); + yield; + db = Services.storage.openDatabase(do_get_cookie_file(profile)); + do_check_eq(do_count_cookies_in_db(db), 0); + db.close(); + + // Check that the original database was renamed. + do_check_true(do_get_backup_file(profile).exists()); + do_check_eq(do_get_backup_file(profile).fileSize, size); + + // Clean up. + do_get_cookie_file(profile).remove(false); + do_get_backup_file(profile).remove(false); + do_check_false(do_get_cookie_file(profile).exists()); + do_check_false(do_get_backup_file(profile).exists()); + do_run_generator(generator); +} + +function run_test_4(generator) +{ + // Load the profile and populate it. + do_load_profile(); + for (let i = 0; i < 3000; ++i) { + let uri = NetUtil.newURI("http://" + i + ".com/"); + Services.cookies.setCookieString(uri, null, "oh=hai; max-age=1000", null); + } + + // Close the profile. + do_close_profile(sub_generator); + yield; + + // Corrupt the database file. + let size = do_corrupt_db(do_get_cookie_file(profile)); + + // Load the profile. + do_load_profile(); + + // At this point, the database connection should be open. Ensure that it + // succeeded. + do_check_false(do_get_backup_file(profile).exists()); + + // Synchronously read in the first cookie. This will cause it to go into the + // cookie table, whereupon it will be written out during database rebuild. + do_check_eq(Services.cookiemgr.countCookiesFromHost("0.com"), 1); + + // Queue up an INSERT for the same base domain. This should also go into + // memory and be written out during database rebuild. + let uri = NetUtil.newURI("http://0.com/"); + Services.cookies.setCookieString(uri, null, "oh2=hai; max-age=1000", null); + + // Wait for the asynchronous read to choke and the insert to fail shortly + // thereafter, at which point the backup file will be created and the database + // rebuilt. + new _observer(sub_generator, "cookie-db-rebuilding"); + yield; + do_execute_soon(function() { do_run_generator(sub_generator); }); + yield; + + // Close the profile. + do_close_profile(sub_generator); + yield; + + // Check that the original database was renamed. + do_check_true(do_get_backup_file(profile).exists()); + do_check_eq(do_get_backup_file(profile).fileSize, size); + let db = Services.storage.openDatabase(do_get_cookie_file(profile)); + do_check_eq(do_count_cookies_in_db(db, "0.com"), 2); + db.close(); + + // Load the profile, and check that it contains the new cookie. + do_load_profile(); + do_check_eq(Services.cookiemgr.countCookiesFromHost("0.com"), 2); + do_check_eq(do_count_cookies(), 2); + + // Close the profile. + do_close_profile(sub_generator); + yield; + + // Clean up. + do_get_cookie_file(profile).remove(false); + do_get_backup_file(profile).remove(false); + do_check_false(do_get_cookie_file(profile).exists()); + do_check_false(do_get_backup_file(profile).exists()); + do_run_generator(generator); +} + +function run_test_4(generator) +{ + // Load the profile and populate it. + do_load_profile(); + for (let i = 0; i < 3000; ++i) { + let uri = NetUtil.newURI("http://" + i + ".com/"); + Services.cookies.setCookieString(uri, null, "oh=hai; max-age=1000", null); + } + + // Close the profile. + do_close_profile(sub_generator); + yield; + + // Corrupt the database file. + let size = do_corrupt_db(do_get_cookie_file(profile)); + + // Load the profile. + do_load_profile(); + + // At this point, the database connection should be open. Ensure that it + // succeeded. + do_check_false(do_get_backup_file(profile).exists()); + + // Synchronously read in the first cookie. This will cause it to go into the + // cookie table, whereupon it will be written out during database rebuild. + do_check_eq(Services.cookiemgr.countCookiesFromHost("0.com"), 1); + + // Queue up an INSERT for the same base domain. This should also go into + // memory and be written out during database rebuild. + let uri = NetUtil.newURI("http://0.com/"); + Services.cookies.setCookieString(uri, null, "oh2=hai; max-age=1000", null); + + // Wait for the asynchronous read to choke and the insert to fail shortly + // thereafter, at which point the backup file will be created and the database + // rebuilt. + new _observer(sub_generator, "cookie-db-rebuilding"); + yield; + do_execute_soon(function() { do_run_generator(sub_generator); }); + yield; + + // At this point, the cookies should still be in memory. + do_check_eq(Services.cookiemgr.countCookiesFromHost("0.com"), 2); + do_check_eq(do_count_cookies(), 2); + + // Close the profile. + do_close_profile(sub_generator); + yield; + + // Check that the original database was renamed. + do_check_true(do_get_backup_file(profile).exists()); + do_check_eq(do_get_backup_file(profile).fileSize, size); + let db = Services.storage.openDatabase(do_get_cookie_file(profile)); + do_check_eq(do_count_cookies_in_db(db, "0.com"), 2); + db.close(); + + // Load the profile, and check that it contains the new cookie. + do_load_profile(); + do_check_eq(Services.cookiemgr.countCookiesFromHost("0.com"), 2); + do_check_eq(do_count_cookies(), 2); + + // Close the profile. + do_close_profile(sub_generator); + yield; + + // Clean up. + do_get_cookie_file(profile).remove(false); + do_get_backup_file(profile).remove(false); + do_check_false(do_get_cookie_file(profile).exists()); + do_check_false(do_get_backup_file(profile).exists()); + do_run_generator(generator); +} + +function run_test_5(generator) +{ + // Load the profile and populate it. + do_load_profile(); + let uri = NetUtil.newURI("http://bar.com/"); + Services.cookies.setCookieString(uri, null, "oh=hai; path=/; max-age=1000", + null); + for (let i = 0; i < 3000; ++i) { + let uri = NetUtil.newURI("http://" + i + ".com/"); + Services.cookies.setCookieString(uri, null, "oh=hai; max-age=1000", null); + } + + // Close the profile. + do_close_profile(sub_generator); + yield; + + // Corrupt the database file. + let size = do_corrupt_db(do_get_cookie_file(profile)); + + // Load the profile. + do_load_profile(); + + // At this point, the database connection should be open. Ensure that it + // succeeded. + do_check_false(do_get_backup_file(profile).exists()); + + // Synchronously read in the first two cookies. This will cause them to go + // into the cookie table, whereupon it will be written out during database + // rebuild. + do_check_eq(Services.cookiemgr.countCookiesFromHost("bar.com"), 1); + do_check_eq(Services.cookiemgr.countCookiesFromHost("0.com"), 1); + + // Wait for the asynchronous read to choke, at which point the backup file + // will be created and a new connection opened. + new _observer(sub_generator, "cookie-db-rebuilding"); + yield; + + // At this point, the cookies should still be in memory. (Note that these + // calls are re-entrant into the cookie service, but it's OK!) + do_check_eq(Services.cookiemgr.countCookiesFromHost("bar.com"), 1); + do_check_eq(Services.cookiemgr.countCookiesFromHost("0.com"), 1); + do_check_eq(do_count_cookies(), 2); + do_check_true(do_get_backup_file(profile).exists()); + do_check_eq(do_get_backup_file(profile).fileSize, size); + do_check_false(do_get_rebuild_backup_file(profile).exists()); + + // Open a database connection, and write a row that will trigger a constraint + // violation. + let db = new CookieDatabaseConnection(do_get_cookie_file(profile), 4); + db.insertCookie(cookie); + do_check_eq(do_count_cookies_in_db(db.db, "bar.com"), 1); + do_check_eq(do_count_cookies_in_db(db.db), 1); + db.close(); + + // Wait for the rebuild to bail and the database to be closed. + new _observer(sub_generator, "cookie-db-closed"); + yield; + + // Check that the original backup and the database itself are gone. + do_check_true(do_get_rebuild_backup_file(profile).exists()); + do_check_true(do_get_backup_file(profile).exists()); + do_check_eq(do_get_backup_file(profile).fileSize, size); + do_check_false(do_get_cookie_file(profile).exists()); + + // Check that the rebuild backup has the original bar.com cookie, and possibly + // a 0.com cookie depending on whether it got written out first or second. + db = new CookieDatabaseConnection(do_get_rebuild_backup_file(profile), 4); + do_check_eq(do_count_cookies_in_db(db.db, "bar.com"), 1); + let count = do_count_cookies_in_db(db.db); + do_check_true(count == 1 || + count == 2 && do_count_cookies_in_db(db.db, "0.com") == 1); + db.close(); + + do_check_eq(Services.cookiemgr.countCookiesFromHost("bar.com"), 1); + do_check_eq(Services.cookiemgr.countCookiesFromHost("0.com"), 1); + do_check_eq(do_count_cookies(), 2); + + // Close the profile. We do not need to wait for completion, because the + // database has already been closed. + do_close_profile(); + + // Clean up. + do_get_backup_file(profile).remove(false); + do_get_rebuild_backup_file(profile).remove(false); + do_check_false(do_get_cookie_file(profile).exists()); + do_check_false(do_get_backup_file(profile).exists()); + do_check_false(do_get_rebuild_backup_file(profile).exists()); + do_run_generator(generator); +} + diff --git a/extensions/cookie/test/unit/test_cookies_persistence.js b/extensions/cookie/test/unit/test_cookies_persistence.js new file mode 100644 index 000000000..d6de81b72 --- /dev/null +++ b/extensions/cookie/test/unit/test_cookies_persistence.js @@ -0,0 +1,78 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +// test for cookie persistence across sessions, for the cases: +// 1) network.cookie.lifetimePolicy = 0 (expire naturally) +// 2) network.cookie.lifetimePolicy = 2 (expire at end of session) + +var test_generator = do_run_test(); + +function run_test() { + do_test_pending(); + test_generator.next(); +} + +function finish_test() { + do_execute_soon(function() { + test_generator.close(); + do_test_finished(); + }); +} + +function do_run_test() { + // Set up a profile. + let profile = do_get_profile(); + + // Create URIs and channels pointing to foo.com and bar.com. + // We will use these to put foo.com into first and third party contexts. + var spec1 = "http://foo.com/foo.html"; + var spec2 = "http://bar.com/bar.html"; + var uri1 = NetUtil.newURI(spec1); + var uri2 = NetUtil.newURI(spec2); + var channel1 = NetUtil.newChannel({uri: uri1, loadUsingSystemPrincipal: true}); + var channel2 = NetUtil.newChannel({uri: uri2, loadUsingSystemPrincipal: true}); + + // Force the channel URI to be used when determining the originating URI of + // the channel. + var httpchannel1 = channel1.QueryInterface(Ci.nsIHttpChannelInternal); + var httpchannel2 = channel1.QueryInterface(Ci.nsIHttpChannelInternal); + httpchannel1.forceAllowThirdPartyCookie = true; + httpchannel2.forceAllowThirdPartyCookie = true; + + // test with cookies enabled, and third party cookies persistent. + Services.prefs.setIntPref("network.cookie.cookieBehavior", 0); + Services.prefs.setBoolPref("network.cookie.thirdparty.sessionOnly", false); + do_set_cookies(uri1, channel1, false, [1, 2, 3, 4]); + do_set_cookies(uri2, channel2, true, [1, 2, 3, 4]); + + // fake a profile change + do_close_profile(test_generator); + yield; + do_load_profile(); + do_check_eq(Services.cookies.countCookiesFromHost(uri1.host), 4); + do_check_eq(Services.cookies.countCookiesFromHost(uri2.host), 0); + + // Again, but don't wait for the async close to complete. This should always + // work, since we blocked on close above and haven't kicked off any writes + // since then. + do_close_profile(); + do_load_profile(); + do_check_eq(Services.cookies.countCookiesFromHost(uri1.host), 4); + do_check_eq(Services.cookies.countCookiesFromHost(uri2.host), 0); + + // test with cookies set to session-only + Services.prefs.setIntPref("network.cookie.lifetimePolicy", 2); + Services.cookies.removeAll(); + do_set_cookies(uri1, channel1, false, [1, 2, 3, 4]); + do_set_cookies(uri2, channel2, true, [1, 2, 3, 4]); + + // fake a profile change + do_close_profile(test_generator); + yield; + do_load_profile(); + do_check_eq(Services.cookies.countCookiesFromHost(uri1.host), 0); + do_check_eq(Services.cookies.countCookiesFromHost(uri2.host), 0); + + finish_test(); +} + diff --git a/extensions/cookie/test/unit/test_cookies_privatebrowsing.js b/extensions/cookie/test/unit/test_cookies_privatebrowsing.js new file mode 100644 index 000000000..ab35f8ef7 --- /dev/null +++ b/extensions/cookie/test/unit/test_cookies_privatebrowsing.js @@ -0,0 +1,115 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Test private browsing mode. + +var test_generator = do_run_test(); + +function run_test() { + do_test_pending(); + do_run_generator(test_generator); +} + +function finish_test() { + do_execute_soon(function() { + test_generator.close(); + do_test_finished(); + }); +} + +function make_channel(url) { + return NetUtil.newChannel({uri: url, loadUsingSystemPrincipal: true}) + .QueryInterface(Ci.nsIHttpChannel); +} + +function do_run_test() { + // Set up a profile. + let profile = do_get_profile(); + + // Test with cookies enabled. + Services.prefs.setIntPref("network.cookie.cookieBehavior", 0); + + // Create URIs pointing to foo.com and bar.com. + let uri1 = NetUtil.newURI("http://foo.com/foo.html"); + let uri2 = NetUtil.newURI("http://bar.com/bar.html"); + + // Set a cookie for host 1. + Services.cookies.setCookieString(uri1, null, "oh=hai; max-age=1000", null); + do_check_eq(Services.cookiemgr.countCookiesFromHost(uri1.host), 1); + + // Enter private browsing mode, set a cookie for host 2, and check the counts. + var chan1 = make_channel(uri1.spec); + chan1.QueryInterface(Ci.nsIPrivateBrowsingChannel); + chan1.setPrivate(true); + + var chan2 = make_channel(uri2.spec); + chan2.QueryInterface(Ci.nsIPrivateBrowsingChannel); + chan2.setPrivate(true); + + Services.cookies.setCookieString(uri2, null, "oh=hai; max-age=1000", chan2); + do_check_eq(Services.cookiemgr.getCookieString(uri1, chan1), null); + do_check_eq(Services.cookiemgr.getCookieString(uri2, chan2), "oh=hai"); + + // Remove cookies and check counts. + Services.obs.notifyObservers(null, "last-pb-context-exited", null); + do_check_eq(Services.cookiemgr.getCookieString(uri1, chan1), null); + do_check_eq(Services.cookiemgr.getCookieString(uri2, chan2), null); + + Services.cookies.setCookieString(uri2, null, "oh=hai; max-age=1000", chan2); + do_check_eq(Services.cookiemgr.getCookieString(uri2, chan2), "oh=hai"); + + // Leave private browsing mode and check counts. + Services.obs.notifyObservers(null, "last-pb-context-exited", null); + do_check_eq(Services.cookiemgr.countCookiesFromHost(uri1.host), 1); + do_check_eq(Services.cookiemgr.countCookiesFromHost(uri2.host), 0); + + // Fake a profile change. + do_close_profile(test_generator); + yield; + do_load_profile(); + + // Check that the right cookie persisted. + do_check_eq(Services.cookiemgr.countCookiesFromHost(uri1.host), 1); + do_check_eq(Services.cookiemgr.countCookiesFromHost(uri2.host), 0); + + // Enter private browsing mode, set a cookie for host 2, and check the counts. + do_check_eq(Services.cookiemgr.getCookieString(uri1, chan1), null); + do_check_eq(Services.cookiemgr.getCookieString(uri2, chan2), null); + Services.cookies.setCookieString(uri2, null, "oh=hai; max-age=1000", chan2); + do_check_eq(Services.cookiemgr.getCookieString(uri2, chan2), "oh=hai"); + + // Fake a profile change. + do_close_profile(test_generator); + yield; + do_load_profile(); + + // We're still in private browsing mode, but should have a new session. + // Check counts. + do_check_eq(Services.cookiemgr.getCookieString(uri1, chan1), null); + do_check_eq(Services.cookiemgr.getCookieString(uri2, chan2), null); + + // Leave private browsing mode and check counts. + Services.obs.notifyObservers(null, "last-pb-context-exited", null); + do_check_eq(Services.cookiemgr.countCookiesFromHost(uri1.host), 1); + do_check_eq(Services.cookiemgr.countCookiesFromHost(uri2.host), 0); + + // Enter private browsing mode. + + // Fake a profile change, but wait for async read completion. + do_close_profile(test_generator); + yield; + do_load_profile(test_generator); + yield; + + // We're still in private browsing mode, but should have a new session. + // Check counts. + do_check_eq(Services.cookiemgr.getCookieString(uri1, chan1), null); + do_check_eq(Services.cookiemgr.getCookieString(uri2, chan2), null); + + // Leave private browsing mode and check counts. + Services.obs.notifyObservers(null, "last-pb-context-exited", null); + do_check_eq(Services.cookiemgr.countCookiesFromHost(uri1.host), 1); + do_check_eq(Services.cookiemgr.countCookiesFromHost(uri2.host), 0); + + finish_test(); +} diff --git a/extensions/cookie/test/unit/test_cookies_profile_close.js b/extensions/cookie/test/unit/test_cookies_profile_close.js new file mode 100644 index 000000000..bae6e3a59 --- /dev/null +++ b/extensions/cookie/test/unit/test_cookies_profile_close.js @@ -0,0 +1,92 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Test that the cookie APIs behave sanely after 'profile-before-change'. + +var test_generator = do_run_test(); + +function run_test() { + do_test_pending(); + test_generator.next(); +} + +function finish_test() { + do_execute_soon(function() { + test_generator.close(); + do_test_finished(); + }); +} + +function do_run_test() { + // Set up a profile. + let profile = do_get_profile(); + + // Allow all cookies. + Services.prefs.setIntPref("network.cookie.cookieBehavior", 0); + + // Start the cookieservice. + Services.cookies; + + // Set a cookie. + let uri = NetUtil.newURI("http://foo.com"); + Services.cookies.setCookieString(uri, null, "oh=hai; max-age=1000", null); + let enumerator = Services.cookiemgr.enumerator; + do_check_true(enumerator.hasMoreElements()); + let cookie = enumerator.getNext(); + do_check_false(enumerator.hasMoreElements()); + + // Fire 'profile-before-change'. + do_close_profile(); + + // Check that the APIs behave appropriately. + do_check_eq(Services.cookies.getCookieString(uri, null), null); + do_check_eq(Services.cookies.getCookieStringFromHttp(uri, null, null), null); + Services.cookies.setCookieString(uri, null, "oh2=hai", null); + Services.cookies.setCookieStringFromHttp(uri, null, null, "oh3=hai", null, null); + do_check_eq(Services.cookies.getCookieString(uri, null), null); + + do_check_throws(function() { + Services.cookiemgr.removeAll(); + }, Cr.NS_ERROR_NOT_AVAILABLE); + + do_check_throws(function() { + Services.cookiemgr.enumerator; + }, Cr.NS_ERROR_NOT_AVAILABLE); + + do_check_throws(function() { + Services.cookiemgr.add("foo.com", "", "oh4", "hai", false, false, false, 0, {}); + }, Cr.NS_ERROR_NOT_AVAILABLE); + + do_check_throws(function() { + Services.cookiemgr.remove("foo.com", "", "oh4", false, {}); + }, Cr.NS_ERROR_NOT_AVAILABLE); + + do_check_throws(function() { + let file = profile.clone(); + file.append("cookies.txt"); + Services.cookiemgr.importCookies(file); + }, Cr.NS_ERROR_NOT_AVAILABLE); + + do_check_throws(function() { + Services.cookiemgr.cookieExists(cookie); + }, Cr.NS_ERROR_NOT_AVAILABLE); + + do_check_throws(function() { + Services.cookies.countCookiesFromHost("foo.com"); + }, Cr.NS_ERROR_NOT_AVAILABLE); + + do_check_throws(function() { + Services.cookies.getCookiesFromHost("foo.com", {}); + }, Cr.NS_ERROR_NOT_AVAILABLE); + + // Wait for the database to finish closing. + new _observer(test_generator, "cookie-db-closed"); + yield; + + // Load the profile and check that the API is available. + do_load_profile(); + do_check_true(Services.cookiemgr.cookieExists(cookie)); + + finish_test(); +} + diff --git a/extensions/cookie/test/unit/test_cookies_read.js b/extensions/cookie/test/unit/test_cookies_read.js new file mode 100644 index 000000000..b389ad8cc --- /dev/null +++ b/extensions/cookie/test/unit/test_cookies_read.js @@ -0,0 +1,122 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +// test cookie database asynchronous read operation. + +var test_generator = do_run_test(); + +var CMAX = 1000; // # of cookies to create + +function run_test() { + do_test_pending(); + test_generator.next(); +} + +function finish_test() { + do_execute_soon(function() { + test_generator.close(); + do_test_finished(); + }); +} + +function do_run_test() { + // Set up a profile. + let profile = do_get_profile(); + + // Allow all cookies. + Services.prefs.setIntPref("network.cookie.cookieBehavior", 0); + + // Start the cookieservice, to force creation of a database. + Services.cookies; + + // Open a database connection now, after synchronous initialization has + // completed. We may not be able to open one later once asynchronous writing + // begins. + do_check_true(do_get_cookie_file(profile).exists()); + let db = new CookieDatabaseConnection(do_get_cookie_file(profile), 4); + + for (let i = 0; i < CMAX; ++i) { + let uri = NetUtil.newURI("http://" + i + ".com/"); + Services.cookies.setCookieString(uri, null, "oh=hai; max-age=1000", null); + } + + do_check_eq(do_count_cookies(), CMAX); + + // Wait until all CMAX cookies have been written out to the database. + while (do_count_cookies_in_db(db.db) < CMAX) { + do_execute_soon(function() { + do_run_generator(test_generator); + }); + yield; + } + + // Check the WAL file size. We set it to 16 pages of 32k, which means it + // should be around 500k. + let file = db.db.databaseFile; + do_check_true(file.exists()); + do_check_true(file.fileSize < 1e6); + db.close(); + + // fake a profile change + do_close_profile(test_generator); + yield; + do_load_profile(); + + // test a few random cookies + do_check_eq(Services.cookiemgr.countCookiesFromHost("999.com"), 1); + do_check_eq(Services.cookiemgr.countCookiesFromHost("abc.com"), 0); + do_check_eq(Services.cookiemgr.countCookiesFromHost("100.com"), 1); + do_check_eq(Services.cookiemgr.countCookiesFromHost("400.com"), 1); + do_check_eq(Services.cookiemgr.countCookiesFromHost("xyz.com"), 0); + + // force synchronous load of everything + do_check_eq(do_count_cookies(), CMAX); + + // check that everything's precisely correct + for (let i = 0; i < CMAX; ++i) { + let host = i.toString() + ".com"; + do_check_eq(Services.cookiemgr.countCookiesFromHost(host), 1); + } + + // reload again, to make sure the additions were written correctly + do_close_profile(test_generator); + yield; + do_load_profile(); + + // remove some of the cookies, in both reverse and forward order + for (let i = 100; i-- > 0; ) { + let host = i.toString() + ".com"; + Services.cookiemgr.remove(host, "oh", "/", false, {}); + } + for (let i = CMAX - 100; i < CMAX; ++i) { + let host = i.toString() + ".com"; + Services.cookiemgr.remove(host, "oh", "/", false, {}); + } + + // check the count + do_check_eq(do_count_cookies(), CMAX - 200); + + // reload again, to make sure the removals were written correctly + do_close_profile(test_generator); + yield; + do_load_profile(); + + // check the count + do_check_eq(do_count_cookies(), CMAX - 200); + + // reload again, but wait for async read completion + do_close_profile(test_generator); + yield; + do_load_profile(test_generator); + yield; + + // check that everything's precisely correct + do_check_eq(do_count_cookies(), CMAX - 200); + for (let i = 100; i < CMAX - 100; ++i) { + let host = i.toString() + ".com"; + do_check_eq(Services.cookiemgr.countCookiesFromHost(host), 1); + } + + finish_test(); +} + diff --git a/extensions/cookie/test/unit/test_cookies_sync_failure.js b/extensions/cookie/test/unit/test_cookies_sync_failure.js new file mode 100644 index 000000000..2de1de628 --- /dev/null +++ b/extensions/cookie/test/unit/test_cookies_sync_failure.js @@ -0,0 +1,286 @@ +/* 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. + +var COOKIE_DATABASE_SCHEMA_CURRENT = 7; + +var test_generator = do_run_test(); + +function run_test() { + do_test_pending(); + do_run_generator(test_generator); +} + +function finish_test() { + do_execute_soon(function() { + test_generator.close(); + do_test_finished(); + }); +} + +function do_run_test() { + // Set up a profile. + this.profile = do_get_profile(); + + // Allow all cookies. + Services.prefs.setIntPref("network.cookie.cookieBehavior", 0); + + // Get the cookie file and the backup file. + this.cookieFile = profile.clone(); + cookieFile.append("cookies.sqlite"); + this.backupFile = profile.clone(); + backupFile.append("cookies.sqlite.bak"); + do_check_false(cookieFile.exists()); + do_check_false(backupFile.exists()); + + // Create a cookie object for testing. + this.now = Date.now() * 1000; + this.futureExpiry = Math.round(this.now / 1e6 + 1000); + this.cookie = new Cookie("oh", "hai", "bar.com", "/", this.futureExpiry, + this.now, this.now, false, false, false); + + this.sub_generator = run_test_1(test_generator); + sub_generator.next(); + yield; + + this.sub_generator = run_test_2(test_generator); + sub_generator.next(); + yield; + + this.sub_generator = run_test_3(test_generator, 99); + sub_generator.next(); + yield; + + this.sub_generator = run_test_3(test_generator, COOKIE_DATABASE_SCHEMA_CURRENT); + sub_generator.next(); + yield; + + this.sub_generator = run_test_3(test_generator, 4); + sub_generator.next(); + yield; + + this.sub_generator = run_test_3(test_generator, 3); + sub_generator.next(); + yield; + + this.sub_generator = run_test_4_exists(test_generator, 1, + "ALTER TABLE moz_cookies ADD lastAccessed INTEGER"); + sub_generator.next(); + yield; + + this.sub_generator = run_test_4_exists(test_generator, 2, + "ALTER TABLE moz_cookies ADD baseDomain TEXT"); + sub_generator.next(); + yield; + + this.sub_generator = run_test_4_baseDomain(test_generator); + sub_generator.next(); + yield; + + this.sub_generator = run_test_4_exists(test_generator, 3, + "ALTER TABLE moz_cookies ADD creationTime INTEGER"); + sub_generator.next(); + yield; + + this.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(); + return; +} + +const garbage = "hello thar!"; + +function create_garbage_file(file) +{ + // Create an empty database file. + file.create(Ci.nsIFile.NORMAL_FILE_TYPE, -1); + do_check_true(file.exists()); + do_check_eq(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. + do_check_eq(file.fileSize, garbage.length); +} + +function check_garbage_file(file) +{ + do_check_true(file.exists()); + do_check_eq(file.fileSize, garbage.length); + file.remove(false); + do_check_false(file.exists()); +} + +function run_test_1(generator) +{ + // Create a garbage database file. + create_garbage_file(cookieFile); + + // Load the profile and populate it. + let uri = NetUtil.newURI("http://foo.com/"); + Services.cookies.setCookieString(uri, null, "oh=hai; max-age=1000", null); + + // 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. + do_check_eq(do_count_cookies(), 1); + check_garbage_file(backupFile); + + // Close the profile. + do_close_profile(sub_generator); + yield; + + // Clean up. + cookieFile.remove(false); + do_check_false(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/"); + Services.cookies.setCookieString(uri, null, "oh=hai; max-age=1000", null); + + // 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(); + do_check_eq(do_count_cookies(), 0); + do_check_false(backupFile.exists()); + + // Close the profile. + do_close_profile(sub_generator); + yield; + + // Clean up. + cookieFile.remove(false); + do_check_false(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(); + do_check_eq(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); + do_check_eq(db.schemaVersion, COOKIE_DATABASE_SCHEMA_CURRENT); + db.close(); + + // Clean up. + cookieFile.remove(false); + do_check_false(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(); + do_check_eq(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); + do_check_eq(db.schemaVersion, COOKIE_DATABASE_SCHEMA_CURRENT); + db.close(); + do_check_true(backupFile.exists()); + + // Clean up. + cookieFile.remove(false); + backupFile.remove(false); + do_check_false(cookieFile.exists()); + do_check_false(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", ".", "/", this.futureExpiry, this.now, + this.now, false, false, false); + db.insertCookie(badCookie); + db.close(); + + // Load the profile and check that migration fails. + do_load_profile(); + do_check_eq(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); + do_check_eq(db.schemaVersion, COOKIE_DATABASE_SCHEMA_CURRENT); + db.close(); + do_check_true(backupFile.exists()); + + // Clean up. + cookieFile.remove(false); + backupFile.remove(false); + do_check_false(cookieFile.exists()); + do_check_false(backupFile.exists()); + do_run_generator(generator); +} diff --git a/extensions/cookie/test/unit/test_cookies_thirdparty.js b/extensions/cookie/test/unit/test_cookies_thirdparty.js new file mode 100644 index 000000000..a39f807f2 --- /dev/null +++ b/extensions/cookie/test/unit/test_cookies_thirdparty.js @@ -0,0 +1,147 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +// test third party cookie blocking, for the cases: +// 1) with null channel +// 2) with channel, but with no docshell parent + +function run_test() { + // Create URIs and channels pointing to foo.com and bar.com. + // We will use these to put foo.com into first and third party contexts. + var spec1 = "http://foo.com/foo.html"; + var spec2 = "http://bar.com/bar.html"; + var uri1 = NetUtil.newURI(spec1); + var uri2 = NetUtil.newURI(spec2); + var channel1 = NetUtil.newChannel({uri: uri1, loadUsingSystemPrincipal: true}); + var channel2 = NetUtil.newChannel({uri: uri2, loadUsingSystemPrincipal: true}); + + // test with cookies enabled + Services.prefs.setIntPref("network.cookie.cookieBehavior", 0); + do_set_cookies(uri1, channel1, true, [1, 2, 3, 4]); + Services.cookies.removeAll(); + do_set_cookies(uri1, channel2, true, [1, 2, 3, 4]); + Services.cookies.removeAll(); + + // test with third party cookies blocked + Services.prefs.setIntPref("network.cookie.cookieBehavior", 1); + do_set_cookies(uri1, channel1, true, [0, 0, 0, 0]); + Services.cookies.removeAll(); + do_set_cookies(uri1, channel2, true, [0, 0, 0, 0]); + Services.cookies.removeAll(); + + // Force the channel URI to be used when determining the originating URI of + // the channel. + var httpchannel1 = channel1.QueryInterface(Ci.nsIHttpChannelInternal); + var httpchannel2 = channel2.QueryInterface(Ci.nsIHttpChannelInternal); + httpchannel1.forceAllowThirdPartyCookie = true; + httpchannel2.forceAllowThirdPartyCookie = true; + + // test with cookies enabled + Services.prefs.setIntPref("network.cookie.cookieBehavior", 0); + do_set_cookies(uri1, channel1, true, [1, 2, 3, 4]); + Services.cookies.removeAll(); + do_set_cookies(uri1, channel2, true, [1, 2, 3, 4]); + Services.cookies.removeAll(); + + // test with third party cookies blocked + Services.prefs.setIntPref("network.cookie.cookieBehavior", 1); + do_set_cookies(uri1, channel1, true, [0, 1, 1, 2]); + Services.cookies.removeAll(); + do_set_cookies(uri1, channel2, true, [0, 0, 0, 0]); + Services.cookies.removeAll(); + + // test with third party cookies limited + Services.prefs.setIntPref("network.cookie.cookieBehavior", 3); + do_set_cookies(uri1, channel1, true, [0, 1, 2, 3]); + Services.cookies.removeAll(); + do_set_cookies(uri1, channel2, true, [0, 0, 0, 0]); + Services.cookies.removeAll(); + do_set_single_http_cookie(uri1, channel1, 1); + do_set_cookies(uri1, channel2, true, [2, 3, 4, 5]); + Services.cookies.removeAll(); + + // Test per-site 3rd party cookie blocking with cookies enabled + Services.prefs.setIntPref("network.cookie.cookieBehavior", 0); + var kPermissionType = "cookie"; + var ALLOW_FIRST_PARTY_ONLY = 9; + // ALLOW_FIRST_PARTY_ONLY overrides + Services.perms.add(uri1, kPermissionType, ALLOW_FIRST_PARTY_ONLY); + do_set_cookies(uri1, channel1, true, [0, 1, 1, 2]); + Services.cookies.removeAll(); + do_set_cookies(uri1, channel2, true, [0, 0, 0, 0]); + Services.cookies.removeAll(); + + // Test per-site 3rd party cookie blocking with 3rd party cookies disabled + Services.prefs.setIntPref("network.cookie.cookieBehavior", 1); + do_set_cookies(uri1, channel1, true, [0, 1, 1, 2]); + Services.cookies.removeAll(); + // No preference has been set for uri2, but it should act as if + // ALLOW_FIRST_PARTY_ONLY has been set + do_set_cookies(uri2, channel2, true, [0, 1, 1, 2]); + Services.cookies.removeAll(); + do_set_cookies(uri1, channel2, true, [0, 0, 0, 0]); + Services.cookies.removeAll(); + + // Test per-site 3rd party cookie blocking with 3rd party cookies limited + Services.prefs.setIntPref("network.cookie.cookieBehavior", 3); + do_set_cookies(uri1, channel1, true, [0, 1, 1, 2]); + Services.cookies.removeAll(); + // No preference has been set for uri2, but it should act as if + // LIMIT_THIRD_PARTY has been set + do_set_cookies(uri2, channel2, true, [0, 1, 2, 3]); + Services.cookies.removeAll(); + do_set_single_http_cookie(uri2, channel2, 1); + do_set_cookies(uri2, channel2, true, [2, 3, 4, 5]); + Services.cookies.removeAll(); + do_set_cookies(uri1, channel2, true, [0, 0, 0, 0]); + Services.cookies.removeAll(); + do_set_single_http_cookie(uri1, channel1, 1); + do_set_cookies(uri1, channel2, true, [1, 1, 1, 1]); + Services.cookies.removeAll(); + + // Test per-site 3rd party cookie limiting with cookies enabled + Services.prefs.setIntPref("network.cookie.cookieBehavior", 0); + var kPermissionType = "cookie"; + var LIMIT_THIRD_PARTY = 10; + // LIMIT_THIRD_PARTY overrides + Services.perms.add(uri1, kPermissionType, LIMIT_THIRD_PARTY); + do_set_cookies(uri1, channel1, true, [0, 1, 2, 3]); + Services.cookies.removeAll(); + do_set_cookies(uri1, channel2, true, [0, 0, 0, 0]); + Services.cookies.removeAll(); + do_set_single_http_cookie(uri1, channel1, 1); + do_set_cookies(uri1, channel2, true, [2, 3, 4, 5]); + Services.cookies.removeAll(); + + // Test per-site 3rd party cookie limiting with 3rd party cookies disabled + Services.prefs.setIntPref("network.cookie.cookieBehavior", 1); + do_set_cookies(uri1, channel1, true, [0, 1, 2, 3]); + Services.cookies.removeAll(); + // No preference has been set for uri2, but it should act as if + // ALLOW_FIRST_PARTY_ONLY has been set + do_set_cookies(uri2, channel2, true, [0, 1, 1, 2]); + Services.cookies.removeAll(); + do_set_cookies(uri1, channel2, true, [0, 0, 0, 0]); + Services.cookies.removeAll(); + do_set_single_http_cookie(uri1, channel1, 1); + do_set_cookies(uri1, channel2, true, [2, 3, 4, 5]); + Services.cookies.removeAll(); + + // Test per-site 3rd party cookie limiting with 3rd party cookies limited + Services.prefs.setIntPref("network.cookie.cookieBehavior", 3); + do_set_cookies(uri1, channel1, true, [0, 1, 2, 3]); + Services.cookies.removeAll(); + // No preference has been set for uri2, but it should act as if + // LIMIT_THIRD_PARTY has been set + do_set_cookies(uri2, channel2, true, [0, 1, 2, 3]); + Services.cookies.removeAll(); + do_set_single_http_cookie(uri2, channel2, 1); + do_set_cookies(uri2, channel2, true, [2, 3, 4, 5]); + Services.cookies.removeAll(); + do_set_cookies(uri1, channel2, true, [0, 0, 0, 0]); + Services.cookies.removeAll(); + do_set_single_http_cookie(uri1, channel1, 1); + do_set_cookies(uri1, channel2, true, [2, 3, 4, 5]); + Services.cookies.removeAll(); +} + diff --git a/extensions/cookie/test/unit/test_cookies_thirdparty_session.js b/extensions/cookie/test/unit/test_cookies_thirdparty_session.js new file mode 100644 index 000000000..36e5fb11a --- /dev/null +++ b/extensions/cookie/test/unit/test_cookies_thirdparty_session.js @@ -0,0 +1,69 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +// test third party persistence across sessions, for the cases: +// 1) network.cookie.thirdparty.sessionOnly = false +// 2) network.cookie.thirdparty.sessionOnly = true + +var test_generator = do_run_test(); + +function run_test() { + do_test_pending(); + test_generator.next(); +} + +function finish_test() { + do_execute_soon(function() { + test_generator.close(); + do_test_finished(); + }); +} + +function do_run_test() { + // Set up a profile. + let profile = do_get_profile(); + + // Create URIs and channels pointing to foo.com and bar.com. + // We will use these to put foo.com into first and third party contexts. + var spec1 = "http://foo.com/foo.html"; + var spec2 = "http://bar.com/bar.html"; + var uri1 = NetUtil.newURI(spec1); + var uri2 = NetUtil.newURI(spec2); + var channel1 = NetUtil.newChannel({uri: uri1, loadUsingSystemPrincipal: true}); + var channel2 = NetUtil.newChannel({uri: uri2, loadUsingSystemPrincipal: true}); + + // Force the channel URI to be used when determining the originating URI of + // the channel. + var httpchannel1 = channel1.QueryInterface(Ci.nsIHttpChannelInternal); + var httpchannel2 = channel2.QueryInterface(Ci.nsIHttpChannelInternal); + httpchannel1.forceAllowThirdPartyCookie = true; + httpchannel2.forceAllowThirdPartyCookie = true; + + // test with cookies enabled, and third party cookies persistent. + Services.prefs.setIntPref("network.cookie.cookieBehavior", 0); + Services.prefs.setBoolPref("network.cookie.thirdparty.sessionOnly", false); + do_set_cookies(uri1, channel2, false, [1, 2, 3, 4]); + do_set_cookies(uri2, channel1, true, [1, 2, 3, 4]); + + // fake a profile change + do_close_profile(test_generator); + yield; + do_load_profile(); + do_check_eq(Services.cookies.countCookiesFromHost(uri1.host), 4); + do_check_eq(Services.cookies.countCookiesFromHost(uri2.host), 0); + + // test with third party cookies for session only. + Services.prefs.setBoolPref("network.cookie.thirdparty.sessionOnly", true); + Services.cookies.removeAll(); + do_set_cookies(uri1, channel2, false, [1, 2, 3, 4]); + do_set_cookies(uri2, channel1, true, [1, 2, 3, 4]); + + // fake a profile change + do_close_profile(test_generator); + yield; + do_load_profile(); + do_check_eq(Services.cookies.countCookiesFromHost(uri1.host), 0); + do_check_eq(Services.cookies.countCookiesFromHost(uri2.host), 0); + + finish_test(); +} diff --git a/extensions/cookie/test/unit/test_domain_eviction.js b/extensions/cookie/test/unit/test_domain_eviction.js new file mode 100644 index 000000000..349f6d77e --- /dev/null +++ b/extensions/cookie/test/unit/test_domain_eviction.js @@ -0,0 +1,153 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Test that domain eviction occurs when the cookies per base domain limit is +// reached, and that expired cookies are evicted before live cookies. + +var test_generator = do_run_test(); + +function run_test() +{ + do_test_pending(); + do_run_generator(test_generator); +} + +function continue_test() +{ + do_run_generator(test_generator); +} + +function do_run_test() +{ + // Set the base domain limit to 50 so we have a known value. + Services.prefs.setIntPref("network.cookie.maxPerHost", 50); + + let futureExpiry = Math.floor(Date.now() / 1000 + 1000); + + // test eviction under the 50 cookies per base domain limit. this means + // that cookies for foo.com and bar.foo.com should count toward this limit, + // while cookies for baz.com should not. there are several tests we perform + // to make sure the base domain logic is working correctly. + + // 1) simplest case: set 100 cookies for "foo.bar" and make sure 50 survive. + setCookies("foo.bar", 100, futureExpiry); + do_check_eq(countCookies("foo.bar", "foo.bar"), 50); + + // 2) set cookies for different subdomains of "foo.baz", and an unrelated + // domain, and make sure all 50 within the "foo.baz" base domain are counted. + setCookies("foo.baz", 10, futureExpiry); + setCookies(".foo.baz", 10, futureExpiry); + setCookies("bar.foo.baz", 10, futureExpiry); + setCookies("baz.bar.foo.baz", 10, futureExpiry); + setCookies("unrelated.domain", 50, futureExpiry); + do_check_eq(countCookies("foo.baz", "baz.bar.foo.baz"), 40); + setCookies("foo.baz", 20, futureExpiry); + do_check_eq(countCookies("foo.baz", "baz.bar.foo.baz"), 50); + + // 3) ensure cookies are evicted by order of lastAccessed time, if the + // limit on cookies per base domain is reached. + setCookies("horse.radish", 10, futureExpiry); + + // Wait a while, to make sure the first batch of cookies is older than + // the second (timer resolution varies on different platforms). + do_timeout(100, continue_test); + yield; + + setCookies("tasty.horse.radish", 50, futureExpiry); + do_check_eq(countCookies("horse.radish", "horse.radish"), 50); + + let enumerator = Services.cookiemgr.enumerator; + while (enumerator.hasMoreElements()) { + let cookie = enumerator.getNext().QueryInterface(Ci.nsICookie2); + + if (cookie.host == "horse.radish") + do_throw("cookies not evicted by lastAccessed order"); + } + + // Test that expired cookies for a domain are evicted before live ones. + let shortExpiry = Math.floor(Date.now() / 1000 + 2); + setCookies("captchart.com", 49, futureExpiry); + Services.cookiemgr.add("captchart.com", "", "test100", "eviction", + false, false, false, shortExpiry, {}); + do_timeout(2100, continue_test); + yield; + + do_check_eq(countCookies("captchart.com", "captchart.com"), 50); + Services.cookiemgr.add("captchart.com", "", "test200", "eviction", + false, false, false, futureExpiry, {}); + do_check_eq(countCookies("captchart.com", "captchart.com"), 50); + + enumerator = Services.cookiemgr.getCookiesFromHost("captchart.com", {}); + while (enumerator.hasMoreElements()) { + let cookie = enumerator.getNext().QueryInterface(Ci.nsICookie2); + do_check_true(cookie.expiry == futureExpiry); + } + + do_finish_generator_test(test_generator); +} + +// set 'aNumber' cookies with host 'aHost', with distinct names. +function +setCookies(aHost, aNumber, aExpiry) +{ + for (let i = 0; i < aNumber; ++i) + Services.cookiemgr.add(aHost, "", "test" + i, "eviction", + false, false, false, aExpiry, {}); +} + +// count how many cookies are within domain 'aBaseDomain', using three +// independent interface methods on nsICookieManager2: +// 1) 'enumerator', an enumerator of all cookies; +// 2) 'countCookiesFromHost', which returns the number of cookies within the +// base domain of 'aHost', +// 3) 'getCookiesFromHost', which returns an enumerator of 2). +function +countCookies(aBaseDomain, aHost) +{ + let enumerator = Services.cookiemgr.enumerator; + + // count how many cookies are within domain 'aBaseDomain' using the cookie + // enumerator. + let cookies = []; + while (enumerator.hasMoreElements()) { + let cookie = enumerator.getNext().QueryInterface(Ci.nsICookie2); + + if (cookie.host.length >= aBaseDomain.length && + cookie.host.slice(cookie.host.length - aBaseDomain.length) == aBaseDomain) + cookies.push(cookie); + } + + // confirm the count using countCookiesFromHost and getCookiesFromHost. + let result = cookies.length; + do_check_eq(Services.cookiemgr.countCookiesFromHost(aBaseDomain), + cookies.length); + do_check_eq(Services.cookiemgr.countCookiesFromHost(aHost), cookies.length); + + enumerator = Services.cookiemgr.getCookiesFromHost(aHost, {}); + while (enumerator.hasMoreElements()) { + let cookie = enumerator.getNext().QueryInterface(Ci.nsICookie2); + + if (cookie.host.length >= aBaseDomain.length && + cookie.host.slice(cookie.host.length - aBaseDomain.length) == aBaseDomain) { + let found = false; + for (let i = 0; i < cookies.length; ++i) { + if (cookies[i].host == cookie.host && cookies[i].name == cookie.name) { + found = true; + cookies.splice(i, 1); + break; + } + } + + if (!found) + do_throw("cookie " + cookie.name + " not found in master enumerator"); + + } else { + do_throw("cookie host " + cookie.host + " not within domain " + aBaseDomain); + } + } + + do_check_eq(cookies.length, 0); + + return result; +} + diff --git a/extensions/cookie/test/unit/test_eviction.js b/extensions/cookie/test/unit/test_eviction.js new file mode 100644 index 000000000..7b1550208 --- /dev/null +++ b/extensions/cookie/test/unit/test_eviction.js @@ -0,0 +1,249 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +var test_generator = do_run_test(); + +function run_test() +{ + do_test_pending(); + do_run_generator(test_generator); +} + +function continue_test() +{ + do_run_generator(test_generator); +} + +function repeat_test() +{ + // The test is probably going to fail because setting a batch of cookies took + // a significant fraction of 'gPurgeAge'. Compensate by rerunning the + // test with a larger purge age. + do_check_true(gPurgeAge < 64); + gPurgeAge *= 2; + gShortExpiry *= 2; + + do_execute_soon(function() { + test_generator.close(); + test_generator = do_run_test(); + do_run_generator(test_generator); + }); +} + +// Purge threshold, in seconds. +var gPurgeAge = 1; + +// Short expiry age, in seconds. +var gShortExpiry = 2; + +// Required delay to ensure a purge occurs, in milliseconds. This must be at +// least gPurgeAge + 10%, and includes a little fuzz to account for timer +// resolution and possible differences between PR_Now() and Date.now(). +function get_purge_delay() +{ + return gPurgeAge * 1100 + 100; +} + +// Required delay to ensure a cookie set with an expiry time 'gShortExpiry' into +// the future will have expired. +function get_expiry_delay() +{ + return gShortExpiry * 1000 + 100; +} + +function do_run_test() +{ + // Set up a profile. + let profile = do_get_profile(); + + // twiddle prefs to convenient values for this test + Services.prefs.setIntPref("network.cookie.purgeAge", gPurgeAge); + Services.prefs.setIntPref("network.cookie.maxNumber", 100); + + let expiry = Date.now() / 1000 + 1000; + + // eviction is performed based on two limits: when the total number of cookies + // exceeds maxNumber + 10% (110), and when cookies are older than purgeAge + // (1 second). purging is done when both conditions are satisfied, and only + // those cookies are purged. + + // we test the following cases of eviction: + // 1) excess and age are satisfied, but only some of the excess are old enough + // to be purged. + Services.cookiemgr.removeAll(); + if (!set_cookies(0, 5, expiry)) { + repeat_test(); + return; + } + // Sleep a while, to make sure the first batch of cookies is older than + // the second (timer resolution varies on different platforms). + do_timeout(get_purge_delay(), continue_test); + yield; + if (!set_cookies(5, 111, expiry)) { + repeat_test(); + return; + } + + // Fake a profile change, to ensure eviction affects the database correctly. + do_close_profile(test_generator); + yield; + do_load_profile(); + do_check_true(check_remaining_cookies(111, 5, 106)); + + // 2) excess and age are satisfied, and all of the excess are old enough + // to be purged. + Services.cookiemgr.removeAll(); + if (!set_cookies(0, 10, expiry)) { + repeat_test(); + return; + } + do_timeout(get_purge_delay(), continue_test); + yield; + if (!set_cookies(10, 111, expiry)) { + repeat_test(); + return; + } + + do_close_profile(test_generator); + yield; + do_load_profile(); + do_check_true(check_remaining_cookies(111, 10, 101)); + + // 3) excess and age are satisfied, and more than the excess are old enough + // to be purged. + Services.cookiemgr.removeAll(); + if (!set_cookies(0, 50, expiry)) { + repeat_test(); + return; + } + do_timeout(get_purge_delay(), continue_test); + yield; + if (!set_cookies(50, 111, expiry)) { + repeat_test(); + return; + } + + do_close_profile(test_generator); + yield; + do_load_profile(); + do_check_true(check_remaining_cookies(111, 50, 101)); + + // 4) excess but not age are satisfied. + Services.cookiemgr.removeAll(); + if (!set_cookies(0, 120, expiry)) { + repeat_test(); + return; + } + + do_close_profile(test_generator); + yield; + do_load_profile(); + do_check_true(check_remaining_cookies(120, 0, 120)); + + // 5) age but not excess are satisfied. + Services.cookiemgr.removeAll(); + if (!set_cookies(0, 20, expiry)) { + repeat_test(); + return; + } + do_timeout(get_purge_delay(), continue_test); + yield; + if (!set_cookies(20, 110, expiry)) { + repeat_test(); + return; + } + + do_close_profile(test_generator); + yield; + do_load_profile(); + do_check_true(check_remaining_cookies(110, 20, 110)); + + // 6) Excess and age are satisfied, but the cookie limit can be satisfied by + // purging expired cookies. + Services.cookiemgr.removeAll(); + let shortExpiry = Math.floor(Date.now() / 1000) + gShortExpiry; + if (!set_cookies(0, 20, shortExpiry)) { + repeat_test(); + return; + } + do_timeout(get_expiry_delay(), continue_test); + yield; + if (!set_cookies(20, 110, expiry)) { + repeat_test(); + return; + } + do_timeout(get_purge_delay(), continue_test); + yield; + if (!set_cookies(110, 111, expiry)) { + repeat_test(); + return; + } + + do_close_profile(test_generator); + yield; + do_load_profile(); + do_check_true(check_remaining_cookies(111, 20, 91)); + + do_finish_generator_test(test_generator); +} + +// Set 'end - begin' total cookies, with consecutively increasing hosts numbered +// 'begin' to 'end'. +function set_cookies(begin, end, expiry) +{ + do_check_true(begin != end); + + let beginTime; + for (let i = begin; i < end; ++i) { + let host = "eviction." + i + ".tests"; + Services.cookiemgr.add(host, "", "test", "eviction", false, false, false, + expiry, {}); + + if (i == begin) + beginTime = get_creationTime(i); + } + + let endTime = get_creationTime(end - 1); + do_check_true(begin == end - 1 || endTime > beginTime); + if (endTime - beginTime > gPurgeAge * 1000000) { + // Setting cookies took an amount of time very close to the purge threshold. + // Retry the test with a larger threshold. + return false; + } + + return true; +} + +function get_creationTime(i) +{ + let host = "eviction." + i + ".tests"; + let enumerator = Services.cookiemgr.getCookiesFromHost(host, {}); + do_check_true(enumerator.hasMoreElements()); + let cookie = enumerator.getNext().QueryInterface(Ci.nsICookie2); + return cookie.creationTime; +} + +// Test that 'aNumberToExpect' cookies remain after purging is complete, and +// that the cookies that remain consist of the set expected given the number of +// of older and newer cookies -- eviction should occur by order of lastAccessed +// time, if both the limit on total cookies (maxNumber + 10%) and the purge age +// + 10% are exceeded. +function check_remaining_cookies(aNumberTotal, aNumberOld, aNumberToExpect) { + var enumerator = Services.cookiemgr.enumerator; + + let i = 0; + while (enumerator.hasMoreElements()) { + var cookie = enumerator.getNext().QueryInterface(Ci.nsICookie2); + ++i; + + if (aNumberTotal != aNumberToExpect) { + // make sure the cookie is one of the batch we expect was purged. + var hostNumber = new Number(cookie.rawHost.split(".")[1]); + if (hostNumber < (aNumberOld - aNumberToExpect)) break; + } + } + + return i == aNumberToExpect; +} diff --git a/extensions/cookie/test/unit/test_permmanager_cleardata.js b/extensions/cookie/test/unit/test_permmanager_cleardata.js new file mode 100644 index 000000000..faa2579f5 --- /dev/null +++ b/extensions/cookie/test/unit/test_permmanager_cleardata.js @@ -0,0 +1,68 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +var pm; + +// Create a principal based on the { origin, originAttributes }. +function createPrincipal(aOrigin, aOriginAttributes) +{ + return Services.scriptSecurityManager.createCodebasePrincipal(NetUtil.newURI(aOrigin), aOriginAttributes); +} + +// Return the data required by 'clear-origin-attributes-data' notification. +function getData(aPattern) +{ + return JSON.stringify(aPattern); +} + +// Use aEntries to create principals, add permissions to them and check that they have them. +// Then, it is notifying 'clear-origin-attributes-data' with the given aData and check if the permissions +// of principals[i] matches the permission in aResults[i]. +function test(aEntries, aData, aResults) +{ + let principals = []; + + for (entry of aEntries) { + principals.push(createPrincipal(entry.origin, entry.originAttributes)); + } + + for (principal of principals) { + do_check_eq(pm.testPermissionFromPrincipal(principal, "test/clear-origin"), pm.UNKNOWN_ACTION); + pm.addFromPrincipal(principal, "test/clear-origin", pm.ALLOW_ACTION, pm.EXPIRE_NEVER, 0); + do_check_eq(pm.testPermissionFromPrincipal(principal, "test/clear-origin"), pm.ALLOW_ACTION); + } + + Services.obs.notifyObservers(null, 'clear-origin-attributes-data', aData); + + var length = aEntries.length; + for (let i=0; i<length; ++i) { + do_check_eq(pm.testPermissionFromPrincipal(principals[i], 'test/clear-origin'), aResults[i]); + + // Remove allowed actions. + if (aResults[i] == pm.ALLOW_ACTION) { + pm.removeFromPrincipal(principals[i], 'test/clear-origin'); + } + } +} + +function run_test() +{ + do_get_profile(); + + pm = Cc["@mozilla.org/permissionmanager;1"] + .getService(Ci.nsIPermissionManager); + + let entries = [ + { origin: 'http://example.com', originAttributes: { appId: 1 } }, + { origin: 'http://example.com', originAttributes: { appId: 1, inIsolatedMozBrowser: true } }, + { origin: 'http://example.com', originAttributes: {} }, + { origin: 'http://example.com', originAttributes: { appId: 2 } }, + ]; + + // In that case, all permissions from app 1 should be removed but not the other ones. + test(entries, getData({appId: 1}), [ pm.UNKNOWN_ACTION, pm.UNKNOWN_ACTION, pm.ALLOW_ACTION, pm.ALLOW_ACTION ]); + + // In that case, only the permissions of app 1 related to a browserElement should be removed. + // All the other permissions should stay. + test(entries, getData({appId: 1, inIsolatedMozBrowser: true}), [ pm.ALLOW_ACTION, pm.UNKNOWN_ACTION, pm.ALLOW_ACTION, pm.ALLOW_ACTION ]); +} diff --git a/extensions/cookie/test/unit/test_permmanager_defaults.js b/extensions/cookie/test/unit/test_permmanager_defaults.js new file mode 100644 index 000000000..604f2b4d9 --- /dev/null +++ b/extensions/cookie/test/unit/test_permmanager_defaults.js @@ -0,0 +1,295 @@ +/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// The origin we use in most of the tests.
+const TEST_ORIGIN = NetUtil.newURI("http://example.org");
+const TEST_ORIGIN_HTTPS = NetUtil.newURI("https://example.org");
+const TEST_ORIGIN_2 = NetUtil.newURI("http://example.com");
+const TEST_ORIGIN_3 = NetUtil.newURI("https://example2.com:8080");
+const TEST_PERMISSION = "test-permission";
+Components.utils.import("resource://gre/modules/Promise.jsm");
+
+function promiseTimeout(delay) {
+ let deferred = Promise.defer();
+ do_timeout(delay, deferred.resolve);
+ return deferred.promise;
+}
+
+function run_test() {
+ run_next_test();
+}
+
+add_task(function* do_test() {
+ // setup a profile.
+ do_get_profile();
+
+ // create a file in the temp directory with the defaults.
+ let file = do_get_tempdir();
+ file.append("test_default_permissions");
+
+ // write our test data to it.
+ let ostream = Cc["@mozilla.org/network/file-output-stream;1"].
+ createInstance(Ci.nsIFileOutputStream);
+ ostream.init(file, -1, 0o666, 0);
+ let conv = Cc["@mozilla.org/intl/converter-output-stream;1"].
+ createInstance(Ci.nsIConverterOutputStream);
+ conv.init(ostream, "UTF-8", 0, 0);
+
+ conv.writeString("# this is a comment\n");
+ conv.writeString("\n"); // a blank line!
+ conv.writeString("host\t" + TEST_PERMISSION + "\t1\t" + TEST_ORIGIN.host + "\n");
+ conv.writeString("host\t" + TEST_PERMISSION + "\t1\t" + TEST_ORIGIN_2.host + "\n");
+ conv.writeString("origin\t" + TEST_PERMISSION + "\t1\t" + TEST_ORIGIN_3.spec + "\n");
+ conv.writeString("origin\t" + TEST_PERMISSION + "\t1\t" + TEST_ORIGIN.spec + "^appId=1000&inBrowser=1\n");
+ ostream.close();
+
+ // Set the preference used by the permission manager so the file is read.
+ Services.prefs.setCharPref("permissions.manager.defaultsUrl", "file://" + file.path);
+
+ // initialize the permission manager service - it will read that default.
+ let pm = Cc["@mozilla.org/permissionmanager;1"].
+ getService(Ci.nsIPermissionManager);
+
+ // test the default permission was applied.
+ let principal = Services.scriptSecurityManager.createCodebasePrincipal(TEST_ORIGIN, {});
+ let principalHttps = Services.scriptSecurityManager.createCodebasePrincipal(TEST_ORIGIN_HTTPS, {});
+ let principal2 = Services.scriptSecurityManager.createCodebasePrincipal(TEST_ORIGIN_2, {});
+ let principal3 = Services.scriptSecurityManager.createCodebasePrincipal(TEST_ORIGIN_3, {});
+
+ let attrs = {appId: 1000, inIsolatedMozBrowser: true};
+ let principal4 = Services.scriptSecurityManager.createCodebasePrincipal(TEST_ORIGIN, attrs);
+ let principal5 = Services.scriptSecurityManager.createCodebasePrincipal(TEST_ORIGIN_3, attrs);
+
+ attrs = {userContextId: 1};
+ let principal6 = Services.scriptSecurityManager.createCodebasePrincipal(TEST_ORIGIN, attrs);
+ attrs = {firstPartyDomain: "cnn.com"};
+ let principal7 = Services.scriptSecurityManager.createCodebasePrincipal(TEST_ORIGIN, attrs);
+ attrs = {userContextId: 1, firstPartyDomain: "cnn.com"};
+ let principal8 = Services.scriptSecurityManager.createCodebasePrincipal(TEST_ORIGIN, attrs);
+
+ do_check_eq(Ci.nsIPermissionManager.ALLOW_ACTION,
+ pm.testPermissionFromPrincipal(principal, TEST_PERMISSION));
+ do_check_eq(Ci.nsIPermissionManager.ALLOW_ACTION,
+ pm.testPermissionFromPrincipal(principalHttps, TEST_PERMISSION));
+ do_check_eq(Ci.nsIPermissionManager.ALLOW_ACTION,
+ pm.testPermissionFromPrincipal(principal3, TEST_PERMISSION));
+ do_check_eq(Ci.nsIPermissionManager.ALLOW_ACTION,
+ pm.testPermissionFromPrincipal(principal4, TEST_PERMISSION));
+
+ // Didn't add
+ do_check_eq(Ci.nsIPermissionManager.UNKNOWN_ACTION,
+ pm.testPermissionFromPrincipal(principal5, TEST_PERMISSION));
+
+ // the permission should exist in the enumerator.
+ do_check_eq(Ci.nsIPermissionManager.ALLOW_ACTION, findCapabilityViaEnum(TEST_ORIGIN));
+ do_check_eq(Ci.nsIPermissionManager.ALLOW_ACTION, findCapabilityViaEnum(TEST_ORIGIN_3));
+
+ // but should not have been written to the DB
+ yield checkCapabilityViaDB(null);
+
+ // remove all should not throw and the default should remain
+ pm.removeAll();
+
+ do_check_eq(Ci.nsIPermissionManager.ALLOW_ACTION,
+ pm.testPermissionFromPrincipal(principal, TEST_PERMISSION));
+ do_check_eq(Ci.nsIPermissionManager.ALLOW_ACTION,
+ pm.testPermissionFromPrincipal(principal3, TEST_PERMISSION));
+ do_check_eq(Ci.nsIPermissionManager.ALLOW_ACTION,
+ pm.testPermissionFromPrincipal(principal4, TEST_PERMISSION));
+ // make sure principals with userContextId or firstPartyDomain use the same permissions
+ do_check_eq(Ci.nsIPermissionManager.ALLOW_ACTION,
+ pm.testPermissionFromPrincipal(principal6, TEST_PERMISSION));
+ do_check_eq(Ci.nsIPermissionManager.ALLOW_ACTION,
+ pm.testPermissionFromPrincipal(principal7, TEST_PERMISSION));
+ do_check_eq(Ci.nsIPermissionManager.ALLOW_ACTION,
+ pm.testPermissionFromPrincipal(principal8, TEST_PERMISSION));
+
+ // Asking for this permission to be removed should result in that permission
+ // having UNKNOWN_ACTION
+ pm.removeFromPrincipal(principal, TEST_PERMISSION);
+ do_check_eq(Ci.nsIPermissionManager.UNKNOWN_ACTION,
+ pm.testPermissionFromPrincipal(principal, TEST_PERMISSION));
+ // make sure principals with userContextId or firstPartyDomain use the same permissions
+ do_check_eq(Ci.nsIPermissionManager.UNKNOWN_ACTION,
+ pm.testPermissionFromPrincipal(principal6, TEST_PERMISSION));
+ do_check_eq(Ci.nsIPermissionManager.UNKNOWN_ACTION,
+ pm.testPermissionFromPrincipal(principal7, TEST_PERMISSION));
+ do_check_eq(Ci.nsIPermissionManager.UNKNOWN_ACTION,
+ pm.testPermissionFromPrincipal(principal8, TEST_PERMISSION));
+ // and we should have this UNKNOWN_ACTION reflected in the DB
+ yield checkCapabilityViaDB(Ci.nsIPermissionManager.UNKNOWN_ACTION);
+ // but the permission should *not* appear in the enumerator.
+ do_check_eq(null, findCapabilityViaEnum());
+
+ // and a subsequent RemoveAll should restore the default
+ pm.removeAll();
+
+ do_check_eq(Ci.nsIPermissionManager.ALLOW_ACTION,
+ pm.testPermissionFromPrincipal(principal, TEST_PERMISSION));
+ // make sure principals with userContextId or firstPartyDomain use the same permissions
+ do_check_eq(Ci.nsIPermissionManager.ALLOW_ACTION,
+ pm.testPermissionFromPrincipal(principal6, TEST_PERMISSION));
+ do_check_eq(Ci.nsIPermissionManager.ALLOW_ACTION,
+ pm.testPermissionFromPrincipal(principal7, TEST_PERMISSION));
+ do_check_eq(Ci.nsIPermissionManager.ALLOW_ACTION,
+ pm.testPermissionFromPrincipal(principal8, TEST_PERMISSION));
+ // and allow it to again be seen in the enumerator.
+ do_check_eq(Ci.nsIPermissionManager.ALLOW_ACTION, findCapabilityViaEnum());
+
+ // now explicitly add a permission - this too should override the default.
+ pm.addFromPrincipal(principal, TEST_PERMISSION, Ci.nsIPermissionManager.DENY_ACTION);
+
+ // it should be reflected in a permission check, in the enumerator and the DB
+ do_check_eq(Ci.nsIPermissionManager.DENY_ACTION,
+ pm.testPermissionFromPrincipal(principal, TEST_PERMISSION));
+ // make sure principals with userContextId or firstPartyDomain use the same permissions
+ do_check_eq(Ci.nsIPermissionManager.DENY_ACTION,
+ pm.testPermissionFromPrincipal(principal6, TEST_PERMISSION));
+ do_check_eq(Ci.nsIPermissionManager.DENY_ACTION,
+ pm.testPermissionFromPrincipal(principal7, TEST_PERMISSION));
+ do_check_eq(Ci.nsIPermissionManager.DENY_ACTION,
+ pm.testPermissionFromPrincipal(principal8, TEST_PERMISSION));
+ do_check_eq(Ci.nsIPermissionManager.DENY_ACTION, findCapabilityViaEnum());
+ yield checkCapabilityViaDB(Ci.nsIPermissionManager.DENY_ACTION);
+
+ // explicitly add a different permission - in this case we are no longer
+ // replacing the default, but instead replacing the replacement!
+ pm.addFromPrincipal(principal, TEST_PERMISSION, Ci.nsIPermissionManager.PROMPT_ACTION);
+
+ // it should be reflected in a permission check, in the enumerator and the DB
+ do_check_eq(Ci.nsIPermissionManager.PROMPT_ACTION,
+ pm.testPermissionFromPrincipal(principal, TEST_PERMISSION));
+ // make sure principals with userContextId or firstPartyDomain use the same permissions
+ do_check_eq(Ci.nsIPermissionManager.PROMPT_ACTION,
+ pm.testPermissionFromPrincipal(principal6, TEST_PERMISSION));
+ do_check_eq(Ci.nsIPermissionManager.PROMPT_ACTION,
+ pm.testPermissionFromPrincipal(principal7, TEST_PERMISSION));
+ do_check_eq(Ci.nsIPermissionManager.PROMPT_ACTION,
+ pm.testPermissionFromPrincipal(principal8, TEST_PERMISSION));
+ do_check_eq(Ci.nsIPermissionManager.PROMPT_ACTION, findCapabilityViaEnum());
+ yield checkCapabilityViaDB(Ci.nsIPermissionManager.PROMPT_ACTION);
+
+ // --------------------------------------------------------------
+ // check default permissions and removeAllSince work as expected.
+ pm.removeAll(); // ensure only defaults are there.
+
+ // default for both principals is allow.
+ do_check_eq(Ci.nsIPermissionManager.ALLOW_ACTION,
+ pm.testPermissionFromPrincipal(principal, TEST_PERMISSION));
+ do_check_eq(Ci.nsIPermissionManager.ALLOW_ACTION,
+ pm.testPermissionFromPrincipal(principal2, TEST_PERMISSION));
+
+ // Add a default override for TEST_ORIGIN_2 - this one should *not* be
+ // restored in removeAllSince()
+ pm.addFromPrincipal(principal2, TEST_PERMISSION, Ci.nsIPermissionManager.DENY_ACTION);
+ do_check_eq(Ci.nsIPermissionManager.DENY_ACTION,
+ pm.testPermissionFromPrincipal(principal2, TEST_PERMISSION));
+ yield promiseTimeout(20);
+
+ let since = Number(Date.now());
+ yield promiseTimeout(20);
+
+ // explicitly add a permission which overrides the default for the first
+ // principal - this one *should* be removed by removeAllSince.
+ pm.addFromPrincipal(principal, TEST_PERMISSION, Ci.nsIPermissionManager.DENY_ACTION);
+ do_check_eq(Ci.nsIPermissionManager.DENY_ACTION,
+ pm.testPermissionFromPrincipal(principal, TEST_PERMISSION));
+
+ // do a removeAllSince.
+ pm.removeAllSince(since);
+
+ // the default for the first principal should re-appear as we modified it
+ // later then |since|
+ do_check_eq(Ci.nsIPermissionManager.ALLOW_ACTION,
+ pm.testPermissionFromPrincipal(principal, TEST_PERMISSION));
+
+ // but the permission for principal2 should remain as we added that before |since|.
+ do_check_eq(Ci.nsIPermissionManager.DENY_ACTION,
+ pm.testPermissionFromPrincipal(principal2, TEST_PERMISSION));
+
+ // remove the temp file we created.
+ file.remove(false);
+});
+
+// use an enumerator to find the requested permission. Returns the permission
+// value (ie, the "capability" in nsIPermission parlance) or null if it can't
+// be found.
+function findCapabilityViaEnum(origin = TEST_ORIGIN, type = TEST_PERMISSION) {
+ let result = undefined;
+ let e = Services.perms.enumerator;
+ while (e.hasMoreElements()) {
+ let perm = e.getNext().QueryInterface(Ci.nsIPermission);
+ if (perm.matchesURI(origin, true) &&
+ perm.type == type) {
+ if (result !== undefined) {
+ // we've already found one previously - that's bad!
+ do_throw("enumerator found multiple entries");
+ }
+ result = perm.capability;
+ }
+ }
+ return result || null;
+}
+
+// A function to check the DB has the specified capability. As the permission
+// manager uses async DB operations without a completion callback, the
+// distinct possibility exists that our checking of the DB will happen before
+// the permission manager update has completed - so we just retry a few times.
+// Returns a promise.
+function checkCapabilityViaDB(expected, origin = TEST_ORIGIN, type = TEST_PERMISSION) {
+ let deferred = Promise.defer();
+ let count = 0;
+ let max = 20;
+ let do_check = () => {
+ let got = findCapabilityViaDB(origin, type);
+ if (got == expected) {
+ // the do_check_eq() below will succeed - which is what we want.
+ do_check_eq(got, expected, "The database has the expected value");
+ deferred.resolve();
+ return;
+ }
+ // value isn't correct - see if we've retried enough
+ if (count++ == max) {
+ // the do_check_eq() below will fail - which is what we want.
+ do_check_eq(got, expected, "The database wasn't updated with the expected value");
+ deferred.resolve();
+ return;
+ }
+ // we can retry...
+ do_timeout(100, do_check);
+ }
+ do_check();
+ return deferred.promise;
+}
+
+// use the DB to find the requested permission. Returns the permission
+// value (ie, the "capability" in nsIPermission parlance) or null if it can't
+// be found.
+function findCapabilityViaDB(origin = TEST_ORIGIN, type = TEST_PERMISSION) {
+ let principal = Services.scriptSecurityManager.createCodebasePrincipal(origin, {});
+ let originStr = principal.origin;
+
+ let file = Services.dirsvc.get("ProfD", Ci.nsIFile);
+ file.append("permissions.sqlite");
+
+ let storage = Cc["@mozilla.org/storage/service;1"]
+ .getService(Ci.mozIStorageService);
+
+ let connection = storage.openDatabase(file);
+
+ let query = connection.createStatement(
+ "SELECT permission FROM moz_perms WHERE origin = :origin AND type = :type");
+ query.bindByName("origin", originStr);
+ query.bindByName("type", type);
+
+ if (!query.executeStep()) {
+ // no row
+ return null;
+ }
+ let result = query.getInt32(0);
+ if (query.executeStep()) {
+ // this is bad - we never expect more than 1 row here.
+ do_throw("More than 1 row found!")
+ }
+ return result;
+}
diff --git a/extensions/cookie/test/unit/test_permmanager_expiration.js b/extensions/cookie/test/unit/test_permmanager_expiration.js new file mode 100644 index 000000000..7ab8e88ae --- /dev/null +++ b/extensions/cookie/test/unit/test_permmanager_expiration.js @@ -0,0 +1,82 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Test that permissions with specific expiry times behave as expected. + +var test_generator = do_run_test(); + +function run_test() { + do_test_pending(); + test_generator.next(); +} + +function continue_test() +{ + do_run_generator(test_generator); +} + +function do_run_test() { + // Set up a profile. + let profile = do_get_profile(); + + let pm = Services.perms; + let permURI = NetUtil.newURI("http://example.com"); + let principal = Services.scriptSecurityManager.createCodebasePrincipal(permURI, {}); + + let now = Number(Date.now()); + + // add a permission with *now* expiration + pm.addFromPrincipal(principal, "test/expiration-perm-exp", 1, pm.EXPIRE_TIME, now); + pm.addFromPrincipal(principal, "test/expiration-session-exp", 1, pm.EXPIRE_SESSION, now); + + // add a permission with future expiration (100 milliseconds) + pm.addFromPrincipal(principal, "test/expiration-perm-exp2", 1, pm.EXPIRE_TIME, now + 100); + pm.addFromPrincipal(principal, "test/expiration-session-exp2", 1, pm.EXPIRE_SESSION, now + 100); + + // add a permission with future expiration (1000 seconds) + pm.addFromPrincipal(principal, "test/expiration-perm-exp3", 1, pm.EXPIRE_TIME, now + 1e6); + pm.addFromPrincipal(principal, "test/expiration-session-exp3", 1, pm.EXPIRE_SESSION, now + 1e6); + + // add a permission without expiration + pm.addFromPrincipal(principal, "test/expiration-perm-nexp", 1, pm.EXPIRE_NEVER, 0); + + // add a permission for renewal + pm.addFromPrincipal(principal, "test/expiration-perm-renewable", 1, pm.EXPIRE_TIME, now + 100); + pm.addFromPrincipal(principal, "test/expiration-session-renewable", 1, pm.EXPIRE_SESSION, now + 100); + + // And immediately renew them with longer timeouts + pm.updateExpireTime(principal, "test/expiration-perm-renewable", true, now + 100, now + 1e6); + pm.updateExpireTime(principal, "test/expiration-session-renewable", true, now + 1e6, now + 100); + + // check that the second two haven't expired yet + do_check_eq(1, pm.testPermissionFromPrincipal(principal, "test/expiration-perm-exp3")); + do_check_eq(1, pm.testPermissionFromPrincipal(principal, "test/expiration-session-exp3")); + do_check_eq(1, pm.testPermissionFromPrincipal(principal, "test/expiration-perm-nexp")); + do_check_eq(1, pm.testPermissionFromPrincipal(principal, "test/expiration-perm-renewable")); + do_check_eq(1, pm.testPermissionFromPrincipal(principal, "test/expiration-session-renewable")); + + // ... and the first one has + do_timeout(10, continue_test); + yield; + do_check_eq(0, pm.testPermissionFromPrincipal(principal, "test/expiration-perm-exp")); + do_check_eq(0, pm.testPermissionFromPrincipal(principal, "test/expiration-session-exp")); + + // ... and that the short-term one will + do_timeout(200, continue_test); + yield; + do_check_eq(0, pm.testPermissionFromPrincipal(principal, "test/expiration-perm-exp2")); + do_check_eq(0, pm.testPermissionFromPrincipal(principal, "test/expiration-session-exp2")); + + // Check that .getPermission returns a matching result + do_check_null(pm.getPermissionObject(principal, "test/expiration-perm-exp", false)); + do_check_null(pm.getPermissionObject(principal, "test/expiration-session-exp", false)); + do_check_null(pm.getPermissionObject(principal, "test/expiration-perm-exp2", false)); + do_check_null(pm.getPermissionObject(principal, "test/expiration-session-exp2", false)); + + // Check that the renewable permissions actually got renewed + do_check_eq(1, pm.testPermissionFromPrincipal(principal, "test/expiration-perm-renewable")); + do_check_eq(1, pm.testPermissionFromPrincipal(principal, "test/expiration-session-renewable")); + + do_finish_generator_test(test_generator); +} + diff --git a/extensions/cookie/test/unit/test_permmanager_getAllForURI.js b/extensions/cookie/test/unit/test_permmanager_getAllForURI.js new file mode 100644 index 000000000..7fec41826 --- /dev/null +++ b/extensions/cookie/test/unit/test_permmanager_getAllForURI.js @@ -0,0 +1,78 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +function check_enumerator(uri, permissions) { + let pm = Cc["@mozilla.org/permissionmanager;1"] + .getService(Ci.nsIPermissionManager); + + let enumerator = pm.getAllForURI(uri); + for ([type, capability] of permissions) { + let perm = enumerator.getNext(); + do_check_true(perm != null); + do_check_true(perm.principal.URI.equals(uri)); + do_check_eq(perm.type, type); + do_check_eq(perm.capability, capability); + do_check_eq(perm.expireType, pm.EXPIRE_NEVER); + } + do_check_false(enumerator.hasMoreElements()); +} + +function run_test() { + let pm = Cc["@mozilla.org/permissionmanager;1"] + .getService(Ci.nsIPermissionManager); + + let uri = NetUtil.newURI("http://example.com"); + let sub = NetUtil.newURI("http://sub.example.com"); + + check_enumerator(uri, [ ]); + + pm.add(uri, "test/getallforuri", pm.ALLOW_ACTION); + check_enumerator(uri, [ + [ "test/getallforuri", pm.ALLOW_ACTION ] + ]); + + // check that uris are matched exactly + check_enumerator(sub, [ ]); + + pm.add(sub, "test/getallforuri", pm.PROMPT_ACTION); + pm.add(sub, "test/getallforuri2", pm.DENY_ACTION); + + check_enumerator(sub, [ + [ "test/getallforuri", pm.PROMPT_ACTION ], + [ "test/getallforuri2", pm.DENY_ACTION ] + ]); + + // check that the original uri list has not changed + check_enumerator(uri, [ + [ "test/getallforuri", pm.ALLOW_ACTION ] + ]); + + // check that UNKNOWN_ACTION permissions are ignored + pm.add(uri, "test/getallforuri2", pm.UNKNOWN_ACTION); + pm.add(uri, "test/getallforuri3", pm.DENY_ACTION); + + check_enumerator(uri, [ + [ "test/getallforuri", pm.ALLOW_ACTION ], + [ "test/getallforuri3", pm.DENY_ACTION ] + ]); + + // check that permission updates are reflected + pm.add(uri, "test/getallforuri", pm.PROMPT_ACTION); + + check_enumerator(uri, [ + [ "test/getallforuri", pm.PROMPT_ACTION ], + [ "test/getallforuri3", pm.DENY_ACTION ] + ]); + + // check that permission removals are reflected + pm.remove(uri, "test/getallforuri"); + + check_enumerator(uri, [ + [ "test/getallforuri3", pm.DENY_ACTION ] + ]); + + pm.removeAll(); + check_enumerator(uri, [ ]); + check_enumerator(sub, [ ]); +} + diff --git a/extensions/cookie/test/unit/test_permmanager_getPermissionObject.js b/extensions/cookie/test/unit/test_permmanager_getPermissionObject.js new file mode 100644 index 000000000..d01b51923 --- /dev/null +++ b/extensions/cookie/test/unit/test_permmanager_getPermissionObject.js @@ -0,0 +1,95 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +function getPrincipalFromURI(aURI) { + let ssm = Cc["@mozilla.org/scriptsecuritymanager;1"] + .getService(Ci.nsIScriptSecurityManager); + let uri = NetUtil.newURI(aURI); + return ssm.createCodebasePrincipal(uri, {}); +} + +function getSystemPrincipal() { + return Cc["@mozilla.org/scriptsecuritymanager;1"] + .getService(Ci.nsIScriptSecurityManager) + .getSystemPrincipal(); +} + +function run_test() { + var pm = Cc["@mozilla.org/permissionmanager;1"]. + getService(Ci.nsIPermissionManager); + + do_check_null(pm.getPermissionObject(getSystemPrincipal(), "test/pobject", false)); + + let principal = getPrincipalFromURI("http://example.com"); + let subPrincipal = getPrincipalFromURI("http://sub.example.com"); + let subSubPrincipal = getPrincipalFromURI("http://sub.sub.example.com"); + + do_check_null(pm.getPermissionObject(principal, "test/pobject", false)); + do_check_null(pm.getPermissionObject(principal, "test/pobject", true)); + + pm.addFromPrincipal(principal, "test/pobject", pm.ALLOW_ACTION); + var rootPerm = pm.getPermissionObject(principal, "test/pobject", false); + do_check_true(rootPerm != null); + do_check_eq(rootPerm.principal.origin, "http://example.com"); + do_check_eq(rootPerm.type, "test/pobject"); + do_check_eq(rootPerm.capability, pm.ALLOW_ACTION); + do_check_eq(rootPerm.expireType, pm.EXPIRE_NEVER); + + var rootPerm2 = pm.getPermissionObject(principal, "test/pobject", true); + do_check_true(rootPerm != null); + do_check_eq(rootPerm.principal.origin, "http://example.com"); + + var subPerm = pm.getPermissionObject(subPrincipal, "test/pobject", true); + do_check_null(subPerm); + subPerm = pm.getPermissionObject(subPrincipal, "test/pobject", false); + do_check_true(subPerm != null); + do_check_eq(subPerm.principal.origin, "http://example.com"); + do_check_eq(subPerm.type, "test/pobject"); + do_check_eq(subPerm.capability, pm.ALLOW_ACTION); + + subPerm = pm.getPermissionObject(subSubPrincipal, "test/pobject", true); + do_check_null(subPerm); + subPerm = pm.getPermissionObject(subSubPrincipal, "test/pobject", false); + do_check_true(subPerm != null); + do_check_eq(subPerm.principal.origin, "http://example.com"); + + pm.addFromPrincipal(principal, "test/pobject", pm.DENY_ACTION, pm.EXPIRE_SESSION); + + // make sure permission objects are not dynamic + do_check_eq(rootPerm.capability, pm.ALLOW_ACTION); + + // but do update on change + rootPerm = pm.getPermissionObject(principal, "test/pobject", true); + do_check_eq(rootPerm.capability, pm.DENY_ACTION); + do_check_eq(rootPerm.expireType, pm.EXPIRE_SESSION); + + subPerm = pm.getPermissionObject(subPrincipal, "test/pobject", false); + do_check_eq(subPerm.principal.origin, "http://example.com"); + do_check_eq(subPerm.capability, pm.DENY_ACTION); + do_check_eq(subPerm.expireType, pm.EXPIRE_SESSION); + + pm.addFromPrincipal(subPrincipal, "test/pobject", pm.PROMPT_ACTION); + rootPerm = pm.getPermissionObject(principal, "test/pobject", true); + do_check_eq(rootPerm.principal.origin, "http://example.com"); + do_check_eq(rootPerm.capability, pm.DENY_ACTION); + + subPerm = pm.getPermissionObject(subPrincipal, "test/pobject", true); + do_check_eq(subPerm.principal.origin, "http://sub.example.com"); + do_check_eq(subPerm.capability, pm.PROMPT_ACTION); + + subPerm = pm.getPermissionObject(subPrincipal, "test/pobject", false); + do_check_eq(subPerm.principal.origin, "http://sub.example.com"); + do_check_eq(subPerm.capability, pm.PROMPT_ACTION); + + subPerm = pm.getPermissionObject(subSubPrincipal, "test/pobject", true); + do_check_null(subPerm); + + subPerm = pm.getPermissionObject(subSubPrincipal, "test/pobject", false); + do_check_eq(subPerm.principal.origin, "http://sub.example.com"); + do_check_eq(subPerm.capability, pm.PROMPT_ACTION); + + pm.removeFromPrincipal(principal, "test/pobject"); + + rootPerm = pm.getPermissionObject(principal, "test/pobject", true); + do_check_null(rootPerm); +} diff --git a/extensions/cookie/test/unit/test_permmanager_idn.js b/extensions/cookie/test/unit/test_permmanager_idn.js new file mode 100644 index 000000000..72fc06bba --- /dev/null +++ b/extensions/cookie/test/unit/test_permmanager_idn.js @@ -0,0 +1,49 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +function getPrincipalFromDomain(aDomain) { + let ssm = Cc["@mozilla.org/scriptsecuritymanager;1"] + .getService(Ci.nsIScriptSecurityManager); + let uri = NetUtil.newURI("http://" + aDomain); + return ssm.createCodebasePrincipal(uri, {}); +} + +function run_test() { + let profile = do_get_profile(); + let pm = Services.perms; + let perm = 'test-idn'; + + // We create three principal linked to IDN. + // One with just a domain, one with a subdomain and one with the TLD + // containing a UTF-8 character. + let mainDomainPrincipal = getPrincipalFromDomain("fôû.com"); + let subDomainPrincipal = getPrincipalFromDomain("fôô.bà r.com"); + let tldPrincipal = getPrincipalFromDomain("fôû.bà r.côm"); + + // We add those to the permission manager. + pm.addFromPrincipal(mainDomainPrincipal, perm, pm.ALLOW_ACTION, 0, 0); + pm.addFromPrincipal(subDomainPrincipal, perm, pm.ALLOW_ACTION, 0, 0); + pm.addFromPrincipal(tldPrincipal, perm, pm.ALLOW_ACTION, 0, 0); + + // They should obviously be there now.. + do_check_eq(pm.testPermissionFromPrincipal(mainDomainPrincipal, perm), pm.ALLOW_ACTION); + do_check_eq(pm.testPermissionFromPrincipal(subDomainPrincipal, perm), pm.ALLOW_ACTION); + do_check_eq(pm.testPermissionFromPrincipal(tldPrincipal, perm), pm.ALLOW_ACTION); + + // We do the same thing with the puny-encoded versions of the IDN. + let punyMainDomainPrincipal = getPrincipalFromDomain('xn--f-xgav.com'); + let punySubDomainPrincipal = getPrincipalFromDomain('xn--f-xgaa.xn--br-jia.com'); + let punyTldPrincipal = getPrincipalFromDomain('xn--f-xgav.xn--br-jia.xn--cm-8ja'); + + // Those principals should have the permission granted too. + do_check_eq(pm.testPermissionFromPrincipal(punyMainDomainPrincipal, perm), pm.ALLOW_ACTION); + do_check_eq(pm.testPermissionFromPrincipal(punySubDomainPrincipal, perm), pm.ALLOW_ACTION); + do_check_eq(pm.testPermissionFromPrincipal(punyTldPrincipal, perm), pm.ALLOW_ACTION); + + // However, those two principals shouldn't be allowed because they are like + // the IDN but without the UT8-8 characters. + let witnessPrincipal = getPrincipalFromDomain("foo.com"); + do_check_eq(pm.testPermissionFromPrincipal(witnessPrincipal, perm), pm.UNKNOWN_ACTION); + witnessPrincipal = getPrincipalFromDomain("foo.bar.com"); + do_check_eq(pm.testPermissionFromPrincipal(witnessPrincipal, perm), pm.UNKNOWN_ACTION); +} diff --git a/extensions/cookie/test/unit/test_permmanager_load_invalid_entries.js b/extensions/cookie/test/unit/test_permmanager_load_invalid_entries.js new file mode 100644 index 000000000..8d36a9667 --- /dev/null +++ b/extensions/cookie/test/unit/test_permmanager_load_invalid_entries.js @@ -0,0 +1,142 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +var DEBUG_TEST = false; + +function run_test() { + // Setup a profile directory. + var dir = do_get_profile(); + // Get the db file. + var file = dir.clone(); + file.append("permissions.sqlite"); + + var storage = Cc["@mozilla.org/storage/service;1"] + .getService(Ci.mozIStorageService); + + // Create database. + var connection = storage.openDatabase(file); + // The file should now exist. + do_check_true(file.exists()); + + connection.schemaVersion = 3; + connection.executeSimpleSQL( + "CREATE TABLE moz_hosts (" + + " id INTEGER PRIMARY KEY" + + ",host TEXT" + + ",type TEXT" + + ",permission INTEGER" + + ",expireType INTEGER" + + ",expireTime INTEGER" + + ",appId INTEGER" + + ",isInBrowserElement INTEGER" + + ")"); + + // Now we can inject garbadge in the database. + var garbadge = [ + // Regular entry. + { host: '42', type: '0', permission: 1, expireType: 0, expireTime: 0, + appId: 0, isInBrowserElement: 0 }, + + // Special values in host (some being invalid). + { host: 'scheme:file', type: '1', permission: 0, expireType: 0, + expireTime: 0, appId: 0, isInBrowserElement: 0 }, + { host: '192.168.0.1', type: '2', permission: 0, expireType: 0, + expireTime: 0, appId: 0, isInBrowserElement: 0 }, + { host: '2001:0db8:0000:0000:0000:ff00:0042:8329', type: '3', permission: 0, + expireType: 0, expireTime: 0, appId: 0, isInBrowserElement: 0 }, + { host: '::1', type: '4', permission: 0, expireType: 0, expireTime: 0, + appId: 0, isInBrowserElement: 0 }, + + // Permission is UNKNOWN_ACTION. + { host: '42', type: '5', permission: Ci.nsIPermissionManager.UNKNOWN_ACTION, + expireType: 0, expireTime: 0, appId: 0, isInBrowserElement: 0 }, + + // Permission is out of range. + { host: '42', type: '6', permission: 100, expireType: 0, expireTime: 0, + appId: 0, isInBrowserElement: 0 }, + { host: '42', type: '7', permission: -100, expireType: 0, expireTime: 0, + appId: 0, isInBrowserElement: 0 }, + + // ExpireType is out of range. + { host: '42', type: '8', permission: 1, expireType: -100, expireTime: 0, + appId: 0, isInBrowserElement: 0 }, + { host: '42', type: '9', permission: 1, expireType: 100, expireTime: 0, + appId: 0, isInBrowserElement: 0 }, + + // ExpireTime is at 0 with ExpireType = Time. + { host: '42', type: '10', permission: 1, + expireType: Ci.nsIPermissionManager.EXPIRE_TIME, expireTime: 0, appId: 0, + isInBrowserElement: 0 }, + + // ExpireTime has a value with ExpireType != Time + { host: '42', type: '11', permission: 1, + expireType: Ci.nsIPermissionManager.EXPIRE_SESSION, expireTime: 1000, + appId: 0, isInBrowserElement: 0 }, + { host: '42', type: '12', permission: 1, + expireType: Ci.nsIPermissionManager.EXPIRE_NEVER, expireTime: 1000, + appId: 0, isInBrowserElement: 0 }, + + // ExpireTime is negative. + { host: '42', type: '13', permission: 1, + expireType: Ci.nsIPermissionManager.EXPIRE_TIME, expireTime: -1, + appId: 0, isInBrowserElement: 0 }, + + // AppId is negative. + { host: '42', type: '14', permission: 1, expireType: 0, expireTime: 0, + appId: -1, isInBrowserElement: 0 }, + + // IsInBrowserElement is negative or higher than 1. + { host: '42', type: '15', permission: 1, expireType: 0, expireTime: 0, + appId: 0, isInBrowserElement: -1 }, + { host: '42', type: '16', permission: 1, expireType: 0, expireTime: 0, + appId: 0, isInBrowserElement: 10 }, + + // This insertion should be the last one. It is used to make sure we always + // load it regardless of the previous entries validities. + { host: 'example.org', type: 'test-load-invalid-entries', + permission: Ci.nsIPermissionManager.ALLOW_ACTION, expireType: 0, + expireTime: 0, appId: 0, isInBrowserElement: 0 }, + ]; + + for (var i=0; i<garbadge.length; ++i) { + if (DEBUG_TEST) { + dump("\n value #" + i + "\n\n"); + } + var data = garbadge[i]; + connection.executeSimpleSQL( + "INSERT INTO moz_hosts " + + " (id, host, type, permission, expireType, expireTime, appId, isInBrowserElement) " + + "VALUES (" + i + ", '" + data.host + "', '" + data.type + "', " + + data.permission + ", " + data.expireType + ", " + + data.expireTime + ", " + data.appId + ", " + + data.isInBrowserElement + ")" + ); + } + + let earliestNow = Number(Date.now()); + // Initialize the permission manager service + var pm = Cc["@mozilla.org/permissionmanager;1"] + .getService(Ci.nsIPermissionManager); + let latestNow = Number(Date.now()); + + // The schema should be upgraded to 9, and a 'modificationTime' column should + // exist with all records having a value of 0. + do_check_eq(connection.schemaVersion, 9); + + let select = connection.createStatement("SELECT modificationTime FROM moz_perms") + let numMigrated = 0; + while (select.executeStep()) { + let thisModTime = select.getInt64(0); + do_check_true(thisModTime == 0, "new modifiedTime field is correct"); + numMigrated += 1; + } + // check we found at least 1 record that was migrated. + do_check_true(numMigrated > 0, "we found at least 1 record that was migrated"); + + // This permission should always be there. + let ssm = Cc["@mozilla.org/scriptsecuritymanager;1"] + .getService(Ci.nsIScriptSecurityManager); + let uri = NetUtil.newURI("http://example.org"); + let principal = ssm.createCodebasePrincipal(uri, {}); + do_check_eq(pm.testPermissionFromPrincipal(principal, 'test-load-invalid-entries'), Ci.nsIPermissionManager.ALLOW_ACTION); +} diff --git a/extensions/cookie/test/unit/test_permmanager_local_files.js b/extensions/cookie/test/unit/test_permmanager_local_files.js new file mode 100644 index 000000000..b9c803f5c --- /dev/null +++ b/extensions/cookie/test/unit/test_permmanager_local_files.js @@ -0,0 +1,43 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Test that permissions work for file:// URIs (aka local files). + +function getPrincipalFromURIString(uriStr) +{ + let uri = NetUtil.newURI(uriStr); + return Services.scriptSecurityManager.createCodebasePrincipal(uri, {}); +} + +function run_test() { + let pm = Services.perms; + + // If we add a permission to a file:// URI, the test should return true. + let principal = getPrincipalFromURIString("file:///foo/bar"); + pm.addFromPrincipal(principal, "test/local-files", pm.ALLOW_ACTION, 0, 0); + do_check_eq(pm.testPermissionFromPrincipal(principal, "test/local-files"), pm.ALLOW_ACTION); + + // Another file:// URI should have the same permission. + let witnessPrincipal = getPrincipalFromURIString("file:///bar/foo"); + do_check_eq(pm.testPermissionFromPrincipal(witnessPrincipal, "test/local-files"), pm.UNKNOWN_ACTION); + + // Giving "file:///" a permission shouldn't give it to all file:// URIs. + let rootPrincipal = getPrincipalFromURIString("file:///"); + pm.addFromPrincipal(rootPrincipal, "test/local-files", pm.ALLOW_ACTION, 0, 0); + do_check_eq(pm.testPermissionFromPrincipal(witnessPrincipal, "test/local-files"), pm.UNKNOWN_ACTION); + + // Giving "file://" a permission shouldn't give it to all file:// URIs. + let schemeRootPrincipal = getPrincipalFromURIString("file://"); + pm.addFromPrincipal(schemeRootPrincipal, "test/local-files", pm.ALLOW_ACTION, 0, 0); + do_check_eq(pm.testPermissionFromPrincipal(witnessPrincipal, "test/local-files"), pm.UNKNOWN_ACTION); + + // Giving 'node' a permission shouldn't give it to its 'children'. + let fileInDirPrincipal = getPrincipalFromURIString("file:///foo/bar/foobar.txt"); + do_check_eq(pm.testPermissionFromPrincipal(fileInDirPrincipal, "test/local-files"), pm.UNKNOWN_ACTION); + + // Revert "file:///foo/bar" permission and check that it has been correctly taken into account. + pm.removeFromPrincipal(principal, "test/local-files"); + do_check_eq(pm.testPermissionFromPrincipal(principal, "test/local-files"), pm.UNKNOWN_ACTION); + do_check_eq(pm.testPermissionFromPrincipal(witnessPrincipal, "test/local-files"), pm.UNKNOWN_ACTION); + do_check_eq(pm.testPermissionFromPrincipal(fileInDirPrincipal, "test/local-files"), pm.UNKNOWN_ACTION); +} diff --git a/extensions/cookie/test/unit/test_permmanager_matches.js b/extensions/cookie/test/unit/test_permmanager_matches.js new file mode 100644 index 000000000..c15288890 --- /dev/null +++ b/extensions/cookie/test/unit/test_permmanager_matches.js @@ -0,0 +1,183 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +function matches_always(perm, principals) { + principals.forEach((principal) => { + do_check_true(perm.matches(principal, true), "perm: " + perm.principal.origin + ", princ: " + principal.origin); + do_check_true(perm.matches(principal, false), "perm: " + perm.principal.origin + ", princ: " + principal.origin); + }); +} + +function matches_weak(perm, principals) { + principals.forEach((principal) => { + do_check_false(perm.matches(principal, true), "perm: " + perm.principal.origin + ", princ: " + principal.origin); + do_check_true(perm.matches(principal, false), "perm: " + perm.principal.origin + ", princ: " + principal.origin); + }); +} + +function matches_never(perm, principals) { + principals.forEach((principal) => { + do_check_false(perm.matches(principal, true), "perm: " + perm.principal.origin + ", princ: " + principal.origin); + do_check_false(perm.matches(principal, false), "perm: " + perm.principal.origin + ", princ: " + principal.origin); + }); +} + +function run_test() { + // initialize the permission manager service + let pm = Cc["@mozilla.org/permissionmanager;1"]. + getService(Ci.nsIPermissionManager); + + let secMan = Cc["@mozilla.org/scriptsecuritymanager;1"] + .getService(Ci.nsIScriptSecurityManager); + + // Add some permissions + let uri0 = NetUtil.newURI("http://google.com/search?q=foo#hashtag", null, null); + let uri1 = NetUtil.newURI("http://hangouts.google.com/subdir", null, null); + let uri2 = NetUtil.newURI("http://google.org/", null, null); + let uri3 = NetUtil.newURI("https://google.com/some/random/subdirectory", null, null); + let uri4 = NetUtil.newURI("https://hangouts.google.com/#!/hangout", null, null); + let uri5 = NetUtil.newURI("http://google.com:8096/", null, null); + + let uri0_n_n = secMan.createCodebasePrincipal(uri0, {}); + let uri1_n_n = secMan.createCodebasePrincipal(uri1, {}); + let uri2_n_n = secMan.createCodebasePrincipal(uri2, {}); + let uri3_n_n = secMan.createCodebasePrincipal(uri3, {}); + let uri4_n_n = secMan.createCodebasePrincipal(uri4, {}); + let uri5_n_n = secMan.createCodebasePrincipal(uri5, {}); + + let attrs = {appId: 1000}; + let uri0_1000_n = secMan.createCodebasePrincipal(uri0, attrs); + let uri1_1000_n = secMan.createCodebasePrincipal(uri1, attrs); + let uri2_1000_n = secMan.createCodebasePrincipal(uri2, attrs); + let uri3_1000_n = secMan.createCodebasePrincipal(uri3, attrs); + let uri4_1000_n = secMan.createCodebasePrincipal(uri4, attrs); + let uri5_1000_n = secMan.createCodebasePrincipal(uri5, attrs); + + attrs = {appId: 1000, inIsolatedMozBrowser: true}; + let uri0_1000_y = secMan.createCodebasePrincipal(uri0, attrs); + let uri1_1000_y = secMan.createCodebasePrincipal(uri1, attrs); + let uri2_1000_y = secMan.createCodebasePrincipal(uri2, attrs); + let uri3_1000_y = secMan.createCodebasePrincipal(uri3, attrs); + let uri4_1000_y = secMan.createCodebasePrincipal(uri4, attrs); + let uri5_1000_y = secMan.createCodebasePrincipal(uri5, attrs); + + attrs = {appId: 2000}; + let uri0_2000_n = secMan.createCodebasePrincipal(uri0, attrs); + let uri1_2000_n = secMan.createCodebasePrincipal(uri1, attrs); + let uri2_2000_n = secMan.createCodebasePrincipal(uri2, attrs); + let uri3_2000_n = secMan.createCodebasePrincipal(uri3, attrs); + let uri4_2000_n = secMan.createCodebasePrincipal(uri4, attrs); + let uri5_2000_n = secMan.createCodebasePrincipal(uri5, attrs); + + attrs = {appId: 2000, inIsolatedMozBrowser: true}; + let uri0_2000_y = secMan.createCodebasePrincipal(uri0, attrs); + let uri1_2000_y = secMan.createCodebasePrincipal(uri1, attrs); + let uri2_2000_y = secMan.createCodebasePrincipal(uri2, attrs); + let uri3_2000_y = secMan.createCodebasePrincipal(uri3, attrs); + let uri4_2000_y = secMan.createCodebasePrincipal(uri4, attrs); + let uri5_2000_y = secMan.createCodebasePrincipal(uri5, attrs); + + attrs = {userContextId: 1}; + let uri0_1 = secMan.createCodebasePrincipal(uri0, attrs); + let uri1_1 = secMan.createCodebasePrincipal(uri1, attrs); + let uri2_1 = secMan.createCodebasePrincipal(uri2, attrs); + let uri3_1 = secMan.createCodebasePrincipal(uri3, attrs); + let uri4_1 = secMan.createCodebasePrincipal(uri4, attrs); + let uri5_1 = secMan.createCodebasePrincipal(uri5, attrs); + + attrs = {firstPartyDomain: "cnn.com"}; + let uri0_cnn = secMan.createCodebasePrincipal(uri0, attrs); + let uri1_cnn = secMan.createCodebasePrincipal(uri1, attrs); + let uri2_cnn = secMan.createCodebasePrincipal(uri2, attrs); + let uri3_cnn = secMan.createCodebasePrincipal(uri3, attrs); + let uri4_cnn = secMan.createCodebasePrincipal(uri4, attrs); + let uri5_cnn = secMan.createCodebasePrincipal(uri5, attrs); + + pm.addFromPrincipal(uri0_n_n, "test/matches", pm.ALLOW_ACTION); + let perm_n_n = pm.getPermissionObject(uri0_n_n, "test/matches", true); + pm.addFromPrincipal(uri0_1000_n, "test/matches", pm.ALLOW_ACTION); + let perm_1000_n = pm.getPermissionObject(uri0_1000_n, "test/matches", true); + pm.addFromPrincipal(uri0_1000_y, "test/matches", pm.ALLOW_ACTION); + let perm_1000_y = pm.getPermissionObject(uri0_1000_y, "test/matches", true); + pm.addFromPrincipal(uri0_2000_n, "test/matches", pm.ALLOW_ACTION); + let perm_2000_n = pm.getPermissionObject(uri0_2000_n, "test/matches", true); + pm.addFromPrincipal(uri0_2000_y, "test/matches", pm.ALLOW_ACTION); + let perm_2000_y = pm.getPermissionObject(uri0_2000_y, "test/matches", true); + pm.addFromPrincipal(uri0_1, "test/matches", pm.ALLOW_ACTION); + let perm_1 = pm.getPermissionObject(uri0_n_n, "test/matches", true); + pm.addFromPrincipal(uri0_cnn, "test/matches", pm.ALLOW_ACTION); + let perm_cnn = pm.getPermissionObject(uri0_n_n, "test/matches", true); + + matches_always(perm_n_n, [uri0_n_n, uri0_1, uri0_cnn]); + matches_weak(perm_n_n, [uri1_n_n, uri1_1, uri1_cnn]); + matches_never(perm_n_n, [uri2_n_n, uri3_n_n, uri4_n_n, uri5_n_n, + uri0_1000_n, uri1_1000_n, uri2_1000_n, uri3_1000_n, uri4_1000_n, uri5_1000_n, + uri0_1000_y, uri1_1000_y, uri2_1000_y, uri3_1000_y, uri4_1000_y, uri5_1000_y, + uri0_2000_n, uri1_2000_n, uri2_2000_n, uri3_2000_n, uri4_2000_n, uri5_2000_n, + uri0_2000_y, uri1_2000_y, uri2_2000_y, uri3_2000_y, uri4_2000_y, uri5_2000_y, + uri2_1, uri3_1, uri4_1, uri5_1, + uri2_cnn, uri3_cnn, uri4_cnn, uri5_cnn]); + + matches_always(perm_1000_n, [uri0_1000_n]); + matches_weak(perm_1000_n, [uri1_1000_n]); + matches_never(perm_1000_n, [uri2_1000_n, uri3_1000_n, uri4_1000_n, uri5_1000_n, + uri0_n_n, uri1_n_n, uri2_n_n, uri3_n_n, uri4_n_n, uri5_n_n, + uri0_1000_y, uri1_1000_y, uri2_1000_y, uri3_1000_y, uri4_1000_y, uri5_1000_y, + uri0_2000_n, uri1_2000_n, uri2_2000_n, uri3_2000_n, uri4_2000_n, uri5_2000_n, + uri0_2000_y, uri1_2000_y, uri2_2000_y, uri3_2000_y, uri4_2000_y, uri5_2000_y, + uri0_1, uri1_1, uri2_1, uri3_1, uri4_1, uri5_1, + uri0_cnn, uri1_cnn, uri2_cnn, uri3_cnn, uri4_cnn, uri5_cnn]); + + matches_always(perm_1000_y, [uri0_1000_y]); + matches_weak(perm_1000_y, [uri1_1000_y]); + matches_never(perm_1000_y, [uri2_1000_y, uri3_1000_y, uri4_1000_y, uri5_1000_y, + uri0_n_n, uri1_n_n, uri2_n_n, uri3_n_n, uri4_n_n, uri5_n_n, + uri0_1000_n, uri1_1000_n, uri2_1000_n, uri3_1000_n, uri4_1000_n, uri5_1000_n, + uri0_2000_n, uri1_2000_n, uri2_2000_n, uri3_2000_n, uri4_2000_n, uri5_2000_n, + uri0_2000_y, uri1_2000_y, uri2_2000_y, uri3_2000_y, uri4_2000_y, uri5_2000_y, + uri0_1, uri1_1, uri2_1, uri3_1, uri4_1, uri5_1, + uri0_cnn, uri1_cnn, uri2_cnn, uri3_cnn, uri4_cnn, uri5_cnn]); + + matches_always(perm_2000_n, [uri0_2000_n]); + matches_weak(perm_2000_n, [uri1_2000_n]); + matches_never(perm_2000_n, [uri2_2000_n, uri3_2000_n, uri4_2000_n, uri5_2000_n, + uri0_n_n, uri1_n_n, uri2_n_n, uri3_n_n, uri4_n_n, uri5_n_n, + uri0_2000_y, uri1_2000_y, uri2_2000_y, uri3_2000_y, uri4_2000_y, uri5_2000_y, + uri0_1000_n, uri1_1000_n, uri2_1000_n, uri3_1000_n, uri4_1000_n, uri5_1000_n, + uri0_1000_y, uri1_1000_y, uri2_1000_y, uri3_1000_y, uri4_1000_y, uri5_1000_y, + uri0_1, uri1_1, uri2_1, uri3_1, uri4_1, uri5_1, + uri0_cnn, uri1_cnn, uri2_cnn, uri3_cnn, uri4_cnn, uri5_cnn]); + + matches_always(perm_2000_y, [uri0_2000_y]); + matches_weak(perm_2000_y, [uri1_2000_y]); + matches_never(perm_2000_y, [uri2_2000_y, uri3_2000_y, uri4_2000_y, uri5_2000_y, + uri0_n_n, uri1_n_n, uri2_n_n, uri3_n_n, uri4_n_n, uri5_n_n, + uri0_2000_n, uri1_2000_n, uri2_2000_n, uri3_2000_n, uri4_2000_n, uri5_2000_n, + uri0_1000_n, uri1_1000_n, uri2_1000_n, uri3_1000_n, uri4_1000_n, uri5_1000_n, + uri0_1000_y, uri1_1000_y, uri2_1000_y, uri3_1000_y, uri4_1000_y, uri5_1000_y, + uri0_1, uri1_1, uri2_1, uri3_1, uri4_1, uri5_1, + uri0_cnn, uri1_cnn, uri2_cnn, uri3_cnn, uri4_cnn, uri5_cnn]); + + matches_always(perm_1, [uri0_n_n, uri0_1, uri0_cnn]); + matches_weak(perm_1, [uri1_n_n, uri1_1, uri1_cnn]); + matches_never(perm_1, [uri2_n_n, uri3_n_n, uri4_n_n, uri5_n_n, + uri0_1000_n, uri1_1000_n, uri2_1000_n, uri3_1000_n, uri4_1000_n, uri5_1000_n, + uri0_1000_y, uri1_1000_y, uri2_1000_y, uri3_1000_y, uri4_1000_y, uri5_1000_y, + uri0_2000_n, uri1_2000_n, uri2_2000_n, uri3_2000_n, uri4_2000_n, uri5_2000_n, + uri0_2000_y, uri1_2000_y, uri2_2000_y, uri3_2000_y, uri4_2000_y, uri5_2000_y, + uri2_1, uri3_1, uri4_1, uri5_1, + uri2_cnn, uri3_cnn, uri4_cnn, uri5_cnn]); + + matches_always(perm_cnn, [uri0_n_n, uri0_1, uri0_cnn]); + matches_weak(perm_cnn, [uri1_n_n, uri1_1, uri1_cnn]); + matches_never(perm_cnn, [uri2_n_n, uri3_n_n, uri4_n_n, uri5_n_n, + uri0_1000_n, uri1_1000_n, uri2_1000_n, uri3_1000_n, uri4_1000_n, uri5_1000_n, + uri0_1000_y, uri1_1000_y, uri2_1000_y, uri3_1000_y, uri4_1000_y, uri5_1000_y, + uri0_2000_n, uri1_2000_n, uri2_2000_n, uri3_2000_n, uri4_2000_n, uri5_2000_n, + uri0_2000_y, uri1_2000_y, uri2_2000_y, uri3_2000_y, uri4_2000_y, uri5_2000_y, + uri2_1, uri3_1, uri4_1, uri5_1, + uri2_cnn, uri3_cnn, uri4_cnn, uri5_cnn]); + + // Clean up! + pm.removeAll(); +} diff --git a/extensions/cookie/test/unit/test_permmanager_matchesuri.js b/extensions/cookie/test/unit/test_permmanager_matchesuri.js new file mode 100644 index 000000000..88578166c --- /dev/null +++ b/extensions/cookie/test/unit/test_permmanager_matchesuri.js @@ -0,0 +1,150 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +function matches_always(perm, uris) { + uris.forEach((uri) => { + do_check_true(perm.matchesURI(uri, true), "perm: " + perm.principal.origin + ", URI: " + uri.spec); + do_check_true(perm.matchesURI(uri, false), "perm: " + perm.principal.origin + ", URI: " + uri.spec); + }); +} + +function matches_weak(perm, uris) { + uris.forEach((uri) => { + do_check_false(perm.matchesURI(uri, true), "perm: " + perm.principal.origin + ", URI: " + uri.spec); + do_check_true(perm.matchesURI(uri, false), "perm: " + perm.principal.origin + ", URI: " + uri.spec); + }); +} + +function matches_never(perm, uris) { + uris.forEach((uri) => { + do_check_false(perm.matchesURI(uri, true), "perm: " + perm.principal.origin + ", URI: " + uri.spec); + do_check_false(perm.matchesURI(uri, false), "perm: " + perm.principal.origin + ", URI: " + uri.spec); + }); +} + +function mk_permission(uri, isAppPermission = false) { + let pm = Cc["@mozilla.org/permissionmanager;1"]. + getService(Ci.nsIPermissionManager); + + let secMan = Cc["@mozilla.org/scriptsecuritymanager;1"] + .getService(Ci.nsIScriptSecurityManager); + + // Get the permission from the principal! + let attrs = {appId: 1000}; + let principal = + secMan.createCodebasePrincipal(uri, isAppPermission ? attrs : {}); + + pm.addFromPrincipal(principal, "test/matchesuri", pm.ALLOW_ACTION); + let permission = pm.getPermissionObject(principal, "test/matchesuri", true); + + return permission; +} + +function run_test() { + // initialize the permission manager service + let pm = Cc["@mozilla.org/permissionmanager;1"]. + getService(Ci.nsIPermissionManager); + + let secMan = Cc["@mozilla.org/scriptsecuritymanager;1"] + .getService(Ci.nsIScriptSecurityManager); + + let fileprefix = "file:///"; + if (Services.appinfo.OS == "WINNT") { + // Windows rejects files if they don't have a drive. See Bug 1180870 + fileprefix += "c:/"; + } + + // Add some permissions + let uri0 = NetUtil.newURI("http://google.com:9091/just/a/path", null, null); + let uri1 = NetUtil.newURI("http://hangouts.google.com:9091/some/path", null, null); + let uri2 = NetUtil.newURI("http://google.com:9091/", null, null); + let uri3 = NetUtil.newURI("http://google.org:9091/", null, null); + let uri4 = NetUtil.newURI("http://deeper.hangouts.google.com:9091/", null, null); + let uri5 = NetUtil.newURI("https://google.com/just/a/path", null, null); + let uri6 = NetUtil.newURI("https://hangouts.google.com", null, null); + let uri7 = NetUtil.newURI("https://google.com/", null, null); + + let fileuri1 = NetUtil.newURI(fileprefix + "a/file/path", null, null); + let fileuri2 = NetUtil.newURI(fileprefix + "a/file/path/deeper", null, null); + let fileuri3 = NetUtil.newURI(fileprefix + "a/file/otherpath", null, null); + + { + let perm = mk_permission(uri0); + matches_always(perm, [uri0, uri2]); + matches_weak(perm, [uri1, uri4]); + matches_never(perm, [uri3, uri5, uri6, uri7, fileuri1, fileuri2, fileuri3]); + } + + { + let perm = mk_permission(uri1); + matches_always(perm, [uri1]); + matches_weak(perm, [uri4]); + matches_never(perm, [uri0, uri2, uri3, uri5, uri6, uri7, fileuri1, fileuri2, fileuri3]); + } + + { + let perm = mk_permission(uri2); + matches_always(perm, [uri0, uri2]); + matches_weak(perm, [uri1, uri4]); + matches_never(perm, [uri3, uri5, uri6, uri7, fileuri1, fileuri2, fileuri3]); + } + + { + let perm = mk_permission(uri3); + matches_always(perm, [uri3]); + matches_weak(perm, []); + matches_never(perm, [uri1, uri2, uri4, uri5, uri6, uri7, fileuri1, fileuri2, fileuri3]); + } + + { + let perm = mk_permission(uri4); + matches_always(perm, [uri4]); + matches_weak(perm, []); + matches_never(perm, [uri1, uri2, uri3, uri5, uri6, uri7, fileuri1, fileuri2, fileuri3]); + } + + { + let perm = mk_permission(uri5); + matches_always(perm, [uri5, uri7]); + matches_weak(perm, [uri6]); + matches_never(perm, [uri0, uri1, uri2, uri3, uri4, fileuri1, fileuri2, fileuri3]); + } + + { + let perm = mk_permission(uri6); + matches_always(perm, [uri6]); + matches_weak(perm, []); + matches_never(perm, [uri0, uri1, uri2, uri3, uri4, uri5, uri7, fileuri1, fileuri2, fileuri3]); + } + + { + let perm = mk_permission(uri7); + matches_always(perm, [uri5, uri7]); + matches_weak(perm, [uri6]); + matches_never(perm, [uri0, uri1, uri2, uri3, uri4, fileuri1, fileuri2, fileuri3]); + } + + { + let perm = mk_permission(fileuri1); + matches_always(perm, [fileuri1]); + matches_weak(perm, []); + matches_never(perm, [uri0, uri1, uri2, uri3, uri4, uri5, uri6, uri7, fileuri2, fileuri3]); + } + + { + let perm = mk_permission(fileuri2); + matches_always(perm, [fileuri2]); + matches_weak(perm, []); + matches_never(perm, [uri0, uri1, uri2, uri3, uri4, uri5, uri6, uri7, fileuri1, fileuri3]); + } + + { + let perm = mk_permission(fileuri3); + matches_always(perm, [fileuri3]); + matches_weak(perm, []); + matches_never(perm, [uri0, uri1, uri2, uri3, uri4, uri5, uri6, uri7, fileuri1, fileuri2]); + } + + // Clean up! + pm.removeAll(); +} diff --git a/extensions/cookie/test/unit/test_permmanager_migrate_4-7.js b/extensions/cookie/test/unit/test_permmanager_migrate_4-7.js new file mode 100644 index 000000000..4749422da --- /dev/null +++ b/extensions/cookie/test/unit/test_permmanager_migrate_4-7.js @@ -0,0 +1,207 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +XPCOMUtils.defineLazyModuleGetter(this, "PlacesTestUtils", + "resource://testing-common/PlacesTestUtils.jsm"); + +var PERMISSIONS_FILE_NAME = "permissions.sqlite"; + +function GetPermissionsFile(profile) +{ + let file = profile.clone(); + file.append(PERMISSIONS_FILE_NAME); + return file; +} + +function run_test() { + run_next_test(); +} + +add_task(function test() { + /* Create and set up the permissions database */ + let profile = do_get_profile(); + + let db = Services.storage.openDatabase(GetPermissionsFile(profile)); + db.schemaVersion = 4; + + db.executeSimpleSQL( + "CREATE TABLE moz_hosts (" + + " id INTEGER PRIMARY KEY" + + ",host TEXT" + + ",type TEXT" + + ",permission INTEGER" + + ",expireType INTEGER" + + ",expireTime INTEGER" + + ",modificationTime INTEGER" + + ",appId INTEGER" + + ",isInBrowserElement INTEGER" + + ")"); + + let stmtInsert = db.createStatement( + "INSERT INTO moz_hosts (" + + "id, host, type, permission, expireType, expireTime, modificationTime, appId, isInBrowserElement" + + ") VALUES (" + + ":id, :host, :type, :permission, :expireType, :expireTime, :modificationTime, :appId, :isInBrowserElement" + + ")"); + + let id = 0; + + function insertHost(host, type, permission, expireType, expireTime, modificationTime, appId, isInBrowserElement) { + let thisId = id++; + + stmtInsert.bindByName("id", thisId); + stmtInsert.bindByName("host", host); + stmtInsert.bindByName("type", type); + stmtInsert.bindByName("permission", permission); + stmtInsert.bindByName("expireType", expireType); + stmtInsert.bindByName("expireTime", expireTime); + stmtInsert.bindByName("modificationTime", modificationTime); + stmtInsert.bindByName("appId", appId); + stmtInsert.bindByName("isInBrowserElement", isInBrowserElement); + + stmtInsert.execute(); + + return { + id: thisId, + host: host, + type: type, + permission: permission, + expireType: expireType, + expireTime: expireTime, + modificationTime: modificationTime, + appId: appId, + isInBrowserElement: isInBrowserElement + }; + } + + // Add some rows to the database + let created = [ + insertHost("foo.com", "A", 1, 0, 0, 0, 0, false), + insertHost("foo.com", "C", 1, 0, 0, 0, 0, false), + insertHost("foo.com", "A", 1, 0, 0, 0, 1000, false), + insertHost("foo.com", "A", 1, 0, 0, 0, 2000, true), + insertHost("sub.foo.com", "B", 1, 0, 0, 0, 0, false), + insertHost("subber.sub.foo.com", "B", 1, 0, 0, 0, 0, false), + insertHost("bar.ca", "B", 1, 0, 0, 0, 0, false), + insertHost("bar.ca", "B", 1, 0, 0, 0, 1000, false), + insertHost("bar.ca", "A", 1, 0, 0, 0, 1000, true), + insertHost("localhost", "A", 1, 0, 0, 0, 0, false), + insertHost("127.0.0.1", "A", 1, 0, 0, 0, 0, false), + insertHost("192.0.2.235", "A", 1, 0, 0, 0, 0, false), + insertHost("file:///some/path/to/file.html", "A", 1, 0, 0, 0, 0, false), + insertHost("file:///another/file.html", "A", 1, 0, 0, 0, 0, false), + insertHost("moz-nullprincipal:{8695105a-adbe-4e4e-8083-851faa5ca2d7}", "A", 1, 0, 0, 0, 0, false), + insertHost("moz-nullprincipal:{12ahjksd-akjs-asd3-8393-asdu2189asdu}", "B", 1, 0, 0, 0, 0, false), + insertHost("<file>", "A", 1, 0, 0, 0, 0, false), + insertHost("<file>", "B", 1, 0, 0, 0, 0, false), + ]; + + // CLose the db connection + stmtInsert.finalize(); + db.close(); + stmtInsert = null; + db = null; + + let expected = [ + // The http:// entries under foo.com won't be inserted, as there are history entries for foo.com, + // and http://foo.com or a subdomain are never visited. + // ["http://foo.com", "A", 1, 0, 0], + // ["http://foo.com^appId=1000", "A", 1, 0, 0], + // ["http://foo.com^appId=2000&inBrowser=1", "A", 1, 0, 0], + // + // Because we search for port/scheme combinations under eTLD+1, we should not have http:// entries + // for subdomains of foo.com either + // ["http://sub.foo.com", "B", 1, 0, 0], + // ["http://subber.sub.foo.com", "B", 1, 0, 0], + + ["https://foo.com", "A", 1, 0, 0], + ["https://foo.com", "C", 1, 0, 0], + ["https://foo.com^appId=1000", "A", 1, 0, 0], + ["https://foo.com^appId=2000&inBrowser=1", "A", 1, 0, 0], + ["https://sub.foo.com", "B", 1, 0, 0], + ["https://subber.sub.foo.com", "B", 1, 0, 0], + + // bar.ca will have both http:// and https:// for all entries, because there are no associated history entries + ["http://bar.ca", "B", 1, 0, 0], + ["https://bar.ca", "B", 1, 0, 0], + ["http://bar.ca^appId=1000", "B", 1, 0, 0], + ["https://bar.ca^appId=1000", "B", 1, 0, 0], + ["http://bar.ca^appId=1000&inBrowser=1", "A", 1, 0, 0], + ["https://bar.ca^appId=1000&inBrowser=1", "A", 1, 0, 0], + ["file:///some/path/to/file.html", "A", 1, 0, 0], + ["file:///another/file.html", "A", 1, 0, 0], + + // Because we put ftp://some.subdomain.of.foo.com:8000/some/subdirectory in the history, we should + // also have these entries + ["ftp://foo.com:8000", "A", 1, 0, 0], + ["ftp://foo.com:8000", "C", 1, 0, 0], + ["ftp://foo.com:8000^appId=1000", "A", 1, 0, 0], + ["ftp://foo.com:8000^appId=2000&inBrowser=1", "A", 1, 0, 0], + + // In addition, because we search for port/scheme combinations under eTLD+1, we should have the + // following entries + ["ftp://sub.foo.com:8000", "B", 1, 0, 0], + ["ftp://subber.sub.foo.com:8000", "B", 1, 0, 0], + + // Make sure that we also support localhost, and IP addresses + ["http://localhost", "A", 1, 0, 0], + ["https://localhost", "A", 1, 0, 0], + ["http://127.0.0.1", "A", 1, 0, 0], + ["https://127.0.0.1", "A", 1, 0, 0], + ["http://192.0.2.235", "A", 1, 0, 0], + ["https://192.0.2.235", "A", 1, 0, 0], + ]; + + let found = expected.map((it) => 0); + + // Add some places to the places database + yield PlacesTestUtils.addVisits(Services.io.newURI("https://foo.com/some/other/subdirectory", null, null)); + yield PlacesTestUtils.addVisits(Services.io.newURI("ftp://some.subdomain.of.foo.com:8000/some/subdirectory", null, null)); + + // Force initialization of the nsPermissionManager + let enumerator = Services.perms.enumerator; + while (enumerator.hasMoreElements()) { + let permission = enumerator.getNext().QueryInterface(Ci.nsIPermission); + let isExpected = false; + + expected.forEach((it, i) => { + if (permission.principal.origin == it[0] && + permission.type == it[1] && + permission.capability == it[2] && + permission.expireType == it[3] && + permission.expireTime == it[4]) { + isExpected = true; + found[i]++; + } + }); + + do_check_true(isExpected, + "Permission " + (isExpected ? "should" : "shouldn't") + + " be in permission database: " + + permission.principal.origin + ", " + + permission.type + ", " + + permission.capability + ", " + + permission.expireType + ", " + + permission.expireTime); + } + + found.forEach((count, i) => { + do_check_true(count == 1, "Expected count = 1, got count = " + count + " for permission " + expected[i]); + }); + + // Check to make sure that all of the tables which we care about are present + { + let db = Services.storage.openDatabase(GetPermissionsFile(profile)); + do_check_true(db.tableExists("moz_perms")); + do_check_true(db.tableExists("moz_hosts")); + do_check_false(db.tableExists("moz_hosts_is_backup")); + do_check_false(db.tableExists("moz_perms_v6")); + + // The moz_hosts table should still exist but be empty + let mozHostsCount = db.createStatement("SELECT count(*) FROM moz_hosts"); + mozHostsCount.executeStep(); + do_check_eq(mozHostsCount.getInt64(0), 0); + + db.close(); + } +}); diff --git a/extensions/cookie/test/unit/test_permmanager_migrate_4-7_no_history.js b/extensions/cookie/test/unit/test_permmanager_migrate_4-7_no_history.js new file mode 100644 index 000000000..38646660a --- /dev/null +++ b/extensions/cookie/test/unit/test_permmanager_migrate_4-7_no_history.js @@ -0,0 +1,226 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +var PERMISSIONS_FILE_NAME = "permissions.sqlite"; + +/* + * Prevent the nsINavHistoryService from being avaliable for the migration + */ + +var CONTRACT_ID = "@mozilla.org/browser/nav-history-service;1"; +var factory = { + createInstance: function() { + throw new Error("There is no history service"); + }, + lockFactory: function() { + throw Components.results.NS_ERROR_NOT_IMPLEMENTED; + }, + QueryInterface: XPCOMUtils.generateQI([Ci.nsIFactory]) +}; + +var newClassID = Cc["@mozilla.org/uuid-generator;1"].getService(Ci.nsIUUIDGenerator).generateUUID(); + +var registrar = Components.manager.QueryInterface(Ci.nsIComponentRegistrar); +var oldClassID = registrar.contractIDToCID(CONTRACT_ID); +var oldFactory = Components.manager.getClassObject(Cc[CONTRACT_ID], Ci.nsIFactory); +registrar.unregisterFactory(oldClassID, oldFactory); +registrar.registerFactory(newClassID, "", CONTRACT_ID, factory); + +function cleanupFactory() { + registrar.unregisterFactory(newClassID, factory); + registrar.registerFactory(oldClassID, "", CONTRACT_ID, oldFactory); +} + +function GetPermissionsFile(profile) +{ + let file = profile.clone(); + file.append(PERMISSIONS_FILE_NAME); + return file; +} + +/* + * Done nsINavHistoryService code + */ + +function run_test() { + run_next_test(); +} + +add_task(function test() { + /* Create and set up the permissions database */ + let profile = do_get_profile(); + + // Make sure that we can't resolve the nsINavHistoryService + try { + Cc['@mozilla.org/browser/nav-history-service;1'].getService(Ci.nsINavHistoryService); + do_check_true(false, "There shouldn't have been a nsINavHistoryService"); + } catch (e) { + do_check_true(true, "There wasn't a nsINavHistoryService"); + } + + let db = Services.storage.openDatabase(GetPermissionsFile(profile)); + db.schemaVersion = 4; + + db.executeSimpleSQL( + "CREATE TABLE moz_hosts (" + + " id INTEGER PRIMARY KEY" + + ",host TEXT" + + ",type TEXT" + + ",permission INTEGER" + + ",expireType INTEGER" + + ",expireTime INTEGER" + + ",modificationTime INTEGER" + + ",appId INTEGER" + + ",isInBrowserElement INTEGER" + + ")"); + + let stmtInsert = db.createStatement( + "INSERT INTO moz_hosts (" + + "id, host, type, permission, expireType, expireTime, modificationTime, appId, isInBrowserElement" + + ") VALUES (" + + ":id, :host, :type, :permission, :expireType, :expireTime, :modificationTime, :appId, :isInBrowserElement" + + ")"); + + let id = 0; + + function insertHost(host, type, permission, expireType, expireTime, modificationTime, appId, isInBrowserElement) { + let thisId = id++; + + stmtInsert.bindByName("id", thisId); + stmtInsert.bindByName("host", host); + stmtInsert.bindByName("type", type); + stmtInsert.bindByName("permission", permission); + stmtInsert.bindByName("expireType", expireType); + stmtInsert.bindByName("expireTime", expireTime); + stmtInsert.bindByName("modificationTime", modificationTime); + stmtInsert.bindByName("appId", appId); + stmtInsert.bindByName("isInBrowserElement", isInBrowserElement); + + stmtInsert.execute(); + + return { + id: thisId, + host: host, + type: type, + permission: permission, + expireType: expireType, + expireTime: expireTime, + modificationTime: modificationTime, + appId: appId, + isInBrowserElement: isInBrowserElement + }; + } + + // Add some rows to the database + let created = [ + insertHost("foo.com", "A", 1, 0, 0, 0, 0, false), + insertHost("foo.com", "C", 1, 0, 0, 0, 0, false), + insertHost("foo.com", "A", 1, 0, 0, 0, 1000, false), + insertHost("foo.com", "A", 1, 0, 0, 0, 2000, true), + insertHost("sub.foo.com", "B", 1, 0, 0, 0, 0, false), + insertHost("subber.sub.foo.com", "B", 1, 0, 0, 0, 0, false), + insertHost("bar.ca", "B", 1, 0, 0, 0, 0, false), + insertHost("bar.ca", "B", 1, 0, 0, 0, 1000, false), + insertHost("bar.ca", "A", 1, 0, 0, 0, 1000, true), + insertHost("localhost", "A", 1, 0, 0, 0, 0, false), + insertHost("127.0.0.1", "A", 1, 0, 0, 0, 0, false), + insertHost("263.123.555.676", "A", 1, 0, 0, 0, 0, false), + insertHost("file:///some/path/to/file.html", "A", 1, 0, 0, 0, 0, false), + insertHost("file:///another/file.html", "A", 1, 0, 0, 0, 0, false), + insertHost("moz-nullprincipal:{8695105a-adbe-4e4e-8083-851faa5ca2d7}", "A", 1, 0, 0, 0, 0, false), + insertHost("moz-nullprincipal:{12ahjksd-akjs-asd3-8393-asdu2189asdu}", "B", 1, 0, 0, 0, 0, false), + insertHost("<file>", "A", 1, 0, 0, 0, 0, false), + insertHost("<file>", "B", 1, 0, 0, 0, 0, false), + ]; + + // CLose the db connection + stmtInsert.finalize(); + db.close(); + stmtInsert = null; + db = null; + + let expected = [ + ["http://foo.com", "A", 1, 0, 0], + ["http://foo.com", "C", 1, 0, 0], + ["http://foo.com^appId=1000", "A", 1, 0, 0], + ["http://foo.com^appId=2000&inBrowser=1", "A", 1, 0, 0], + ["http://sub.foo.com", "B", 1, 0, 0], + ["http://subber.sub.foo.com", "B", 1, 0, 0], + + ["https://foo.com", "A", 1, 0, 0], + ["https://foo.com", "C", 1, 0, 0], + ["https://foo.com^appId=1000", "A", 1, 0, 0], + ["https://foo.com^appId=2000&inBrowser=1", "A", 1, 0, 0], + ["https://sub.foo.com", "B", 1, 0, 0], + ["https://subber.sub.foo.com", "B", 1, 0, 0], + + // bar.ca will have both http:// and https:// for all entries, because there are no associated history entries + ["http://bar.ca", "B", 1, 0, 0], + ["https://bar.ca", "B", 1, 0, 0], + ["http://bar.ca^appId=1000", "B", 1, 0, 0], + ["https://bar.ca^appId=1000", "B", 1, 0, 0], + ["http://bar.ca^appId=1000&inBrowser=1", "A", 1, 0, 0], + ["https://bar.ca^appId=1000&inBrowser=1", "A", 1, 0, 0], + ["file:///some/path/to/file.html", "A", 1, 0, 0], + ["file:///another/file.html", "A", 1, 0, 0], + + // Make sure that we also support localhost, and IP addresses + ["http://localhost", "A", 1, 0, 0], + ["https://localhost", "A", 1, 0, 0], + ["http://127.0.0.1", "A", 1, 0, 0], + ["https://127.0.0.1", "A", 1, 0, 0], + ["http://263.123.555.676", "A", 1, 0, 0], + ["https://263.123.555.676", "A", 1, 0, 0], + ]; + + let found = expected.map((it) => 0); + + // Force initialization of the nsPermissionManager + let enumerator = Services.perms.enumerator; + while (enumerator.hasMoreElements()) { + let permission = enumerator.getNext().QueryInterface(Ci.nsIPermission); + let isExpected = false; + + expected.forEach((it, i) => { + if (permission.principal.origin == it[0] && + permission.type == it[1] && + permission.capability == it[2] && + permission.expireType == it[3] && + permission.expireTime == it[4]) { + isExpected = true; + found[i]++; + } + }); + + do_check_true(isExpected, + "Permission " + (isExpected ? "should" : "shouldn't") + + " be in permission database: " + + permission.principal.origin + ", " + + permission.type + ", " + + permission.capability + ", " + + permission.expireType + ", " + + permission.expireTime); + } + + found.forEach((count, i) => { + do_check_true(count == 1, "Expected count = 1, got count = " + count + " for permission " + expected[i]); + }); + + // Check to make sure that all of the tables which we care about are present + { + let db = Services.storage.openDatabase(GetPermissionsFile(profile)); + do_check_true(db.tableExists("moz_perms")); + do_check_true(db.tableExists("moz_hosts")); + do_check_false(db.tableExists("moz_hosts_is_backup")); + do_check_false(db.tableExists("moz_perms_v6")); + + // The moz_hosts table should still exist but be empty + let mozHostsCount = db.createStatement("SELECT count(*) FROM moz_hosts"); + mozHostsCount.executeStep(); + do_check_eq(mozHostsCount.getInt64(0), 0); + + db.close(); + } + + cleanupFactory(); +}); diff --git a/extensions/cookie/test/unit/test_permmanager_migrate_5-7a.js b/extensions/cookie/test/unit/test_permmanager_migrate_5-7a.js new file mode 100644 index 000000000..4d38d72e8 --- /dev/null +++ b/extensions/cookie/test/unit/test_permmanager_migrate_5-7a.js @@ -0,0 +1,284 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +XPCOMUtils.defineLazyModuleGetter(this, "PlacesTestUtils", + "resource://testing-common/PlacesTestUtils.jsm"); + +var PERMISSIONS_FILE_NAME = "permissions.sqlite"; + +function GetPermissionsFile(profile) +{ + let file = profile.clone(); + file.append(PERMISSIONS_FILE_NAME); + return file; +} + +function run_test() { + run_next_test(); +} + +add_task(function test() { + /* Create and set up the permissions database */ + let profile = do_get_profile(); + + let db = Services.storage.openDatabase(GetPermissionsFile(profile)); + db.schemaVersion = 5; + + /* + * V5 table + */ + db.executeSimpleSQL( + "CREATE TABLE moz_hosts (" + + " id INTEGER PRIMARY KEY" + + ",origin TEXT" + + ",type TEXT" + + ",permission INTEGER" + + ",expireType INTEGER" + + ",expireTime INTEGER" + + ",modificationTime INTEGER" + + ")"); + + let stmt5Insert = db.createStatement( + "INSERT INTO moz_hosts (" + + "id, origin, type, permission, expireType, expireTime, modificationTime" + + ") VALUES (" + + ":id, :origin, :type, :permission, :expireType, :expireTime, :modificationTime" + + ")"); + + /* + * V4 table + */ + db.executeSimpleSQL( + "CREATE TABLE moz_hosts_v4 (" + + " id INTEGER PRIMARY KEY" + + ",host TEXT" + + ",type TEXT" + + ",permission INTEGER" + + ",expireType INTEGER" + + ",expireTime INTEGER" + + ",modificationTime INTEGER" + + ",appId INTEGER" + + ",isInBrowserElement INTEGER" + + ")"); + + let stmtInsert = db.createStatement( + "INSERT INTO moz_hosts_v4 (" + + "id, host, type, permission, expireType, expireTime, modificationTime, appId, isInBrowserElement" + + ") VALUES (" + + ":id, :host, :type, :permission, :expireType, :expireTime, :modificationTime, :appId, :isInBrowserElement" + + ")"); + + let id = 0; + + function insertOrigin(origin, type, permission, expireType, expireTime, modificationTime) { + let thisId = id++; + + stmt5Insert.bindByName("id", thisId); + stmt5Insert.bindByName("origin", origin); + stmt5Insert.bindByName("type", type); + stmt5Insert.bindByName("permission", permission); + stmt5Insert.bindByName("expireType", expireType); + stmt5Insert.bindByName("expireTime", expireTime); + stmt5Insert.bindByName("modificationTime", modificationTime); + + stmt5Insert.execute(); + + return { + id: thisId, + origin: origin, + type: type, + permission: permission, + expireType: expireType, + expireTime: expireTime, + modificationTime: modificationTime + }; + } + + function insertHost(host, type, permission, expireType, expireTime, modificationTime, appId, isInBrowserElement) { + let thisId = id++; + + stmtInsert.bindByName("id", thisId); + stmtInsert.bindByName("host", host); + stmtInsert.bindByName("type", type); + stmtInsert.bindByName("permission", permission); + stmtInsert.bindByName("expireType", expireType); + stmtInsert.bindByName("expireTime", expireTime); + stmtInsert.bindByName("modificationTime", modificationTime); + stmtInsert.bindByName("appId", appId); + stmtInsert.bindByName("isInBrowserElement", isInBrowserElement); + + stmtInsert.execute(); + + return { + id: thisId, + host: host, + type: type, + permission: permission, + expireType: expireType, + expireTime: expireTime, + modificationTime: modificationTime, + appId: appId, + isInBrowserElement: isInBrowserElement + }; + } + + let created5 = [ + insertOrigin("https://foo.com", "A", 2, 0, 0, 0), + insertOrigin("http://foo.com", "A", 2, 0, 0, 0), + insertOrigin("http://foo.com^appId=1000&inBrowser=1", "A", 2, 0, 0, 0), + ]; + + // Add some rows to the database + let created = [ + insertHost("foo.com", "A", 1, 0, 0, 0, 0, false), + insertHost("foo.com", "C", 1, 0, 0, 0, 0, false), + insertHost("foo.com", "A", 1, 0, 0, 0, 1000, false), + insertHost("foo.com", "A", 1, 0, 0, 0, 2000, true), + insertHost("sub.foo.com", "B", 1, 0, 0, 0, 0, false), + insertHost("subber.sub.foo.com", "B", 1, 0, 0, 0, 0, false), + insertHost("bar.ca", "B", 1, 0, 0, 0, 0, false), + insertHost("bar.ca", "B", 1, 0, 0, 0, 1000, false), + insertHost("bar.ca", "A", 1, 0, 0, 0, 1000, true), + insertHost("localhost", "A", 1, 0, 0, 0, 0, false), + insertHost("127.0.0.1", "A", 1, 0, 0, 0, 0, false), + insertHost("192.0.2.235", "A", 1, 0, 0, 0, 0, false), + insertHost("file:///some/path/to/file.html", "A", 1, 0, 0, 0, 0, false), + insertHost("file:///another/file.html", "A", 1, 0, 0, 0, 0, false), + insertHost("moz-nullprincipal:{8695105a-adbe-4e4e-8083-851faa5ca2d7}", "A", 1, 0, 0, 0, 0, false), + insertHost("moz-nullprincipal:{12ahjksd-akjs-asd3-8393-asdu2189asdu}", "B", 1, 0, 0, 0, 0, false), + insertHost("<file>", "A", 1, 0, 0, 0, 0, false), + insertHost("<file>", "B", 1, 0, 0, 0, 0, false), + ]; + + // CLose the db connection + stmtInsert.finalize(); + db.close(); + stmtInsert = null; + db = null; + + let expected = [ + // The http:// entries under foo.com won't be inserted, as there are history entries for foo.com, + // and http://foo.com or a subdomain are never visited. + // ["http://foo.com", "A", 1, 0, 0], + // ["http://foo.com^appId=1000", "A", 1, 0, 0], + // ["http://foo.com^appId=2000&inBrowser=1", "A", 1, 0, 0], + // + // Because we search for port/scheme combinations under eTLD+1, we should not have http:// entries + // for subdomains of foo.com either + // ["http://sub.foo.com", "B", 1, 0, 0], + // ["http://subber.sub.foo.com", "B", 1, 0, 0], + + ["https://foo.com", "A", 1, 0, 0], + ["https://foo.com", "C", 1, 0, 0], + ["https://foo.com^appId=1000", "A", 1, 0, 0], + ["https://foo.com^appId=2000&inBrowser=1", "A", 1, 0, 0], + ["https://sub.foo.com", "B", 1, 0, 0], + ["https://subber.sub.foo.com", "B", 1, 0, 0], + + // bar.ca will have both http:// and https:// for all entries, because there are no associated history entries + ["http://bar.ca", "B", 1, 0, 0], + ["https://bar.ca", "B", 1, 0, 0], + ["http://bar.ca^appId=1000", "B", 1, 0, 0], + ["https://bar.ca^appId=1000", "B", 1, 0, 0], + ["http://bar.ca^appId=1000&inBrowser=1", "A", 1, 0, 0], + ["https://bar.ca^appId=1000&inBrowser=1", "A", 1, 0, 0], + ["file:///some/path/to/file.html", "A", 1, 0, 0], + ["file:///another/file.html", "A", 1, 0, 0], + + // Because we put ftp://some.subdomain.of.foo.com:8000/some/subdirectory in the history, we should + // also have these entries + ["ftp://foo.com:8000", "A", 1, 0, 0], + ["ftp://foo.com:8000", "C", 1, 0, 0], + ["ftp://foo.com:8000^appId=1000", "A", 1, 0, 0], + ["ftp://foo.com:8000^appId=2000&inBrowser=1", "A", 1, 0, 0], + + // In addition, because we search for port/scheme combinations under eTLD+1, we should have the + // following entries + ["ftp://sub.foo.com:8000", "B", 1, 0, 0], + ["ftp://subber.sub.foo.com:8000", "B", 1, 0, 0], + + // Make sure that we also support localhost, and IP addresses + ["http://localhost", "A", 1, 0, 0], + ["https://localhost", "A", 1, 0, 0], + ["http://127.0.0.1", "A", 1, 0, 0], + ["https://127.0.0.1", "A", 1, 0, 0], + ["http://192.0.2.235", "A", 1, 0, 0], + ["https://192.0.2.235", "A", 1, 0, 0], + ]; + + let found = expected.map((it) => 0); + + // Add some places to the places database + yield PlacesTestUtils.addVisits(Services.io.newURI("https://foo.com/some/other/subdirectory", null, null)); + yield PlacesTestUtils.addVisits(Services.io.newURI("ftp://some.subdomain.of.foo.com:8000/some/subdirectory", null, null)); + + // Force initialization of the nsPermissionManager + let enumerator = Services.perms.enumerator; + while (enumerator.hasMoreElements()) { + let permission = enumerator.getNext().QueryInterface(Ci.nsIPermission); + let isExpected = false; + + expected.forEach((it, i) => { + if (permission.principal.origin == it[0] && + permission.type == it[1] && + permission.capability == it[2] && + permission.expireType == it[3] && + permission.expireTime == it[4]) { + isExpected = true; + found[i]++; + } + }); + + do_check_true(isExpected, + "Permission " + (isExpected ? "should" : "shouldn't") + + " be in permission database: " + + permission.principal.origin + ", " + + permission.type + ", " + + permission.capability + ", " + + permission.expireType + ", " + + permission.expireTime); + } + + found.forEach((count, i) => { + do_check_true(count == 1, "Expected count = 1, got count = " + count + " for permission " + expected[i]); + }); + + // Check to make sure that all of the tables which we care about are present + { + let db = Services.storage.openDatabase(GetPermissionsFile(profile)); + do_check_true(db.tableExists("moz_perms")); + do_check_true(db.tableExists("moz_hosts")); + do_check_false(db.tableExists("moz_hosts_is_backup")); + do_check_true(db.tableExists("moz_perms_v6")); + + // The moz_hosts table should still exist but be empty + let mozHostsCount = db.createStatement("SELECT count(*) FROM moz_hosts"); + mozHostsCount.executeStep(); + do_check_eq(mozHostsCount.getInt64(0), 0); + + // Check that the moz_perms_v6 table contains the backup of the entry we created + let mozPermsV6Stmt = db.createStatement("SELECT " + + "origin, type, permission, expireType, expireTime, modificationTime " + + "FROM moz_perms_v6 WHERE id = :id"); + + // Check that the moz_hosts table still contains the correct values. + created5.forEach((it) => { + mozPermsV6Stmt.reset(); + mozPermsV6Stmt.bindByName("id", it.id); + mozPermsV6Stmt.executeStep(); + do_check_eq(mozPermsV6Stmt.getUTF8String(0), it.origin); + do_check_eq(mozPermsV6Stmt.getUTF8String(1), it.type); + do_check_eq(mozPermsV6Stmt.getInt64(2), it.permission); + do_check_eq(mozPermsV6Stmt.getInt64(3), it.expireType); + do_check_eq(mozPermsV6Stmt.getInt64(4), it.expireTime); + do_check_eq(mozPermsV6Stmt.getInt64(5), it.modificationTime); + }); + + // Check that there are the right number of values + let mozPermsV6Count = db.createStatement("SELECT count(*) FROM moz_perms_v6"); + mozPermsV6Count.executeStep(); + do_check_eq(mozPermsV6Count.getInt64(0), created5.length); + + db.close(); + } +}); diff --git a/extensions/cookie/test/unit/test_permmanager_migrate_5-7b.js b/extensions/cookie/test/unit/test_permmanager_migrate_5-7b.js new file mode 100644 index 000000000..0e5820d2b --- /dev/null +++ b/extensions/cookie/test/unit/test_permmanager_migrate_5-7b.js @@ -0,0 +1,168 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +XPCOMUtils.defineLazyModuleGetter(this, "PlacesTestUtils", + "resource://testing-common/PlacesTestUtils.jsm"); + +var PERMISSIONS_FILE_NAME = "permissions.sqlite"; + +function GetPermissionsFile(profile) +{ + let file = profile.clone(); + file.append(PERMISSIONS_FILE_NAME); + return file; +} + +function run_test() { + run_next_test(); +} + +add_task(function test() { + /* Create and set up the permissions database */ + let profile = do_get_profile(); + + let db = Services.storage.openDatabase(GetPermissionsFile(profile)); + db.schemaVersion = 5; + + /* + * V5 table + */ + db.executeSimpleSQL( + "CREATE TABLE moz_hosts (" + + " id INTEGER PRIMARY KEY" + + ",origin TEXT" + + ",type TEXT" + + ",permission INTEGER" + + ",expireType INTEGER" + + ",expireTime INTEGER" + + ",modificationTime INTEGER" + + ")"); + + let stmt5Insert = db.createStatement( + "INSERT INTO moz_hosts (" + + "id, origin, type, permission, expireType, expireTime, modificationTime" + + ") VALUES (" + + ":id, :origin, :type, :permission, :expireType, :expireTime, :modificationTime" + + ")"); + + let id = 0; + + function insertOrigin(origin, type, permission, expireType, expireTime, modificationTime) { + let thisId = id++; + + stmt5Insert.bindByName("id", thisId); + stmt5Insert.bindByName("origin", origin); + stmt5Insert.bindByName("type", type); + stmt5Insert.bindByName("permission", permission); + stmt5Insert.bindByName("expireType", expireType); + stmt5Insert.bindByName("expireTime", expireTime); + stmt5Insert.bindByName("modificationTime", modificationTime); + + stmt5Insert.execute(); + + return { + id: thisId, + host: origin, + type: type, + permission: permission, + expireType: expireType, + expireTime: expireTime, + modificationTime: modificationTime + }; + } + + let created5 = [ + insertOrigin("https://foo.com", "A", 2, 0, 0, 0), + insertOrigin("http://foo.com", "A", 2, 0, 0, 0), + insertOrigin("http://foo.com^appId=1000&inBrowser=1", "A", 2, 0, 0, 0), + + insertOrigin("http://127.0.0.1", "B", 2, 0, 0, 0), + insertOrigin("http://localhost", "B", 2, 0, 0, 0), + ]; + + let created4 = []; // Didn't create any v4 entries, so the DB should be empty + + // CLose the db connection + stmt5Insert.finalize(); + db.close(); + stmt5Insert = null; + db = null; + + let expected = [ + ["https://foo.com", "A", 2, 0, 0, 0], + ["http://foo.com", "A", 2, 0, 0, 0], + ["http://foo.com^appId=1000&inBrowser=1", "A", 2, 0, 0, 0], + + ["http://127.0.0.1", "B", 2, 0, 0, 0], + ["http://localhost", "B", 2, 0, 0, 0], + ]; + + let found = expected.map((it) => 0); + + // Force initialization of the nsPermissionManager + let enumerator = Services.perms.enumerator; + while (enumerator.hasMoreElements()) { + let permission = enumerator.getNext().QueryInterface(Ci.nsIPermission); + let isExpected = false; + + expected.forEach((it, i) => { + if (permission.principal.origin == it[0] && + permission.type == it[1] && + permission.capability == it[2] && + permission.expireType == it[3] && + permission.expireTime == it[4]) { + isExpected = true; + found[i]++; + } + }); + + do_check_true(isExpected, + "Permission " + (isExpected ? "should" : "shouldn't") + + " be in permission database: " + + permission.principal.origin + ", " + + permission.type + ", " + + permission.capability + ", " + + permission.expireType + ", " + + permission.expireTime); + } + + found.forEach((count, i) => { + do_check_true(count == 1, "Expected count = 1, got count = " + count + " for permission " + expected[i]); + }); + + // Check to make sure that all of the tables which we care about are present + { + let db = Services.storage.openDatabase(GetPermissionsFile(profile)); + do_check_true(db.tableExists("moz_perms")); + do_check_true(db.tableExists("moz_hosts")); + do_check_false(db.tableExists("moz_hosts_is_backup")); + do_check_false(db.tableExists("moz_perms_v6")); + + let mozHostsStmt = db.createStatement("SELECT " + + "host, type, permission, expireType, expireTime, " + + "modificationTime, appId, isInBrowserElement " + + "FROM moz_hosts WHERE id = :id"); + + // Check that the moz_hosts table still contains the correct values. + created4.forEach((it) => { + mozHostsStmt.reset(); + mozHostsStmt.bindByName("id", it.id); + mozHostsStmt.executeStep(); + do_check_eq(mozHostsStmt.getUTF8String(0), it.host); + do_check_eq(mozHostsStmt.getUTF8String(1), it.type); + do_check_eq(mozHostsStmt.getInt64(2), it.permission); + do_check_eq(mozHostsStmt.getInt64(3), it.expireType); + do_check_eq(mozHostsStmt.getInt64(4), it.expireTime); + do_check_eq(mozHostsStmt.getInt64(5), it.modificationTime); + do_check_eq(mozHostsStmt.getInt64(6), it.appId); + do_check_eq(mozHostsStmt.getInt64(7), it.isInBrowserElement); + }); + + // Check that there are the right number of values + let mozHostsCount = db.createStatement("SELECT count(*) FROM moz_hosts"); + mozHostsCount.executeStep(); + do_check_eq(mozHostsCount.getInt64(0), created4.length); + + db.close(); + } +}); diff --git a/extensions/cookie/test/unit/test_permmanager_migrate_6-7a.js b/extensions/cookie/test/unit/test_permmanager_migrate_6-7a.js new file mode 100644 index 000000000..aa634c7eb --- /dev/null +++ b/extensions/cookie/test/unit/test_permmanager_migrate_6-7a.js @@ -0,0 +1,284 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +XPCOMUtils.defineLazyModuleGetter(this, "PlacesTestUtils", + "resource://testing-common/PlacesTestUtils.jsm"); + +var PERMISSIONS_FILE_NAME = "permissions.sqlite"; + +function GetPermissionsFile(profile) +{ + let file = profile.clone(); + file.append(PERMISSIONS_FILE_NAME); + return file; +} + +function run_test() { + run_next_test(); +} + +add_task(function test() { + /* Create and set up the permissions database */ + let profile = do_get_profile(); + + let db = Services.storage.openDatabase(GetPermissionsFile(profile)); + db.schemaVersion = 6; + + /* + * V5 table + */ + db.executeSimpleSQL( + "CREATE TABLE moz_perms (" + + " id INTEGER PRIMARY KEY" + + ",origin TEXT" + + ",type TEXT" + + ",permission INTEGER" + + ",expireType INTEGER" + + ",expireTime INTEGER" + + ",modificationTime INTEGER" + + ")"); + + let stmt6Insert = db.createStatement( + "INSERT INTO moz_perms (" + + "id, origin, type, permission, expireType, expireTime, modificationTime" + + ") VALUES (" + + ":id, :origin, :type, :permission, :expireType, :expireTime, :modificationTime" + + ")"); + + /* + * V4 table + */ + db.executeSimpleSQL( + "CREATE TABLE moz_hosts (" + + " id INTEGER PRIMARY KEY" + + ",host TEXT" + + ",type TEXT" + + ",permission INTEGER" + + ",expireType INTEGER" + + ",expireTime INTEGER" + + ",modificationTime INTEGER" + + ",appId INTEGER" + + ",isInBrowserElement INTEGER" + + ")"); + + let stmtInsert = db.createStatement( + "INSERT INTO moz_hosts (" + + "id, host, type, permission, expireType, expireTime, modificationTime, appId, isInBrowserElement" + + ") VALUES (" + + ":id, :host, :type, :permission, :expireType, :expireTime, :modificationTime, :appId, :isInBrowserElement" + + ")"); + + let id = 0; + + function insertOrigin(origin, type, permission, expireType, expireTime, modificationTime) { + let thisId = id++; + + stmt6Insert.bindByName("id", thisId); + stmt6Insert.bindByName("origin", origin); + stmt6Insert.bindByName("type", type); + stmt6Insert.bindByName("permission", permission); + stmt6Insert.bindByName("expireType", expireType); + stmt6Insert.bindByName("expireTime", expireTime); + stmt6Insert.bindByName("modificationTime", modificationTime); + + stmt6Insert.execute(); + + return { + id: thisId, + origin: origin, + type: type, + permission: permission, + expireType: expireType, + expireTime: expireTime, + modificationTime: modificationTime + }; + } + + function insertHost(host, type, permission, expireType, expireTime, modificationTime, appId, isInBrowserElement) { + let thisId = id++; + + stmtInsert.bindByName("id", thisId); + stmtInsert.bindByName("host", host); + stmtInsert.bindByName("type", type); + stmtInsert.bindByName("permission", permission); + stmtInsert.bindByName("expireType", expireType); + stmtInsert.bindByName("expireTime", expireTime); + stmtInsert.bindByName("modificationTime", modificationTime); + stmtInsert.bindByName("appId", appId); + stmtInsert.bindByName("isInBrowserElement", isInBrowserElement); + + stmtInsert.execute(); + + return { + id: thisId, + host: host, + type: type, + permission: permission, + expireType: expireType, + expireTime: expireTime, + modificationTime: modificationTime, + appId: appId, + isInBrowserElement: isInBrowserElement + }; + } + + let created6 = [ + insertOrigin("https://foo.com", "A", 2, 0, 0, 0), + insertOrigin("http://foo.com", "A", 2, 0, 0, 0), + insertOrigin("http://foo.com^appId=1000&inBrowser=1", "A", 2, 0, 0, 0), + ]; + + // Add some rows to the database + let created = [ + insertHost("foo.com", "A", 1, 0, 0, 0, 0, false), + insertHost("foo.com", "C", 1, 0, 0, 0, 0, false), + insertHost("foo.com", "A", 1, 0, 0, 0, 1000, false), + insertHost("foo.com", "A", 1, 0, 0, 0, 2000, true), + insertHost("sub.foo.com", "B", 1, 0, 0, 0, 0, false), + insertHost("subber.sub.foo.com", "B", 1, 0, 0, 0, 0, false), + insertHost("bar.ca", "B", 1, 0, 0, 0, 0, false), + insertHost("bar.ca", "B", 1, 0, 0, 0, 1000, false), + insertHost("bar.ca", "A", 1, 0, 0, 0, 1000, true), + insertHost("localhost", "A", 1, 0, 0, 0, 0, false), + insertHost("127.0.0.1", "A", 1, 0, 0, 0, 0, false), + insertHost("192.0.2.235", "A", 1, 0, 0, 0, 0, false), + insertHost("file:///some/path/to/file.html", "A", 1, 0, 0, 0, 0, false), + insertHost("file:///another/file.html", "A", 1, 0, 0, 0, 0, false), + insertHost("moz-nullprincipal:{8695105a-adbe-4e4e-8083-851faa5ca2d7}", "A", 1, 0, 0, 0, 0, false), + insertHost("moz-nullprincipal:{12ahjksd-akjs-asd3-8393-asdu2189asdu}", "B", 1, 0, 0, 0, 0, false), + insertHost("<file>", "A", 1, 0, 0, 0, 0, false), + insertHost("<file>", "B", 1, 0, 0, 0, 0, false), + ]; + + // CLose the db connection + stmtInsert.finalize(); + db.close(); + stmtInsert = null; + db = null; + + let expected = [ + // The http:// entries under foo.com won't be inserted, as there are history entries for foo.com, + // and http://foo.com or a subdomain are never visited. + // ["http://foo.com", "A", 1, 0, 0], + // ["http://foo.com^appId=1000", "A", 1, 0, 0], + // ["http://foo.com^appId=2000&inBrowser=1", "A", 1, 0, 0], + // + // Because we search for port/scheme combinations under eTLD+1, we should not have http:// entries + // for subdomains of foo.com either + // ["http://sub.foo.com", "B", 1, 0, 0], + // ["http://subber.sub.foo.com", "B", 1, 0, 0], + + ["https://foo.com", "A", 1, 0, 0], + ["https://foo.com", "C", 1, 0, 0], + ["https://foo.com^appId=1000", "A", 1, 0, 0], + ["https://foo.com^appId=2000&inBrowser=1", "A", 1, 0, 0], + ["https://sub.foo.com", "B", 1, 0, 0], + ["https://subber.sub.foo.com", "B", 1, 0, 0], + + // bar.ca will have both http:// and https:// for all entries, because there are no associated history entries + ["http://bar.ca", "B", 1, 0, 0], + ["https://bar.ca", "B", 1, 0, 0], + ["http://bar.ca^appId=1000", "B", 1, 0, 0], + ["https://bar.ca^appId=1000", "B", 1, 0, 0], + ["http://bar.ca^appId=1000&inBrowser=1", "A", 1, 0, 0], + ["https://bar.ca^appId=1000&inBrowser=1", "A", 1, 0, 0], + ["file:///some/path/to/file.html", "A", 1, 0, 0], + ["file:///another/file.html", "A", 1, 0, 0], + + // Because we put ftp://some.subdomain.of.foo.com:8000/some/subdirectory in the history, we should + // also have these entries + ["ftp://foo.com:8000", "A", 1, 0, 0], + ["ftp://foo.com:8000", "C", 1, 0, 0], + ["ftp://foo.com:8000^appId=1000", "A", 1, 0, 0], + ["ftp://foo.com:8000^appId=2000&inBrowser=1", "A", 1, 0, 0], + + // In addition, because we search for port/scheme combinations under eTLD+1, we should have the + // following entries + ["ftp://sub.foo.com:8000", "B", 1, 0, 0], + ["ftp://subber.sub.foo.com:8000", "B", 1, 0, 0], + + // Make sure that we also support localhost, and IP addresses + ["http://localhost", "A", 1, 0, 0], + ["https://localhost", "A", 1, 0, 0], + ["http://127.0.0.1", "A", 1, 0, 0], + ["https://127.0.0.1", "A", 1, 0, 0], + ["http://192.0.2.235", "A", 1, 0, 0], + ["https://192.0.2.235", "A", 1, 0, 0], + ]; + + let found = expected.map((it) => 0); + + // Add some places to the places database + yield PlacesTestUtils.addVisits(Services.io.newURI("https://foo.com/some/other/subdirectory", null, null)); + yield PlacesTestUtils.addVisits(Services.io.newURI("ftp://some.subdomain.of.foo.com:8000/some/subdirectory", null, null)); + + // Force initialization of the nsPermissionManager + let enumerator = Services.perms.enumerator; + while (enumerator.hasMoreElements()) { + let permission = enumerator.getNext().QueryInterface(Ci.nsIPermission); + let isExpected = false; + + expected.forEach((it, i) => { + if (permission.principal.origin == it[0] && + permission.type == it[1] && + permission.capability == it[2] && + permission.expireType == it[3] && + permission.expireTime == it[4]) { + isExpected = true; + found[i]++; + } + }); + + do_check_true(isExpected, + "Permission " + (isExpected ? "should" : "shouldn't") + + " be in permission database: " + + permission.principal.origin + ", " + + permission.type + ", " + + permission.capability + ", " + + permission.expireType + ", " + + permission.expireTime); + } + + found.forEach((count, i) => { + do_check_true(count == 1, "Expected count = 1, got count = " + count + " for permission " + expected[i]); + }); + + // Check to make sure that all of the tables which we care about are present + { + let db = Services.storage.openDatabase(GetPermissionsFile(profile)); + do_check_true(db.tableExists("moz_perms")); + do_check_true(db.tableExists("moz_hosts")); + do_check_false(db.tableExists("moz_hosts_is_backup")); + do_check_true(db.tableExists("moz_perms_v6")); + + // The moz_hosts table should still exist but be empty + let mozHostsCount = db.createStatement("SELECT count(*) FROM moz_hosts"); + mozHostsCount.executeStep(); + do_check_eq(mozHostsCount.getInt64(0), 0); + + // Check that the moz_perms_v6 table contains the backup of the entry we created + let mozPermsV6Stmt = db.createStatement("SELECT " + + "origin, type, permission, expireType, expireTime, modificationTime " + + "FROM moz_perms_v6 WHERE id = :id"); + + // Check that the moz_hosts table still contains the correct values. + created6.forEach((it) => { + mozPermsV6Stmt.reset(); + mozPermsV6Stmt.bindByName("id", it.id); + mozPermsV6Stmt.executeStep(); + do_check_eq(mozPermsV6Stmt.getUTF8String(0), it.origin); + do_check_eq(mozPermsV6Stmt.getUTF8String(1), it.type); + do_check_eq(mozPermsV6Stmt.getInt64(2), it.permission); + do_check_eq(mozPermsV6Stmt.getInt64(3), it.expireType); + do_check_eq(mozPermsV6Stmt.getInt64(4), it.expireTime); + do_check_eq(mozPermsV6Stmt.getInt64(5), it.modificationTime); + }); + + // Check that there are the right number of values + let mozPermsV6Count = db.createStatement("SELECT count(*) FROM moz_perms_v6"); + mozPermsV6Count.executeStep(); + do_check_eq(mozPermsV6Count.getInt64(0), created6.length); + + db.close(); + } +}); diff --git a/extensions/cookie/test/unit/test_permmanager_migrate_6-7b.js b/extensions/cookie/test/unit/test_permmanager_migrate_6-7b.js new file mode 100644 index 000000000..7b138ff29 --- /dev/null +++ b/extensions/cookie/test/unit/test_permmanager_migrate_6-7b.js @@ -0,0 +1,162 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +XPCOMUtils.defineLazyModuleGetter(this, "PlacesTestUtils", + "resource://testing-common/PlacesTestUtils.jsm"); + +var PERMISSIONS_FILE_NAME = "permissions.sqlite"; + +function GetPermissionsFile(profile) +{ + let file = profile.clone(); + file.append(PERMISSIONS_FILE_NAME); + return file; +} + +function run_test() { + run_next_test(); +} + +add_task(function test() { + /* Create and set up the permissions database */ + let profile = do_get_profile(); + + let db = Services.storage.openDatabase(GetPermissionsFile(profile)); + db.schemaVersion = 6; + + /* + * V5 table + */ + db.executeSimpleSQL( + "CREATE TABLE moz_perms (" + + " id INTEGER PRIMARY KEY" + + ",origin TEXT" + + ",type TEXT" + + ",permission INTEGER" + + ",expireType INTEGER" + + ",expireTime INTEGER" + + ",modificationTime INTEGER" + + ")"); + + let stmt6Insert = db.createStatement( + "INSERT INTO moz_perms (" + + "id, origin, type, permission, expireType, expireTime, modificationTime" + + ") VALUES (" + + ":id, :origin, :type, :permission, :expireType, :expireTime, :modificationTime" + + ")"); + + let id = 0; + + function insertOrigin(origin, type, permission, expireType, expireTime, modificationTime) { + let thisId = id++; + + stmt6Insert.bindByName("id", thisId); + stmt6Insert.bindByName("origin", origin); + stmt6Insert.bindByName("type", type); + stmt6Insert.bindByName("permission", permission); + stmt6Insert.bindByName("expireType", expireType); + stmt6Insert.bindByName("expireTime", expireTime); + stmt6Insert.bindByName("modificationTime", modificationTime); + + stmt6Insert.execute(); + + return { + id: thisId, + host: origin, + type: type, + permission: permission, + expireType: expireType, + expireTime: expireTime, + modificationTime: modificationTime + }; + } + + let created6 = [ + insertOrigin("https://foo.com", "A", 2, 0, 0, 0), + insertOrigin("http://foo.com", "A", 2, 0, 0, 0), + insertOrigin("http://foo.com^appId=1000&inBrowser=1", "A", 2, 0, 0, 0), + ]; + + let created4 = []; // Didn't create any v4 entries, so the DB should be empty + + // CLose the db connection + stmt6Insert.finalize(); + db.close(); + stmt6Insert = null; + db = null; + + let expected = [ + ["https://foo.com", "A", 2, 0, 0, 0], + ["http://foo.com", "A", 2, 0, 0, 0], + ["http://foo.com^appId=1000&inBrowser=1", "A", 2, 0, 0, 0] + ]; + + let found = expected.map((it) => 0); + + // Force initialization of the nsPermissionManager + let enumerator = Services.perms.enumerator; + while (enumerator.hasMoreElements()) { + let permission = enumerator.getNext().QueryInterface(Ci.nsIPermission); + let isExpected = false; + + expected.forEach((it, i) => { + if (permission.principal.origin == it[0] && + permission.type == it[1] && + permission.capability == it[2] && + permission.expireType == it[3] && + permission.expireTime == it[4]) { + isExpected = true; + found[i]++; + } + }); + + do_check_true(isExpected, + "Permission " + (isExpected ? "should" : "shouldn't") + + " be in permission database: " + + permission.principal.origin + ", " + + permission.type + ", " + + permission.capability + ", " + + permission.expireType + ", " + + permission.expireTime); + } + + found.forEach((count, i) => { + do_check_true(count == 1, "Expected count = 1, got count = " + count + " for permission " + expected[i]); + }); + + // Check to make sure that all of the tables which we care about are present + { + let db = Services.storage.openDatabase(GetPermissionsFile(profile)); + do_check_true(db.tableExists("moz_perms")); + do_check_true(db.tableExists("moz_hosts")); + do_check_false(db.tableExists("moz_hosts_is_backup")); + do_check_false(db.tableExists("moz_perms_v6")); + + let mozHostsStmt = db.createStatement("SELECT " + + "host, type, permission, expireType, expireTime, " + + "modificationTime, appId, isInBrowserElement " + + "FROM moz_hosts WHERE id = :id"); + + // Check that the moz_hosts table still contains the correct values. + created4.forEach((it) => { + mozHostsStmt.reset(); + mozHostsStmt.bindByName("id", it.id); + mozHostsStmt.executeStep(); + do_check_eq(mozHostsStmt.getUTF8String(0), it.host); + do_check_eq(mozHostsStmt.getUTF8String(1), it.type); + do_check_eq(mozHostsStmt.getInt64(2), it.permission); + do_check_eq(mozHostsStmt.getInt64(3), it.expireType); + do_check_eq(mozHostsStmt.getInt64(4), it.expireTime); + do_check_eq(mozHostsStmt.getInt64(5), it.modificationTime); + do_check_eq(mozHostsStmt.getInt64(6), it.appId); + do_check_eq(mozHostsStmt.getInt64(7), it.isInBrowserElement); + }); + + // Check that there are the right number of values + let mozHostsCount = db.createStatement("SELECT count(*) FROM moz_hosts"); + mozHostsCount.executeStep(); + do_check_eq(mozHostsCount.getInt64(0), created4.length); + + db.close(); + } +}); diff --git a/extensions/cookie/test/unit/test_permmanager_migrate_7-8.js b/extensions/cookie/test/unit/test_permmanager_migrate_7-8.js new file mode 100644 index 000000000..56d5e2347 --- /dev/null +++ b/extensions/cookie/test/unit/test_permmanager_migrate_7-8.js @@ -0,0 +1,246 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +XPCOMUtils.defineLazyModuleGetter(this, "PlacesTestUtils", + "resource://testing-common/PlacesTestUtils.jsm"); + +var PERMISSIONS_FILE_NAME = "permissions.sqlite"; + +function GetPermissionsFile(profile) +{ + let file = profile.clone(); + file.append(PERMISSIONS_FILE_NAME); + return file; +} + +function run_test() { + run_next_test(); +} + +add_task(function test() { + /* Create and set up the permissions database */ + let profile = do_get_profile(); + + let db = Services.storage.openDatabase(GetPermissionsFile(profile)); + db.schemaVersion = 7; + + /* + * V5 table + */ + db.executeSimpleSQL( + "CREATE TABLE moz_perms (" + + " id INTEGER PRIMARY KEY" + + ",origin TEXT" + + ",type TEXT" + + ",permission INTEGER" + + ",expireType INTEGER" + + ",expireTime INTEGER" + + ",modificationTime INTEGER" + + ")"); + + let stmt6Insert = db.createStatement( + "INSERT INTO moz_perms (" + + "id, origin, type, permission, expireType, expireTime, modificationTime" + + ") VALUES (" + + ":id, :origin, :type, :permission, :expireType, :expireTime, :modificationTime" + + ")"); + + /* + * V4 table + */ + db.executeSimpleSQL( + "CREATE TABLE moz_hosts (" + + " id INTEGER PRIMARY KEY" + + ",host TEXT" + + ",type TEXT" + + ",permission INTEGER" + + ",expireType INTEGER" + + ",expireTime INTEGER" + + ",modificationTime INTEGER" + + ",appId INTEGER" + + ",isInBrowserElement INTEGER" + + ")"); + + let stmtInsert = db.createStatement( + "INSERT INTO moz_hosts (" + + "id, host, type, permission, expireType, expireTime, modificationTime, appId, isInBrowserElement" + + ") VALUES (" + + ":id, :host, :type, :permission, :expireType, :expireTime, :modificationTime, :appId, :isInBrowserElement" + + ")"); + + /* + * The v4 table is a backup + */ + db.executeSimpleSQL("CREATE TABLE moz_hosts_is_backup (dummy INTEGER PRIMARY KEY)"); + + let id = 0; + + function insertOrigin(origin, type, permission, expireType, expireTime, modificationTime) { + let thisId = id++; + + stmt6Insert.bindByName("id", thisId); + stmt6Insert.bindByName("origin", origin); + stmt6Insert.bindByName("type", type); + stmt6Insert.bindByName("permission", permission); + stmt6Insert.bindByName("expireType", expireType); + stmt6Insert.bindByName("expireTime", expireTime); + stmt6Insert.bindByName("modificationTime", modificationTime); + + stmt6Insert.execute(); + + return { + id: thisId, + origin: origin, + type: type, + permission: permission, + expireType: expireType, + expireTime: expireTime, + modificationTime: modificationTime + }; + } + + function insertHost(host, type, permission, expireType, expireTime, modificationTime, appId, isInBrowserElement) { + let thisId = id++; + + stmtInsert.bindByName("id", thisId); + stmtInsert.bindByName("host", host); + stmtInsert.bindByName("type", type); + stmtInsert.bindByName("permission", permission); + stmtInsert.bindByName("expireType", expireType); + stmtInsert.bindByName("expireTime", expireTime); + stmtInsert.bindByName("modificationTime", modificationTime); + stmtInsert.bindByName("appId", appId); + stmtInsert.bindByName("isInBrowserElement", isInBrowserElement); + + stmtInsert.execute(); + + return { + id: thisId, + host: host, + type: type, + permission: permission, + expireType: expireType, + expireTime: expireTime, + modificationTime: modificationTime, + appId: appId, + isInBrowserElement: isInBrowserElement + }; + } + + let created7 = [ + insertOrigin("https://foo.com", "A", 2, 0, 0, 0), + insertOrigin("http://foo.com", "A", 2, 0, 0, 0), + insertOrigin("http://foo.com^appId=1000&inBrowser=1", "A", 2, 0, 0, 0), + insertOrigin("https://192.0.2.235", "A", 2, 0, 0), + ]; + + // Add some rows to the database + let created = [ + insertHost("foo.com", "A", 1, 0, 0, 0, 0, false), + insertHost("foo.com", "C", 1, 0, 0, 0, 0, false), + insertHost("foo.com", "A", 1, 0, 0, 0, 1000, false), + insertHost("foo.com", "A", 1, 0, 0, 0, 2000, true), + insertHost("sub.foo.com", "B", 1, 0, 0, 0, 0, false), + insertHost("subber.sub.foo.com", "B", 1, 0, 0, 0, 0, false), + insertHost("bar.ca", "B", 1, 0, 0, 0, 0, false), + insertHost("bar.ca", "B", 1, 0, 0, 0, 1000, false), + insertHost("bar.ca", "A", 1, 0, 0, 0, 1000, true), + insertHost("localhost", "A", 1, 0, 0, 0, 0, false), + insertHost("127.0.0.1", "A", 1, 0, 0, 0, 0, false), + insertHost("192.0.2.235", "A", 1, 0, 0, 0, 0, false), + // Although ipv6 addresses are written with [] around the IP address, + // the .host property doesn't contain these []s, which means that we + // write it like this + insertHost("2001:db8::ff00:42:8329", "C", 1, 0, 0, 0, 0, false), + insertHost("file:///some/path/to/file.html", "A", 1, 0, 0, 0, 0, false), + insertHost("file:///another/file.html", "A", 1, 0, 0, 0, 0, false), + insertHost("moz-nullprincipal:{8695105a-adbe-4e4e-8083-851faa5ca2d7}", "A", 1, 0, 0, 0, 0, false), + insertHost("moz-nullprincipal:{12ahjksd-akjs-asd3-8393-asdu2189asdu}", "B", 1, 0, 0, 0, 0, false), + insertHost("<file>", "A", 1, 0, 0, 0, 0, false), + insertHost("<file>", "B", 1, 0, 0, 0, 0, false), + ]; + + // CLose the db connection + stmtInsert.finalize(); + db.close(); + stmtInsert = null; + db = null; + + let expected = [ + // We should have kept the previously migrated entries + ["https://foo.com", "A", 2, 0, 0, 0], + ["http://foo.com", "A", 2, 0, 0, 0], + ["http://foo.com^appId=1000&inBrowser=1", "A", 2, 0, 0, 0], + + // Make sure that we also support localhost, and IP addresses + ["https://localhost:8080", "A", 1, 0, 0], + ["ftp://127.0.0.1:8080", "A", 1, 0, 0], + + ["http://[2001:db8::ff00:42:8329]", "C", 1, 0, 0], + ["https://[2001:db8::ff00:42:8329]", "C", 1, 0, 0], + ["http://192.0.2.235", "A", 1, 0, 0], + + // There should only be one entry of this type in the database + ["https://192.0.2.235", "A", 2, 0, 0], + ]; + + let found = expected.map((it) => 0); + + // Add some places to the places database + yield PlacesTestUtils.addVisits(Services.io.newURI("https://foo.com/some/other/subdirectory", null, null)); + yield PlacesTestUtils.addVisits(Services.io.newURI("ftp://some.subdomain.of.foo.com:8000/some/subdirectory", null, null)); + yield PlacesTestUtils.addVisits(Services.io.newURI("ftp://127.0.0.1:8080", null, null)); + yield PlacesTestUtils.addVisits(Services.io.newURI("https://localhost:8080", null, null)); + + // Force initialization of the nsPermissionManager + let enumerator = Services.perms.enumerator; + while (enumerator.hasMoreElements()) { + let permission = enumerator.getNext().QueryInterface(Ci.nsIPermission); + let isExpected = false; + + expected.forEach((it, i) => { + if (permission.principal.origin == it[0] && + permission.type == it[1] && + permission.capability == it[2] && + permission.expireType == it[3] && + permission.expireTime == it[4]) { + isExpected = true; + found[i]++; + } + }); + + do_check_true(isExpected, + "Permission " + (isExpected ? "should" : "shouldn't") + + " be in permission database: " + + permission.principal.origin + ", " + + permission.type + ", " + + permission.capability + ", " + + permission.expireType + ", " + + permission.expireTime); + } + + found.forEach((count, i) => { + do_check_true(count == 1, "Expected count = 1, got count = " + count + " for permission " + expected[i]); + }); + + // Check to make sure that all of the tables which we care about are present + { + let db = Services.storage.openDatabase(GetPermissionsFile(profile)); + do_check_true(db.tableExists("moz_perms")); + do_check_true(db.tableExists("moz_hosts")); + do_check_false(db.tableExists("moz_hosts_is_backup")); + do_check_false(db.tableExists("moz_perms_v6")); + + // The moz_hosts table should still exist but be empty + let mozHostsCount = db.createStatement("SELECT count(*) FROM moz_hosts"); + mozHostsCount.executeStep(); + do_check_eq(mozHostsCount.getInt64(0), 0); + + // Check that there are the right number of values in the permissions database + let mozPermsCount = db.createStatement("SELECT count(*) FROM moz_perms"); + mozPermsCount.executeStep(); + do_check_eq(mozPermsCount.getInt64(0), expected.length); + + db.close(); + } +}); diff --git a/extensions/cookie/test/unit/test_permmanager_notifications.js b/extensions/cookie/test/unit/test_permmanager_notifications.js new file mode 100644 index 000000000..5708fe8ec --- /dev/null +++ b/extensions/cookie/test/unit/test_permmanager_notifications.js @@ -0,0 +1,140 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Test that the permissionmanager 'added', 'changed', 'deleted', and 'cleared' +// notifications behave as expected. + +var test_generator = do_run_test(); + +function run_test() { + do_test_pending(); + test_generator.next(); +} + +function continue_test() +{ + do_run_generator(test_generator); +} + +function do_run_test() { + // Set up a profile. + let profile = do_get_profile(); + + let pm = Services.perms; + let now = Number(Date.now()); + let permType = "test/expiration-perm"; + let ssm = Cc["@mozilla.org/scriptsecuritymanager;1"] + .getService(Ci.nsIScriptSecurityManager); + let uri = NetUtil.newURI("http://example.com"); + let principal = ssm.createCodebasePrincipal(uri, {}); + + let observer = new permission_observer(test_generator, now, permType); + Services.obs.addObserver(observer, "perm-changed", false); + + // Add a permission, to test the 'add' notification. Note that we use + // do_execute_soon() so that we can use our generator to continue the test + // where we left off. + do_execute_soon(function() { + pm.addFromPrincipal(principal, permType, pm.ALLOW_ACTION, pm.EXPIRE_TIME, now + 100000); + }); + yield; + + // Alter a permission, to test the 'changed' notification. + do_execute_soon(function() { + pm.addFromPrincipal(principal, permType, pm.ALLOW_ACTION, pm.EXPIRE_TIME, now + 200000); + }); + yield; + + // Remove a permission, to test the 'deleted' notification. + do_execute_soon(function() { + pm.removeFromPrincipal(principal, permType); + }); + yield; + + // Clear permissions, to test the 'cleared' notification. + do_execute_soon(function() { + pm.removeAll(); + }); + yield; + + Services.obs.removeObserver(observer, "perm-changed"); + do_check_eq(observer.adds, 1); + do_check_eq(observer.changes, 1); + do_check_eq(observer.deletes, 1); + do_check_true(observer.cleared); + + do_finish_generator_test(test_generator); +} + +function permission_observer(generator, now, type) { + // Set up our observer object. + this.generator = generator; + this.pm = Services.perms; + this.now = now; + this.type = type; + this.adds = 0; + this.changes = 0; + this.deletes = 0; + this.cleared = false; +} + +permission_observer.prototype = { + observe: function(subject, topic, data) { + do_check_eq(topic, "perm-changed"); + + // "deleted" means a permission was deleted. aPermission is the deleted permission. + // "added" means a permission was added. aPermission is the added permission. + // "changed" means a permission was altered. aPermission is the new permission. + // "cleared" means the entire permission list was cleared. aPermission is null. + if (data == "added") { + var perm = subject.QueryInterface(Ci.nsIPermission); + this.adds++; + switch (this.adds) { + case 1: + do_check_eq(this.type, perm.type); + do_check_eq(this.pm.EXPIRE_TIME, perm.expireType); + do_check_eq(this.now + 100000, perm.expireTime); + break; + default: + do_throw("too many add notifications posted."); + } + + } else if (data == "changed") { + let perm = subject.QueryInterface(Ci.nsIPermission); + this.changes++; + switch (this.changes) { + case 1: + do_check_eq(this.type, perm.type); + do_check_eq(this.pm.EXPIRE_TIME, perm.expireType); + do_check_eq(this.now + 200000, perm.expireTime); + break; + default: + do_throw("too many change notifications posted."); + } + + } else if (data == "deleted") { + var perm = subject.QueryInterface(Ci.nsIPermission); + this.deletes++; + switch (this.deletes) { + case 1: + do_check_eq(this.type, perm.type); + break; + default: + do_throw("too many delete notifications posted."); + } + + } else if (data == "cleared") { + // only clear once: at the end + do_check_false(this.cleared); + do_check_eq(do_count_enumerator(Services.perms.enumerator), 0); + this.cleared = true; + + } else { + do_throw("unexpected data '" + data + "'!"); + } + + // Continue the test. + do_run_generator(this.generator); + }, +}; + diff --git a/extensions/cookie/test/unit/test_permmanager_removeall.js b/extensions/cookie/test/unit/test_permmanager_removeall.js new file mode 100644 index 000000000..54294a915 --- /dev/null +++ b/extensions/cookie/test/unit/test_permmanager_removeall.js @@ -0,0 +1,36 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +function run_test() { + // setup a profile directory + var dir = do_get_profile(); + + // initialize the permission manager service + var pm = Cc["@mozilla.org/permissionmanager;1"]. + getService(Ci.nsIPermissionManager); + + // get the db file + var file = dir.clone(); + file.append("permissions.sqlite"); + do_check_true(file.exists()); + + // corrupt the file + var ostream = Cc["@mozilla.org/network/file-output-stream;1"]. + createInstance(Ci.nsIFileOutputStream); + ostream.init(file, 0x02, 0o666, 0); + var conv = Cc["@mozilla.org/intl/converter-output-stream;1"]. + createInstance(Ci.nsIConverterOutputStream); + conv.init(ostream, "UTF-8", 0, 0); + for (var i = 0; i < file.fileSize; ++i) + conv.writeString("a"); + conv.close(); + + // prepare an empty hostperm.1 file so that it can be used for importing + var hostperm = dir.clone(); + hostperm.append("hostperm.1"); + ostream.init(hostperm, 0x02 | 0x08, 0o666, 0); + ostream.close(); + + // remove all should not throw + pm.removeAll(); +} diff --git a/extensions/cookie/test/unit/test_permmanager_removeforapp.js b/extensions/cookie/test/unit/test_permmanager_removeforapp.js new file mode 100644 index 000000000..152409de2 --- /dev/null +++ b/extensions/cookie/test/unit/test_permmanager_removeforapp.js @@ -0,0 +1,99 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +function run_test() { + // initialize the permission manager service + let ssm = Services.scriptSecurityManager; + let pm = Services.perms; + + function mkPrin(uri, appId, inIsolatedMozBrowser) { + return ssm.createCodebasePrincipal(Services.io.newURI(uri, null, null), + {appId: appId, inIsolatedMozBrowser: inIsolatedMozBrowser}); + } + + function checkPerms(perms) { + perms.forEach((perm) => { + // Look up the expected permission + do_check_eq(pm.getPermissionObject(mkPrin(perm[0], perm[1], perm[2]), + perm[3], true).capability, + perm[4], "Permission is expected in the permission database"); + }); + + // Count the entries + let count = 0; + let enumerator = Services.perms.enumerator; + while (enumerator.hasMoreElements()) { enumerator.getNext(); count++; } + + do_check_eq(count, perms.length, "There should be the right number of permissions in the DB"); + } + + checkPerms([]); + + let permissions = [ + ['http://google.com', 1001, false, 'a', 1], + ['http://google.com', 1001, false, 'b', 1], + ['http://mozilla.com', 1001, false, 'b', 1], + ['http://mozilla.com', 1001, false, 'a', 1], + + ['http://google.com', 1001, true, 'a', 1], + ['http://google.com', 1001, true, 'b', 1], + ['http://mozilla.com', 1001, true, 'b', 1], + ['http://mozilla.com', 1001, true, 'a', 1], + + ['http://google.com', 1011, false, 'a', 1], + ['http://google.com', 1011, false, 'b', 1], + ['http://mozilla.com', 1011, false, 'b', 1], + ['http://mozilla.com', 1011, false, 'a', 1], + ]; + + permissions.forEach((perm) => { + pm.addFromPrincipal(mkPrin(perm[0], perm[1], perm[2]), perm[3], perm[4]); + }); + + checkPerms(permissions); + + let remove_false_perms = [ + ['http://google.com', 1011, false, 'a', 1], + ['http://google.com', 1011, false, 'b', 1], + ['http://mozilla.com', 1011, false, 'b', 1], + ['http://mozilla.com', 1011, false, 'a', 1], + ]; + + let attrs = { appId: 1001 }; + pm.removePermissionsWithAttributes(JSON.stringify(attrs)); + checkPerms(remove_false_perms); + + let restore = [ + ['http://google.com', 1001, false, 'a', 1], + ['http://google.com', 1001, false, 'b', 1], + ['http://mozilla.com', 1001, false, 'b', 1], + ['http://mozilla.com', 1001, false, 'a', 1], + + ['http://google.com', 1001, true, 'a', 1], + ['http://google.com', 1001, true, 'b', 1], + ['http://mozilla.com', 1001, true, 'b', 1], + ['http://mozilla.com', 1001, true, 'a', 1], + ]; + + restore.forEach((perm) => { + pm.addFromPrincipal(mkPrin(perm[0], perm[1], perm[2]), perm[3], perm[4]); + }); + checkPerms(permissions); + + let remove_true_perms = [ + ['http://google.com', 1001, false, 'a', 1], + ['http://google.com', 1001, false, 'b', 1], + ['http://mozilla.com', 1001, false, 'b', 1], + ['http://mozilla.com', 1001, false, 'a', 1], + + ['http://google.com', 1011, false, 'a', 1], + ['http://google.com', 1011, false, 'b', 1], + ['http://mozilla.com', 1011, false, 'b', 1], + ['http://mozilla.com', 1011, false, 'a', 1], + ]; + + attrs = { appId: 1001, + inIsolatedMozBrowser: true }; + pm.removePermissionsWithAttributes(JSON.stringify(attrs)); + checkPerms(remove_true_perms); +} diff --git a/extensions/cookie/test/unit/test_permmanager_removepermission.js b/extensions/cookie/test/unit/test_permmanager_removepermission.js new file mode 100644 index 000000000..e8f5817e2 --- /dev/null +++ b/extensions/cookie/test/unit/test_permmanager_removepermission.js @@ -0,0 +1,67 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +function run_test() { + // initialize the permission manager service + let pm = Cc["@mozilla.org/permissionmanager;1"]. + getService(Ci.nsIPermissionManager); + + do_check_eq(perm_count(), 0); + + // add some permissions + let uri = NetUtil.newURI("http://amazon.com:8080/foobarbaz", null, null); + let uri2 = NetUtil.newURI("http://google.com:2048/quxx", null, null); + + pm.add(uri, "apple", 0); + pm.add(uri, "apple", 3); + pm.add(uri, "pear", 3); + pm.add(uri, "pear", 1); + pm.add(uri, "cucumber", 1); + pm.add(uri, "cucumber", 1); + pm.add(uri, "cucumber", 1); + + pm.add(uri2, "apple", 2); + pm.add(uri2, "pear", 0); + pm.add(uri2, "pear", 2); + + // Make sure that removePermission doesn't remove more than one permission each time + do_check_eq(perm_count(), 5); + + remove_one_by_type("apple"); + do_check_eq(perm_count(), 4); + + remove_one_by_type("apple"); + do_check_eq(perm_count(), 3); + + remove_one_by_type("pear"); + do_check_eq(perm_count(), 2); + + remove_one_by_type("cucumber"); + do_check_eq(perm_count(), 1); + + remove_one_by_type("pear"); + do_check_eq(perm_count(), 0); + + + function perm_count() { + let enumerator = pm.enumerator; + let count = 0; + while (enumerator.hasMoreElements()) { + count++; + enumerator.getNext(); + } + + return count; + } + + function remove_one_by_type(type) { + let enumerator = pm.enumerator; + while (enumerator.hasMoreElements()) { + let it = enumerator.getNext().QueryInterface(Ci.nsIPermission); + if (it.type == type) { + pm.removePermission(it); + break; + } + } + } +} diff --git a/extensions/cookie/test/unit/test_permmanager_removesince.js b/extensions/cookie/test/unit/test_permmanager_removesince.js new file mode 100644 index 000000000..719f7f6f6 --- /dev/null +++ b/extensions/cookie/test/unit/test_permmanager_removesince.js @@ -0,0 +1,69 @@ +/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Test that removing permissions since a specified time behaves as expected.
+
+var test_generator = do_run_test();
+
+function run_test() {
+ do_test_pending();
+ test_generator.next();
+}
+
+function continue_test()
+{
+ do_run_generator(test_generator);
+}
+
+function do_run_test() {
+ // Set up a profile.
+ let profile = do_get_profile();
+
+ let pm = Services.perms;
+
+ // to help with testing edge-cases, we will arrange for .removeAllSince to
+ // remove *all* permissions from one principal and one permission from another.
+ let permURI1 = NetUtil.newURI("http://example.com");
+ let principal1 = Services.scriptSecurityManager.createCodebasePrincipal(permURI1, {});
+
+ let permURI2 = NetUtil.newURI("http://example.org");
+ let principal2 = Services.scriptSecurityManager.createCodebasePrincipal(permURI2, {});
+
+ // add a permission now - this isn't going to be removed.
+ pm.addFromPrincipal(principal1, "test/remove-since", 1);
+
+ // sleep briefly, then record the time - we'll remove all since then.
+ do_timeout(20, continue_test);
+ yield;
+
+ let since = Number(Date.now());
+
+ // *sob* - on Windows at least, the now recorded by nsPermissionManager.cpp
+ // might be a couple of ms *earlier* than what JS sees. So another sleep
+ // to ensure our |since| is greater than the time of the permissions we
+ // are now adding. Sadly this means we'll never be able to test when since
+ // exactly equals the modTime, but there you go...
+ do_timeout(20, continue_test);
+ yield;
+
+ // add another item - this second one should get nuked.
+ pm.addFromPrincipal(principal1, "test/remove-since-2", 1);
+
+ // add 2 items for the second principal - both will be removed.
+ pm.addFromPrincipal(principal2, "test/remove-since", 1);
+ pm.addFromPrincipal(principal2, "test/remove-since-2", 1);
+
+ // do the removal.
+ pm.removeAllSince(since);
+
+ // principal1 - the first one should remain.
+ do_check_eq(1, pm.testPermissionFromPrincipal(principal1, "test/remove-since"));
+ // but the second should have been removed.
+ do_check_eq(0, pm.testPermissionFromPrincipal(principal1, "test/remove-since-2"));
+
+ // principal2 - both should have been removed.
+ do_check_eq(0, pm.testPermissionFromPrincipal(principal2, "test/remove-since"));
+ do_check_eq(0, pm.testPermissionFromPrincipal(principal2, "test/remove-since-2"));
+
+ do_finish_generator_test(test_generator);
+}
diff --git a/extensions/cookie/test/unit/test_permmanager_subdomains.js b/extensions/cookie/test/unit/test_permmanager_subdomains.js new file mode 100644 index 000000000..4e78e4d46 --- /dev/null +++ b/extensions/cookie/test/unit/test_permmanager_subdomains.js @@ -0,0 +1,57 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +function getPrincipalFromURI(aURI) { + let ssm = Cc["@mozilla.org/scriptsecuritymanager;1"] + .getService(Ci.nsIScriptSecurityManager); + let uri = NetUtil.newURI(aURI); + return ssm.createCodebasePrincipal(uri, {}); +} + +function run_test() { + var pm = Cc["@mozilla.org/permissionmanager;1"]. + getService(Ci.nsIPermissionManager); + + // Adds a permission to a sub-domain. Checks if it is working. + let sub1Principal = getPrincipalFromURI("http://sub1.example.com"); + pm.addFromPrincipal(sub1Principal, "test/subdomains", pm.ALLOW_ACTION, 0, 0); + do_check_eq(pm.testPermissionFromPrincipal(sub1Principal, "test/subdomains"), pm.ALLOW_ACTION); + + // A sub-sub-domain should get the permission. + let subsubPrincipal = getPrincipalFromURI("http://sub.sub1.example.com"); + do_check_eq(pm.testPermissionFromPrincipal(subsubPrincipal, "test/subdomains"), pm.ALLOW_ACTION); + + // Another sub-domain shouldn't get the permission. + let sub2Principal = getPrincipalFromURI("http://sub2.example.com"); + do_check_eq(pm.testPermissionFromPrincipal(sub2Principal, "test/subdomains"), pm.UNKNOWN_ACTION); + + // Remove current permissions. + pm.removeFromPrincipal(sub1Principal, "test/subdomains"); + do_check_eq(pm.testPermissionFromPrincipal(sub1Principal, "test/subdomains"), pm.UNKNOWN_ACTION); + + // Adding the permission to the main domain. Checks if it is working. + let mainPrincipal = getPrincipalFromURI("http://example.com"); + pm.addFromPrincipal(mainPrincipal, "test/subdomains", pm.ALLOW_ACTION, 0, 0); + do_check_eq(pm.testPermissionFromPrincipal(mainPrincipal, "test/subdomains"), pm.ALLOW_ACTION); + + // All sub-domains should have the permission now. + do_check_eq(pm.testPermissionFromPrincipal(sub1Principal, "test/subdomains"), pm.ALLOW_ACTION); + do_check_eq(pm.testPermissionFromPrincipal(sub2Principal, "test/subdomains"), pm.ALLOW_ACTION); + do_check_eq(pm.testPermissionFromPrincipal(subsubPrincipal, "test/subdomains"), pm.ALLOW_ACTION); + + // Remove current permissions. + pm.removeFromPrincipal(mainPrincipal, "test/subdomains"); + do_check_eq(pm.testPermissionFromPrincipal(mainPrincipal, "test/subdomains"), pm.UNKNOWN_ACTION); + do_check_eq(pm.testPermissionFromPrincipal(sub1Principal, "test/subdomains"), pm.UNKNOWN_ACTION); + do_check_eq(pm.testPermissionFromPrincipal(sub2Principal, "test/subdomains"), pm.UNKNOWN_ACTION); + do_check_eq(pm.testPermissionFromPrincipal(subsubPrincipal, "test/subdomains"), pm.UNKNOWN_ACTION); + + // A sanity check that the previous implementation wasn't passing... + let crazyPrincipal = getPrincipalFromURI("http://com"); + pm.addFromPrincipal(crazyPrincipal, "test/subdomains", pm.ALLOW_ACTION, 0, 0); + do_check_eq(pm.testPermissionFromPrincipal(crazyPrincipal, "test/subdomains"), pm.ALLOW_ACTION); + do_check_eq(pm.testPermissionFromPrincipal(mainPrincipal, "test/subdomains"), pm.UNKNOWN_ACTION); + do_check_eq(pm.testPermissionFromPrincipal(sub1Principal, "test/subdomains"), pm.UNKNOWN_ACTION); + do_check_eq(pm.testPermissionFromPrincipal(sub2Principal, "test/subdomains"), pm.UNKNOWN_ACTION); + do_check_eq(pm.testPermissionFromPrincipal(subsubPrincipal, "test/subdomains"), pm.UNKNOWN_ACTION); +} diff --git a/extensions/cookie/test/unit/test_schema_2_migration.js b/extensions/cookie/test/unit/test_schema_2_migration.js new file mode 100644 index 000000000..7dc5e823c --- /dev/null +++ b/extensions/cookie/test/unit/test_schema_2_migration.js @@ -0,0 +1,207 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Test cookie database migration from version 2 (Gecko 1.9.3) to the current +// version, presently 4 (Gecko 2.0). + +var test_generator = do_run_test(); + +function run_test() { + do_test_pending(); + test_generator.next(); +} + +function finish_test() { + do_execute_soon(function() { + test_generator.close(); + do_test_finished(); + }); +} + +function do_run_test() { + // Set up a profile. + let profile = do_get_profile(); + + // Create a schema 2 database. + let schema2db = new CookieDatabaseConnection(do_get_cookie_file(profile), 2); + + let now = Date.now() * 1000; + let futureExpiry = Math.round(now / 1e6 + 1000); + let pastExpiry = Math.round(now / 1e6 - 1000); + + // Populate it, with: + // 1) Unexpired, unique cookies. + for (let i = 0; i < 20; ++i) { + let cookie = new Cookie("oh" + i, "hai", "foo.com", "/", + futureExpiry, now, now + i, false, false, false); + + schema2db.insertCookie(cookie); + } + + // 2) Expired, unique cookies. + for (let i = 20; i < 40; ++i) { + let cookie = new Cookie("oh" + i, "hai", "bar.com", "/", + pastExpiry, now, now + i, false, false, false); + + schema2db.insertCookie(cookie); + } + + // 3) Many copies of the same cookie, some of which have expired and + // some of which have not. + for (let i = 40; i < 45; ++i) { + let cookie = new Cookie("oh", "hai", "baz.com", "/", + futureExpiry + i, now, now + i, false, false, false); + + schema2db.insertCookie(cookie); + } + for (let i = 45; i < 50; ++i) { + let cookie = new Cookie("oh", "hai", "baz.com", "/", + pastExpiry - i, now, now + i, false, false, false); + + schema2db.insertCookie(cookie); + } + for (let i = 50; i < 55; ++i) { + let cookie = new Cookie("oh", "hai", "baz.com", "/", + futureExpiry - i, now, now + i, false, false, false); + + schema2db.insertCookie(cookie); + } + for (let i = 55; i < 60; ++i) { + let cookie = new Cookie("oh", "hai", "baz.com", "/", + pastExpiry + i, now, now + i, false, false, false); + + schema2db.insertCookie(cookie); + } + + // Close it. + schema2db.close(); + schema2db = null; + + // Load the database, forcing migration to the current schema version. Then + // test the expected set of cookies: + // 1) All unexpired, unique cookies exist. + do_check_eq(Services.cookiemgr.countCookiesFromHost("foo.com"), 20); + + // 2) All expired, unique cookies exist. + do_check_eq(Services.cookiemgr.countCookiesFromHost("bar.com"), 20); + + // 3) Only one cookie remains, and it's the one with the highest expiration + // time. + do_check_eq(Services.cookiemgr.countCookiesFromHost("baz.com"), 1); + let enumerator = Services.cookiemgr.getCookiesFromHost("baz.com", {}); + let cookie = enumerator.getNext().QueryInterface(Ci.nsICookie2); + do_check_eq(cookie.expiry, futureExpiry + 44); + + do_close_profile(test_generator); + yield; + + // Open the database so we can execute some more schema 2 statements on it. + schema2db = new CookieDatabaseConnection(do_get_cookie_file(profile), 2); + + // Populate it with more cookies. + for (let i = 60; i < 80; ++i) { + let cookie = new Cookie("oh" + i, "hai", "foo.com", "/", + futureExpiry, now, now + i, false, false, false); + + schema2db.insertCookie(cookie); + } + for (let i = 80; i < 100; ++i) { + let cookie = new Cookie("oh" + i, "hai", "cat.com", "/", + futureExpiry, now, now + i, false, false, false); + + schema2db.insertCookie(cookie); + } + + // Attempt to add a cookie with the same (name, host, path) values as another + // cookie. This should succeed since we have a REPLACE clause for conflict on + // the unique index. + cookie = new Cookie("oh", "hai", "baz.com", "/", + futureExpiry, now, now + 100, false, false, false); + + schema2db.insertCookie(cookie); + + // Check that there is, indeed, a singular cookie for baz.com. + do_check_eq(do_count_cookies_in_db(schema2db.db, "baz.com"), 1); + + // Close it. + schema2db.close(); + schema2db = null; + + // Back up the database, so we can test both asynchronous and synchronous + // loading separately. + let file = do_get_cookie_file(profile); + let copy = profile.clone(); + copy.append("cookies.sqlite.copy"); + file.copyTo(null, copy.leafName); + + // Load the database asynchronously, forcing a purge of the newly-added + // cookies. (Their baseDomain column will be NULL.) + do_load_profile(test_generator); + yield; + + // Test the expected set of cookies. + do_check_eq(Services.cookiemgr.countCookiesFromHost("foo.com"), 20); + do_check_eq(Services.cookiemgr.countCookiesFromHost("bar.com"), 20); + do_check_eq(Services.cookiemgr.countCookiesFromHost("baz.com"), 0); + do_check_eq(Services.cookiemgr.countCookiesFromHost("cat.com"), 0); + + do_close_profile(test_generator); + yield; + + // Open the database and prove that they were deleted. + schema2db = new CookieDatabaseConnection(do_get_cookie_file(profile), 2); + do_check_eq(do_count_cookies_in_db(schema2db.db), 40); + do_check_eq(do_count_cookies_in_db(schema2db.db, "foo.com"), 20); + do_check_eq(do_count_cookies_in_db(schema2db.db, "bar.com"), 20); + schema2db.close(); + + // Copy the database back. + file.remove(false); + copy.copyTo(null, file.leafName); + + // Load the database host-at-a-time. + do_load_profile(); + + // Test the expected set of cookies. + do_check_eq(Services.cookiemgr.countCookiesFromHost("foo.com"), 20); + do_check_eq(Services.cookiemgr.countCookiesFromHost("bar.com"), 20); + do_check_eq(Services.cookiemgr.countCookiesFromHost("baz.com"), 0); + do_check_eq(Services.cookiemgr.countCookiesFromHost("cat.com"), 0); + + do_close_profile(test_generator); + yield; + + // Open the database and prove that they were deleted. + schema2db = new CookieDatabaseConnection(do_get_cookie_file(profile), 2); + do_check_eq(do_count_cookies_in_db(schema2db.db), 40); + do_check_eq(do_count_cookies_in_db(schema2db.db, "foo.com"), 20); + do_check_eq(do_count_cookies_in_db(schema2db.db, "bar.com"), 20); + schema2db.close(); + + // Copy the database back. + file.remove(false); + copy.copyTo(null, file.leafName); + + // Load the database synchronously, in its entirety. + do_load_profile(); + do_check_eq(do_count_cookies(), 40); + + // Test the expected set of cookies. + do_check_eq(Services.cookiemgr.countCookiesFromHost("foo.com"), 20); + do_check_eq(Services.cookiemgr.countCookiesFromHost("bar.com"), 20); + do_check_eq(Services.cookiemgr.countCookiesFromHost("baz.com"), 0); + do_check_eq(Services.cookiemgr.countCookiesFromHost("cat.com"), 0); + + do_close_profile(test_generator); + yield; + + // Open the database and prove that they were deleted. + schema2db = new CookieDatabaseConnection(do_get_cookie_file(profile), 2); + do_check_eq(do_count_cookies_in_db(schema2db.db), 40); + do_check_eq(do_count_cookies_in_db(schema2db.db, "foo.com"), 20); + do_check_eq(do_count_cookies_in_db(schema2db.db, "bar.com"), 20); + schema2db.close(); + + finish_test(); +} + diff --git a/extensions/cookie/test/unit/test_schema_3_migration.js b/extensions/cookie/test/unit/test_schema_3_migration.js new file mode 100644 index 000000000..40a23f7e4 --- /dev/null +++ b/extensions/cookie/test/unit/test_schema_3_migration.js @@ -0,0 +1,125 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Test cookie database migration from version 3 (prerelease Gecko 2.0) to the +// current version, presently 4 (Gecko 2.0). + +var test_generator = do_run_test(); + +function run_test() { + do_test_pending(); + test_generator.next(); +} + +function finish_test() { + do_execute_soon(function() { + test_generator.close(); + do_test_finished(); + }); +} + +function do_run_test() { + // Set up a profile. + let profile = do_get_profile(); + + // Create a schema 3 database. + let schema3db = new CookieDatabaseConnection(do_get_cookie_file(profile), 3); + + let now = Date.now() * 1000; + let futureExpiry = Math.round(now / 1e6 + 1000); + let pastExpiry = Math.round(now / 1e6 - 1000); + + // Populate it, with: + // 1) Unexpired, unique cookies. + for (let i = 0; i < 20; ++i) { + let cookie = new Cookie("oh" + i, "hai", "foo.com", "/", + futureExpiry, now, now + i, false, false, false); + + schema3db.insertCookie(cookie); + } + + // 2) Expired, unique cookies. + for (let i = 20; i < 40; ++i) { + let cookie = new Cookie("oh" + i, "hai", "bar.com", "/", + pastExpiry, now, now + i, false, false, false); + + schema3db.insertCookie(cookie); + } + + // 3) Many copies of the same cookie, some of which have expired and + // some of which have not. + for (let i = 40; i < 45; ++i) { + let cookie = new Cookie("oh", "hai", "baz.com", "/", + futureExpiry + i, now, now + i, false, false, false); + + schema3db.insertCookie(cookie); + } + for (let i = 45; i < 50; ++i) { + let cookie = new Cookie("oh", "hai", "baz.com", "/", + pastExpiry - i, now, now + i, false, false, false); + + schema3db.insertCookie(cookie); + } + for (let i = 50; i < 55; ++i) { + let cookie = new Cookie("oh", "hai", "baz.com", "/", + futureExpiry - i, now, now + i, false, false, false); + + schema3db.insertCookie(cookie); + } + for (let i = 55; i < 60; ++i) { + let cookie = new Cookie("oh", "hai", "baz.com", "/", + pastExpiry + i, now, now + i, false, false, false); + + schema3db.insertCookie(cookie); + } + + // Close it. + schema3db.close(); + schema3db = null; + + // Load the database, forcing migration to the current schema version. Then + // test the expected set of cookies: + // 1) All unexpired, unique cookies exist. + do_check_eq(Services.cookiemgr.countCookiesFromHost("foo.com"), 20); + + // 2) All expired, unique cookies exist. + do_check_eq(Services.cookiemgr.countCookiesFromHost("bar.com"), 20); + + // 3) Only one cookie remains, and it's the one with the highest expiration + // time. + do_check_eq(Services.cookiemgr.countCookiesFromHost("baz.com"), 1); + let enumerator = Services.cookiemgr.getCookiesFromHost("baz.com", {}); + let cookie = enumerator.getNext().QueryInterface(Ci.nsICookie2); + do_check_eq(cookie.expiry, futureExpiry + 44); + + do_close_profile(test_generator); + yield; + + // Open the database so we can execute some more schema 3 statements on it. + schema3db = new CookieDatabaseConnection(do_get_cookie_file(profile), 3); + + // Populate it with more cookies. + for (let i = 60; i < 80; ++i) { + let cookie = new Cookie("oh" + i, "hai", "cat.com", "/", + futureExpiry, now, now + i, false, false, false); + + schema3db.insertCookie(cookie); + } + + // Close it. + schema3db.close(); + schema3db = null; + + // Load the database. The cookies added immediately prior will have a NULL + // creationTime column. + do_load_profile(); + + // Test the expected set of cookies. + do_check_eq(Services.cookiemgr.countCookiesFromHost("cat.com"), 20); + enumerator = Services.cookiemgr.getCookiesFromHost("cat.com", {}); + cookie = enumerator.getNext().QueryInterface(Ci.nsICookie2); + do_check_eq(cookie.creationTime, 0); + + finish_test(); +} + diff --git a/extensions/cookie/test/unit/xpcshell.ini b/extensions/cookie/test/unit/xpcshell.ini new file mode 100644 index 000000000..e20d0142c --- /dev/null +++ b/extensions/cookie/test/unit/xpcshell.ini @@ -0,0 +1,48 @@ +[DEFAULT] +head = head_cookies.js +tail = +skip-if = toolkit == 'android' +support-files = + cookieprompt.js + cookieprompt.manifest + +[test_bug526789.js] +[test_bug650522.js] +[test_bug667087.js] +[test_cookies_async_failure.js] +[test_cookies_persistence.js] +skip-if = true # Bug 863738 +[test_cookies_privatebrowsing.js] +[test_cookies_profile_close.js] +[test_cookies_read.js] +[test_cookies_sync_failure.js] +[test_cookies_thirdparty.js] +[test_cookies_thirdparty_session.js] +[test_domain_eviction.js] +[test_eviction.js] +[test_permmanager_defaults.js] +[test_permmanager_expiration.js] +[test_permmanager_getAllForURI.js] +[test_permmanager_getPermissionObject.js] +[test_permmanager_notifications.js] +[test_permmanager_removeall.js] +[test_permmanager_removesince.js] +[test_permmanager_removeforapp.js] +[test_permmanager_load_invalid_entries.js] +skip-if = debug == true +[test_permmanager_idn.js] +[test_permmanager_subdomains.js] +[test_permmanager_local_files.js] +[test_permmanager_cleardata.js] +[test_schema_2_migration.js] +[test_schema_3_migration.js] +[test_permmanager_removepermission.js] +[test_permmanager_matchesuri.js] +[test_permmanager_matches.js] +[test_permmanager_migrate_4-7.js] +[test_permmanager_migrate_5-7a.js] +[test_permmanager_migrate_5-7b.js] +[test_permmanager_migrate_6-7a.js] +[test_permmanager_migrate_6-7b.js] +[test_permmanager_migrate_4-7_no_history.js] +[test_permmanager_migrate_7-8.js] |