[Beacon] Add support for CORS-preflighting for WK2 / NETWORK_SESSION
authorcdumez@apple.com <cdumez@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 9 Aug 2017 05:15:47 +0000 (05:15 +0000)
committercdumez@apple.com <cdumez@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 9 Aug 2017 05:15:47 +0000 (05:15 +0000)
https://bugs.webkit.org/show_bug.cgi?id=175264
<rdar://problem/33547793>

Reviewed by Youenn Fablet.

Source/WebCore:

Pass additional information when creating a PingHandle so that the PingLoad
can deal with CORS-preflighting on Network process side.

Tests: http/wpt/beacon/cors/cors-preflight-arraybufferview-failure.html
       http/wpt/beacon/cors/cors-preflight-arraybufferview-success.html
       http/wpt/beacon/cors/cors-preflight-blob-failure.html
       http/wpt/beacon/cors/cors-preflight-blob-success.html
       http/wpt/beacon/cors/cors-preflight-cookie.html

* WebCore.xcodeproj/project.pbxproj:
* loader/CrossOriginAccessControl.cpp:
(WebCore::validatePreflightResponse):
* loader/CrossOriginAccessControl.h:
* loader/CrossOriginPreflightChecker.cpp:
(WebCore::CrossOriginPreflightChecker::validatePreflightResponse):
* loader/CrossOriginPreflightResultCache.h:
* loader/LoaderStrategy.h:
* loader/PingLoader.cpp:
(WebCore::PingLoader::loadImage):
(WebCore::PingLoader::sendPing):
(WebCore::PingLoader::sendViolationReport):
(WebCore::PingLoader::startPingLoad):
* loader/PingLoader.h:
* loader/cache/CachedResource.cpp:
(WebCore::CachedResource::load):
* page/SecurityOrigin.h:

Source/WebKit:

Implement CORS-preflighting for beacons with a payload that has a non
safelisted MIME type, as per:
- https://w3c.github.io/beacon/#privacy
- https://www.w3.org/TR/beacon/#sec-processing-model

CORS-preflighting is completely handled on Network Process side because
a beacon request can outlive its page and therefore its WebContent
process. This requires us to pass a little more information to the
Network process, in particular the source origin and the corsMode.

The current implementation does not currently deal with CORS preflights
needed upon a redirect. This will be added in a follow-up.

* CMakeLists.txt:
* NetworkProcess/NetworkCORSPreflightChecker.cpp: Added.
(WebKit::NetworkCORSPreflightChecker::NetworkCORSPreflightChecker):
(WebKit::NetworkCORSPreflightChecker::~NetworkCORSPreflightChecker):
(WebKit::NetworkCORSPreflightChecker::startPreflight):
(WebKit::NetworkCORSPreflightChecker::willPerformHTTPRedirection):
(WebKit::NetworkCORSPreflightChecker::didReceiveChallenge):
(WebKit::NetworkCORSPreflightChecker::didReceiveResponseNetworkSession):
(WebKit::NetworkCORSPreflightChecker::didReceiveData):
(WebKit::NetworkCORSPreflightChecker::didCompleteWithError):
(WebKit::NetworkCORSPreflightChecker::didSendData):
(WebKit::NetworkCORSPreflightChecker::wasBlocked):
(WebKit::NetworkCORSPreflightChecker::cannotShowURL):
* NetworkProcess/NetworkCORSPreflightChecker.h: Added.
* NetworkProcess/NetworkConnectionToWebProcess.cpp:
(WebKit::NetworkConnectionToWebProcess::loadPing):
* NetworkProcess/NetworkConnectionToWebProcess.h:
* NetworkProcess/NetworkResourceLoadParameters.cpp:
(WebKit::NetworkResourceLoadParameters::encode const):
(WebKit::NetworkResourceLoadParameters::decode):
* NetworkProcess/NetworkResourceLoadParameters.h:
* NetworkProcess/PingLoad.cpp: Added.
(WebKit::PingLoad::PingLoad):
(WebKit::PingLoad::~PingLoad):
(WebKit::PingLoad::startNetworkLoad):
(WebKit::PingLoad::willPerformHTTPRedirection):
(WebKit::PingLoad::didReceiveChallenge):
(WebKit::PingLoad::didReceiveResponseNetworkSession):
(WebKit::PingLoad::didReceiveData):
(WebKit::PingLoad::didCompleteWithError):
(WebKit::PingLoad::didSendData):
(WebKit::PingLoad::wasBlocked):
(WebKit::PingLoad::cannotShowURL):
(WebKit::PingLoad::timeoutTimerFired):
(WebKit::PingLoad::needsCORSPreflight const):
(WebKit::PingLoad::doCORSPreflight):
* NetworkProcess/PingLoad.h:
* WebKit.xcodeproj/project.pbxproj:
* WebProcess/Network/WebLoaderStrategy.cpp:
(WebKit::WebLoaderStrategy::createPingHandle):
* WebProcess/Network/WebLoaderStrategy.h:

Source/WebKitLegacy:

createPingHandle() now takes new parameters but there is currently no behavior
change on WebKit1.

* WebCoreSupport/WebResourceLoadScheduler.cpp:
(WebResourceLoadScheduler::createPingHandle):
* WebCoreSupport/WebResourceLoadScheduler.h:

LayoutTests:

Add layout test coverage.

* http/wpt/beacon/cors/cors-preflight-arraybufferview-failure-expected.txt: Added.
* http/wpt/beacon/cors/cors-preflight-arraybufferview-failure.html: Added.
* http/wpt/beacon/cors/cors-preflight-arraybufferview-success-expected.txt: Added.
* http/wpt/beacon/cors/cors-preflight-arraybufferview-success.html: Added.
* http/wpt/beacon/cors/cors-preflight-blob-failure-expected.txt: Added.
* http/wpt/beacon/cors/cors-preflight-blob-failure.html: Added.
* http/wpt/beacon/cors/cors-preflight-blob-success-expected.txt: Added.
* http/wpt/beacon/cors/cors-preflight-blob-success.html: Added.
* http/wpt/beacon/cors/cors-preflight-cookie-expected.txt: Added.
* http/wpt/beacon/cors/cors-preflight-cookie.html: Added.
* http/wpt/beacon/resources/beacon-preflight.py: Added.
(respondToCORSPreflight):
(main):
* http/wpt/beacon/resources/set-cookie.py: Added.
(main):
* platform/mac-wk1/TestExpectations:
* platform/mac-wk2/TestExpectations:
* platform/win/TestExpectations:

git-svn-id: http://svn.webkit.org/repository/webkit/trunk@220442 268f45cc-cd09-0410-ab3c-d52691b4dbfc

43 files changed:
LayoutTests/ChangeLog
LayoutTests/http/wpt/beacon/cors/cors-preflight-arraybufferview-failure-expected.txt [new file with mode: 0644]
LayoutTests/http/wpt/beacon/cors/cors-preflight-arraybufferview-failure.html [new file with mode: 0644]
LayoutTests/http/wpt/beacon/cors/cors-preflight-arraybufferview-success-expected.txt [new file with mode: 0644]
LayoutTests/http/wpt/beacon/cors/cors-preflight-arraybufferview-success.html [new file with mode: 0644]
LayoutTests/http/wpt/beacon/cors/cors-preflight-blob-failure-expected.txt [new file with mode: 0644]
LayoutTests/http/wpt/beacon/cors/cors-preflight-blob-failure.html [new file with mode: 0644]
LayoutTests/http/wpt/beacon/cors/cors-preflight-blob-success-expected.txt [new file with mode: 0644]
LayoutTests/http/wpt/beacon/cors/cors-preflight-blob-success.html [new file with mode: 0644]
LayoutTests/http/wpt/beacon/cors/cors-preflight-cookie-expected.txt [new file with mode: 0644]
LayoutTests/http/wpt/beacon/cors/cors-preflight-cookie.html [new file with mode: 0644]
LayoutTests/http/wpt/beacon/resources/beacon-preflight.py [new file with mode: 0644]
LayoutTests/http/wpt/beacon/resources/set-cookie.py [new file with mode: 0644]
LayoutTests/platform/mac-wk1/TestExpectations
LayoutTests/platform/mac-wk2/TestExpectations
LayoutTests/platform/win/TestExpectations
Source/WebCore/ChangeLog
Source/WebCore/WebCore.xcodeproj/project.pbxproj
Source/WebCore/loader/CrossOriginAccessControl.cpp
Source/WebCore/loader/CrossOriginAccessControl.h
Source/WebCore/loader/CrossOriginPreflightChecker.cpp
Source/WebCore/loader/CrossOriginPreflightResultCache.h
Source/WebCore/loader/LoaderStrategy.h
Source/WebCore/loader/PingLoader.cpp
Source/WebCore/loader/PingLoader.h
Source/WebCore/loader/cache/CachedResource.cpp
Source/WebCore/page/SecurityOrigin.h
Source/WebKit/CMakeLists.txt
Source/WebKit/ChangeLog
Source/WebKit/NetworkProcess/NetworkCORSPreflightChecker.cpp [new file with mode: 0644]
Source/WebKit/NetworkProcess/NetworkCORSPreflightChecker.h [new file with mode: 0644]
Source/WebKit/NetworkProcess/NetworkConnectionToWebProcess.cpp
Source/WebKit/NetworkProcess/NetworkConnectionToWebProcess.h
Source/WebKit/NetworkProcess/NetworkResourceLoadParameters.cpp
Source/WebKit/NetworkProcess/NetworkResourceLoadParameters.h
Source/WebKit/NetworkProcess/PingLoad.cpp [new file with mode: 0644]
Source/WebKit/NetworkProcess/PingLoad.h
Source/WebKit/WebKit.xcodeproj/project.pbxproj
Source/WebKit/WebProcess/Network/WebLoaderStrategy.cpp
Source/WebKit/WebProcess/Network/WebLoaderStrategy.h
Source/WebKitLegacy/ChangeLog
Source/WebKitLegacy/WebCoreSupport/WebResourceLoadScheduler.cpp
Source/WebKitLegacy/WebCoreSupport/WebResourceLoadScheduler.h

index d068e21..a0b1175 100644 (file)
@@ -1,3 +1,32 @@
+2017-08-08  Chris Dumez  <cdumez@apple.com>
+
+        [Beacon] Add support for CORS-preflighting for WK2 / NETWORK_SESSION
+        https://bugs.webkit.org/show_bug.cgi?id=175264
+        <rdar://problem/33547793>
+
+        Reviewed by Youenn Fablet.
+
+        Add layout test coverage.
+
+        * http/wpt/beacon/cors/cors-preflight-arraybufferview-failure-expected.txt: Added.
+        * http/wpt/beacon/cors/cors-preflight-arraybufferview-failure.html: Added.
+        * http/wpt/beacon/cors/cors-preflight-arraybufferview-success-expected.txt: Added.
+        * http/wpt/beacon/cors/cors-preflight-arraybufferview-success.html: Added.
+        * http/wpt/beacon/cors/cors-preflight-blob-failure-expected.txt: Added.
+        * http/wpt/beacon/cors/cors-preflight-blob-failure.html: Added.
+        * http/wpt/beacon/cors/cors-preflight-blob-success-expected.txt: Added.
+        * http/wpt/beacon/cors/cors-preflight-blob-success.html: Added.
+        * http/wpt/beacon/cors/cors-preflight-cookie-expected.txt: Added.
+        * http/wpt/beacon/cors/cors-preflight-cookie.html: Added.
+        * http/wpt/beacon/resources/beacon-preflight.py: Added.
+        (respondToCORSPreflight):
+        (main):
+        * http/wpt/beacon/resources/set-cookie.py: Added.
+        (main):
+        * platform/mac-wk1/TestExpectations:
+        * platform/mac-wk2/TestExpectations:
+        * platform/win/TestExpectations:
+
 2017-08-08  Devin Rousso  <drousso@apple.com>
 
         Web Inspector: Canvas: support editing WebGL shaders
