Service Worker should get the body of intercepted requests
authorcommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 17 Nov 2017 02:36:03 +0000 (02:36 +0000)
committercommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 17 Nov 2017 02:36:03 +0000 (02:36 +0000)
https://bugs.webkit.org/show_bug.cgi?id=179776

Patch by Youenn Fablet <youenn@apple.com> on 2017-11-16
Reviewed by Alex Christensen.

LayoutTests/imported/w3c:

* imported/w3c/web-platform-tests/fetch/api/request/request-consume-empty-expected.txt:
* imported/w3c/web-platform-tests/fetch/api/request/request-init-002-expected.txt:
* imported/w3c/web-platform-tests/fetch/api/response/response-consume-empty-expected.txt:
* imported/w3c/web-platform-tests/fetch/api/response/response-consume-expected.txt:
* imported/w3c/web-platform-tests/fetch/api/response/response-init-002-expected.txt:
* web-platform-tests/service-workers/service-worker/fetch-event.https-expected.txt:
* web-platform-tests/service-workers/service-worker/fetch-request-xhr.https-expected.txt:

Source/WebCore:

Test: http/tests/workers/service/service-worker-request-with-body.https.html

Make use of FetchBodyConsumer to store raw data for FetchRequest.
This is used when setting FetchRequest body from a FormData.
If FormData is only bytes (no blob, no file), FetchBodyConsumer will store that data.
This allows Service Worker to get access to simple request bodies.

* Modules/fetch/FetchBody.cpp:
(WebCore::FetchBody::fromFormData):
(WebCore::FetchBody::consume):
(WebCore::FetchBody::bodyAsFormData const): Making sure body is set appropriately when used to make fetch load.
* Modules/fetch/FetchBody.h: Making some methods private.
* Modules/fetch/FetchBodyConsumer.h: Adding accessors.
(WebCore::FetchBodyConsumer::hasData const):
(WebCore::FetchBodyConsumer::data const):
* Modules/fetch/FetchRequest.h:
(WebCore::FetchRequest::FetchRequest):
* platform/network/FormData.cpp:
(WebCore::FormData::asSharedBuffer const):
* platform/network/FormData.h:
* workers/service/context/ServiceWorkerFetch.cpp:
(WebCore::ServiceWorkerFetch::dispatchFetchEvent): Setting FetchRequest body based on given FormData.

Source/WebKit:

Pass a FormDataReference when starting fetch IPC.
Convert this FormDataReference in a FormData and using it to set the FetchRequest body properly in Service Worker process.
Forbid fetch interception when URL is not HTTP/HTTPS.

* Platform/IPC/FormDataReference.h:
(IPC::FormDataReference::FormDataReference):
(IPC::FormDataReference::takeData):
(IPC::FormDataReference::encode const):
(IPC::FormDataReference::decode):
* StorageProcess/ServiceWorker/WebSWServerConnection.cpp:
(WebKit::WebSWServerConnection::startFetch):
* StorageProcess/ServiceWorker/WebSWServerConnection.h:
* StorageProcess/ServiceWorker/WebSWServerConnection.messages.in:
* WebProcess/Storage/WebSWClientConnection.cpp:
(WebKit::WebSWClientConnection::startFetch):
* WebProcess/Storage/WebSWContextManagerConnection.cpp:
(WebKit::WebSWContextManagerConnection::startFetch):
* WebProcess/Storage/WebSWContextManagerConnection.h:
* WebProcess/Storage/WebSWContextManagerConnection.messages.in:
* WebProcess/Storage/WebServiceWorkerProvider.cpp:
(WebKit::WebServiceWorkerProvider::handleFetch):

LayoutTests:

* http/tests/workers/service/resources/service-worker-fetch.js:
* http/tests/workers/service/service-worker-fetch.https-expected.txt:
* http/tests/workers/service/resources/service-worker-request-with-body-worker.js: Added.
* http/tests/workers/service/service-worker-request-with-body.https-expected.txt: Added.
* http/tests/workers/service/service-worker-request-with-body.https.html: Added.

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

32 files changed:
LayoutTests/ChangeLog
LayoutTests/http/tests/workers/service/resources/service-worker-fetch.js
LayoutTests/http/tests/workers/service/resources/service-worker-request-with-body-worker.js [new file with mode: 0644]
LayoutTests/http/tests/workers/service/service-worker-fetch.https-expected.txt
LayoutTests/http/tests/workers/service/service-worker-request-with-body.https-expected.txt [new file with mode: 0644]
LayoutTests/http/tests/workers/service/service-worker-request-with-body.https.html [new file with mode: 0644]
LayoutTests/imported/w3c/ChangeLog
LayoutTests/imported/w3c/web-platform-tests/fetch/api/request/request-consume-empty-expected.txt
LayoutTests/imported/w3c/web-platform-tests/fetch/api/request/request-init-002-expected.txt
LayoutTests/imported/w3c/web-platform-tests/fetch/api/response/response-consume-empty-expected.txt
LayoutTests/imported/w3c/web-platform-tests/fetch/api/response/response-consume-expected.txt
LayoutTests/imported/w3c/web-platform-tests/fetch/api/response/response-init-002-expected.txt
LayoutTests/imported/w3c/web-platform-tests/service-workers/service-worker/fetch-event.https-expected.txt
LayoutTests/imported/w3c/web-platform-tests/service-workers/service-worker/fetch-request-xhr.https-expected.txt
Source/WebCore/ChangeLog
Source/WebCore/Modules/fetch/FetchBody.cpp
Source/WebCore/Modules/fetch/FetchBody.h
Source/WebCore/Modules/fetch/FetchBodyConsumer.h
Source/WebCore/Modules/fetch/FetchRequest.h
Source/WebCore/platform/network/FormData.cpp
Source/WebCore/platform/network/FormData.h
Source/WebCore/workers/service/context/ServiceWorkerFetch.cpp
Source/WebKit/ChangeLog
Source/WebKit/Platform/IPC/FormDataReference.h
Source/WebKit/StorageProcess/ServiceWorker/WebSWServerConnection.cpp
Source/WebKit/StorageProcess/ServiceWorker/WebSWServerConnection.h
Source/WebKit/StorageProcess/ServiceWorker/WebSWServerConnection.messages.in
Source/WebKit/WebProcess/Storage/WebSWClientConnection.cpp
Source/WebKit/WebProcess/Storage/WebSWContextManagerConnection.cpp
Source/WebKit/WebProcess/Storage/WebSWContextManagerConnection.h
Source/WebKit/WebProcess/Storage/WebSWContextManagerConnection.messages.in
Source/WebKit/WebProcess/Storage/WebServiceWorkerProvider.cpp

