Title: [266087] trunk
Revision
266087
Author
commit-qu...@webkit.org
Date
2020-08-24 15:36:02 -0700 (Mon, 24 Aug 2020)

Log Message

Implement Request/Response consuming as FormData
https://bugs.webkit.org/show_bug.cgi?id=215671

Patch by Alex Christensen <achristen...@webkit.org> on 2020-08-24
Reviewed by Darin Adler.

LayoutTests/imported/w3c:

* web-platform-tests/fetch/api/abort/general.any-expected.txt:
* web-platform-tests/fetch/api/abort/general.any.worker-expected.txt:
* web-platform-tests/fetch/api/request/request-consume-empty-expected.txt:
This remaining failing test now fails similarly in all browsers.
* web-platform-tests/fetch/api/request/request-consume-expected.txt:
* web-platform-tests/fetch/api/request/request-init-002-expected.txt:
* web-platform-tests/fetch/api/response/response-consume-empty-expected.txt:
This remaining failing test now fails similarly in all browsers.
* web-platform-tests/fetch/api/response/response-consume-expected.txt:
* web-platform-tests/fetch/api/response/response-error-from-stream-expected.txt:
This change makes the formData failures in this file look like all the other failures in this file,
which should be fixed together in a separate patch.
* web-platform-tests/fetch/api/response/response-init-002-expected.txt:
* web-platform-tests/url/urlencoded-parser.any-expected.txt:
* web-platform-tests/url/urlencoded-parser.any.worker-expected.txt:
* web-platform-tests/service-workers/service-worker/fetch-event-respond-with-custom-response.https-expected.txt:

Source/WebCore:

Covered by many newly passing WPT tests, for most of which Safari was the only failing browser.

* Modules/fetch/FetchBody.cpp:
(WebCore::FetchBody::formData):
(WebCore::FetchBody::consume):
(WebCore::FetchBody::consumeFormData):
* Modules/fetch/FetchBody.h:
* Modules/fetch/FetchBodyConsumer.cpp:
(WebCore::formDataFromData):
(WebCore::resolveWithTypeAndData):
(WebCore::FetchBodyConsumer::resolve):
* Modules/fetch/FetchBodyConsumer.h:

Source/WebKit:

* WebProcess/Storage/WebServiceWorkerFetchTaskClient.cpp:
(WebKit::WebServiceWorkerFetchTaskClient::didReceiveFormDataAndFinish):
Add a fast path that allows non-blob FormData responses from service workers to not hang.
This part is covered by this layout test:
imported/w3c/web-platform-tests/service-workers/service-worker/fetch-event-respond-with-custom-response.https.html

Source/WTF:

In order to be compatible with other browsers, we need a verson of String::fromUTF8 that
uses U8_NEXT_OR_FFFD instead of U8_NEXT, but changing that across the board will break other things.
Leave everything else as it is, use templates and constexpr to not add any branches, but add
String::fromUTF8ReplacingInvalidSequences to allow me to make our FormData consuming compatible with other browsers.

* wtf/text/WTFString.cpp:
(WTF::fromUTF8Helper):
(WTF::String::fromUTF8):
(WTF::String::fromUTF8ReplacingInvalidSequences):
* wtf/text/WTFString.h:
* wtf/unicode/UTF8Conversion.cpp:
(WTF::Unicode::convertUTF8ToUTF16Impl):
(WTF::Unicode::convertUTF8ToUTF16):
(WTF::Unicode::convertUTF8ToUTF16ReplacingInvalidSequences):
* wtf/unicode/UTF8Conversion.h:

Modified Paths

Diff

Modified: trunk/LayoutTests/imported/w3c/ChangeLog (266086 => 266087)


--- trunk/LayoutTests/imported/w3c/ChangeLog	2020-08-24 21:41:43 UTC (rev 266086)
+++ trunk/LayoutTests/imported/w3c/ChangeLog	2020-08-24 22:36:02 UTC (rev 266087)
@@ -1,3 +1,27 @@
+2020-08-24  Alex Christensen  <achristen...@webkit.org>
+
+        Implement Request/Response consuming as FormData
+        https://bugs.webkit.org/show_bug.cgi?id=215671
+
+        Reviewed by Darin Adler.
+
+        * web-platform-tests/fetch/api/abort/general.any-expected.txt:
+        * web-platform-tests/fetch/api/abort/general.any.worker-expected.txt:
+        * web-platform-tests/fetch/api/request/request-consume-empty-expected.txt:
+        This remaining failing test now fails similarly in all browsers.
+        * web-platform-tests/fetch/api/request/request-consume-expected.txt:
+        * web-platform-tests/fetch/api/request/request-init-002-expected.txt:
+        * web-platform-tests/fetch/api/response/response-consume-empty-expected.txt:
+        This remaining failing test now fails similarly in all browsers.
+        * web-platform-tests/fetch/api/response/response-consume-expected.txt:
+        * web-platform-tests/fetch/api/response/response-error-from-stream-expected.txt:
+        This change makes the formData failures in this file look like all the other failures in this file,
+        which should be fixed together in a separate patch.
+        * web-platform-tests/fetch/api/response/response-init-002-expected.txt:
+        * web-platform-tests/url/urlencoded-parser.any-expected.txt:
+        * web-platform-tests/url/urlencoded-parser.any.worker-expected.txt:
+        * web-platform-tests/service-workers/service-worker/fetch-event-respond-with-custom-response.https-expected.txt:
+
 2020-08-24  Emilio Cobos Álvarez  <emi...@crisal.io>
 
         Support quotes:auto and fix quotes serialization.

Modified: trunk/LayoutTests/imported/w3c/web-platform-tests/fetch/api/abort/general.any-expected.txt (266086 => 266087)


--- trunk/LayoutTests/imported/w3c/web-platform-tests/fetch/api/abort/general.any-expected.txt	2020-08-24 21:41:43 UTC (rev 266086)
+++ trunk/LayoutTests/imported/w3c/web-platform-tests/fetch/api/abort/general.any-expected.txt	2020-08-24 22:36:02 UTC (rev 266087)
@@ -38,7 +38,7 @@
 PASS Underlying connection is closed when aborting after receiving response - no-cors 
 PASS Fetch aborted & connection closed when aborted after calling response.arrayBuffer() 
 PASS Fetch aborted & connection closed when aborted after calling response.blob() 
-FAIL Fetch aborted & connection closed when aborted after calling response.formData() promise_rejects_dom: function "function () { throw e }" threw object "NotSupportedError: The operation is not supported." that is not a DOMException AbortError: property "code" is equal to 9, expected 20
+PASS Fetch aborted & connection closed when aborted after calling response.formData() 
 PASS Fetch aborted & connection closed when aborted after calling response.json() 
 PASS Fetch aborted & connection closed when aborted after calling response.text() 
 PASS Stream errors once aborted. Underlying connection closed. 

Modified: trunk/LayoutTests/imported/w3c/web-platform-tests/fetch/api/abort/general.any.worker-expected.txt (266086 => 266087)


--- trunk/LayoutTests/imported/w3c/web-platform-tests/fetch/api/abort/general.any.worker-expected.txt	2020-08-24 21:41:43 UTC (rev 266086)
+++ trunk/LayoutTests/imported/w3c/web-platform-tests/fetch/api/abort/general.any.worker-expected.txt	2020-08-24 22:36:02 UTC (rev 266087)
@@ -38,7 +38,7 @@
 PASS Underlying connection is closed when aborting after receiving response - no-cors 
 PASS Fetch aborted & connection closed when aborted after calling response.arrayBuffer() 
 PASS Fetch aborted & connection closed when aborted after calling response.blob() 
-FAIL Fetch aborted & connection closed when aborted after calling response.formData() promise_rejects_dom: function "function () { throw e }" threw object "NotSupportedError: The operation is not supported." that is not a DOMException AbortError: property "code" is equal to 9, expected 20
+PASS Fetch aborted & connection closed when aborted after calling response.formData() 
 PASS Fetch aborted & connection closed when aborted after calling response.json() 
 PASS Fetch aborted & connection closed when aborted after calling response.text() 
 PASS Stream errors once aborted. Underlying connection closed. 

Modified: trunk/LayoutTests/imported/w3c/web-platform-tests/fetch/api/request/request-consume-empty-expected.txt (266086 => 266087)


--- trunk/LayoutTests/imported/w3c/web-platform-tests/fetch/api/request/request-consume-empty-expected.txt	2020-08-24 21:41:43 UTC (rev 266086)
+++ trunk/LayoutTests/imported/w3c/web-platform-tests/fetch/api/request/request-consume-empty-expected.txt	2020-08-24 22:36:02 UTC (rev 266087)
@@ -11,6 +11,6 @@
 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: object "NotSupportedError: The operation is not supported."
+FAIL Consume empty FormData request body as text assert_equals: Resolved value should be empty expected 0 but got 44
 PASS Consume empty ArrayBuffer request body as text 
 

Modified: trunk/LayoutTests/imported/w3c/web-platform-tests/fetch/api/request/request-consume-expected.txt (266086 => 266087)


--- trunk/LayoutTests/imported/w3c/web-platform-tests/fetch/api/request/request-consume-expected.txt	2020-08-24 21:41:43 UTC (rev 266086)
+++ trunk/LayoutTests/imported/w3c/web-platform-tests/fetch/api/request/request-consume-expected.txt	2020-08-24 22:36:02 UTC (rev 266087)
@@ -23,7 +23,7 @@
 PASS Consume DataView request's body as blob 
 PASS Consume DataView request's body as arrayBuffer 
 PASS Consume DataView request's body as JSON 
-FAIL Consume FormData request's body as FormData promise_test: Unhandled rejection with value: object "NotSupportedError: The operation is not supported."
+PASS Consume FormData request's body as FormData 
 PASS Consume blob response's body as blob 
 PASS Consume blob response's body as text 
 PASS Consume blob response's body as json 

Modified: trunk/LayoutTests/imported/w3c/web-platform-tests/fetch/api/request/request-init-002-expected.txt (266086 => 266087)


--- trunk/LayoutTests/imported/w3c/web-platform-tests/fetch/api/request/request-init-002-expected.txt	2020-08-24 21:41:43 UTC (rev 266086)
+++ trunk/LayoutTests/imported/w3c/web-platform-tests/fetch/api/request/request-init-002-expected.txt	2020-08-24 22:36:02 UTC (rev 266087)
@@ -3,7 +3,7 @@
 PASS Initialize Request's body with "undefined", undefined 
 PASS Initialize Request's body with "null", null 
 PASS Initialize Request's body with "[object Blob]", application/octet-binary 
-FAIL Initialize Request's body with "[object FormData]", multipart/form-data promise_test: Unhandled rejection with value: object "NotSupportedError: The operation is not supported."
+PASS Initialize Request's body with "[object FormData]", multipart/form-data 
 PASS Initialize Request's body with "This is a USVString", text/plain;charset=UTF-8 
 PASS Initialize Request's body with "hi!", text/plain;charset=UTF-8 
 PASS Initialize Request's body with "name=value", application/x-www-form-urlencoded;charset=UTF-8 

Modified: trunk/LayoutTests/imported/w3c/web-platform-tests/fetch/api/response/response-consume-empty-expected.txt (266086 => 266087)


--- trunk/LayoutTests/imported/w3c/web-platform-tests/fetch/api/response/response-consume-empty-expected.txt	2020-08-24 21:41:43 UTC (rev 266086)
+++ trunk/LayoutTests/imported/w3c/web-platform-tests/fetch/api/response/response-consume-empty-expected.txt	2020-08-24 22:36:02 UTC (rev 266087)
@@ -11,6 +11,6 @@
 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: object "NotSupportedError: The operation is not supported."
+FAIL Consume empty FormData response body as text assert_equals: Resolved value should be empty expected 0 but got 44
 PASS Consume empty ArrayBuffer response body as text 
 

Modified: trunk/LayoutTests/imported/w3c/web-platform-tests/fetch/api/response/response-consume-expected.txt (266086 => 266087)


