Fix problems with cross-origin redirects.

Three problems exist in the current code:

1) If a same-origin request causes a redirect to a different origin,
   do not enforce access control checks for the redirect response
   itself, because the request which resulted in the redirect was
   same-origin.

2) If a same-origin request causes a redirect to a different origin,
   use the original request's URL as the origin for the new request;
   do not use a unique security origin.

3) Track whether the client (i.e., XMLHttpRequest) actually requested
   that credentials be sent in the first place. When a same-origin
   request redirects to a different origin, the original request will
   send cookies whether requested or not, because it is same-origin.
   The new cross-origin request should not send cookies unless they
   were requested, so that the access control checks on the response
   will succeed if the server granted "Access-Control-Allow-Origin=*".

BUG=226897

Review URL: https://chromiumcodereview.appspot.com/14557011

git-svn-id: svn://svn.chromium.org/blink/trunk@150130 bbb929c8-8fbe-4397-9dbb-9b2b20218538
diff --git a/LayoutTests/http/tests/xmlhttprequest/access-control-and-redirects-async-expected.txt b/LayoutTests/http/tests/xmlhttprequest/access-control-and-redirects-async-expected.txt
index 062da2d..9051404 100644
--- a/LayoutTests/http/tests/xmlhttprequest/access-control-and-redirects-async-expected.txt
+++ b/LayoutTests/http/tests/xmlhttprequest/access-control-and-redirects-async-expected.txt
@@ -1,33 +1,24 @@
 Tests that asynchronous XMLHttpRequests handle redirects according to the CORS standard.
 
-Testing resources/redirect-cors.php?url=http://localhost:8000/xmlhttprequest/resources/access-control-basic-allow-star.cgi
+Testing http://localhost:8000/xmlhttprequest/resources/redirect-cors.php?url=http://localhost:8000/xmlhttprequest/resources/access-control-basic-allow-star.cgi without credentials
 Expecting success: false
 PASS: 0
-Testing resources/redirect-cors.php?url=http://localhost:8000/xmlhttprequest/resources/access-control-basic-allow-star.cgi&  access-control-allow-origin=http://localhost:8000&  access-control-allow-credentials=true
-Expecting success: false
-PASS: 0
-Testing resources/redirect-cors.php?url=http://localhost:8000/xmlhttprequest/resources/access-control-basic-allow.cgi&  access-control-allow-origin=http://localhost:8000&  access-control-allow-credentials=true
-Expecting success: false
-PASS: 0
-Testing http://localhost:8000/xmlhttprequest/resources/redirect-cors.php?url=http://localhost:8000/xmlhttprequest/resources/access-control-basic-allow-star.cgi
-Expecting success: false
-PASS: 0
-Testing http://localhost:8000/xmlhttprequest/resources/redirect-cors.php?url=http://localhost:8000/xmlhttprequest/resources/access-control-basic-allow-star.cgi&  access-control-allow-origin=http://localhost:8000
+Testing http://localhost:8000/xmlhttprequest/resources/redirect-cors.php?url=http://localhost:8000/xmlhttprequest/resources/access-control-basic-allow-star.cgi&  access-control-allow-origin=http://localhost:8000 without credentials
 Expecting success: true
 FAIL: 0
-Testing http://localhost:8000/xmlhttprequest/resources/redirect-cors.php?url=http://username:password@localhost:8000/xmlhttprequest/resources/access-control-basic-allow-star.cgi&  access-control-allow-origin=http://localhost:8000
+Testing http://localhost:8000/xmlhttprequest/resources/redirect-cors.php?url=http://username:password@localhost:8000/xmlhttprequest/resources/access-control-basic-allow-star.cgi&  access-control-allow-origin=http://localhost:8000 without credentials
 Expecting success: false
 PASS: 0
-Testing http://localhost:8000/xmlhttprequest/resources/redirect-cors.php?url=foo://bar.cgi&  access-control-allow-origin=http://localhost:8000
+Testing http://localhost:8000/xmlhttprequest/resources/redirect-cors.php?url=foo://bar.cgi&  access-control-allow-origin=http://localhost:8000 without credentials
 Expecting success: false
 PASS: 0
-Testing http://localhost:8000/xmlhttprequest/resources/redirect-cors.php?redirect-preflight=true&  url=http://localhost:8000/xmlhttprequest/resources/access-control-basic-allow-star.cgi&  access-control-allow-origin=*
+Testing http://localhost:8000/xmlhttprequest/resources/redirect-cors.php?redirect-preflight=true&  url=http://localhost:8000/xmlhttprequest/resources/access-control-basic-allow-star.cgi&  access-control-allow-origin=* without credentials
 Expecting success: false
 PASS: 0