index d96b20f..6fb4e65 100644 (file)
@@ -1,3 +1,16 @@
+2017-11-16  Youenn Fablet  <youenn@apple.com>
+
+        Service Worker should get the body of intercepted requests
+        https://bugs.webkit.org/show_bug.cgi?id=179776
+
+        Reviewed by Alex Christensen.
+
+        * http/tests/workers/service/resources/service-worker-fetch.js:
+        * http/tests/workers/service/service-worker-fetch.https-expected.txt:
+        * http/tests/workers/service/resources/service-worker-request-with-body-worker.js: Added.
+        * http/tests/workers/service/service-worker-request-with-body.https-expected.txt: Added.
+        * http/tests/workers/service/service-worker-request-with-body.https.html: Added.
+
 2017-11-16  Nan Wang  <n_wang@apple.com>
 
         AX: AOM: Implement string type properties
index acec4cb..b3eb701 100644 (file)
@@ -12,9 +12,17 @@ async function test()
         log("Got image response with buffer byte length being " + buffer.byteLength);
 
         response = await fetch("/resources/square100.png.bodyasanemptystream");
-        var buffer =  await response.arrayBuffer();
+        buffer =  await response.arrayBuffer();
         log("Got should-be-empty response with buffer byte length being " + buffer.byteLength + " and status is " + response.statusText);
 