diff --git a/LayoutTests/http/wpt/beacon/cors/cors-preflight-arraybufferview-failure-expected.txt b/LayoutTests/http/wpt/beacon/cors/cors-preflight-arraybufferview-failure-expected.txt
new file mode 100644 (file)
index 0000000..11932f7
--- /dev/null
@@ -0,0 +1,3 @@
+
+PASS CORS preflight failure test 
+
diff --git a/LayoutTests/http/wpt/beacon/cors/cors-preflight-arraybufferview-failure.html b/LayoutTests/http/wpt/beacon/cors/cors-preflight-arraybufferview-failure.html
new file mode 100644 (file)
index 0000000..73c3b2d
--- /dev/null
@@ -0,0 +1,64 @@
+<!doctype html>
+<html>
+  <head>
+    <meta charset="utf-8">
+    <title>SendBeacon CORS preflight test</title>
+    <script src=/resources/testharness.js></script>
+    <script src=/resources/testharnessreport.js></script>
+  </head>
+  <body>
+    <script src="/common/utils.js"></script>
+    <script src="/common/get-host-info.sub.js"></script>
+    <script>
+var RESOURCES_DIR = "/WebKit/beacon/resources/";
+
+function pollResult(test, id) {
+  var checkUrl = RESOURCES_DIR + "beacon-preflight.py?cmd=get&id=" + id;
+
+  return new Promise(resolve => {
+    step_timeout(test.step_func(() => {
+      fetch(checkUrl).then(response => {
+        response.text().then(body => {
+          resolve(body);
+        });
+      });
+    }), 1000);
+  });
+}
+
+function testCORSPreflightFailure(what) {
+  var testBase = get_host_info().HTTP_REMOTE_ORIGIN + RESOURCES_DIR;
+  var id = self.token();
+  var testUrl = testBase + "beacon-preflight.py?allowCors=0&cmd=put&id=" + id;
+
+  promise_test(function(test) {
+    assert_true(navigator.sendBeacon(testUrl, what), "SendBeacon Succeeded");
+    return pollResult(test, id) .then(json => {
+      result = JSON.parse(json);
+      assert_equals(result['preflight'], 1, "Received preflight")
+      assert_equals(result['preflight_referer'], document.URL, "Preflight referer header")
+      assert_equals(result['preflight_requested_method'], "POST", "Preflight requested method")
+      let requested_headers = result['preflight_requested_headers'].toLowerCase()
+      assert_false(requested_headers.includes("content-type"), "Content-Type header is not requested")
+      assert_true(requested_headers.includes("referer"), "Referer header is requested")
+      assert_true(requested_headers.includes("origin"), "Origin header is requested")
+      assert_equals(result['beacon'], 0, "Did not receive beacon")
+    });
+  }, "CORS preflight failure test");
+}
+
+function stringToArrayBufferView(input) {
+  var buffer = new ArrayBuffer(input.length * 2);
+  var view = new Uint16Array(buffer);
+
+  // dumbly copy over the bytes
+  for (var i = 0, len = input.length; i < len; i++) {
+    view[i] = input.charCodeAt(i);
+  }
+  return view;
+}
+
+testCORSPreflightFailure(stringToArrayBufferView("123"));
+    </script>
+  </body>
+</html>
diff --git a/LayoutTests/http/wpt/beacon/cors/cors-preflight-arraybufferview-success-expected.txt b/LayoutTests/http/wpt/beacon/cors/cors-preflight-arraybufferview-success-expected.txt
new file mode 100644 (file)
index 0000000..1544982
--- /dev/null
@@ -0,0 +1,3 @@
+
+PASS CORS preflight success test 
+
diff --git a/LayoutTests/http/wpt/beacon/cors/cors-preflight-arraybufferview-success.html b/LayoutTests/http/wpt/beacon/cors/cors-preflight-arraybufferview-success.html
new file mode 100644 (file)
index 0000000..c711313
--- /dev/null
@@ -0,0 +1,64 @@
+<!doctype html>
+<html>
+  <head>
+    <meta charset="utf-8">
+    <title>SendBeacon CORS preflight test</title>
+    <script src=/resources/testharness.js></script>
+    <script src=/resources/testharnessreport.js></script>
+  </head>
+  <body>
+    <script src="/common/utils.js"></script>
+    <script src="/common/get-host-info.sub.js"></script>
+    <script>
+var RESOURCES_DIR = "/WebKit/beacon/resources/";
+
+function pollResult(test, id) {
+  var checkUrl = RESOURCES_DIR + "beacon-preflight.py?cmd=get&id=" + id;
+
+  return new Promise(resolve => {
+    step_timeout(test.step_func(() => {
+      fetch(checkUrl).then(response => {
+        response.text().then(body => {
+          resolve(body);
+        });
+      });
+    }), 1000);
+  });
+}
+
+function testCORSPreflightSuccess(what) {
+  var testBase = get_host_info().HTTP_REMOTE_ORIGIN + RESOURCES_DIR;
+  var id = self.token();
+  var testUrl = testBase + "beacon-preflight.py?allowCors=1&cmd=put&id=" + id;
+
+  promise_test(function(test) {
+    assert_true(navigator.sendBeacon(testUrl, what), "SendBeacon Succeeded");
+    return pollResult(test, id) .then(json => {
+      result = JSON.parse(json);
+      assert_equals(result['preflight'], 1, "Received preflight")
+      assert_equals(result['preflight_referer'], document.URL, "Preflight referer header")
+      assert_equals(result['preflight_requested_method'], "POST", "Preflight requested method")
+      let requested_headers = result['preflight_requested_headers'].toLowerCase()
+      assert_false(requested_headers.includes("content-type"), "Content-Type header is not requested")
+      assert_true(requested_headers.includes("referer"), "Referer header is requested")
+      assert_true(requested_headers.includes("origin"), "Origin header is requested")
+      assert_equals(result['beacon'], 1, "Received beacon")
+    });
+  }, "CORS preflight success test");
+}
+
+function stringToArrayBufferView(input) {
+  var buffer = new ArrayBuffer(input.length * 2);
+  var view = new Uint16Array(buffer);
+
+  // dumbly copy over the bytes
+  for (var i = 0, len = input.length; i < len; i++) {
+    view[i] = input.charCodeAt(i);
+  }
+  return view;
+}
+
+testCORSPreflightSuccess(stringToArrayBufferView("123"));
+    </script>
+  </body>
+</html>
diff --git a/LayoutTests/http/wpt/beacon/cors/cors-preflight-blob-failure-expected.txt b/LayoutTests/http/wpt/beacon/cors/cors-preflight-blob-failure-expected.txt
new file mode 100644 (file)
index 0000000..11932f7
--- /dev/null
@@ -0,0 +1,3 @@
+
+PASS CORS preflight failure test 
+
diff --git a/LayoutTests/http/wpt/beacon/cors/cors-preflight-blob-failure.html b/LayoutTests/http/wpt/beacon/cors/cors-preflight-blob-failure.html
new file mode 100644 (file)
index 0000000..061fcd2
--- /dev/null
@@ -0,0 +1,54 @@
+<!doctype html>
+<html>
+  <head>
+    <meta charset="utf-8">
+    <title>SendBeacon CORS preflight test</title>
+    <script src=/resources/testharness.js></script>
+    <script src=/resources/testharnessreport.js></script>
+  </head>
+  <body>
+    <script src="/common/utils.js"></script>
+    <script src="/common/get-host-info.sub.js"></script>
+    <script>
+var RESOURCES_DIR = "/WebKit/beacon/resources/";
+
+function pollResult(test, id) {
+  var checkUrl = RESOURCES_DIR + "beacon-preflight.py?cmd=get&id=" + id;
+
+  return new Promise(resolve => {
+    step_timeout(test.step_func(() => {
+      fetch(checkUrl).then(response => {
+        response.text().then(body => {
+          resolve(body);
+        });
+      });
+    }), 1000);
+  });
+}
+
+function testCORSPreflightFailure(what) {
+  var testBase = get_host_info().HTTP_REMOTE_ORIGIN + RESOURCES_DIR;
+  var id = self.token();
+  var testUrl = testBase + "beacon-preflight.py?allowCors=0&cmd=put&id=" + id;
+
+  promise_test(function(test) {
+    assert_true(navigator.sendBeacon(testUrl, what), "SendBeacon Succeeded");
+    return pollResult(test, id) .then(json => {
+      result = JSON.parse(json);
+      assert_equals(result['preflight'], 1, "Received preflight")
+      assert_equals(result['preflight_referer'], document.URL, "Preflight referer header")
+      assert_equals(result['preflight_requested_method'], "POST", "Preflight requested method")
+      let requested_headers = result['preflight_requested_headers'].toLowerCase()
+      assert_true(requested_headers.includes("content-type"), "Content-Type header is requested")
+      assert_true(requested_headers.includes("referer"), "Referer header is requested")
+      assert_true(requested_headers.includes("origin"), "Origin header is requested")
+      assert_equals(result['beacon'], 0, "Did not receive beacon")
+    });
+  }, "CORS preflight failure test");
+}
+
+let blob = new Blob(["123"], {type: "application/octet-stream"});
+testCORSPreflightFailure(blob);
+    </script>
+  </body>
+</html>
diff --git a/LayoutTests/http/wpt/beacon/cors/cors-preflight-blob-success-expected.txt b/LayoutTests/http/wpt/beacon/cors/cors-preflight-blob-success-expected.txt
new file mode 100644 (file)
index 0000000..1544982
--- /dev/null
@@ -0,0 +1,3 @@
+
+PASS CORS preflight success test 
+
diff --git a/LayoutTests/http/wpt/beacon/cors/cors-preflight-blob-success.html b/LayoutTests/http/wpt/beacon/cors/cors-preflight-blob-success.html
new file mode 100644 (file)
index 0000000..a0d9092
--- /dev/null
@@ -0,0 +1,54 @@
+<!doctype html>
+<html>
+  <head>
+    <meta charset="utf-8">
+    <title>SendBeacon CORS preflight test</title>
+    <script src=/resources/testharness.js></script>
+    <script src=/resources/testharnessreport.js></script>
+  </head>
+  <body>
+    <script src="/common/utils.js"></script>
+    <script src="/common/get-host-info.sub.js"></script>
+    <script>
+var RESOURCES_DIR = "/WebKit/beacon/resources/";
+
+function pollResult(test, id) {
+  var checkUrl = RESOURCES_DIR + "beacon-preflight.py?cmd=get&id=" + id;
+
+  return new Promise(resolve => {
+    step_timeout(test.step_func(() => {
+      fetch(checkUrl).then(response => {
+        response.text().then(body => {
+          resolve(body);
+        });
+      });
+    }), 1000);
+  });
+}
+
+function testCORSPreflightSuccess(what) {
+  var testBase = get_host_info().HTTP_REMOTE_ORIGIN + RESOURCES_DIR;
+  var id = self.token();
+  var testUrl = testBase + "beacon-preflight.py?allowCors=1&cmd=put&id=" + id;
+
+  promise_test(function(test) {
+    assert_true(navigator.sendBeacon(testUrl, what), "SendBeacon Succeeded");
+    return pollResult(test, id) .then(json => {
+      result = JSON.parse(json);
+      assert_equals(result['preflight'], 1, "Received preflight")
+      assert_equals(result['preflight_referer'], document.URL, "Preflight referer header")
+      assert_equals(result['preflight_requested_method'], "POST", "Preflight requested method")
+      let requested_headers = result['preflight_requested_headers'].toLowerCase()
+      assert_true(requested_headers.includes("content-type"), "Content-Type header is requested")
+      assert_true(requested_headers.includes("referer"), "Referer header is requested")
+      assert_true(requested_headers.includes("origin"), "Origin header is requested")
+      assert_equals(result['beacon'], 1, "Received beacon")
+    });
+  }, "CORS preflight success test");
+}
+
+let blob = new Blob(["123"], {type: "application/octet-stream"});
+testCORSPreflightSuccess(blob);
+    </script>
+  </body>
+</html>
diff --git a/LayoutTests/http/wpt/beacon/cors/cors-preflight-cookie-expected.txt b/LayoutTests/http/wpt/beacon/cors/cors-preflight-cookie-expected.txt
new file mode 100644 (file)
index 0000000..2f6a37e
--- /dev/null
@@ -0,0 +1,4 @@
+
+
+PASS CORS preflight success test 
+
diff --git a/LayoutTests/http/wpt/beacon/cors/cors-preflight-cookie.html b/LayoutTests/http/wpt/beacon/cors/cors-preflight-cookie.html
new file mode 100644 (file)
index 0000000..b7957ee
--- /dev/null
@@ -0,0 +1,64 @@
+<!doctype html>
+<html>
+  <head>
+    <meta charset="utf-8">
+    <title>SendBeacon CORS preflight test with cookie</title>
+    <script src=/resources/testharness.js></script>
+    <script src=/resources/testharnessreport.js></script>
+  </head>
+  <body>
+    <script src="/common/utils.js"></script>
+    <script src="/common/get-host-info.sub.js"></script>
+    <script>
+var RESOURCES_DIR = "/WebKit/beacon/resources/";
+
+if (window.testRunner)
+  testRunner.setAlwaysAcceptCookies(true);
+
+function pollResult(test, id) {
+  var checkUrl = RESOURCES_DIR + "beacon-preflight.py?cmd=get&id=" + id;
+
+  return new Promise(resolve => {
+    step_timeout(test.step_func(() => {
+      fetch(checkUrl).then(response => {
+        response.text().then(body => {
+          resolve(body);
+        });
+      });
+    }), 1000);
+  });
+}
+
+function fetchCORSCookie(testBase, name, path) {
+  return new Promise(resolve => {
+    let frame = document.createElement("iframe");
+    frame.src = testBase + "set-cookie.py?name=" + encodeURIComponent(name) + "&path=" + encodeURIComponent(path);
+    frame.onload = function() { resolve(); };
+    document.body.append(frame);
+  });
+}
+
+function testCORSPreflightSuccessWithCookie(what) {
+  var testBase = get_host_info().HTTP_REMOTE_ORIGIN + RESOURCES_DIR;
+  var id = self.token();
+  var testUrl = testBase + "beacon-preflight.py?allowCors=1&cmd=put&id=" + id; 
+
+  promise_test(function(test) {
+    return fetchCORSCookie(testBase, "testCookie", "/").then(() => {
+      assert_true(navigator.sendBeacon(testUrl, what), "SendBeacon Succeeded");
+      return pollResult(test, id).then(json => {
+        result = JSON.parse(json);
+        assert_equals(result['preflight'], 1, "Received preflight")
+        assert_equals(result['preflight_cookie_header'], "", "Preflight cookie header")
+        assert_equals(result['beacon'], 1, "Received beacon")
+        assert_equals(result['beacon_cookie_header'], "testCookie=1", "Beacon Cookie header")
+      });
+    });
+  }, "CORS preflight success test");
+}
+
+let blob = new Blob(["123"], {type: "application/octet-stream"});
+testCORSPreflightSuccessWithCookie(blob);
+    </script>
+  </body>
+</html>
diff --git a/LayoutTests/http/wpt/beacon/resources/beacon-preflight.py b/LayoutTests/http/wpt/beacon/resources/beacon-preflight.py
new file mode 100644 (file)
index 0000000..cce4cd1
--- /dev/null
@@ -0,0 +1,52 @@
+import json
+
+def respondToCORSPreflight(request, response):
+  allow_cors = int(request.GET.first("allowCors", 0)) != 0;
+  
+  if not allow_cors:
+    response.set_error(400, "Not allowed")
+    return "ERROR: Not allowed"
+  
+  if not "Access-Control-Request-Method" in request.headers:
+    response.set_error(400, "No Access-Control-Request-Method header")
+    return "ERROR: No access-control-request-method in preflight!"
+  
+  headers = [("Content-Type", "text/plain")]
+  headers.append(("Access-Control-Allow-Origin", request.headers.get("Origin", "*")))
+  headers.append(("Access-Control-Allow-Credentials", "true"))
+  requested_method = request.headers.get("Access-Control-Request-Method", None)
+  headers.append(("Access-Control-Allow-Methods", requested_method))
+  requested_headers = request.headers.get("Access-Control-Request-Headers", None)
+  headers.append(("Access-Control-Allow-Headers", requested_headers))
+  headers.append(("Access-Control-Max-Age", "60"))
+  return headers, ""
+
+def main(request, response):
+  command = request.GET.first("cmd").lower();
+  test_id = request.GET.first("id")
+  stashed_data = request.server.stash.take(test_id)
+  if stashed_data is None:
+    stashed_data = { 'preflight': 0, 'beacon': 0, 'preflight_requested_method': '', 'preflight_requested_headers': '', 'preflight_referrer': '', 'preflight_cookie_header': '', 'beacon_cookie_header': '' }
+
+  if command == "put":
+    if request.method == "OPTIONS":
+      stashed_data['preflight'] = 1;
+      stashed_data['preflight_requested_method'] = request.headers.get("Access-Control-Request-Method", "")
+      stashed_data['preflight_requested_headers'] = request.headers.get("Access-Control-Request-Headers", "")
+      stashed_data['preflight_cookie_header'] = request.headers.get("Cookie", "");
+      stashed_data['preflight_referer'] = request.headers.get("Referer", "")
+      request.server.stash.put(test_id, stashed_data)
+      return respondToCORSPreflight(request, response)
+    elif request.method == "POST":
+      stashed_data['beacon'] = 1;
+      stashed_data['beacon_cookie_header'] = request.headers.get("Cookie", "")
+      request.server.stash.put(test_id, stashed_data)
+    return [("Content-Type", "text/plain")], ""
+  
+  if command == "get":
+    if stashed_data is not None:
+      return [("Content-Type", "text/plain")], json.dumps(stashed_data)
+    return [("Content-Type", "text/plain")], ""
+
+  response.set_error(400, "Bad Command")
+  return "ERROR: Bad Command!"
diff --git a/LayoutTests/http/wpt/beacon/resources/set-cookie.py b/LayoutTests/http/wpt/beacon/resources/set-cookie.py
new file mode 100644 (file)
index 0000000..12964ea
--- /dev/null
@@ -0,0 +1,14 @@
+
+import sys
+import urlparse
+
+def main(request, response):
+    params = urlparse.parse_qs(request.url_parts.query)
+    headers = [
+        ("Content-Type", "application/json"),
+        ("Cache-Control", "no-store"),
+        ("Access-Control-Allow-Origin", "*"),
+        ("Set-Cookie", "{name[0]}=1; Path={path[0]}; Expires=Wed, 09 Jun 2021 10:18:14 GMT".format(**params))
+    ]
+    body = "{}"
+    return headers, body
index 95ca42e..239036e 100644 (file)
@@ -360,6 +360,9 @@ css3/viewport-percentage-lengths/vh-auto-size.html [ Skip ]
 
 webkit.org/b/170877 [ Debug ] webgl/1.0.2/conformance/glsl/misc/shader-with-reserved-words.html [ Pass Timeout ]
 