-Testing http://localhost:8000/xmlhttprequest/resources/redirect-cors.php?redirect-preflight=false&  url=http://localhost:8000/xmlhttprequest/resources/access-control-basic-allow-star.cgi&  access-control-allow-origin=*&  access-control-allow-headers=x-webkit
+Testing http://localhost:8000/xmlhttprequest/resources/redirect-cors.php?redirect-preflight=false&  url=http://localhost:8000/xmlhttprequest/resources/access-control-basic-allow-star.cgi&  access-control-allow-origin=*&  access-control-allow-headers=x-webkit without credentials
 Expecting success: false
 PASS: 0
-Testing resources/redirect-cors.php?url=http://127.0.0.1:8000/xmlhttprequest/resources/get.txt
+Testing resources/redirect-cors.php?url=http://127.0.0.1:8000/xmlhttprequest/resources/get.txt without credentials
 Expecting success: true
 PASS: PASS
 
diff --git a/LayoutTests/http/tests/xmlhttprequest/access-control-and-redirects-async-same-origin-expected.txt b/LayoutTests/http/tests/xmlhttprequest/access-control-and-redirects-async-same-origin-expected.txt
new file mode 100644
index 0000000..a5d8af3
--- /dev/null
+++ b/LayoutTests/http/tests/xmlhttprequest/access-control-and-redirects-async-same-origin-expected.txt
@@ -0,0 +1,27 @@
+CONSOLE MESSAGE: XMLHttpRequest cannot load http://localhost:8000/xmlhttprequest/resources/access-control-basic-allow-star.cgi. Cannot use wildcard in Access-Control-Allow-Origin when credentials flag is true.
+CONSOLE MESSAGE: XMLHttpRequest cannot load http://localhost:8000/xmlhttprequest/resources/access-control-basic-allow-no-credentials.cgi. Credentials flag is true, but Access-Control-Allow-Credentials is not "true".
+Tests that asynchronous XMLHttpRequests handle redirects according to the CORS standard.
+
+Testing resources/redirect.php?url=http://localhost:8000/xmlhttprequest/resources/access-control-basic-allow-star.cgi without credentials
+Expecting success: true
+PASS: PASS: Cross-domain access allowed.
+
+Testing resources/redirect.php?url=http://localhost:8000/xmlhttprequest/resources/access-control-basic-allow-star.cgi with credentials
+Expecting success: false
+PASS: 0
+Testing resources/redirect.php?url=http://localhost:8000/xmlhttprequest/resources/access-control-basic-allow.cgi without credentials
+Expecting success: true
+PASS: PASS: Cross-domain access allowed.
+
+Testing resources/redirect.php?url=http://localhost:8000/xmlhttprequest/resources/access-control-basic-allow.cgi with credentials
+Expecting success: true
+PASS: PASS: Cross-domain access allowed.
+
+Testing resources/redirect.php?url=http://localhost:8000/xmlhttprequest/resources/access-control-basic-allow-no-credentials.cgi without credentials
+Expecting success: true
+PASS: PASS: Cross-domain access allowed.
+
+Testing resources/redirect.php?url=http://localhost:8000/xmlhttprequest/resources/access-control-basic-allow-no-credentials.cgi with credentials
+Expecting success: false
+PASS: 0
+
diff --git a/LayoutTests/http/tests/xmlhttprequest/access-control-and-redirects-async-same-origin.html b/LayoutTests/http/tests/xmlhttprequest/access-control-and-redirects-async-same-origin.html
new file mode 100644
index 0000000..ec891bd
--- /dev/null
+++ b/LayoutTests/http/tests/xmlhttprequest/access-control-and-redirects-async-same-origin.html
@@ -0,0 +1,88 @@
+<p>Tests that asynchronous XMLHttpRequests handle redirects according to the CORS standard.</p>
+
+<pre id="console"></pre>
+<script>
+if (window.testRunner) {
+    testRunner.dumpAsText();
+    testRunner.waitUntilDone();
+}
+
+function log(message)
+{
+    document.getElementById('console').appendChild(document.createTextNode(message + '\n'));
+}
+
+function runTestAsync(url, credentials, addCustomHeader, expectSuccess) {
+    log("Testing " + url + (credentials ? " with " : " without ") + "credentials");
+    log("Expecting success: " + expectSuccess);
+
+    xhr = new XMLHttpRequest();
+    xhr.withCredentials = credentials;
+    xhr.open("GET", url, true);
+    if (addCustomHeader)
+        xhr.setRequestHeader("x-webkit", "foo");
+
+    xhr.onload = function() {
+        log((expectSuccess ? "PASS" : "FAIL") + ": " + xhr.responseText);
+        nextTest();
+    }
+    xhr.onerror = function() {
+        log((expectSuccess ? "FAIL" : "PASS") + ": " + xhr.status);
+        nextTest();
+    }
+    xhr.send(null);
+}
+
+var withoutCredentials = false;
+var withCredentials = true;
+var noCustomHeader = false;
+var addCustomHeader = true;
+var succeeds = true;
+var fails = false;
+
+var tests = [
+// Test simple same origin requests that receive cross origin redirects.
+
+// Request without credentials is redirected to a cross-origin response with Access-Control-Allow-Origin=*.
+// The redirect response passes the access check.
+["resources/redirect.php?url=http://localhost:8000/xmlhttprequest/resources/access-control-basic-allow-star.cgi",
+  withoutCredentials, noCustomHeader, succeeds],
+
+// Request with credentials is redirected to a cross-origin response with Access-Control-Allow-Origin=*.
+// The redirect response fails the access check because credentials were sent.
+["resources/redirect.php?url=http://localhost:8000/xmlhttprequest/resources/access-control-basic-allow-star.cgi",
+  withCredentials, noCustomHeader, fails],
+
+// Request without credentials is redirected to a cross-origin response with a specific Access-Control-Allow-Origin.
+// The redirect response passes the access check.
+["resources/redirect.php?url=http://localhost:8000/xmlhttprequest/resources/access-control-basic-allow.cgi",
+  withoutCredentials, noCustomHeader, succeeds],
+
+// Request with credentials is redirected to a cross-origin response with a specific Access-Control-Allow-Origin.
+// The redirect response passes the access check.
+["resources/redirect.php?url=http://localhost:8000/xmlhttprequest/resources/access-control-basic-allow.cgi",
+  withCredentials, noCustomHeader, succeeds],
+
+// Request without credentials is redirected to a cross-origin response with a specific Access-Control-Allow-Origin
+// forbidding credentials. The redirect response passes the access check.
+["resources/redirect.php?url=http://localhost:8000/xmlhttprequest/resources/access-control-basic-allow-no-credentials.cgi",
+  withoutCredentials, noCustomHeader, succeeds],
+
+// Request with credentials is redirected to a cross-origin response with a specific Access-Control-Allow-Origin
+// forbidding credentials. The redirect response fails the access check.
+["resources/redirect.php?url=http://localhost:8000/xmlhttprequest/resources/access-control-basic-allow-no-credentials.cgi",
+  withCredentials, noCustomHeader, fails],
+
+]
+
+var currentTest = 0;
+
+function nextTest() {
+    if (currentTest < tests.length)
+        runTestAsync.apply(null, tests[currentTest++]);
+    else if (window.testRunner)
+        testRunner.notifyDone();
+}
+
+nextTest();
+</script>
diff --git a/LayoutTests/http/tests/xmlhttprequest/access-control-and-redirects-async.html b/LayoutTests/http/tests/xmlhttprequest/access-control-and-redirects-async.html
index 086a16c..83fc3b6 100644
--- a/LayoutTests/http/tests/xmlhttprequest/access-control-and-redirects-async.html
+++ b/LayoutTests/http/tests/xmlhttprequest/access-control-and-redirects-async.html
@@ -12,11 +12,12 @@
     document.getElementById('console').appendChild(document.createTextNode(message + '\n'));
 }
 