+        response = await fetch("data:text/plain;base64,cmVzcG9uc2UncyBib2R5");
+        var text  =  await response.text();
+        log("Got data URL response with data=\"" + text + "\" and status is " + response.statusText);
+
+        response = await fetch(window.URL.createObjectURL(new Blob(["PASS"])));
+        text =  await response.text();
+        log("Got blob response with data=\"" + text + "\" and status is " + response.statusText);
+
         response = await fetch("status");
         log("Status is " + response.statusText);
     } catch(e) {
diff --git a/LayoutTests/http/tests/workers/service/resources/service-worker-request-with-body-worker.js b/LayoutTests/http/tests/workers/service/resources/service-worker-request-with-body-worker.js
new file mode 100644 (file)
index 0000000..67b6701
--- /dev/null
@@ -0,0 +1,21 @@
+self.addEventListener("fetch", (event) => {
+    if (event.request.url.endsWith("request-with-text-body.fromserviceworker")) {
+        event.respondWith(
+            event.request.text().then(value => {
+                return new Response(value);
+            })
+        );
+        return;
+    }
+    if (event.request.url.endsWith("request-with-cloned-text-body.fromserviceworker")) {
+        event.respondWith(
+            event.request.clone().text().then(value => {
+                return new Response(value);
+            })
+        );
+    }
+    if (event.request.url.endsWith("post-echo.cgi")) {
+        event.respondWith(fetch(event.request));
+        return;
+    }
+});
index 15a17b0..1d5a727 100644 (file)
@@ -2,5 +2,7 @@
 Status is no status
 Got image response with buffer byte length being 12940
 Got should-be-empty response with buffer byte length being 0 and status is Empty stream
+Got data URL response with data="response's body" and status is OK
+Got blob response with data="PASS" and status is OK
 Status is https://127.0.0.1:8443/resources/square100.png through fetch
 
diff --git a/LayoutTests/http/tests/workers/service/service-worker-request-with-body.https-expected.txt b/LayoutTests/http/tests/workers/service/service-worker-request-with-body.https-expected.txt
new file mode 100644 (file)
index 0000000..710cc50
--- /dev/null
@@ -0,0 +1,8 @@
+
+
+PASS Setup test 
+PASS Request with text body 
+PASS Request with arrayBuffer body 
+PASS Request with cloned text body 
+PASS Request with text body then fetched 
+
diff --git a/LayoutTests/http/tests/workers/service/service-worker-request-with-body.https.html b/LayoutTests/http/tests/workers/service/service-worker-request-with-body.https.html
new file mode 100644 (file)
index 0000000..754362f
--- /dev/null
@@ -0,0 +1,63 @@
+<html>
+<head>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+</head>
+<body>
+<script>
+async function interceptedFrame(workerURL, scopeURL)
+{
+    var registration = await navigator.serviceWorker.register(workerURL, { scope : scopeURL });
+    await new Promise(resolve => {
+        if (registration.active)
+            resolve();
+        worker = registration.installing;
+        if (worker.state === "activated")
+            resolve();
+        worker.addEventListener("statechange", () => {
+            if (worker.state === "activated")
+                resolve();
+        });
+    });
+
+    return await new Promise((resolve) => {
+        var frame = document.createElement('iframe');
+        frame.src = scopeURL;
+        frame.onload = function() { resolve(frame); };
+        document.body.appendChild(frame);
+    });
+}
+
+var fetch;
+
+promise_test(async (test) => {
+    var frame = await interceptedFrame("resources/service-worker-request-with-body-worker.js", "/");
+    fetch = frame.contentWindow.fetch;
+}, "Setup test");
+
+promise_test(async (test) => {
+    response = await fetch("request-with-text-body.fromserviceworker", { method: "POST", body: "PASS" });
+    var buffer =  await response.text();
+    assert_equals(buffer, "PASS");
+}, "Request with text body");
+
+promise_test(async (test) => {
+    response = await fetch("request-with-text-body.fromserviceworker", { method: "POST", body: new TextEncoder().encode("PASS") });
+    var buffer =  await response.text();
+    assert_equals(buffer, "PASS");
+}, "Request with arrayBuffer body");
+
+promise_test(async (test) => {
+    response = await fetch("request-with-cloned-text-body.fromserviceworker", { method: "POST", body: "PASS" });
+    var buffer =  await response.text();
+    assert_equals(buffer, "PASS");
+}, "Request with cloned text body");
+
+promise_test(async (test) => {
+    response = await fetch("/xmlhttprequest/resources/post-echo.cgi", { method: "POST", body: "PASS" });
+    var buffer =  await response.text();
+    assert_equals(buffer, "PASS");
+}, "Request with text body then fetched");
+</script>
+</body>
+</html>
index 1aeeea9..38e9391 100644 (file)
@@ -1,3 +1,18 @@
+2017-11-16  Youenn Fablet  <youenn@apple.com>
+
+        Service Worker should get the body of intercepted requests
+        https://bugs.webkit.org/show_bug.cgi?id=179776
+
+        Reviewed by Alex Christensen.
+
+        * imported/w3c/web-platform-tests/fetch/api/request/request-consume-empty-expected.txt:
+        * imported/w3c/web-platform-tests/fetch/api/request/request-init-002-expected.txt:
+        * imported/w3c/web-platform-tests/fetch/api/response/response-consume-empty-expected.txt:
+        * imported/w3c/web-platform-tests/fetch/api/response/response-consume-expected.txt:
+        * imported/w3c/web-platform-tests/fetch/api/response/response-init-002-expected.txt:
+        * web-platform-tests/service-workers/service-worker/fetch-event.https-expected.txt:
+        * web-platform-tests/service-workers/service-worker/fetch-request-xhr.https-expected.txt:
+
 2017-11-16  Chris Dumez  <cdumez@apple.com>
 
         [Service Worker] Implement "Try Clear Registration" algorithm
index 3e6719b..863f1dd 100644 (file)
@@ -11,6 +11,6 @@ PASS Consume empty text request body as arrayBuffer
 PASS Consume empty blob request body as text 
 PASS Consume empty text request body as text 
 PASS Consume empty URLSearchParams request body as text 
-FAIL Consume empty FormData request body as text promise_test: Unhandled rejection with value: undefined
+FAIL Consume empty FormData request body as text promise_test: Unhandled rejection with value: object "NotSupportedError: The operation is not supported."
 PASS Consume empty ArrayBuffer request body as text 
 
index a733501..009a00d 100644 (file)
@@ -3,7 +3,7 @@ PASS Initialize Request with headers values
 PASS Initialize Request's body with undefined 
 PASS Initialize Request's body with null 
 PASS Initialize Request's body with application/octet-binary 
-FAIL Initialize Request's body with multipart/form-data promise_test: Unhandled rejection with value: undefined
+FAIL Initialize Request's body with multipart/form-data promise_test: Unhandled rejection with value: object "NotSupportedError: The operation is not supported."
 PASS Initialize Request's body with text/plain;charset=UTF-8 
 PASS Initialize Request's body with application/x-www-form-urlencoded;charset=UTF-8 
 
index 222562d..fa8c6f4 100644 (file)
@@ -11,6 +11,6 @@ PASS Consume empty text response body as arrayBuffer
 PASS Consume empty blob response body as text 
 PASS Consume empty text response body as text 
 PASS Consume empty URLSearchParams response body as text 
-FAIL Consume empty FormData response body as text promise_test: Unhandled rejection with value: undefined
+FAIL Consume empty FormData response body as text promise_test: Unhandled rejection with value: object "NotSupportedError: The operation is not supported."
 PASS Consume empty ArrayBuffer response body as text 
 
index 4b9a4b0..5fb8de8 100644 (file)
@@ -17,9 +17,9 @@ FAIL Consume response's body: from blob with correct urlencoded type to formData
 FAIL Consume response's body: from blob without correct urlencoded type to formData (error case) assert_throws: function "function () { throw e }" threw object "NotSupportedError: The operation is not supported." ("NotSupportedError") expected object "TypeError" ("TypeError")
 FAIL Consume response's body: from FormData to formData promise_test: Unhandled rejection with value: object "NotSupportedError: The operation is not supported."
 FAIL Consume response's body: from FormData without correct type to formData (error case) assert_throws: function "function () { throw e }" threw object "NotSupportedError: The operation is not supported." ("NotSupportedError") expected object "TypeError" ("TypeError")
-FAIL Consume response's body: from FormData to blob promise_test: Unhandled rejection with value: undefined
-FAIL Consume response's body: from FormData to text promise_test: Unhandled rejection with value: undefined
-FAIL Consume response's body: from FormData to arrayBuffer promise_test: Unhandled rejection with value: undefined
+FAIL Consume response's body: from FormData to blob promise_test: Unhandled rejection with value: object "NotSupportedError: The operation is not supported."
+FAIL Consume response's body: from FormData to text promise_test: Unhandled rejection with value: object "NotSupportedError: The operation is not supported."
+FAIL Consume response's body: from FormData to arrayBuffer promise_test: Unhandled rejection with value: object "NotSupportedError: The operation is not supported."
 FAIL Consume response's body: from URLSearchParams to formData promise_test: Unhandled rejection with value: object "NotSupportedError: The operation is not supported."
 FAIL Consume response's body: from URLSearchParams without correct type to formData (error case) assert_throws: function "function () { throw e }" threw object "NotSupportedError: The operation is not supported." ("NotSupportedError") expected object "TypeError" ("TypeError")
 FAIL Consume response's body: from URLSearchParams to blob assert_equals: Blob body type should be computed from the response Content-Type expected "application/x-www-form-urlencoded;charset=utf-8" but got "application/x-www-form-urlencoded"
index ed160c0..65e968b 100644 (file)
@@ -1,7 +1,7 @@
 
 PASS Initialize Response with headers values 
 PASS Initialize Response's body with application/octet-binary 
-FAIL Initialize Response's body with multipart/form-data promise_test: Unhandled rejection with value: undefined
+FAIL Initialize Response's body with multipart/form-data promise_test: Unhandled rejection with value: object "NotSupportedError: The operation is not supported."
 PASS Initialize Response's body with application/x-www-form-urlencoded;charset=UTF-8 
 PASS Initialize Response's body with text/plain;charset=UTF-8 
 PASS Read Response's body as readableStream 
index 21e1216..fb0b2f8 100644 (file)
@@ -8,14 +8,14 @@ FAIL Service Worker responds to fetch event with an existing client id assert_un
 PASS Service Worker does not respond to fetch event 
 PASS Service Worker responds to fetch event with null response body 
 PASS Service Worker fetches other file in fetch event 
-FAIL Service Worker responds to fetch event with POST form assert_unreached: unexpected rejection: assert_equals: expected "POST:application/x-www-form-urlencoded:testName1=testValue1&testName2=testValue2" but got "POST:application/x-www-form-urlencoded:" Reached unreachable code
+PASS Service Worker responds to fetch event with POST form 
 PASS Multiple calls of respondWith must throw InvalidStateErrors 
 PASS Service Worker event.respondWith must set the used flag 
 PASS Service Worker should expose FetchEvent URL fragments. 
 FAIL Service Worker responds to fetch event with the correct cache types assert_unreached: unexpected rejection: assert_equals: expected "no-cache" but got "default" Reached unreachable code
 PASS Service Worker should intercept EventSource 
 PASS Service Worker responds to fetch event with the correct integrity_metadata 
-FAIL FetchEvent#body is a string assert_equals: expected "i am the request body" but got ""
+PASS FetchEvent#body is a string 
 FAIL FetchEvent#body is a blob assert_equals: expected "it's me the blob and more blob!" but got ""
 PASS Service Worker responds to fetch event with the correct keepalive value 
 
index f342141..e2da83a 100644 (file)
@@ -4,11 +4,11 @@ FAIL event.request has the expected headers for same-origin GET. promise_test: U
 FAIL event.request has the expected headers for same-origin POST. promise_test: Unhandled rejection with value: object "Error: assert_array_equals: event.request has the expected headers for same-origin POST. lengths differ, expected 2 got 5"
 FAIL event.request has the expected headers for cross-origin GET. promise_test: Unhandled rejection with value: object "Error: assert_array_equals: event.request has the expected headers for cross-origin GET. lengths differ, expected 1 got 4"
 FAIL event.request has the expected headers for cross-origin POST. promise_test: Unhandled rejection with value: object "Error: assert_array_equals: event.request has the expected headers for cross-origin POST. lengths differ, expected 2 got 5"
-FAIL FetchEvent#request.body contains XHR request data (string) promise_test: Unhandled rejection with value: object "Error: assert_equals: expected "test string" but got """
+PASS FetchEvent#request.body contains XHR request data (string) 
 FAIL FetchEvent#request.body contains XHR request data (blob) promise_test: Unhandled rejection with value: object "Error: assert_equals: expected "test blob" but got """
-FAIL FetchEvent#request.method is set to XHR method promise_test: Unhandled rejection with value: object "Error: assert_equals: expected "test string xxx" but got """
-FAIL XHR using OPTIONS method promise_test: Unhandled rejection with value: object "Error: assert_equals: expected "test string xxx" but got """
-FAIL XHR with form data promise_test: Unhandled rejection with value: object "Error: assert_equals: expected "------WebKitFormBoundaryXQcTUBfCg2hFT0zU\r\nContent-Disposition: form-data; name=\"sample string\"\r\n\r\n1234567890\r\n------WebKitFormBoundaryXQcTUBfCg2hFT0zU\r\nContent-Disposition: form-data; name=\"sample blob\"; filename=\"blob\"\r\nContent-Type: application/octet-stream\r\n\r\nblob content\r\n------WebKitFormBoundaryXQcTUBfCg2hFT0zU\r\nContent-Disposition: form-data; name=\"sample file\"; filename=\"file.dat\"\r\nContent-Type: application/octet-stream\r\n\r\nfile content\r\n------WebKitFormBoundaryXQcTUBfCg2hFT0zU--\r\n" but got """
+PASS FetchEvent#request.method is set to XHR method 
+PASS XHR using OPTIONS method 
+FAIL XHR with form data promise_test: Unhandled rejection with value: object "Error: assert_equals: expected "------WebKitFormBoundaryvjEQu9gV1uP6AQOG\r\nContent-Disposition: form-data; name=\"sample string\"\r\n\r\n1234567890\r\n------WebKitFormBoundaryvjEQu9gV1uP6AQOG\r\nContent-Disposition: form-data; name=\"sample blob\"; filename=\"blob\"\r\nContent-Type: application/octet-stream\r\n\r\nblob content\r\n------WebKitFormBoundaryvjEQu9gV1uP6AQOG\r\nContent-Disposition: form-data; name=\"sample file\"; filename=\"file.dat\"\r\nContent-Type: application/octet-stream\r\n\r\nfile content\r\n------WebKitFormBoundaryvjEQu9gV1uP6AQOG--\r\n" but got """
 FAIL XHR with mode/credentials set promise_test: Unhandled rejection with value: object "Error: assert_equals: expected "include" but got "same-origin""
 PASS XHR to data URL 
 PASS restore global state 
index 776132c..d435084 100644 (file)
@@ -1,3 +1,33 @@
+2017-11-16  Youenn Fablet  <youenn@apple.com>
+
+        Service Worker should get the body of intercepted requests
+        https://bugs.webkit.org/show_bug.cgi?id=179776
+
+        Reviewed by Alex Christensen.
+
+        Test: http/tests/workers/service/service-worker-request-with-body.https.html
+
+        Make use of FetchBodyConsumer to store raw data for FetchRequest.
+        This is used when setting FetchRequest body from a FormData.
+        If FormData is only bytes (no blob, no file), FetchBodyConsumer will store that data.
+        This allows Service Worker to get access to simple request bodies.
+
+        * Modules/fetch/FetchBody.cpp:
+        (WebCore::FetchBody::fromFormData):
+        (WebCore::FetchBody::consume):
+        (WebCore::FetchBody::bodyAsFormData const): Making sure body is set appropriately when used to make fetch load.
+        * Modules/fetch/FetchBody.h: Making some methods private.
+        * Modules/fetch/FetchBodyConsumer.h: Adding accessors.
+        (WebCore::FetchBodyConsumer::hasData const):
+        (WebCore::FetchBodyConsumer::data const):
+        * Modules/fetch/FetchRequest.h:
+        (WebCore::FetchRequest::FetchRequest):
+        * platform/network/FormData.cpp:
+        (WebCore::FormData::asSharedBuffer const):
+        * platform/network/FormData.h:
+        * workers/service/context/ServiceWorkerFetch.cpp:
+        (WebCore::ServiceWorkerFetch::dispatchFetchEvent): Setting FetchRequest body based on given FormData.
+
 2017-11-16  Chris Dumez  <cdumez@apple.com>
 
         Make sure service workers get terminated between tests
index 2e15cd7..fa98dd8 100644 (file)
@@ -69,6 +69,21 @@ FetchBody FetchBody::extract(ScriptExecutionContext& context, Init&& value, Stri
     });
 }
 