--- trunk/LayoutTests/imported/w3c/web-platform-tests/fetch/api/response/response-consume-expected.txt	2020-08-24 21:41:43 UTC (rev 266086)
+++ trunk/LayoutTests/imported/w3c/web-platform-tests/fetch/api/response/response-consume-expected.txt	2020-08-24 22:36:02 UTC (rev 266087)
@@ -3,39 +3,27 @@
 PASS Consume response's body: from text to blob 
 PASS Consume response's body: from text to arrayBuffer 
 PASS Consume response's body: from text to json 
-FAIL Consume response's body: from text with correct multipart type to formData promise_test: Unhandled rejection with value: object "NotSupportedError: The operation is not supported."
-FAIL Consume response's body: from text with correct multipart type to formData with BOM promise_test: Unhandled rejection with value: object "NotSupportedError: The operation is not supported."
-FAIL Consume response's body: from text without correct multipart type to formData (error case) promise_rejects_js: function "function () { throw e }" threw object "NotSupportedError: The operation is not supported." ("NotSupportedError") expected instance of function "function TypeError() {
-    [native code]
-}" ("TypeError")
-FAIL Consume response's body: from text with correct urlencoded type to formData promise_test: Unhandled rejection with value: object "NotSupportedError: The operation is not supported."
-FAIL Consume response's body: from text without correct urlencoded type to formData (error case) promise_rejects_js: function "function () { throw e }" threw object "NotSupportedError: The operation is not supported." ("NotSupportedError") expected instance of function "function TypeError() {
-    [native code]
-}" ("TypeError")
+PASS Consume response's body: from text with correct multipart type to formData 
+PASS Consume response's body: from text with correct multipart type to formData with BOM 
+PASS Consume response's body: from text without correct multipart type to formData (error case) 
+PASS Consume response's body: from text with correct urlencoded type to formData 
+PASS Consume response's body: from text without correct urlencoded type to formData (error case) 
 PASS Consume response's body: from blob to blob 
 PASS Consume response's body: from blob to text 
 PASS Consume response's body: from blob to arrayBuffer 
 PASS Consume response's body: from blob to json 
-FAIL Consume response's body: from blob with correct multipart type to formData promise_test: Unhandled rejection with value: object "NotSupportedError: The operation is not supported."
-FAIL Consume response's body: from blob without correct multipart type to formData (error case) promise_rejects_js: function "function () { throw e }" threw object "NotSupportedError: The operation is not supported." ("NotSupportedError") expected instance of function "function TypeError() {
-    [native code]
-}" ("TypeError")
-FAIL Consume response's body: from blob with correct urlencoded type to formData promise_test: Unhandled rejection with value: object "NotSupportedError: The operation is not supported."
-FAIL Consume response's body: from blob without correct urlencoded type to formData (error case) promise_rejects_js: function "function () { throw e }" threw object "NotSupportedError: The operation is not supported." ("NotSupportedError") expected instance of function "function TypeError() {
-    [native code]
-}" ("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) promise_rejects_js: function "function () { throw e }" threw object "NotSupportedError: The operation is not supported." ("NotSupportedError") expected instance of function "function TypeError() {
-    [native code]
-}" ("TypeError")
-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) promise_rejects_js: function "function () { throw e }" threw object "NotSupportedError: The operation is not supported." ("NotSupportedError") expected instance of function "function TypeError() {
-    [native code]
-}" ("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"
+PASS Consume response's body: from blob with correct multipart type to formData 
+PASS Consume response's body: from blob without correct multipart type to formData (error case) 
+PASS Consume response's body: from blob with correct urlencoded type to formData 
+PASS Consume response's body: from blob without correct urlencoded type to formData (error case) 
+PASS Consume response's body: from FormData to formData 
+PASS Consume response's body: from FormData without correct type to formData (error case) 
+PASS Consume response's body: from FormData to blob 
+PASS Consume response's body: from FormData to text 
+PASS Consume response's body: from FormData to arrayBuffer 
+PASS Consume response's body: from URLSearchParams to formData 
+PASS Consume response's body: from URLSearchParams without correct type to formData (error case) 
+PASS Consume response's body: from URLSearchParams to blob 
 PASS Consume response's body: from URLSearchParams to text 
 PASS Consume response's body: from URLSearchParams to arrayBuffer 
 PASS Consume response's body: from stream to blob 
@@ -42,19 +30,13 @@
 PASS Consume response's body: from stream to text 
 PASS Consume response's body: from stream to arrayBuffer 
 PASS Consume response's body: from stream to json 
-FAIL Consume response's body: from stream with correct multipart type to formData promise_test: Unhandled rejection with value: object "NotSupportedError: The operation is not supported."
-FAIL Consume response's body: from stream without correct multipart type to formData (error case) promise_rejects_js: function "function () { throw e }" threw object "NotSupportedError: The operation is not supported." ("NotSupportedError") expected instance of function "function TypeError() {
-    [native code]
-}" ("TypeError")
-FAIL Consume response's body: from stream with correct urlencoded type to formData promise_test: Unhandled rejection with value: object "NotSupportedError: The operation is not supported."
-FAIL Consume response's body: from stream without correct urlencoded type to formData (error case) promise_rejects_js: function "function () { throw e }" threw object "NotSupportedError: The operation is not supported." ("NotSupportedError") expected instance of function "function TypeError() {
-    [native code]
-}" ("TypeError")
+PASS Consume response's body: from stream with correct multipart type to formData 
+PASS Consume response's body: from stream without correct multipart type to formData (error case) 
+PASS Consume response's body: from stream with correct urlencoded type to formData 
+PASS Consume response's body: from stream without correct urlencoded type to formData (error case) 
 PASS Consume response's body: from fetch to blob 
 PASS Consume response's body: from fetch to text 
 PASS Consume response's body: from fetch to arrayBuffer 
-FAIL Consume response's body: from fetch without correct type to formData (error case) promise_rejects_js: function "function () { throw e }" threw object "NotSupportedError: The operation is not supported." ("NotSupportedError") expected instance of function "function TypeError() {
-    [native code]
-}" ("TypeError")
+PASS Consume response's body: from fetch without correct type to formData (error case) 
 FAIL Consume response's body: from multipart form data blob to formData promise_test: Unhandled rejection with value: object "NotSupportedError: The operation is not supported."
 

Modified: trunk/LayoutTests/imported/w3c/web-platform-tests/fetch/api/response/response-error-from-stream-expected.txt (266086 => 266087)


--- trunk/LayoutTests/imported/w3c/web-platform-tests/fetch/api/response/response-error-from-stream-expected.txt	2020-08-24 21:41:43 UTC (rev 266086)
+++ trunk/LayoutTests/imported/w3c/web-platform-tests/fetch/api/response/response-error-from-stream-expected.txt	2020-08-24 22:36:02 UTC (rev 266087)
@@ -3,12 +3,12 @@
 PASS ReadableStreamDefaultReader Promise receives ReadableStream pull() Error 
 FAIL ReadableStream start() Error propagates to Response.arrayBuffer() Promise promise_rejects_exactly: CustomTestError should propagate function "function () { throw e }" threw object "TypeError: Error: Start error" but we expected it to throw object "Error: Start error"
 FAIL ReadableStream start() Error propagates to Response.blob() Promise promise_rejects_exactly: CustomTestError should propagate function "function () { throw e }" threw object "TypeError: Error: Start error" but we expected it to throw object "Error: Start error"
-FAIL ReadableStream start() Error propagates to Response.formData() Promise promise_rejects_exactly: CustomTestError should propagate function "function () { throw e }" threw object "NotSupportedError: The operation is not supported." but we expected it to throw object "Error: Start error"
+FAIL ReadableStream start() Error propagates to Response.formData() Promise promise_rejects_exactly: CustomTestError should propagate function "function () { throw e }" threw object "TypeError: Error: Start error" but we expected it to throw object "Error: Start error"
 FAIL ReadableStream start() Error propagates to Response.json() Promise promise_rejects_exactly: CustomTestError should propagate function "function () { throw e }" threw object "TypeError: Error: Start error" but we expected it to throw object "Error: Start error"
 FAIL ReadableStream start() Error propagates to Response.text() Promise promise_rejects_exactly: CustomTestError should propagate function "function () { throw e }" threw object "TypeError: Error: Start error" but we expected it to throw object "Error: Start error"
 FAIL ReadableStream pull() Error propagates to Response.arrayBuffer() Promise promise_rejects_exactly: CustomTestError should propagate function "function () { throw e }" threw object "TypeError: Error: Pull error" but we expected it to throw object "Error: Pull error"
 FAIL ReadableStream pull() Error propagates to Response.blob() Promise promise_rejects_exactly: CustomTestError should propagate function "function () { throw e }" threw object "TypeError: Error: Pull error" but we expected it to throw object "Error: Pull error"
-FAIL ReadableStream pull() Error propagates to Response.formData() Promise promise_rejects_exactly: CustomTestError should propagate function "function () { throw e }" threw object "NotSupportedError: The operation is not supported." but we expected it to throw object "Error: Pull error"
+FAIL ReadableStream pull() Error propagates to Response.formData() Promise promise_rejects_exactly: CustomTestError should propagate function "function () { throw e }" threw object "TypeError: Error: Pull error" but we expected it to throw object "Error: Pull error"
 FAIL ReadableStream pull() Error propagates to Response.json() Promise promise_rejects_exactly: CustomTestError should propagate function "function () { throw e }" threw object "TypeError: Error: Pull error" but we expected it to throw object "Error: Pull error"
 FAIL ReadableStream pull() Error propagates to Response.text() Promise promise_rejects_exactly: CustomTestError should propagate function "function () { throw e }" threw object "TypeError: Error: Pull error" but we expected it to throw object "Error: Pull error"
 

Modified: trunk/LayoutTests/imported/w3c/web-platform-tests/fetch/api/response/response-init-002-expected.txt (266086 => 266087)


--- trunk/LayoutTests/imported/w3c/web-platform-tests/fetch/api/response/response-init-002-expected.txt	2020-08-24 21:41:43 UTC (rev 266086)
+++ trunk/LayoutTests/imported/w3c/web-platform-tests/fetch/api/response/response-init-002-expected.txt	2020-08-24 22:36:02 UTC (rev 266087)
@@ -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: object "NotSupportedError: The operation is not supported."
+PASS Initialize Response's body with multipart/form-data 
 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 

Modified: trunk/LayoutTests/imported/w3c/web-platform-tests/service-workers/service-worker/fetch-event-respond-with-custom-response.https-expected.txt (266086 => 266087)


--- trunk/LayoutTests/imported/w3c/web-platform-tests/service-workers/service-worker/fetch-event-respond-with-custom-response.https-expected.txt	2020-08-24 21:41:43 UTC (rev 266086)
+++ trunk/LayoutTests/imported/w3c/web-platform-tests/service-workers/service-worker/fetch-event-respond-with-custom-response.https-expected.txt	2020-08-24 22:36:02 UTC (rev 266087)
@@ -3,7 +3,7 @@
 PASS Subresource built from a blob 
 PASS Subresource built from a buffer 
 PASS Subresource built from a buffer-view 
-FAIL Subresource built from form-data promise_test: Unhandled rejection with value: object "NotSupportedError: The operation is not supported."
+PASS Subresource built from form-data 
 PASS Subresource built from search-params 
 PASS Navigation resource built from a string 
 PASS Navigation resource built from a blob 

Modified: trunk/LayoutTests/imported/w3c/web-platform-tests/url/urlencoded-parser.any-expected.txt (266086 => 266087)


--- trunk/LayoutTests/imported/w3c/web-platform-tests/url/urlencoded-parser.any-expected.txt	2020-08-24 21:41:43 UTC (rev 266086)
+++ trunk/LayoutTests/imported/w3c/web-platform-tests/url/urlencoded-parser.any-expected.txt	2020-08-24 22:36:02 UTC (rev 266087)
@@ -1,104 +1,104 @@
 
 PASS URLSearchParams constructed with: test 