-function runTestAsync(url, addCustomHeader, expectSuccess) {
-    log("Testing " + url);
+function runTestAsync(url, credentials, addCustomHeader, expectSuccess) {
+    log("Testing " + url + (credentials ? " with " : " without ") + "credentials");
     log("Expecting success: " + expectSuccess);
 
     xhr = new XMLHttpRequest();
+    xhr.withCredentials = credentials;
     xhr.open("GET", url, true);
     if (addCustomHeader)
         xhr.setRequestHeader("x-webkit", "foo");
@@ -32,72 +33,57 @@
     xhr.send(null);
 }
 
+var withoutCredentials = false;
+var withCredentials = true;
 var noCustomHeader = false;
 var addCustomHeader = true;
 var succeeds = true;
 var fails = false;
 
 var tests = [
-// 1) Test simple same origin requests that receive cross origin redirects.
-
-// Request receives a cross-origin redirect response without CORS headers. The redirect response fails the access check.
-["resources/redirect-cors.php?url=http://localhost:8000/xmlhttprequest/resources/access-control-basic-allow-star.cgi",
-  noCustomHeader, fails],
-
-// Request receives a cross-origin redirect response with CORS headers. The redirect response passes the access check,
-// but  the resource response fails its access check because the security origin is a globally unique identifier after
-// the redirect and the same origin XHR has 'allowCredentials' true.
-["resources/redirect-cors.php?url=http://localhost:8000/xmlhttprequest/resources/access-control-basic-allow-star.cgi&\
-  access-control-allow-origin=http://localhost:8000&\
-  access-control-allow-credentials=true",
-  noCustomHeader, fails],
-
-// Same as above, but to a less permissive resource that only allows the requesting origin.
-["resources/redirect-cors.php?url=http://localhost:8000/xmlhttprequest/resources/access-control-basic-allow.cgi&\
-  access-control-allow-origin=http://localhost:8000&\
-  access-control-allow-credentials=true",
-  noCustomHeader, fails],
-
-// 2) Test simple cross origin requests that receive redirects.
+// 1) Test simple cross origin requests that receive redirects.
 
 // Receives a redirect response without CORS headers. The redirect response fails the access check.
 ["http://localhost:8000/xmlhttprequest/resources/redirect-cors.php?url=http://localhost:8000/xmlhttprequest/resources/access-control-basic-allow-star.cgi",
-  noCustomHeader, fails],
+  withoutCredentials, noCustomHeader, fails],
 
 // Receives a redirect response with CORS headers. The redirect response passes the access check and the resource response
 // passes the access check.
+// FIXME: this test fails because the redirect is vetoed. There are continued bugs with redirects when the original
+// request was cross-origin.
 ["http://localhost:8000/xmlhttprequest/resources/redirect-cors.php?url=http://localhost:8000/xmlhttprequest/resources/access-control-basic-allow-star.cgi&\
   access-control-allow-origin=http://localhost:8000",
-  noCustomHeader, succeeds],
+  withoutCredentials, noCustomHeader, succeeds],
 
 // Receives a redirect response with a URL containing the userinfo production.
 ["http://localhost:8000/xmlhttprequest/resources/redirect-cors.php?url=http://username:password@localhost:8000/xmlhttprequest/resources/access-control-basic-allow-star.cgi&\
   access-control-allow-origin=http://localhost:8000",
-  noCustomHeader, fails],
+  withoutCredentials, noCustomHeader, fails],
 
 // Receives a redirect response with a URL with an unsupported scheme.
 ["http://localhost:8000/xmlhttprequest/resources/redirect-cors.php?url=foo://bar.cgi&\
   access-control-allow-origin=http://localhost:8000",
-  noCustomHeader, fails],
+  withoutCredentials, noCustomHeader, fails],
 