+std::optional<FetchBody> FetchBody::fromFormData(FormData* formData)
+{
+    if (!formData || formData->isEmpty())
+        return std::nullopt;
+
+    if (auto buffer = formData->asSharedBuffer()) {
+        FetchBody body;
+        body.m_consumer.setData(buffer.releaseNonNull());
+        return WTFMove(body);
+    }
+
+    // FIXME: Support blob and form data bodies.
+    return std::nullopt;
+}
+
 void FetchBody::arrayBuffer(FetchBodyOwner& owner, Ref<DeferredPromise>&& promise)
 {
     m_consumer.setType(FetchBodyConsumer::Type::ArrayBuffer);
@@ -134,7 +149,7 @@ void FetchBody::consume(FetchBodyOwner& owner, Ref<DeferredPromise>&& promise)
     }
     if (isFormData()) {
         // FIXME: Support consuming FormData.
-        promise->reject();
+        promise->reject(NotSupportedError);
         return;
     }
 
@@ -231,6 +246,9 @@ RefPtr<FormData> FetchBody::bodyAsFormData(ScriptExecutionContext& context) cons
         body->generateFiles(&downcast<Document>(context));
         return body;
     }
+    if (auto* data = m_consumer.data())
+        return FormData::create(data->data(), data->size());
+
     ASSERT_NOT_REACHED();
     return nullptr;
 }