-FAIL request.formData() with input: test promise_test: Unhandled rejection with value: object "NotSupportedError: The operation is not supported."
-FAIL response.formData() with input: test promise_test: Unhandled rejection with value: object "NotSupportedError: The operation is not supported."
+PASS request.formData() with input: test 
+PASS response.formData() with input: test 
 PASS URLSearchParams constructed with: test= 
-FAIL request.formData() with input: test= promise_test: Unhandled rejection with value: object "NotSupportedError: The operation is not supported."
-FAIL response.formData() with input: test= promise_test: Unhandled rejection with value: object "NotSupportedError: The operation is not supported."
+PASS request.formData() with input: test= 
+PASS response.formData() with input: test= 
 PASS URLSearchParams constructed with: %EF%BB%BFtest=%EF%BB%BF 
-FAIL request.formData() with input: %EF%BB%BFtest=%EF%BB%BF promise_test: Unhandled rejection with value: object "NotSupportedError: The operation is not supported."
-FAIL response.formData() with input: %EF%BB%BFtest=%EF%BB%BF promise_test: Unhandled rejection with value: object "NotSupportedError: The operation is not supported."
-FAIL URLSearchParams constructed with: %FE%FF assert_array_equals: expected property 0 to be "\ufffd\ufffd" but got "" (expected array ["\ufffd\ufffd", ""] got ["", ""])
-FAIL request.formData() with input: %FE%FF promise_test: Unhandled rejection with value: object "NotSupportedError: The operation is not supported."
-FAIL response.formData() with input: %FE%FF promise_test: Unhandled rejection with value: object "NotSupportedError: The operation is not supported."
-FAIL URLSearchParams constructed with: %FF%FE assert_array_equals: expected property 0 to be "\ufffd\ufffd" but got "" (expected array ["\ufffd\ufffd", ""] got ["", ""])
-FAIL request.formData() with input: %FF%FE promise_test: Unhandled rejection with value: object "NotSupportedError: The operation is not supported."
-FAIL response.formData() with input: %FF%FE promise_test: Unhandled rejection with value: object "NotSupportedError: The operation is not supported."
+PASS request.formData() with input: %EF%BB%BFtest=%EF%BB%BF 
+PASS response.formData() with input: %EF%BB%BFtest=%EF%BB%BF 
+PASS URLSearchParams constructed with: %FE%FF 
+PASS request.formData() with input: %FE%FF 
+PASS response.formData() with input: %FE%FF 
+PASS URLSearchParams constructed with: %FF%FE 
+PASS request.formData() with input: %FF%FE 
+PASS response.formData() with input: %FF%FE 
 PASS URLSearchParams constructed with: †&†=x 
-FAIL request.formData() with input: †&†=x promise_test: Unhandled rejection with value: object "NotSupportedError: The operation is not supported."
-FAIL response.formData() with input: †&†=x promise_test: Unhandled rejection with value: object "NotSupportedError: The operation is not supported."
-FAIL URLSearchParams constructed with: %C2 assert_array_equals: expected property 0 to be "\ufffd" but got "" (expected array ["\ufffd", ""] got ["", ""])
-FAIL request.formData() with input: %C2 promise_test: Unhandled rejection with value: object "NotSupportedError: The operation is not supported."
-FAIL response.formData() with input: %C2 promise_test: Unhandled rejection with value: object "NotSupportedError: The operation is not supported."
-FAIL URLSearchParams constructed with: %C2x assert_array_equals: expected property 0 to be "\ufffdx" but got "" (expected array ["\ufffdx", ""] got ["", ""])
-FAIL request.formData() with input: %C2x promise_test: Unhandled rejection with value: object "NotSupportedError: The operation is not supported."
-FAIL response.formData() with input: %C2x promise_test: Unhandled rejection with value: object "NotSupportedError: The operation is not supported."
-FAIL URLSearchParams constructed with: _charset_=windows-1252&test=%C2x assert_array_equals: expected property 1 to be "\ufffdx" but got "" (expected array ["test", "\ufffdx"] got ["test", ""])
-FAIL request.formData() with input: _charset_=windows-1252&test=%C2x promise_test: Unhandled rejection with value: object "NotSupportedError: The operation is not supported."
-FAIL response.formData() with input: _charset_=windows-1252&test=%C2x promise_test: Unhandled rejection with value: object "NotSupportedError: The operation is not supported."
+PASS request.formData() with input: †&†=x 
+PASS response.formData() with input: †&†=x 
+PASS URLSearchParams constructed with: %C2 
+PASS request.formData() with input: %C2 
+PASS response.formData() with input: %C2 
+PASS URLSearchParams constructed with: %C2x 
+PASS request.formData() with input: %C2x 
+PASS response.formData() with input: %C2x 
+PASS URLSearchParams constructed with: _charset_=windows-1252&test=%C2x 
+PASS request.formData() with input: _charset_=windows-1252&test=%C2x 
+PASS response.formData() with input: _charset_=windows-1252&test=%C2x 
 PASS URLSearchParams constructed with:  
-FAIL request.formData() with input:  promise_test: Unhandled rejection with value: object "NotSupportedError: The operation is not supported."
-FAIL response.formData() with input:  promise_test: Unhandled rejection with value: object "NotSupportedError: The operation is not supported."
+PASS request.formData() with input:  
+PASS response.formData() with input:  
 PASS URLSearchParams constructed with: a 
-FAIL request.formData() with input: a promise_test: Unhandled rejection with value: object "NotSupportedError: The operation is not supported."
-FAIL response.formData() with input: a promise_test: Unhandled rejection with value: object "NotSupportedError: The operation is not supported."
+PASS request.formData() with input: a 
+PASS response.formData() with input: a 
 PASS URLSearchParams constructed with: a=b 
-FAIL request.formData() with input: a=b promise_test: Unhandled rejection with value: object "NotSupportedError: The operation is not supported."
-FAIL response.formData() with input: a=b promise_test: Unhandled rejection with value: object "NotSupportedError: The operation is not supported."
+PASS request.formData() with input: a=b 
+PASS response.formData() with input: a=b 
 PASS URLSearchParams constructed with: a= 
-FAIL request.formData() with input: a= promise_test: Unhandled rejection with value: object "NotSupportedError: The operation is not supported."
-FAIL response.formData() with input: a= promise_test: Unhandled rejection with value: object "NotSupportedError: The operation is not supported."
+PASS request.formData() with input: a= 
+PASS response.formData() with input: a= 
 PASS URLSearchParams constructed with: =b 
-FAIL request.formData() with input: =b promise_test: Unhandled rejection with value: object "NotSupportedError: The operation is not supported."
-FAIL response.formData() with input: =b promise_test: Unhandled rejection with value: object "NotSupportedError: The operation is not supported."
+PASS request.formData() with input: =b 
+PASS response.formData() with input: =b 
 PASS URLSearchParams constructed with: & 
-FAIL request.formData() with input: & promise_test: Unhandled rejection with value: object "NotSupportedError: The operation is not supported."
-FAIL response.formData() with input: & promise_test: Unhandled rejection with value: object "NotSupportedError: The operation is not supported."
+PASS request.formData() with input: & 
+PASS response.formData() with input: & 
 PASS URLSearchParams constructed with: &a 
-FAIL request.formData() with input: &a promise_test: Unhandled rejection with value: object "NotSupportedError: The operation is not supported."
-FAIL response.formData() with input: &a promise_test: Unhandled rejection with value: object "NotSupportedError: The operation is not supported."
+PASS request.formData() with input: &a 
+PASS response.formData() with input: &a 
 PASS URLSearchParams constructed with: a& 
-FAIL request.formData() with input: a& promise_test: Unhandled rejection with value: object "NotSupportedError: The operation is not supported."
-FAIL response.formData() with input: a& promise_test: Unhandled rejection with value: object "NotSupportedError: The operation is not supported."
+PASS request.formData() with input: a& 
+PASS response.formData() with input: a& 
 PASS URLSearchParams constructed with: a&a 
-FAIL request.formData() with input: a&a promise_test: Unhandled rejection with value: object "NotSupportedError: The operation is not supported."
-FAIL response.formData() with input: a&a promise_test: Unhandled rejection with value: object "NotSupportedError: The operation is not supported."
+PASS request.formData() with input: a&a 
+PASS response.formData() with input: a&a 
 PASS URLSearchParams constructed with: a&b&c 
-FAIL request.formData() with input: a&b&c promise_test: Unhandled rejection with value: object "NotSupportedError: The operation is not supported."
-FAIL response.formData() with input: a&b&c promise_test: Unhandled rejection with value: object "NotSupportedError: The operation is not supported."
+PASS request.formData() with input: a&b&c 
+PASS response.formData() with input: a&b&c 
 PASS URLSearchParams constructed with: a=b&c=d 
-FAIL request.formData() with input: a=b&c=d promise_test: Unhandled rejection with value: object "NotSupportedError: The operation is not supported."
-FAIL response.formData() with input: a=b&c=d promise_test: Unhandled rejection with value: object "NotSupportedError: The operation is not supported."
+PASS request.formData() with input: a=b&c=d 
+PASS response.formData() with input: a=b&c=d 
 PASS URLSearchParams constructed with: a=b&c=d& 
-FAIL request.formData() with input: a=b&c=d& promise_test: Unhandled rejection with value: object "NotSupportedError: The operation is not supported."
-FAIL response.formData() with input: a=b&c=d& promise_test: Unhandled rejection with value: object "NotSupportedError: The operation is not supported."
+PASS request.formData() with input: a=b&c=d& 
+PASS response.formData() with input: a=b&c=d& 
 PASS URLSearchParams constructed with: &&&a=b&&&&c=d& 
-FAIL request.formData() with input: &&&a=b&&&&c=d& promise_test: Unhandled rejection with value: object "NotSupportedError: The operation is not supported."
-FAIL response.formData() with input: &&&a=b&&&&c=d& promise_test: Unhandled rejection with value: object "NotSupportedError: The operation is not supported."
+PASS request.formData() with input: &&&a=b&&&&c=d& 
+PASS response.formData() with input: &&&a=b&&&&c=d& 
 PASS URLSearchParams constructed with: a=a&a=b&a=c 
-FAIL request.formData() with input: a=a&a=b&a=c promise_test: Unhandled rejection with value: object "NotSupportedError: The operation is not supported."
-FAIL response.formData() with input: a=a&a=b&a=c promise_test: Unhandled rejection with value: object "NotSupportedError: The operation is not supported."
+PASS request.formData() with input: a=a&a=b&a=c 
+PASS response.formData() with input: a=a&a=b&a=c 
 PASS URLSearchParams constructed with: a==a 
-FAIL request.formData() with input: a==a promise_test: Unhandled rejection with value: object "NotSupportedError: The operation is not supported."
-FAIL response.formData() with input: a==a promise_test: Unhandled rejection with value: object "NotSupportedError: The operation is not supported."
+PASS request.formData() with input: a==a 
+PASS response.formData() with input: a==a 
 PASS URLSearchParams constructed with: a=a+b+c+d 
-FAIL request.formData() with input: a=a+b+c+d promise_test: Unhandled rejection with value: object "NotSupportedError: The operation is not supported."
-FAIL response.formData() with input: a=a+b+c+d promise_test: Unhandled rejection with value: object "NotSupportedError: The operation is not supported."
+PASS request.formData() with input: a=a+b+c+d 
+PASS response.formData() with input: a=a+b+c+d 
 PASS URLSearchParams constructed with: %=a 
-FAIL request.formData() with input: %=a promise_test: Unhandled rejection with value: object "NotSupportedError: The operation is not supported."
-FAIL response.formData() with input: %=a promise_test: Unhandled rejection with value: object "NotSupportedError: The operation is not supported."
+PASS request.formData() with input: %=a 
+PASS response.formData() with input: %=a 
 PASS URLSearchParams constructed with: %a=a 
-FAIL request.formData() with input: %a=a promise_test: Unhandled rejection with value: object "NotSupportedError: The operation is not supported."
-FAIL response.formData() with input: %a=a promise_test: Unhandled rejection with value: object "NotSupportedError: The operation is not supported."
+PASS request.formData() with input: %a=a 
+PASS response.formData() with input: %a=a 
 PASS URLSearchParams constructed with: %a_=a 