-// 3) Test preflighted cross origin requests that receive redirects.
+// 2) Test preflighted cross origin requests that receive redirects.
 
 // Receives a redirect response to the preflight request and fails.
 ["http://localhost:8000/xmlhttprequest/resources/redirect-cors.php?redirect-preflight=true&\
   url=http://localhost:8000/xmlhttprequest/resources/access-control-basic-allow-star.cgi&\
   access-control-allow-origin=*",
-  addCustomHeader, fails],
+  withoutCredentials, addCustomHeader, fails],
 
 // Successful preflight and receives a redirect response to the actual request and fails.
 ["http://localhost:8000/xmlhttprequest/resources/redirect-cors.php?redirect-preflight=false&\
   url=http://localhost:8000/xmlhttprequest/resources/access-control-basic-allow-star.cgi&\
   access-control-allow-origin=*&\
   access-control-allow-headers=x-webkit",
-  addCustomHeader, fails],
+  withoutCredentials, addCustomHeader, fails],
 
-// 4) Test same origin requests with a custom header that receive a same origin redirect.
+// 3) Test same origin requests with a custom header that receive a same origin redirect.
 ["resources/redirect-cors.php?url=http://127.0.0.1:8000/xmlhttprequest/resources/get.txt",
-  addCustomHeader, succeeds],
+  withoutCredentials, addCustomHeader, succeeds],
+
 ]
 
 var currentTest = 0;
diff --git a/LayoutTests/http/tests/xmlhttprequest/access-control-and-redirects-expected.txt b/LayoutTests/http/tests/xmlhttprequest/access-control-and-redirects-expected.txt
index 4471326..e5b2fe7 100644
--- a/LayoutTests/http/tests/xmlhttprequest/access-control-and-redirects-expected.txt
+++ b/LayoutTests/http/tests/xmlhttprequest/access-control-and-redirects-expected.txt
@@ -6,8 +6,9 @@
 Expecting success: false
 PASS: Error: NetworkError: DOM Exception 19
 Testing /resources/redirect.php?url=http://localhost:8000/xmlhttprequest/resources/access-control-basic-allow.cgi(async)
-Expecting success: false
-PASS: 0
+Expecting success: true
+PASS: PASS: Cross-domain access allowed.
+
 Testing http://localhost:8000/resources/redirect.php?url=http://127.0.0.1:8000/xmlhttprequest/resources/access-control-basic-allow.cgi (sync)
 Expecting success: false
 PASS: Error: NetworkError: DOM Exception 19
