summaryrefslogtreecommitdiffstats
path: root/toolkit/components/extensions/test/mochitest/head_cookies.js
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--toolkit/components/extensions/test/mochitest/head_cookies.js287
1 files changed, 287 insertions, 0 deletions
diff --git a/toolkit/components/extensions/test/mochitest/head_cookies.js b/toolkit/components/extensions/test/mochitest/head_cookies.js
new file mode 100644
index 0000000000..610c800c94
--- /dev/null
+++ b/toolkit/components/extensions/test/mochitest/head_cookies.js
@@ -0,0 +1,287 @@
+/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set sts=2 sw=2 et tw=80: */
+"use strict";
+
+/* exported testCookies */
+/* import-globals-from head.js */
+
+async function testCookies(options) {
+ // Changing the options object is a bit of a hack, but it allows us to easily
+ // pass an expiration date to the background script.
+ options.expiry = Date.now() / 1000 + 3600;
+
+ async function background(backgroundOptions) {
+ // Ask the parent scope to change some cookies we may or may not have
+ // permission for.
+ let awaitChanges = new Promise(resolve => {
+ browser.test.onMessage.addListener(msg => {
+ browser.test.assertEq("cookies-changed", msg, "browser.test.onMessage");
+ resolve();
+ });
+ });
+
+ let changed = [];
+ browser.cookies.onChanged.addListener(event => {
+ changed.push(`${event.cookie.name}:${event.cause}`);
+ });
+ browser.test.sendMessage("change-cookies");
+
+ // Try to access some cookies in various ways.
+ let { url, domain, secure } = backgroundOptions;
+
+ let failures = 0;
+ let tallyFailure = error => {
+ failures++;
+ };
+
+ try {
+ await awaitChanges;
+
+ let cookie = await browser.cookies.get({ url, name: "foo" });
+ browser.test.assertEq(
+ backgroundOptions.shouldPass,
+ cookie != null,
+ "should pass == get cookie"
+ );
+
+ let cookies = await browser.cookies.getAll({ domain });
+ if (backgroundOptions.shouldPass) {
+ browser.test.assertEq(2, cookies.length, "expected number of cookies");
+ } else {
+ browser.test.assertEq(0, cookies.length, "expected number of cookies");
+ }
+
+ await Promise.all([
+ browser.cookies
+ .set({
+ url,
+ domain,
+ secure,
+ name: "foo",
+ value: "baz",
+ expirationDate: backgroundOptions.expiry,
+ })
+ .catch(tallyFailure),
+ browser.cookies
+ .set({
+ url,
+ domain,
+ secure,
+ name: "bar",
+ value: "quux",
+ expirationDate: backgroundOptions.expiry,
+ })
+ .catch(tallyFailure),
+ browser.cookies.remove({ url, name: "deleted" }),
+ ]);
+
+ if (backgroundOptions.shouldPass) {
+ // The order of eviction events isn't guaranteed, so just check that
+ // it's there somewhere.
+ let evicted = changed.indexOf("evicted:evicted");
+ if (evicted < 0) {
+ browser.test.fail("got no eviction event");
+ } else {
+ browser.test.succeed("got eviction event");
+ changed.splice(evicted, 1);
+ }
+
+ browser.test.assertEq(
+ "x:explicit,x:overwrite,x:explicit,x:explicit,foo:overwrite,foo:explicit,bar:explicit,deleted:explicit",
+ changed.join(","),
+ "expected changes"
+ );
+ } else {
+ browser.test.assertEq("", changed.join(","), "expected no changes");
+ }
+
+ if (!(backgroundOptions.shouldPass || backgroundOptions.shouldWrite)) {
+ browser.test.assertEq(2, failures, "Expected failures");
+ } else {
+ browser.test.assertEq(0, failures, "Expected no failures");
+ }
+
+ browser.test.notifyPass("cookie-permissions");
+ } catch (error) {
+ browser.test.fail(`Error: ${error} :: ${error.stack}`);
+ browser.test.notifyFail("cookie-permissions");
+ }
+ }
+
+ let extension = ExtensionTestUtils.loadExtension({
+ manifest: {
+ permissions: options.permissions,
+ },
+
+ background: `(${background})(${JSON.stringify(options)})`,
+ });
+
+ let stepOne = loadChromeScript(() => {
+ const { addMessageListener, sendAsyncMessage } = this;
+ addMessageListener("options", options => {
+ let domain = options.domain.replace(/^\.?/, ".");
+ // This will be evicted after we add a fourth cookie.
+ Services.cookies.add(
+ domain,
+ "/",
+ "evicted",
+ "bar",
+ options.secure,
+ false,
+ false,
+ options.expiry,
+ {},
+ Ci.nsICookie.SAMESITE_NONE,
+ options.url.startsWith("https")
+ ? Ci.nsICookie.SCHEME_HTTPS
+ : Ci.nsICookie.SCHEME_HTTP
+ );
+ // This will be modified by the background script.
+ Services.cookies.add(
+ domain,
+ "/",
+ "foo",
+ "bar",
+ options.secure,
+ false,
+ false,
+ options.expiry,
+ {},
+ Ci.nsICookie.SAMESITE_NONE,
+ options.url.startsWith("https")
+ ? Ci.nsICookie.SCHEME_HTTPS
+ : Ci.nsICookie.SCHEME_HTTP
+ );
+ // This will be deleted by the background script.
+ Services.cookies.add(
+ domain,
+ "/",
+ "deleted",
+ "bar",
+ options.secure,
+ false,
+ false,
+ options.expiry,
+ {},
+ Ci.nsICookie.SAMESITE_NONE,
+ options.url.startsWith("https")
+ ? Ci.nsICookie.SCHEME_HTTPS
+ : Ci.nsICookie.SCHEME_HTTP
+ );
+ sendAsyncMessage("done");
+ });
+ });
+ stepOne.sendAsyncMessage("options", options);
+ await stepOne.promiseOneMessage("done");
+ stepOne.destroy();
+
+ await extension.startup();
+
+ await extension.awaitMessage("change-cookies");
+
+ let stepTwo = loadChromeScript(() => {
+ const { addMessageListener, sendAsyncMessage } = this;
+ addMessageListener("options", options => {
+ let domain = options.domain.replace(/^\.?/, ".");
+
+ Services.cookies.add(
+ domain,
+ "/",
+ "x",
+ "y",
+ options.secure,
+ false,
+ false,
+ options.expiry,
+ {},
+ Ci.nsICookie.SAMESITE_NONE,
+ options.url.startsWith("https")
+ ? Ci.nsICookie.SCHEME_HTTPS
+ : Ci.nsICookie.SCHEME_HTTP
+ );
+ Services.cookies.add(
+ domain,
+ "/",
+ "x",
+ "z",
+ options.secure,
+ false,
+ false,
+ options.expiry,
+ {},
+ Ci.nsICookie.SAMESITE_NONE,
+ options.url.startsWith("https")
+ ? Ci.nsICookie.SCHEME_HTTPS
+ : Ci.nsICookie.SCHEME_HTTP
+ );
+ Services.cookies.remove(domain, "x", "/", {});
+ sendAsyncMessage("done");
+ });
+ });
+ stepTwo.sendAsyncMessage("options", options);
+ await stepTwo.promiseOneMessage("done");
+ stepTwo.destroy();
+
+ extension.sendMessage("cookies-changed");
+
+ await extension.awaitFinish("cookie-permissions");
+ await extension.unload();
+
+ let stepThree = loadChromeScript(() => {
+ const { addMessageListener, sendAsyncMessage, assert } = this;
+ let cookieSvc = Services.cookies;
+
+ function getCookies(host) {
+ let cookies = [];
+ for (let cookie of cookieSvc.getCookiesFromHost(host, {})) {
+ cookies.push(cookie);
+ }
+ return cookies.sort((a, b) => a.name.localeCompare(b.name));
+ }
+
+ addMessageListener("options", options => {
+ let cookies = getCookies(options.domain);
+
+ if (options.shouldPass) {
+ assert.equal(cookies.length, 2, "expected two cookies for host");
+
+ assert.equal(cookies[0].name, "bar", "correct cookie name");
+ assert.equal(cookies[0].value, "quux", "correct cookie value");
+
+ assert.equal(cookies[1].name, "foo", "correct cookie name");
+ assert.equal(cookies[1].value, "baz", "correct cookie value");
+ } else if (options.shouldWrite) {
+ // Note: |shouldWrite| applies only when |shouldPass| is false.
+ // This is necessary because, unfortunately, websites (and therefore web
+ // extensions) are allowed to write some cookies which they're not allowed
+ // to read.
+ assert.equal(cookies.length, 3, "expected three cookies for host");
+
+ assert.equal(cookies[0].name, "bar", "correct cookie name");
+ assert.equal(cookies[0].value, "quux", "correct cookie value");
+
+ assert.equal(cookies[1].name, "deleted", "correct cookie name");
+
+ assert.equal(cookies[2].name, "foo", "correct cookie name");
+ assert.equal(cookies[2].value, "baz", "correct cookie value");
+ } else {
+ assert.equal(cookies.length, 2, "expected two cookies for host");
+
+ assert.equal(cookies[0].name, "deleted", "correct second cookie name");
+
+ assert.equal(cookies[1].name, "foo", "correct cookie name");
+ assert.equal(cookies[1].value, "bar", "correct cookie value");
+ }
+
+ for (let cookie of cookies) {
+ cookieSvc.remove(cookie.host, cookie.name, "/", {});
+ }
+ // Make sure we don't silently poison subsequent tests if something goes wrong.
+ assert.equal(getCookies(options.domain).length, 0, "cookies cleared");
+ sendAsyncMessage("done");
+ });
+ });
+ stepThree.sendAsyncMessage("options", options);
+ await stepThree.promiseOneMessage("done");
+ stepThree.destroy();
+}