index dbaa844..6dce116 100644 (file)
@@ -54,18 +54,12 @@ public:
     void consumeAsStream(FetchBodyOwner&, FetchBodySource&);
 #endif
 
-    bool isBlob() const { return WTF::holds_alternative<Ref<const Blob>>(m_data); }
-    bool isFormData() const { return WTF::holds_alternative<Ref<FormData>>(m_data); }
-    bool isArrayBuffer() const { return WTF::holds_alternative<Ref<const ArrayBuffer>>(m_data); }
-    bool isArrayBufferView() const { return WTF::holds_alternative<Ref<const ArrayBufferView>>(m_data); }
-    bool isURLSearchParams() const { return WTF::holds_alternative<Ref<const URLSearchParams>>(m_data); }
-    bool isText() const { return WTF::holds_alternative<String>(m_data); }
-    bool hasReadableStream() const { return !!m_readableStream; }
-
     using Init = Variant<RefPtr<Blob>, RefPtr<ArrayBufferView>, RefPtr<ArrayBuffer>, RefPtr<DOMFormData>, RefPtr<URLSearchParams>, RefPtr<ReadableStream>, String>;
     static FetchBody extract(ScriptExecutionContext&, Init&&, String&);
     FetchBody() = default;
 
+    static std::optional<FetchBody> fromFormData(FormData*);
+
     void loadingFailed();
     void loadingSucceeded();
 
@@ -81,6 +75,8 @@ public:
     void cleanConsumer() { m_consumer.clean(); }
 
     FetchBody clone();
+
+    bool hasReadableStream() const { return !!m_readableStream; }
     const ReadableStream* readableStream() const { return m_readableStream.get(); }
     ReadableStream* readableStream() { return m_readableStream.get(); }
     void setReadableStream(Ref<ReadableStream>&& stream)
@@ -106,6 +102,13 @@ private:
     void consumeText(Ref<DeferredPromise>&&, const String&);
     void consumeBlob(FetchBodyOwner&, Ref<DeferredPromise>&&);
 