diff --git a/LayoutTests/http/tests/xmlhttprequest/access-control-and-redirects.html b/LayoutTests/http/tests/xmlhttprequest/access-control-and-redirects.html
index 27f55be..9792c36 100644
--- a/LayoutTests/http/tests/xmlhttprequest/access-control-and-redirects.html
+++ b/LayoutTests/http/tests/xmlhttprequest/access-control-and-redirects.html
@@ -45,7 +45,7 @@
 }
 
 var tests = [
-    ["/resources/redirect.php?url=http://localhost:8000/xmlhttprequest/resources/access-control-basic-allow.cgi", false, false],
+    ["/resources/redirect.php?url=http://localhost:8000/xmlhttprequest/resources/access-control-basic-allow.cgi", false, true],
     ["http://localhost:8000/resources/redirect.php?url=http://127.0.0.1:8000/xmlhttprequest/resources/access-control-basic-allow.cgi", false, false],
     ["http://localhost:8000/resources/redirect.php?url=http://localhost:8000/xmlhttprequest/resources/access-control-basic-allow.cgi", false, false]
 ]
diff --git a/LayoutTests/http/tests/xmlhttprequest/redirect-cross-origin-2-expected.txt b/LayoutTests/http/tests/xmlhttprequest/redirect-cross-origin-2-expected.txt
index 14ecdda..a187ec2 100644
--- a/LayoutTests/http/tests/xmlhttprequest/redirect-cross-origin-2-expected.txt
+++ b/LayoutTests/http/tests/xmlhttprequest/redirect-cross-origin-2-expected.txt
@@ -1,3 +1,4 @@
+CONSOLE MESSAGE: XMLHttpRequest cannot load http://localhost:8000/xmlhttprequest/resources/reply.xml. Origin http://127.0.0.1:8000 is not allowed by Access-Control-Allow-Origin.
 Test that a cross-origin redirect to a server that responds is indistinguishable from one that does not. Should say PASS:
 
 PASS
diff --git a/LayoutTests/http/tests/xmlhttprequest/redirect-cross-origin-expected.txt b/LayoutTests/http/tests/xmlhttprequest/redirect-cross-origin-expected.txt
index 14ecdda..a187ec2 100644
--- a/LayoutTests/http/tests/xmlhttprequest/redirect-cross-origin-expected.txt
+++ b/LayoutTests/http/tests/xmlhttprequest/redirect-cross-origin-expected.txt
@@ -1,3 +1,4 @@
+CONSOLE MESSAGE: XMLHttpRequest cannot load http://localhost:8000/xmlhttprequest/resources/reply.xml. Origin http://127.0.0.1:8000 is not allowed by Access-Control-Allow-Origin.
 Test that a cross-origin redirect to a server that responds is indistinguishable from one that does not. Should say PASS:
 
 PASS
diff --git a/LayoutTests/http/tests/xmlhttprequest/redirect-cross-origin-post-expected.txt b/LayoutTests/http/tests/xmlhttprequest/redirect-cross-origin-post-expected.txt
index 14ecdda..a187ec2 100644
--- a/LayoutTests/http/tests/xmlhttprequest/redirect-cross-origin-post-expected.txt
+++ b/LayoutTests/http/tests/xmlhttprequest/redirect-cross-origin-post-expected.txt
@@ -1,3 +1,4 @@
+CONSOLE MESSAGE: XMLHttpRequest cannot load http://localhost:8000/xmlhttprequest/resources/reply.xml. Origin http://127.0.0.1:8000 is not allowed by Access-Control-Allow-Origin.
 Test that a cross-origin redirect to a server that responds is indistinguishable from one that does not. Should say PASS:
 
 PASS
