rfc6265bis tests

Tests for https://tools.ietf.org/html/draft-ietf-httpbis-rfc6265bis.
diff --git a/cookies/prefix/__host.document-cookie.non-secure.html b/cookies/prefix/__host.document-cookie.non-secure.html
new file mode 100644
index 0000000..e1a272a
--- /dev/null
+++ b/cookies/prefix/__host.document-cookie.non-secure.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/cookies/resources/cookie-helper.sub.js"></script>
+<script>
+  ["", "domain="+document.location.hostname, "MaxAge=10", "HttpOnly"].forEach(extraParams => {
+    // Without 'secure'
+    set_prefixed_cookie_via_dom_test({
+      prefix: "__Host-",
+      params: "Path=/;" + extraParams,
+      shouldExistInDOM: false,
+      shouldExistViaHTTP: false,
+      title: "__Host: Non-secure origin: 'Path=/;" + extraParams + "'"
+    });
+
+    // With 'secure'
+    set_prefixed_cookie_via_dom_test({
+      prefix: "__Host-",
+      params: "Secure; Path=/;" + extraParams,
+      shouldExistInDOM: false,
+      shouldExistViaHTTP: false,
+      title: "__Host: Non-secure origin: 'Secure; Path=/;" + extraParams + "'"
+    });
+  });
+
+  set_prefixed_cookie_via_dom_test({
+    prefix: "__Host-",
+    params: "Secure; Path=/cookies/resources/list.py",
+    shouldExistInDOM: false,
+    shouldExistViaHTTP: false,
+    title: "__Host: Non-secure origin: 'Secure; Path=/cookies/resources/list.py'"
+  });
+</script>
diff --git a/cookies/prefix/__host.http.non-secure.html b/cookies/prefix/__host.http.non-secure.html
new file mode 100644
index 0000000..3ce5873
--- /dev/null
+++ b/cookies/prefix/__host.http.non-secure.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/cookies/resources/cookie-helper.sub.js"></script>
+<script>
+  ["", "domain="+document.location.hostname, "MaxAge=10", "HttpOnly"].forEach(extraParams => {
+    // Without 'secure'
+    set_prefixed_cookie_via_http_test({
+      prefix: "__Host-",
+      params: "Path=/;" + extraParams,
+      shouldExistInDOM: false,
+      shouldExistViaHTTP: false,
+      title: "__Host: Non-secure origin: 'Path=/;" + extraParams + "'"
+    });
+
+    // With 'secure'
+    set_prefixed_cookie_via_http_test({
+      prefix: "__Host-",
+      params: "Secure; Path=/;" + extraParams,
+      shouldExistInDOM: false,
+      shouldExistViaHTTP: false,
+      title: "__Host: Non-secure origin: 'Secure; Path=/;" + extraParams + "'"
+    });
+  });
+
+  set_prefixed_cookie_via_http_test({
+    prefix: "__Host-",
+    params: "Secure; Path=/cookies/resources/list.py",
+    shouldExistInDOM: false,
+    shouldExistViaHTTP: false,
+    title: "__Host: Non-secure origin: 'Secure; Path=/cookies/resources/list.py'"
+  });
+</script>
+
diff --git a/cookies/prefix/__secure.document-cookie.non-secure.html b/cookies/prefix/__secure.document-cookie.non-secure.html
new file mode 100644
index 0000000..bf898f4
--- /dev/null
+++ b/cookies/prefix/__secure.document-cookie.non-secure.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/cookies/resources/cookie-helper.sub.js"></script>
+<script>
+  ["", "domain="+document.location.hostname, "MaxAge=10", "HttpOnly"].forEach(extraParams => {
+    // Without 'secure'
+    set_prefixed_cookie_via_dom_test({
+      prefix: "__Secure-",
+      params: "Path=/;" + extraParams,
+      shouldExistInDOM: false,
+      shouldExistViaHTTP: false,
+      title: "__Secure: Non-secure origin: 'Path=/;" + extraParams + "'"
+    });
+
+    // With 'secure'
+    set_prefixed_cookie_via_dom_test({
+      prefix: "__Secure-",
+      params: "Secure; Path=/;" + extraParams,
+      shouldExistInDOM: false,
+      shouldExistViaHTTP: false,
+      title: "__Secure: Non-secure origin: 'Secure; Path=/;" + extraParams + "'"
+    });
+  });
+</script>
diff --git a/cookies/prefix/__secure.http.non-secure.html b/cookies/prefix/__secure.http.non-secure.html
new file mode 100644
index 0000000..af844a9
--- /dev/null
+++ b/cookies/prefix/__secure.http.non-secure.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/cookies/resources/cookie-helper.sub.js"></script>
+<script>
+  ["", "domain="+document.location.hostname, "MaxAge=10", "HttpOnly"].forEach(extraParams => {
+    // Without 'secure'
+    set_prefixed_cookie_via_http_test({
+      prefix: "__Secure-",
+      params: "Path=/;" + extraParams,
+      shouldExistInDOM: false,
+      shouldExistViaHTTP: false,
+      title: "__Secure: Non-secure origin: 'Path=/;" + extraParams + "'"
+    });
+
+    // With 'secure'
+    set_prefixed_cookie_via_http_test({
+      prefix: "__Secure-",
+      params: "Secure; Path=/;" + extraParams,
+      shouldExistInDOM: false,
+      shouldExistViaHTTP: false,
+      title: "__Secure: Non-secure origin: 'Secure; Path=/;" + extraParams + "'"
+    });
+  });
+</script>
diff --git a/cookies/prefix/__secure.http.secure.html b/cookies/prefix/__secure.http.secure.html
new file mode 100644
index 0000000..4b413e9
--- /dev/null
+++ b/cookies/prefix/__secure.http.secure.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/cookies/resources/cookie-helper.sub.js"></script>
+<script>
+  ["", "domain="+CROSS_SITE_HOST, "MaxAge=10", "HttpOnly"].forEach(extraParams => {
+    // Without 'secure'
+    set_prefixed_cookie_via_http_test({
+      origin: SECURE_CROSS_SITE_ORIGIN,
+      prefix: "__Secure-",
+      params: "Path=/;" + extraParams,
+      shouldExistInDOM: false,
+      shouldExistViaHTTP: false,
+      title: "__Secure: secure origin: 'Path=/;" + extraParams + "'"
+    });
+
+    // With 'secure'
+    set_prefixed_cookie_via_http_test({
+      origin: SECURE_CROSS_SITE_ORIGIN,
+      prefix: "__Secure-",
+      params: "Secure;Path=/;" + extraParams,
+      shouldExistInDOM: false,
+      shouldExistViaHTTP: true,
+      title: "__Secure: secure origin: 'Secure;Path=/;" + extraParams + "'"
+    });
+  });
+</script>
diff --git a/cookies/prefix/document-cookie.non-secure.html b/cookies/prefix/document-cookie.non-secure.html
new file mode 100644
index 0000000..bc6832b
--- /dev/null
+++ b/cookies/prefix/document-cookie.non-secure.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/cookies/resources/cookie-helper.sub.js"></script>
+<script>
+  function create_test(prefix, params, shouldExistInDOM, shouldExistViaHTTP, title) {
+    promise_test(t => {
+      var name = prefix + "prefixtestcookie";
+      erase_cookie_from_js(name);
+      var value = "" + Math.random();
+      document.cookie = name + "=" + value + ";" + params;
+
+      assert_dom_cookie(name, value, shouldExistInDOM);
+
+      return credFetch("/cookies/rfx6265bis/resources/list.py")
+        .then(r => r.json())
+        .then(cookies => assert_equals(cookies[name], shouldExistViaHTTP ? value : undefined));
+    }, title);
+  }
+
+  // No prefix
+  create_test("", "path=/", true, true, "No prefix, root path, no special behavior");
+  create_test("", "path=/;domain=" + document.location.hostname, true, true, "No prefix, domain, no special behavior");
+
+  // `__Secure-` Prefix
+  ["", "domain="+document.location.hostname, "MaxAge=10", "HttpOnly"].forEach(params => {
+    create_test("__Secure-", "Path=/;" + params, false, false, "__Secure: Non-secure origin: 'Path=/;" + params + "'");
+    create_test("__Secure-", "Secure; Path=/;" + params, false, false, "__Secure: Non-secure origin: 'Secure; Path=/;" + params + "'");
+  });
+
+  // `__Host-` Prefix
+  ["", "domain="+document.location.hostname, "MaxAge=10", "HttpOnly"].forEach(params => {
+    create_test("__Secure-", "Path=/;" + params, false, false, "__Host: Non-secure origin: 'Path=/; " + params + "'");
+    create_test("__Secure-", "Secure; Path=/;" + params, false, false, "__Host: Non-secure origin: 'Secure; Path=/; " + params + "'");
+  });
+  create_test("__Secure-", "Path=/cookies/resources/list.py;Secure", false, false, "__Host: Non-secure origin: 'Path=/cookies/resources/list.py;Secure'");
+</script>
diff --git a/cookies/resources/cookie-helper.sub.js b/cookies/resources/cookie-helper.sub.js
new file mode 100644
index 0000000..852fbb6
--- /dev/null
+++ b/cookies/resources/cookie-helper.sub.js
@@ -0,0 +1,206 @@
+// Set up exciting global variables for cookie tests.
+(_ => {
+  var HOST = "{{host}}";
+  var SECURE_PORT = ":{{ports[https][0]}}";
+  var PORT = ":{{ports[http][0]}}";
+  var CROSS_ORIGIN_HOST = "{{hosts[alt][]}}";
+  var SECURE_CROSS_ORIGIN_HOST = "{{hosts[alt][]}}";
+
+  //For secure cookie verification
+  window.SECURE_ORIGIN = "https://" + HOST + SECURE_PORT;
+  window.INSECURE_ORIGIN = "http://" + HOST + PORT;
+
+  //standard references
+  window.ORIGIN = "http://" + HOST + PORT;
+  window.WWW_ORIGIN = "http://{{domains[www]}}" + PORT;
+  window.SUBDOMAIN_ORIGIN = "http://{{domains[www1]}}" + PORT;
+  window.CROSS_SITE_ORIGIN = "http://" + CROSS_ORIGIN_HOST + PORT;
+  window.SECURE_CROSS_SITE_ORIGIN = "https://" + SECURE_CROSS_ORIGIN_HOST + SECURE_PORT;
+  window.CROSS_SITE_HOST = SECURE_CROSS_ORIGIN_HOST;
+
+  // Set the global cookie name.
+  window.HTTP_COOKIE = "cookie_via_http";
+
+  // If we're not on |HOST|, move ourselves there:
+  if (window.location.hostname != HOST)
+    window.location.hostname = HOST;
+})();
+
+// A tiny helper which returns the result of fetching |url| with credentials.
+function credFetch(url) {
+  return fetch(url, {"credentials": "include"});
+}
+
+// Returns a URL on |origin| which redirects to a given absolute URL.
+function redirectTo(origin, url) {
+  return origin + "/cookies/resources/redirectWithCORSHeaders.py?status=307&location=" + encodeURIComponent(url);
+}
+
+// Asserts that `document.cookie` contains or does not contain (according to
+// the value of |present|) a cookie named |name| with a value of |value|.
+function assert_dom_cookie(name, value, present) {
+  var re = new RegExp("(?:^|; )" + name + "=" + value + "(?:$|;)");
+  assert_equals(re.test(document.cookie), present, "`" + name + "=" + value + "` in `document.cookie`");
+}
+
+function assert_cookie(origin, obj, name, value, present) {
+  assert_equals(obj[name], present ? value : undefined, "`" + name + "=" + value + "` in request to `" + origin + "`.");
+}
+
+// Remove the cookie named |name| from |origin|, then set it on |origin| anew.
+// If |origin| matches `document.origin`, also assert (via `document.cookie`) that
+// the cookie was correctly removed and reset.
+function create_cookie(origin, name, value, extras) {
+  alert("Create_cookie: " + origin + "/cookies/resources/drop.py?name=" + name);
+  return credFetch(origin + "/cookies/resources/drop.py?name=" + name)
+    .then(_ => {
+      if (origin == document.origin)
+        assert_dom_cookie(name, value, false);
+    })
+    .then(_ => {
+      return credFetch(origin + "/cookies/resources/set.py?" + name + "=" + value + ";path=/;" + extras)
+        .then(_ => {
+          if (origin == document.origin)
+            assert_dom_cookie(name, value, true);
+        });
+    });
+}
+
+//
+// Prefix-specific test helpers
+//
+function set_prefixed_cookie_via_dom_test(options) {
+  promise_test(t => {
+    var name = options.prefix + "prefixtestcookie";
+    erase_cookie_from_js(name);
+    var value = "" + Math.random();
+    document.cookie = name + "=" + value + ";" + options.params;
+
+    assert_dom_cookie(name, value, options.shouldExistInDOM);
+
+    return credFetch("/cookies/resources/list.py")
+      .then(r => r.json())
+      .then(cookies => assert_equals(cookies[name], options.shouldExistViaHTTP ? value : undefined));
+  }, options.title);
+}
+
+function set_prefixed_cookie_via_http_test(options) {
+  promise_test(t => {
+    var postDelete = _ => {
+      var value = "" + Math.random();
+      return credFetch(options.origin + "/cookies/resources/set.py?" + name + "=" + value + ";" + options.params)
+        .then(_ => credFetch(options.origin + "/cookies/resources/list.py"))
+        .then(r => r.json())
+        .then(cookies => assert_equals(cookies[name], options.shouldExistViaHTTP ? value : undefined));
+    };
+
+    var name = options.prefix + "prefixtestcookie";
+    if (!options.origin) {
+      options.origin = document.origin;
+      erase_cookie_from_js(name);
+      return postDelete;
+    } else {
+      return credFetch(options.origin + "/cookies/resources/drop.py?name=" + name)
+        .then(_ => postDelete());
+    }
+  }, options.title);
+}
+
+//
+// SameSite-specific test helpers:
+//
+
+window.SameSiteStatus = {
+  CROSS_SITE: "cross-site",
+  LAX: "lax",
+  STRICT: "strict"
+};
+
+// Reset SameSite test cookies on |origin|. If |origin| matches `document.origin`, assert
+// (via `document.cookie`) that they were properly removed and reset.
+function resetSameSiteCookies(origin, value) {
+  return credFetch(origin + "/cookies/resources/dropSameSite.py")
+    .then(_ => {
+      if (origin == document.origin) {
+        assert_dom_cookie("samesite_strict", value, false);
+        assert_dom_cookie("samesite_lax", value, false);
+        assert_dom_cookie("samesite_none", value, false);
+      }
+    })
+    .then(_ => {
+      return credFetch(origin + "/cookies/resources/setSameSite.py?" + value)
+        .then(_ => {
+          if (origin == document.origin) {
+            assert_dom_cookie("samesite_strict", value, true);
+            assert_dom_cookie("samesite_lax", value, true);
+            assert_dom_cookie("samesite_none", value, true);
+          }
+        })
+    })
+}
+
+// Given an |expectedStatus| and |expectedValue|, assert the |cookies| contains the
+// proper set of cookie names and values.
+function verifySameSiteCookieState(expectedStatus, expectedValue, cookies) {
+    assert_equals(cookies["samesite_none"], expectedValue, "Non-SameSite cookies are always sent.");
+    if (expectedStatus == SameSiteStatus.CROSS_SITE) {
+      assert_not_equals(cookies["samesite_strict"], expectedValue, "SameSite=Strict cookies are not sent with cross-site requests.");
+      assert_not_equals(cookies["samesite_lax"], expectedValue, "SameSite=Lax cookies are not sent with cross-site requests.");
+    } else if (expectedStatus == SameSiteStatus.LAX) {
+      assert_not_equals(cookies["samesite_strict"], expectedValue, "SameSite=Strict cookies are not sent with lax requests.");
+      assert_equals(cookies["samesite_lax"], expectedValue, "SameSite=Lax cookies are sent with lax requests.");
+    } else if (expectedStatus == SameSiteStatus.STRICT) {
+      assert_equals(cookies["samesite_strict"], expectedValue, "SameSite=Strict cookies are sent with strict requests.");
+      assert_equals(cookies["samesite_lax"], expectedValue, "SameSite=Lax cookies are sent with strict requests.");
+    }
+}
+
+//
+// LeaveSecureCookiesAlone-specific test helpers:
+//
+
+window.SecureStatus = {
+  INSECURE_COOKIE_ONLY: "1",
+  BOTH_COOKIES: "2",
+};
+
+//Reset SameSite test cookies on |origin|. If |origin| matches `document.origin`, assert
+//(via `document.cookie`) that they were properly removed and reset.
+function resetSecureCookies(origin, value) {
+return credFetch(origin + "/cookies/resources/dropSecure.py")
+ .then(_ => {
+   if (origin == document.origin) {
+     assert_dom_cookie("alone_secure", value, false);
+     assert_dom_cookie("alone_insecure", value, false);
+   }
+ })
+ .then(_ => {
+     return credFetch(origin + "/cookie/resources/setSecure.py?" + value)
+ })
+}
+
+//
+// DOM based cookie manipulation API's
+//
+
+// borrowed from http://www.quirksmode.org/js/cookies.html
+function create_cookie_from_js(name, value, days, secure_flag) {
+  if (days) {
+    var date = new Date();
+    date.setTime(date.getTime()+(days*24*60*60*1000));
+    var expires = "; expires="+date.toGMTString();
+  }
+  else var expires = "";
+
+  var secure = "";
+  if (secure_flag == true) {
+    secure = "secure; ";
+  }
+  document.cookie = name+"="+value+expires+"; "+secure+"path=/";
+}
+
+// erase cookie value and set for expiration
+function erase_cookie_from_js(name) {
+  create_cookie_from_js(name,"",-1);
+  assert_dom_cookie(name, "", false);
+}
diff --git a/cookies/resources/drop.py b/cookies/resources/drop.py
new file mode 100644
index 0000000..7491dad
--- /dev/null
+++ b/cookies/resources/drop.py
@@ -0,0 +1,15 @@
+from helpers import makeDropCookie, readParameter, setNoCacheAndCORSHeaders
+
+def main(request, response):
+    """Respond to `/cookie/drop?name={name}` by expiring the cookie named `{name}`."""
+    headers = setNoCacheAndCORSHeaders(request, response)
+    try:
+        # Expire the named cookie, and return a JSON-encoded success code.
+        name = readParameter(request, paramName="name", requireValue=True)
+        scheme = request.url_parts.scheme
+        headers.append(makeDropCookie(name,  "https" == scheme))
+        return headers, '{"success": true}'
+    except:
+        return 500, headers, '{"error" : "Empty or missing name parameter."}'
+
+
diff --git a/cookies/resources/dropSameSite.py b/cookies/resources/dropSameSite.py
new file mode 100644
index 0000000..803dbeb
--- /dev/null
+++ b/cookies/resources/dropSameSite.py
@@ -0,0 +1,12 @@
+from helpers import makeDropCookie, readParameter, setNoCacheAndCORSHeaders
+
+def main(request, response):
+    """Respond to `/cookie/same-site/resources/dropSameSite.py by dropping the
+    three cookies set by setSameSiteCookies.py"""
+    headers = setNoCacheAndCORSHeaders(request, response)
+
+    # Expire the cookies, and return a JSON-encoded success code.
+    headers.append(makeDropCookie("samesite_strict", False))
+    headers.append(makeDropCookie("samesite_lax", False))
+    headers.append(makeDropCookie("samesite_none", False))
+    return headers, '{"success": true}'
diff --git a/cookies/resources/dropSecure.py b/cookies/resources/dropSecure.py
new file mode 100644
index 0000000..f95e9a9
--- /dev/null
+++ b/cookies/resources/dropSecure.py
@@ -0,0 +1,11 @@
+from helpers import makeDropCookie, readParameter, setNoCacheAndCORSHeaders
+
+def main(request, response):
+    """Respond to `/cookie/drop/secure` by dropping the two cookie set by
+    `setSecureTestCookies()`"""
+    headers = setNoCacheAndCORSHeaders(request, response)
+
+    # Expire the cookies, and return a JSON-encoded success code.
+    headers.append(makeDropCookie("alone_secure", False))
+    headers.append(makeDropCookie("alone_insecure", False))
+    return headers, '{"success": true}'
diff --git a/cookies/resources/helpers.py b/cookies/resources/helpers.py
new file mode 100644
index 0000000..145f2fe
--- /dev/null
+++ b/cookies/resources/helpers.py
@@ -0,0 +1,55 @@
+import urlparse
+
+def setNoCacheAndCORSHeaders(request, response):
+    """Set Cache-Control, CORS and Content-Type headers appropriate for the cookie tests."""
+    headers = [("Content-Type", "application/json"),
+               ("Access-Control-Allow-Credentials", "true")]
+
+    origin = "*"
+    if "origin" in request.headers:
+        origin = request.headers["origin"]
+
+    headers.append(("Access-Control-Allow-Origin", origin))
+    #headers.append(("Access-Control-Allow-Credentials", "true"))
+    headers.append(("Cache-Control", "no-cache"))
+    headers.append(("Expires", "Fri, 01 Jan 1990 00:00:00 GMT"))
+
+    return headers
+
+def makeCookieHeader(name, value, otherAttrs):
+    """Make a Set-Cookie header for a cookie with the name, value and attributes provided."""
+    def makeAV(a, v):
+        if None == v or "" == v:
+            return a
+        return "%s=%s" % (a, v)
+
+    # ensure cookie name is always first
+    attrs = ["%s=%s" % (name, value)]
+    attrs.extend(makeAV(a, v) for (a,v) in otherAttrs.iteritems())
+    return ("Set-Cookie", "; ".join(attrs))
+
+def makeDropCookie(name, secure):
+    attrs = {"MaxAge": 0, "path": "/"}
+    if secure:
+        attrs["secure"] = ""
+    return makeCookieHeader(name, "", attrs)
+
+def readParameter(request, paramName, requireValue):
+    """Read a parameter from the request. Raise if requireValue is set and the
+    parameter has an empty value or is not present."""
+    params = urlparse.parse_qs(request.url_parts.query)
+    param = params[paramName][0].strip()
+    if len(param) == 0:
+        raise Exception("Empty or missing name parameter.")
+    return param
+
+def readCookies(request):
+    """Read the cookies from the client present in the request."""
+    cookies = {}
+    for key in request.cookies:
+        for cookie in request.cookies.get_list(key):
+            # do we care we'll clobber cookies here? If so, do we
+            # need to modify the test to take cookie names and value lists?
+            cookies[key] = cookie.value
+    return cookies
+
diff --git a/cookies/resources/imgIfMatch.py b/cookies/resources/imgIfMatch.py
new file mode 100644
index 0000000..0866441
--- /dev/null
+++ b/cookies/resources/imgIfMatch.py
@@ -0,0 +1,16 @@
+import helpers
+
+def main(request, response):
+    """Respond to `/cookie/imgIfMatch?name={name}&value={value}` with a 404 if
+       the cookie isn't present, and a transparent GIF otherwise."""
+    headers = helpers.setNoCacheAndCORSHeaders(request, response)
+    name = helpers.readParameter(request, paramName="name", requireValue=True)
+    value = helpers.readParameter(request, paramName="value", requireValue=True)
+    cookiesWithMatchingNames = request.cookies.get_list(name)
+    for cookie in cookiesWithMatchingNames:
+        if cookie.value == value:
+            # From https://github.com/mathiasbynens/small/blob/master/gif-transparent.gif
+            headers.append(("Content-Type","image/gif"))
+            gif = "\x47\x49\x46\x38\x39\x61\x01\x00\x01\x00\x80\x00\x00\xFF\xFF\xFF\x00\x00\x00\x21\xF9\x04\x01\x00\x00\x00\x00\x2C\x00\x00\x00\x00\x01\x00\x01\x00\x00\x02\x02\x44\x01\x00\x3B"
+            return headers, gif
+    return 500, headers, '{"error": {"message": "The cookie\'s value did not match the given value."}}'
diff --git a/cookies/resources/list.py b/cookies/resources/list.py
new file mode 100644
index 0000000..3fe7dd6
--- /dev/null
+++ b/cookies/resources/list.py
@@ -0,0 +1,7 @@
+import json
+import helpers
+
+def main(request, response):
+    headers = helpers.setNoCacheAndCORSHeaders(request, response)
+    cookies = helpers.readCookies(request)
+    return headers, json.dumps(cookies)
diff --git a/cookies/resources/postToParent.py b/cookies/resources/postToParent.py
new file mode 100644
index 0000000..68e85d3
--- /dev/null
+++ b/cookies/resources/postToParent.py
@@ -0,0 +1,27 @@
+import json
+import helpers
+
+def main(request, response):
+    headers = helpers.setNoCacheAndCORSHeaders(request, response)
+    cookies = helpers.readCookies(request)
+    headers.append(("Content-Type", "text/html; charset=utf-8"))
+
+    tmpl = """
+<!DOCTYPE html>
+<script>
+  var data = %s;
+
+  if (window.parent != window)
+    window.parent.postMessage(data, "*");
+
+  if (window.opener)
+    window.opener.postMessage(data, "*");
+
+  window.addEventListener("message", e => {
+    console.log(e);
+    if (e.data == "reload")
+      window.location.reload();
+  });
+</script>
+"""
+    return headers, tmpl % json.dumps(cookies)
diff --git a/cookies/resources/redirectWithCORSHeaders.py b/cookies/resources/redirectWithCORSHeaders.py
new file mode 100644
index 0000000..89ca1af
--- /dev/null
+++ b/cookies/resources/redirectWithCORSHeaders.py
@@ -0,0 +1,22 @@
+from helpers import setNoCacheAndCORSHeaders
+
+def main(request, response):
+    """Simple handler that causes redirection.
+
+    The request should typically have two query parameters:
+    status - The status to use for the redirection. Defaults to 302.
+    location - The resource to redirect to.
+    """
+    status = 302
+    if "status" in request.GET:
+        try:
+            status = int(request.GET.first("status"))
+        except ValueError:
+            pass
+    headers = setNoCacheAndCORSHeaders(request, response)
+
+    location = request.GET.first("location")
+
+    headers.append(("Location", location))
+
+    return status, headers, ""
diff --git a/cookies/resources/set.py b/cookies/resources/set.py
new file mode 100644
index 0000000..abfb8c8
--- /dev/null
+++ b/cookies/resources/set.py
@@ -0,0 +1,7 @@
+import helpers
+
+def main(request, response):
+    """Respond to `/cookie/set?{cookie}` by echoing `{cookie}` as a `Set-Cookie` header."""
+    headers = helpers.setNoCacheAndCORSHeaders(request, response)
+    headers.append(("Set-Cookie", request.url_parts.query))
+    return headers, '{"success": true}'
diff --git a/cookies/resources/setSameSite.py b/cookies/resources/setSameSite.py
new file mode 100644
index 0000000..8ae1776
--- /dev/null
+++ b/cookies/resources/setSameSite.py
@@ -0,0 +1,14 @@
+from helpers import makeCookieHeader, readParameter, setNoCacheAndCORSHeaders
+
+def main(request, response):
+    """Respond to `/cookie/set/samesite?{value}` by setting three cookies:
+    1. `samesite_strict={value};SameSite=Strict;path=/`
+    2. `samesite_lax={value};SameSite=Lax;path=/`
+    3. `samesite_none={value};path=/`"""
+    headers = setNoCacheAndCORSHeaders(request, response)
+    value = request.url_parts.query
+
+    headers.append(makeCookieHeader("samesite_strict", value, {"SameSite":"Strict","path":"/"}))
+    headers.append(makeCookieHeader("samesite_lax", value, {"SameSite":"Lax","path":"/"}))
+    headers.append(makeCookieHeader("samesite_none", value, {"path":"/"}))
+    return headers, '{"success": true}'
diff --git a/cookies/resources/setSecure.py b/cookies/resources/setSecure.py
new file mode 100644
index 0000000..c8ec017
--- /dev/null
+++ b/cookies/resources/setSecure.py
@@ -0,0 +1,12 @@
+from helpers import makeCookieHeader, readParameter, setNoCacheAndCORSHeaders
+
+def main(request, response):
+    """Respond to `/cookie/set/secure?{value}` by setting two cookies:
+    alone_secure={value};secure;path=/`
+    alone_insecure={value};path=/"""
+    headers = setNoCacheAndCORSHeaders(request, response)
+    value = request.url_parts.query
+
+    headers.append(makeCookieHeader("alone_secure", value, {"secure": "","path": "/"}))
+    headers.append(makeCookieHeader("alone_insecure", value, {"path": "/"}))
+    return headers, '{"success": true}'
diff --git a/cookies/samesite/fetch.html b/cookies/samesite/fetch.html
new file mode 100644
index 0000000..734462a
--- /dev/null
+++ b/cookies/samesite/fetch.html
@@ -0,0 +1,39 @@
+<!DOCTYPE html>
+<meta charset="utf-8"/>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/cookies/resources/cookie-helper.sub.js"></script>
+<script>
+  function create_test(origin, target, expectedStatus, title) {
+    promise_test(t => {
+      var value = "" + Math.random();
+      return resetSameSiteCookies(origin, value)
+        .then(_ => {
+          return credFetch(target + "/cookies/resources/list.py")
+
+            .then(r => r.json())
+            .then(cookies => verifySameSiteCookieState(expectedStatus, value, cookies));
+        });
+    }, title);
+  }
+
+  // No redirect:
+  create_test(ORIGIN, ORIGIN, SameSiteStatus.STRICT, "Same-host fetches are strictly same-site");
+  create_test(SUBDOMAIN_ORIGIN, SUBDOMAIN_ORIGIN, SameSiteStatus.STRICT, "Subdomain fetches are strictly same-site");
+  create_test(CROSS_SITE_ORIGIN, CROSS_SITE_ORIGIN, SameSiteStatus.CROSS_SITE, "Cross-site fetches are cross-site");
+
+  // Redirect from {same-host,subdomain,cross-site} to same-host:
+  create_test(ORIGIN, redirectTo(ORIGIN, ORIGIN), SameSiteStatus.STRICT, "Same-host redirecting to same-host fetches are strictly same-site");
+  create_test(ORIGIN, redirectTo(SUBDOMAIN_ORIGIN, ORIGIN), SameSiteStatus.STRICT, "Subdomain redirecting to same-host fetches are strictly same-site");
+  create_test(ORIGIN, redirectTo(CROSS_SITE_ORIGIN, ORIGIN), SameSiteStatus.STRICT, "Cross-site redirecting to same-host fetches are strictly same-site");
+
+  // Redirect from {same-host,subdomain,cross-site} to same-host:
+  create_test(SUBDOMAIN_ORIGIN, redirectTo(ORIGIN, SUBDOMAIN_ORIGIN), SameSiteStatus.STRICT, "Same-host redirecting to subdomain fetches are strictly same-site");
+  create_test(SUBDOMAIN_ORIGIN, redirectTo(SUBDOMAIN_ORIGIN, SUBDOMAIN_ORIGIN), SameSiteStatus.STRICT, "Subdomain redirecting to subdomain fetches are strictly same-site");
+  create_test(SUBDOMAIN_ORIGIN, redirectTo(CROSS_SITE_ORIGIN, SUBDOMAIN_ORIGIN), SameSiteStatus.STRICT, "Cross-site redirecting to subdomain fetches are strictly same-site");
+
+  // Redirect from {same-host,subdomain,cross-site} to cross-site:
+  create_test(CROSS_SITE_ORIGIN, redirectTo(ORIGIN, CROSS_SITE_ORIGIN), SameSiteStatus.CROSS_SITE, "Same-host redirecting to cross-site fetches are cross-site");
+  create_test(CROSS_SITE_ORIGIN, redirectTo(SUBDOMAIN_ORIGIN, CROSS_SITE_ORIGIN), SameSiteStatus.CROSS_SITE, "Subdomain redirecting to cross-site fetches are cross-site");
+  create_test(CROSS_SITE_ORIGIN, redirectTo(CROSS_SITE_ORIGIN, CROSS_SITE_ORIGIN), SameSiteStatus.CROSS_SITE, "Cross-site redirecting to cross-site fetches are cross-site");
+</script>
diff --git a/cookies/samesite/form-get-blank-reload.html b/cookies/samesite/form-get-blank-reload.html
new file mode 100644
index 0000000..09f3ee9
--- /dev/null
+++ b/cookies/samesite/form-get-blank-reload.html
@@ -0,0 +1,57 @@
+<!DOCTYPE html>
+<meta charset="utf-8"/>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/cookies/resources/cookie-helper.sub.js"></script>
+<script>
+  function create_test(origin, target, expectedStatus, title) {
+    promise_test(t => {
+      var value = "" + Math.random();
+      return resetSameSiteCookies(origin, value)
+        .then(_ => {
+          return new Promise((resolve, reject) => {
+            var f = document.createElement('form');
+            f.action = target + "/cookies/resources/postToParent.py";
+            f.target = "_blank";
+            f.method = "GET";
+
+            // If |target| contains a `redir` parameter, extract it, and add it
+            // to the form so it doesn't get dropped in the submission.
+            var url = new URL(f.action);
+            if (url.pathname = "/cookies/rfc6265/resources/redirectWithCORSHeaders.py") {
+              var i = document.createElement("input");
+              i.name = "location";
+              i.value = url.searchParams.get("location");
+              i.type = "hidden";
+              f.appendChild(i);
+            }
+            var reloaded = false;
+            var msgHandler = e => {
+              try {
+                verifySameSiteCookieState(expectedStatus, value, e.data);
+              } catch (e) {
+                reject(e);
+              }
+
+              if (reloaded) {
+                window.removeEventListener("message", msgHandler);
+                e.source.close();
+                resolve("Popup received the cookie.");
+              } else {
+                reloaded = true;
+                e.source.postMessage("reload", "*");
+              }
+            };
+            window.addEventListener("message", msgHandler);
+            document.body.appendChild(f);
+
+            f.submit();
+          });
+        });
+    }, title);
+  }
+
+  create_test(ORIGIN, ORIGIN, SameSiteStatus.STRICT, "Reloaded same-host top-level form GETs are strictly same-site");
+  create_test(SUBDOMAIN_ORIGIN, SUBDOMAIN_ORIGIN, SameSiteStatus.STRICT, "Reloaded subdomain top-level form GETs are strictly same-site");
+  create_test(CROSS_SITE_ORIGIN, CROSS_SITE_ORIGIN, SameSiteStatus.LAX, "Reloaded cross-site top-level form GETs are laxly same-site");
+</script>
diff --git a/cookies/samesite/form-get-blank.html b/cookies/samesite/form-get-blank.html
new file mode 100644
index 0000000..a86f34b
--- /dev/null
+++ b/cookies/samesite/form-get-blank.html
@@ -0,0 +1,66 @@
+<!DOCTYPE html>
+<meta charset="utf-8"/>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/cookies/resources/cookie-helper.sub.js"></script>
+<script>
+  function create_test(origin, target, expectedStatus, title) {
+    promise_test(t => {
+      var value = "" + Math.random();
+      return resetSameSiteCookies(origin, value)
+        .then(_ => {
+          return new Promise((resolve, reject) => {
+            var f = document.createElement('form');
+            f.action = target + "/cookies/resources/postToParent.py";
+            f.target = "_blank";
+            f.method = "GET";
+
+            // If |target| contains a `redir` parameter, extract it, and add it
+            // to the form so it doesn't get dropped in the submission.
+            var url = new URL(f.action);
+            if (url.pathname == "/cookies/resources/redirectWithCORSHeaders.py") {
+              var i = document.createElement("input");
+              i.name = "location";
+              i.type="hidden";
+              i.value = url.searchParams.get("location");
+              f.appendChild(i);
+            }
+
+            var msgHandler = e => {
+              window.removeEventListener("message", msgHandler);
+              e.source.close();
+              try {
+                verifySameSiteCookieState(expectedStatus, value, e.data);
+                resolve("Popup received the cookie.");
+              } catch (e) {
+                reject(e);
+              }
+            };
+            window.addEventListener("message", msgHandler);
+            document.body.appendChild(f);
+            f.submit();
+          });
+        });
+    }, title);
+  }
+
+  // No redirect:
+  create_test(ORIGIN, ORIGIN, SameSiteStatus.STRICT, "Same-host top-level form GETs are strictly same-site");
+  create_test(SUBDOMAIN_ORIGIN, SUBDOMAIN_ORIGIN, SameSiteStatus.STRICT, "Subdomain top-level form GETs are strictly same-site");
+  create_test(CROSS_SITE_ORIGIN, CROSS_SITE_ORIGIN, SameSiteStatus.LAX, "Cross-site top-level form GETs are laxly same-site");
+
+  // Redirect from {same-host,subdomain,cross-site} to same-host:
+  create_test(ORIGIN, redirectTo(ORIGIN, ORIGIN), SameSiteStatus.STRICT, "Same-host redirecting to same-host top-level form GETs are strictly same-site");
+  create_test(ORIGIN, redirectTo(SUBDOMAIN_ORIGIN, ORIGIN), SameSiteStatus.STRICT, "Subdomain redirecting to same-host top-level form GETs are strictly same-site");
+  create_test(ORIGIN, redirectTo(CROSS_SITE_ORIGIN, ORIGIN), SameSiteStatus.STRICT, "Cross-site redirecting to same-host top-level form GETs are strictly same-site");
+
+  // Redirect from {same-host,subdomain,cross-site} to same-host:
+  create_test(SUBDOMAIN_ORIGIN, redirectTo(ORIGIN, SUBDOMAIN_ORIGIN), SameSiteStatus.STRICT, "Same-host redirecting to subdomain top-level form GETs are strictly same-site");
+  create_test(SUBDOMAIN_ORIGIN, redirectTo(SUBDOMAIN_ORIGIN, SUBDOMAIN_ORIGIN), SameSiteStatus.STRICT, "Subdomain redirecting to subdomain top-level form GETs are strictly same-site");
+  create_test(SUBDOMAIN_ORIGIN, redirectTo(CROSS_SITE_ORIGIN, SUBDOMAIN_ORIGIN), SameSiteStatus.STRICT, "Cross-site redirecting to subdomain top-level form GETs are strictly same-site");
+
+  // Redirect from {same-host,subdomain,cross-site} to cross-site:
+  create_test(CROSS_SITE_ORIGIN, redirectTo(ORIGIN, CROSS_SITE_ORIGIN), SameSiteStatus.LAX, "Same-host redirecting to cross-site top-level form GETs are laxly same-site");
+  create_test(CROSS_SITE_ORIGIN, redirectTo(SUBDOMAIN_ORIGIN, CROSS_SITE_ORIGIN), SameSiteStatus.LAX, "Subdomain redirecting to cross-site top-level form GETs are laxly same-site");
+  create_test(CROSS_SITE_ORIGIN, redirectTo(CROSS_SITE_ORIGIN, CROSS_SITE_ORIGIN), SameSiteStatus.LAX, "Cross-site redirecting to cross-site top-level form GETs are laxly same-site");
+</script>
diff --git a/cookies/samesite/form-post-blank-reload.html b/cookies/samesite/form-post-blank-reload.html
new file mode 100644
index 0000000..f9449bf
--- /dev/null
+++ b/cookies/samesite/form-post-blank-reload.html
@@ -0,0 +1,47 @@
+<!DOCTYPE html>
+<meta charset="utf-8"/>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/cookies/resources/cookie-helper.sub.js"></script>
+<script>
+  function create_test(origin, target, expectedStatus, title) {
+    promise_test(t => {
+      var value = "" + Math.random();
+      return resetSameSiteCookies(origin, value)
+        .then(_ => {
+          return new Promise((resolve, reject) => {
+            var f = document.createElement('form');
+            f.action = target + "/cookies/resources/postToParent.py";
+            f.target = "_blank";
+            f.method = "POST";
+
+            var reloaded = false;
+            var msgHandler = e => {
+              try {
+                verifySameSiteCookieState(expectedStatus, value, e.data);
+              } catch (e) {
+                reject(e);
+              }
+
+              if (reloaded) {
+                window.removeEventListener("message", msgHandler);
+                e.source.close();
+                resolve("Popup received the cookie.");
+              } else {
+                reloaded = true;
+                e.source.postMessage("reload", "*");
+              }
+            };
+            window.addEventListener("message", msgHandler);
+
+            document.body.appendChild(f);
+            f.submit();
+          });
+        });
+    }, title);
+  }
+
+  create_test(ORIGIN, ORIGIN, SameSiteStatus.STRICT, "Reloaded same-host top-level form POSTs are strictly same-site");
+  create_test(SUBDOMAIN_ORIGIN, SUBDOMAIN_ORIGIN, SameSiteStatus.STRICT, "Reloaded subdomain top-level form POSTs are strictly same-site");
+  create_test(CROSS_SITE_ORIGIN, CROSS_SITE_ORIGIN, SameSiteStatus.CROSS_SITE, "Reloaded cross-site top-level form POSTs are not same-site");
+</script>
diff --git a/cookies/samesite/form-post-blank.html b/cookies/samesite/form-post-blank.html
new file mode 100644
index 0000000..115c6a1
--- /dev/null
+++ b/cookies/samesite/form-post-blank.html
@@ -0,0 +1,55 @@
+<!DOCTYPE html>
+<meta charset="utf-8"/>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/cookies/resources/cookie-helper.sub.js"></script>
+<script>
+  function create_test(origin, target, expectedStatus, title) {
+    promise_test(t => {
+      var value = "" + Math.random();
+      return resetSameSiteCookies(origin, value)
+        .then(_ => {
+          return new Promise((resolve, reject) => {
+            var f = document.createElement('form');
+            f.action = target + "/cookies/resources/postToParent.py";
+            f.target = "_blank";
+            f.method = "POST";
+
+            var msgHandler = e => {
+              window.removeEventListener("message", msgHandler);
+              e.source.close();
+              try {
+                verifySameSiteCookieState(expectedStatus, value, e.data);
+                resolve("Popup received the cookie.");
+              } catch (e) {
+                reject(e);
+              }
+            };
+            window.addEventListener("message", msgHandler);
+            document.body.appendChild(f);
+            f.submit();
+          });
+        });
+    }, title);
+  }
+
+  // No redirect:
+  create_test(ORIGIN, ORIGIN, SameSiteStatus.STRICT, "Same-host top-level form POSTs are strictly same-site");
+  create_test(SUBDOMAIN_ORIGIN, SUBDOMAIN_ORIGIN, SameSiteStatus.STRICT, "Subdomain top-level form POSTs are strictly same-site");
+  create_test(CROSS_SITE_ORIGIN, CROSS_SITE_ORIGIN, SameSiteStatus.CROSS_SITE, "Cross-site top-level form POSTs are cross-site");
+
+  // Redirect from {same-host,subdomain,cross-site} to same-host:
+  create_test(ORIGIN, redirectTo(ORIGIN, ORIGIN), SameSiteStatus.STRICT, "Same-host redirecting to same-host top-level form POSTs are strictly same-site");
+  create_test(ORIGIN, redirectTo(SUBDOMAIN_ORIGIN, ORIGIN), SameSiteStatus.STRICT, "Subdomain redirecting to same-host top-level form POSTs are strictly same-site");
+  create_test(ORIGIN, redirectTo(CROSS_SITE_ORIGIN, ORIGIN), SameSiteStatus.STRICT, "Cross-site redirecting to same-host top-level form POSTs are strictly same-site");
+
+  // Redirect from {same-host,subdomain,cross-site} to same-host:
+  create_test(SUBDOMAIN_ORIGIN, redirectTo(ORIGIN, SUBDOMAIN_ORIGIN), SameSiteStatus.STRICT, "Same-host redirecting to subdomain top-level form POSTs are strictly same-site");
+  create_test(SUBDOMAIN_ORIGIN, redirectTo(SUBDOMAIN_ORIGIN, SUBDOMAIN_ORIGIN), SameSiteStatus.STRICT, "Subdomain redirecting to subdomain top-level form POSTs are strictly same-site");
+  create_test(SUBDOMAIN_ORIGIN, redirectTo(CROSS_SITE_ORIGIN, SUBDOMAIN_ORIGIN), SameSiteStatus.STRICT, "Cross-site redirecting to subdomain top-level form POSTs are strictly same-site");
+
+  // Redirect from {same-host,subdomain,cross-site} to cross-site:
+  create_test(CROSS_SITE_ORIGIN, redirectTo(ORIGIN, CROSS_SITE_ORIGIN), SameSiteStatus.CROSS_SITE, "Same-host redirecting to cross-site top-level form POSTs are cross-site");
+  create_test(CROSS_SITE_ORIGIN, redirectTo(SUBDOMAIN_ORIGIN, CROSS_SITE_ORIGIN), SameSiteStatus.CROSS_SITE, "Subdomain redirecting to cross-site top-level form POSTs are cross-site");
+  create_test(CROSS_SITE_ORIGIN, redirectTo(CROSS_SITE_ORIGIN, CROSS_SITE_ORIGIN), SameSiteStatus.CROSS_SITE, "Cross-site redirecting to cross-site top-level form POSTs are cross-site");
+</script>
diff --git a/cookies/samesite/iframe-reload.html b/cookies/samesite/iframe-reload.html
new file mode 100644
index 0000000..759fc7b
--- /dev/null
+++ b/cookies/samesite/iframe-reload.html
@@ -0,0 +1,47 @@
+<!DOCTYPE html>
+<meta charset="utf-8"/>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/cookies/resources/cookie-helper.sub.js"></script>
+<!-- We're appending an <iframe> to the document's body, so execute tests after we have a body -->
+<body>
+<script>
+  function create_test(origin, target, expectedStatus, title) {
+    promise_test(t => {
+      var value = "" + Math.random();
+      return resetSameSiteCookies(origin, value)
+        .then(_ => {
+          return new Promise((resolve, reject) => {
+            var iframe = document.createElement("iframe");
+            iframe.onerror = _ => reject("IFrame could not be loaded.");
+
+            var reloaded = false;
+            var msgHandler = e => {
+              try {
+                verifySameSiteCookieState(expectedStatus, value, e.data);
+              } catch (e) {
+                reject(e);
+              }
+
+              if (reloaded) {
+                window.removeEventListener("message", msgHandler);
+                document.body.removeChild(iframe);
+                resolve("IFrame received the cookie.");
+              } else {
+                reloaded = true;
+                e.source.postMessage("reload", "*");
+              }
+            };
+            window.addEventListener("message", msgHandler);
+
+            iframe.src = target + "/cookies/resources/postToParent.py";
+            document.body.appendChild(iframe);
+          });
+        });
+    }, title);
+  }
+
+  create_test(ORIGIN, ORIGIN, SameSiteStatus.STRICT, "Reloaded same-host fetches are strictly same-site");
+  create_test(SUBDOMAIN_ORIGIN, SUBDOMAIN_ORIGIN, SameSiteStatus.STRICT, "Reloaded subdomain fetches are strictly same-site");
+  create_test(CROSS_SITE_ORIGIN, CROSS_SITE_ORIGIN, SameSiteStatus.CROSS_SITE, "Reloaded cross-site fetches are cross-site");
+</script>
diff --git a/cookies/samesite/iframe.html b/cookies/samesite/iframe.html
new file mode 100644
index 0000000..38a7701
--- /dev/null
+++ b/cookies/samesite/iframe.html
@@ -0,0 +1,59 @@
+<!DOCTYPE html>
+<meta charset="utf-8"/>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/cookies/resources/cookie-helper.sub.js"></script>
+<!-- We're appending an <iframe> to the document's body, so execute tests after we have a body -->
+<body>
+<script>
+  function create_test(origin, target, expectedStatus, title) {
+    promise_test(t => {
+      var value = "" + Math.random();
+      return resetSameSiteCookies(origin, value)
+        .then(_ => {
+          return new Promise((resolve, reject) => {
+            var iframe = document.createElement("iframe");
+            iframe.onerror = _ => reject("IFrame could not be loaded.");
+
+            var msgHandler = e => {
+              if (e.source == iframe.contentWindow) {
+                // Cleanup, then verify cookie state:
+                document.body.removeChild(iframe);
+                window.removeEventListener("message", msgHandler);
+                try {
+                  verifySameSiteCookieState(expectedStatus, value, e.data);
+                  resolve();
+                } catch(e) {
+                  reject(e);
+                }
+              }
+            };
+            window.addEventListener("message", msgHandler);
+
+            iframe.src = target + "/cookies/resources/postToParent.py";
+            document.body.appendChild(iframe);
+          });
+        });
+    }, title);
+  }
+
+  // No redirect:
+  create_test(ORIGIN, ORIGIN, SameSiteStatus.STRICT, "Same-host fetches are strictly same-site");
+  create_test(SUBDOMAIN_ORIGIN, SUBDOMAIN_ORIGIN, SameSiteStatus.STRICT, "Subdomain fetches are strictly same-site");
+  create_test(CROSS_SITE_ORIGIN, CROSS_SITE_ORIGIN, SameSiteStatus.CROSS_SITE, "Cross-site fetches are cross-site");
+
+  // Redirect from {same-host,subdomain,cross-site} to same-host:
+  create_test(ORIGIN, redirectTo(ORIGIN, ORIGIN), SameSiteStatus.STRICT, "Same-host redirecting to same-host fetches are strictly same-site");
+  create_test(ORIGIN, redirectTo(SUBDOMAIN_ORIGIN, ORIGIN), SameSiteStatus.STRICT, "Subdomain redirecting to same-host fetches are strictly same-site");
+  create_test(ORIGIN, redirectTo(CROSS_SITE_ORIGIN, ORIGIN), SameSiteStatus.STRICT, "Cross-site redirecting to same-host fetches are strictly same-site");
+
+  // Redirect from {same-host,subdomain,cross-site} to same-host:
+  create_test(SUBDOMAIN_ORIGIN, redirectTo(ORIGIN, SUBDOMAIN_ORIGIN), SameSiteStatus.STRICT, "Same-host redirecting to subdomain fetches are strictly same-site");
+  create_test(SUBDOMAIN_ORIGIN, redirectTo(SUBDOMAIN_ORIGIN, SUBDOMAIN_ORIGIN), SameSiteStatus.STRICT, "Subdomain redirecting to subdomain fetches are strictly same-site");
+  create_test(SUBDOMAIN_ORIGIN, redirectTo(CROSS_SITE_ORIGIN, SUBDOMAIN_ORIGIN), SameSiteStatus.STRICT, "Cross-site redirecting to subdomain fetches are strictly same-site");
+
+  // Redirect from {same-host,subdomain,cross-site} to cross-site:
+  create_test(CROSS_SITE_ORIGIN, redirectTo(ORIGIN, CROSS_SITE_ORIGIN), SameSiteStatus.CROSS_SITE, "Same-host redirecting to cross-site fetches are cross-site");
+  create_test(CROSS_SITE_ORIGIN, redirectTo(SUBDOMAIN_ORIGIN, CROSS_SITE_ORIGIN), SameSiteStatus.CROSS_SITE, "Subdomain redirecting to cross-site fetches are cross-site");
+  create_test(CROSS_SITE_ORIGIN, redirectTo(CROSS_SITE_ORIGIN, CROSS_SITE_ORIGIN), SameSiteStatus.CROSS_SITE, "Cross-site redirecting to cross-site fetches are cross-site");
+</script>
diff --git a/cookies/samesite/img.html b/cookies/samesite/img.html
new file mode 100644
index 0000000..b1b0434
--- /dev/null
+++ b/cookies/samesite/img.html
@@ -0,0 +1,72 @@
+<!DOCTYPE html>
+<meta charset="utf-8"/>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/cookies/resources/cookie-helper.sub.js"></script>
+<script>
+  function assert_cookie_present(origin, name, value) {
+    return new Promise((resolve, reject) => {
+      var img = document.createElement("img");
+      img.onload = _ => resolve("'" + name + "=" + value + "' present on " + origin);
+      img.onerror = _ => reject("'" + name + "=" + value + "' not present on " + origin);
+
+      // We need to URL encode the destination path/query if we're redirecting:
+      if (origin.match(/\/redir/))
+        img.src = origin + encodeURIComponent("/cookies/resources/imgIfMatch.py?name=" + name + "&value=" + value);
+      else
+        img.src = origin + "/cookies/resources/imgIfMatch.py?name=" + name + "&value=" + value;
+    });
+  }
+
+  function assert_cookie_absent(origin, name, value) {
+    return new Promise((resolve, reject) => {
+      var img = document.createElement("img");
+      img.onload = _ => reject("'" + name + "=" + value + "' present on " + origin);
+      img.onerror = _ => resolve("'" + name + "=" + value + "' not present on " + origin);
+
+      // We need to URL encode the destination path/query if we're redirecting:
+      if (origin.match(/\/redir/))
+        img.src = origin + encodeURIComponent("/cookies/resources/imgIfMatch.py?name=" + name + "&value=" + value);
+      else
+        img.src = origin + "/cookies/resources/imgIfMatch.py?name=" + name + "&value=" + value;
+    });
+  }
+
+  function create_test(origin, target, expectedStatus, title) {
+    promise_test(t => {
+      var value = "" + Math.random();
+      return resetSameSiteCookies(origin, value)
+        .then(_ => {
+          return Promise.all([
+            assert_cookie_present(target, "samesite_none", value),
+            expectedStatus == SameSiteStatus.STRICT ?
+              assert_cookie_present(target, "samesite_strict", value) :
+              assert_cookie_absent(target, "samesite_strict", value),
+            expectedStatus == SameSiteStatus.CROSS_SITE ?
+              assert_cookie_absent(target, "samesite_lax", value) :
+              assert_cookie_present(target, "samesite_lax", value)
+          ]);
+        });
+    }, title);
+  }
+
+  // No redirect:
+  create_test(ORIGIN, ORIGIN, SameSiteStatus.STRICT, "Same-host images are strictly same-site");
+  create_test(SUBDOMAIN_ORIGIN, SUBDOMAIN_ORIGIN, SameSiteStatus.STRICT, "Subdomain images are strictly same-site");
+  create_test(CROSS_SITE_ORIGIN, CROSS_SITE_ORIGIN, SameSiteStatus.CROSS_SITE, "Cross-site images are cross-site");
+
+  // Redirect from {same-host,subdomain,cross-site} to same-host:
+  create_test(ORIGIN, redirectTo(ORIGIN, ORIGIN), SameSiteStatus.STRICT, "Same-host redirecting to same-host images are strictly same-site");
+  create_test(ORIGIN, redirectTo(SUBDOMAIN_ORIGIN, ORIGIN), SameSiteStatus.STRICT, "Subdomain redirecting to same-host images are strictly same-site");
+  create_test(ORIGIN, redirectTo(CROSS_SITE_ORIGIN, ORIGIN), SameSiteStatus.STRICT, "Cross-site redirecting to same-host images are strictly same-site");
+
+  // Redirect from {same-host,subdomain,cross-site} to same-host:
+  create_test(SUBDOMAIN_ORIGIN, redirectTo(ORIGIN, SUBDOMAIN_ORIGIN), SameSiteStatus.STRICT, "Same-host redirecting to subdomain images are strictly same-site");
+  create_test(SUBDOMAIN_ORIGIN, redirectTo(SUBDOMAIN_ORIGIN, SUBDOMAIN_ORIGIN), SameSiteStatus.STRICT, "Subdomain redirecting to subdomain images are strictly same-site");
+  create_test(SUBDOMAIN_ORIGIN, redirectTo(CROSS_SITE_ORIGIN, SUBDOMAIN_ORIGIN), SameSiteStatus.STRICT, "Cross-site redirecting to subdomain images are strictly same-site");
+
+  // Redirect from {same-host,subdomain,cross-site} to cross-site:
+  create_test(CROSS_SITE_ORIGIN, redirectTo(ORIGIN, CROSS_SITE_ORIGIN), SameSiteStatus.CROSS_SITE, "Same-host redirecting to cross-site images are cross-site");
+  create_test(CROSS_SITE_ORIGIN, redirectTo(SUBDOMAIN_ORIGIN, CROSS_SITE_ORIGIN), SameSiteStatus.CROSS_SITE, "Subdomain redirecting to cross-site images are cross-site");
+  create_test(CROSS_SITE_ORIGIN, redirectTo(CROSS_SITE_ORIGIN, CROSS_SITE_ORIGIN), SameSiteStatus.CROSS_SITE, "Cross-site redirecting to cross-site images are cross-site");
+</script>
diff --git a/cookies/samesite/window-open-reload.html b/cookies/samesite/window-open-reload.html
new file mode 100644
index 0000000..b37cff8
--- /dev/null
+++ b/cookies/samesite/window-open-reload.html
@@ -0,0 +1,44 @@
+<!DOCTYPE html>
+<meta charset="utf-8"/>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/cookies/resources/cookie-helper.sub.js"></script>
+<script>
+  function create_test(origin, target, expectedStatus, title) {
+    promise_test(t => {
+      var value = "" + Math.random();
+      return resetSameSiteCookies(origin, value)
+        .then(_ => {
+          return new Promise((resolve, reject) => {
+            var w = window.open(origin + "/cookies/resources/postToParent.py");
+
+            var reloaded = false;
+            var msgHandler = e => {
+              try {
+                verifySameSiteCookieState(expectedStatus, value, e.data);
+              } catch (e) {
+                reject(e);
+              }
+
+              if (reloaded) {
+                window.removeEventListener("message", msgHandler);
+                w.close();
+                resolve("Popup received the cookie.");
+              } else {
+                reloaded = true;
+                w.postMessage("reload", "*");
+              }
+            };
+            window.addEventListener("message", msgHandler);
+
+            if (!w)
+              reject("Popup could not be opened (did you whitelist the test site in your popup blocker?).");
+          });
+        });
+    }, title);
+  }
+
+  create_test(ORIGIN, ORIGIN, SameSiteStatus.STRICT, "Reloaded same-host auxiliary navigations are strictly same-site.");
+  create_test(SUBDOMAIN_ORIGIN, SUBDOMAIN_ORIGIN, SameSiteStatus.STRICT, "Reloaded subdomain auxiliary navigations are strictly same-site.");
+  create_test(CROSS_SITE_ORIGIN, CROSS_SITE_ORIGIN, SameSiteStatus.LAX, "Reloaded ross-site auxiliary navigations are laxly same-site");
+</script>
diff --git a/cookies/samesite/window-open.html b/cookies/samesite/window-open.html
new file mode 100644
index 0000000..1aa8e5e
--- /dev/null
+++ b/cookies/samesite/window-open.html
@@ -0,0 +1,53 @@
+<!DOCTYPE html>
+<meta charset="utf-8"/>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/cookies/resources/cookie-helper.sub.js"></script>
+<script>
+  function create_test(origin, target, expectedStatus, title) {
+    promise_test(t => {
+      var value = "" + Math.random();
+      return resetSameSiteCookies(origin, value)
+        .then(_ => {
+          return new Promise((resolve, reject) => {
+            var w = window.open(origin + "/cookies/resources/postToParent.py");
+
+            var msgHandler = e => {
+              window.removeEventListener("message", msgHandler);
+              w.close();
+              try {
+                verifySameSiteCookieState(expectedStatus, value, e.data);
+                resolve("Popup received the cookie.");
+              } catch (e) {
+                reject(e);
+              }
+            };
+            window.addEventListener("message", msgHandler);
+
+            if (!w)
+              reject("Popup could not be opened (did you whitelist the test site in your popup blocker?).");
+          });
+        });
+    }, title);
+  }
+
+  // No redirect:
+  create_test(ORIGIN, ORIGIN, SameSiteStatus.STRICT, "Same-host auxiliary navigations are strictly same-site");
+  create_test(SUBDOMAIN_ORIGIN, SUBDOMAIN_ORIGIN, SameSiteStatus.STRICT, "Subdomain auxiliary navigations are strictly same-site");
+  create_test(CROSS_SITE_ORIGIN, CROSS_SITE_ORIGIN, SameSiteStatus.LAX, "Cross-site auxiliary navigations are laxly same-site");
+
+  // Redirect from {same-host,subdomain,cross-site} to same-host:
+  create_test(ORIGIN, redirectTo(ORIGIN, ORIGIN), SameSiteStatus.STRICT, "Same-host redirecting to same-host auxiliary navigations are strictly same-site");
+  create_test(ORIGIN, redirectTo(SUBDOMAIN_ORIGIN, ORIGIN), SameSiteStatus.STRICT, "Subdomain redirecting to same-host auxiliary navigations are strictly same-site");
+  create_test(ORIGIN, redirectTo(CROSS_SITE_ORIGIN, ORIGIN), SameSiteStatus.STRICT, "Cross-site redirecting to same-host auxiliary navigations are strictly same-site");
+
+  // Redirect from {same-host,subdomain,cross-site} to same-host:
+  create_test(SUBDOMAIN_ORIGIN, redirectTo(ORIGIN, SUBDOMAIN_ORIGIN), SameSiteStatus.STRICT, "Same-host redirecting to subdomain auxiliary navigations are strictly same-site");
+  create_test(SUBDOMAIN_ORIGIN, redirectTo(SUBDOMAIN_ORIGIN, SUBDOMAIN_ORIGIN), SameSiteStatus.STRICT, "Subdomain redirecting to subdomain auxiliary navigations are strictly same-site");
+  create_test(SUBDOMAIN_ORIGIN, redirectTo(CROSS_SITE_ORIGIN, SUBDOMAIN_ORIGIN), SameSiteStatus.STRICT, "Cross-site redirecting to subdomain auxiliary navigations are strictly same-site");
+
+  // Redirect from {same-host,subdomain,cross-site} to cross-site:
+  create_test(CROSS_SITE_ORIGIN, redirectTo(ORIGIN, CROSS_SITE_ORIGIN), SameSiteStatus.LAX, "Same-host redirecting to cross-site auxiliary navigations are laxly same-site");
+  create_test(CROSS_SITE_ORIGIN, redirectTo(SUBDOMAIN_ORIGIN, CROSS_SITE_ORIGIN), SameSiteStatus.LAX, "Subdomain redirecting to cross-site auxiliary navigations are laxly same-site");
+  create_test(CROSS_SITE_ORIGIN, redirectTo(CROSS_SITE_ORIGIN, CROSS_SITE_ORIGIN), SameSiteStatus.LAX, "Cross-site redirecting to cross-site auxiliary navigations are laxly same-site");
+</script>
diff --git a/cookies/secure/cookie-forcing.html b/cookies/secure/cookie-forcing.html
new file mode 100644
index 0000000..3ea59e1
--- /dev/null
+++ b/cookies/secure/cookie-forcing.html
@@ -0,0 +1,46 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/cookies/resources/cookie-helper.sub.js"></script>
+<script>
+  function cookie_force_test(secure_origin, secure_cookie, present, title) {
+    var counter = 0;
+    promise_test(t => {
+
+      var testCookieValue = "" + Math.random();
+      var markerCookieName = "marker";
+      var markerCookieValue = "markerVal";
+
+      var brakes = 5000; //limit cookie setting limit in case browers are magic
+
+      // Set an initial cookie as a marker
+      create_cookie_from_js(markerCookieName, markerCookieValue, 10, secure_cookie);
+      //TODO we cant trust document.cookie to set secure cookies. Need a round trip to a secure origin.
+      assert_dom_cookie(markerCookieName, markerCookieValue, true);
+
+      // Set new cookies until marker is gone
+      try {
+        for (i = 0; i < brakes; i++) {
+          create_cookie_from_js(markerCookieName + counter++, markerCookieValue, 10, secure_cookie);
+          assert_dom_cookie(markerCookieName, markerCookieValue, true);
+        }
+      } catch(err) {
+        //shame on me, just fiddling for now
+      }
+
+      assert_dom_cookie(markerCookieName, markerCookieValue, present);
+
+      if (present == false) {
+        alert("It took " + counter + " cookies to force out the marker cookie");
+      } else {
+        alert("Even after " + counter + " cookies the marker cookie was not forced out. Try incresing the current limit of " + brakes);
+      }
+
+    }, title);
+  }
+
+
+  //actual tests to verify that non-secure origins should "leave secure cookies alone"
+  cookie_force_test(false, false, false, "non-secure origins should be able to force out insecure cookies.");
+
+</script>
diff --git a/cookies/secure/create-cookie-http.html b/cookies/secure/create-cookie-http.html
new file mode 100644
index 0000000..425f66f
--- /dev/null
+++ b/cookies/secure/create-cookie-http.html
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/cookies/resources/cookie-helper.sub.js"></script>
+<script>
+  //origin is who sets the cookie
+  //target is who tries to send cookie
+  function create_test(origin, target, expectedStatus, title) {
+    promise_test(t => {
+      var value = "" + Math.random();
+      return resetSecureCookies(origin, value)
+        .then(_ => {
+          return credFetch(target + "/cookies/resources/list.py")
+            .then(r => r.json())
+            .then(cookies => verifySecureCookieState(expectedStatus, value, cookies));
+        });
+    }, title);
+  }
+
+  //Given an |expectedStatus| and |expectedValue|, assert the |cookies| contains the
+  //proper set of cookie names and values.
+  function verifySecureCookieState(expectedStatus, expectedValue, cookies) {
+    assert_equals(cookies["alone_insecure"], expectedValue, "Insecure cookies are always present");
+    if (expectedStatus == SecureStatus.INSECURE_COOKIE_ONLY) {
+    	assert_equals(cookies["alone_secure"], undefined, "Secure cookies are not present");
+    } else if (expectedStatus == SecureStatus.BOTH_COOKIES) {
+    	assert_equals(cookies["alone_secure"], expectedValue, "Secure cookies are present");
+    }
+  }
+
+  //cookies set by insecure origins
+  create_test(INSECURE_ORIGIN, INSECURE_ORIGIN, SecureStatus.INSECURE_COOKIE_ONLY, "Secure cookies cannot be set by insecure origins");
+  //create_test(INSECURE_ORIGIN, SECURE_ORIGIN, SecureStatus.INSECURE_COOKIE_ONLY, "Secure cookies cannot be set by insecure origins, even if read from a secure origin");
+
+  //This test should set the secure cookie right but not be able to read it from the secure origin
+  //create_test(SECURE_ORIGIN, INSECURE_ORIGIN, SecureStatus.INSECURE_COOKIE_ONLY, "Secure cookies should not be read by insecure origins");
+  //create_test(SECURE_ORIGIN, SECURE_ORIGIN, SecureStatus.BOTH_COOKIES, "Secure cookies should be set and read by secure domains")
+</script>