-FAIL request.formData() with input: %a_=a promise_test: Unhandled rejection with value: object "NotSupportedError: The operation is not supported."
-FAIL response.formData() with input: %a_=a promise_test: Unhandled rejection with value: object "NotSupportedError: The operation is not supported."
+PASS request.formData() with input: %a_=a 
+PASS response.formData() with input: %a_=a 
 PASS URLSearchParams constructed with: %61=a 
-FAIL request.formData() with input: %61=a promise_test: Unhandled rejection with value: object "NotSupportedError: The operation is not supported."
-FAIL response.formData() with input: %61=a promise_test: Unhandled rejection with value: object "NotSupportedError: The operation is not supported."
+PASS request.formData() with input: %61=a 
+PASS response.formData() with input: %61=a 
 PASS URLSearchParams constructed with: %61+%4d%4D= 
-FAIL request.formData() with input: %61+%4d%4D= promise_test: Unhandled rejection with value: object "NotSupportedError: The operation is not supported."
-FAIL response.formData() with input: %61+%4d%4D= promise_test: Unhandled rejection with value: object "NotSupportedError: The operation is not supported."
+PASS request.formData() with input: %61+%4d%4D= 
+PASS response.formData() with input: %61+%4d%4D= 
 PASS URLSearchParams constructed with: id=0&value=% 
-FAIL request.formData() with input: id=0&value=% promise_test: Unhandled rejection with value: object "NotSupportedError: The operation is not supported."
-FAIL response.formData() with input: id=0&value=% promise_test: Unhandled rejection with value: object "NotSupportedError: The operation is not supported."
+PASS request.formData() with input: id=0&value=% 
+PASS response.formData() with input: id=0&value=% 
 PASS URLSearchParams constructed with: b=%2sf%2a 
-FAIL request.formData() with input: b=%2sf%2a promise_test: Unhandled rejection with value: object "NotSupportedError: The operation is not supported."
-FAIL response.formData() with input: b=%2sf%2a promise_test: Unhandled rejection with value: object "NotSupportedError: The operation is not supported."
+PASS request.formData() with input: b=%2sf%2a 
+PASS response.formData() with input: b=%2sf%2a 
 PASS URLSearchParams constructed with: b=%2%2af%2a 
-FAIL request.formData() with input: b=%2%2af%2a promise_test: Unhandled rejection with value: object "NotSupportedError: The operation is not supported."
-FAIL response.formData() with input: b=%2%2af%2a promise_test: Unhandled rejection with value: object "NotSupportedError: The operation is not supported."
+PASS request.formData() with input: b=%2%2af%2a 
+PASS response.formData() with input: b=%2%2af%2a 
 PASS URLSearchParams constructed with: b=%%2a 
-FAIL request.formData() with input: b=%%2a promise_test: Unhandled rejection with value: object "NotSupportedError: The operation is not supported."
-FAIL response.formData() with input: b=%%2a promise_test: Unhandled rejection with value: object "NotSupportedError: The operation is not supported."
+PASS request.formData() with input: b=%%2a 
+PASS response.formData() with input: b=%%2a 
 

Modified: trunk/LayoutTests/imported/w3c/web-platform-tests/url/urlencoded-parser.any.worker-expected.txt (266086 => 266087)


--- trunk/LayoutTests/imported/w3c/web-platform-tests/url/urlencoded-parser.any.worker-expected.txt	2020-08-24 21:41:43 UTC (rev 266086)
+++ trunk/LayoutTests/imported/w3c/web-platform-tests/url/urlencoded-parser.any.worker-expected.txt	2020-08-24 22:36:02 UTC (rev 266087)
@@ -1,104 +1,104 @@
 
 PASS URLSearchParams constructed with: test 
-FAIL request.formData() with input: test promise_test: Unhandled rejection with value: object "NotSupportedError: The operation is not supported."
-FAIL response.formData() with input: test promise_test: Unhandled rejection with value: object "NotSupportedError: The operation is not supported."
+PASS request.formData() with input: test 
+PASS response.formData() with input: test 
 PASS URLSearchParams constructed with: test= 
-FAIL request.formData() with input: test= promise_test: Unhandled rejection with value: object "NotSupportedError: The operation is not supported."
-FAIL response.formData() with input: test= promise_test: Unhandled rejection with value: object "NotSupportedError: The operation is not supported."
+PASS request.formData() with input: test= 
+PASS response.formData() with input: test= 
 PASS URLSearchParams constructed with: %EF%BB%BFtest=%EF%BB%BF 
-FAIL request.formData() with input: %EF%BB%BFtest=%EF%BB%BF promise_test: Unhandled rejection with value: object "NotSupportedError: The operation is not supported."
-FAIL response.formData() with input: %EF%BB%BFtest=%EF%BB%BF promise_test: Unhandled rejection with value: object "NotSupportedError: The operation is not supported."
-FAIL URLSearchParams constructed with: %FE%FF assert_array_equals: expected property 0 to be "\ufffd\ufffd" but got "" (expected array ["\ufffd\ufffd", ""] got ["", ""])
-FAIL request.formData() with input: %FE%FF promise_test: Unhandled rejection with value: object "NotSupportedError: The operation is not supported."
-FAIL response.formData() with input: %FE%FF promise_test: Unhandled rejection with value: object "NotSupportedError: The operation is not supported."
-FAIL URLSearchParams constructed with: %FF%FE assert_array_equals: expected property 0 to be "\ufffd\ufffd" but got "" (expected array ["\ufffd\ufffd", ""] got ["", ""])
-FAIL request.formData() with input: %FF%FE promise_test: Unhandled rejection with value: object "NotSupportedError: The operation is not supported."
-FAIL response.formData() with input: %FF%FE promise_test: Unhandled rejection with value: object "NotSupportedError: The operation is not supported."
+PASS request.formData() with input: %EF%BB%BFtest=%EF%BB%BF 
+PASS response.formData() with input: %EF%BB%BFtest=%EF%BB%BF 
+PASS URLSearchParams constructed with: %FE%FF 
+PASS request.formData() with input: %FE%FF 
+PASS response.formData() with input: %FE%FF 
+PASS URLSearchParams constructed with: %FF%FE 
+PASS request.formData() with input: %FF%FE 
+PASS response.formData() with input: %FF%FE 
 PASS URLSearchParams constructed with: †&†=x 
-FAIL request.formData() with input: †&†=x promise_test: Unhandled rejection with value: object "NotSupportedError: The operation is not supported."
-FAIL response.formData() with input: †&†=x promise_test: Unhandled rejection with value: object "NotSupportedError: The operation is not supported."
-FAIL URLSearchParams constructed with: %C2 assert_array_equals: expected property 0 to be "\ufffd" but got "" (expected array ["\ufffd", ""] got ["", ""])
-FAIL request.formData() with input: %C2 promise_test: Unhandled rejection with value: object "NotSupportedError: The operation is not supported."
-FAIL response.formData() with input: %C2 promise_test: Unhandled rejection with value: object "NotSupportedError: The operation is not supported."
-FAIL URLSearchParams constructed with: %C2x assert_array_equals: expected property 0 to be "\ufffdx" but got "" (expected array ["\ufffdx", ""] got ["", ""])
-FAIL request.formData() with input: %C2x promise_test: Unhandled rejection with value: object "NotSupportedError: The operation is not supported."
-FAIL response.formData() with input: %C2x promise_test: Unhandled rejection with value: object "NotSupportedError: The operation is not supported."
-FAIL URLSearchParams constructed with: _charset_=windows-1252&test=%C2x assert_array_equals: expected property 1 to be "\ufffdx" but got "" (expected array ["test", "\ufffdx"] got ["test", ""])
-FAIL request.formData() with input: _charset_=windows-1252&test=%C2x promise_test: Unhandled rejection with value: object "NotSupportedError: The operation is not supported."
-FAIL response.formData() with input: _charset_=windows-1252&test=%C2x promise_test: Unhandled rejection with value: object "NotSupportedError: The operation is not supported."
+PASS request.formData() with input: †&†=x 
+PASS response.formData() with input: †&†=x 
+PASS URLSearchParams constructed with: %C2 
+PASS request.formData() with input: %C2 
+PASS response.formData() with input: %C2 
+PASS URLSearchParams constructed with: %C2x 
+PASS request.formData() with input: %C2x 
+PASS response.formData() with input: %C2x 
+PASS URLSearchParams constructed with: _charset_=windows-1252&test=%C2x 
+PASS request.formData() with input: _charset_=windows-1252&test=%C2x 
+PASS response.formData() with input: _charset_=windows-1252&test=%C2x 
 PASS URLSearchParams constructed with:  
-FAIL request.formData() with input:  promise_test: Unhandled rejection with value: object "NotSupportedError: The operation is not supported."
-FAIL response.formData() with input:  promise_test: Unhandled rejection with value: object "NotSupportedError: The operation is not supported."
+PASS request.formData() with input:  
+PASS response.formData() with input:  
 PASS URLSearchParams constructed with: a 
-FAIL request.formData() with input: a promise_test: Unhandled rejection with value: object "NotSupportedError: The operation is not supported."
-FAIL response.formData() with input: a promise_test: Unhandled rejection with value: object "NotSupportedError: The operation is not supported."
+PASS request.formData() with input: a 
+PASS response.formData() with input: a 
 PASS URLSearchParams constructed with: a=b 
-FAIL request.formData() with input: a=b promise_test: Unhandled rejection with value: object "NotSupportedError: The operation is not supported."
-FAIL response.formData() with input: a=b promise_test: Unhandled rejection with value: object "NotSupportedError: The operation is not supported."
+PASS request.formData() with input: a=b 
+PASS response.formData() with input: a=b 
 PASS URLSearchParams constructed with: a= 
-FAIL request.formData() with input: a= promise_test: Unhandled rejection with value: object "NotSupportedError: The operation is not supported."
-FAIL response.formData() with input: a= promise_test: Unhandled rejection with value: object "NotSupportedError: The operation is not supported."
+PASS request.formData() with input: a= 
+PASS response.formData() with input: a= 
 PASS URLSearchParams constructed with: =b 
-FAIL request.formData() with input: =b promise_test: Unhandled rejection with value: object "NotSupportedError: The operation is not supported."
-FAIL response.formData() with input: =b promise_test: Unhandled rejection with value: object "NotSupportedError: The operation is not supported."
+PASS request.formData() with input: =b 
+PASS response.formData() with input: =b 
 PASS URLSearchParams constructed with: & 
-FAIL request.formData() with input: & promise_test: Unhandled rejection with value: object "NotSupportedError: The operation is not supported."
-FAIL response.formData() with input: & promise_test: Unhandled rejection with value: object "NotSupportedError: The operation is not supported."
+PASS request.formData() with input: & 
+PASS response.formData() with input: & 
 PASS URLSearchParams constructed with: &a 
-FAIL request.formData() with input: &a promise_test: Unhandled rejection with value: object "NotSupportedError: The operation is not supported."
-FAIL response.formData() with input: &a promise_test: Unhandled rejection with value: object "NotSupportedError: The operation is not supported."
+PASS request.formData() with input: &a 
+PASS response.formData() with input: &a 
 PASS URLSearchParams constructed with: a& 
-FAIL request.formData() with input: a& promise_test: Unhandled rejection with value: object "NotSupportedError: The operation is not supported."
-FAIL response.formData() with input: a& promise_test: Unhandled rejection with value: object "NotSupportedError: The operation is not supported."
+PASS request.formData() with input: a& 
+PASS response.formData() with input: a& 
 PASS URLSearchParams constructed with: a&a 
-FAIL request.formData() with input: a&a promise_test: Unhandled rejection with value: object "NotSupportedError: The operation is not supported."
-FAIL response.formData() with input: a&a promise_test: Unhandled rejection with value: object "NotSupportedError: The operation is not supported."
+PASS request.formData() with input: a&a 
+PASS response.formData() with input: a&a 
 PASS URLSearchParams constructed with: a&b&c 