diff --git a/LayoutTests/http/tests/xmlhttprequest/redirect-cross-origin-tripmine-expected.txt b/LayoutTests/http/tests/xmlhttprequest/redirect-cross-origin-tripmine-expected.txt
index a507a97..3bfe79b 100644
--- a/LayoutTests/http/tests/xmlhttprequest/redirect-cross-origin-tripmine-expected.txt
+++ b/LayoutTests/http/tests/xmlhttprequest/redirect-cross-origin-tripmine-expected.txt
@@ -1,3 +1,13 @@
+CONSOLE MESSAGE: XMLHttpRequest cannot load http://localhost:8000/resources/tripmine.php. Origin http://127.0.0.1:8000 is not allowed by Access-Control-Allow-Origin.
+CONSOLE MESSAGE: XMLHttpRequest cannot load http://localhost:8000/resources/tripmine.php. Origin http://127.0.0.1:8000 is not allowed by Access-Control-Allow-Origin.
+CONSOLE MESSAGE: XMLHttpRequest cannot load http://localhost:8000/resources/tripmine.php. Origin http://127.0.0.1:8000 is not allowed by Access-Control-Allow-Origin.
+CONSOLE MESSAGE: XMLHttpRequest cannot load http://localhost:8000/resources/tripmine.php. Origin http://127.0.0.1:8000 is not allowed by Access-Control-Allow-Origin.
+CONSOLE MESSAGE: XMLHttpRequest cannot load http://localhost:8000/resources/tripmine.php. Origin http://127.0.0.1:8000 is not allowed by Access-Control-Allow-Origin.
+CONSOLE MESSAGE: XMLHttpRequest cannot load http://localhost:8000/resources/tripmine.php. Origin http://127.0.0.1:8000 is not allowed by Access-Control-Allow-Origin.
+CONSOLE MESSAGE: XMLHttpRequest cannot load http://localhost:8000/resources/tripmine.php. Origin http://127.0.0.1:8000 is not allowed by Access-Control-Allow-Origin.
+CONSOLE MESSAGE: XMLHttpRequest cannot load http://localhost:8000/resources/tripmine.php. Origin http://127.0.0.1:8000 is not allowed by Access-Control-Allow-Origin.
+CONSOLE MESSAGE: XMLHttpRequest cannot load http://localhost:8000/resources/tripmine.php. Origin http://127.0.0.1:8000 is not allowed by Access-Control-Allow-Origin.
+CONSOLE MESSAGE: XMLHttpRequest cannot load http://localhost:8000/resources/tripmine.php. Origin http://127.0.0.1:8000 is not allowed by Access-Control-Allow-Origin.
 Test that a cross-origin redirect does not result in a non-simple request being sent to the target.
 
 Asynchronous XMLHttpRequest 307 POST redirect:
diff --git a/LayoutTests/http/tests/xmlhttprequest/resources/access-control-basic-allow-no-credentials.cgi b/LayoutTests/http/tests/xmlhttprequest/resources/access-control-basic-allow-no-credentials.cgi
new file mode 100755
index 0000000..da20100
--- /dev/null
+++ b/LayoutTests/http/tests/xmlhttprequest/resources/access-control-basic-allow-no-credentials.cgi
@@ -0,0 +1,7 @@
+#!/usr/bin/perl -wT
+use strict;
+
+print "Content-Type: text/plain\n";
+print "Access-Control-Allow-Origin: http://127.0.0.1:8000\n\n";
+
+print "PASS: Cross-domain access allowed.\n";
diff --git a/LayoutTests/http/tests/xmlhttprequest/xmlhttprequest-unsafe-redirect-expected.txt b/LayoutTests/http/tests/xmlhttprequest/xmlhttprequest-unsafe-redirect-expected.txt
index df9988a..ecaf62f 100644
--- a/LayoutTests/http/tests/xmlhttprequest/xmlhttprequest-unsafe-redirect-expected.txt
+++ b/LayoutTests/http/tests/xmlhttprequest/xmlhttprequest-unsafe-redirect-expected.txt
@@ -1,3 +1,4 @@
+CONSOLE MESSAGE: XMLHttpRequest cannot load http://localhost:8080/xmlhttprequest/resources/forbidden.txt. Origin http://127.0.0.1:8000 is not allowed by Access-Control-Allow-Origin.
 This tests that unsafe redirects won't be allowed when making an XMLHttpRequest.
 Sync XHR started.
 readyState change 1
diff --git a/Source/core/loader/DocumentLoader.cpp b/Source/core/loader/DocumentLoader.cpp
index 1aac7ce..eea87e8 100644
--- a/Source/core/loader/DocumentLoader.cpp
+++ b/Source/core/loader/DocumentLoader.cpp
@@ -1077,7 +1077,7 @@
 
     ResourceRequest request(m_request);
     DEFINE_STATIC_LOCAL(ResourceLoaderOptions, mainResourceLoadOptions,
-        (SendCallbacks, SniffContent, BufferData, AllowStoredCredentials, AskClientForCrossOriginCredentials, SkipSecurityCheck));
+        (SendCallbacks, SniffContent, BufferData, AllowStoredCredentials, ClientRequestedCredentials, AskClientForCrossOriginCredentials, SkipSecurityCheck));
     CachedResourceRequest cachedResourceRequest(request, mainResourceLoadOptions);
     m_mainResource = m_cachedResourceLoader->requestMainResource(cachedResourceRequest);
     if (!m_mainResource) {
diff --git a/Source/core/loader/DocumentThreadableLoader.cpp b/Source/core/loader/DocumentThreadableLoader.cpp
index 5ae09a9..a40b7fc 100644
--- a/Source/core/loader/DocumentThreadableLoader.cpp
+++ b/Source/core/loader/DocumentThreadableLoader.cpp
@@ -187,7 +187,8 @@
     }
 
     // When using access control, only simple cross origin requests are allowed to redirect. The new request URL must have a supported