+    bool isBlob() const { return WTF::holds_alternative<Ref<const Blob>>(m_data); }
+    bool isFormData() const { return WTF::holds_alternative<Ref<FormData>>(m_data); }
+    bool isArrayBuffer() const { return WTF::holds_alternative<Ref<const ArrayBuffer>>(m_data); }
+    bool isArrayBufferView() const { return WTF::holds_alternative<Ref<const ArrayBufferView>>(m_data); }
+    bool isURLSearchParams() const { return WTF::holds_alternative<Ref<const URLSearchParams>>(m_data); }
+    bool isText() const { return WTF::holds_alternative<String>(m_data); }
+
     const Blob& blobBody() const { return WTF::get<Ref<const Blob>>(m_data).get(); }
     FormData& formDataBody() { return WTF::get<Ref<FormData>>(m_data).get(); }
     const FormData& formDataBody() const { return WTF::get<Ref<FormData>>(m_data).get(); }
index 7a34054..b467db6 100644 (file)
@@ -48,6 +48,8 @@ public:
     void append(const char* data, unsigned);
     void append(const unsigned char* data, unsigned);
 
+    bool hasData() const { return !!m_buffer; }
+    const SharedBuffer* data() const { return m_buffer.get(); }
     void setData(Ref<SharedBuffer>&& data) { m_buffer = WTFMove(data); }
 
     RefPtr<SharedBuffer> takeData();
@@ -64,8 +66,6 @@ public:
     void resolve(Ref<DeferredPromise>&&, ReadableStream*);
     void resolveWithData(Ref<DeferredPromise>&&, const unsigned char*, unsigned);
 
-    bool hasData() const { return !!m_buffer; }
-
     void loadingFailed();
     void loadingSucceeded();
 
index a0423ce..0568517 100644 (file)
@@ -104,6 +104,7 @@ inline FetchRequest::FetchRequest(ScriptExecutionContext& context, std::optional
     , m_options(WTFMove(options))
     , m_referrer(WTFMove(referrer))
 {
+    updateContentType();
 }
 
 } // namespace WebCore
index 724423e..3c8ee08 100644 (file)
@@ -428,4 +428,13 @@ uint64_t FormData::lengthInBytes() const
     return *m_lengthInBytes;
 }
 
+RefPtr<SharedBuffer> FormData::asSharedBuffer() const
+{
+    for (auto& element : m_elements) {
+        if (element.m_type != FormDataElement::Type::Data)
+            return nullptr;
+    }
+    return SharedBuffer::create(flatten());
+}
+
 } // namespace WebCore