+# CORS-preflighting for Beacon is not supported on WK1.
+webkit.org/b/175330 http/wpt/beacon/cors/ [ Skip ]
+
 # This was a WK2-only fix.
 http/tests/css/filters-on-iframes.html [ Skip ]
 
index 8e6e13a..10cd10a 100644 (file)
@@ -722,6 +722,10 @@ webkit.org/b/172834 [ ElCapitan ] imported/w3c/web-platform-tests/IndexedDB/idbi
 
 webkit.org/b/172834 [ ElCapitan ] imported/w3c/web-platform-tests/IndexedDB/idbobjectstore_getAllKeys.html [ Pass Failure ]
 
+
+# CORS-preflighting for Beacon is not supported on WK2 for non NETWORK_SESSION code path.
+webkit.org/b/175330 [ ElCapitan ] http/wpt/beacon/cors/ [ Skip ]
+
 webkit.org/b/172201 webaudio/silent-audio-interrupted-in-background.html [ Pass Timeout ]
 
 webkit.org/b/167757 workers/bomb.html [ Pass Timeout ]
index 81e25ac..1120c80 100644 (file)
@@ -4047,6 +4047,9 @@ svg/animations/animated-svg-image-outside-viewport-paused.html [ Skip ]
 # This test requires Skia, which isn't available on Windows.
 webkit.org/b/174079 fast/text/variations/skia-postscript-name.html [ ImageOnlyFailure ]
 
+# CORS-preflighting for Beacon is not supported on WK1.
+webkit.org/b/175330 http/wpt/beacon/cors/ [ Skip ]
+
 # Async image tests are currently failing on Windows.
 webkit.org/b/174653 fast/images/async-image-background-image-repeated.html [ Timeout ]
 webkit.org/b/174653 fast/images/async-image-background-image.html [ Timeout ]
index f075404..5504f8f 100644 (file)
@@ -1,3 +1,38 @@
+2017-08-08  Chris Dumez  <cdumez@apple.com>
+
+        [Beacon] Add support for CORS-preflighting for WK2 / NETWORK_SESSION
+        https://bugs.webkit.org/show_bug.cgi?id=175264
+        <rdar://problem/33547793>
+
+        Reviewed by Youenn Fablet.
+
+        Pass additional information when creating a PingHandle so that the PingLoad
+        can deal with CORS-preflighting on Network process side.
+
+        Tests: http/wpt/beacon/cors/cors-preflight-arraybufferview-failure.html
+               http/wpt/beacon/cors/cors-preflight-arraybufferview-success.html
+               http/wpt/beacon/cors/cors-preflight-blob-failure.html
+               http/wpt/beacon/cors/cors-preflight-blob-success.html
+               http/wpt/beacon/cors/cors-preflight-cookie.html
+
+        * WebCore.xcodeproj/project.pbxproj:
+        * loader/CrossOriginAccessControl.cpp:
+        (WebCore::validatePreflightResponse):
+        * loader/CrossOriginAccessControl.h:
+        * loader/CrossOriginPreflightChecker.cpp:
+        (WebCore::CrossOriginPreflightChecker::validatePreflightResponse):
+        * loader/CrossOriginPreflightResultCache.h:
+        * loader/LoaderStrategy.h:
+        * loader/PingLoader.cpp:
+        (WebCore::PingLoader::loadImage):
+        (WebCore::PingLoader::sendPing):
+        (WebCore::PingLoader::sendViolationReport):
+        (WebCore::PingLoader::startPingLoad):
+        * loader/PingLoader.h:
+        * loader/cache/CachedResource.cpp:
+        (WebCore::CachedResource::load):
+        * page/SecurityOrigin.h:
+
 2017-08-08  Sam Weinig  <sam@webkit.org>
 
         Address review feedback from https://bugs.webkit.org/show_bug.cgi?id=175246.