-    // scheme and not contain the userinfo production. In addition, the redirect response must pass the access control check.
+    // scheme and not contain the userinfo production. In addition, the redirect response must pass the access control check if the
+    // original request was not same-origin.
     if (m_options.crossOriginRequestPolicy == UseAccessControl) {
         bool allowRedirect = false;
         if (m_simpleRequest) {
@@ -195,7 +196,7 @@
             allowRedirect = SchemeRegistry::shouldTreatURLSchemeAsCORSEnabled(request.url().protocol())
                             && request.url().user().isEmpty()
                             && request.url().pass().isEmpty()
-                            && passesAccessControlCheck(redirectResponse, m_options.allowCredentials, securityOrigin(), accessControlErrorDescription);
+                            && (m_sameOriginRequest || passesAccessControlCheck(redirectResponse, m_options.allowCredentials, securityOrigin(), accessControlErrorDescription));
         }
 
         if (allowRedirect) {
@@ -204,12 +205,19 @@
 
             RefPtr<SecurityOrigin> originalOrigin = SecurityOrigin::createFromString(redirectResponse.url());
             RefPtr<SecurityOrigin> requestOrigin = SecurityOrigin::createFromString(request.url());
-            // If the request URL origin is not same origin with the original URL origin, set source origin to a globally unique identifier.
-            if (!originalOrigin->isSameSchemeHostPort(requestOrigin.get()))
+            // If the original request wasn't same-origin, then if the request URL origin is not same origin with the original URL origin,
+            // set the source origin to a globally unique identifier. (If the original request was same-origin, the origin of the new request
+            // should be the original URL origin.)
+            if (!m_sameOriginRequest && !originalOrigin->isSameSchemeHostPort(requestOrigin.get()))
                 m_options.securityOrigin = SecurityOrigin::createUnique();
             // Force any subsequent requests to use these checks.
             m_sameOriginRequest = false;
 
+            // Since the request is no longer same-origin, if the user didn't request credentials in
+            // the first place, update our state so we neither request them nor expect they must be allowed.
+            if (m_options.credentialsRequested == ClientDidNotRequestCredentials)
+                m_options.allowCredentials = DoNotAllowStoredCredentials;
+
             // Remove any headers that may have been added by the network layer that cause access control to fail.
             request.clearHTTPContentType();
             request.clearHTTPReferrer();
diff --git a/Source/core/loader/ResourceLoaderOptions.h b/Source/core/loader/ResourceLoaderOptions.h
index ad1adaa..88c1fc0 100644
--- a/Source/core/loader/ResourceLoaderOptions.h
+++ b/Source/core/loader/ResourceLoaderOptions.h
@@ -61,12 +61,28 @@
 };
 
 struct ResourceLoaderOptions {
-    ResourceLoaderOptions() : sendLoadCallbacks(DoNotSendCallbacks), sniffContent(DoNotSniffContent), dataBufferingPolicy(BufferData), allowCredentials(DoNotAllowStoredCredentials), crossOriginCredentialPolicy(DoNotAskClientForCrossOriginCredentials), securityCheck(DoSecurityCheck) { }
-    ResourceLoaderOptions(SendCallbackPolicy sendLoadCallbacks, ContentSniffingPolicy sniffContent, DataBufferingPolicy dataBufferingPolicy, StoredCredentials allowCredentials, ClientCrossOriginCredentialPolicy crossOriginCredentialPolicy, SecurityCheckPolicy securityCheck)
+    ResourceLoaderOptions()
+        : sendLoadCallbacks(DoNotSendCallbacks)
+        , sniffContent(DoNotSniffContent)
+        , dataBufferingPolicy(BufferData)
+        , allowCredentials(DoNotAllowStoredCredentials)
+        , credentialsRequested(ClientDidNotRequestCredentials)
+        , crossOriginCredentialPolicy(DoNotAskClientForCrossOriginCredentials)
+        , securityCheck(DoSecurityCheck) { }
+
+    ResourceLoaderOptions(
+        SendCallbackPolicy sendLoadCallbacks,
+        ContentSniffingPolicy sniffContent,
+        DataBufferingPolicy dataBufferingPolicy,
+        StoredCredentials allowCredentials,
+        CredentialRequest credentialsRequested,
+        ClientCrossOriginCredentialPolicy crossOriginCredentialPolicy,
+        SecurityCheckPolicy securityCheck)
         : sendLoadCallbacks(sendLoadCallbacks)
         , sniffContent(sniffContent)
         , dataBufferingPolicy(dataBufferingPolicy)
         , allowCredentials(allowCredentials)
+        , credentialsRequested(credentialsRequested)
         , crossOriginCredentialPolicy(crossOriginCredentialPolicy)
         , securityCheck(securityCheck)
     {
@@ -75,6 +91,7 @@
     ContentSniffingPolicy sniffContent;
     DataBufferingPolicy dataBufferingPolicy;
     StoredCredentials allowCredentials; // Whether HTTP credentials and cookies are sent with the request.
+    CredentialRequest credentialsRequested; // Whether the client (e.g. XHR) wanted credentials in the first place.
     ClientCrossOriginCredentialPolicy crossOriginCredentialPolicy; // Whether we will ask the client for credentials (if we allow credentials at all).
     SecurityCheckPolicy securityCheck;
 };
diff --git a/Source/core/loader/cache/CachedResourceLoader.cpp b/Source/core/loader/cache/CachedResourceLoader.cpp
index ca469a6..d43b915 100644
--- a/Source/core/loader/cache/CachedResourceLoader.cpp
+++ b/Source/core/loader/cache/CachedResourceLoader.cpp
@@ -186,7 +186,7 @@
         memoryCache()->remove(existing);
     }
 
-    request.setOptions(ResourceLoaderOptions(DoNotSendCallbacks, SniffContent, BufferData, AllowStoredCredentials, AskClientForCrossOriginCredentials, SkipSecurityCheck));
+    request.setOptions(ResourceLoaderOptions(DoNotSendCallbacks, SniffContent, BufferData, AllowStoredCredentials, ClientRequestedCredentials, AskClientForCrossOriginCredentials, SkipSecurityCheck));
     return static_cast<CachedCSSStyleSheet*>(requestResource(CachedResource::CSSStyleSheet, request).get());
 }
 