index fe4f58e..6f9a7a7 100644 (file)
@@ -31,6 +31,7 @@ namespace WebCore {
 class DOMFormData;
 class Document;
 class File;
+class SharedBuffer;
 class TextEncoding;
 
 class FormDataElement {
@@ -234,6 +235,8 @@ public:
     const Vector<FormDataElement>& elements() const { return m_elements; }
     const Vector<char>& boundary() const { return m_boundary; }
 
+    RefPtr<SharedBuffer> asSharedBuffer() const;
+
     void generateFiles(Document*);
     void removeGeneratedFilesIfNeeded();
 
index b85d64b..7a2b03f 100644 (file)
@@ -93,9 +93,9 @@ Ref<FetchEvent> dispatchFetchEvent(Ref<Client>&& client, WorkerGlobalScope& glob
 {
     ASSERT(globalScope.isServiceWorkerGlobalScope());
 
-    // FIXME: Set request body and referrer.
+    // FIXME: Set request referrer.
     auto requestHeaders = FetchHeaders::create(FetchHeaders::Guard::Immutable, HTTPHeaderMap { request.httpHeaderFields() });
-    auto fetchRequest = FetchRequest::create(globalScope, std::nullopt, WTFMove(requestHeaders),  WTFMove(request), WTFMove(options), { });
+    auto fetchRequest = FetchRequest::create(globalScope, FetchBody::fromFormData(request.httpBody()), WTFMove(requestHeaders),  WTFMove(request), WTFMove(options), { });
 
     // FIXME: Initialize other FetchEvent::Init fields.
     FetchEvent::Init init;
index 22a7d1e..8412204 100644 (file)
@@ -1,3 +1,32 @@
+2017-11-16  Youenn Fablet  <youenn@apple.com>
+
+        Service Worker should get the body of intercepted requests
+        https://bugs.webkit.org/show_bug.cgi?id=179776
+
+        Reviewed by Alex Christensen.
+
+        Pass a FormDataReference when starting fetch IPC.
+        Convert this FormDataReference in a FormData and using it to set the FetchRequest body properly in Service Worker process.
+        Forbid fetch interception when URL is not HTTP/HTTPS.
+
+        * Platform/IPC/FormDataReference.h:
+        (IPC::FormDataReference::FormDataReference):
+        (IPC::FormDataReference::takeData):
+        (IPC::FormDataReference::encode const):
+        (IPC::FormDataReference::decode):
+        * StorageProcess/ServiceWorker/WebSWServerConnection.cpp:
+        (WebKit::WebSWServerConnection::startFetch):
+        * StorageProcess/ServiceWorker/WebSWServerConnection.h:
+        * StorageProcess/ServiceWorker/WebSWServerConnection.messages.in:
+        * WebProcess/Storage/WebSWClientConnection.cpp:
+        (WebKit::WebSWClientConnection::startFetch):
+        * WebProcess/Storage/WebSWContextManagerConnection.cpp:
+        (WebKit::WebSWContextManagerConnection::startFetch):
+        * WebProcess/Storage/WebSWContextManagerConnection.h:
+        * WebProcess/Storage/WebSWContextManagerConnection.messages.in:
+        * WebProcess/Storage/WebServiceWorkerProvider.cpp:
+        (WebKit::WebServiceWorkerProvider::handleFetch):
+
 2017-11-16  Daniel Bates  <dabates@apple.com>
 
         Add feature define for alternative presentation button element
index cd9da6a..a6d2ea8 100644 (file)
 #include "Decoder.h"
 #include "Encoder.h"
 #include <WebCore/FormData.h>
-#include <wtf/Vector.h>
 
 namespace IPC {
 
+// FIXME: In case of file based form data, we should pass sandbox extensions as well.
 class FormDataReference {
 public:
     FormDataReference() = default;
-
-    explicit FormDataReference(Ref<WebCore::FormData>&& data)
+    explicit FormDataReference(RefPtr<WebCore::FormData>&& data)
         : m_data(WTFMove(data))
     {
     }
 
-    Ref<WebCore::FormData> takeData() { return m_data.releaseNonNull(); }
+    RefPtr<WebCore::FormData> takeData() { return WTFMove(m_data); }
+
+    void encode(Encoder& encoder) const
+    {
+        encoder << !!m_data;
+        if (m_data)
+            encoder << *m_data;
+    }
 
-    void encode(Encoder& encoder) const { encoder << *m_data; }
     static std::optional<FormDataReference> decode(Decoder& decoder)
     {
+        std::optional<bool> hasFormData;
+        decoder >> hasFormData;
+        if (!hasFormData)
+            return std::nullopt;
+        if (!hasFormData.value())
+            return FormDataReference { };
+
         auto formData = WebCore::FormData::decode(decoder);
         if (!formData)
             return std::nullopt;
+
         return FormDataReference { formData.releaseNonNull() };
     }
 
index b7a9029..c1f154e 100644 (file)
@@ -107,9 +107,9 @@ void WebSWServerConnection::updateWorkerStateInClient(ServiceWorkerIdentifier wo
     send(Messages::WebSWClientConnection::UpdateWorkerState(worker, state));
 }
 
-void WebSWServerConnection::startFetch(uint64_t fetchIdentifier, std::optional<ServiceWorkerIdentifier> serviceWorkerIdentifier, const ResourceRequest& request, const FetchOptions& options)
+void WebSWServerConnection::startFetch(uint64_t fetchIdentifier, std::optional<ServiceWorkerIdentifier> serviceWorkerIdentifier, const ResourceRequest& request, const FetchOptions& options, const IPC::FormDataReference& formData)
 {
-    sendToContextProcess(Messages::WebSWContextManagerConnection::StartFetch(identifier(), fetchIdentifier, serviceWorkerIdentifier, request, options));
+    sendToContextProcess(Messages::WebSWContextManagerConnection::StartFetch { identifier(), fetchIdentifier, serviceWorkerIdentifier, request, options, formData });
 }
 
 void WebSWServerConnection::postMessageToServiceWorkerGlobalScope(ServiceWorkerIdentifier destinationServiceWorkerIdentifier, const IPC::DataReference& message, ServiceWorkerClientData&& source)
index 2fdef3c..3f13272 100644 (file)
@@ -74,7 +74,7 @@ private:
     void updateWorkerStateInClient(WebCore::ServiceWorkerIdentifier, WebCore::ServiceWorkerState) final;
     void fireUpdateFoundEvent(WebCore::ServiceWorkerRegistrationIdentifier) final;
 
-    void startFetch(uint64_t fetchIdentifier, std::optional<WebCore::ServiceWorkerIdentifier>, const WebCore::ResourceRequest&, const WebCore::FetchOptions&);
+    void startFetch(uint64_t fetchIdentifier, std::optional<WebCore::ServiceWorkerIdentifier>, const WebCore::ResourceRequest&, const WebCore::FetchOptions&, const IPC::FormDataReference&);
 
     void postMessageToServiceWorkerGlobalScope(WebCore::ServiceWorkerIdentifier destinationIdentifier, const IPC::DataReference& message, WebCore::ServiceWorkerClientData&& source);
 
index a881186..e214613 100644 (file)
@@ -31,7 +31,7 @@ messages -> WebSWServerConnection {
     ServiceWorkerStartedControllingClient(WebCore::ServiceWorkerIdentifier serviceWorkerIdentifier, uint64_t scriptExecutionContextIdentifier)
     ServiceWorkerStoppedControllingClient(WebCore::ServiceWorkerIdentifier serviceWorkerIdentifier, uint64_t scriptExecutionContextIdentifier)
 
-    StartFetch(uint64_t identifier, std::optional<WebCore::ServiceWorkerIdentifier> serviceWorkerIdentifier, WebCore::ResourceRequest request, struct WebCore::FetchOptions options)
+    StartFetch(uint64_t identifier, std::optional<WebCore::ServiceWorkerIdentifier> serviceWorkerIdentifier, WebCore::ResourceRequest request, struct WebCore::FetchOptions options, IPC::FormDataReference requestBody)
     PostMessageToServiceWorkerGlobalScope(WebCore::ServiceWorkerIdentifier destinationServiceWorkerIdentifier, IPC::DataReference message, struct WebCore::ServiceWorkerClientData source)
 
     DidResolveRegistrationPromise(WebCore::ServiceWorkerRegistrationKey key)
index 71b3b00..08fa802 100644 (file)
@@ -29,6 +29,7 @@
 #if ENABLE(SERVICE_WORKER)
 
 #include "DataReference.h"
+#include "FormDataReference.h"
 #include "Logging.h"
 #include "ServiceWorkerClientFetch.h"
 #include "StorageToWebProcessConnectionMessages.h"
@@ -159,7 +160,7 @@ Ref<ServiceWorkerClientFetch> WebSWClientConnection::startFetch(WebServiceWorker
 {
     ASSERT(loader->options().serviceWorkersMode != ServiceWorkersMode::None && loader->options().serviceWorkerIdentifier);
 
-    send(Messages::WebSWServerConnection::StartFetch(identifier, loader->options().serviceWorkerIdentifier, loader->originalRequest(), loader->options()));
+    send(Messages::WebSWServerConnection::StartFetch { identifier, loader->options().serviceWorkerIdentifier, loader->originalRequest(), loader->options(), IPC::FormDataReference { loader->originalRequest().httpBody() } });
     return ServiceWorkerClientFetch::create(provider, WTFMove(loader), identifier, m_connection.get(), WTFMove(callback));
 }
 
index 3de1b05..94aca3d 100644 (file)
@@ -29,6 +29,7 @@
 #if ENABLE(SERVICE_WORKER)
 
 #include "DataReference.h"
+#include "FormDataReference.h"
 #include "Logging.h"
 #include "StorageProcessMessages.h"
 #include "WebCacheStorageProvider.h"
@@ -131,7 +132,7 @@ void WebSWContextManagerConnection::serviceWorkerStartedWithMessage(ServiceWorke
         m_connectionToStorageProcess->send(Messages::WebSWServerToContextConnection::ScriptContextFailedToStart(serviceWorkerIdentifier, exceptionMessage), 0);
 }
 
-void WebSWContextManagerConnection::startFetch(uint64_t serverConnectionIdentifier, uint64_t fetchIdentifier, std::optional<ServiceWorkerIdentifier> serviceWorkerIdentifier, ResourceRequest&& request, FetchOptions&& options)
+void WebSWContextManagerConnection::startFetch(uint64_t serverConnectionIdentifier, uint64_t fetchIdentifier, std::optional<ServiceWorkerIdentifier> serviceWorkerIdentifier, ResourceRequest&& request, FetchOptions&& options, IPC::FormDataReference&& formData)
 {
     auto* serviceWorkerThreadProxy = serviceWorkerIdentifier ? SWContextManager::singleton().serviceWorkerThreadProxy(*serviceWorkerIdentifier) : nullptr;
     if (!serviceWorkerThreadProxy) {
@@ -140,6 +141,8 @@ void WebSWContextManagerConnection::startFetch(uint64_t serverConnectionIdentifi
     }
 
     auto client = WebServiceWorkerFetchTaskClient::create(m_connectionToStorageProcess.copyRef(), serverConnectionIdentifier, fetchIdentifier);
+
+    request.setHTTPBody(formData.takeData());
     serviceWorkerThreadProxy->thread().postFetchTask(WTFMove(client), WTFMove(request), WTFMove(options));
 }
 
index f199ce7..abc5251 100644 (file)
 #include "MessageReceiver.h"
 #include <WebCore/SWContextManager.h>
 
+namespace IPC {
+class FormDataReference;
+}
+
 namespace WebCore {
 struct FetchOptions;
 class ResourceRequest;
@@ -60,7 +64,7 @@ private:
     // IPC messages.
     void serviceWorkerStartedWithMessage(WebCore::ServiceWorkerIdentifier, const String& exceptionMessage) final;
     void installServiceWorker(const WebCore::ServiceWorkerContextData&);
-    void startFetch(uint64_t serverConnectionIdentifier, uint64_t fetchIdentifier, std::optional<WebCore::ServiceWorkerIdentifier>, WebCore::ResourceRequest&&, WebCore::FetchOptions&&);
+    void startFetch(uint64_t serverConnectionIdentifier, uint64_t fetchIdentifier, std::optional<WebCore::ServiceWorkerIdentifier>, WebCore::ResourceRequest&&, WebCore::FetchOptions&&, IPC::FormDataReference&&);
     void postMessageToServiceWorkerGlobalScope(WebCore::ServiceWorkerIdentifier destinationIdentifier, const IPC::DataReference& message, WebCore::ServiceWorkerClientData&& source);
     void fireInstallEvent(WebCore::ServiceWorkerIdentifier);
     void fireActivateEvent(WebCore::ServiceWorkerIdentifier);
index 8ab0580..73732cc 100644 (file)
@@ -24,7 +24,7 @@
 
 messages -> WebSWContextManagerConnection {
     InstallServiceWorker(struct WebCore::ServiceWorkerContextData contextData)
-    StartFetch(uint64_t serverConnectionIdentifier, uint64_t fetchIdentifier, std::optional<WebCore::ServiceWorkerIdentifier> serviceWorkerIdentifier, WebCore::ResourceRequest request, struct WebCore::FetchOptions options)
+    StartFetch(uint64_t serverConnectionIdentifier, uint64_t fetchIdentifier, std::optional<WebCore::ServiceWorkerIdentifier> serviceWorkerIdentifier, WebCore::ResourceRequest request, struct WebCore::FetchOptions options, IPC::FormDataReference requestBody)
     PostMessageToServiceWorkerGlobalScope(WebCore::ServiceWorkerIdentifier destinationIdentifier, IPC::DataReference message, struct WebCore::ServiceWorkerClientData source)
     FireInstallEvent(WebCore::ServiceWorkerIdentifier identifier)
     FireActivateEvent(WebCore::ServiceWorkerIdentifier identifier)
index 3782da6..99967d7 100644 (file)
@@ -72,7 +72,7 @@ static inline bool shouldHandleFetch(const ResourceLoaderOptions& options)
 
 void WebServiceWorkerProvider::handleFetch(ResourceLoader& loader, CachedResource* resource, PAL::SessionID sessionID, ServiceWorkerClientFetch::Callback&& callback)
 {
-    if (!shouldHandleFetch(loader.options())) {
+    if (!loader.request().url().protocolIsInHTTPFamily() || !shouldHandleFetch(loader.options())) {
         callback(ServiceWorkerClientFetch::Result::Unhandled);
         return;
     }