index 11462b9..3c4dd0d 100644 (file)
                E1C36D350EB0A094007410BC /* JSWorkerGlobalScopeBase.h in Headers */ = {isa = PBXBuildFile; fileRef = E1C36D330EB0A094007410BC /* JSWorkerGlobalScopeBase.h */; };
                E1C415DA0F655D6F0092D2FB /* CrossOriginPreflightResultCache.h in Headers */ = {isa = PBXBuildFile; fileRef = E1C415D90F655D6F0092D2FB /* CrossOriginPreflightResultCache.h */; settings = {ATTRIBUTES = (Private, ); }; };
                E1C415DE0F655D7C0092D2FB /* CrossOriginPreflightResultCache.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E1C415DD0F655D7C0092D2FB /* CrossOriginPreflightResultCache.cpp */; };
-               E1C416120F6562FD0092D2FB /* CrossOriginAccessControl.h in Headers */ = {isa = PBXBuildFile; fileRef = E1C416110F6562FD0092D2FB /* CrossOriginAccessControl.h */; };
+               E1C416120F6562FD0092D2FB /* CrossOriginAccessControl.h in Headers */ = {isa = PBXBuildFile; fileRef = E1C416110F6562FD0092D2FB /* CrossOriginAccessControl.h */; settings = {ATTRIBUTES = (Private, ); }; };
                E1C416170F6563180092D2FB /* CrossOriginAccessControl.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E1C416160F6563180092D2FB /* CrossOriginAccessControl.cpp */; };
                E1C4DE690EA75C1E0023CCD6 /* ActiveDOMObject.h in Headers */ = {isa = PBXBuildFile; fileRef = E1C4DE680EA75C1E0023CCD6 /* ActiveDOMObject.h */; settings = {ATTRIBUTES = (Private, ); }; };
                E1C4DE6E0EA75C650023CCD6 /* ActiveDOMObject.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E1C4DE6D0EA75C650023CCD6 /* ActiveDOMObject.cpp */; };
index 18bcea4..f4f6c0a 100644 (file)
@@ -27,6 +27,7 @@
 #include "config.h"
 #include "CrossOriginAccessControl.h"
 
+#include "CrossOriginPreflightResultCache.h"
 #include "HTTPHeaderNames.h"
 #include "HTTPParsers.h"
 #include "ResourceRequest.h"
@@ -156,4 +157,25 @@ bool passesAccessControlCheck(const ResourceResponse& response, StoredCredential
     return true;
 }
 
+bool validatePreflightResponse(const ResourceRequest& request, const ResourceResponse& response, StoredCredentials includeCredentials, SecurityOrigin& securityOrigin, String& errorDescription)
+{
+    if (!response.isSuccessful()) {
+        errorDescription = ASCIILiteral("Preflight response is not successful");
+        return false;
+    }
+
+    if (!passesAccessControlCheck(response, includeCredentials, securityOrigin, errorDescription))
+        return false;
+
+    auto result = std::make_unique<CrossOriginPreflightResultCacheItem>(includeCredentials);
+    if (!result->parse(response, errorDescription)
+        || !result->allowsCrossOriginMethod(request.httpMethod(), errorDescription)
+        || !result->allowsCrossOriginHeaders(request.httpHeaderFields(), errorDescription)) {
+        return false;
+    }
+
+    CrossOriginPreflightResultCache::singleton().appendEntry(securityOrigin.toString(), request.url(), WTFMove(result));
+    return true;
+}
+
 } // namespace WebCore
index 0d684b3..5e87b1f 100644 (file)
@@ -41,11 +41,12 @@ bool isSimpleCrossOriginAccessRequest(const String& method, const HTTPHeaderMap&
 bool isOnAccessControlSimpleRequestMethodWhitelist(const String&);
 
 void updateRequestForAccessControl(ResourceRequest&, SecurityOrigin&, StoredCredentials);
-ResourceRequest createAccessControlPreflightRequest(const ResourceRequest&, SecurityOrigin&, const String&);
+WEBCORE_EXPORT ResourceRequest createAccessControlPreflightRequest(const ResourceRequest&, SecurityOrigin&, const String&);
 
 bool isValidCrossOriginRedirectionURL(const URL&);
 void cleanRedirectedRequestForAccessControl(ResourceRequest&);
 
-bool passesAccessControlCheck(const ResourceResponse&, StoredCredentials, SecurityOrigin&, String& errorDescription);
+WEBCORE_EXPORT bool passesAccessControlCheck(const ResourceResponse&, StoredCredentials, SecurityOrigin&, String& errorDescription);
+WEBCORE_EXPORT bool validatePreflightResponse(const ResourceRequest&, const ResourceResponse&, StoredCredentials, SecurityOrigin&, String& errorDescription);
 
 } // namespace WebCore
index 6cfd427..a6e309c 100644 (file)
@@ -60,27 +60,14 @@ CrossOriginPreflightChecker::~CrossOriginPreflightChecker()
 
 void CrossOriginPreflightChecker::validatePreflightResponse(DocumentThreadableLoader& loader, ResourceRequest&& request, unsigned long identifier, const ResourceResponse& response)
 {
-    Frame* frame = loader.document().frame();
-    ASSERT(frame);
-
-    if (!response.isSuccessful()) {
-        loader.preflightFailure(identifier, ResourceError(errorDomainWebKitInternal, 0, request.url(), ASCIILiteral("Preflight response is not successful"), ResourceError::Type::AccessControl));
+    String errorDescription;
+    if (!WebCore::validatePreflightResponse(request, response, loader.options().allowCredentials, loader.securityOrigin(), errorDescription)) {
+        loader.preflightFailure(identifier, ResourceError(errorDomainWebKitInternal, 0, request.url(), errorDescription, ResourceError::Type::AccessControl));
         return;
     }
 
-    String description;
-    if (!passesAccessControlCheck(response, loader.options().allowCredentials, loader.securityOrigin(), description)) {
-        loader.preflightFailure(identifier, ResourceError(errorDomainWebKitInternal, 0, request.url(), description, ResourceError::Type::AccessControl));
-        return;
-    }
-
-    auto result = std::make_unique<CrossOriginPreflightResultCacheItem>(loader.options().allowCredentials);
-    if (!result->parse(response, description)
-        || !result->allowsCrossOriginMethod(request.httpMethod(), description)
-        || !result->allowsCrossOriginHeaders(request.httpHeaderFields(), description)) {
-        loader.preflightFailure(identifier, ResourceError(errorDomainWebKitInternal, 0, request.url(), description, ResourceError::Type::AccessControl));
-        return;
-    }
+    Frame* frame = loader.document().frame();
+    ASSERT(frame);
 
     // FIXME: <https://webkit.org/b/164889> Web Inspector: Show Preflight Request information in inspector
     // This is only showing success preflight requests and responses but we should show network events
@@ -89,7 +76,6 @@ void CrossOriginPreflightChecker::validatePreflightResponse(DocumentThreadableLo
     InspectorInstrumentation::didReceiveResourceResponse(*frame, identifier, frame->loader().documentLoader(), response, nullptr);
     InspectorInstrumentation::didFinishLoading(frame, frame->loader().documentLoader(), identifier, emptyMetrics, nullptr);
 
-    CrossOriginPreflightResultCache::singleton().appendEntry(loader.securityOrigin().toString(), request.url(), WTFMove(result));
     loader.preflightSuccess(WTFMove(request));
 }
 
index 094496e..c8d0100 100644 (file)
@@ -46,9 +46,9 @@ public:
     {
     }
 
-    bool parse(const ResourceResponse&, String& errorDescription);
-    bool allowsCrossOriginMethod(const String&, String& errorDescription) const;
-    bool allowsCrossOriginHeaders(const HTTPHeaderMap&, String& errorDescription) const;
+    WEBCORE_EXPORT bool parse(const ResourceResponse&, String& errorDescription);
+    WEBCORE_EXPORT bool allowsCrossOriginMethod(const String&, String& errorDescription) const;
+    WEBCORE_EXPORT bool allowsCrossOriginHeaders(const HTTPHeaderMap&, String& errorDescription) const;
     bool allowsRequest(StoredCredentials, const String& method, const HTTPHeaderMap& requestHeaders) const;
 
 private:
@@ -67,8 +67,8 @@ class CrossOriginPreflightResultCache {
 public:
     WEBCORE_EXPORT static CrossOriginPreflightResultCache& singleton();
 
-    void appendEntry(const String& origin, const URL&, std::unique_ptr<CrossOriginPreflightResultCacheItem>);
-    bool canSkipPreflight(const String& origin, const URL&, StoredCredentials, const String& method, const HTTPHeaderMap& requestHeaders);
+    WEBCORE_EXPORT void appendEntry(const String& origin, const URL&, std::unique_ptr<CrossOriginPreflightResultCacheItem>);
+    WEBCORE_EXPORT bool canSkipPreflight(const String& origin, const URL&, StoredCredentials, const String& method, const HTTPHeaderMap& requestHeaders);
 
     WEBCORE_EXPORT void empty();
 
index 2871a21..863aa9c 100644 (file)
@@ -42,10 +42,13 @@ class ResourceError;
 class ResourceLoader;
 class ResourceRequest;
 class ResourceResponse;
+class SecurityOrigin;
 class SharedBuffer;
 class SubresourceLoader;
 class URL;
 
+struct FetchOptions;
+
 class WEBCORE_EXPORT LoaderStrategy {
 public:
     virtual RefPtr<SubresourceLoader> loadResource(Frame&, CachedResource&, const ResourceRequest&, const ResourceLoaderOptions&) = 0;
@@ -59,7 +62,7 @@ public:
     virtual void suspendPendingRequests() = 0;
     virtual void resumePendingRequests() = 0;
 
-    virtual void createPingHandle(NetworkingContext*, ResourceRequest&, bool shouldUseCredentialStorage, bool shouldFollowRedirects) = 0;
+    virtual void createPingHandle(NetworkingContext*, ResourceRequest&, Ref<SecurityOrigin>&& sourceOrigin, const FetchOptions&) = 0;
 
     virtual void storeDerivedDataToCache(const SHA1::Digest& bodyKey, const String& type, const String& partition, WebCore::SharedBuffer&) = 0;
 
index 865eb1c..89bbade 100644 (file)
@@ -103,7 +103,7 @@ void PingLoader::loadImage(Frame& frame, const URL& url)
         request.setHTTPReferrer(referrer);
     frame.loader().addExtraFieldsToSubresourceRequest(request);
 
-    startPingLoad(frame, request, ShouldFollowRedirects::Yes);
+    startPingLoad(frame, request, document.securityOrigin(), ShouldFollowRedirects::Yes);
 }
 
 // http://www.whatwg.org/specs/web-apps/current-work/multipage/links.html#hyperlink-auditing
@@ -139,7 +139,7 @@ void PingLoader::sendPing(Frame& frame, const URL& pingURL, const URL& destinati
         }
     }
 
-    startPingLoad(frame, request, ShouldFollowRedirects::Yes);
+    startPingLoad(frame, request, sourceOrigin, ShouldFollowRedirects::Yes);
 }
 
 void PingLoader::sendViolationReport(Frame& frame, const URL& reportURL, Ref<FormData>&& report, ViolationReportType reportType)
@@ -176,10 +176,10 @@ void PingLoader::sendViolationReport(Frame& frame, const URL& reportURL, Ref<For
     if (!referrer.isEmpty())
         request.setHTTPReferrer(referrer);
 
-    startPingLoad(frame, request, ShouldFollowRedirects::No);
+    startPingLoad(frame, request, document.securityOrigin(), ShouldFollowRedirects::No);
 }
 
-void PingLoader::startPingLoad(Frame& frame, ResourceRequest& request, ShouldFollowRedirects shouldFollowRedirects)
+void PingLoader::startPingLoad(Frame& frame, ResourceRequest& request, SecurityOrigin& sourceOrigin, ShouldFollowRedirects shouldFollowRedirects)
 {
     unsigned long identifier = frame.page()->progress().createUniqueIdentifier();
     // FIXME: Why activeDocumentLoader? I would have expected documentLoader().
@@ -188,10 +188,13 @@ void PingLoader::startPingLoad(Frame& frame, ResourceRequest& request, ShouldFol
     // with the provisional DocumentLoader if there is a provisional
     // DocumentLoader.
     bool shouldUseCredentialStorage = frame.loader().client().shouldUseCredentialStorage(frame.loader().activeDocumentLoader(), identifier);
+    FetchOptions options;
+    options.credentials = shouldUseCredentialStorage ? FetchOptions::Credentials::Include : FetchOptions::Credentials::Omit;
+    options.redirect = shouldFollowRedirects == ShouldFollowRedirects::Yes ? FetchOptions::Redirect::Follow : FetchOptions::Redirect::Error;
 
     InspectorInstrumentation::continueAfterPingLoader(frame, identifier, frame.loader().activeDocumentLoader(), request, ResourceResponse());
 
-    platformStrategies()->loaderStrategy()->createPingHandle(frame.loader().networkingContext(), request, shouldUseCredentialStorage, shouldFollowRedirects == ShouldFollowRedirects::Yes);
+    platformStrategies()->loaderStrategy()->createPingHandle(frame.loader().networkingContext(), request, sourceOrigin, options);
 }
 
 }
index b7937de..97354fb 100644 (file)
@@ -41,6 +41,7 @@ class FormData;
 class Frame;
 class URL;
 class ResourceRequest;
+class SecurityOrigin;
 
 enum class ViolationReportType {
     ContentSecurityPolicy,
@@ -55,7 +56,7 @@ public:
 
 private:
     enum class ShouldFollowRedirects { No, Yes };
-    static void startPingLoad(Frame&, ResourceRequest&, ShouldFollowRedirects);
+    static void startPingLoad(Frame&, ResourceRequest&, SecurityOrigin& sourceOrigin, ShouldFollowRedirects);
 };
 
 } // namespace WebCore