-FAIL request.formData() with input: a&b&c promise_test: Unhandled rejection with value: object "NotSupportedError: The operation is not supported."
-FAIL response.formData() with input: a&b&c promise_test: Unhandled rejection with value: object "NotSupportedError: The operation is not supported."
+PASS request.formData() with input: a&b&c 
+PASS response.formData() with input: a&b&c 
 PASS URLSearchParams constructed with: a=b&c=d 
-FAIL request.formData() with input: a=b&c=d promise_test: Unhandled rejection with value: object "NotSupportedError: The operation is not supported."
-FAIL response.formData() with input: a=b&c=d promise_test: Unhandled rejection with value: object "NotSupportedError: The operation is not supported."
+PASS request.formData() with input: a=b&c=d 
+PASS response.formData() with input: a=b&c=d 
 PASS URLSearchParams constructed with: a=b&c=d& 
-FAIL request.formData() with input: a=b&c=d& promise_test: Unhandled rejection with value: object "NotSupportedError: The operation is not supported."
-FAIL response.formData() with input: a=b&c=d& promise_test: Unhandled rejection with value: object "NotSupportedError: The operation is not supported."
+PASS request.formData() with input: a=b&c=d& 
+PASS response.formData() with input: a=b&c=d& 
 PASS URLSearchParams constructed with: &&&a=b&&&&c=d& 
-FAIL request.formData() with input: &&&a=b&&&&c=d& promise_test: Unhandled rejection with value: object "NotSupportedError: The operation is not supported."
-FAIL response.formData() with input: &&&a=b&&&&c=d& promise_test: Unhandled rejection with value: object "NotSupportedError: The operation is not supported."
+PASS request.formData() with input: &&&a=b&&&&c=d& 
+PASS response.formData() with input: &&&a=b&&&&c=d& 
 PASS URLSearchParams constructed with: a=a&a=b&a=c 
-FAIL request.formData() with input: a=a&a=b&a=c promise_test: Unhandled rejection with value: object "NotSupportedError: The operation is not supported."
-FAIL response.formData() with input: a=a&a=b&a=c promise_test: Unhandled rejection with value: object "NotSupportedError: The operation is not supported."
+PASS request.formData() with input: a=a&a=b&a=c 
+PASS response.formData() with input: a=a&a=b&a=c 
 PASS URLSearchParams constructed with: a==a 
-FAIL request.formData() with input: a==a promise_test: Unhandled rejection with value: object "NotSupportedError: The operation is not supported."
-FAIL response.formData() with input: a==a promise_test: Unhandled rejection with value: object "NotSupportedError: The operation is not supported."
+PASS request.formData() with input: a==a 
+PASS response.formData() with input: a==a 
 PASS URLSearchParams constructed with: a=a+b+c+d 
-FAIL request.formData() with input: a=a+b+c+d promise_test: Unhandled rejection with value: object "NotSupportedError: The operation is not supported."
-FAIL response.formData() with input: a=a+b+c+d promise_test: Unhandled rejection with value: object "NotSupportedError: The operation is not supported."
+PASS request.formData() with input: a=a+b+c+d 
+PASS response.formData() with input: a=a+b+c+d 
 PASS URLSearchParams constructed with: %=a 
-FAIL request.formData() with input: %=a promise_test: Unhandled rejection with value: object "NotSupportedError: The operation is not supported."
-FAIL response.formData() with input: %=a promise_test: Unhandled rejection with value: object "NotSupportedError: The operation is not supported."
+PASS request.formData() with input: %=a 
+PASS response.formData() with input: %=a 
 PASS URLSearchParams constructed with: %a=a 
-FAIL request.formData() with input: %a=a promise_test: Unhandled rejection with value: object "NotSupportedError: The operation is not supported."
-FAIL response.formData() with input: %a=a promise_test: Unhandled rejection with value: object "NotSupportedError: The operation is not supported."
+PASS request.formData() with input: %a=a 
+PASS response.formData() with input: %a=a 
 PASS URLSearchParams constructed with: %a_=a 
-FAIL request.formData() with input: %a_=a promise_test: Unhandled rejection with value: object "NotSupportedError: The operation is not supported."
-FAIL response.formData() with input: %a_=a promise_test: Unhandled rejection with value: object "NotSupportedError: The operation is not supported."
+PASS request.formData() with input: %a_=a 
+PASS response.formData() with input: %a_=a 
 PASS URLSearchParams constructed with: %61=a 
-FAIL request.formData() with input: %61=a promise_test: Unhandled rejection with value: object "NotSupportedError: The operation is not supported."
-FAIL response.formData() with input: %61=a promise_test: Unhandled rejection with value: object "NotSupportedError: The operation is not supported."
+PASS request.formData() with input: %61=a 
+PASS response.formData() with input: %61=a 
 PASS URLSearchParams constructed with: %61+%4d%4D= 
-FAIL request.formData() with input: %61+%4d%4D= promise_test: Unhandled rejection with value: object "NotSupportedError: The operation is not supported."
-FAIL response.formData() with input: %61+%4d%4D= promise_test: Unhandled rejection with value: object "NotSupportedError: The operation is not supported."
+PASS request.formData() with input: %61+%4d%4D= 
+PASS response.formData() with input: %61+%4d%4D= 
 PASS URLSearchParams constructed with: id=0&value=% 
-FAIL request.formData() with input: id=0&value=% promise_test: Unhandled rejection with value: object "NotSupportedError: The operation is not supported."
-FAIL response.formData() with input: id=0&value=% promise_test: Unhandled rejection with value: object "NotSupportedError: The operation is not supported."
+PASS request.formData() with input: id=0&value=% 
+PASS response.formData() with input: id=0&value=% 
 PASS URLSearchParams constructed with: b=%2sf%2a 
-FAIL request.formData() with input: b=%2sf%2a promise_test: Unhandled rejection with value: object "NotSupportedError: The operation is not supported."
-FAIL response.formData() with input: b=%2sf%2a promise_test: Unhandled rejection with value: object "NotSupportedError: The operation is not supported."
+PASS request.formData() with input: b=%2sf%2a 
+PASS response.formData() with input: b=%2sf%2a 
 PASS URLSearchParams constructed with: b=%2%2af%2a 
-FAIL request.formData() with input: b=%2%2af%2a promise_test: Unhandled rejection with value: object "NotSupportedError: The operation is not supported."
-FAIL response.formData() with input: b=%2%2af%2a promise_test: Unhandled rejection with value: object "NotSupportedError: The operation is not supported."
+PASS request.formData() with input: b=%2%2af%2a 
+PASS response.formData() with input: b=%2%2af%2a 
 PASS URLSearchParams constructed with: b=%%2a 
-FAIL request.formData() with input: b=%%2a promise_test: Unhandled rejection with value: object "NotSupportedError: The operation is not supported."
-FAIL response.formData() with input: b=%%2a promise_test: Unhandled rejection with value: object "NotSupportedError: The operation is not supported."
+PASS request.formData() with input: b=%%2a 
+PASS response.formData() with input: b=%%2a 
 

Modified: trunk/Source/WTF/ChangeLog (266086 => 266087)


--- trunk/Source/WTF/ChangeLog	2020-08-24 21:41:43 UTC (rev 266086)
+++ trunk/Source/WTF/ChangeLog	2020-08-24 22:36:02 UTC (rev 266087)
@@ -1,5 +1,28 @@
 2020-08-24  Alex Christensen  <achristen...@webkit.org>
 
+        Implement Request/Response consuming as FormData
+        https://bugs.webkit.org/show_bug.cgi?id=215671
+
+        Reviewed by Darin Adler.
+
+        In order to be compatible with other browsers, we need a verson of String::fromUTF8 that
+        uses U8_NEXT_OR_FFFD instead of U8_NEXT, but changing that across the board will break other things.
+        Leave everything else as it is, use templates and constexpr to not add any branches, but add
+        String::fromUTF8ReplacingInvalidSequences to allow me to make our FormData consuming compatible with other browsers.
+
+        * wtf/text/WTFString.cpp:
+        (WTF::fromUTF8Helper):
+        (WTF::String::fromUTF8):
+        (WTF::String::fromUTF8ReplacingInvalidSequences):
+        * wtf/text/WTFString.h:
+        * wtf/unicode/UTF8Conversion.cpp:
+        (WTF::Unicode::convertUTF8ToUTF16Impl):
+        (WTF::Unicode::convertUTF8ToUTF16):
+        (WTF::Unicode::convertUTF8ToUTF16ReplacingInvalidSequences):
+        * wtf/unicode/UTF8Conversion.h:
+
+2020-08-24  Alex Christensen  <achristen...@webkit.org>
+
         Make _WKWebsiteDataStoreConfiguration SPI for HSTS storage to replace _WKProcessPoolConfiguration.hstsStorageDirectory
         https://bugs.webkit.org/show_bug.cgi?id=213048
 

Modified: trunk/Source/WTF/wtf/PlatformHave.h (266086 => 266087)


--- trunk/Source/WTF/wtf/PlatformHave.h	2020-08-24 21:41:43 UTC (rev 266086)
+++ trunk/Source/WTF/wtf/PlatformHave.h	2020-08-24 22:36:02 UTC (rev 266087)
@@ -308,6 +308,7 @@
 
 #if !OS(WINDOWS)
 #define HAVE_STACK_BOUNDS_FOR_NEW_THREAD 1
+#define HAVE_MEMMEM 1
 #endif
 
 #if PLATFORM(MAC) || PLATFORM(IOS) || PLATFORM(MACCATALYST)

Modified: trunk/Source/WTF/wtf/StringExtras.h (266086 => 266087)


--- trunk/Source/WTF/wtf/StringExtras.h	2020-08-24 21:41:43 UTC (rev 266086)
+++ trunk/Source/WTF/wtf/StringExtras.h	2020-08-24 22:36:02 UTC (rev 266087)
@@ -43,3 +43,19 @@
 }
 
 #endif
+
+#if !HAVE(MEMMEM)
+
+inline const void* memmem(const void* haystack, size_t haystackLength, const void* needle, size_t needleLength)
+{
+    const char* pointer = static_cast<const char*>(haystack);
+    while (haystackLength >= needleLength) {
+        if (!memcmp(pointer, needle, needleLength))
+            return pointer;
+        pointer++;
+        haystackLength--;
+    }
+    return nullptr;
+}
+
+#endif

Modified: trunk/Source/WTF/wtf/URLParser.cpp (266086 => 266087)


--- trunk/Source/WTF/wtf/URLParser.cpp	2020-08-24 21:41:43 UTC (rev 266086)
+++ trunk/Source/WTF/wtf/URLParser.cpp	2020-08-24 22:36:02 UTC (rev 266087)
@@ -2773,7 +2773,7 @@
     if (utf8.isNull())
         return WTF::nullopt;
     auto percentDecoded = percentDecode(reinterpret_cast<const LChar*>(utf8.data()), utf8.length());
-    return String::fromUTF8(percentDecoded.data(), percentDecoded.size());
+    return String::fromUTF8ReplacingInvalidSequences(percentDecoded.data(), percentDecoded.size());
 }
 
 // https://url.spec.whatwg.org/#concept-urlencoded-parser

Modified: trunk/Source/WTF/wtf/text/WTFString.cpp (266086 => 266087)


--- trunk/Source/WTF/wtf/text/WTFString.cpp	2020-08-24 21:41:43 UTC (rev 266086)
+++ trunk/Source/WTF/wtf/text/WTFString.cpp	2020-08-24 22:36:02 UTC (rev 266087)
@@ -844,9 +844,10 @@
     return result;
 }
 
