summaryrefslogtreecommitdiff
path: root/extensions/cookie/test
diff options
context:
space:
mode:
Diffstat (limited to 'extensions/cookie/test')
-rw-r--r--extensions/cookie/test/beltzner.jpgbin0 -> 9995 bytes
-rw-r--r--extensions/cookie/test/beltzner.jpg^headers^3
-rw-r--r--extensions/cookie/test/browser.ini3
-rw-r--r--extensions/cookie/test/browser_test_favicon.js28
-rw-r--r--extensions/cookie/test/damonbowling.jpgbin0 -> 44008 bytes
-rw-r--r--extensions/cookie/test/damonbowling.jpg^headers^2
-rw-r--r--extensions/cookie/test/file_chromecommon.js15
-rw-r--r--extensions/cookie/test/file_domain_hierarchy_inner.html14
-rw-r--r--extensions/cookie/test/file_domain_hierarchy_inner_inner.html14
-rw-r--r--extensions/cookie/test/file_domain_hierarchy_inner_inner_inner.html14
-rw-r--r--extensions/cookie/test/file_domain_inner.html14
-rw-r--r--extensions/cookie/test/file_domain_inner_inner.html14
-rw-r--r--extensions/cookie/test/file_image_inner.html15
-rw-r--r--extensions/cookie/test/file_image_inner_inner.html20
-rw-r--r--extensions/cookie/test/file_loadflags_inner.html17
-rw-r--r--extensions/cookie/test/file_localhost_inner.html14
-rw-r--r--extensions/cookie/test/file_loopback_inner.html14
-rw-r--r--extensions/cookie/test/file_subdomain_inner.html14
-rw-r--r--extensions/cookie/test/file_testcommon.js70
-rw-r--r--extensions/cookie/test/file_testloadflags.js104
-rw-r--r--extensions/cookie/test/file_testloadflags_chromescript.js112
-rw-r--r--extensions/cookie/test/image1.pngbin0 -> 821 bytes
-rw-r--r--extensions/cookie/test/image1.png^headers^3
-rw-r--r--extensions/cookie/test/image2.pngbin0 -> 821 bytes
-rw-r--r--extensions/cookie/test/image2.png^headers^3
-rw-r--r--extensions/cookie/test/mochitest.ini41
-rw-r--r--extensions/cookie/test/moz.build15
-rw-r--r--extensions/cookie/test/test1.css2
-rw-r--r--extensions/cookie/test/test1.css^headers^3
-rw-r--r--extensions/cookie/test/test2.css2
-rw-r--r--extensions/cookie/test/test2.css^headers^3
-rw-r--r--extensions/cookie/test/test_different_domain_in_hierarchy.html15
-rw-r--r--extensions/cookie/test/test_differentdomain.html15
-rw-r--r--extensions/cookie/test/test_image.html14
-rw-r--r--extensions/cookie/test/test_loadflags.html21
-rw-r--r--extensions/cookie/test/test_same_base_domain.html15
-rw-r--r--extensions/cookie/test/test_same_base_domain_2.html15
-rw-r--r--extensions/cookie/test/test_same_base_domain_3.html15
-rw-r--r--extensions/cookie/test/test_same_base_domain_4.html15
-rw-r--r--extensions/cookie/test/test_same_base_domain_5.html15
-rw-r--r--extensions/cookie/test/test_same_base_domain_6.html15
-rw-r--r--extensions/cookie/test/test_samedomain.html15
-rw-r--r--extensions/cookie/test/unit/cookieprompt.js22
-rw-r--r--extensions/cookie/test/unit/cookieprompt.manifest2
-rw-r--r--extensions/cookie/test/unit/head_cookies.js570
-rw-r--r--extensions/cookie/test/unit/test_bug526789.js248
-rw-r--r--extensions/cookie/test/unit/test_bug650522.js16
-rw-r--r--extensions/cookie/test/unit/test_bug667087.js16
-rw-r--r--extensions/cookie/test/unit/test_cookies_async_failure.js600
-rw-r--r--extensions/cookie/test/unit/test_cookies_persistence.js78
-rw-r--r--extensions/cookie/test/unit/test_cookies_privatebrowsing.js115
-rw-r--r--extensions/cookie/test/unit/test_cookies_profile_close.js92
-rw-r--r--extensions/cookie/test/unit/test_cookies_read.js122
-rw-r--r--extensions/cookie/test/unit/test_cookies_sync_failure.js286
-rw-r--r--extensions/cookie/test/unit/test_cookies_thirdparty.js147
-rw-r--r--extensions/cookie/test/unit/test_cookies_thirdparty_session.js69
-rw-r--r--extensions/cookie/test/unit/test_domain_eviction.js153
-rw-r--r--extensions/cookie/test/unit/test_eviction.js249
-rw-r--r--extensions/cookie/test/unit/test_permmanager_cleardata.js68
-rw-r--r--extensions/cookie/test/unit/test_permmanager_defaults.js295
-rw-r--r--extensions/cookie/test/unit/test_permmanager_expiration.js82
-rw-r--r--extensions/cookie/test/unit/test_permmanager_getAllForURI.js78
-rw-r--r--extensions/cookie/test/unit/test_permmanager_getPermissionObject.js95
-rw-r--r--extensions/cookie/test/unit/test_permmanager_idn.js49
-rw-r--r--extensions/cookie/test/unit/test_permmanager_load_invalid_entries.js142
-rw-r--r--extensions/cookie/test/unit/test_permmanager_local_files.js43
-rw-r--r--extensions/cookie/test/unit/test_permmanager_matches.js183
-rw-r--r--extensions/cookie/test/unit/test_permmanager_matchesuri.js150
-rw-r--r--extensions/cookie/test/unit/test_permmanager_migrate_4-7.js207
-rw-r--r--extensions/cookie/test/unit/test_permmanager_migrate_4-7_no_history.js226
-rw-r--r--extensions/cookie/test/unit/test_permmanager_migrate_5-7a.js284
-rw-r--r--extensions/cookie/test/unit/test_permmanager_migrate_5-7b.js168
-rw-r--r--extensions/cookie/test/unit/test_permmanager_migrate_6-7a.js284
-rw-r--r--extensions/cookie/test/unit/test_permmanager_migrate_6-7b.js162
-rw-r--r--extensions/cookie/test/unit/test_permmanager_migrate_7-8.js246
-rw-r--r--extensions/cookie/test/unit/test_permmanager_notifications.js140
-rw-r--r--extensions/cookie/test/unit/test_permmanager_removeall.js36
-rw-r--r--extensions/cookie/test/unit/test_permmanager_removeforapp.js99
-rw-r--r--extensions/cookie/test/unit/test_permmanager_removepermission.js67
-rw-r--r--extensions/cookie/test/unit/test_permmanager_removesince.js69
-rw-r--r--extensions/cookie/test/unit/test_permmanager_subdomains.js57
-rw-r--r--extensions/cookie/test/unit/test_schema_2_migration.js207
-rw-r--r--extensions/cookie/test/unit/test_schema_3_migration.js125
-rw-r--r--extensions/cookie/test/unit/xpcshell.ini48
-rw-r--r--extensions/cookie/test/unit_ipc/test_child.js59
-rw-r--r--extensions/cookie/test/unit_ipc/test_parent.js59
-rw-r--r--extensions/cookie/test/unit_ipc/xpcshell.ini7
87 files changed, 7263 insertions, 0 deletions
diff --git a/extensions/cookie/test/beltzner.jpg b/extensions/cookie/test/beltzner.jpg
new file mode 100644
index 0000000000..75849bc40d
--- /dev/null
+++ b/extensions/cookie/test/beltzner.jpg
Binary files differ
diff --git a/extensions/cookie/test/beltzner.jpg^headers^ b/extensions/cookie/test/beltzner.jpg^headers^
new file mode 100644
index 0000000000..cb51f27018
--- /dev/null
+++ b/extensions/cookie/test/beltzner.jpg^headers^
@@ -0,0 +1,3 @@
+Cache-Control: no-store
+Set-Cookie: mike=beltzer
+
diff --git a/extensions/cookie/test/browser.ini b/extensions/cookie/test/browser.ini
new file mode 100644
index 0000000000..da7b1548f4
--- /dev/null
+++ b/extensions/cookie/test/browser.ini
@@ -0,0 +1,3 @@
+[DEFAULT]
+
+[browser_test_favicon.js]
diff --git a/extensions/cookie/test/browser_test_favicon.js b/extensions/cookie/test/browser_test_favicon.js
new file mode 100644
index 0000000000..a89a700177
--- /dev/null
+++ b/extensions/cookie/test/browser_test_favicon.js
@@ -0,0 +1,28 @@
+// tests third party cookie blocking using a favicon load directly from chrome.
+// in this case, the docshell of the channel is chrome, not content; thus
+// the cookie should be considered third party.
+
+function test() {
+ waitForExplicitFinish();
+
+ Services.prefs.setIntPref("network.cookie.cookieBehavior", 1);
+
+ Services.obs.addObserver(function (theSubject, theTopic, theData) {
+ var uri = theSubject.QueryInterface(Components.interfaces.nsIURI);
+ var domain = uri.host;
+
+ if (domain == "example.org") {
+ ok(true, "foreign favicon cookie was blocked");
+
+ Services.prefs.setIntPref("network.cookie.cookieBehavior", 0);
+
+ Services.obs.removeObserver(arguments.callee, "cookie-rejected");
+
+ finish();
+ }
+ }, "cookie-rejected", false);
+
+ // kick off a favicon load
+ gBrowser.setIcon(gBrowser.selectedTab, "http://example.org/tests/extensions/cookie/test/damonbowling.jpg",
+ Services.scriptSecurityManager.getSystemPrincipal());
+}
diff --git a/extensions/cookie/test/damonbowling.jpg b/extensions/cookie/test/damonbowling.jpg
new file mode 100644
index 0000000000..8bdb2b6042
--- /dev/null
+++ b/extensions/cookie/test/damonbowling.jpg
Binary files differ
diff --git a/extensions/cookie/test/damonbowling.jpg^headers^ b/extensions/cookie/test/damonbowling.jpg^headers^
new file mode 100644
index 0000000000..77f4f49089
--- /dev/null
+++ b/extensions/cookie/test/damonbowling.jpg^headers^
@@ -0,0 +1,2 @@
+Cache-Control: no-store
+Set-Cookie: damon=bowling
diff --git a/extensions/cookie/test/file_chromecommon.js b/extensions/cookie/test/file_chromecommon.js
new file mode 100644
index 0000000000..4383876b25
--- /dev/null
+++ b/extensions/cookie/test/file_chromecommon.js
@@ -0,0 +1,15 @@
+let { classes: Cc, utils: Cu, interfaces: Ci } = Components;
+
+let cs = Cc["@mozilla.org/cookiemanager;1"]
+ .getService(Ci.nsICookieManager2);
+
+addMessageListener("getCookieCountAndClear", () => {
+ let count = 0;
+ for (let list = cs.enumerator; list.hasMoreElements(); list.getNext())
+ ++count;
+ cs.removeAll();
+
+ sendAsyncMessage("getCookieCountAndClear:return", { count });
+});
+
+cs.removeAll();
diff --git a/extensions/cookie/test/file_domain_hierarchy_inner.html b/extensions/cookie/test/file_domain_hierarchy_inner.html
new file mode 100644
index 0000000000..1a37345975
--- /dev/null
+++ b/extensions/cookie/test/file_domain_hierarchy_inner.html
@@ -0,0 +1,14 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <META HTTP-EQUIV="Set-Cookie" CONTENT="meta=tag">
+ <script type="text/javascript">
+ document.cookie = "can=has";
+
+ // send a message to our test document, to say we're done loading
+ window.opener.postMessage("message", "http://mochi.test:8888");
+ </script>
+<body>
+<iframe name="frame1" src="http://example.com/tests/extensions/cookie/test/file_domain_hierarchy_inner_inner.html"></iframe>
+</body>
+</html>
diff --git a/extensions/cookie/test/file_domain_hierarchy_inner_inner.html b/extensions/cookie/test/file_domain_hierarchy_inner_inner.html
new file mode 100644
index 0000000000..3075b971a5
--- /dev/null
+++ b/extensions/cookie/test/file_domain_hierarchy_inner_inner.html
@@ -0,0 +1,14 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <META HTTP-EQUIV="Set-Cookie" CONTENT="meta2=tag2">
+ <script type="text/javascript">
+ document.cookie = "can2=has2";
+
+ // send a message to our test document, to say we're done loading
+ window.parent.opener.postMessage("message", "http://mochi.test:8888");
+ </script>
+<body>
+<iframe name="frame1" src="http://example.org/tests/extensions/cookie/test/file_domain_hierarchy_inner_inner_inner.html"></iframe>
+</body>
+</html>
diff --git a/extensions/cookie/test/file_domain_hierarchy_inner_inner_inner.html b/extensions/cookie/test/file_domain_hierarchy_inner_inner_inner.html
new file mode 100644
index 0000000000..e8d09338b3
--- /dev/null
+++ b/extensions/cookie/test/file_domain_hierarchy_inner_inner_inner.html
@@ -0,0 +1,14 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <META HTTP-EQUIV="Set-Cookie" CONTENT="meta3=tag3">
+ <script type="text/javascript">
+ document.cookie = "can3=has3";
+
+ // send a message to our test document, to say we're done loading
+ window.parent.parent.opener.postMessage("message", "http://mochi.test:8888");
+ </script>
+</head>
+<body>
+</body>
+</html>
diff --git a/extensions/cookie/test/file_domain_inner.html b/extensions/cookie/test/file_domain_inner.html
new file mode 100644
index 0000000000..1a05c45f80
--- /dev/null
+++ b/extensions/cookie/test/file_domain_inner.html
@@ -0,0 +1,14 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <META HTTP-EQUIV="Set-Cookie" CONTENT="meta=tag">
+ <script type="text/javascript">
+ document.cookie = "can=has";
+
+ // send a message to our test document, to say we're done loading
+ window.opener.postMessage("message", "http://mochi.test:8888");
+ </script>
+<body>
+<iframe name="frame1" src="http://example.org/tests/extensions/cookie/test/file_domain_inner_inner.html"></iframe>
+</body>
+</html>
diff --git a/extensions/cookie/test/file_domain_inner_inner.html b/extensions/cookie/test/file_domain_inner_inner.html
new file mode 100644
index 0000000000..7bc0e44a98
--- /dev/null
+++ b/extensions/cookie/test/file_domain_inner_inner.html
@@ -0,0 +1,14 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <META HTTP-EQUIV="Set-Cookie" CONTENT="meta2=tag2">
+ <script type="text/javascript">
+ document.cookie = "can2=has2";
+
+ // send a message to our test document, to say we're done loading
+ window.parent.opener.postMessage("message", "http://mochi.test:8888");
+ </script>
+</head>
+<body>
+</body>
+</html>
diff --git a/extensions/cookie/test/file_image_inner.html b/extensions/cookie/test/file_image_inner.html
new file mode 100644
index 0000000000..aada82d71a
--- /dev/null
+++ b/extensions/cookie/test/file_image_inner.html
@@ -0,0 +1,15 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <META HTTP-EQUIV="Set-Cookie" CONTENT="meta=tag">
+ <script type="text/javascript">
+ document.cookie = "can=has";
+
+ // send a message to our test document, to say we're done loading
+ window.opener.postMessage("message", "http://mochi.test:8888");
+ </script>
+</head>
+<body>
+<iframe name="frame1" src="http://example.org/tests/extensions/cookie/test/file_image_inner_inner.html"></iframe>
+</body>
+</html>
diff --git a/extensions/cookie/test/file_image_inner_inner.html b/extensions/cookie/test/file_image_inner_inner.html
new file mode 100644
index 0000000000..1989b53de7
--- /dev/null
+++ b/extensions/cookie/test/file_image_inner_inner.html
@@ -0,0 +1,20 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <link rel="stylesheet" type="text/css" media="all" href="http://example.org/tests/extensions/cookie/test/test1.css" />
+ <link rel="stylesheet" type="text/css" media="all" href="http://example.com/tests/extensions/cookie/test/test2.css" />
+ <META HTTP-EQUIV="Set-Cookie" CONTENT="meta2=tag2">
+ <script type="text/javascript">
+ function runTest() {
+ document.cookie = "can2=has2";
+
+ // send a message to our test document, to say we're done loading
+ window.parent.opener.postMessage("message", "http://mochi.test:8888");
+ }
+ </script>
+</head>
+<body>
+<img src="http://example.org/tests/extensions/cookie/test/image1.png" onload="runTest()" />
+<img src="http://example.com/tests/extensions/cookie/test/image2.png" onload="runTest()" />
+</body>
+</html>
diff --git a/extensions/cookie/test/file_loadflags_inner.html b/extensions/cookie/test/file_loadflags_inner.html
new file mode 100644
index 0000000000..5a7a6ccc13
--- /dev/null
+++ b/extensions/cookie/test/file_loadflags_inner.html
@@ -0,0 +1,17 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <META HTTP-EQUIV="Set-Cookie" CONTENT="meta=tag">
+ <script type="text/javascript">
+ function runTest() {
+ document.cookie = "can=has";
+
+ // send a message to our test document, to say we're done loading
+ window.opener.postMessage("f_lf_i msg data img", "http://mochi.test:8888");
+ }
+ </script>
+</head>
+<body onload="window.opener.postMessage('f_lf_i msg data page', 'http://mochi.test:8888');">
+<img src="http://example.org/tests/extensions/cookie/test/beltzner.jpg" onload="runTest()" />
+</body>
+</html>
diff --git a/extensions/cookie/test/file_localhost_inner.html b/extensions/cookie/test/file_localhost_inner.html
new file mode 100644
index 0000000000..db9917f000
--- /dev/null
+++ b/extensions/cookie/test/file_localhost_inner.html
@@ -0,0 +1,14 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <META HTTP-EQUIV="Set-Cookie" CONTENT="meta=tag">
+ <script type="text/javascript">
+ document.cookie = "can=has";
+
+ // send a message to our test document, to say we're done loading
+ window.opener.postMessage("message", "http://mochi.test:8888");
+ </script>
+<body>
+<iframe name="frame1" src="http://mochi.test:8888/tests/extensions/cookie/test/file_domain_inner_inner.html"></iframe>
+</body>
+</html>
diff --git a/extensions/cookie/test/file_loopback_inner.html b/extensions/cookie/test/file_loopback_inner.html
new file mode 100644
index 0000000000..e6c115c50b
--- /dev/null
+++ b/extensions/cookie/test/file_loopback_inner.html
@@ -0,0 +1,14 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <META HTTP-EQUIV="Set-Cookie" CONTENT="meta=tag">
+ <script type="text/javascript">
+ document.cookie = "can=has";
+
+ // send a message to our test document, to say we're done loading
+ window.opener.postMessage("message", "http://mochi.test:8888");
+ </script>
+<body>
+<iframe name="frame1" src="http://127.0.0.1:8888/tests/extensions/cookie/test/file_domain_inner_inner.html"></iframe>
+</body>
+</html>
diff --git a/extensions/cookie/test/file_subdomain_inner.html b/extensions/cookie/test/file_subdomain_inner.html
new file mode 100644
index 0000000000..5f608b8110
--- /dev/null
+++ b/extensions/cookie/test/file_subdomain_inner.html
@@ -0,0 +1,14 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <META HTTP-EQUIV="Set-Cookie" CONTENT="meta=tag">
+ <script type="text/javascript">
+ document.cookie = "can=has";
+
+ // send a message to our test document, to say we're done loading
+ window.opener.postMessage("message", "http://mochi.test:8888");
+ </script>
+<body>
+<iframe name="frame1" src="http://test2.example.org/tests/extensions/cookie/test/file_domain_inner_inner.html"></iframe>
+</body>
+</html>
diff --git a/extensions/cookie/test/file_testcommon.js b/extensions/cookie/test/file_testcommon.js
new file mode 100644
index 0000000000..3ee53c52cb
--- /dev/null
+++ b/extensions/cookie/test/file_testcommon.js
@@ -0,0 +1,70 @@
+const SCRIPT_URL = SimpleTest.getTestFileURL("file_chromecommon.js");
+
+var gExpectedCookies;
+var gExpectedLoads;
+
+var gPopup;
+
+var gScript;
+
+var gLoads = 0;
+
+function setupTest(uri, cookies, loads) {
+ SimpleTest.waitForExplicitFinish();
+
+ var prefSet = new Promise(resolve => {
+ SpecialPowers.pushPrefEnv({ set: [["network.cookie.cookieBehavior", 1]] }, resolve);
+ });
+
+ gScript = SpecialPowers.loadChromeScript(SCRIPT_URL);
+ gExpectedCookies = cookies;
+ gExpectedLoads = loads;
+
+ // Listen for MessageEvents.
+ window.addEventListener("message", messageReceiver, false);
+
+ prefSet.then(() => {
+ // load a window which contains an iframe; each will attempt to set
+ // cookies from their respective domains.
+ gPopup = window.open(uri, 'hai', 'width=100,height=100');
+ });
+}
+
+function finishTest() {
+ gScript.destroy();
+ SimpleTest.finish();
+}
+
+/** Receives MessageEvents to this window. */
+// Count and check loads.
+function messageReceiver(evt) {
+ is(evt.data, "message", "message data received from popup");
+ if (evt.data != "message") {
+ gPopup.close();
+ window.removeEventListener("message", messageReceiver, false);
+
+ finishTest();
+ return;
+ }
+
+ // only run the test when all our children are done loading & setting cookies
+ if (++gLoads == gExpectedLoads) {
+ gPopup.close();
+ window.removeEventListener("message", messageReceiver, false);
+
+ runTest();
+ }
+}
+
+// runTest() is run by messageReceiver().
+// Count and check cookies.
+function runTest() {
+ // set a cookie from a domain of "localhost"
+ document.cookie = "oh=hai";
+
+ gScript.addMessageListener("getCookieCountAndClear:return", ({ count }) => {
+ is(count, gExpectedCookies, "total number of cookies");
+ finishTest();
+ });
+ gScript.sendAsyncMessage("getCookieCountAndClear");
+}
diff --git a/extensions/cookie/test/file_testloadflags.js b/extensions/cookie/test/file_testloadflags.js
new file mode 100644
index 0000000000..9cf4a15a6b
--- /dev/null
+++ b/extensions/cookie/test/file_testloadflags.js
@@ -0,0 +1,104 @@
+const SCRIPT_URL = SimpleTest.getTestFileURL('file_testloadflags_chromescript.js');
+
+var gExpectedCookies;
+var gExpectedHeaders;
+var gExpectedLoads;
+
+var gObs;
+var gPopup;
+
+var gHeaders = 0;
+var gLoads = 0;
+
+// setupTest() is run from 'onload='.
+function setupTest(uri, domain, cookies, loads, headers) {
+ info("setupTest uri: " + uri + " domain: " + domain + " cookies: " + cookies +
+ " loads: " + loads + " headers: " + headers);
+
+ SimpleTest.waitForExplicitFinish();
+
+ var prefSet = new Promise(resolve => {
+ SpecialPowers.pushPrefEnv({ set: [["network.cookie.cookieBehavior", 1]] }, resolve);
+ });
+
+ gExpectedCookies = cookies;
+ gExpectedLoads = loads;
+ gExpectedHeaders = headers;
+
+ gScript = SpecialPowers.loadChromeScript(SCRIPT_URL);
+ gScript.addMessageListener("info", ({ str }) => info(str));
+ gScript.addMessageListener("ok", ({ c, m }) => ok(c, m));
+ gScript.addMessageListener("observer:gotCookie", ({ cookie, uri }) => {
+ isnot(cookie.indexOf("oh=hai"), -1,
+ "cookie 'oh=hai' is in header for " + uri);
+ ++gHeaders;
+ });
+
+ var scriptReady = new Promise(resolve => {
+ gScript.addMessageListener("init:return", resolve);
+ gScript.sendAsyncMessage("init", { domain });
+ });
+
+ // Listen for MessageEvents.
+ window.addEventListener("message", messageReceiver, false);
+
+ Promise.all([ prefSet, scriptReady ]).then(() => {
+ // load a window which contains an iframe; each will attempt to set
+ // cookies from their respective domains.
+ gPopup = window.open(uri, 'hai', 'width=100,height=100');
+ });
+}
+
+function finishTest()
+{
+ gScript.addMessageListener("shutdown:return", () => {
+ gScript.destroy();
+ SimpleTest.finish();
+ });
+ gScript.sendAsyncMessage("shutdown");
+}
+
+/** Receives MessageEvents to this window. */
+// Count and check loads.
+function messageReceiver(evt)
+{
+ ok(evt.data == "f_lf_i msg data img" || evt.data == "f_lf_i msg data page",
+ "message data received from popup");
+ if (evt.data == "f_lf_i msg data img") {
+ info("message data received from popup for image");
+ }
+ if (evt.data == "f_lf_i msg data page") {
+ info("message data received from popup for page");
+ }
+ if (evt.data != "f_lf_i msg data img" && evt.data != "f_lf_i msg data page") {
+ info("got this message but don't know what it is " + evt.data);
+ gPopup.close();
+ window.removeEventListener("message", messageReceiver, false);
+
+ finishTest();
+ return;
+ }
+
+ // only run the test when all our children are done loading & setting cookies
+ if (++gLoads == gExpectedLoads) {
+ gPopup.close();
+ window.removeEventListener("message", messageReceiver, false);
+
+ runTest();
+ }
+}
+
+// runTest() is run by messageReceiver().
+// Check headers, and count and check cookies.
+function runTest() {
+ // set a cookie from a domain of "localhost"
+ document.cookie = "o=noes";
+
+ is(gHeaders, gExpectedHeaders, "number of observed request headers");
+ gScript.addMessageListener("getCookieCount:return", ({ count }) => {
+ is(count, gExpectedCookies, "total number of cookies");
+ finishTest();
+ });
+
+ gScript.sendAsyncMessage("getCookieCount");
+}
diff --git a/extensions/cookie/test/file_testloadflags_chromescript.js b/extensions/cookie/test/file_testloadflags_chromescript.js
new file mode 100644
index 0000000000..26eedacd96
--- /dev/null
+++ b/extensions/cookie/test/file_testloadflags_chromescript.js
@@ -0,0 +1,112 @@
+let { classes: Cc, interfaces: Ci } = Components;
+
+var gObs;
+
+function info(s) {
+ sendAsyncMessage("info", { str: String(s) });
+}
+
+function ok(c, m) {
+ sendAsyncMessage("ok", { c, m });
+}
+
+function is(a, b, m) {
+ ok(Object.is(a, b), m + " (" + a + " === " + b + ")");
+}
+
+// Count headers.
+function obs() {
+ info("adding observer");
+
+ this.os = Cc["@mozilla.org/observer-service;1"]
+ .getService(Ci.nsIObserverService);
+ this.os.addObserver(this, "http-on-modify-request", false);
+}
+
+obs.prototype = {
+ observe(theSubject, theTopic, theData) {
+ info("theSubject " + theSubject);
+ info("theTopic " + theTopic);
+ info("theData " + theData);
+
+ var channel = theSubject.QueryInterface(Ci.nsIHttpChannel);
+ info("channel " + channel);
+ try {
+ info("channel.URI " + channel.URI);
+ info("channel.URI.spec " + channel.URI.spec);
+ channel.visitRequestHeaders({
+ visitHeader: function(aHeader, aValue) {
+ info(aHeader + ": " + aValue);
+ }});
+ } catch (err) {
+ ok(false, "catch error " + err);
+ }
+
+ // Ignore notifications we don't care about (like favicons)
+ if (channel.URI.spec.indexOf(
+ "http://example.org/tests/extensions/cookie/test/") == -1) {
+ info("ignoring this one");
+ return;
+ }
+
+ sendAsyncMessage("observer:gotCookie",
+ { cookie: channel.getRequestHeader("Cookie"),
+ uri: channel.URI.spec });
+ },
+
+ remove() {
+ info("removing observer");
+
+ this.os.removeObserver(this, "http-on-modify-request");
+ this.os = null;
+ }
+}
+
+function getCookieCount(cs) {
+ let count = 0;
+ let list = cs.enumerator;
+ while (list.hasMoreElements()) {
+ let cookie = list.getNext().QueryInterface(Ci.nsICookie);
+ info("cookie: " + cookie);
+ info("cookie host " + cookie.host + " path " + cookie.path + " name " + cookie.name +
+ " value " + cookie.value + " isSecure " + cookie.isSecure + " expires " + cookie.expires);
+ ++count;
+ }
+
+ return count;
+}
+
+addMessageListener("init", ({ domain }) => {
+ let cs = Cc["@mozilla.org/cookiemanager;1"]
+ .getService(Ci.nsICookieManager2);
+
+ info("we are going to remove these cookies");
+
+ let count = getCookieCount(cs);
+ info(count + " cookies");
+
+ cs.removeAll();
+ cs.add(domain, "", "oh", "hai", false, false, true, Math.pow(2, 62), {});
+ is(cs.countCookiesFromHost(domain), 1, "number of cookies for domain " + domain);
+
+ gObs = new obs();
+ sendAsyncMessage("init:return");
+});
+
+addMessageListener("getCookieCount", () => {
+ let cs = Cc["@mozilla.org/cookiemanager;1"]
+ .getService(Ci.nsICookieManager);
+ let count = getCookieCount(cs);
+
+ cs.removeAll();
+ sendAsyncMessage("getCookieCount:return", { count });
+});
+
+addMessageListener("shutdown", () => {
+ gObs.remove();
+
+ let cs = Cc["@mozilla.org/cookiemanager;1"]
+ .getService(Ci.nsICookieManager2);
+ cs.removeAll();
+ sendAsyncMessage("shutdown:return");
+});
diff --git a/extensions/cookie/test/image1.png b/extensions/cookie/test/image1.png
new file mode 100644
index 0000000000..272d67c0ce
--- /dev/null
+++ b/extensions/cookie/test/image1.png
Binary files differ
diff --git a/extensions/cookie/test/image1.png^headers^ b/extensions/cookie/test/image1.png^headers^
new file mode 100644
index 0000000000..2390289e01
--- /dev/null
+++ b/extensions/cookie/test/image1.png^headers^
@@ -0,0 +1,3 @@
+Cache-Control: no-store
+Set-Cookie: foo=bar
+
diff --git a/extensions/cookie/test/image2.png b/extensions/cookie/test/image2.png
new file mode 100644
index 0000000000..272d67c0ce
--- /dev/null
+++ b/extensions/cookie/test/image2.png
Binary files differ
diff --git a/extensions/cookie/test/image2.png^headers^ b/extensions/cookie/test/image2.png^headers^
new file mode 100644
index 0000000000..6c0eea5ab6
--- /dev/null
+++ b/extensions/cookie/test/image2.png^headers^
@@ -0,0 +1,3 @@
+Cache-Control: no-store
+Set-Cookie: foo2=bar2
+
diff --git a/extensions/cookie/test/mochitest.ini b/extensions/cookie/test/mochitest.ini
new file mode 100644
index 0000000000..673fad7b8d
--- /dev/null
+++ b/extensions/cookie/test/mochitest.ini
@@ -0,0 +1,41 @@
+[DEFAULT]
+support-files =
+ beltzner.jpg
+ beltzner.jpg^headers^
+ damonbowling.jpg
+ damonbowling.jpg^headers^
+ file_chromecommon.js
+ file_domain_hierarchy_inner.html
+ file_domain_hierarchy_inner_inner.html
+ file_domain_hierarchy_inner_inner_inner.html
+ file_domain_inner.html
+ file_domain_inner_inner.html
+ file_image_inner.html
+ file_image_inner_inner.html
+ file_loadflags_inner.html
+ file_localhost_inner.html
+ file_loopback_inner.html
+ file_subdomain_inner.html
+ file_testcommon.js
+ file_testloadflags.js
+ file_testloadflags_chromescript.js
+ image1.png
+ image1.png^headers^
+ image2.png
+ image2.png^headers^
+ test1.css
+ test1.css^headers^
+ test2.css
+ test2.css^headers^
+
+[test_different_domain_in_hierarchy.html]
+[test_differentdomain.html]
+[test_image.html]
+[test_loadflags.html]
+[test_same_base_domain.html]
+[test_same_base_domain_2.html]
+[test_same_base_domain_3.html]
+[test_same_base_domain_4.html]
+[test_same_base_domain_5.html]
+[test_same_base_domain_6.html]
+[test_samedomain.html]
diff --git a/extensions/cookie/test/moz.build b/extensions/cookie/test/moz.build
new file mode 100644
index 0000000000..7474661d8c
--- /dev/null
+++ b/extensions/cookie/test/moz.build
@@ -0,0 +1,15 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+XPCSHELL_TESTS_MANIFESTS += [
+ 'unit/xpcshell.ini',
+ 'unit_ipc/xpcshell.ini',
+]
+
+MOCHITEST_MANIFESTS += ['mochitest.ini']
+
+BROWSER_CHROME_MANIFESTS += ['browser.ini']
+
diff --git a/extensions/cookie/test/test1.css b/extensions/cookie/test/test1.css
new file mode 100644
index 0000000000..139597f9cb
--- /dev/null
+++ b/extensions/cookie/test/test1.css
@@ -0,0 +1,2 @@
+
+
diff --git a/extensions/cookie/test/test1.css^headers^ b/extensions/cookie/test/test1.css^headers^
new file mode 100644
index 0000000000..729babb5a4
--- /dev/null
+++ b/extensions/cookie/test/test1.css^headers^
@@ -0,0 +1,3 @@
+Cache-Control: no-cache
+Set-Cookie: css=bar
+
diff --git a/extensions/cookie/test/test2.css b/extensions/cookie/test/test2.css
new file mode 100644
index 0000000000..139597f9cb
--- /dev/null
+++ b/extensions/cookie/test/test2.css
@@ -0,0 +1,2 @@
+
+
diff --git a/extensions/cookie/test/test2.css^headers^ b/extensions/cookie/test/test2.css^headers^
new file mode 100644
index 0000000000..b12d32c72b
--- /dev/null
+++ b/extensions/cookie/test/test2.css^headers^
@@ -0,0 +1,3 @@
+Cache-Control: no-cache
+Set-Cookie: css2=bar2
+
diff --git a/extensions/cookie/test/test_different_domain_in_hierarchy.html b/extensions/cookie/test/test_different_domain_in_hierarchy.html
new file mode 100644
index 0000000000..61bc585397
--- /dev/null
+++ b/extensions/cookie/test/test_different_domain_in_hierarchy.html
@@ -0,0 +1,15 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test cookie requests from within a window hierarchy of different base domains</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body onload="setupTest('http://example.org/tests/extensions/cookie/test/file_domain_hierarchy_inner.html', 3, 3)">
+<p id="display"></p>
+<pre id="test">
+<script class="testbody" type="text/javascript" src="file_testcommon.js">
+</script>
+</pre>
+</body>
+</html>
diff --git a/extensions/cookie/test/test_differentdomain.html b/extensions/cookie/test/test_differentdomain.html
new file mode 100644
index 0000000000..a74bceaf43
--- /dev/null
+++ b/extensions/cookie/test/test_differentdomain.html
@@ -0,0 +1,15 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test for Cross domain access to properties</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body onload="setupTest('http://example.com/tests/extensions/cookie/test/file_domain_inner.html', 3, 2)">
+<p id="display"></p>
+<pre id="test">
+<script class="testbody" type="text/javascript" src="file_testcommon.js">
+</script>
+</pre>
+</body>
+</html>
diff --git a/extensions/cookie/test/test_image.html b/extensions/cookie/test/test_image.html
new file mode 100644
index 0000000000..f39a19894e
--- /dev/null
+++ b/extensions/cookie/test/test_image.html
@@ -0,0 +1,14 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test for Cross domain access to properties</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body onload="setupTest('http://example.org/tests/extensions/cookie/test/file_image_inner.html', 7, 3)">
+<p id="display"></p>
+<pre id="test">
+<script class="testbody" type="text/javascript" src="file_testcommon.js"></script>
+</pre>
+</body>
+</html>
diff --git a/extensions/cookie/test/test_loadflags.html b/extensions/cookie/test/test_loadflags.html
new file mode 100644
index 0000000000..3eea710f27
--- /dev/null
+++ b/extensions/cookie/test/test_loadflags.html
@@ -0,0 +1,21 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test for Cross domain access to properties</title>
+ <script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<!--
+ *5 cookies: 1+1 from file_testloadflags.js, 2 from file_loadflags_inner.html + 1 from beltzner.jpg.
+ *1 load: file_loadflags_inner.html.
+ *2 headers: 1 for file_loadflags_inner.html + 1 for beltzner.jpg.
+ -->
+<body onload="setupTest('http://example.org/tests/extensions/cookie/test/file_loadflags_inner.html', 'example.org', 5, 2, 2)">
+<p id="display"></p>
+<pre id="test">
+<script class="testbody" type="text/javascript" src="file_testloadflags.js">
+</script>
+</pre>
+</body>
+</html>
diff --git a/extensions/cookie/test/test_same_base_domain.html b/extensions/cookie/test/test_same_base_domain.html
new file mode 100644
index 0000000000..9232b4e09b
--- /dev/null
+++ b/extensions/cookie/test/test_same_base_domain.html
@@ -0,0 +1,15 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test for Cross domain access to properties</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body onload="setupTest('http://test1.example.org/tests/extensions/cookie/test/file_domain_inner.html', 5, 2)">
+<p id="display"></p>
+<pre id="test">
+<script class="testbody" type="text/javascript" src="file_testcommon.js">
+</script>
+</pre>
+</body>
+</html>
diff --git a/extensions/cookie/test/test_same_base_domain_2.html b/extensions/cookie/test/test_same_base_domain_2.html
new file mode 100644
index 0000000000..d692524c5b
--- /dev/null
+++ b/extensions/cookie/test/test_same_base_domain_2.html
@@ -0,0 +1,15 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test for Cross domain access to properties</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body onload="setupTest('http://test1.example.org/tests/extensions/cookie/test/file_subdomain_inner.html', 5, 2)">
+<p id="display"></p>
+<pre id="test">
+<script class="testbody" type="text/javascript" src="file_testcommon.js">
+</script>
+</pre>
+</body>
+</html>
diff --git a/extensions/cookie/test/test_same_base_domain_3.html b/extensions/cookie/test/test_same_base_domain_3.html
new file mode 100644
index 0000000000..f0cd687f91
--- /dev/null
+++ b/extensions/cookie/test/test_same_base_domain_3.html
@@ -0,0 +1,15 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test for Cross domain access to properties</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body onload="setupTest('http://example.org/tests/extensions/cookie/test/file_subdomain_inner.html', 5, 2)">
+<p id="display"></p>
+<pre id="test">
+<script class="testbody" type="text/javascript" src="file_testcommon.js">
+</script>
+</pre>
+</body>
+</html>
diff --git a/extensions/cookie/test/test_same_base_domain_4.html b/extensions/cookie/test/test_same_base_domain_4.html
new file mode 100644
index 0000000000..0a4026fe25
--- /dev/null
+++ b/extensions/cookie/test/test_same_base_domain_4.html
@@ -0,0 +1,15 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test for Cross domain access to properties</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body onload="setupTest('http://mochi.test:8888/tests/extensions/cookie/test/file_localhost_inner.html', 5, 2)">
+<p id="display"></p>
+<pre id="test">
+<script class="testbody" type="text/javascript" src="file_testcommon.js">
+</script>
+</pre>
+</body>
+</html>
diff --git a/extensions/cookie/test/test_same_base_domain_5.html b/extensions/cookie/test/test_same_base_domain_5.html
new file mode 100644
index 0000000000..49b2379b62
--- /dev/null
+++ b/extensions/cookie/test/test_same_base_domain_5.html
@@ -0,0 +1,15 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test for Cross domain access to properties</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body onload="setupTest('http://sub1.test1.example.org/tests/extensions/cookie/test/file_subdomain_inner.html', 5, 2)">
+<p id="display"></p>
+<pre id="test">
+<script class="testbody" type="text/javascript" src="file_testcommon.js">
+</script>
+</pre>
+</body>
+</html>
diff --git a/extensions/cookie/test/test_same_base_domain_6.html b/extensions/cookie/test/test_same_base_domain_6.html
new file mode 100644
index 0000000000..9056739a64
--- /dev/null
+++ b/extensions/cookie/test/test_same_base_domain_6.html
@@ -0,0 +1,15 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test for Cross domain access to properties</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body onload="setupTest('http://127.0.0.1:8888/tests/extensions/cookie/test/file_loopback_inner.html', 5, 2)">
+<p id="display"></p>
+<pre id="test">
+<script class="testbody" type="text/javascript" src="file_testcommon.js">
+</script>
+</pre>
+</body>
+</html>
diff --git a/extensions/cookie/test/test_samedomain.html b/extensions/cookie/test/test_samedomain.html
new file mode 100644
index 0000000000..9051d3b396
--- /dev/null
+++ b/extensions/cookie/test/test_samedomain.html
@@ -0,0 +1,15 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test for Cross domain access to properties</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body onload="setupTest('http://example.org/tests/extensions/cookie/test/file_domain_inner.html', 5, 2)">
+<p id="display"></p>
+<pre id="test">
+<script class="testbody" type="text/javascript" src="file_testcommon.js">
+</script>
+</pre>
+</body>
+</html>
diff --git a/extensions/cookie/test/unit/cookieprompt.js b/extensions/cookie/test/unit/cookieprompt.js
new file mode 100644
index 0000000000..c0a17e0060
--- /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 0000000000..5a3bcad40a
--- /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 0000000000..fe4c0a53dc
--- /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 0000000000..0eac1d4929
--- /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 0000000000..1d99cb2334
--- /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 0000000000..2400dea51d
--- /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 0000000000..628553a4e6
--- /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 0000000000..d6de81b728
--- /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 0000000000..ab35f8ef78
--- /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 0000000000..bae6e3a59a
--- /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 0000000000..b389ad8cc2
--- /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 0000000000..2de1de628d
--- /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 0000000000..a39f807f22
--- /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 0000000000..36e5fb11a3
--- /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 0000000000..349f6d77e1
--- /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 0000000000..7b15502083
--- /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 0000000000..faa2579f52
--- /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 0000000000..604f2b4d91
--- /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 0000000000..7ab8e88ae2
--- /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 0000000000..7fec418260
--- /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 0000000000..d01b51923d
--- /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 0000000000..72fc06bbaa
--- /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 0000000000..8d36a9667e
--- /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 0000000000..b9c803f5c2
--- /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 0000000000..c15288890c
--- /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 0000000000..88578166ca
--- /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 0000000000..4749422da3
--- /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 0000000000..38646660a6
--- /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 0000000000..4d38d72e89
--- /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 0000000000..0e5820d2bf
--- /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 0000000000..aa634c7ebc
--- /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 0000000000..7b138ff290
--- /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 0000000000..56d5e23477
--- /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 0000000000..5708fe8ec0
--- /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 0000000000..54294a9157
--- /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 0000000000..152409de24
--- /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 0000000000..e8f5817e24
--- /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 0000000000..719f7f6f69
--- /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 0000000000..4e78e4d466
--- /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 0000000000..7dc5e823c6
--- /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 0000000000..40a23f7e47
--- /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 0000000000..e20d0142cc
--- /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]
diff --git a/extensions/cookie/test/unit_ipc/test_child.js b/extensions/cookie/test/unit_ipc/test_child.js
new file mode 100644
index 0000000000..40a48d8b80
--- /dev/null
+++ b/extensions/cookie/test/unit_ipc/test_child.js
@@ -0,0 +1,59 @@
+var Ci = Components.interfaces;
+var Cc = Components.classes;
+var Cr = Components.results;
+
+var gIoService = Components.classes["@mozilla.org/network/io-service;1"]
+ .getService(Components.interfaces.nsIIOService);
+
+function isParentProcess() {
+ let appInfo = Cc["@mozilla.org/xre/app-info;1"];
+ return (!appInfo || appInfo.getService(Ci.nsIXULRuntime).processType == Ci.nsIXULRuntime.PROCESS_TYPE_DEFAULT);
+}
+
+function getPrincipalForURI(aURI) {
+ var uri = gIoService.newURI(aURI, null, null);
+ var ssm = Cc["@mozilla.org/scriptsecuritymanager;1"]
+ .getService(Ci.nsIScriptSecurityManager);
+ return ssm.createCodebasePrincipal(uri, {});
+}
+
+function run_test() {
+ if (!isParentProcess()) {
+ const Ci = Components.interfaces;
+ const Cc = Components.classes;
+
+ var mM = Cc["@mozilla.org/childprocessmessagemanager;1"].
+ getService(Ci.nsISyncMessageSender);
+
+ var messageListener = {
+ receiveMessage: function(aMessage) {
+ switch(aMessage.name) {
+ case "TESTING:Stage2A":
+ // Permissions created after the child is present
+ do_check_eq(pm.testPermissionFromPrincipal(getPrincipalForURI("http://mozilla.org"), "cookie1"), pm.ALLOW_ACTION);
+ do_check_eq(pm.testPermissionFromPrincipal(getPrincipalForURI("http://mozilla.com"), "cookie2"), pm.DENY_ACTION);
+ do_check_eq(pm.testPermissionFromPrincipal(getPrincipalForURI("http://mozilla.net"), "cookie3"), pm.ALLOW_ACTION);
+ do_check_eq(pm.testPermissionFromPrincipal(getPrincipalForURI("http://firefox.org"), "cookie1"), pm.ALLOW_ACTION);
+ do_check_eq(pm.testPermissionFromPrincipal(getPrincipalForURI("http://firefox.com"), "cookie2"), pm.DENY_ACTION);
+ do_check_eq(pm.testPermissionFromPrincipal(getPrincipalForURI("http://firefox.net"), "cookie3"), pm.ALLOW_ACTION);
+
+ mM.sendAsyncMessage("TESTING:Stage3");
+ break;
+
+ }
+ return true;
+ },
+ };
+
+ mM.addMessageListener("TESTING:Stage2A", messageListener);
+
+ // Permissions created before the child is present
+ var pm = Cc["@mozilla.org/permissionmanager;1"].getService(Ci.nsIPermissionManager);
+ do_check_eq(pm.testPermissionFromPrincipal(getPrincipalForURI("http://mozilla.org"), "cookie1"), pm.ALLOW_ACTION);
+ do_check_eq(pm.testPermissionFromPrincipal(getPrincipalForURI("http://mozilla.com"), "cookie2"), pm.DENY_ACTION);
+ do_check_eq(pm.testPermissionFromPrincipal(getPrincipalForURI("http://mozilla.net"), "cookie3"), pm.ALLOW_ACTION);
+
+ mM.sendAsyncMessage("TESTING:Stage2");
+ }
+}
+
diff --git a/extensions/cookie/test/unit_ipc/test_parent.js b/extensions/cookie/test/unit_ipc/test_parent.js
new file mode 100644
index 0000000000..5423dd5942
--- /dev/null
+++ b/extensions/cookie/test/unit_ipc/test_parent.js
@@ -0,0 +1,59 @@
+var Ci = Components.interfaces;
+var Cc = Components.classes;
+var Cr = Components.results;
+
+var gIoService = Components.classes["@mozilla.org/network/io-service;1"]
+ .getService(Components.interfaces.nsIIOService);
+
+function isParentProcess() {
+ let appInfo = Cc["@mozilla.org/xre/app-info;1"];
+ return (!appInfo || appInfo.getService(Ci.nsIXULRuntime).processType == Ci.nsIXULRuntime.PROCESS_TYPE_DEFAULT);
+}
+
+function getPrincipalForURI(aURI) {
+ var uri = gIoService.newURI(aURI, null, null);
+ var ssm = Cc["@mozilla.org/scriptsecuritymanager;1"]
+ .getService(Ci.nsIScriptSecurityManager);
+ return ssm.createCodebasePrincipal(uri, {});
+}
+
+function run_test() {
+ if (isParentProcess()) {
+ var pm = Cc["@mozilla.org/permissionmanager;1"].getService(Ci.nsIPermissionManager);
+
+ // Permissions created before the child is present
+ pm.addFromPrincipal(getPrincipalForURI("http://mozilla.org"), "cookie1", pm.ALLOW_ACTION, pm.EXPIRE_NEVER, 0);
+ pm.addFromPrincipal(getPrincipalForURI("http://mozilla.com"), "cookie2", pm.DENY_ACTION, pm.EXPIRE_SESSION, 0);
+ pm.addFromPrincipal(getPrincipalForURI("http://mozilla.net"), "cookie3", pm.ALLOW_ACTION, pm.EXPIRE_TIME, Date.now() + 1000*60*60*24);
+
+ var mM = Cc["@mozilla.org/parentprocessmessagemanager;1"].
+ getService(Ci.nsIMessageBroadcaster);
+
+ var messageListener = {
+ receiveMessage: function(aMessage) {
+ switch(aMessage.name) {
+ case "TESTING:Stage2":
+ // Permissions created after the child is present
+ pm.addFromPrincipal(getPrincipalForURI("http://firefox.org"), "cookie1", pm.ALLOW_ACTION, pm.EXPIRE_NEVER, 0);
+ pm.addFromPrincipal(getPrincipalForURI("http://firefox.com"), "cookie2", pm.DENY_ACTION, pm.EXPIRE_SESSION, 0);
+ pm.addFromPrincipal(getPrincipalForURI("http://firefox.net"), "cookie3", pm.ALLOW_ACTION, pm.EXPIRE_TIME, Date.now() + 1000*60*60*24);
+ mM.broadcastAsyncMessage("TESTING:Stage2A");
+ break;
+
+ case "TESTING:Stage3":
+ do_test_finished();
+ break;
+ }
+ return true;
+ },
+ };
+
+ mM.addMessageListener("TESTING:Stage2", messageListener);
+ mM.addMessageListener("TESTING:Stage3", messageListener);
+
+ do_test_pending();
+ do_load_child_test_harness();
+ run_test_in_child("test_child.js");
+ }
+}
+
diff --git a/extensions/cookie/test/unit_ipc/xpcshell.ini b/extensions/cookie/test/unit_ipc/xpcshell.ini
new file mode 100644
index 0000000000..5640ea0e30
--- /dev/null
+++ b/extensions/cookie/test/unit_ipc/xpcshell.ini
@@ -0,0 +1,7 @@
+[DEFAULT]
+head =
+tail =
+skip-if = toolkit == 'android'
+
+[test_child.js]
+[test_parent.js]