index 5fff7a9..b5b0c8b 100644 (file)
@@ -261,7 +261,8 @@ void CachedResource::load(CachedResourceLoader& cachedResourceLoader)
 
     // FIXME: We should not special-case Beacon here.
     if (m_options.keepAlive && type() == CachedResource::Beacon) {
-        platformStrategies()->loaderStrategy()->createPingHandle(frame.loader().networkingContext(), request, m_options.credentials == FetchOptions::Credentials::Include, m_options.redirect == FetchOptions::Redirect::Follow);
+        ASSERT(m_origin);
+        platformStrategies()->loaderStrategy()->createPingHandle(frame.loader().networkingContext(), request, *m_origin, m_options);
         return;
     }
 
index d56a6ff..0ead4bc 100644 (file)
@@ -97,7 +97,7 @@ public:
     // Returns true if this SecurityOrigin can read content retrieved from
     // the given URL. For example, call this function before issuing
     // XMLHttpRequests.
-    bool canRequest(const URL&) const;
+    WEBCORE_EXPORT bool canRequest(const URL&) const;
 
     // Returns true if this SecurityOrigin can receive drag content from the
     // initiator. For example, call this function before allowing content to be
index 4bb1205..334f668 100644 (file)
@@ -100,6 +100,7 @@ set(WebKit2_SOURCES
 
     NetworkProcess/FileAPI/NetworkBlobRegistry.cpp
 
+    NetworkProcess/NetworkCORSPreflightChecker.cpp
     NetworkProcess/NetworkConnectionToWebProcess.cpp
     NetworkProcess/NetworkDataTask.cpp
     NetworkProcess/NetworkDataTaskBlob.cpp
@@ -111,6 +112,7 @@ set(WebKit2_SOURCES
     NetworkProcess/NetworkResourceLoader.cpp
     NetworkProcess/NetworkSession.cpp
     NetworkProcess/NetworkSocketStream.cpp
+    NetworkProcess/PingLoad.cpp
 
     NetworkProcess/cache/NetworkCache.cpp
     NetworkProcess/cache/NetworkCacheBlobStorage.cpp
index ddf0be0..9fb541c 100644 (file)
@@ -1,3 +1,66 @@
+2017-08-08  Chris Dumez  <cdumez@apple.com>
+
+        [Beacon] Add support for CORS-preflighting for WK2 / NETWORK_SESSION
+        https://bugs.webkit.org/show_bug.cgi?id=175264
+        <rdar://problem/33547793>
+
+        Reviewed by Youenn Fablet.
+
+        Implement CORS-preflighting for beacons with a payload that has a non
+        safelisted MIME type, as per:
+        - https://w3c.github.io/beacon/#privacy
+        - https://www.w3.org/TR/beacon/#sec-processing-model
+
+        CORS-preflighting is completely handled on Network Process side because
+        a beacon request can outlive its page and therefore its WebContent
+        process. This requires us to pass a little more information to the
+        Network process, in particular the source origin and the corsMode.
+
+        The current implementation does not currently deal with CORS preflights
+        needed upon a redirect. This will be added in a follow-up.
+
+        * CMakeLists.txt:
+        * NetworkProcess/NetworkCORSPreflightChecker.cpp: Added.
+        (WebKit::NetworkCORSPreflightChecker::NetworkCORSPreflightChecker):
+        (WebKit::NetworkCORSPreflightChecker::~NetworkCORSPreflightChecker):
+        (WebKit::NetworkCORSPreflightChecker::startPreflight):
+        (WebKit::NetworkCORSPreflightChecker::willPerformHTTPRedirection):
+        (WebKit::NetworkCORSPreflightChecker::didReceiveChallenge):
+        (WebKit::NetworkCORSPreflightChecker::didReceiveResponseNetworkSession):
+        (WebKit::NetworkCORSPreflightChecker::didReceiveData):
+        (WebKit::NetworkCORSPreflightChecker::didCompleteWithError):
+        (WebKit::NetworkCORSPreflightChecker::didSendData):
+        (WebKit::NetworkCORSPreflightChecker::wasBlocked):
+        (WebKit::NetworkCORSPreflightChecker::cannotShowURL):
+        * NetworkProcess/NetworkCORSPreflightChecker.h: Added.
+        * NetworkProcess/NetworkConnectionToWebProcess.cpp:
+        (WebKit::NetworkConnectionToWebProcess::loadPing):
+        * NetworkProcess/NetworkConnectionToWebProcess.h:
+        * NetworkProcess/NetworkResourceLoadParameters.cpp:
+        (WebKit::NetworkResourceLoadParameters::encode const):
+        (WebKit::NetworkResourceLoadParameters::decode):
+        * NetworkProcess/NetworkResourceLoadParameters.h:
+        * NetworkProcess/PingLoad.cpp: Added.
+        (WebKit::PingLoad::PingLoad):
+        (WebKit::PingLoad::~PingLoad):
+        (WebKit::PingLoad::startNetworkLoad):
+        (WebKit::PingLoad::willPerformHTTPRedirection):
+        (WebKit::PingLoad::didReceiveChallenge):
+        (WebKit::PingLoad::didReceiveResponseNetworkSession):
+        (WebKit::PingLoad::didReceiveData):
+        (WebKit::PingLoad::didCompleteWithError):
+        (WebKit::PingLoad::didSendData):
+        (WebKit::PingLoad::wasBlocked):
+        (WebKit::PingLoad::cannotShowURL):
+        (WebKit::PingLoad::timeoutTimerFired):
+        (WebKit::PingLoad::needsCORSPreflight const):
+        (WebKit::PingLoad::doCORSPreflight):
+        * NetworkProcess/PingLoad.h:
+        * WebKit.xcodeproj/project.pbxproj:
+        * WebProcess/Network/WebLoaderStrategy.cpp:
+        (WebKit::WebLoaderStrategy::createPingHandle):
+        * WebProcess/Network/WebLoaderStrategy.h:
+
 2017-08-08  Megan Gardner  <megan_gardner@apple.com>
 
         Remove old and unused pointIsInAssistedNode definition
diff --git a/Source/WebKit/NetworkProcess/NetworkCORSPreflightChecker.cpp b/Source/WebKit/NetworkProcess/NetworkCORSPreflightChecker.cpp
new file mode 100644 (file)
index 0000000..c21012e
--- /dev/null
@@ -0,0 +1,143 @@
+/*
+ * Copyright (C) 2017 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "NetworkCORSPreflightChecker.h"
+
+#if USE(NETWORK_SESSION)
+
+#include "AuthenticationManager.h"
+#include "Logging.h"
+#include "NetworkLoadParameters.h"
+#include "SessionTracker.h"
+#include <WebCore/CrossOriginAccessControl.h>
+#include <WebCore/CrossOriginPreflightResultCache.h>
+#include <WebCore/SecurityOrigin.h>
+
+#define RELEASE_LOG_IF_ALLOWED(fmt, ...) RELEASE_LOG_IF(m_parameters.sessionID.isAlwaysOnLoggingAllowed(), Network, "%p - NetworkCORSPreflightChecker::" fmt, this, ##__VA_ARGS__)
+
+namespace WebKit {
+
+using namespace WebCore;
+
+NetworkCORSPreflightChecker::NetworkCORSPreflightChecker(Parameters&& parameters, CompletionCallback&& completionCallback)
+    : m_parameters(WTFMove(parameters))
+    , m_completionCallback(WTFMove(completionCallback))
+{
+}
+
+NetworkCORSPreflightChecker::~NetworkCORSPreflightChecker()
+{
+    if (m_task) {
+        ASSERT(m_task->client() == this);
+        m_task->clearClient();
+        m_task->cancel();
+    }
+}
+
+void NetworkCORSPreflightChecker::startPreflight()
+{
+    RELEASE_LOG_IF_ALLOWED("startPreflight");
+    if (CrossOriginPreflightResultCache::singleton().canSkipPreflight(m_parameters.sourceOrigin->toString(), m_parameters.originalRequest.url(), m_parameters.allowStoredCredentials, m_parameters.originalRequest.httpMethod(), m_parameters.originalRequest.httpHeaderFields())) {
+        RELEASE_LOG_IF_ALLOWED("startPreflight - preflight can be skipped thanks to cached result");
+        m_completionCallback(Result::Success);
+        return;
+    }
+
+    NetworkLoadParameters loadParameters;
+    loadParameters.sessionID = m_parameters.sessionID;
+    loadParameters.request = createAccessControlPreflightRequest(m_parameters.originalRequest, m_parameters.sourceOrigin, m_parameters.originalRequest.httpReferrer());
+    loadParameters.shouldFollowRedirects = false;
+    if (auto* networkSession = SessionTracker::networkSession(loadParameters.sessionID)) {
+        m_task = NetworkDataTask::create(*networkSession, *this, WTFMove(loadParameters));
+        m_task->resume();
+    } else
+        ASSERT_NOT_REACHED();
+}
+
+void NetworkCORSPreflightChecker::willPerformHTTPRedirection(WebCore::ResourceResponse&&, WebCore::ResourceRequest&&, RedirectCompletionHandler&& completionHandler)
+{
+    RELEASE_LOG_IF_ALLOWED("willPerformHTTPRedirection");
+    completionHandler({ });
+    m_completionCallback(Result::Failure);
+}
+
+void NetworkCORSPreflightChecker::didReceiveChallenge(const WebCore::AuthenticationChallenge&, ChallengeCompletionHandler&& completionHandler)
+{
+    RELEASE_LOG_IF_ALLOWED("didReceiveChallenge");
+    completionHandler(AuthenticationChallengeDisposition::Cancel, { });
+    m_completionCallback(Result::Failure);
+}
+
+void NetworkCORSPreflightChecker::didReceiveResponseNetworkSession(WebCore::ResourceResponse&& response, ResponseCompletionHandler&& completionHandler)
+{
+    RELEASE_LOG_IF_ALLOWED("didReceiveResponseNetworkSession");
+    m_response = WTFMove(response);
+    completionHandler(PolicyAction::PolicyUse);
+}
+
+void NetworkCORSPreflightChecker::didReceiveData(Ref<WebCore::SharedBuffer>&&)
+{
+    RELEASE_LOG_IF_ALLOWED("didReceiveData");
+}
+
+void NetworkCORSPreflightChecker::didCompleteWithError(const WebCore::ResourceError& error, const WebCore::NetworkLoadMetrics&)
+{
+    if (!error.isNull()) {
+        RELEASE_LOG_IF_ALLOWED("didCompleteWithError");
+        m_completionCallback(Result::Failure);
+        return;
+    }
+
+    RELEASE_LOG_IF_ALLOWED("didComplete http_status_code: %d", m_response.httpStatusCode());
+
+    String errorDescription;
+    if (!validatePreflightResponse(m_parameters.originalRequest, m_response, m_parameters.allowStoredCredentials, m_parameters.sourceOrigin, errorDescription)) {
+        RELEASE_LOG_IF_ALLOWED("didComplete, AccessControl error: %s", errorDescription.utf8().data());
+        m_completionCallback(Result::Failure);
+        return;
+    }
+    m_completionCallback(Result::Success);
+}
+
+void NetworkCORSPreflightChecker::didSendData(uint64_t totalBytesSent, uint64_t totalBytesExpectedToSend)
+{
+}
+
+void NetworkCORSPreflightChecker::wasBlocked()
+{
+    RELEASE_LOG_IF_ALLOWED("wasBlocked");
+    m_completionCallback(Result::Failure);
+}
+
+void NetworkCORSPreflightChecker::cannotShowURL()
+{
+    RELEASE_LOG_IF_ALLOWED("cannotShowURL");
+    m_completionCallback(Result::Failure);
+}
+
+} // Namespace WebKit
+
+#endif // USE(NETWORK_SESSION)
diff --git a/Source/WebKit/NetworkProcess/NetworkCORSPreflightChecker.h b/Source/WebKit/NetworkProcess/NetworkCORSPreflightChecker.h
new file mode 100644 (file)
index 0000000..446b67d
--- /dev/null
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2017 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#if USE(NETWORK_SESSION)
+
+#include "NetworkDataTask.h"
+#include <WebCore/ResourceHandleTypes.h>
+#include <WebCore/ResourceRequest.h>
+#include <WebCore/ResourceResponse.h>
+#include <WebCore/SessionID.h>
+#include <wtf/Function.h>
+
+namespace WebCore {
+class SecurityOrigin;
+}
+
+namespace WebKit {
+
+class NetworkCORSPreflightChecker final : private NetworkDataTaskClient  {
+    WTF_MAKE_FAST_ALLOCATED;
+public:
+    struct Parameters {
+        WebCore::ResourceRequest originalRequest;
+        Ref<WebCore::SecurityOrigin> sourceOrigin;
+        WebCore::SessionID sessionID;
+        WebCore::StoredCredentials allowStoredCredentials;
+    };
+    enum class Result { Success, Failure };
+    using CompletionCallback = WTF::Function<void(Result)>;
+
+    NetworkCORSPreflightChecker(Parameters&&, CompletionCallback&&);
+    ~NetworkCORSPreflightChecker();
+
+    void startPreflight();
+
+private:
+    void willPerformHTTPRedirection(WebCore::ResourceResponse&&, WebCore::ResourceRequest&&, RedirectCompletionHandler&&) final;
+    void didReceiveChallenge(const WebCore::AuthenticationChallenge&, ChallengeCompletionHandler&&) final;
+    void didReceiveResponseNetworkSession(WebCore::ResourceResponse&&, ResponseCompletionHandler&&) final;
+    void didReceiveData(Ref<WebCore::SharedBuffer>&&) final;
+    void didCompleteWithError(const WebCore::ResourceError&, const WebCore::NetworkLoadMetrics&) final;
+    void didSendData(uint64_t totalBytesSent, uint64_t totalBytesExpectedToSend) final;
+    void wasBlocked() final;
+    void cannotShowURL() final;
+
+    Parameters m_parameters;
+    WebCore::ResourceResponse m_response;
+    CompletionCallback m_completionCallback;
+    RefPtr<NetworkDataTask> m_task;
+};
+
+} // namespace WebKit
+
+#endif // USE(NETWORK_SESSION)
index f36a981..73ba60c 100644 (file)
@@ -222,11 +222,11 @@ void NetworkConnectionToWebProcess::performSynchronousLoad(const NetworkResource
     loader->start();
 }
 
-void NetworkConnectionToWebProcess::loadPing(const NetworkResourceLoadParameters& loadParameters)
+void NetworkConnectionToWebProcess::loadPing(NetworkResourceLoadParameters&& loadParameters)
 {
 #if USE(NETWORK_SESSION)
     // PingLoad manages its own lifetime, deleting itself when its purpose has been fulfilled.
-    new PingLoad(loadParameters);
+    new PingLoad(WTFMove(loadParameters));
 #else
     RefPtr<NetworkingContext> context = RemoteNetworkingContext::create(loadParameters.sessionID, loadParameters.shouldClearReferrerOnHTTPSToHTTPRedirect);
 
index f5274ce..384cce7 100644 (file)
@@ -82,7 +82,7 @@ private:
 
     void scheduleResourceLoad(const NetworkResourceLoadParameters&);
     void performSynchronousLoad(const NetworkResourceLoadParameters&, Ref<Messages::NetworkConnectionToWebProcess::PerformSynchronousLoad::DelayedReply>&&);
-    void loadPing(const NetworkResourceLoadParameters&);
+    void loadPing(NetworkResourceLoadParameters&&);
     void prefetchDNS(const String&);
 
     void removeLoadIdentifier(ResourceLoadIdentifier);
index 4e197c8..26b9dc1 100644 (file)
@@ -29,6 +29,7 @@
 #include "ArgumentCoders.h"
 #include "DataReference.h"
 #include "WebCoreArgumentCoders.h"
+#include <WebCore/SecurityOriginData.h>
 
 using namespace WebCore;
 
@@ -81,6 +82,11 @@ void NetworkResourceLoadParameters::encode(IPC::Encoder& encoder) const
     encoder << needsCertificateInfo;
     encoder << maximumBufferingTime;
     encoder << derivedCachedDataTypesToRetrieve;
+
+    encoder << static_cast<bool>(sourceOrigin);
+    if (sourceOrigin)
+        encoder << SecurityOriginData::fromSecurityOrigin(*sourceOrigin);
+    encoder.encodeEnum(mode);
 }
 
 bool NetworkResourceLoadParameters::decode(IPC::Decoder& decoder, NetworkResourceLoadParameters& result)
@@ -145,6 +151,19 @@ bool NetworkResourceLoadParameters::decode(IPC::Decoder& decoder, NetworkResourc
     if (!decoder.decode(result.derivedCachedDataTypesToRetrieve))
         return false;
 
+    bool hasSourceOrigin;
+    if (!decoder.decode(hasSourceOrigin))
+        return false;
+    if (hasSourceOrigin) {
+        SecurityOriginData sourceOriginData;
+        if (!decoder.decode(sourceOriginData))
+            return false;
+        ASSERT(!sourceOriginData.isEmpty());
+        result.sourceOrigin = sourceOriginData.securityOrigin();
+    }
+    if (!decoder.decodeEnum(result.mode))
+        return false;
+
     return true;
 }
     
index c1da457..36ca0e8 100644 (file)
 
 #include "NetworkLoadParameters.h"
 #include "SandboxExtension.h"
+#include <WebCore/FetchOptions.h>
 #include <WebCore/ResourceHandle.h>
 #include <WebCore/ResourceLoaderOptions.h>
 #include <WebCore/ResourceRequest.h>
+#include <WebCore/SecurityOrigin.h>
 #include <WebCore/SessionID.h>
 #include <wtf/Seconds.h>
 
@@ -53,6 +55,8 @@ public:
     RefPtr<SandboxExtension> resourceSandboxExtension; // Created automatically for the sender.
     Seconds maximumBufferingTime;
     Vector<String> derivedCachedDataTypesToRetrieve;
+    RefPtr<WebCore::SecurityOrigin> sourceOrigin;
+    WebCore::FetchOptions::Mode mode;
 };
 
 } // namespace WebKit
diff --git a/Source/WebKit/NetworkProcess/PingLoad.cpp b/Source/WebKit/NetworkProcess/PingLoad.cpp
new file mode 100644 (file)
index 0000000..2df014c
--- /dev/null
@@ -0,0 +1,168 @@
+/*
+ * Copyright (C) 2016-2017 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "PingLoad.h"
+
+#if USE(NETWORK_SESSION)
+
+#include "AuthenticationManager.h"
+#include "Logging.h"
+#include "NetworkCORSPreflightChecker.h"
+#include "SessionTracker.h"
+
+#define RELEASE_LOG_IF_ALLOWED(fmt, ...) RELEASE_LOG_IF(m_parameters.sessionID.isAlwaysOnLoggingAllowed(), Network, "%p - PingLoad::" fmt, this, ##__VA_ARGS__)
+
+namespace WebKit {
+
+using namespace WebCore;
+
+PingLoad::PingLoad(NetworkResourceLoadParameters&& parameters)
+    : m_parameters(WTFMove(parameters))
+    , m_timeoutTimer(*this, &PingLoad::timeoutTimerFired)
+{
+    // If the server never responds, this object will hang around forever.
+    // Set a very generous timeout, just in case.
+    m_timeoutTimer.startOneShot(60000_s);
+
+    if (needsCORSPreflight(m_parameters.request))
+        doCORSPreflight(m_parameters.request);
+    else
+        startNetworkLoad();
+}
+
+PingLoad::~PingLoad()
+{
+    if (m_task) {
+        ASSERT(m_task->client() == this);
+        m_task->clearClient();
+        m_task->cancel();
+    }
+}
+
+void PingLoad::startNetworkLoad()
+{
+    RELEASE_LOG_IF_ALLOWED("startNetworkLoad");
+    if (auto* networkSession = SessionTracker::networkSession(m_parameters.sessionID)) {
+        m_task = NetworkDataTask::create(*networkSession, *this, m_parameters);
+        m_task->resume();
+    } else
+        ASSERT_NOT_REACHED();
+}
+
+void PingLoad::willPerformHTTPRedirection(ResourceResponse&&, ResourceRequest&& request, RedirectCompletionHandler&& completionHandler)
+{
+    RELEASE_LOG_IF_ALLOWED("willPerformHTTPRedirection");
+    // FIXME: Do a CORS preflight if necessary.
+    // FIXME: We should ensure the number of redirects does not exceed 20.
+    completionHandler(m_parameters.shouldFollowRedirects ? request : ResourceRequest());
+}
+
+void PingLoad::didReceiveChallenge(const AuthenticationChallenge&, ChallengeCompletionHandler&& completionHandler)
+{
+    RELEASE_LOG_IF_ALLOWED("didReceiveChallenge");
+    completionHandler(AuthenticationChallengeDisposition::Cancel, { });
+    delete this;
+}
+
+void PingLoad::didReceiveResponseNetworkSession(ResourceResponse&&, ResponseCompletionHandler&& completionHandler)
+{
+    RELEASE_LOG_IF_ALLOWED("didReceiveResponseNetworkSession");
+    completionHandler(PolicyAction::PolicyIgnore);
+    delete this;
+}
+
+void PingLoad::didReceiveData(Ref<SharedBuffer>&&)
+{
+    RELEASE_LOG_IF_ALLOWED("didReceiveData");
+    ASSERT_NOT_REACHED();
+}
+
+void PingLoad::didCompleteWithError(const ResourceError& error, const NetworkLoadMetrics&)
+{
+    if (error.isNull())
+        RELEASE_LOG_IF_ALLOWED("didComplete");
+    else
+        RELEASE_LOG_IF_ALLOWED("didCompleteWithError, error_code: %d", error.errorCode());
+    delete this;
+}
+
+void PingLoad::didSendData(uint64_t totalBytesSent, uint64_t totalBytesExpectedToSend)
+{
+}
+
+void PingLoad::wasBlocked()
+{
+    RELEASE_LOG_IF_ALLOWED("wasBlocked");
+    delete this;
+}
+
+void PingLoad::cannotShowURL()
+{
+    RELEASE_LOG_IF_ALLOWED("cannotShowURL");
+    delete this;
+}
+
+void PingLoad::timeoutTimerFired()
+{
+    RELEASE_LOG_IF_ALLOWED("timeoutTimerFired");
+    delete this;
+}
+
+bool PingLoad::needsCORSPreflight(const ResourceRequest& request) const
+{
+    if (m_parameters.mode == FetchOptions::Mode::Cors) {
+        ASSERT(m_parameters.sourceOrigin);
+        return !m_parameters.sourceOrigin->canRequest(request.url());
+    }
+    return false;
+}
+
+void PingLoad::doCORSPreflight(const ResourceRequest& request)
+{
+    RELEASE_LOG_IF_ALLOWED("doCORSPreflight");
+    ASSERT(!m_corsPreflightChecker);
+    ASSERT(m_parameters.sourceOrigin);
+
+    NetworkCORSPreflightChecker::Parameters parameters = {
+        request,
+        *m_parameters.sourceOrigin,
+        m_parameters.sessionID,
+        m_parameters.allowStoredCredentials
+    };
+    m_corsPreflightChecker = std::make_unique<NetworkCORSPreflightChecker>(WTFMove(parameters), [this](NetworkCORSPreflightChecker::Result result) {
+        RELEASE_LOG_IF_ALLOWED("doCORSPreflight complete, success: %d", result == NetworkCORSPreflightChecker::Result::Success);
+        if (result == NetworkCORSPreflightChecker::Result::Success) {
+            m_corsPreflightChecker = nullptr;
+            startNetworkLoad();
+        } else
+            delete this;
+    });
+    m_corsPreflightChecker->startPreflight();
+}
+
+} // namespace WebKit
+
+#endif // USE(NETWORK_SESSION)
index 39be06d..8872d2f 100644 (file)
  * THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-#ifndef PingLoad_h
-#define PingLoad_h
+#pragma once
+
+#if USE(NETWORK_SESSION)
 
-#include "AuthenticationManager.h"
 #include "NetworkDataTask.h"
-#include "SessionTracker.h"
+#include "NetworkResourceLoadParameters.h"
 
 namespace WebKit {
 
+class NetworkCORSPreflightChecker;
+
 class PingLoad final : private NetworkDataTaskClient {
 public:
-    PingLoad(const NetworkResourceLoadParameters& parameters)
-        : m_timeoutTimer(*this, &PingLoad::timeoutTimerFired)
-        , m_shouldFollowRedirects(parameters.shouldFollowRedirects)
-    {
-        if (auto* networkSession = SessionTracker::networkSession(parameters.sessionID)) {
-            m_task = NetworkDataTask::create(*networkSession, *this, parameters);
-            m_task->resume();
-        } else
-            ASSERT_NOT_REACHED();
-
-        // If the server never responds, this object will hang around forever.
-        // Set a very generous timeout, just in case.
-        m_timeoutTimer.startOneShot(60000_s);
-    }
+    explicit PingLoad(NetworkResourceLoadParameters&&);
     
 private:
-    void willPerformHTTPRedirection(WebCore::ResourceResponse&&, WebCore::ResourceRequest&& request, RedirectCompletionHandler&& completionHandler) final
-    {
-        completionHandler(m_shouldFollowRedirects ? request : WebCore::ResourceRequest());
-    }
-    void didReceiveChallenge(const WebCore::AuthenticationChallenge&, ChallengeCompletionHandler&& completionHandler) final
-    {
-        completionHandler(AuthenticationChallengeDisposition::Cancel, { });
-        delete this;
-    }
-    void didReceiveResponseNetworkSession(WebCore::ResourceResponse&&, ResponseCompletionHandler&& completionHandler) final
-    {
-        completionHandler(WebCore::PolicyAction::PolicyIgnore);
-        delete this;
-    }
-    void didReceiveData(Ref<WebCore::SharedBuffer>&&) final { ASSERT_NOT_REACHED(); }
-    void didCompleteWithError(const WebCore::ResourceError&, const WebCore::NetworkLoadMetrics&) final { delete this; }
-    void didSendData(uint64_t totalBytesSent, uint64_t totalBytesExpectedToSend) final { }
-    void wasBlocked() final { delete this; }
-    void cannotShowURL() final { delete this; }
+    ~PingLoad();
 
-    void timeoutTimerFired() { delete this; }
-    
-    virtual ~PingLoad()
-    {
-        if (m_task) {
-            ASSERT(m_task->client() == this);
-            m_task->clearClient();
-            m_task->cancel();
-        }
-    }
+    void willPerformHTTPRedirection(WebCore::ResourceResponse&&, WebCore::ResourceRequest&&, RedirectCompletionHandler&&) final;
+    void didReceiveChallenge(const WebCore::AuthenticationChallenge&, ChallengeCompletionHandler&&) final;
+    void didReceiveResponseNetworkSession(WebCore::ResourceResponse&&, ResponseCompletionHandler&&) final;
+    void didReceiveData(Ref<WebCore::SharedBuffer>&&) final;
+    void didCompleteWithError(const WebCore::ResourceError&, const WebCore::NetworkLoadMetrics&) final;
+    void didSendData(uint64_t totalBytesSent, uint64_t totalBytesExpectedToSend) final;
+    void wasBlocked() final;
+    void cannotShowURL() final;
+    void timeoutTimerFired();
+
+    void startNetworkLoad();
+    bool needsCORSPreflight(const WebCore::ResourceRequest&) const;
+    void doCORSPreflight(const WebCore::ResourceRequest&);
     
+    NetworkResourceLoadParameters m_parameters;
     RefPtr<NetworkDataTask> m_task;
     WebCore::Timer m_timeoutTimer;
-    bool m_shouldFollowRedirects;
+    std::unique_ptr<NetworkCORSPreflightChecker> m_corsPreflightChecker;
 };
 
 }
 
-#endif
+#endif // USE(NETWORK_SESSION)
index 217710d..9d07d07 100644 (file)
                41FAF5F81E3C1021001AE678 /* LibWebRTCResolver.h in Headers */ = {isa = PBXBuildFile; fileRef = 41FAF5F61E3C0B47001AE678 /* LibWebRTCResolver.h */; };
                41FAF5F91E3C1025001AE678 /* LibWebRTCResolver.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 41FAF5F71E3C0B47001AE678 /* LibWebRTCResolver.cpp */; };
                4450AEC01DC3FAE5009943F2 /* SharedMemoryCocoa.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4450AEBF1DC3FAE5009943F2 /* SharedMemoryCocoa.cpp */; };