-String String::fromUTF8(const LChar* stringStart, size_t length)
+template<bool replaceInvalidSequences>
+String fromUTF8Impl(const LChar* stringStart, size_t length)
 {
-    if (length > MaxLength)
+    if (length > StringImplShape::MaxLength)
         CRASH();
 
     if (!stringStart)
@@ -863,14 +864,25 @@
  
     UChar* bufferCurrent = bufferStart;
     const char* stringCurrent = reinterpret_cast<const char*>(stringStart);
-    if (!convertUTF8ToUTF16(stringCurrent, reinterpret_cast<const char *>(stringStart + length), &bufferCurrent, bufferCurrent + buffer.size()))
+    constexpr auto function = replaceInvalidSequences ? convertUTF8ToUTF16ReplacingInvalidSequences : convertUTF8ToUTF16;
+    if (!function(stringCurrent, reinterpret_cast<const char*>(stringStart + length), &bufferCurrent, bufferCurrent + buffer.size(), nullptr))
         return String();
 
     unsigned utf16Length = bufferCurrent - bufferStart;
-    ASSERT_WITH_SECURITY_IMPLICATION(utf16Length < length);
+    RELEASE_ASSERT_WITH_SECURITY_IMPLICATION(utf16Length <= length);
     return StringImpl::create(bufferStart, utf16Length);
 }
 
+String String::fromUTF8(const LChar* stringStart, size_t length)
+{
+    return fromUTF8Impl<false>(stringStart, length);
+}
+
+String String::fromUTF8ReplacingInvalidSequences(const LChar* characters, size_t length)
+{
+    return fromUTF8Impl<true>(characters, length);
+}
+
 String String::fromUTF8(const LChar* string)
 {
     if (!string)

Modified: trunk/Source/WTF/wtf/text/WTFString.h (266086 => 266087)


--- trunk/Source/WTF/wtf/text/WTFString.h	2020-08-24 21:41:43 UTC (rev 266086)
+++ trunk/Source/WTF/wtf/text/WTFString.h	2020-08-24 22:36:02 UTC (rev 266087)
@@ -350,6 +350,7 @@
     static String fromUTF8(const char* string) { return fromUTF8(reinterpret_cast<const LChar*>(string)); };
     WTF_EXPORT_PRIVATE static String fromUTF8(const CString&);
     static String fromUTF8(const Vector<LChar>& characters);
+    static String fromUTF8ReplacingInvalidSequences(const LChar*, size_t);
 
     // Tries to convert the passed in string to UTF-8, but will fall back to Latin-1 if the string is not valid UTF-8.
     WTF_EXPORT_PRIVATE static String fromUTF8WithLatin1Fallback(const LChar*, size_t);

Modified: trunk/Source/WTF/wtf/unicode/UTF8Conversion.cpp (266086 => 266087)


--- trunk/Source/WTF/wtf/unicode/UTF8Conversion.cpp	2020-08-24 21:41:43 UTC (rev 266086)
+++ trunk/Source/WTF/wtf/unicode/UTF8Conversion.cpp	2020-08-24 22:36:02 UTC (rev 266087)
@@ -87,7 +87,8 @@
     return result;
 }
 
-bool convertUTF8ToUTF16(const char* source, const char* sourceEnd, UChar** targetStart, UChar* targetEnd, bool* sourceAllASCII)
+template<bool replaceInvalidSequences>
+bool convertUTF8ToUTF16Impl(const char* source, const char* sourceEnd, UChar** targetStart, UChar* targetEnd, bool* sourceAllASCII)
 {
     RELEASE_ASSERT(sourceEnd - source <= std::numeric_limits<int>::max());
     UBool error = false;
@@ -97,7 +98,11 @@
     int targetOffset = 0;
     for (int sourceOffset = 0; sourceOffset < sourceEnd - source; ) {
         UChar32 character;
-        U8_NEXT(reinterpret_cast<const uint8_t*>(source), sourceOffset, sourceEnd - source, character);
+        if constexpr (replaceInvalidSequences) {
+            U8_NEXT_OR_FFFD(reinterpret_cast<const uint8_t*>(source), sourceOffset, sourceEnd - source, character);
+        } else {
+            U8_NEXT(reinterpret_cast<const uint8_t*>(source), sourceOffset, sourceEnd - source, character);
+        }
         if (character < 0)
             return false;
         U16_APPEND(target, targetOffset, targetEnd - target, character, error);
@@ -112,6 +117,16 @@
     return true;
 }
 
+bool convertUTF8ToUTF16(const char* source, const char* sourceEnd, UChar** targetStart, UChar* targetEnd, bool* sourceAllASCII)
+{
+    return convertUTF8ToUTF16Impl<false>(source, sourceEnd, targetStart, targetEnd, sourceAllASCII);
+}
+
+bool convertUTF8ToUTF16ReplacingInvalidSequences(const char* source, const char* sourceEnd, UChar** targetStart, UChar* targetEnd, bool* sourceAllASCII)
+{
+    return convertUTF8ToUTF16Impl<true>(source, sourceEnd, targetStart, targetEnd, sourceAllASCII);
+}
+
 unsigned calculateStringHashAndLengthFromUTF8MaskingTop8Bits(const char* data, const char* dataEnd, unsigned& dataLength, unsigned& utf16Length)
 {
     StringHasher stringHasher;

Modified: trunk/Source/WTF/wtf/unicode/UTF8Conversion.h (266086 => 266087)


--- trunk/Source/WTF/wtf/unicode/UTF8Conversion.h	2020-08-24 21:41:43 UTC (rev 266086)
+++ trunk/Source/WTF/wtf/unicode/UTF8Conversion.h	2020-08-24 22:36:02 UTC (rev 266087)
@@ -45,6 +45,7 @@
 // at the end of the source, which will instead cause a SourceExhausted error.
 
 WTF_EXPORT_PRIVATE bool convertUTF8ToUTF16(const char* sourceStart, const char* sourceEnd, UChar** targetStart, UChar* targetEnd, bool* isSourceAllASCII = nullptr);
+WTF_EXPORT_PRIVATE bool convertUTF8ToUTF16ReplacingInvalidSequences(const char* sourceStart, const char* sourceEnd, UChar** targetStart, UChar* targetEnd, bool* isSourceAllASCII = nullptr);
 WTF_EXPORT_PRIVATE bool convertLatin1ToUTF8(const LChar** sourceStart, const LChar* sourceEnd, char** targetStart, char* targetEnd);
 WTF_EXPORT_PRIVATE ConversionResult convertUTF16ToUTF8(const UChar** sourceStart, const UChar* sourceEnd, char** targetStart, char* targetEnd, bool strict = true);
 

Modified: trunk/Source/WebCore/ChangeLog (266086 => 266087)


--- trunk/Source/WebCore/ChangeLog	2020-08-24 21:41:43 UTC (rev 266086)
+++ trunk/Source/WebCore/ChangeLog	2020-08-24 22:36:02 UTC (rev 266087)
@@ -1,3 +1,23 @@
+2020-08-24  Alex Christensen  <achristen...@webkit.org>
+
+        Implement Request/Response consuming as FormData
+        https://bugs.webkit.org/show_bug.cgi?id=215671
+
+        Reviewed by Darin Adler.
+
+        Covered by many newly passing WPT tests, for most of which Safari was the only failing browser.
+
+        * Modules/fetch/FetchBody.cpp:
+        (WebCore::FetchBody::formData):
+        (WebCore::FetchBody::consume):
+        (WebCore::FetchBody::consumeFormData):
+        * Modules/fetch/FetchBody.h:
+        * Modules/fetch/FetchBodyConsumer.cpp:
+        (WebCore::formDataFromData):
+        (WebCore::resolveWithTypeAndData):
+        (WebCore::FetchBodyConsumer::resolve):
+        * Modules/fetch/FetchBodyConsumer.h:
+
 2020-08-24  Emilio Cobos Álvarez  <emi...@crisal.io>
 
         Support quotes:auto and fix quotes serialization.

Modified: trunk/Source/WebCore/Modules/fetch/FetchBody.cpp (266086 => 266087)


--- trunk/Source/WebCore/Modules/fetch/FetchBody.cpp	2020-08-24 21:41:43 UTC (rev 266086)
+++ trunk/Source/WebCore/Modules/fetch/FetchBody.cpp	2020-08-24 22:36:02 UTC (rev 266087)
@@ -35,6 +35,7 @@
 #include "FetchHeaders.h"
 #include "HTTPHeaderValues.h"
 #include "HTTPParsers.h"
+#include "JSDOMFormData.h"
 #include "JSDOMPromiseDeferred.h"
 #include "ReadableStreamSource.h"
 #include <_javascript_Core/ArrayBufferView.h>
@@ -130,9 +131,10 @@
     consume(owner, WTFMove(promise));
 }
 
-void FetchBody::formData(FetchBodyOwner&, Ref<DeferredPromise>&& promise)
+void FetchBody::formData(FetchBodyOwner& owner, Ref<DeferredPromise>&& promise)
 {
-    promise.get().reject(NotSupportedError);
+    m_consumer.setType(FetchBodyConsumer::Type::FormData);
+    consume(owner, WTFMove(promise));
 }
 
 void FetchBody::consumeOnceLoadingFinished(FetchBodyConsumer::Type type, Ref<DeferredPromise>&& promise, const String& contentType)
@@ -146,19 +148,19 @@
 void FetchBody::consume(FetchBodyOwner& owner, Ref<DeferredPromise>&& promise)
 {
     if (isArrayBuffer()) {
-        consumeArrayBuffer(WTFMove(promise));
+        consumeArrayBuffer(owner, WTFMove(promise));
         return;
     }
     if (isArrayBufferView()) {
-        consumeArrayBufferView(WTFMove(promise));
+        consumeArrayBufferView(owner, WTFMove(promise));
         return;
     }
     if (isText()) {
-        consumeText(WTFMove(promise), textBody());
+        consumeText(owner, WTFMove(promise), textBody());
         return;
     }
     if (isURLSearchParams()) {
-        consumeText(WTFMove(promise), urlSearchParamsBody().toString());
+        consumeText(owner, WTFMove(promise), urlSearchParamsBody().toString());
         return;
     }
     if (isBlob()) {
@@ -166,12 +168,11 @@
         return;
     }
     if (isFormData()) {
-        // FIXME: Support consuming FormData.
-        promise->reject(NotSupportedError);
+        consumeFormData(owner, WTFMove(promise));
         return;
     }
 
-    m_consumer.resolve(WTFMove(promise), m_readableStream.get());
+    m_consumer.resolve(WTFMove(promise), owner.contentType(), m_readableStream.get());
 }
 
 void FetchBody::consumeAsStream(FetchBodyOwner& owner, FetchBodySource& source)
@@ -205,22 +206,22 @@
         source.close();
 }
 
-void FetchBody::consumeArrayBuffer(Ref<DeferredPromise>&& promise)
+void FetchBody::consumeArrayBuffer(FetchBodyOwner& owner, Ref<DeferredPromise>&& promise)
 {
-    m_consumer.resolveWithData(WTFMove(promise), static_cast<const uint8_t*>(arrayBufferBody().data()), arrayBufferBody().byteLength());
+    m_consumer.resolveWithData(WTFMove(promise), owner.contentType(), static_cast<const uint8_t*>(arrayBufferBody().data()), arrayBufferBody().byteLength());
     m_data = nullptr;
 }
 
-void FetchBody::consumeArrayBufferView(Ref<DeferredPromise>&& promise)
+void FetchBody::consumeArrayBufferView(FetchBodyOwner& owner, Ref<DeferredPromise>&& promise)
 {
-    m_consumer.resolveWithData(WTFMove(promise), static_cast<const uint8_t*>(arrayBufferViewBody().baseAddress()), arrayBufferViewBody().byteLength());
+    m_consumer.resolveWithData(WTFMove(promise), owner.contentType(), static_cast<const uint8_t*>(arrayBufferViewBody().baseAddress()), arrayBufferViewBody().byteLength());
     m_data = nullptr;
 }
 
-void FetchBody::consumeText(Ref<DeferredPromise>&& promise, const String& text)
+void FetchBody::consumeText(FetchBodyOwner& owner, Ref<DeferredPromise>&& promise, const String& text)
 {
     auto data = "" UnencodableHandling::Entities);
-    m_consumer.resolveWithData(WTFMove(promise), data.data(), data.size());
+    m_consumer.resolveWithData(WTFMove(promise), owner.contentType(), data.data(), data.size());
     m_data = nullptr;
 }
 
@@ -231,14 +232,26 @@
     m_data = nullptr;
 }
 
