summaryrefslogtreecommitdiffstats
path: root/testing/web-platform/tests/cors
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
commit26a029d407be480d791972afb5975cf62c9360a6 (patch)
treef435a8308119effd964b339f76abb83a57c29483 /testing/web-platform/tests/cors
parentInitial commit. (diff)
downloadfirefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz
firefox-26a029d407be480d791972afb5975cf62c9360a6.zip
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'testing/web-platform/tests/cors')
-rw-r--r--testing/web-platform/tests/cors/304.htm98
-rw-r--r--testing/web-platform/tests/cors/META.yml6
-rw-r--r--testing/web-platform/tests/cors/README.md8
-rw-r--r--testing/web-platform/tests/cors/access-control-expose-headers-parsing.window.js15
-rw-r--r--testing/web-platform/tests/cors/basic.htm55
-rw-r--r--testing/web-platform/tests/cors/client-hint-request-headers-2.tentative.htm47
-rw-r--r--testing/web-platform/tests/cors/client-hint-request-headers.htm82
-rw-r--r--testing/web-platform/tests/cors/cors-safelisted-request-header.any.js71
-rw-r--r--testing/web-platform/tests/cors/credentials-flag.htm134
-rw-r--r--testing/web-platform/tests/cors/image-tainting-in-cross-origin-iframe.sub.html23
-rw-r--r--testing/web-platform/tests/cors/late-upload-events.htm40
-rw-r--r--testing/web-platform/tests/cors/origin.htm126
-rw-r--r--testing/web-platform/tests/cors/preflight-cache-partitioning.sub.window.js42
-rw-r--r--testing/web-platform/tests/cors/preflight-cache.htm150
-rw-r--r--testing/web-platform/tests/cors/preflight-failure.htm62
-rw-r--r--testing/web-platform/tests/cors/redirect-origin.htm195
-rw-r--r--testing/web-platform/tests/cors/redirect-preflight-2.htm55
-rw-r--r--testing/web-platform/tests/cors/redirect-preflight.htm47
-rw-r--r--testing/web-platform/tests/cors/redirect-userinfo.htm99
-rw-r--r--testing/web-platform/tests/cors/remote-origin.htm121
-rw-r--r--testing/web-platform/tests/cors/request-headers.htm80
-rw-r--r--testing/web-platform/tests/cors/resources/304.py60
-rw-r--r--testing/web-platform/tests/cors/resources/access-control-expose-headers.json62
-rw-r--r--testing/web-platform/tests/cors/resources/cache-304.py10
-rw-r--r--testing/web-platform/tests/cors/resources/checkandremove.py6
-rw-r--r--testing/web-platform/tests/cors/resources/cors-cookie.py21
-rw-r--r--testing/web-platform/tests/cors/resources/cors-headers.asis25
-rw-r--r--testing/web-platform/tests/cors/resources/cors-makeheader.py69
-rw-r--r--testing/web-platform/tests/cors/resources/expose-headers.py11
-rw-r--r--testing/web-platform/tests/cors/resources/image-tainting-checker.sub.html22
-rw-r--r--testing/web-platform/tests/cors/resources/preflight-cache-partitioning-iframe.sub.html27
-rw-r--r--testing/web-platform/tests/cors/resources/preflight-cache-partitioning.sub.html20
-rw-r--r--testing/web-platform/tests/cors/resources/preflight-partitioning.py35
-rw-r--r--testing/web-platform/tests/cors/resources/preflight.py35
-rw-r--r--testing/web-platform/tests/cors/resources/remote-xhrer.html28
-rw-r--r--testing/web-platform/tests/cors/resources/status.py39
-rw-r--r--testing/web-platform/tests/cors/response-headers.htm105
-rw-r--r--testing/web-platform/tests/cors/script-304.html40
-rw-r--r--testing/web-platform/tests/cors/simple-requests-ch.tentative.htm60
-rw-r--r--testing/web-platform/tests/cors/simple-requests.htm90
-rw-r--r--testing/web-platform/tests/cors/status-async.htm114
-rw-r--r--testing/web-platform/tests/cors/status-preflight.htm65
-rw-r--r--testing/web-platform/tests/cors/status.htm80
-rw-r--r--testing/web-platform/tests/cors/support.js17
44 files changed, 2597 insertions, 0 deletions
diff --git a/testing/web-platform/tests/cors/304.htm b/testing/web-platform/tests/cors/304.htm
new file mode 100644
index 0000000000..2aee01417a
--- /dev/null
+++ b/testing/web-platform/tests/cors/304.htm
@@ -0,0 +1,98 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>CORS - 304 Responses</title>
+<meta name="timeout" content="long">
+<meta name=author title="Mark Nottingham" href="mailto:mnot@mnot.net">
+
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=support.js?pipe=sub></script>
+
+<h1>CORS - 304 Responses</h1>
+<div id=log></div>
+<script>
+
+
+/*
+ * 304 Responses
+ */
+
+// A header used to correlate requests and responses
+var state_header = "content-language"
+
+/* Make a request; call ready(client) when done */
+function req(url, id, t, ready) {
+ var client = new XMLHttpRequest()
+ client.open('GET', url, true)
+ client.setRequestHeader(state_header, id)
+ client.send()
+ client.onreadystatechange = function() {
+ if (client.readyState == client.DONE) {
+ t.step(function() {
+ assert_not_equals(client.status, 299, "req " + id + " server says: " + client.responseText)
+ })
+ ready(client)
+ }
+ }
+ return client
+}
+
+/*
+ * Make two requests to test cache behaviour.
+ * The second is made after the first is done and a delay, to make sure it gets into cache.
+ */
+function two_reqs(id1, id2, should_have_same_body, t, done) {
+ var rand = Date.now()
+ var url = CROSSDOMAIN + 'resources/304.py?id=' + id1 + '&r=%s' + rand
+
+ var client1 = req(url, id1, t, function(client1) {
+ t.step(function() {
+ assert_equals(client1.response, "Success", "didn't get successful 1st response;")
+ assert_equals(client1.getResponseHeader(state_header), id1, "1st response didn't come from server;")
+ })
+
+ t.step_timeout(function() {
+ req(url, id2, t, function(client2) {
+ t.step(function() {
+ if (should_have_same_body) {
+ assert_equals(client1.response, client2.response, "response bodies were different;")
+// var res_id2 = client2.getResponseHeader(state_header)
+// assert_not_equals(res_id2, id1, "2nd response doesn't appear to have updated cached headers;")
+// assert_not_equals(res_id2, null, "2nd response didn't expose request identifier;")
+// assert_equals(res_id2, id2, "2nd response is associated with a different request (!);")
+ }
+ done(client1, client2)
+ })
+ t.done()
+ })
+ }, 5000)
+ })
+}
+
+async_test(function(t) {
+ two_reqs('1', '2', true, t, function(client1, client2) {
+ assert_equals(client1.getResponseHeader("A"), null, "'A' header exposed without permission;")
+ })
+}, "A 304 response with no CORS headers inherits from the stored response")
+
+async_test(function(t) {
+ two_reqs('3', '4', true, t, function(client1, client2) {
+ assert_equals(client2.getResponseHeader("A"), "4", "304 didn't expose 'A' header, even though allowed;")
+ assert_equals(client2.getResponseHeader("B"), "4", "304 didn't expose 'B' header even though allowed;")
+ })
+}, "A 304 can expand Access-Control-Expose-Headers")
+
+async_test(function(t) {
+ two_reqs('5', '6', true, t, function(client1, client2) {
+ assert_equals(client2.getResponseHeader("B"), null, "2nd 304 exposed 'B' header;")
+ })
+}, "A 304 can contract Access-Control-Expose-Headers")
+
+async_test(function(t) {
+ two_reqs('7', '8', false, t, function(client1, client2) {
+ assert_not_equals(client1.response, client2.response, "Access granted even though 304 updated it to disallow;")
+ })
+}, "A 304 can change Access-Control-Allow-Origin")
+
+
+</script>
diff --git a/testing/web-platform/tests/cors/META.yml b/testing/web-platform/tests/cors/META.yml
new file mode 100644
index 0000000000..3187818d44
--- /dev/null
+++ b/testing/web-platform/tests/cors/META.yml
@@ -0,0 +1,6 @@
+spec: https://fetch.spec.whatwg.org/#http-cors-protocol
+suggested_reviewers:
+ - odinho
+ - hillbrad
+ - jdm
+ - annevk
diff --git a/testing/web-platform/tests/cors/README.md b/testing/web-platform/tests/cors/README.md
new file mode 100644
index 0000000000..aa5ff90d48
--- /dev/null
+++ b/testing/web-platform/tests/cors/README.md
@@ -0,0 +1,8 @@
+Tests for the [Fetch Standard](https://fetch.spec.whatwg.org/).
+
+These tests are located here as originally the CORS protocol was defined on its own.
+
+More CORS tests can be found in
+
+* /fetch
+* /xhr
diff --git a/testing/web-platform/tests/cors/access-control-expose-headers-parsing.window.js b/testing/web-platform/tests/cors/access-control-expose-headers-parsing.window.js
new file mode 100644
index 0000000000..a139ff6f1e
--- /dev/null
+++ b/testing/web-platform/tests/cors/access-control-expose-headers-parsing.window.js
@@ -0,0 +1,15 @@
+promise_test(() => fetch("resources/access-control-expose-headers.json").then(res => res.json()).then(runTests), "Loading JSON…");
+
+function runTests(allTestData) {
+ allTestData.forEach(testData => {
+ const encodedInput = encodeURIComponent(testData.input);
+ promise_test(() => {
+ const relativeURL = "resources/expose-headers.py?expose=" + encodedInput,
+ url = new URL(relativeURL, location.href).href.replace("://", "://élève.");
+ return fetch(url).then(res => {
+ assert_equals(res.headers.get("content-language"), "mkay");
+ assert_equals(res.headers.get("bb-8"), (testData.exposed ? "hey" : null));
+ });
+ }, "Parsing: " + encodedInput);
+ })
+}
diff --git a/testing/web-platform/tests/cors/basic.htm b/testing/web-platform/tests/cors/basic.htm
new file mode 100644
index 0000000000..cbdb4ca9aa
--- /dev/null
+++ b/testing/web-platform/tests/cors/basic.htm
@@ -0,0 +1,55 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>Basic CORS</title>
+<link rel=help href=https://fetch.spec.whatwg.org/>
+<meta name=author title="Odin Hørthe Omdal" href="mailto:odiho@opera.com">
+
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/utils.js></script>
+<script src=support.js?pipe=sub></script>
+<div id=log></div>
+
+<script>
+function cors(desc, scheme, subdomain = "", port = location.port) {
+ const sameorigin = !scheme;
+ const base =
+ sameorigin ? "" : `${scheme}://${subdomain}${location.hostname}:${port}${dirname(location.pathname)}`;
+
+ async_test((t) => {
+ const client = new XMLHttpRequest();
+ client.open("GET", `${base}resources/cors-makeheader.py?get_value=hest_er_best&origin=none&${token()}`);
+ client.send();
+
+ client.onload = t.step_func_done(() => {
+ assert_true(sameorigin, "Cross origin request must be rejected.");
+ assert_true(client.response.includes("hest_er_best"), "Got response");
+ });
+ client.onerror = t.step_func_done(() => {
+ assert_false(sameorigin, "Same origin request must be accepted.");
+ });
+ }, `${desc}, origin: none`);
+
+ async_test((t) => {
+ const client = new XMLHttpRequest();
+ client.open("GET", `${base}resources/cors-makeheader.py?get_value=hest_er_best&${token()}`);
+ client.send();
+
+ client.onload = t.step_func_done(() => {
+ assert_true(client.response.includes("hest_er_best"), "Got response");
+ });
+ client.onerror = t.unreached_func("Should be accepted");
+ }, `${desc}, origin: echo`);
+}
+
+cors("Same domain basic usage");
+cors("Cross domain basic usage", "http", "www1.");
+cors("Same domain different port", "http", undefined, PORT);
+
+cors("Cross domain different port", "http", "www1.", PORT);
+
+cors("Cross domain different protocol", "https", "www1.", PORTS);
+
+cors("Same domain different protocol different port", "https", undefined, PORTS);
+
+</script>
diff --git a/testing/web-platform/tests/cors/client-hint-request-headers-2.tentative.htm b/testing/web-platform/tests/cors/client-hint-request-headers-2.tentative.htm
new file mode 100644
index 0000000000..f7ec3dc6d1
--- /dev/null
+++ b/testing/web-platform/tests/cors/client-hint-request-headers-2.tentative.htm
@@ -0,0 +1,47 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>CORS and Client Hints, potentially</title>
+
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=support.js?pipe=sub></script>
+
+<h1>Request headers</h1>
+<div id=log></div>
+<script>
+
+test(function() {
+ var client = new XMLHttpRequest()
+ client.open('GET', CROSSDOMAIN + 'resources/cors-makeheader.py?headers=x-print,', false)
+ client.setRequestHeader('x-print', 'unicorn')
+ client.setRequestHeader('content-type', 'text/plain')
+ client.setRequestHeader('accept', 'test')
+ client.setRequestHeader('accept-language', 'nn')
+ client.setRequestHeader('content-language', 'nn')
+ client.setRequestHeader('save-data', 'on')
+ client.setRequestHeader('device-memory', '1.0')
+ client.setRequestHeader('dpr', '2.0')
+ client.setRequestHeader('width', '35')
+ client.setRequestHeader('viewport-width', '42')
+ client.setRequestHeader('rtt', '1')
+ client.setRequestHeader('downlink', '1.0')
+ client.setRequestHeader('ect', '2g')
+ client.send(null)
+
+ const res = JSON.parse(client.response)
+ assert_equals(res['x-print'], 'unicorn')
+ assert_equals(res['content-type'], 'text/plain')
+ assert_equals(res['accept'], 'test')
+ assert_equals(res['accept-language'], 'nn')
+ assert_equals(res['content-language'], 'nn')
+ assert_equals(res['save-data'], 'on')
+ assert_equals(res['device-memory'], '1.0')
+ assert_equals(res['dpr'], '2.0')
+ assert_equals(res['width'], '35')
+ assert_equals(res['viewport-width'], '42')
+ assert_equals(res['rtt'], '1')
+ assert_equals(res['downlink'], '1.0')
+ assert_equals(res['ect'], '2g')
+}, 'Client hint headers are simple headers')
+
+</script>
diff --git a/testing/web-platform/tests/cors/client-hint-request-headers.htm b/testing/web-platform/tests/cors/client-hint-request-headers.htm
new file mode 100644
index 0000000000..6cf298433e
--- /dev/null
+++ b/testing/web-platform/tests/cors/client-hint-request-headers.htm
@@ -0,0 +1,82 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>CORS and Client Hints</title>
+
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=support.js?pipe=sub></script>
+
+<h1>Request headers</h1>
+<div id=log></div>
+<script>
+
+test(function() {
+ var client = new XMLHttpRequest()
+ client.open('GET', CROSSDOMAIN + 'resources/cors-makeheader.py?headers=x-print', false)
+ client.setRequestHeader('x-print', 'unicorn')
+ client.setRequestHeader('y-print', 'unicorn')
+ assert_throws_dom("NetworkError", function() { client.send(null) })
+}, 'Unspecified request headers are disallowed')
+
+test(function() {
+ var client = new XMLHttpRequest()
+ client.open('GET', CROSSDOMAIN + 'resources/cors-makeheader.py?headers=x-print', false)
+ client.setRequestHeader('device-memory', '')
+ assert_throws_dom("NetworkError", function() { client.send(null) })
+}, 'Unextractable device-memory client hint header is disallowed')
+
+test(function() {
+ var client = new XMLHttpRequest()
+ client.open('GET', CROSSDOMAIN + 'resources/cors-makeheader.py?headers=x-print', false)
+ client.setRequestHeader('dpr', '')
+ assert_throws_dom("NetworkError", function() { client.send(null) })
+}, 'Unextractable DPR client hint header is disallowed')
+
+test(function() {
+ var client = new XMLHttpRequest()
+ client.open('GET', CROSSDOMAIN + 'resources/cors-makeheader.py?headers=x-print', false)
+ client.setRequestHeader('width', '')
+ assert_throws_dom("NetworkError", function() { client.send(null) })
+}, 'Unextractable width client hint header is disallowed')
+
+test(function() {
+ var client = new XMLHttpRequest()
+ client.open('GET', CROSSDOMAIN + 'resources/cors-makeheader.py?headers=x-print', false)
+ client.setRequestHeader('viewport-width', '')
+ assert_throws_dom("NetworkError", function() { client.send(null) })
+}, 'Unextractable viewport-width client hint header is disallowed')
+
+test(function() {
+ var client = new XMLHttpRequest()
+ client.open('GET', CROSSDOMAIN + 'resources/cors-makeheader.py?headers=x-print', false)
+ client.setRequestHeader('rtt', '')
+ assert_throws_dom("NetworkError", function() { client.send(null) })
+ client = new XMLHttpRequest()
+ client.open('GET', CROSSDOMAIN + 'resources/cors-makeheader.py?headers=x-print', false)
+ client.setRequestHeader('rtt', '-1')
+ assert_throws_dom("NetworkError", function() { client.send(null) })
+}, 'Test invalid rtt value is disallowed')
+
+test(function() {
+ var client = new XMLHttpRequest()
+ client.open('GET', CROSSDOMAIN + 'resources/cors-makeheader.py?headers=x-print', false)
+ client.setRequestHeader('downlink', '')
+ assert_throws_dom("NetworkError", function() { client.send(null) })
+ client = new XMLHttpRequest()
+ client.open('GET', CROSSDOMAIN + 'resources/cors-makeheader.py?headers=x-print', false)
+ client.setRequestHeader('downlink', '-1.0')
+ assert_throws_dom("NetworkError", function() { client.send(null) })
+}, 'Test invalid downlink value is disallowed')
+
+test(function() {
+ var client = new XMLHttpRequest()
+ client.open('GET', CROSSDOMAIN + 'resources/cors-makeheader.py?headers=x-print', false)
+ client.setRequestHeader('ect', '')
+ assert_throws_dom("NetworkError", function() { client.send(null) })
+ client = new XMLHttpRequest()
+ client.open('GET', CROSSDOMAIN + 'resources/cors-makeheader.py?headers=x-print', false)
+ client.setRequestHeader('ect', '6g')
+ assert_throws_dom("NetworkError", function() { client.send(null) })
+}, 'Test invalid ect value is disallowed')
+
+</script>
diff --git a/testing/web-platform/tests/cors/cors-safelisted-request-header.any.js b/testing/web-platform/tests/cors/cors-safelisted-request-header.any.js
new file mode 100644
index 0000000000..a0a0417d74
--- /dev/null
+++ b/testing/web-platform/tests/cors/cors-safelisted-request-header.any.js
@@ -0,0 +1,71 @@
+// META: script=support.js?pipe=sub
+// META: script=/common/utils.js
+
+// This is based on simple-requests.htm, with modifications to make the code more modern and test
+// more esoteric cases of header value parsing.
+
+function safelist(headers, expectPreflight = false) {
+ promise_test(async t => {
+ const uuid = token(),
+ url = CROSSDOMAIN + "resources/preflight.py?token=" + uuid,
+ checkURL = "resources/preflight.py?check&token=" + uuid,
+ request = () => fetch(url, { method: "POST", headers, body: "data" });
+ if (expectPreflight) {
+ await promise_rejects_js(t, TypeError, request());
+ } else {
+ const response = await request();
+ assert_equals(response.headers.get("content-type"), "text/plain");
+ assert_equals(await response.text(), "NO");
+ }
+ const checkResponse = await fetch(checkURL, { method: "POST", body: "data" });
+ assert_equals(await checkResponse.text(), (expectPreflight ? "1" : "0"));
+ }, (expectPreflight ? "Preflight" : "No preflight") + " for " + JSON.stringify(headers));
+}
+
+[
+ ["text /plain", true],
+ ["text\t/\tplain", true],
+ ["text/plain;"],
+ ["text/plain;garbage"],
+ ["text/plain;garbage\u0001\u0002", true],
+ ["text/plain,", true],
+ [",text/plain", true],
+ ["text/plain,text/plain", true],
+ ["text/plain,x/x", true],
+ ["text/plain\u000B", true],
+ ["text/plain\u000C", true],
+ ["application/www-form-urlencoded", true],
+ ["application/x-www-form-urlencoded;\u007F", true],
+ ["multipart/form-data"],
+ ["multipart/form-data;\"", true]
+].forEach(([mimeType, preflight = false]) => {
+ safelist({"content-type": mimeType}, preflight);
+});
+
+[
+ ["100-200", true],
+ ["MB=100-200", true],
+ ["bytes=100-200"],
+ ["bytes =100-200", true],
+ ["bytes\t=100-200", true],
+ ["bytes= 100-200", true],
+ ["bytes=\t\t100-200", true],
+ ["bytes= \t100-200", true],
+ ["bytes=100 -200", true],
+ ["bytes=100\t-200", true],
+ ["bytes=100- 200", true],
+ ["bytes=100-\t200", true],
+ ["bytes=100-200hello", true],
+ ["bytes=abc-def", true],
+ ["bytes=100-200,300-400", true],
+ ["bytes=-200", true],
+ ["bytes=200-"],
+ ["bytes=200-100", true],
+ [`bytes=1${'0'.repeat(60)}-2${'0'.repeat(60)}`, true],
+ ["bytes 100-200", true],
+ ["bytes = 100-200", true],
+ [",bytes=100-200", true],
+ ["bytes=,100-200", true],
+].forEach(([value, preflight = false]) => {
+ safelist({"range": value}, preflight);
+});
diff --git a/testing/web-platform/tests/cors/credentials-flag.htm b/testing/web-platform/tests/cors/credentials-flag.htm
new file mode 100644
index 0000000000..45a7143685
--- /dev/null
+++ b/testing/web-platform/tests/cors/credentials-flag.htm
@@ -0,0 +1,134 @@
+<!DOCTYPE html>
+<title>CORS - Access-Control-Allow-Credentials</title>
+<meta name=author title="Odin Hørthe Omdal" href="mailto:odiho@opera.com">
+
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=support.js?pipe=sub></script>
+
+<h1>CORS - Access-Control-Allow-Credentials</h1>
+<div id=log></div>
+<script>
+
+var url = CROSSDOMAIN + 'resources/cors-cookie.py?ident='
+
+
+/*
+ * widthCredentials
+ */
+// XXX Do some https tests here as well
+
+test(function () {
+ var client = new XMLHttpRequest()
+ client.open('GET', CROSSDOMAIN, false)
+ client.withCredentials = true;
+}, 'Setting withCredentials on a sync XHR object should not throw')
+
+async_test(function () {
+ var id = new Date().getTime() + '_1',
+ client = new XMLHttpRequest()
+ client.open("GET", url + id, true)
+ client.onload = this.step_func(function() {
+ assert_equals(client.response, "NO_COOKIE")
+ client.open("GET", url + id, true)
+ client.onload = this.step_func(function() {
+ assert_equals(client.response, "NO_COOKIE")
+ this.done()
+ })
+ client.send(null)
+ })
+ client.send(null)
+
+}, "Don't send cookie by default");
+
+async_test(function () {
+ var id = new Date().getTime() + '_2',
+ client = new XMLHttpRequest()
+
+ client.open("GET", url + id, true)
+ client.withCredentials = true
+ client.onload = this.step_func(function() {
+ assert_equals(client.response, "NO_COOKIE", "No cookie in initial request");
+
+ /* We have cookie, but the browser shouldn't send */
+ client.open("GET", url + id, true)
+ client.withCredentials = false
+ client.onload = this.step_func(function() {
+ assert_equals(client.response, "NO_COOKIE", "No cookie after withCredentials=false sync request")
+
+ /* Reads and deletes the cookie */
+ client.open("GET", url + id, true)
+ client.withCredentials = true
+ client.onload = this.step_func(function() {
+ assert_equals(client.response, "COOKIE", "Cookie sent in withCredentials=true sync request")
+ this.done()
+ })
+ client.send(null)
+ })
+ client.send(null)
+ })
+ client.send(null)
+}, "Don't send cookie part 2");
+
+async_test(function () {
+ var id = new Date().getTime() + '_3',
+ client = new XMLHttpRequest()
+
+ /* Shouldn't set the response cookie */
+ client.open("GET", url + id, true)
+ client.withCredentials = false
+ client.onload = this.step_func(function() {
+ assert_equals(client.response, "NO_COOKIE", "first");
+
+ /* Sets the cookie */
+ client.open("GET", url + id, true)
+ client.withCredentials = true
+ client.onload = this.step_func(function() {
+ assert_equals(client.response, "NO_COOKIE", "second")
+
+ /* Reads and deletes the cookie */
+ client.open("GET", url + id, true)
+ client.withCredentials = true
+ client.onload = this.step_func(function() {
+ assert_equals(client.response, "COOKIE", "third")
+ this.done()
+ })
+ client.send(null)
+ })
+ client.send(null)
+ })
+ client.send(null)
+}, "Don't obey Set-Cookie when withCredentials=false");
+
+function test_response_header(allow) {
+ var resp_test = async_test('Access-Control-Allow-Credentials: ' + allow + ' should be disallowed (async)')
+ resp_test.step(function() {
+ var client = new XMLHttpRequest()
+ client.open('GET',
+ CROSSDOMAIN + 'resources/cors-makeheader.py?credentials=' + allow,
+ true)
+ client.withCredentials = true;
+ client.onload = resp_test.step_func(function() {
+ assert_unreached("onload")
+ })
+ client.onerror = resp_test.step_func(function () {
+ assert_equals(client.readyState, client.DONE, 'readyState')
+ resp_test.done()
+ })
+ client.send()
+ })
+}
+
+test_response_header('TRUE')
+test_response_header('True')
+test_response_header('"true"')
+test_response_header("'true'");
+test_response_header('false')
+test_response_header('1')
+test_response_header('0')
+test_response_header(',true');
+test_response_header('true,');
+test_response_header('true%0B');
+test_response_header('true%0C');
+
+</script>
diff --git a/testing/web-platform/tests/cors/image-tainting-in-cross-origin-iframe.sub.html b/testing/web-platform/tests/cors/image-tainting-in-cross-origin-iframe.sub.html
new file mode 100644
index 0000000000..0cd87756e9
--- /dev/null
+++ b/testing/web-platform/tests/cors/image-tainting-in-cross-origin-iframe.sub.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<body>
+</body>
+<script>
+async_test(t => {
+ const img = document.createElement('img');
+ img.onload = t.step_func(() => {
+ const iframe = document.createElement('iframe');
+ window.onmessage = t.step_func_done(e => {
+ assert_equals(e.data, 'DONE');
+ });
+ iframe.src = 'http://{{domains[www1]}}:{{ports[http][0]}}/cors/resources/image-tainting-checker.sub.html';
+ document.body.appendChild(iframe);
+ });
+ img.src = '/images/blue-png-cachable.py';
+ document.body.appendChild(img);
+}, 'An image resource that is same-origin to the top-level frame loaded in ' +
+ 'the frame is not treated as same-origin for an iframe that is ' +
+ 'cross-origin to the top-level frame, and therefore a canvas where the ' +
+ 'image is drawn gets tainted.');
+</script>
diff --git a/testing/web-platform/tests/cors/late-upload-events.htm b/testing/web-platform/tests/cors/late-upload-events.htm
new file mode 100644
index 0000000000..02cd27322b
--- /dev/null
+++ b/testing/web-platform/tests/cors/late-upload-events.htm
@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>Adding upload event listeners after send()</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=support.js?pipe=sub></script>
+
+<h1>Adding upload event listeners after send()</h1>
+
+<div id=log></div>
+
+<script>
+function doTest(desc, headers) {
+ async_test("Late listeners: " + desc).step(function() {
+ var client = new XMLHttpRequest();
+ var eventCounter = 0;
+ client.open("POST", CROSSDOMAIN + "resources/status.py?headers=custom-header");
+
+ for (var name in headers) {
+ client.setRequestHeader(name, headers[name]);
+ }
+
+ client.onreadystatechange = this.step_func(function(e) {
+ // Irrelevant if request is not finished
+ if (client.readyState < 4) return;
+ assert_equals(client.status, 200);
+ assert_equals(eventCounter, 0, 'Events fired, but should not have');
+ this.done();
+ });
+ client.send((new Array(3000)).join('xo'));
+ client.upload.onprogress = client.upload.onloadend = client.upload.onloadstart = client.upload.onload = this.step_func(function(e) {
+ eventCounter++;
+ assert_unreached("Upload events should not fire, but did: " + e.type);
+ });
+ });
+}
+
+doTest("No preflight", {});
+doTest("Preflight", {"custom-header":"test"});
+</script>
diff --git a/testing/web-platform/tests/cors/origin.htm b/testing/web-platform/tests/cors/origin.htm
new file mode 100644
index 0000000000..3d46002933
--- /dev/null
+++ b/testing/web-platform/tests/cors/origin.htm
@@ -0,0 +1,126 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>Access-Control-Allow-Origin handling</title>
+<meta name="timeout" content="long">
+<link rel=help href=https://fetch.spec.whatwg.org/>
+<meta name=author title="Odin Hørthe Omdal" href="mailto:odiho@opera.com">
+
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=support.js?pipe=sub></script>
+
+<h1>Access-Control-Allow-Origin handling</h1>
+
+<div id=log></div>
+
+<script>
+
+/*
+ * Origin header
+ */
+function shouldPass(origin) {
+ test(function () {
+ var client = new XMLHttpRequest()
+ client.open('GET', CROSSDOMAIN
+ + '/resources/cors-makeheader.py?origin='
+ + encodeURIComponent(origin),
+ false)
+ client.send()
+ r = JSON.parse(client.response)
+ var host = location.protocol + "//" + location.host
+ assert_equals(r['origin'], host, 'Request Origin: should be ' + host)
+ }, 'Allow origin: ' + origin.replace(/\t/g, "[tab]").replace(/ /g, '_'));
+}
+
+shouldPass('*');
+shouldPass(' * ');
+shouldPass(' *');
+shouldPass(location.protocol + "//" + location.host);
+shouldPass(" "+location.protocol + "//" + location.host);
+shouldPass(" "+location.protocol + "//" + location.host + " ");
+shouldPass(" "+location.protocol + "//" + location.host);
+
+
+function shouldFail(origin) {
+ test(function () {
+ var client = new XMLHttpRequest()
+ client.open('GET', CROSSDOMAIN
+ + '/resources/cors-makeheader.py?origin='
+ + encodeURIComponent(origin),
+ false)
+ assert_throws_dom("NetworkError", function() { client.send() }, 'send')
+ }, 'Disallow origin: ' + origin.replace(/\0/g, "\\0"));
+}
+
+shouldFail(location.protocol + "//" + SUBDOMAIN + "." + location.host)
+shouldFail("//" + location.host)
+shouldFail("://" + location.host)
+shouldFail("ftp://" + location.host)
+shouldFail("http:://" + location.host)
+shouldFail("http:/" + location.host)
+shouldFail("http:" + location.host)
+shouldFail(location.host)
+shouldFail(location.protocol + "//" + location.host + "?")
+shouldFail(location.protocol + "//" + location.host + "/")
+shouldFail(location.protocol + "//" + location.host + " /")
+shouldFail(location.protocol + "//" + location.host + "#")
+shouldFail(location.protocol + "//" + location.host + "%23")
+shouldFail(location.protocol + "//" + location.host + ":80")
+shouldFail(location.protocol + "//" + location.host + ", *")
+shouldFail(location.protocol + "//" + location.host + "\0")
+shouldFail((location.protocol + "//" + location.host).toUpperCase())
+shouldFail(location.protocol.toUpperCase() + "//" + location.host)
+shouldFail("-")
+shouldFail("**")
+shouldFail(",*");
+shouldFail("*,");
+shouldFail("\0*")
+shouldFail("\u000B*");
+shouldFail("\u000C*");
+shouldFail("*\0")
+shouldFail("*\u000B");
+shouldFail("*\u000C");
+shouldFail("'*'")
+shouldFail('"*"')
+shouldFail("* *")
+shouldFail("* null")
+shouldFail("*" + location.protocol + "//" + "*")
+shouldFail("*" + location.protocol + "//" + location.host)
+shouldFail("* " + location.protocol + "//" + location.host)
+shouldFail("*, " + location.protocol + "//" + location.host)
+shouldFail("\0" + location.protocol + "//" + location.host)
+shouldFail("null " + location.protocol + "//" + location.host)
+shouldFail('http://example.net')
+shouldFail('null')
+shouldFail('null *')
+shouldFail('')
+shouldFail(location.href)
+shouldFail(dirname(location.href))
+shouldFail(CROSSDOMAIN)
+shouldFail(location.host.replace(/^[^\.]+\./, ""))
+shouldFail("." + location.host.replace(/^[^\.]+\./, ""))
+shouldFail("*." + location.host.replace(/^[^\.]+\./, ""))
+shouldFail("http://" + location.host.replace(/^[^\.]+\./, ""))
+shouldFail("http://." + location.host.replace(/^[^\.]+\./, ""))
+shouldFail("http://*." + location.host.replace(/^[^\.]+\./, ""))
+
+function doubleOrigin(origin, origin2) {
+ test(function () {
+ var client = new XMLHttpRequest()
+ client.open('GET', CROSSDOMAIN
+ + '/resources/cors-makeheader.py?origin='
+ + encodeURIComponent(origin)
+ + '&origin2=' + encodeURIComponent(origin2),
+ false)
+ assert_throws_dom("NetworkError", function() { client.send() }, 'send')
+ }, 'Disallow multiple headers (' + origin + ', ' + origin2 + ')');
+}
+
+doubleOrigin('', '*');
+doubleOrigin('*', '');
+doubleOrigin('*', '*');
+doubleOrigin('', location.protocol + "//" + location.host);
+doubleOrigin('*', location.protocol + "//" + location.host);
+doubleOrigin(location.protocol + "//" + location.host, location.protocol + "//" + location.host);
+
+</script>
diff --git a/testing/web-platform/tests/cors/preflight-cache-partitioning.sub.window.js b/testing/web-platform/tests/cors/preflight-cache-partitioning.sub.window.js
new file mode 100644
index 0000000000..6b37a843c4
--- /dev/null
+++ b/testing/web-platform/tests/cors/preflight-cache-partitioning.sub.window.js
@@ -0,0 +1,42 @@
+// META: script=/common/utils.js
+
+const TEST_PAGE =
+ "http://{{host}}:{{ports[http][0]}}/cors/resources/preflight-cache-partitioning.sub.html";
+const TEST_ANOTHER_PAGE =
+ "http://{{hosts[alt][]}}:{{ports[http][0]}}/cors/resources/preflight-cache-partitioning.sub.html";
+
+promise_test(async t => {
+ let uuid_token = token();
+
+ const TEST_PAGES = [TEST_PAGE, TEST_ANOTHER_PAGE];
+
+ // We will load the same page with different top-level origins to check if the
+ // CORS preflight cache is partitioned. The page will load the iframe with one
+ // origin and trigger the CORS preflight through fetching a cross-origin
+ // resources in the iframe.
+
+ for (let test_page of TEST_PAGES) {
+ let win;
+
+ await new Promise(resolve => {
+ window.onmessage = (e) => {
+ if (e.data.type === "loaded") {
+ resolve();
+ }
+ };
+
+ win = window.open(test_page);
+ });
+
+ await new Promise(resolve => {
+ win.postMessage({ type: "run", token: uuid_token }, "*");
+
+ window.onmessage = (e) => {
+ assert_equals(e.data.type, "pass", e.data.msg);
+ resolve();
+ };
+ });
+
+ win.close();
+ }
+}, "The preflight cache should be partitioned");
diff --git a/testing/web-platform/tests/cors/preflight-cache.htm b/testing/web-platform/tests/cors/preflight-cache.htm
new file mode 100644
index 0000000000..b3de663ebc
--- /dev/null
+++ b/testing/web-platform/tests/cors/preflight-cache.htm
@@ -0,0 +1,150 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>CORS - preflight cache</title>
+<meta name="timeout" content="long">
+<meta name=author title="Odin Hørthe Omdal" href="mailto:odiho@opera.com">
+
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/utils.js></script>
+<script src=support.js?pipe=sub></script>
+
+<h1>Preflight cache</h1>
+
+<div id=log></div>
+<script>
+
+/*
+ * Cache
+ */
+
+function did_preflight(expect, client, settings) {
+ var uuid_token = (settings && settings.token) || token();
+ if(!settings)
+ settings = {}
+
+ set = {
+ method: 'method' in settings ? settings.method : 'GET',
+ extra: 'extra' in settings ? '&' + settings.extra : ''
+ }
+
+ client.open(set.method,
+ CROSSDOMAIN + 'resources/preflight.py?token=' + uuid_token + set.extra,
+ false)
+ client.setRequestHeader('x-print', uuid_token)
+ client.send()
+
+ client.open('GET', 'resources/preflight.py?check&token=' + uuid_token, false)
+ client.send()
+ assert_equals(client.response, expect === true ? '1' : '0', "did preflight")
+ return uuid_token;
+}
+
+/*
+ * Should run preflight
+ */
+
+test(function() {
+ var time = new Date().getTime()
+ var client = new XMLHttpRequest()
+ did_preflight(true, client);
+},
+'Test preflight')
+
+test(function() {
+ var time = new Date().getTime()
+ var client = new XMLHttpRequest()
+
+ var id = did_preflight(true, client)
+ did_preflight(false, client, {token: id})
+},
+'preflight for x-print should be cached')
+
+test(function() {
+ var time = new Date().getTime()
+ var client = new XMLHttpRequest()
+
+ var id = did_preflight(true, client, {extra:'max_age='})
+ did_preflight(false, client, {extra:'max_age=', token: id})
+},
+'age = blank, should be cached')
+
+test(function() {
+ var time = new Date().getTime()
+ var client = new XMLHttpRequest()
+
+ var id = did_preflight(true, client, {extra:'max_age=0'})
+ did_preflight(true, client, {extra:'max_age=0', token: id})
+},
+'age = 0, should not be cached')
+
+test(function() {
+ var time = new Date().getTime()
+ var client = new XMLHttpRequest()
+
+ var id = did_preflight(true, client, {extra:'max_age=-1'})
+ did_preflight(true, client, {extra:'max_age=-1', token: id})
+},
+'age = -1, should not be cached');
+
+(function() {
+ var test = async_test("preflight first request, second from cache, wait, third should preflight again"),
+ time = new Date().getTime(),
+ dothing = function (url, msg, set_request, func) {
+ client = new XMLHttpRequest(),
+ client.open('GET', url, true)
+ if (set_request)
+ client.setRequestHeader('x-print', msg)
+ client.onload = test.step_func(function() {
+ assert_equals(client.response, msg, "response " + url)
+ if (func)
+ test.step(func)
+ })
+ client.onerror = test.step_func(function(e) {
+ assert_unreached("Got unexpected error event on the XHR object")
+ })
+ client.send()
+ }
+
+ var token1 = token();
+ test.step(function() {
+ /* First cycle, gets x-print into the cache, with timeout 1 */
+ var request_url = CROSSDOMAIN + 'resources/preflight.py?max_age=1&token=' + token1;
+ dothing(request_url,
+ 'first', true, function() {
+ test = test;
+
+ /* Check if we did a preflight like we expected */
+ dothing('resources/preflight.py?check&1&token=' + token1,
+ '1', false, function() {
+ test = test;
+ dothing(request_url,
+ 'second', true, function() {
+ test = test;
+
+ /* Check that we didn't do a preflight (hasn't gone 1 second yet) */
+ dothing('resources/preflight.py?check&2&token=' + token1,
+ '0', false, function() {
+ test = test;
+
+ /* Wait until the preflight cache age is old (and thus cleared) */
+ test.step_timeout(() => {
+ dothing(request_url,
+ 'third', true, function() {
+ test = test;
+
+ /* Expect that we did indeed do a preflight */
+ dothing('resources/preflight.py?check&3&token=' + token1,
+ '1', false, function() {
+ test.done()
+ })
+ })
+ }, 1500)
+ })
+ })
+ })
+ })
+ })
+})();
+
+</script>
diff --git a/testing/web-platform/tests/cors/preflight-failure.htm b/testing/web-platform/tests/cors/preflight-failure.htm
new file mode 100644
index 0000000000..fd34d55b79
--- /dev/null
+++ b/testing/web-platform/tests/cors/preflight-failure.htm
@@ -0,0 +1,62 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>CORS - Preflight responds with non-2XX status code</title>
+<meta name=author title="Fernando Jiménez Moreno" href="mailto:ferjmoreno@gmail.com">
+
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/common/get-host-info.sub.js></script>
+
+<h1>Preflight responds with non-2XX status code</h1>
+
+<div id=log></div>
+<script>
+
+// Request count for cache busting and easy identifying of request in traffic
+// analyzer.
+var req_c = 0;
+
+var CROSSDOMAIN_URL = get_host_info().HTTP_REMOTE_ORIGIN + '/cors/resources/cors-makeheader.py?';
+
+/*
+ * Redirection with preflights.
+ */
+function preflight_failure(code) {
+ var isCodeOK = code >= 200 && code <= 299,
+ descOK = isCodeOK ? 'succeed' : 'throw error',
+ desc = 'Should ' + descOK + ' if preflight has status ' + code;
+ async_test(desc).step(function() {
+ var client = new XMLHttpRequest();
+ var redirect =
+ encodeURIComponent(CROSSDOMAIN_URL + 'headers=x-test&' + req_c++);
+
+ client.open('GET',
+ CROSSDOMAIN_URL + 'headers=x-test&location=' + redirect
+ + '&code=' + code + '&preflight=' + code
+ + '&' + req_c++,
+ true /* async */);
+ client.setRequestHeader('x-test', 'test');
+ client.onerror = this.step_func(function() {
+ assert_false(isCodeOK);
+ this.done();
+ });
+ client.onreadystatechange = this.step_func(function() {
+ if (!isCodeOK)
+ assert_equals(client.status, 0);
+ });
+ client.onload = this.step_func(function() {
+ assert_true(isCodeOK);
+ this.done();
+ });
+ client.send(null);
+ });
+}
+[200, 299,
+ 300, 301, 302, 303, 304, 305, 306, 307, 308,
+ 400, 401, 402, 403, 404, 405, 406, 407, 408, 409, 410, 411, 412, 413, 414, 415, 416, 417,
+ 500, 501, 502, 503, 504, 505,
+ 680,
+ 790
+].forEach(preflight_failure);
+
+</script>
diff --git a/testing/web-platform/tests/cors/redirect-origin.htm b/testing/web-platform/tests/cors/redirect-origin.htm
new file mode 100644
index 0000000000..5463292fa5
--- /dev/null
+++ b/testing/web-platform/tests/cors/redirect-origin.htm
@@ -0,0 +1,195 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>CORS - redirect</title>
+<meta name=author title="Odin Hørthe Omdal" href="mailto:odiho@opera.com">
+
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=support.js?pipe=sub></script>
+
+<h1>CORS redirect handling</h1>
+
+<div id=log></div>
+
+<script>
+
+ // Test count for cache busting and easy identifying of request in traffic analyzer
+ var num_test = 0,
+
+ origin = location.protocol + "//" + location.host,
+ remote_origin = origin.replace('://', '://' + SUBDOMAIN + '.'),
+
+ local = dirname(location.href) + 'resources/cors-makeheader.py',
+ remote = local.replace('://', '://' + SUBDOMAIN + '.'),
+ remote2 = local.replace('://', '://' + SUBDOMAIN2 + '.');
+
+
+ /* First page Redirect to Expect what */
+
+ // local -> remote
+
+ redir_test([ 'local', '*' ], [ 'remote', '*' ], origin );
+ redir_test([ 'local', '*' ], [ 'remote', origin ], origin );
+ redir_test([ 'local', '*' ], [ 'remote', 'null' ], 'disallow');
+ redir_test([ 'local', '*' ], [ 'remote', 'none' ], 'disallow');
+
+ redir_test([ 'local', origin ], [ 'remote', '*' ], origin );
+ redir_test([ 'local', origin ], [ 'remote', origin ], origin );
+ redir_test([ 'local', origin ], [ 'remote', 'null' ], 'disallow');
+ redir_test([ 'local', origin ], [ 'remote', 'none' ], 'disallow');
+
+ redir_test([ 'local', 'null' ], [ 'remote', '*' ], origin );
+ redir_test([ 'local', 'none' ], [ 'remote', '*' ], origin );
+
+
+ // remote -> local
+
+ redir_test([ 'remote', '*' ], [ 'local', '*' ], 'null' );
+ redir_test([ 'remote', '*' ], [ 'local', origin ], 'disallow');
+ redir_test([ 'remote', '*' ], [ 'local', 'null' ], 'null' );
+ redir_test([ 'remote', '*' ], [ 'local', 'none' ], 'disallow');
+
+ redir_test([ 'remote', origin ], [ 'local', '*' ], 'null' );
+ redir_test([ 'remote', origin ], [ 'local', origin ], 'disallow');
+ redir_test([ 'remote', origin ], [ 'local', 'null' ], 'null' );
+ redir_test([ 'remote', origin ], [ 'local', 'none' ], 'disallow');
+
+ redir_test([ 'remote', 'null' ], [ 'local', '*' ], 'disallow');
+ redir_test([ 'remote', 'none' ], [ 'local', '*' ], 'disallow');
+
+
+ // remote -> remote
+
+ redir_test([ 'remote', '*' ], [ 'remote', '*' ], origin );
+ redir_test([ 'remote', '*' ], [ 'remote', origin ], origin );
+ redir_test([ 'remote', '*' ], [ 'remote', 'null' ], 'disallow');
+ redir_test([ 'remote', '*' ], [ 'remote', 'none' ], 'disallow');
+
+ redir_test([ 'remote', origin ], [ 'remote', '*' ], origin );
+ redir_test([ 'remote', origin ], [ 'remote', origin ], origin );
+ redir_test([ 'remote', origin ], [ 'remote', 'null' ], 'disallow');
+ redir_test([ 'remote', origin ], [ 'remote', 'none' ], 'disallow');
+
+ redir_test([ 'remote', 'null' ], [ 'remote', '*' ], 'disallow');
+ redir_test([ 'remote', 'none' ], [ 'remote', '*' ], 'disallow');
+
+
+ // remote -> remote2
+
+ redir_test([ 'remote', '*' ], [ 'remote2', '*' ], 'null' );
+ redir_test([ 'remote', '*' ], [ 'remote2', origin ], 'disallow');
+ redir_test([ 'remote', '*' ], [ 'remote2', 'null' ], 'null' );
+ redir_test([ 'remote', '*' ], [ 'remote2', 'none' ], 'disallow');
+
+ redir_test([ 'remote', origin ], [ 'remote2', '*' ], 'null' );
+ redir_test([ 'remote', origin ], [ 'remote2', origin ], 'disallow');
+ redir_test([ 'remote', origin ], [ 'remote2', 'null' ], 'null');
+ redir_test([ 'remote', origin ], [ 'remote2', 'none' ], 'disallow');
+
+ redir_test([ 'remote', 'null' ], [ 'remote2', '*' ], 'disallow');
+ redir_test([ 'remote', 'none' ], [ 'remote2', '*' ], 'disallow');
+
+
+ // Bonus weird edge checks
+
+ redir_test([ 'remote', '*' ], [ 'remote', remote_origin ], 'disallow');
+ redir_test([ 'remote', '*' ], [ 'remote2', remote_origin ], 'disallow');
+ redir_test([ 'remote', remote_origin ], [ 'remote', "*" ], 'disallow');
+
+
+
+ /*
+ * The helpers
+ */
+
+ function redir_test(first, second, expect_origin) {
+ var first_url, second_url,
+ urls = { "remote": remote, "local": local, "remote2": remote2 };
+
+ first_url = urls[first[0]] + "?origin=" + first[1];
+ second_url = urls[second[0]] + "?origin=" + second[1];
+
+ if (expect_origin=="disallow") {
+ shouldFail(first[0]+" ("+first[1]+") to "
+ + second[0]+" ("+second[1]+"), expect to fail", [ first_url, second_url ]);
+ }
+ else {
+ shouldPass(first[0]+" ("+first[1]+") to "
+ + second[0]+" ("+second[1]+"), expect origin="+expect_origin, expect_origin, [ first_url, second_url ]);
+ }
+
+ }
+
+ function shouldPass(desc, expected_origin, urls) {
+ var test_id = num_test,
+ t = async_test(desc);
+
+ num_test++;
+
+ t.step(function() {
+ var final_url,
+ client = new XMLHttpRequest();
+
+ client.open('GET', buildURL(urls, test_id));
+
+ client.onreadystatechange = t.step_func(function() {
+ if (client.readyState != client.DONE)
+ return;
+ assert_true(!!client.response, "Got response");
+ r = JSON.parse(client.response)
+ assert_equals(r['origin'], expected_origin, 'Origin Header')
+ assert_equals(r['get_value'], 'last', 'get_value')
+ t.done();
+ });
+ client.send(null)
+ });
+ }
+
+ function shouldFail(desc, urls) {
+ var test_id = num_test,
+ t = async_test(desc);
+
+ num_test++;
+
+ t.step(function() {
+ var client = new XMLHttpRequest();
+
+ client.open('GET', buildURL(urls, test_id));
+
+ client.onreadystatechange = t.step_func(function() {
+ if (client.readyState != client.DONE)
+ return;
+ assert_false(!!client.response, "Got response");
+ });
+ client.onerror = t.step_func(function(e) {
+ t.done();
+ });
+
+ client.send(null)
+ });
+ }
+
+
+ function buildURL(urls, id) {
+ var tmp_url;
+
+ if (typeof(urls) == "string") {
+ return urls + "&" + id + "_0";
+ }
+
+ for (var i = urls.length; i--; ) {
+ if (!tmp_url)
+ {
+ tmp_url = urls[i] + "&get_value=last&" + id + "_" + i;
+ continue;
+ }
+ tmp_url = urls[i]
+ + "&location="
+ + encodeURIComponent(tmp_url)
+ + "&" + id + "_" + i;
+ }
+
+ return tmp_url;
+ }
+
+</script>
diff --git a/testing/web-platform/tests/cors/redirect-preflight-2.htm b/testing/web-platform/tests/cors/redirect-preflight-2.htm
new file mode 100644
index 0000000000..fe58d90a26
--- /dev/null
+++ b/testing/web-platform/tests/cors/redirect-preflight-2.htm
@@ -0,0 +1,55 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>CORS - preflight after a redirect</title>
+<meta name=author title="Odin Hørthe Omdal" href="mailto:odiho@opera.com">
+
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=support.js?pipe=sub></script>
+<script src=/common/utils.js></script>
+
+<h1>Preflight after redirect</h1>
+
+<div id=log></div>
+<script>
+
+async_test(function() {
+ var test_id = "fail_" + new Date().getTime()
+ var client = new XMLHttpRequest()
+ var last_url = CROSSDOMAIN + 'resources/cors-makeheader.py?origin=*&ident=' + test_id
+
+ client.open('GET', 'resources/cors-makeheader.py?origin=*&location=' + encodeURIComponent(last_url))
+ client.setRequestHeader('custom-header', 'admin')
+ client.onerror = this.step_func(function() {
+ this.done()
+ })
+ client.onload = this.step_func(function(e) { assert_unreached("Request should not succeed!") })
+ client.send()
+}, "Same-origin custom-header request, redirect to cross-origin fails after doing a non-successful preflight")
+
+
+async_test(function() {
+ var client = new XMLHttpRequest()
+ var uuid_token = token();
+ var last_url = CROSSDOMAIN + 'resources/cors-makeheader.py?headers=custom-header&origin=*&token=' + uuid_token;
+
+ client.open('GET', 'resources/cors-makeheader.py?origin=*&location=' + encodeURIComponent(last_url))
+ client.setRequestHeader('custom-header', 'admin')
+ client.onload = this.step_func(function() {
+ // Test that I got custom-header
+
+ /* To check whether we did a preflight */
+ client.open('GET', 'resources/cors-makeheader.py?check&token=' + uuid_token)
+ client.onload = this.step_func(function() {
+ assert_equals(client.response, "1", "did preflight")
+ this.done()
+ })
+ client.onerror = this.step_func(function(e) { assert_unreached("Error on getting preflight data") })
+ client.send()
+ })
+ client.onerror = this.step_func(function(e) { assert_unreached("Error during request", e) })
+ client.send()
+}, "Same-origin custom-header request, redirect to cross-origin succeeds after doing a preflight")
+
+
+</script>
diff --git a/testing/web-platform/tests/cors/redirect-preflight.htm b/testing/web-platform/tests/cors/redirect-preflight.htm
new file mode 100644
index 0000000000..ff64284e90
--- /dev/null
+++ b/testing/web-platform/tests/cors/redirect-preflight.htm
@@ -0,0 +1,47 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>CORS - redirect with preflight</title>
+<meta name=author title="Odin Hørthe Omdal" href="mailto:odiho@opera.com">
+<meta name=author title="Fernando Jiménez Moreno" href="mailto:ferjmoreno@gmail.com">
+
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=support.js?pipe=sub></script>
+
+<h1>Redirect with preflight</h1>
+
+<div id=log></div>
+<script>
+
+// Request count for cache busting and easy identifying of request in traffic
+// analyzer.
+var req_c = 0;
+
+var CROSSDOMAIN_URL = CROSSDOMAIN + 'resources/cors-makeheader.py?';
+
+/*
+ * Redirection after successfull (200) preflight.
+ */
+function redir_after_successfull_preflight(code) {
+ var desc = 'Should allow redirect ' + code + ' after succesful (200) preflight';
+ async_test(desc).step(function() {
+ var client = new XMLHttpRequest();
+ var redirect = encodeURIComponent(
+ CROSSDOMAIN + 'resources/cors-makeheader.py?headers=x-test&' + req_c++
+ );
+
+ client.open('GET', CROSSDOMAIN + 'resources/cors-makeheader.py?'
+ + 'preflight=200&headers=x-test&location='
+ + redirect + '&code=' + code + '&' + req_c++,
+ true /* async */);
+ client.setRequestHeader('x-test', 'test');
+ client.onreadystatechange = this.step_func(function() {
+ assert_equals(client.status, 200, 'Successfull redirect');
+ this.done();
+ });
+ client.send(null);
+ });
+}
+[301, 302, 303, 307, 308].forEach(redir_after_successfull_preflight);
+
+</script>
diff --git a/testing/web-platform/tests/cors/redirect-userinfo.htm b/testing/web-platform/tests/cors/redirect-userinfo.htm
new file mode 100644
index 0000000000..fd3864d249
--- /dev/null
+++ b/testing/web-platform/tests/cors/redirect-userinfo.htm
@@ -0,0 +1,99 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>CORS - redirect with userinfo</title>
+<meta name=author title="Odin Hørthe Omdal" href="mailto:odinho@opera.com">
+
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=support.js?pipe=sub></script>
+
+<h1>CORS userinfo redirect handling</h1>
+
+<div id=log></div>
+
+<script>
+
+ // Test count for cache busting and easy identifying of request in traffic analyzer
+ var num_test = 0
+
+ shouldFail("Disallow redirect with userinfo (user:pass@)", [
+ CROSSDOMAIN + "resources/cors-makeheader.py?",
+ CROSSDOMAIN.replace("http://", "http://test:test@") + "resources/cors-makeheader.py?"]);
+
+ shouldFail("Disallow redirect with userinfo (user:@)", [
+ CROSSDOMAIN + "resources/cors-makeheader.py?",
+ CROSSDOMAIN.replace("http://", "http://user:@") + "resources/cors-makeheader.py?"]);
+
+ shouldFail("Disallow redirect with userinfo (user@)", [
+ CROSSDOMAIN + "resources/cors-makeheader.py?",
+ CROSSDOMAIN.replace("http://", "http://user:@") + "resources/cors-makeheader.py?"]);
+
+ shouldPass("Allow redirect without userinfo (:@ is trimmed during URL parsing)", [
+ CROSSDOMAIN + "resources/cors-makeheader.py?",
+ CROSSDOMAIN.replace("http://", "http://:@") + "resources/cors-makeheader.py?"]);
+
+ shouldFail("Disallow redirect with userinfo (:pass@)", [
+ CROSSDOMAIN + "resources/cors-makeheader.py?",
+ CROSSDOMAIN.replace("http://", "http://:pass@") + "resources/cors-makeheader.py?"]);
+
+ shouldPass("Allow redirect without userinfo (@ is trimmed during URL parsing)", [
+ CROSSDOMAIN + "resources/cors-makeheader.py?",
+ CROSSDOMAIN.replace("http://", "http://@") + "resources/cors-makeheader.py?"]);
+
+ function shouldFail(desc, urls) {
+ var test_id = num_test,
+ t = async_test(desc);
+
+ num_test++;
+
+ t.step(function() {
+ var client = new XMLHttpRequest();
+
+ client.open('GET', buildURL(urls, test_id));
+
+ client.onload = t.unreached_func();
+ client.onerror = t.step_func_done();
+
+ client.send(null)
+ });
+ }
+
+ function shouldPass(desc, urls) {
+ var test_id = num_test,
+ t = async_test(desc);
+
+ num_test++;
+
+ t.step(function() {
+ var client = new XMLHttpRequest();
+
+ client.open('GET', buildURL(urls, test_id));
+
+ client.onload = t.step_func_done(function() {
+ r = JSON.parse(client.response)
+ assert_equals(r['get_value'], 'last', 'get_value')
+ });
+ client.onerror = t.unreached_func()
+ client.send(null)
+ });
+ }
+
+ function buildURL(urls, id) {
+ var tmp_url;
+
+ for (var i = urls.length; i--; ) {
+ if (!tmp_url)
+ {
+ tmp_url = urls[i] + "&get_value=last&" + id + "_" + i;
+ continue;
+ }
+ tmp_url = urls[i]
+ + "&location="
+ + encodeURIComponent(tmp_url)
+ + "&" + id + "_" + i;
+ }
+
+ return tmp_url;
+ }
+
+</script>
diff --git a/testing/web-platform/tests/cors/remote-origin.htm b/testing/web-platform/tests/cors/remote-origin.htm
new file mode 100644
index 0000000000..0726775169
--- /dev/null
+++ b/testing/web-platform/tests/cors/remote-origin.htm
@@ -0,0 +1,121 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>Access-Control-Allow-Origin handling</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=support.js?pipe=sub></script>
+
+<h1>Access-Control-Allow-Origin handling</h1>
+
+<div id=log></div>
+
+<script>
+
+var remote_tests = [];
+var iframe = document.createElement("iframe")
+iframe.src = CROSSDOMAIN + 'resources/remote-xhrer.html';
+document.body.appendChild(iframe);
+
+function reverseOrigin(expect_pass, origin)
+{
+ var real_origin = origin.replace("<host>", REMOTE_HOST)
+ .replace("<remote_origin>", location.protocol + "//" + location.host)
+ .replace("<origin>", REMOTE_ORIGIN)
+ .replace("<protocol>", REMOTE_PROTOCOL)
+ .replace("<HOST>", REMOTE_HOST.toUpperCase())
+ .replace("<ORIGIN>", REMOTE_ORIGIN.toUpperCase())
+ .replace("<PROTOCOL>", REMOTE_PROTOCOL.toUpperCase());
+
+ var t = async_test((expect_pass ? 'Allow origin: ' : 'Disallow origin: ') + real_origin
+ .replace(/\0/g, "\\0")
+ .replace(/\t/g, "[tab]")
+ .replace(/ /g, '_'));
+ t.step(function() {
+ this.test_url = dirname(location.href)
+ + 'resources/cors-makeheader.py?origin='
+ + encodeURIComponent(real_origin);
+ iframe.contentWindow.postMessage({ url: this.test_url, origin: origin }, "*");
+ });
+
+ if (expect_pass)
+ {
+ t.callback = t.step_func(function(e) {
+ assert_equals(e.state, "load");
+ r = JSON.parse(e.response)
+ assert_equals(r['origin'], REMOTE_ORIGIN, 'Request Origin: should be ' + REMOTE_ORIGIN)
+ this.done();
+ });
+ }
+ else
+ {
+ t.callback = t.step_func(function(e) {
+ assert_equals(e.state, "error");
+ assert_equals(e.response, "");
+ this.done();
+ });
+ }
+
+ remote_tests[origin] = t;
+}
+
+function shouldPass(origin) { reverseOrigin(true, origin); }
+function shouldFail(origin) { reverseOrigin(false, origin); }
+
+
+iframe.onload = function() {
+ shouldPass('*');
+ shouldPass(' * ');
+ shouldPass(' *');
+ shouldPass("<origin>");
+ shouldPass(" <origin>");
+ shouldPass(" <origin> ");
+ shouldPass(" <origin>");
+
+ shouldFail("<remote_origin>")
+ shouldFail("//" + "<host>")
+ shouldFail("://" + "<host>")
+ shouldFail("ftp://" + "<host>")
+ shouldFail("http:://" + "<host>")
+ shouldFail("http:/" + "<host>")
+ shouldFail("http:" + "<host>")
+ shouldFail("<host>")
+ shouldFail("<origin>" + "?")
+ shouldFail("<origin>" + "/")
+ shouldFail("<origin>" + " /")
+ shouldFail("<origin>" + "#")
+ shouldFail("<origin>" + "%23")
+ shouldFail("<origin>" + ":80")
+ shouldFail("<origin>" + ", *")
+ shouldFail("<origin>" + "\0")
+ shouldFail(("<ORIGIN>"))
+ shouldFail("<PROTOCOL>//<host>")
+ shouldFail("<protocol>//<HOST>")
+ shouldFail("-")
+ shouldFail("**")
+ shouldFail("\0*")
+ shouldFail("*\0")
+ shouldFail("'*'")
+ shouldFail('"*"')
+ shouldFail("* *")
+ shouldFail("*" + "<protocol>" + "//" + "*")
+ shouldFail("*" + "<origin>")
+ shouldFail("* " + "<origin>")
+ shouldFail("*, " + "<origin>")
+ shouldFail("\0" + "<origin>")
+ shouldFail("null " + "<origin>")
+ shouldFail('http://example.net')
+ shouldFail('null')
+ shouldFail('')
+ shouldFail(location.href)
+ shouldFail(dirname(location.href))
+ shouldFail(CROSSDOMAIN)
+}
+
+window.addEventListener("message", function(e) {
+ remote_tests[e.data.origin].callback(e.data);
+});
+
+add_completion_callback(function() {
+ iframe.parentElement.removeChild(iframe);
+});
+</script>
diff --git a/testing/web-platform/tests/cors/request-headers.htm b/testing/web-platform/tests/cors/request-headers.htm
new file mode 100644
index 0000000000..7634eca9fc
--- /dev/null
+++ b/testing/web-platform/tests/cors/request-headers.htm
@@ -0,0 +1,80 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>CORS - request headers - Access-Control-Allow-Headers</title>
+<meta name=author title="Odin Hørthe Omdal" href="mailto:odiho@opera.com">
+
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=support.js?pipe=sub></script>
+
+<h1>Request headers</h1>
+<div id=log></div>
+<script>
+
+/*
+ * Request Headers
+ */
+
+test(function() {
+ var client = new XMLHttpRequest()
+ client.open('GET', CROSSDOMAIN + 'resources/cors-makeheader.py?headers=x-print', false)
+ client.setRequestHeader('x-print', 'unicorn')
+ client.send(null)
+
+ res = JSON.parse(client.response)
+ assert_equals(res['x-print'], 'unicorn')
+}, 'basic request header')
+
+test(function() {
+ var client = new XMLHttpRequest()
+ client.open('GET', CROSSDOMAIN + 'resources/cors-makeheader.py?headers=x-print,', false)
+ client.setRequestHeader('x-print', 'unicorn')
+ client.setRequestHeader('content-type', 'text/plain')
+ client.setRequestHeader('accept', 'test')
+ client.setRequestHeader('accept-language', 'nn')
+ client.setRequestHeader('content-language', 'nn')
+ client.send(null)
+
+ res = JSON.parse(client.response)
+ assert_equals(res['x-print'], 'unicorn')
+ assert_equals(res['content-type'], 'text/plain')
+ assert_equals(res['accept'], 'test')
+ assert_equals(res['accept-language'], 'nn')
+ assert_equals(res['content-language'], 'nn')
+}, 'Simple request headers need not be in allow-headers')
+
+test(function() {
+ var client = new XMLHttpRequest()
+ client.open('GET', CROSSDOMAIN + 'resources/cors-makeheader.py?headers=x-print', false)
+ client.setRequestHeader('x-print', 'unicorn')
+ client.setRequestHeader('y-print', 'unicorn')
+ assert_throws_dom("NetworkError", function() { client.send(null) })
+}, 'Unspecified request headers are disallowed')
+
+test(function() {
+ var client = new XMLHttpRequest()
+ client.open('GET', CROSSDOMAIN + 'resources/cors-makeheader.py?headers=,y-lol,x-PriNT,%20,,,Y-PRINT', false)
+ client.setRequestHeader('x-print', 'unicorn')
+ client.setRequestHeader('y-print', 'narwhal')
+ client.send(null)
+
+ res = JSON.parse(client.response)
+ assert_equals(res['x-print'], 'unicorn')
+ assert_equals(res['y-print'], 'narwhal')
+}, 'Strange allowheaders (case insensitive)')
+
+test(function() {
+ var client = new XMLHttpRequest()
+ assert_throws_dom('INVALID_STATE_ERR', function() { client.setRequestHeader('x-print', 'unicorn') })
+},
+'INVALID_STATE_ERR on setRequestHeader before open()')
+
+test(function() {
+ var client = new XMLHttpRequest()
+ client.open('GET', CROSSDOMAIN + 'resources/cors-makeheader.py?headers=,y-lol,x-PriNT,%20,,,Y-PRINT', false)
+ client.send()
+ assert_throws_dom('INVALID_STATE_ERR', function() { client.setRequestHeader('x-print', 'unicorn') })
+},
+'INVALID_STATE_ERR on setRequestHeader after send()')
+
+</script>
diff --git a/testing/web-platform/tests/cors/resources/304.py b/testing/web-platform/tests/cors/resources/304.py
new file mode 100644
index 0000000000..aa5f5ab560
--- /dev/null
+++ b/testing/web-platform/tests/cors/resources/304.py
@@ -0,0 +1,60 @@
+# A header used to correlate requests and responses
+state_header = b"content-language"
+
+# Static ETag to use (and expect)
+etag = b"abcdef"
+
+def error(msg):
+ return (299, u"Client Error"), [
+ (b'content-type', b'text/plain'),
+ (b'access-control-allow-origin', b"*"),
+ (b'access-control-expose-headers', state_header),
+ (b'cache-control', b'no-store')
+ ], msg
+
+def main(request, response):
+ headers = []
+
+ inm = request.headers.get(b'if-none-match', None)
+ raw_req_num = request.headers.get(state_header, None)
+ if raw_req_num == None:
+ return error(u"no req_num header in request")
+ else:
+ req_num = int(raw_req_num)
+ if req_num > 8:
+ return error(u"req_num %s out of range" % req_num)
+
+ headers.append((b"Access-Control-Expose-Headers", state_header))
+ headers.append((state_header, req_num))
+ headers.append((b"A", req_num))
+ headers.append((b"B", req_num))
+
+ if req_num % 2: # odd requests are the first in a test pair
+ if inm:
+ # what are you doing here? This should be a fresh request.
+ return error(u"If-None-Match on first request")
+ else:
+ status = 200, b"OK"
+ headers.append((b"Access-Control-Allow-Origin", b"*"))
+ headers.append((b"Content-Type", b"text/plain"))
+ headers.append((b"Cache-Control", b"private, max-age=3, must-revalidate"))
+ headers.append((b"ETag", etag))
+ return status, headers, b"Success"
+ else: # even requests are the second in a pair, and should have a good INM.
+ if inm != etag:
+ # Bad browser.
+ if inm == None:
+ return error(u"If-None-Match missing")
+ else:
+ return error(u"If-None-Match '%s' mismatches")
+ else:
+ if req_num == 2:
+ pass # basic, vanilla check
+ elif req_num == 4:
+ headers.append((b"Access-Control-Expose-Headers", b"a, b"))
+ elif req_num == 6:
+ headers.append((b"Access-Control-Expose-Headers", b"a"))
+ elif req_num == 8:
+ headers.append((b"Access-Control-Allow-Origin", b"other.origin.example:80"))
+ status = 304, b"Not Modified"
+ return status, headers, b""
diff --git a/testing/web-platform/tests/cors/resources/access-control-expose-headers.json b/testing/web-platform/tests/cors/resources/access-control-expose-headers.json
new file mode 100644
index 0000000000..e8915f7ffe
--- /dev/null
+++ b/testing/web-platform/tests/cors/resources/access-control-expose-headers.json
@@ -0,0 +1,62 @@
+[
+ {
+ "input": "access-control-expose-headers: BB-8",
+ "exposed": true
+ },
+ {
+ "input": "Access-Control-Expose-Headers: bb-8,,@#$#%%&^&^*()()11!",
+ "exposed": false
+ },
+ {
+ "input": "Access-Control-Expose-Headers: bb-8, no no",
+ "exposed": false
+ },
+ {
+ "input": "Access-Control-Expose-Headers: @#$#%%&^&^*()()11!,bb-8",
+ "exposed": false
+ },
+ {
+ "input": "Access-Control-Expose-Headers: bb-8\r\nAccess-Control-Expose-Headers: no",
+ "exposed": true
+ },
+ {
+ "input": "Access-Control-Expose-Headers: bb-8\r\nAccess-Control-Expose-Headers: no no",
+ "exposed": false
+ },
+ {
+ "input": "Access-Control-Expose-Headers: no\r\nAccess-Control-Expose-Headers: bb-8",
+ "exposed": true
+ },
+ {
+ "input": "Access-Control-Expose-Headers:\r\nAccess-Control-Expose-Headers: bb-8",
+ "exposed": true
+ },
+ {
+ "input": "Access-Control-Expose-Headers: ,bb-8",
+ "exposed": true
+ },
+ {
+ "input": "Access-Control-Expose-Headers: bb-8\u000C",
+ "exposed": false
+ },
+ {
+ "input": "Access-Control-Expose-Headers: bb-8\u000B",
+ "exposed": false
+ },
+ {
+ "input": "Access-Control-Expose-Headers: bb-8\u000B,bb-8",
+ "exposed": false
+ },
+ {
+ "input": "Access-Control-Expose-Headers: 'bb-8'",
+ "exposed": false
+ },
+ {
+ "input": "Access-Control-Expose-Headers: 'bb-8',bb-8",
+ "exposed": true
+ },
+ {
+ "input": "Access-Control-Expose-Headers: \"bb-8\",bb-8",
+ "exposed": false
+ }
+]
diff --git a/testing/web-platform/tests/cors/resources/cache-304.py b/testing/web-platform/tests/cors/resources/cache-304.py
new file mode 100644
index 0000000000..0626091794
--- /dev/null
+++ b/testing/web-platform/tests/cors/resources/cache-304.py
@@ -0,0 +1,10 @@
+def main(request, response):
+ match = request.headers.get(b"If-None-Match", None)
+ if match is not None and match == b"mybestscript-v1":
+ response.status = (304, b"YEP")
+ return b""
+ response.headers.set(b"Access-Control-Allow-Origin", b"*")
+ response.headers.set(b"Cache-Control", b"must-revalidate")
+ response.headers.set(b"ETag", b"mybestscript-v1")
+ response.headers.set(b"Content-Type", b"text/javascript")
+ return b"function hep() { }"
diff --git a/testing/web-platform/tests/cors/resources/checkandremove.py b/testing/web-platform/tests/cors/resources/checkandremove.py
new file mode 100644
index 0000000000..5cd7868199
--- /dev/null
+++ b/testing/web-platform/tests/cors/resources/checkandremove.py
@@ -0,0 +1,6 @@
+def main(request, response):
+ token = request.GET.first(b"token")
+ if request.server.stash.remove(token) is not None:
+ return u"1"
+ else:
+ return u"0"
diff --git a/testing/web-platform/tests/cors/resources/cors-cookie.py b/testing/web-platform/tests/cors/resources/cors-cookie.py
new file mode 100644
index 0000000000..ee42494ad0
--- /dev/null
+++ b/testing/web-platform/tests/cors/resources/cors-cookie.py
@@ -0,0 +1,21 @@
+
+def main(request, response):
+ origin = request.GET.first(b"origin", request.headers[b"origin"])
+ credentials = request.GET.first(b"credentials", b"true")
+
+ headers = [(b"Content-Type", b"text/plain")]
+ if origin != b'none':
+ headers.append((b"Access-Control-Allow-Origin", origin))
+ if credentials != b'none':
+ headers.append((b"Access-Control-Allow-Credentials", credentials))
+
+ ident = request.GET.first(b'ident', b'test')
+
+ if ident in request.cookies:
+ body = request.cookies[ident].value
+ response.delete_cookie(ident)
+ else:
+ response.set_cookie(ident, b"COOKIE")
+ body = u"NO_COOKIE"
+
+ return headers, body
diff --git a/testing/web-platform/tests/cors/resources/cors-headers.asis b/testing/web-platform/tests/cors/resources/cors-headers.asis
new file mode 100644
index 0000000000..2e92da28a0
--- /dev/null
+++ b/testing/web-platform/tests/cors/resources/cors-headers.asis
@@ -0,0 +1,25 @@
+HTTP/1.1 200 OK
+Access-Control-Allow-Origin: *
+Access-Control-Expose-Headers: X-Custom-Header, X-Custom-Header-Empty, X-Custom-Header-Comma, X-Custom-Header-Bytes
+Access-Control-Expose-Headers: X-Second-Expose
+Access-Control-Expose-Headers: Date
+Content-Type: text/plain
+Content-Length: 4
+X-Custom-Header: test
+X-Custom-Header: test
+Set-Cookie: test1=t1;max-age=2
+Set-Cookie2: test2=t2;Max-Age=2
+X-Custom-Header-Empty:
+X-Custom-Header-Comma: 1
+X-Custom-Header-Comma: 2
+X-Custom-Header-Bytes: …
+X-Nonexposed: unicorn
+X-Second-Expose: flyingpig
+Cache-Control: no-cache
+Content-Language: nn
+Expires: Thu, 01 Dec 1994 16:00:00 GMT
+Last-Modified: Thu, 01 Dec 1994 10:00:00 GMT
+Pragma: no-cache
+Date: Wed, 22 Oct 2013 10:00:00 GMT
+
+TEST
diff --git a/testing/web-platform/tests/cors/resources/cors-makeheader.py b/testing/web-platform/tests/cors/resources/cors-makeheader.py
new file mode 100644
index 0000000000..e0576a003d
--- /dev/null
+++ b/testing/web-platform/tests/cors/resources/cors-makeheader.py
@@ -0,0 +1,69 @@
+import json
+
+from wptserve.utils import isomorphic_decode
+
+def main(request, response):
+ origin = request.GET.first(b"origin", request.headers.get(b'origin') or b'none')
+
+ if b"check" in request.GET:
+ token = request.GET.first(b"token")
+ value = request.server.stash.take(token)
+ if value is not None:
+ if request.GET.first(b"check", None) == b"keep":
+ request.server.stash.put(token, value)
+ body = u"1"
+ else:
+ body = u"0"
+ return [(b"Content-Type", b"text/plain")], body
+
+
+ if origin != b'none':
+ response.headers.set(b"Access-Control-Allow-Origin", origin)
+ if b'origin2' in request.GET:
+ response.headers.append(b"Access-Control-Allow-Origin", request.GET.first(b'origin2'))
+
+ #Preflight
+ if b'headers' in request.GET:
+ response.headers.set(b"Access-Control-Allow-Headers", request.GET.first(b'headers'))
+ if b'credentials' in request.GET:
+ response.headers.set(b"Access-Control-Allow-Credentials", request.GET.first(b'credentials'))
+ if b'methods' in request.GET:
+ response.headers.set(b"Access-Control-Allow-Methods", request.GET.first(b'methods'))
+
+ code_raw = request.GET.first(b'code', None)
+ if code_raw:
+ code = int(code_raw)
+ else:
+ code = None
+ if request.method == u'OPTIONS':
+ #Override the response code if we're in a preflight and it's asked
+ if b'preflight' in request.GET:
+ code = int(request.GET.first(b'preflight'))
+
+ #Log that the preflight actually happened if we have an ident
+ if b'token' in request.GET:
+ request.server.stash.put(request.GET[b'token'], True)
+
+ if b'location' in request.GET:
+ if code is None:
+ code = 302
+
+ if code >= 300 and code < 400:
+ response.headers.set(b"Location", request.GET.first(b'location'))
+
+ headers = {}
+ for name, values in request.headers.items():
+ if len(values) == 1:
+ headers[isomorphic_decode(name)] = isomorphic_decode(values[0])
+ else:
+ #I have no idea, really
+ headers[name] = values
+
+ headers[u'get_value'] = isomorphic_decode(request.GET.first(b'get_value', b''))
+
+ body = json.dumps(headers)
+
+ if code:
+ return (code, b"StatusText"), [], body
+ else:
+ return body
diff --git a/testing/web-platform/tests/cors/resources/expose-headers.py b/testing/web-platform/tests/cors/resources/expose-headers.py
new file mode 100644
index 0000000000..4e568f4063
--- /dev/null
+++ b/testing/web-platform/tests/cors/resources/expose-headers.py
@@ -0,0 +1,11 @@
+def main(request, response):
+ response.add_required_headers = False
+ output = b"HTTP/1.1 221 ALL YOUR BASE BELONG TO H1\r\n"
+ output += b"Access-Control-Allow-Origin: *\r\n"
+ output += b"Connection: close\r\n"
+ output += b"BB-8: hey\r\n"
+ output += b"Content-Language: mkay\r\n"
+ output += request.GET.first(b"expose") + b"\r\n"
+ output += b"\r\n"
+ response.writer.write(output)
+ response.close_connection = True
diff --git a/testing/web-platform/tests/cors/resources/image-tainting-checker.sub.html b/testing/web-platform/tests/cors/resources/image-tainting-checker.sub.html
new file mode 100644
index 0000000000..59de9e7a23
--- /dev/null
+++ b/testing/web-platform/tests/cors/resources/image-tainting-checker.sub.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<body>
+<canvas id="canvas"></canvas>
+<script>
+// Used by image-tainting-in-cross-origin-iframe.sub.html to check that an
+// image resource loaded by the top level frame that is same-origin to the
+// frame isn't treated as a same-origin resource in a cross-origin iframe.
+const canvas = document.getElementById('canvas');
+const ctx = canvas.getContext('2d');
+const img = new Image();
+img.src = 'http://{{host}}:{{ports[http][0]}}/images/blue-png-cachable.py';
+img.onload = () => {
+ ctx.drawImage(img, 0, 0);
+ try {
+ ctx.getImageData(0, 0, 1, 1);
+ parent.postMessage('FAIL: getImageData() didn\'t throw', '*');
+ } catch (e) {
+ parent.postMessage('DONE', '*');
+ }
+};
+</script>
+</body>
diff --git a/testing/web-platform/tests/cors/resources/preflight-cache-partitioning-iframe.sub.html b/testing/web-platform/tests/cors/resources/preflight-cache-partitioning-iframe.sub.html
new file mode 100644
index 0000000000..031cc94d10
--- /dev/null
+++ b/testing/web-platform/tests/cors/resources/preflight-cache-partitioning-iframe.sub.html
@@ -0,0 +1,27 @@
+<script>
+window.onmessage = async (e) => {
+ if (e.data.type === "run") {
+ let token = e.data.token;
+ const test_url =
+ `http://{{hosts[alt][]}}:{{ports[http][0]}}/cors/resources/preflight-partitioning.py?token=${token}`;
+
+ let response = await fetch(
+ new Request(test_url, {
+ mode: "cors",
+ method: "GET",
+ headers: [["x-print", token]],
+ })
+ );
+
+ let result = await response.text();
+
+ if (result === "1") {
+ parent.postMessage({ type: "pass", msg: "The CORS preflight was sent" }, "*");
+ } else {
+ parent.postMessage({ type: "fail", msg: "The CORS preflight wasn't sent" }, "*");
+ }
+ }
+};
+
+parent.postMessage({ type: "loaded" }, "*");
+</script>
diff --git a/testing/web-platform/tests/cors/resources/preflight-cache-partitioning.sub.html b/testing/web-platform/tests/cors/resources/preflight-cache-partitioning.sub.html
new file mode 100644
index 0000000000..e4bbdec013
--- /dev/null
+++ b/testing/web-platform/tests/cors/resources/preflight-cache-partitioning.sub.html
@@ -0,0 +1,20 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>Helper page for testing preflight cache partitioning</title>
+<iframe id="iframe" src="http://{{host}}:{{ports[http][0]}}/cors/resources/preflight-cache-partitioning-iframe.sub.html"></iframe>
+<script>
+window.onmessage = (e) => {
+ switch (e.data.type || "") {
+ case "pass":
+ case "fail":
+ case "loaded":
+ opener.postMessage(e.data, "*");
+ break;
+ default:
+ let iframe = document.getElementById("iframe");
+ iframe.contentWindow.postMessage(e.data, "*");
+ break;
+ }
+};
+
+</script>
diff --git a/testing/web-platform/tests/cors/resources/preflight-partitioning.py b/testing/web-platform/tests/cors/resources/preflight-partitioning.py
new file mode 100644
index 0000000000..d0d9a4ebb6
--- /dev/null
+++ b/testing/web-platform/tests/cors/resources/preflight-partitioning.py
@@ -0,0 +1,35 @@
+def main(request, response):
+ headers = [(b"Content-Type", b"text/plain")]
+ headers.append((b"Access-Control-Allow-Origin", b"*"))
+
+ if request.method == u"GET":
+ token = request.GET.first(b"token")
+ value = request.server.stash.take(token)
+ if value == None:
+ body = u"0"
+ else:
+ if request.GET.first(b"check", None) == b"keep":
+ request.server.stash.put(token, value)
+ body = u"1"
+
+ return headers, body
+
+ if request.method == u"OPTIONS":
+ if not b"Access-Control-Request-Method" in request.headers:
+ response.set_error(400, u"No Access-Control-Request-Method header")
+ return u"ERROR: No access-control-request-method in preflight!"
+
+ headers.append((b"Access-Control-Allow-Methods",
+ request.headers[b'Access-Control-Request-Method']))
+
+ if b"max_age" in request.GET:
+ headers.append((b"Access-Control-Max-Age", request.GET[b'max_age']))
+
+ if b"token" in request.GET:
+ request.server.stash.put(request.GET.first(b"token"), 1)
+
+ headers.append((b"Access-Control-Allow-Headers", b"x-print"))
+
+ body = request.headers.get(b"x-print", b"NO")
+
+ return headers, body
diff --git a/testing/web-platform/tests/cors/resources/preflight.py b/testing/web-platform/tests/cors/resources/preflight.py
new file mode 100644
index 0000000000..d8bce26d28
--- /dev/null
+++ b/testing/web-platform/tests/cors/resources/preflight.py
@@ -0,0 +1,35 @@
+def main(request, response):
+ headers = [(b"Content-Type", b"text/plain")]
+
+ if b"check" in request.GET:
+ token = request.GET.first(b"token")
+ value = request.server.stash.take(token)
+ if value == None:
+ body = u"0"
+ else:
+ if request.GET.first(b"check", None) == b"keep":
+ request.server.stash.put(token, value)
+ body = u"1"
+
+ return headers, body
+
+ if request.method == u"OPTIONS":
+ if not b"Access-Control-Request-Method" in request.headers:
+ response.set_error(400, u"No Access-Control-Request-Method header")
+ return u"ERROR: No access-control-request-method in preflight!"
+
+ headers.append((b"Access-Control-Allow-Methods",
+ request.headers[b'Access-Control-Request-Method']))
+
+ if b"max_age" in request.GET:
+ headers.append((b"Access-Control-Max-Age", request.GET[b'max_age']))
+
+ if b"token" in request.GET:
+ request.server.stash.put(request.GET.first(b"token"), 1)
+
+ headers.append((b"Access-Control-Allow-Origin", b"*"))
+ headers.append((b"Access-Control-Allow-Headers", b"x-print"))
+
+ body = request.headers.get(b"x-print", b"NO")
+
+ return headers, body
diff --git a/testing/web-platform/tests/cors/resources/remote-xhrer.html b/testing/web-platform/tests/cors/resources/remote-xhrer.html
new file mode 100644
index 0000000000..73a7cb444f
--- /dev/null
+++ b/testing/web-platform/tests/cors/resources/remote-xhrer.html
@@ -0,0 +1,28 @@
+<!doctype html>
+<title>Child helper</title>
+
+<script>
+window.addEventListener("message", function(e) {
+// e.source.postMessage(e.data, e.origin);
+
+ var client = new XMLHttpRequest();
+ var localurl = e.data.url
+ .replace("<host>", location.host)
+ .replace("<protocol>", location.protocol);
+
+ client.open('GET', localurl, true);
+ client.onload = function() {
+ e.data.state = "load";
+ e.data.response = client.response;
+ e.source.postMessage(e.data, e.origin);
+ }
+ client.onerror = function() {
+ e.data.state = "error";
+ e.data.response = client.response;
+ e.source.postMessage(e.data, e.origin);
+ }
+ client.send();
+});
+</script>
+
+The remote window
diff --git a/testing/web-platform/tests/cors/resources/status.py b/testing/web-platform/tests/cors/resources/status.py
new file mode 100644
index 0000000000..5d37e7a038
--- /dev/null
+++ b/testing/web-platform/tests/cors/resources/status.py
@@ -0,0 +1,39 @@
+from wptserve.utils import isomorphic_encode
+
+def main(request, response):
+ response.headers.set(b"Access-Control-Allow-Origin", request.headers.get(b"origin"))
+ response.headers.set(b"Access-Control-Expose-Headers", b"X-Request-Method")
+
+ if request.method == u'OPTIONS':
+ response.headers.set(b"Access-Control-Allow-Methods", b"GET, CHICKEN, HEAD, POST, PUT")
+
+ if b'headers' in request.GET:
+ response.headers.set(b"Access-Control-Allow-Headers", request.GET.first(b'headers'))
+
+ response.headers.set(b"X-Request-Method", isomorphic_encode(request.method))
+
+ response.headers.set(b"X-A-C-Request-Method", request.headers.get(b"Access-Control-Request-Method", b""))
+
+
+ #This should reasonably work for most response codes.
+ try:
+ code = int(request.GET.first(b"code", 200))
+ except ValueError:
+ code = 200
+
+ text = request.GET.first(b"text", b"OMG")
+
+ if request.method == u"OPTIONS" and b"preflight" in request.GET:
+ try:
+ code = int(request.GET.first(b'preflight'))
+ except KeyError:
+ pass
+
+ status = code, text
+
+ if b"type" in request.GET:
+ response.headers.set(b"Content-Type", request.GET.first(b'type'))
+
+ body = request.GET.first(b'content', b"")
+
+ return status, [], body
diff --git a/testing/web-platform/tests/cors/response-headers.htm b/testing/web-platform/tests/cors/response-headers.htm
new file mode 100644
index 0000000000..cc06a239a6
--- /dev/null
+++ b/testing/web-platform/tests/cors/response-headers.htm
@@ -0,0 +1,105 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>CORS - Response headers</title>
+<meta name=author title="Odin Hørthe Omdal" href="mailto:odiho@opera.com">
+
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=support.js?pipe=sub></script>
+
+<h1>Response headers</h1>
+<div id=log></div>
+<script>
+
+/*
+ * Response Headers
+ */
+
+function check_response_header(head, value, desc) {
+ test(function() {
+ var client = new XMLHttpRequest()
+ client.open('GET', CROSSDOMAIN + 'resources/cors-headers.asis', false)
+ client.send(null)
+
+ if (typeof value === 'function')
+ value(client, head)
+ else
+ assert_equals(client.getResponseHeader(head), value, head)
+ },
+ desc)
+}
+check_response_header('X-Custom-Header-Comma', '1, 2', 'getResponseHeader: Expose Access-Control-Expose-Headers (x-custom-header-comma)')
+check_response_header('X-Second-Expose', 'flyingpig', 'getResponseHeader: Expose second Access-Control-Expose-Headers (x-second-expose)')
+check_response_header(' x-custom-header', null, 'getResponseHeader: Don\'t trim whitespace')
+check_response_header('x-custom-header-bytes', "\xE2\x80\xA6", 'getResponseHeader: x-custom-header bytes')
+check_response_header('Date',
+ function(client, head) { assert_true(client.getResponseHeader(head).length > 2) },
+ 'getResponseHeader: Exposed server field readable (Date)')
+
+function default_readable(head, value) {
+ check_response_header(head, value, 'getResponseHeader: '+head+': readable by default')
+}
+default_readable("Cache-Control", "no-cache");
+default_readable("Content-Language", "nn");
+default_readable("Expires", "Thu, 01 Dec 1994 16:00:00 GMT");
+default_readable("Last-Modified", "Thu, 01 Dec 1994 10:00:00 GMT");
+default_readable("Pragma", "no-cache");
+default_readable("Content-Length", "4");
+default_readable("Content-Type", "text/plain");
+
+
+function default_unreadable(head) {
+ check_response_header(head, null, 'getResponseHeader: '+head+': unreadable by default')
+}
+default_unreadable("Server")
+default_unreadable("X-Powered-By")
+
+
+async_test("getResponseHeader: Combined testing of cors response headers")
+.step(function()
+{
+ var client = new XMLHttpRequest();
+ client.open("GET", CROSSDOMAIN + 'resources/cors-headers.asis')
+ window.c=client;
+ client.onreadystatechange = this.step_func(function()
+ {
+ if (client.readyState == 1)
+ {
+ assert_equals(client.getResponseHeader("x-custom-header"), null, 'x-custom-header')
+ }
+ if (client.readyState > 1)
+ {
+ assert_equals(client.getResponseHeader("x-custom-header"), "test, test", 'x-custom-header')
+ assert_equals(client.getResponseHeader("x-custom-header-empty"), "", 'x-custom-header-empty')
+ assert_equals(client.getResponseHeader("set-cookie"), null)
+ assert_equals(client.getResponseHeader("set-cookie2"), null)
+ assert_equals(client.getResponseHeader("x-non-existent-header"), null)
+ assert_equals(client.getResponseHeader("x-nonexposed"), null)
+ }
+ if (client.readyState == 4)
+ {
+ this.done()
+ }
+ })
+ client.send()
+})
+
+test(function() {
+ var client = new XMLHttpRequest()
+ client.open('GET', CROSSDOMAIN + 'resources/cors-headers.asis', false)
+ client.send(null)
+ assert_equals(client.getResponseHeader("x-custom-header"), "test, test", 'x-custom-header')
+ assert_equals(client.getResponseHeader("x-nonexposed"), null, 'x-nonexposed')
+}, "getResponse: don't expose x-nonexposed")
+
+test(function() {
+ var client = new XMLHttpRequest()
+ client.open('GET', CROSSDOMAIN + 'resources/cors-headers.asis', false)
+ client.send(null)
+
+ h = client.getAllResponseHeaders().toLowerCase()
+ assert_true( h.indexOf('x-custom-header') >= 0, 'x-custom-header present')
+ assert_true( h.indexOf('x-nonexposed') === -1, 'x-nonexposed not present')
+}, "getAllResponseHeaders: don't expose x-nonexposed")
+
+</script>
diff --git a/testing/web-platform/tests/cors/script-304.html b/testing/web-platform/tests/cors/script-304.html
new file mode 100644
index 0000000000..e164ca6f41
--- /dev/null
+++ b/testing/web-platform/tests/cors/script-304.html
@@ -0,0 +1,40 @@
+<!doctype html>
+<html>
+ <head>
+ <meta charset="utf-8">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="/common/get-host-info.sub.js"></script>
+ <script src="/common/utils.js"></script>
+ </head>
+ <body>
+ <div id="testDiv"></div>
+ <script>
+
+const scriptURL = get_host_info().HTTP_REMOTE_ORIGIN + "/cors/resources/cache-304.py?" + token();
+
+function loadScript(test)
+{
+ const script = document.createElement("script");
+ script.crossOrigin = "anonymous";
+ script.src = scriptURL;
+ return new Promise((resolve, reject) => {
+ // Let's add a small timeout so that the script is fully loaded in memory cache before reloading it.
+ script.onload = test.step_timeout(resolve, 50);
+ script.onerror = reject;
+ testDiv.appendChild(script);
+ });
+}
+
+promise_test((test) => {
+ return loadScript(test);
+}, "Load a fresh cross-origin script");
+
+promise_test((test) => {
+ return loadScript(test);
+}, "Reload same cross-origin script from the memory cache after revalidation");
+
+</script>
+ </body>
+</html>
+
diff --git a/testing/web-platform/tests/cors/simple-requests-ch.tentative.htm b/testing/web-platform/tests/cors/simple-requests-ch.tentative.htm
new file mode 100644
index 0000000000..78212de49e
--- /dev/null
+++ b/testing/web-platform/tests/cors/simple-requests-ch.tentative.htm
@@ -0,0 +1,60 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>CORS - simple requests</title>
+<meta name=author title="Odin Hørthe Omdal" href="mailto:odiho@opera.com">
+
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=support.js?pipe=sub></script>
+<script src=/common/utils.js></script>
+
+<h1>Simple requests</h1>
+<p>Simple requests shouldn't trigger preflight</p>
+
+<div id=log></div>
+<script>
+
+var test_c = 0;
+
+function check_simple(method, headers)
+{
+ test(function() {
+ var client = new XMLHttpRequest()
+ var uuid_token = token();
+ client.open(method, CROSSDOMAIN + 'resources/preflight.py?token='
+ + uuid_token, false)
+ for (head in headers)
+ client.setRequestHeader(head, headers[head])
+ client.send("data")
+ assert_equals(client.getResponseHeader('content-type'), "text/plain")
+ if (method == 'HEAD')
+ assert_equals(client.response, '', 'response')
+ else
+ assert_equals(client.response, 'NO', 'response')
+
+ client.open('GET', 'resources/preflight.py?check&token='
+ + uuid_token, false)
+ client.send("data")
+ assert_equals(client.response, "0", "Found preflight log")
+ },
+ 'No preflight ' + method + ' and ' + JSON.stringify(headers))
+}
+
+function check_simple_headers(headers) {
+ check_simple('GET', headers)
+ check_simple('HEAD', headers)
+ check_simple('POST', headers)
+}
+
+check_simple_headers({
+ 'save-data': 'on',
+ 'device-memory': '2.0',
+ 'dpr': '3.0',
+ 'width': '1200',
+ 'viewport-width': '1300',
+ 'rtt': '1',
+ 'downlink': '1.0',
+ 'ect': '2g'
+ })
+
+</script>
diff --git a/testing/web-platform/tests/cors/simple-requests.htm b/testing/web-platform/tests/cors/simple-requests.htm
new file mode 100644
index 0000000000..185e9a75e3
--- /dev/null
+++ b/testing/web-platform/tests/cors/simple-requests.htm
@@ -0,0 +1,90 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>CORS - simple requests</title>
+<meta name=author title="Odin Hørthe Omdal" href="mailto:odiho@opera.com">
+
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=support.js?pipe=sub></script>
+<script src=/common/utils.js></script>
+
+<h1>Simple requests</h1>
+<p>Simple requests shouldn't trigger preflight</p>
+
+<div id=log></div>
+<script>
+
+var test_c = 0;
+
+function check_simple(method, headers)
+{
+ test(function() {
+ var client = new XMLHttpRequest()
+ var uuid_token = token();
+ client.open(method, CROSSDOMAIN + 'resources/preflight.py?token='
+ + uuid_token, false)
+ for (head in headers)
+ client.setRequestHeader(head, headers[head])
+ client.send("data")
+ assert_equals(client.getResponseHeader('content-type'), "text/plain")
+ if (method == 'HEAD')
+ assert_equals(client.response, '', 'response')
+ else
+ assert_equals(client.response, 'NO', 'response')
+
+ client.open('GET', 'resources/preflight.py?check&token='
+ + uuid_token, false)
+ client.send("data")
+ assert_equals(client.response, "0", "Found preflight log")
+ },
+ 'No preflight ' + method + ' and ' + JSON.stringify(headers))
+}
+
+function check_simple_headers(headers) {
+ check_simple('GET', headers)
+ check_simple('HEAD', headers)
+ check_simple('POST', headers)
+}
+
+check_simple_headers({'Accept': 'test'})
+check_simple_headers({'accept-language': 'test'})
+check_simple_headers({'CONTENT-language': 'test'})
+
+check_simple_headers({'Content-Type': 'application/x-www-form-urlencoded'})
+check_simple_headers({'content-type': 'multipart/form-data'})
+check_simple_headers({'content-type': 'text/plain'})
+
+check_simple_headers({
+ 'accept': 'test',
+ 'accept-language': 'test',
+ 'content-language': 'test',
+ 'content-type': 'text/plain; parameter=whatever'
+ })
+
+check_simple('Get', {'content-type': 'text/plain; parameter=extra_bonus'})
+check_simple('post', {'content-type': 'text/plain'})
+
+/* Extra async test */
+
+var simple_async = async_test("Check simple headers (async)")
+simple_async.step(function (){
+ var time = new Date().getTime(),
+ client = new XMLHttpRequest()
+ var uuid_token = token();
+ client.open('POST', CROSSDOMAIN + 'resources/preflight.py?token='
+ + uuid_token, true)
+
+ client.setRequestHeader('Accept', 'jewelry')
+ client.setRequestHeader('accept-language', 'nn-NO,nn,en')
+ client.setRequestHeader('content-type', 'text/plain; parameter=extra')
+ client.setRequestHeader('content-Language', 'nn-NO')
+
+ client.onload = simple_async.step_func(function() {
+ assert_equals(client.getResponseHeader('content-type'), "text/plain", 'content-type response header')
+ assert_equals(client.response, 'NO', 'response')
+ simple_async.done()
+ })
+ client.onerror = simple_async.step_func(function () { assert_unreached('onerror') })
+ client.send()
+})
+</script>
diff --git a/testing/web-platform/tests/cors/status-async.htm b/testing/web-platform/tests/cors/status-async.htm
new file mode 100644
index 0000000000..5bcfa0cf7c
--- /dev/null
+++ b/testing/web-platform/tests/cors/status-async.htm
@@ -0,0 +1,114 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>CORS - status</title>
+<meta name=author title="Odin Hørthe Omdal" href="mailto:odiho@opera.com">
+<meta name=timeout content=long>
+
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=support.js?pipe=sub></script>
+
+<h1>Status returned</h1>
+
+<div id=log></div>
+<script>
+
+function statusRequest(method, code, text, content, type) {
+ async_test("Status on " + method + " " + code)
+ .step(function() {
+ var client = new XMLHttpRequest()
+ client.open(method, CROSSDOMAIN + "resources/status.py?code="
+ + code + "&text=" + text + "&content=" + content + "&type=" + type, true)
+ client.onreadystatechange = this.step_func(function() {
+ if (client.readyState != client.DONE)
+ return
+
+ assert_equals(client.status, code, 'response status')
+ assert_equals(client.statusText, text, 'response status text')
+ assert_equals(client.getResponseHeader("X-Request-Method"), method, 'method')
+ if(method != "HEAD") {
+ if(type == "text/xml") {
+ assert_equals(client.responseXML.documentElement.localName,
+ "x", 'responseXML')
+ }
+ assert_equals(client.response, content, 'response content')
+ }
+ this.done()
+ })
+
+ client.send(null)
+ })
+}
+
+ /* method code text content type */
+ statusRequest("GET", 200, 'OK', 'Not today.', '')
+ statusRequest("GET", 201, 'OK/Created', 'Not today 01.', '')
+ statusRequest("GET", 202, 'OK/Accepted', 'Not today 02.', '')
+ statusRequest("GET", 203, 'OK/Non-Authoritative Information', 'Not today 03.', '')
+ statusRequest("GET", 204, 'OK/No Content', '', '') // specifically no-content
+ statusRequest("GET", 205, 'OK/Reset Content', '', '') // specifically no-content
+ statusRequest("GET", 206, 'OK/Partial Content', 'Not today 06.', '')
+ statusRequest("GET", 209, 'OK', 'Not today 09.', '')
+ statusRequest("GET", 299, 'OK', 'Not today 99.', '')
+ statusRequest("POST", 200, 'OK', '<x>402<\/x>', 'text/xml')
+ statusRequest("HEAD", 200, 'OK', 'Nice!', 'text/doesnotmatter')
+ statusRequest("PUT", 200, 'OK', '400', 'text/plain')
+ statusRequest("CHICKEN", 200, 'OK', 'bah', '')
+
+
+function statusRequestFail(method, code, expect_code, nonsimple) {
+ if (expect_code === undefined)
+ expect_code = code
+
+ async_test("Status on " + method + " " + code + (nonsimple?' (nonsimple)':''))
+ .step(function() {
+ var client = new XMLHttpRequest()
+
+ client.open(method, CROSSDOMAIN + "resources/status.py?code="
+ + code + '&headers=x-nonsimple&text=OHAI', true)
+
+ if (nonsimple)
+ client.setRequestHeader('x-nonsimple', true)
+
+ client.onreadystatechange = this.step_func(function() {
+ if (client.readyState < client.HEADERS_RECEIVED)
+ return
+ assert_equals(client.response, "", "response data")
+ assert_equals(client.status, expect_code, "response status")
+ /* Response code 200 forces webserver to send OK(?) */
+ if(expect_code == 200)
+ assert_equals(client.statusText, "OK", "response statusText")
+ else
+ assert_equals(client.statusText, (expect_code == 0 ? "" : "OHAI"), "response statusText")
+ if (client.readyState == client.DONE)
+ this.done()
+ })
+
+ client.onerror = this.step_func(function(e) {
+ assert_unreached("Got error event.")
+ })
+
+ client.send()
+ })
+}
+
+ /* expect
+ method code status */
+ statusRequestFail("GET", 400)
+ statusRequestFail("HEAD", 401)
+ statusRequestFail("POST", 404)
+ statusRequestFail("POST", 500)
+
+ /* Preflight response status is not 200, so the algorithm set status to 0. */
+ statusRequestFail("PUT", 699, 0)
+ statusRequestFail("CHICKEN", 501, 0)
+
+ /* "forced"
+ preflight */
+ statusRequestFail("GET", 400, 0, true)
+ statusRequestFail("HEAD", 401, 0, true)
+ statusRequestFail("POST", 404, 0, true)
+ statusRequestFail("PUT", 699, 0, true)
+ statusRequestFail("CHICKEN", 501, 0, true)
+
+</script>
diff --git a/testing/web-platform/tests/cors/status-preflight.htm b/testing/web-platform/tests/cors/status-preflight.htm
new file mode 100644
index 0000000000..a72c2fd79f
--- /dev/null
+++ b/testing/web-platform/tests/cors/status-preflight.htm
@@ -0,0 +1,65 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>CORS - status after preflight</title>
+<meta name=author title="Odin Hørthe Omdal" href="mailto:odiho@opera.com">
+
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=support.js?pipe=sub></script>
+
+<h1>Status after preflight</h1>
+
+<div id=log></div>
+<script>
+var counter = 0
+
+function statusAfterPreflight(method, code) {
+ counter++
+
+ async_test(document.title + " on " + method + " " + code).step(function() {
+ var client = new XMLHttpRequest()
+ client.open(method, CROSSDOMAIN + "resources/status.py?" + counter
+ +"&code=" + code + '&headers=x-nonsimple&preflight=200', true)
+
+ client.setRequestHeader('x-nonsimple', true)
+ client.onreadystatechange = this.step_func(function() {
+ if (client.readyState < client.HEADERS_RECEIVED)
+ return
+ assert_equals(client.response, "", "response data")
+ assert_equals(client.status, code, "response status")
+ if (client.readyState == client.DONE) {
+ /* Wait for spurious error events */
+ this.step_timeout(() => { this.done() }, 10)
+ }
+ })
+
+ client.onerror = this.step_func(function() {
+ assert_unreached("Shouldn't throw no error event!")
+ })
+
+ client.send()
+ })
+}
+
+/* method code */
+statusAfterPreflight("GET", 200)
+statusAfterPreflight("GET", 204)
+statusAfterPreflight("GET", 400)
+statusAfterPreflight("GET", 401)
+
+statusAfterPreflight("HEAD", 200)
+statusAfterPreflight("HEAD", 204)
+statusAfterPreflight("HEAD", 400)
+statusAfterPreflight("HEAD", 401)
+statusAfterPreflight("HEAD", 501)
+statusAfterPreflight("HEAD", 699)
+
+statusAfterPreflight("POST", 204)
+statusAfterPreflight("POST", 400)
+statusAfterPreflight("POST", 401)
+statusAfterPreflight("POST", 404)
+
+statusAfterPreflight("PUT", 699)
+statusAfterPreflight("CHICKEN", 501)
+
+</script>
diff --git a/testing/web-platform/tests/cors/status.htm b/testing/web-platform/tests/cors/status.htm
new file mode 100644
index 0000000000..73244288d4
--- /dev/null
+++ b/testing/web-platform/tests/cors/status.htm
@@ -0,0 +1,80 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>CORS status</title>
+<link rel=help href=https://fetch.spec.whatwg.org/>
+<meta name=author title="Odin Hørthe Omdal" href="mailto:odiho@opera.com">
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support.js?pipe=sub"></script>
+
+<h1>The returned status code in different scenarios</h1>
+
+<script>
+
+ var counter = 0
+
+ function testit(allow, preflight, response, status) {
+ async_test(
+ (++counter) + '. ' +
+ (allow ? 'CORS allowed' : 'CORS disallowed') +
+ (preflight ? ', preflight status '+preflight : '') +
+ (response ? ', response status '+response : '') +
+ '.'
+ ).step(function() {
+ var client = new XMLHttpRequest()
+ client.open('GET', CROSSDOMAIN + 'resources/cors-makeheader.py?' + counter +
+ (allow ? '&headers=x-custom': '&origin=none') +
+ (response ? '&code='+response : '') +
+ (preflight ? '&preflight='+preflight : '')
+ )
+
+ if (preflight)
+ client.setRequestHeader('X-Custom', 'preflight')
+
+ client.onload = this.step_func(function() {
+ if (!status)
+ assert_unreached("load event")
+
+ /* Allow spurious error events to fire */
+ this.step_timeout(() => {
+ assert_equals(client.status, status, "status")
+ this.done()
+ }, 10)
+ })
+
+ client.onerror = this.step_func(function() {
+ if (status)
+ assert_unreached("error event")
+
+ assert_equals(client.readyState, client.DONE, 'readyState')
+ assert_equals(client.status, 0, 'status')
+ this.done()
+ })
+
+ client.send()
+
+ })
+ }
+
+ /* allow pref resp status */
+ testit(false, null, 400, 0)
+ testit(false, 200, null, 0)
+ testit(true, null, 400, 400)
+ testit(true, 200, 400, 400)
+ testit(true, 400, null, 0)
+
+</script>
+
+<pre>
+ allowed preflight response | status |
+ ------- --------- -------- | ------ |
+ 1 no x 400 | 0 |
+ 2 no 200 x | 0 |
+ 3 yes x 400 | 400 |
+ 4 yes 200 400 | 400 |
+ 5 yes 400 x | 0 |
+</pre>
+
+<div id=log></div>
+
diff --git a/testing/web-platform/tests/cors/support.js b/testing/web-platform/tests/cors/support.js
new file mode 100644
index 0000000000..2dde95d9a0
--- /dev/null
+++ b/testing/web-platform/tests/cors/support.js
@@ -0,0 +1,17 @@
+function dirname(path) {
+ return path.replace(/\/[^\/]*$/, '/')
+}
+
+/* This subdomain should point to this same location */
+var SUBDOMAIN = 'www1'
+var SUBDOMAIN2 = 'www2'
+var PORT = {{ports[http][1]}}
+//XXX HTTPS
+var PORTS = {{ports[https][0]}}
+
+/* Changes http://example.com/abc/def/cool.htm to http://www1.example.com/abc/def/ */
+var CROSSDOMAIN = dirname(location.href)
+ .replace('://', '://' + SUBDOMAIN + '.')
+var REMOTE_HOST = SUBDOMAIN + '.' + location.host
+var REMOTE_PROTOCOL = location.protocol
+var REMOTE_ORIGIN = REMOTE_PROTOCOL + '//' + REMOTE_HOST