+               462107D81F38DBDB00DD7810 /* PingLoad.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 462107D71F38DBD300DD7810 /* PingLoad.cpp */; };
                463FD4801EB9459600A2982C /* WKProcessTerminationReason.h in Headers */ = {isa = PBXBuildFile; fileRef = 463FD47F1EB9458400A2982C /* WKProcessTerminationReason.h */; settings = {ATTRIBUTES = (Private, ); }; };
                463FD4821EB94EC000A2982C /* ProcessTerminationReason.h in Headers */ = {isa = PBXBuildFile; fileRef = 463FD4811EB94EAD00A2982C /* ProcessTerminationReason.h */; };
                465250E61ECF52DC002025CB /* WebKit2InitializeCocoa.mm in Sources */ = {isa = PBXBuildFile; fileRef = 465250E51ECF52CD002025CB /* WebKit2InitializeCocoa.mm */; };
                46A2B6081E5676A600C3DEDA /* BackgroundProcessResponsivenessTimer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 46A2B6061E5675A200C3DEDA /* BackgroundProcessResponsivenessTimer.cpp */; };
                46A2B6091E5676A600C3DEDA /* BackgroundProcessResponsivenessTimer.h in Headers */ = {isa = PBXBuildFile; fileRef = 46A2B6071E5675A200C3DEDA /* BackgroundProcessResponsivenessTimer.h */; };