+void FetchBody::consumeFormData(FetchBodyOwner& owner, Ref<DeferredPromise>&& promise)
+{
+    if (auto sharedBuffer = formDataBody().asSharedBuffer()) {
+        m_consumer.resolveWithData(WTFMove(promise), owner.contentType(), sharedBuffer->dataAsUInt8Ptr(), sharedBuffer->size());
+        m_data = nullptr;
+    } else {
+        // FIXME: If the form data contains blobs, load them like we do other blobs.
+        // That will fix the last WPT test in response-consume.html.
+        promise->reject(NotSupportedError);
+    }
+}
+
 void FetchBody::loadingFailed(const Exception& exception)
 {
     m_consumer.loadingFailed(exception);
 }
 
-void FetchBody::loadingSucceeded()
+void FetchBody::loadingSucceeded(const String& contentType)
 {
-    m_consumer.loadingSucceeded();
+    m_consumer.loadingSucceeded(contentType);
 }
 
 RefPtr<FormData> FetchBody::bodyAsFormData() const

Modified: trunk/Source/WebCore/Modules/fetch/FetchBody.h (266086 => 266087)


--- trunk/Source/WebCore/Modules/fetch/FetchBody.h	2020-08-24 21:41:43 UTC (rev 266086)
+++ trunk/Source/WebCore/Modules/fetch/FetchBody.h	2020-08-24 22:36:02 UTC (rev 266087)
@@ -60,7 +60,7 @@
     WEBCORE_EXPORT static Optional<FetchBody> fromFormData(FormData&);
 
     void loadingFailed(const Exception&);
-    void loadingSucceeded();
+    void loadingSucceeded(const String& contentType);
 
     RefPtr<FormData> bodyAsFormData() const;
 
@@ -99,10 +99,11 @@
 
     void consume(FetchBodyOwner&, Ref<DeferredPromise>&&);
 
-    void consumeArrayBuffer(Ref<DeferredPromise>&&);
-    void consumeArrayBufferView(Ref<DeferredPromise>&&);
-    void consumeText(Ref<DeferredPromise>&&, const String&);
+    void consumeArrayBuffer(FetchBodyOwner&, Ref<DeferredPromise>&&);
+    void consumeArrayBufferView(FetchBodyOwner&, Ref<DeferredPromise>&&);
+    void consumeText(FetchBodyOwner&, Ref<DeferredPromise>&&, const String&);
     void consumeBlob(FetchBodyOwner&, Ref<DeferredPromise>&&);
+    void consumeFormData(FetchBodyOwner&, Ref<DeferredPromise>&&);
 
     bool isArrayBuffer() const { return WTF::holds_alternative<Ref<const ArrayBuffer>>(m_data); }
     bool isArrayBufferView() const { return WTF::holds_alternative<Ref<const ArrayBufferView>>(m_data); }

Modified: trunk/Source/WebCore/Modules/fetch/FetchBodyConsumer.cpp (266086 => 266087)


--- trunk/Source/WebCore/Modules/fetch/FetchBodyConsumer.cpp	2020-08-24 21:41:43 UTC (rev 266086)
+++ trunk/Source/WebCore/Modules/fetch/FetchBodyConsumer.cpp	2020-08-24 22:36:02 UTC (rev 266087)
@@ -29,9 +29,12 @@
 #include "config.h"
 #include "FetchBodyConsumer.h"
 
+#include "HTTPHeaderField.h"
 #include "JSBlob.h"
 #include "ReadableStreamChunk.h"
 #include "TextResourceDecoder.h"
+#include <wtf/StringExtras.h>
+#include <wtf/URLParser.h>
 
 namespace WebCore {
 
@@ -39,9 +42,175 @@
 {
     Vector<uint8_t> value(length);
     memcpy(value.data(), data, length);
-    return Blob::create(WTFMove(value), contentType);
+    return Blob::create(WTFMove(value), Blob::normalizedContentType(contentType));
 }
 
+// https://mimesniff.spec.whatwg.org/#http-quoted-string-token-code-point
+static bool isHTTPQuotedStringTokenCodePoint(UChar c)
+{
+    return c == 0x09
+        || (c >= 0x20 && c <= 0x7E)
+        || (c >= 0x80 && c <= 0xFF);
+}
+
+struct MimeType {
+    String type;
+    String subtype;
+    HashMap<String, String> parameters;
+};
+
+static HashMap<String, String> parseParameters(StringView input, size_t position)
+{
+    HashMap<String, String> parameters;
+    while (position < input.length()) {
+        while (position < input.length() && RFC7230::isWhitespace(input[position]))
+            position++;
+        size_t nameBegin = position;
+        while (position < input.length() && input[position] != '=' && input[position] != ';')
+            position++;
+        if (position >= input.length())
+            break;
+        if (input[position] == ';') {
+            position++;
+            continue;
+        }
+        StringView parameterName = input.substring(nameBegin, position - nameBegin);
+        position++;
+        if (position >= input.length())
+            break;
+
+        StringView parameterValue;
+        if (position < input.length() && input[position] == '"') {
+            position++;
+            size_t valueBegin = position;
+            while (position < input.length() && input[position] != '"')
+                position++;
+            parameterValue = input.substring(valueBegin, position - valueBegin);
+            position++;
+        } else {
+            size_t valueBegin = position;
+            while (position < input.length() && input[position] != ';')
+                position++;
+            parameterValue = stripLeadingAndTrailingHTTPSpaces(input.substring(valueBegin, position - valueBegin));
+        }
+
+        if (parameterName.length()
+            && isValidHTTPToken(parameterName)
+            && parameterValue.isAllSpecialCharacters<isHTTPQuotedStringTokenCodePoint>()) {
+            String nameString = parameterName.toString();
+            if (!parameters.contains(nameString))
+                parameters.set(nameString, parameterValue.toString());
+        }
+    }
+    return parameters;
+}
+
+// https://mimesniff.spec.whatwg.org/#parsing-a-mime-type
+static Optional<MimeType> parseMIMEType(const String& contentType)
+{
+    String input = stripLeadingAndTrailingHTTPSpaces(contentType);
+    size_t slashIndex = input.find('/');
+    if (slashIndex == notFound)
+        return WTF::nullopt;
+
+    String type = input.substring(0, slashIndex);
+    if (!type.length() || !isValidHTTPToken(type))
+        return WTF::nullopt;
+    
+    size_t semicolonIndex = input.find(';', slashIndex);
+    String subtype = stripLeadingAndTrailingHTTPSpaces(input.substring(slashIndex + 1, semicolonIndex - slashIndex - 1));
+    if (!subtype.length() || !isValidHTTPToken(subtype))
+        return WTF::nullopt;
+
+    return {{ WTFMove(type), WTFMove(subtype), parseParameters(StringView(input), semicolonIndex + 1) }};
+}
+
+// https://fetch.spec.whatwg.org/#concept-body-package-data
+static RefPtr<DOMFormData> packageFormData(const String& contentType, const char* data, size_t length)
+{
+    auto parseMultipartPart = [] (const char* part, size_t partLength, DOMFormData& form) -> bool {
+        const char* headerEnd = static_cast<const char*>(memmem(part, partLength, "\r\n\r\n", 4));
+        if (!headerEnd)
+            return false;
+        const char* headerBegin = part;
+        size_t headerLength = headerEnd - headerBegin;
+
+        const char* bodyBegin = headerEnd + strlen("\r\n\r\n");
+        size_t bodyLength = partLength - (bodyBegin - headerBegin);
+
+        String header = String::fromUTF8(headerBegin, headerLength);
+
+        const char* contentDispositionCharacters = "Content-Disposition:";
+        size_t contentDispositionBegin = header.find(contentDispositionCharacters);
+        if (contentDispositionBegin == notFound)
+            return false;
+        size_t contentDispositionEnd = header.find("\r\n", contentDispositionBegin);
+        size_t contentDispositionParametersBegin = header.find(';', contentDispositionBegin + strlen(contentDispositionCharacters));
+        if (contentDispositionParametersBegin != notFound)
+            contentDispositionParametersBegin++;
+
+        auto parameters = parseParameters(StringView(header).substring(contentDispositionParametersBegin, contentDispositionEnd - contentDispositionParametersBegin), 0);
+        String name = parameters.get("name");
+        if (!name)
+            return false;
+        String filename = parameters.get("filename");
+        if (!filename)
+            form.append(name, String::fromUTF8(bodyBegin, bodyLength));
+        else {
+            String contentType = "text/plain"_s;
+
+            const char* contentTypeCharacters = "Content-Type:";
+            size_t contentTypePrefixLength = strlen(contentTypeCharacters);
+            size_t contentTypeBegin = header.find(contentTypeCharacters);
+            if (contentTypeBegin != notFound) {
+                size_t contentTypeEnd = header.find("\r\n", contentTypeBegin);
+                contentType = stripLeadingAndTrailingHTTPSpaces(header.substring(contentTypeBegin + contentTypePrefixLength, contentTypeEnd - contentTypeBegin - contentTypePrefixLength));
+            }
+
+            form.append(name, File::create(Blob::create(SharedBuffer::create(bodyBegin, bodyLength).get(), Blob::normalizedContentType(contentType)).get(), filename).get(), filename);
+        }
+        return true;
+    };
+    
+    auto parseMultipartBoundary = [] (const Optional<MimeType>& mimeType) -> Optional<String> {
+        if (!mimeType)
+            return WTF::nullopt;
+        if (equalIgnoringASCIICase(mimeType->type, "multipart") && equalIgnoringASCIICase(mimeType->subtype, "form-data")) {
+            auto iterator = mimeType->parameters.find("boundary"_s);
+            if (iterator != mimeType->parameters.end())
+                return iterator->value;
+        }
+        return WTF::nullopt;
+    };
+
+    auto form = DOMFormData::create(UTF8Encoding());
+    auto mimeType = parseMIMEType(contentType);
+    if (auto multipartBoundary = parseMultipartBoundary(mimeType)) {
+        String boundaryWithDashes = makeString("--", *multipartBoundary);
+        const char* boundary = boundaryWithDashes.utf8().data();
+        size_t boundaryLength = strlen(boundary);
+
+        const char* currentBoundary = static_cast<const char*>(memmem(data, length, boundary, boundaryLength));
+        if (!currentBoundary)
+            return nullptr;
+        const char* nextBoundary = static_cast<const char*>(memmem(currentBoundary + boundaryLength, length - (currentBoundary + boundaryLength - data), boundary, boundaryLength));
+        if (!nextBoundary)
+            return nullptr;
+        while (nextBoundary) {
+            parseMultipartPart(currentBoundary + boundaryLength, nextBoundary - currentBoundary - boundaryLength - strlen("\r\n"), form.get());
+            currentBoundary = nextBoundary;
+            nextBoundary = static_cast<const char*>(memmem(nextBoundary + boundaryLength, length - (nextBoundary + boundaryLength - data), boundary, boundaryLength));
+        }
+    } else if (mimeType && equalIgnoringASCIICase(mimeType->type, "application") && equalIgnoringASCIICase(mimeType->subtype, "x-www-form-urlencoded")) {
+        auto dataString = String::fromUTF8(data, length);
+        for (auto& pair : WTF::URLParser::parseURLEncodedForm(dataString))
+            form->append(pair.key, pair.value);
+    } else
+        return nullptr;
+
+    return form;
+}
+
 static void resolveWithTypeAndData(Ref<DeferredPromise>&& promise, FetchBodyConsumer::Type type, const String& contentType, const unsigned char* data, unsigned length)
 {
     switch (type) {
@@ -59,6 +228,12 @@
     case FetchBodyConsumer::Type::Text:
         promise->resolve<IDLDOMString>(TextResourceDecoder::textFromUTF8(data, length));
         return;
+    case FetchBodyConsumer::Type::FormData:
+        if (auto formData = packageFormData(contentType, reinterpret_cast<const char*>(data), length))
+            promise->resolve<IDLInterface<DOMFormData>>(*formData);
+        else
+            promise->reject(TypeError);
+        return;
     case FetchBodyConsumer::Type::None:
         ASSERT_NOT_REACHED();
         return;
@@ -75,9 +250,9 @@
     }
 }
 
-void FetchBodyConsumer::resolveWithData(Ref<DeferredPromise>&& promise, const unsigned char* data, unsigned length)
+void FetchBodyConsumer::resolveWithData(Ref<DeferredPromise>&& promise, const String& contentType, const unsigned char* data, unsigned length)
 {
-    resolveWithTypeAndData(WTFMove(promise), m_type, m_contentType, data, length);
+    resolveWithTypeAndData(WTFMove(promise), m_type, contentType, data, length);
 }
 
 void FetchBodyConsumer::extract(ReadableStream& stream, ReadableStreamToSharedBufferSink::Callback&& callback)
@@ -87,11 +262,11 @@
     m_sink->pipeFrom(stream);
 }
 