@@ -1042,7 +1042,7 @@
 
 const ResourceLoaderOptions& CachedResourceLoader::defaultCachedResourceOptions()
 {
-    static ResourceLoaderOptions options(SendCallbacks, SniffContent, BufferData, AllowStoredCredentials, AskClientForCrossOriginCredentials, DoSecurityCheck);
+    static ResourceLoaderOptions options(SendCallbacks, SniffContent, BufferData, AllowStoredCredentials, ClientRequestedCredentials, AskClientForCrossOriginCredentials, DoSecurityCheck);
     return options;
 }
 
diff --git a/Source/core/page/EventSource.cpp b/Source/core/page/EventSource.cpp
index dc60773..a9deb36 100644
--- a/Source/core/page/EventSource.cpp
+++ b/Source/core/page/EventSource.cpp
@@ -131,6 +131,7 @@
     options.sendLoadCallbacks = SendCallbacks;
     options.sniffContent = DoNotSniffContent;
     options.allowCredentials = (origin->canRequest(m_url) || m_withCredentials) ? AllowStoredCredentials : DoNotAllowStoredCredentials;
+    options.credentialsRequested = m_withCredentials ? ClientRequestedCredentials : ClientDidNotRequestCredentials;
     options.preflightPolicy = PreventPreflight;
     options.crossOriginRequestPolicy = UseAccessControl;
     options.dataBufferingPolicy = DoNotBufferData;
diff --git a/Source/core/platform/network/ResourceHandleTypes.h b/Source/core/platform/network/ResourceHandleTypes.h
index 870e795..4dde871 100644
--- a/Source/core/platform/network/ResourceHandleTypes.h
+++ b/Source/core/platform/network/ResourceHandleTypes.h
@@ -33,6 +33,15 @@
     DoNotAllowStoredCredentials
 };
 
+// APIs like XMLHttpRequest and EventSource let the user decide
+// whether to send credentials, but they're always sent for
+// same-origin requests. Additional information is needed to handle
+// cross-origin redirects correctly.
+enum CredentialRequest {
+    ClientRequestedCredentials,
+    ClientDidNotRequestCredentials
+};
+
 } // namespace WebCore
 
 #endif // ResourceHandleTypes_h
diff --git a/Source/core/xml/XMLHttpRequest.cpp b/Source/core/xml/XMLHttpRequest.cpp
index d173c52..578ba58 100644
--- a/Source/core/xml/XMLHttpRequest.cpp
+++ b/Source/core/xml/XMLHttpRequest.cpp
@@ -749,6 +749,7 @@
     options.sniffContent = DoNotSniffContent;
     options.preflightPolicy = uploadEvents ? ForcePreflight : ConsiderPreflight;
     options.allowCredentials = (m_sameOriginRequest || m_includeCredentials) ? AllowStoredCredentials : DoNotAllowStoredCredentials;
+    options.credentialsRequested = m_includeCredentials ? ClientRequestedCredentials : ClientDidNotRequestCredentials;
     options.crossOriginRequestPolicy = UseAccessControl;
     options.securityOrigin = securityOrigin();
     options.initiator = cachedResourceRequestInitiators().xmlhttprequest;