+               46DF063B1F3905F8001980BB /* NetworkCORSPreflightChecker.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 46DF06391F3905E5001980BB /* NetworkCORSPreflightChecker.cpp */; };
+               46DF063C1F3905F8001980BB /* NetworkCORSPreflightChecker.h in Headers */ = {isa = PBXBuildFile; fileRef = 46DF063A1F3905E5001980BB /* NetworkCORSPreflightChecker.h */; };
                4A3CC18A19B063E700D14AEF /* UserMediaPermissionRequestManagerProxy.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4A410F3919AF7B04002EBAB5 /* UserMediaPermissionRequestManagerProxy.cpp */; };
                4A3CC18B19B0640F00D14AEF /* UserMediaPermissionRequestManagerProxy.h in Headers */ = {isa = PBXBuildFile; fileRef = 4A410F3A19AF7B04002EBAB5 /* UserMediaPermissionRequestManagerProxy.h */; };
                4A3CC18C19B0641500D14AEF /* UserMediaPermissionRequestProxy.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4A410F3B19AF7B04002EBAB5 /* UserMediaPermissionRequestProxy.cpp */; };
                41FAF5F61E3C0B47001AE678 /* LibWebRTCResolver.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = LibWebRTCResolver.h; path = Network/webrtc/LibWebRTCResolver.h; sourceTree = "<group>"; };
                41FAF5F71E3C0B47001AE678 /* LibWebRTCResolver.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = LibWebRTCResolver.cpp; path = Network/webrtc/LibWebRTCResolver.cpp; sourceTree = "<group>"; };
                4450AEBF1DC3FAE5009943F2 /* SharedMemoryCocoa.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SharedMemoryCocoa.cpp; path = cocoa/SharedMemoryCocoa.cpp; sourceTree = "<group>"; };
+               462107D71F38DBD300DD7810 /* PingLoad.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = PingLoad.cpp; path = NetworkProcess/PingLoad.cpp; sourceTree = "<group>"; };
                463FD47F1EB9458400A2982C /* WKProcessTerminationReason.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WKProcessTerminationReason.h; sourceTree = "<group>"; };
                463FD4811EB94EAD00A2982C /* ProcessTerminationReason.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ProcessTerminationReason.h; sourceTree = "<group>"; };
                465250E51ECF52CD002025CB /* WebKit2InitializeCocoa.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = WebKit2InitializeCocoa.mm; sourceTree = "<group>"; };
                46A2B6061E5675A200C3DEDA /* BackgroundProcessResponsivenessTimer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = BackgroundProcessResponsivenessTimer.cpp; sourceTree = "<group>"; };
                46A2B6071E5675A200C3DEDA /* BackgroundProcessResponsivenessTimer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BackgroundProcessResponsivenessTimer.h; sourceTree = "<group>"; };
+               46DF06391F3905E5001980BB /* NetworkCORSPreflightChecker.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = NetworkCORSPreflightChecker.cpp; path = NetworkProcess/NetworkCORSPreflightChecker.cpp; sourceTree = "<group>"; };
+               46DF063A1F3905E5001980BB /* NetworkCORSPreflightChecker.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = NetworkCORSPreflightChecker.h; path = NetworkProcess/NetworkCORSPreflightChecker.h; sourceTree = "<group>"; };
                4A410F3519AF7AC3002EBAB5 /* WKUserMediaPermissionRequest.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = WKUserMediaPermissionRequest.cpp; sourceTree = "<group>"; };
                4A410F3619AF7AC3002EBAB5 /* WKUserMediaPermissionRequest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WKUserMediaPermissionRequest.h; sourceTree = "<group>"; };
                4A410F3919AF7B04002EBAB5 /* UserMediaPermissionRequestManagerProxy.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = UserMediaPermissionRequestManagerProxy.cpp; sourceTree = "<group>"; };
                                2DA944BB188511DD00ED86DB /* ios */,
                                510CC7DC16138E2900D03ED3 /* mac */,
                                413075971DE84ED70039EC69 /* webrtc */,