-void FetchBodyConsumer::resolve(Ref<DeferredPromise>&& promise, ReadableStream* stream)
+void FetchBodyConsumer::resolve(Ref<DeferredPromise>&& promise, const String& contentType, ReadableStream* stream)
 {
     if (stream) {
         ASSERT(!m_sink);
-        m_sink = ReadableStreamToSharedBufferSink::create([promise = WTFMove(promise), data = "" type = m_type, contentType = m_contentType](auto&& result) mutable {
+        m_sink = ReadableStreamToSharedBufferSink::create([promise = WTFMove(promise), data = "" type = m_type, contentType](auto&& result) mutable {
             if (result.hasException()) {
                 promise->reject(result.releaseException());
                 return;
@@ -127,6 +302,14 @@
     case Type::Text:
         promise->resolve<IDLDOMString>(takeAsText());
         return;
+    case FetchBodyConsumer::Type::FormData: {
+        auto buffer = takeData();
+        if (auto formData = packageFormData(contentType, buffer ? buffer->data() : nullptr, buffer ? buffer->size() : 0))
+            promise->resolve<IDLInterface<DOMFormData>>(*formData);
+        else
+            promise->reject(TypeError);
+        return;
+    }
     case Type::None:
         ASSERT_NOT_REACHED();
         return;
@@ -169,7 +352,7 @@
 Ref<Blob> FetchBodyConsumer::takeAsBlob()
 {
     if (!m_buffer)
-        return Blob::create(Vector<uint8_t>(), m_contentType);
+        return Blob::create(Vector<uint8_t>(), Blob::normalizedContentType(m_contentType));
 
     // FIXME: We should try to move m_buffer to Blob without doing extra copy.
     return blobFromData(reinterpret_cast<const unsigned char*>(m_buffer->data()), m_buffer->size(), m_contentType);
@@ -221,16 +404,16 @@
     }
 }
 
-void FetchBodyConsumer::loadingSucceeded()
+void FetchBodyConsumer::loadingSucceeded(const String& contentType)
 {
     m_isLoading = false;
 
     if (m_consumePromise) {
         if (!m_userGestureToken || m_userGestureToken->hasExpired(UserGestureToken::maximumIntervalForUserGestureForwardingForFetch()) || !m_userGestureToken->processingUserGesture())
-            resolve(m_consumePromise.releaseNonNull(), nullptr);
+            resolve(m_consumePromise.releaseNonNull(), contentType, nullptr);
         else {
             UserGestureIndicator gestureIndicator(m_userGestureToken, UserGestureToken::GestureScope::MediaOnly, UserGestureToken::IsPropagatedFromFetch::Yes);
-            resolve(m_consumePromise.releaseNonNull(), nullptr);
+            resolve(m_consumePromise.releaseNonNull(), contentType, nullptr);
         }
     }
     if (m_source) {

Modified: trunk/Source/WebCore/Modules/fetch/FetchBodyConsumer.h (266086 => 266087)


--- trunk/Source/WebCore/Modules/fetch/FetchBodyConsumer.h	2020-08-24 21:41:43 UTC (rev 266086)
+++ trunk/Source/WebCore/Modules/fetch/FetchBodyConsumer.h	2020-08-24 22:36:02 UTC (rev 266087)
@@ -42,7 +42,7 @@
 
 class FetchBodyConsumer {
 public:
-    enum class Type { None, ArrayBuffer, Blob, JSON, Text };
+    enum class Type { None, ArrayBuffer, Blob, JSON, Text, FormData };
 
     explicit FetchBodyConsumer(Type type) : m_type(type) { }
 
@@ -63,11 +63,11 @@
     void clean();
 
     void extract(ReadableStream&, ReadableStreamToSharedBufferSink::Callback&&);
-    void resolve(Ref<DeferredPromise>&&, ReadableStream*);
-    void resolveWithData(Ref<DeferredPromise>&&, const unsigned char*, unsigned);
+    void resolve(Ref<DeferredPromise>&&, const String& contentType, ReadableStream*);
+    void resolveWithData(Ref<DeferredPromise>&&, const String& contentType, const unsigned char*, unsigned);
 
     void loadingFailed(const Exception&);
-    void loadingSucceeded();
+    void loadingSucceeded(const String& contentType);
 
     void setConsumePromise(Ref<DeferredPromise>&&);
     void setSource(Ref<FetchBodySource>&&);

Modified: trunk/Source/WebCore/Modules/fetch/FetchBodyOwner.cpp (266086 => 266087)


--- trunk/Source/WebCore/Modules/fetch/FetchBodyOwner.cpp	2020-08-24 21:41:43 UTC (rev 266086)
+++ trunk/Source/WebCore/Modules/fetch/FetchBodyOwner.cpp	2020-08-24 22:36:02 UTC (rev 266087)
@@ -270,7 +270,7 @@
         m_readableStreamSource = nullptr;
     }
 
-    m_body->loadingSucceeded();
+    m_body->loadingSucceeded(contentType());
     finishBlobLoading();
 }
 

Modified: trunk/Source/WebCore/Modules/fetch/FetchBodyOwner.h (266086 => 266087)


--- trunk/Source/WebCore/Modules/fetch/FetchBodyOwner.h	2020-08-24 21:41:43 UTC (rev 266086)
+++ trunk/Source/WebCore/Modules/fetch/FetchBodyOwner.h	2020-08-24 22:36:02 UTC (rev 266087)
@@ -70,6 +70,8 @@
     ResourceError loadingError() const;
     Optional<Exception> loadingException() const;
 
+    const String& contentType() const { return m_contentType; }
+
 protected:
     const FetchBody& body() const { return *m_body; }
     FetchBody& body() { return *m_body; }

Modified: trunk/Source/WebCore/Modules/fetch/FetchResponse.cpp (266086 => 266087)


--- trunk/Source/WebCore/Modules/fetch/FetchResponse.cpp	2020-08-24 21:41:43 UTC (rev 266086)
+++ trunk/Source/WebCore/Modules/fetch/FetchResponse.cpp	2020-08-24 22:36:02 UTC (rev 266087)
@@ -273,7 +273,7 @@
 void FetchResponse::BodyLoader::didSucceed()
 {
     ASSERT(m_response.hasPendingActivity());
-    m_response.m_body->loadingSucceeded();
+    m_response.m_body->loadingSucceeded(m_response.contentType());
 
     if (m_response.m_readableStreamSource) {
         if (m_response.body().consumer().hasData())

Modified: trunk/Source/WebCore/platform/network/FormData.h (266086 => 266087)


--- trunk/Source/WebCore/platform/network/FormData.h	2020-08-24 21:41:43 UTC (rev 266086)
+++ trunk/Source/WebCore/platform/network/FormData.h	2020-08-24 22:36:02 UTC (rev 266087)
@@ -228,7 +228,7 @@
     const Vector<FormDataElement>& elements() const { return m_elements; }
     const Vector<char>& boundary() const { return m_boundary; }
 
-    RefPtr<SharedBuffer> asSharedBuffer() const;
+    WEBCORE_EXPORT RefPtr<SharedBuffer> asSharedBuffer() const;
 
     bool alwaysStream() const { return m_alwaysStream; }
     void setAlwaysStream(bool alwaysStream) { m_alwaysStream = alwaysStream; }

Modified: trunk/Source/WebCore/platform/network/HTTPParsers.cpp (266086 => 266087)


--- trunk/Source/WebCore/platform/network/HTTPParsers.cpp	2020-08-24 21:41:43 UTC (rev 266086)
+++ trunk/Source/WebCore/platform/network/HTTPParsers.cpp	2020-08-24 22:36:02 UTC (rev 266087)
@@ -184,12 +184,11 @@
 }
 
 // See RFC 7230, Section 3.2.6.
-bool isValidHTTPToken(const String& value)
+bool isValidHTTPToken(StringView value)
 {
     if (value.isEmpty())
         return false;
-    auto valueStringView = StringView(value);
-    for (UChar c : valueStringView.codeUnits()) {
+    for (UChar c : value.codeUnits()) {
         if (!RFC7230::isTokenCharacter(c))
             return false;
     }
@@ -196,6 +195,11 @@
     return true;
 }
 
+bool isValidHTTPToken(const String& value)
+{
+    return isValidHTTPToken(StringView(value));
+}
+
 #if USE(GLIB)
 // True if the character at the given position satisifies a predicate, incrementing "pos" by one.
 // Note: Might return pos == str.length()

Modified: trunk/Source/WebCore/platform/network/HTTPParsers.h (266086 => 266087)


--- trunk/Source/WebCore/platform/network/HTTPParsers.h	2020-08-24 21:41:43 UTC (rev 266086)
+++ trunk/Source/WebCore/platform/network/HTTPParsers.h	2020-08-24 22:36:02 UTC (rev 266087)
@@ -76,6 +76,7 @@
 WEBCORE_EXPORT bool isValidUserAgentHeaderValue(const String&);
 #endif
 bool isValidHTTPToken(const String&);
+bool isValidHTTPToken(StringView);
 Optional<WallTime> parseHTTPDate(const String&);
 String filenameFromHTTPContentDisposition(const String&);
 String extractMIMETypeFromMediaType(const String&);

Modified: trunk/Source/WebKit/ChangeLog (266086 => 266087)


--- trunk/Source/WebKit/ChangeLog	2020-08-24 21:41:43 UTC (rev 266086)
+++ trunk/Source/WebKit/ChangeLog	2020-08-24 22:36:02 UTC (rev 266087)
@@ -1,5 +1,18 @@
 2020-08-24  Alex Christensen  <achristen...@webkit.org>
 
+        Implement Request/Response consuming as FormData
+        https://bugs.webkit.org/show_bug.cgi?id=215671
+
+        Reviewed by Darin Adler.
+
+        * WebProcess/Storage/WebServiceWorkerFetchTaskClient.cpp:
+        (WebKit::WebServiceWorkerFetchTaskClient::didReceiveFormDataAndFinish):
+        Add a fast path that allows non-blob FormData responses from service workers to not hang.
+        This part is covered by this layout test:
+        imported/w3c/web-platform-tests/service-workers/service-worker/fetch-event-respond-with-custom-response.https.html
+
+2020-08-24  Alex Christensen  <achristen...@webkit.org>
+
         Make _WKWebsiteDataStoreConfiguration SPI for HSTS storage to replace _WKProcessPoolConfiguration.hstsStorageDirectory
         https://bugs.webkit.org/show_bug.cgi?id=213048
 

Modified: trunk/Source/WebKit/WebProcess/Storage/WebServiceWorkerFetchTaskClient.cpp (266086 => 266087)


--- trunk/Source/WebKit/WebProcess/Storage/WebServiceWorkerFetchTaskClient.cpp	2020-08-24 21:41:43 UTC (rev 266086)
+++ trunk/Source/WebKit/WebProcess/Storage/WebServiceWorkerFetchTaskClient.cpp	2020-08-24 22:36:02 UTC (rev 266087)
@@ -89,6 +89,12 @@
 
 void WebServiceWorkerFetchTaskClient::didReceiveFormDataAndFinish(Ref<FormData>&& formData)
 {
+    if (auto sharedBuffer = formData->asSharedBuffer()) {
+        didReceiveData(sharedBuffer.releaseNonNull());
+        didFinish();
+        return;
+    }
+
     if (!m_connection)
         return;
 
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to