+                               46DF06391F3905E5001980BB /* NetworkCORSPreflightChecker.cpp */,
+                               46DF063A1F3905E5001980BB /* NetworkCORSPreflightChecker.h */,
                                513A16491630A9BF005D7D22 /* NetworkConnectionToWebProcess.cpp */,
                                513A164A1630A9BF005D7D22 /* NetworkConnectionToWebProcess.h */,
                                513A164B1630A9BF005D7D22 /* NetworkConnectionToWebProcess.messages.in */,
                                5C0B177D1E7C886700E9123C /* NetworkSocketStream.cpp */,
                                5C0B177E1E7C886700E9123C /* NetworkSocketStream.h */,
                                5C0B177F1E7C886700E9123C /* NetworkSocketStream.messages.in */,
+                               462107D71F38DBD300DD7810 /* PingLoad.cpp */,
                                5CE85B1F1C88E6430070BFCE /* PingLoad.h */,
                                E1B78470163F24690007B692 /* RemoteNetworkingContext.h */,
                        );
                                1AF1AC6C1651759E00C17D7F /* RemoteLayerTreeTransaction.h in Headers */,
                                E1B78471163F24690007B692 /* RemoteNetworkingContext.h in Headers */,
                                1A5704FC1BE1751100874AF1 /* RemoteObjectInvocation.h in Headers */,
+                               46DF063C1F3905F8001980BB /* NetworkCORSPreflightChecker.h in Headers */,
                                1AC1338018590AE400F3EC05 /* RemoteObjectRegistry.h in Headers */,
                                1AC1338618590C4600F3EC05 /* RemoteObjectRegistryMessages.h in Headers */,
                                0F594790187B3B3A00437857 /* RemoteScrollingCoordinator.h in Headers */,
                                BCEE966C112FAF57006BCC24 /* Attachment.cpp in Sources */,
                                E1A31735134CEA80007C9A4F /* AttributedString.mm in Sources */,
                                512F589612A8838800629530 /* AuthenticationChallengeProxy.cpp in Sources */,
+                               46DF063B1F3905F8001980BB /* NetworkCORSPreflightChecker.cpp in Sources */,
                                512F589812A8838800629530 /* AuthenticationDecisionListener.cpp in Sources */,
                                518E8EF816B2091C00E91429 /* AuthenticationManager.cpp in Sources */,
                                518E8EFB16B2091C00E91429 /* AuthenticationManager.mac.mm in Sources */,
                                51E35209180F5D6B00E53BE9 /* StorageServiceEntryPoint.mm in Sources */,
                                51E35200180F5D0F00E53BE9 /* StorageToWebProcessConnection.cpp in Sources */,
                                5118E9AC1F295977003EF9F5 /* StorageToWebProcessConnectionMessageReceiver.cpp in Sources */,
+                               462107D81F38DBDB00DD7810 /* PingLoad.cpp in Sources */,
                                1AE00D6B18327C1200087DD7 /* StringReference.cpp in Sources */,
                                296BD85E15019BC30071F424 /* StringUtilities.mm in Sources */,
                                1ZZ417EF12C00D87002BE67B /* TextCheckerCompletion.cpp in Sources */,
index 20253cc..c16d08b 100644 (file)
 #include <WebCore/DiagnosticLoggingKeys.h>
 #include <WebCore/Document.h>
 #include <WebCore/DocumentLoader.h>
+#include <WebCore/FetchOptions.h>
 #include <WebCore/Frame.h>
 #include <WebCore/FrameLoader.h>
 #include <WebCore/NetscapePlugInStreamLoader.h>
 #include <WebCore/PlatformStrategies.h>
 #include <WebCore/ReferrerPolicy.h>
 #include <WebCore/ResourceLoader.h>
+#include <WebCore/SecurityOrigin.h>
 #include <WebCore/SessionID.h>
 #include <WebCore/Settings.h>
 #include <WebCore/SubresourceLoader.h>
@@ -383,7 +385,7 @@ void WebLoaderStrategy::loadResourceSynchronously(NetworkingContext* context, un
     }
 }
 
-void WebLoaderStrategy::createPingHandle(NetworkingContext* networkingContext, ResourceRequest& request, bool shouldUseCredentialStorage, bool shouldFollowRedirects)
+void WebLoaderStrategy::createPingHandle(NetworkingContext* networkingContext, ResourceRequest& request, Ref<SecurityOrigin>&& sourceOrigin, const FetchOptions& options)
 {
     // It's possible that call to createPingHandle might be made during initial empty Document creation before a NetworkingContext exists.
     // It is not clear that we should send ping loads during that process anyways.
@@ -397,9 +399,11 @@ void WebLoaderStrategy::createPingHandle(NetworkingContext* networkingContext, R
     
     NetworkResourceLoadParameters loadParameters;
     loadParameters.request = request;
+    loadParameters.sourceOrigin = WTFMove(sourceOrigin);
     loadParameters.sessionID = webPage ? webPage->sessionID() : SessionID::defaultSessionID();
-    loadParameters.allowStoredCredentials = shouldUseCredentialStorage ? AllowStoredCredentials : DoNotAllowStoredCredentials;
-    loadParameters.shouldFollowRedirects = shouldFollowRedirects;
+    loadParameters.allowStoredCredentials = options.credentials == FetchOptions::Credentials::Omit ? DoNotAllowStoredCredentials : AllowStoredCredentials;
+    loadParameters.mode = options.mode;
+    loadParameters.shouldFollowRedirects = options.redirect == FetchOptions::Redirect::Follow;
     loadParameters.shouldClearReferrerOnHTTPSToHTTPRedirect = networkingContext->shouldClearReferrerOnHTTPSToHTTPRedirect();
 
     WebProcess::singleton().networkConnection().connection().send(Messages::NetworkConnectionToWebProcess::LoadPing(loadParameters), 0);
index 947843b..bfc1074 100644 (file)
 #include <wtf/HashSet.h>
 #include <wtf/RunLoop.h>
 
+namespace WebCore {
+struct FetchOptions;
+}
+
 namespace WebKit {
 
 class NetworkProcessConnection;
@@ -55,7 +59,7 @@ public:
     void suspendPendingRequests() override;
     void resumePendingRequests() override;
 
-    void createPingHandle(WebCore::NetworkingContext*, WebCore::ResourceRequest&, bool shouldUseCredentialStorage, bool shouldFollowRedirects) override;
+    void createPingHandle(WebCore::NetworkingContext*, WebCore::ResourceRequest&, Ref<WebCore::SecurityOrigin>&& sourceOrigin, const WebCore::FetchOptions&) override;
 
     void storeDerivedDataToCache(const SHA1::Digest& bodyHash, const String& type, const String& partition, WebCore::SharedBuffer&) override;
 
index c28212b..bafe14c 100644 (file)
@@ -1,3 +1,18 @@
+2017-08-08  Chris Dumez  <cdumez@apple.com>
+
+        [Beacon] Add support for CORS-preflighting for WK2 / NETWORK_SESSION
+        https://bugs.webkit.org/show_bug.cgi?id=175264
+        <rdar://problem/33547793>
+
+        Reviewed by Youenn Fablet.
+
+        createPingHandle() now takes new parameters but there is currently no behavior
+        change on WebKit1.
+
+        * WebCoreSupport/WebResourceLoadScheduler.cpp:
+        (WebResourceLoadScheduler::createPingHandle):
+        * WebCoreSupport/WebResourceLoadScheduler.h:
+
 2017-08-03  Per Arne Vollan  <pvollan@apple.com>
 
         [Win] WebKit COM header file is not placed in the correct location.
index 2c7be1a..479776e 100644 (file)
@@ -26,6 +26,7 @@
 
 #include <WebCore/Document.h>
 #include <WebCore/DocumentLoader.h>
+#include <WebCore/FetchOptions.h>
 #include <WebCore/Frame.h>
 #include <WebCore/FrameLoader.h>
 #include <WebCore/NetscapePlugInStreamLoader.h>
@@ -362,9 +363,9 @@ bool WebResourceLoadScheduler::HostInformation::limitRequests(ResourceLoadPriori
     return m_requestsLoading.size() >= (webResourceLoadScheduler().isSerialLoadingEnabled() ? 1 : m_maxRequestsInFlight);
 }
 
-void WebResourceLoadScheduler::createPingHandle(NetworkingContext* networkingContext, ResourceRequest& request, bool shouldUseCredentialStorage, bool shouldFollowRedirects)
+void WebResourceLoadScheduler::createPingHandle(NetworkingContext* networkingContext, ResourceRequest& request, Ref<SecurityOrigin>&&, const FetchOptions& options)
 {
     // PingHandle manages its own lifetime, deleting itself when its purpose has been fulfilled.
-    new PingHandle(networkingContext, request, shouldUseCredentialStorage, PingHandle::UsesAsyncCallbacks::No, shouldFollowRedirects);
+    new PingHandle(networkingContext, request, options.credentials != FetchOptions::Credentials::Omit, PingHandle::UsesAsyncCallbacks::No, options.redirect == FetchOptions::Redirect::Follow);
 }
 
index 076a334..60970bb 100644 (file)
 
 class WebResourceLoadScheduler;
 
+namespace WebCore {
+struct FetchOptions;
+class SecurityOrigin;
+}
+
 WebResourceLoadScheduler& webResourceLoadScheduler();
 
 class WebResourceLoadScheduler : public WebCore::LoaderStrategy {
@@ -54,7 +59,7 @@ public:
     void suspendPendingRequests() override;
     void resumePendingRequests() override;
 
-    void createPingHandle(WebCore::NetworkingContext*, WebCore::ResourceRequest&, bool shouldUseCredentialStorage, bool shouldFollowRedirects) override;
+    void createPingHandle(WebCore::NetworkingContext*, WebCore::ResourceRequest&, Ref<WebCore::SecurityOrigin>&& sourceOrigin, const WebCore::FetchOptions&) override;
 
     void storeDerivedDataToCache(const SHA1::Digest&, const String&, const String&, WebCore::SharedBuffer&) override { }