Align FetchResponse and FetchRequest body handling
authorcommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 7 Sep 2017 23:23:57 +0000 (23:23 +0000)
committercommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 7 Sep 2017 23:23:57 +0000 (23:23 +0000)
https://bugs.webkit.org/show_bug.cgi?id=176539

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

LayoutTests/imported/w3c:

* web-platform-tests/fetch/api/request/request-consume-empty-expected.txt:
* web-platform-tests/fetch/api/request/request-consume-expected.txt:
* web-platform-tests/fetch/api/request/request-idl-expected.txt:
* web-platform-tests/fetch/api/request/request-structure-expected.txt:
* web-platform-tests/fetch/api/response/response-consume-empty-expected.txt:
* web-platform-tests/fetch/api/response/response-consume-expected.txt:
* web-platform-tests/url/urlencoded-parser-expected.txt:

Source/WebCore:

Covered by rebased tests.

Removing most of FetchResponse JS Builtins now that ReadableStream has full support.
Implementing FetchResponse body cloning through ReadableStream.
Cloning a loading FetchResponse still requires to create a ReadableStream for the purpose of teeing.

Moving exposure of the body from FetchResponse to FetchBodyOwner.
This is controlled by a boolean flag set according response tainting.

Moving handling of body ReadableStream consuming from FetchResponse to FetchBodyConsumer.
For that purpose, a loading boolean flag is added to FetchBodyConsumer so that it will resolve consume promises
when loading is finished.

Added support for getting a body in case the request/response body is already consumed.
In that case, a locked ReadableStream is returned.

* Modules/cache/DOMCache.cpp:
(WebCore::DOMCache::doMatch):
(WebCore::DOMCache::matchAll):
* Modules/fetch/FetchBody.h:
(WebCore::FetchBody::loadingBody):
(WebCore::FetchBody::FetchBody):
* Modules/fetch/FetchBody.idl:
* Modules/fetch/FetchBodyConsumer.cpp:
(WebCore::FetchBodyConsumer::resolve):
(WebCore::FetchBodyConsumer::loadingFailed):
(WebCore::FetchBodyConsumer::loadingSucceeded):
* Modules/fetch/FetchBodyConsumer.h:
(WebCore::FetchBodyConsumer::setAsLoading):
* Modules/fetch/FetchBodyOwner.cpp:
(WebCore::FetchBodyOwner::arrayBuffer):
(WebCore::FetchBodyOwner::blob):
(WebCore::FetchBodyOwner::formData):
(WebCore::FetchBodyOwner::json):
(WebCore::FetchBodyOwner::text):
(WebCore::FetchBodyOwner::readableStream):
* Modules/fetch/FetchBodyOwner.h:
(WebCore::FetchBodyOwner::isBodyNullOrOpaque const):
(WebCore::FetchBodyOwner::setBodyAsOpaque):
(WebCore::FetchBodyOwner::isBodyOpaque const):
* Modules/fetch/FetchInternals.js:
(fillFetchHeaders):
* Modules/fetch/FetchResponse.cpp:
(WebCore::FetchResponse::clone):
(WebCore::FetchResponse::fetch):
(WebCore::FetchResponse::BodyLoader::didReceiveResponse):
(WebCore::FetchResponse::setBodyData):
(WebCore::FetchResponse::consumeChunk):
(WebCore::FetchResponse::consumeBodyAsStream):
* Modules/fetch/FetchResponse.h:
* Modules/fetch/FetchResponse.idl:
* Modules/fetch/FetchResponse.js:
(initializeFetchResponse):
* bindings/js/ReadableStream.cpp:
(WebCore::ReadableStream::create):
(WebCore::ReadableStream::lock):
* bindings/js/ReadableStream.h:
* bindings/js/WebCoreBuiltinNames.h:

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

24 files changed:
LayoutTests/imported/w3c/ChangeLog
LayoutTests/imported/w3c/web-platform-tests/fetch/api/request/request-consume-empty-expected.txt
LayoutTests/imported/w3c/web-platform-tests/fetch/api/request/request-consume-expected.txt
LayoutTests/imported/w3c/web-platform-tests/fetch/api/request/request-idl-expected.txt
LayoutTests/imported/w3c/web-platform-tests/fetch/api/request/request-structure-expected.txt
LayoutTests/imported/w3c/web-platform-tests/fetch/api/response/response-consume-empty-expected.txt
LayoutTests/imported/w3c/web-platform-tests/fetch/api/response/response-consume-expected.txt
LayoutTests/imported/w3c/web-platform-tests/url/urlencoded-parser-expected.txt
Source/WebCore/ChangeLog
Source/WebCore/Modules/cache/DOMCache.cpp
Source/WebCore/Modules/fetch/FetchBody.h
Source/WebCore/Modules/fetch/FetchBody.idl
Source/WebCore/Modules/fetch/FetchBodyConsumer.cpp
Source/WebCore/Modules/fetch/FetchBodyConsumer.h
Source/WebCore/Modules/fetch/FetchBodyOwner.cpp
Source/WebCore/Modules/fetch/FetchBodyOwner.h
Source/WebCore/Modules/fetch/FetchInternals.js
Source/WebCore/Modules/fetch/FetchResponse.cpp
Source/WebCore/Modules/fetch/FetchResponse.h
Source/WebCore/Modules/fetch/FetchResponse.idl
Source/WebCore/Modules/fetch/FetchResponse.js
Source/WebCore/bindings/js/ReadableStream.cpp
Source/WebCore/bindings/js/ReadableStream.h
Source/WebCore/bindings/js/WebCoreBuiltinNames.h

index e223c6d..c05152b 100644 (file)
@@ -1,3 +1,18 @@
+2017-09-07  Youenn Fablet  <youenn@apple.com>
+
+        Align FetchResponse and FetchRequest body handling
+        https://bugs.webkit.org/show_bug.cgi?id=176539
+
+        Reviewed by Alex Christensen.
+
+        * web-platform-tests/fetch/api/request/request-consume-empty-expected.txt:
+        * web-platform-tests/fetch/api/request/request-consume-expected.txt:
+        * web-platform-tests/fetch/api/request/request-idl-expected.txt:
+        * web-platform-tests/fetch/api/request/request-structure-expected.txt:
+        * web-platform-tests/fetch/api/response/response-consume-empty-expected.txt:
+        * web-platform-tests/fetch/api/response/response-consume-expected.txt:
+        * web-platform-tests/url/urlencoded-parser-expected.txt:
+
 2017-09-06  Youenn Fablet  <youenn@apple.com>
 
         Support caching of Response with a ReadableStream body
index 1bcdc44..3e6719b 100644 (file)
@@ -3,9 +3,9 @@ PASS Consume request's body as text
 PASS Consume request's body as blob 
 PASS Consume request's body as arrayBuffer 
 PASS Consume request's body as json (error case) 
-FAIL Consume request's body as formData with correct multipart type (error case) request.formData is not a function. (In 'request.formData()', 'request.formData' is undefined)
-FAIL Consume request's body as formData with correct urlencoded type request.formData is not a function. (In 'request.formData()', 'request.formData' is undefined)
-FAIL Consume request's body as formData without correct type (error case) request.formData is not a function. (In 'request.formData()', 'request.formData' is undefined)
+FAIL Consume request's body as formData with correct multipart type (error case) promise_test: Unhandled rejection with value: object "TypeError: undefined is not an object (evaluating 'e.name')"
+FAIL Consume request's body as formData with correct urlencoded type promise_test: Unhandled rejection with value: undefined
+FAIL Consume request's body as formData without correct type (error case) promise_test: Unhandled rejection with value: object "TypeError: undefined is not an object (evaluating 'e.name')"
 PASS Consume empty blob request body as arrayBuffer 
 PASS Consume empty text request body as arrayBuffer 
 PASS Consume empty blob request body as text 
index adde350..336d954 100644 (file)
@@ -23,7 +23,7 @@ PASS Consume DataView request's body as text
 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 request.formData is not a function. (In 'request.formData()', 'request.formData' is undefined)
+FAIL Consume FormData request's body as FormData promise_test: Unhandled rejection with value: object "NotSupportedError: The operation is not supported."
 PASS Consume blob response's body as blob 
 PASS Consume blob response's body as text 
 PASS Consume blob response's body as json 
index bec92c2..3c59fc0 100644 (file)
@@ -22,7 +22,7 @@ PASS Request interface: attribute body
 PASS Request interface: attribute bodyUsed 
 PASS Request interface: operation arrayBuffer() 
 PASS Request interface: operation blob() 
-FAIL Request interface: operation formData() assert_own_property: interface prototype object missing non-static operation expected property "formData" missing
+PASS Request interface: operation formData() 
 PASS Request interface: operation json() 
 PASS Request interface: operation text() 
 PASS Request interface: new Request("") must inherit property "method" with the proper type (0) 
@@ -43,7 +43,7 @@ PASS Request interface: new Request("") must inherit property "body" with the pr
 PASS Request interface: new Request("") must inherit property "bodyUsed" with the proper type (15) 
 PASS Request interface: new Request("") must inherit property "arrayBuffer" with the proper type (16) 
 PASS Request interface: new Request("") must inherit property "blob" with the proper type (17) 
-FAIL Request interface: new Request("") must inherit property "formData" with the proper type (18) assert_inherits: property "formData" not found in prototype chain
+PASS Request interface: new Request("") must inherit property "formData" with the proper type (18) 
 PASS Request interface: new Request("") must inherit property "json" with the proper type (19) 
 PASS Request interface: new Request("") must inherit property "text" with the proper type (20) 
 
index c12e184..9374957 100644 (file)
@@ -2,7 +2,7 @@
 PASS Request has clone method 
 PASS Request has arrayBuffer method 
 PASS Request has blob method 
-FAIL Request has formData method assert_true: request has formData method expected true got false
+PASS Request has formData method 
 PASS Request has json method 
 PASS Request has text method 
 PASS Check method attribute 
index 044c56e..222562d 100644 (file)
@@ -3,9 +3,9 @@ PASS Consume response's body as text
 PASS Consume response's body as blob 
 PASS Consume response's body as arrayBuffer 
 PASS Consume response's body as json (error case) 
-FAIL Consume response's body as formData with correct multipart type (error case) assert_throws: function "function () { throw e }" threw "Not implemented" (undefined) expected object "TypeError" ("TypeError")
-FAIL Consume response's body as formData with correct urlencoded type promise_test: Unhandled rejection with value: "Not implemented"
-FAIL Consume response's body as formData without correct type (error case) assert_throws: function "function () { throw e }" threw "Not implemented" (undefined) expected object "TypeError" ("TypeError")
+FAIL Consume response's body as formData with correct multipart type (error case) promise_test: Unhandled rejection with value: object "TypeError: undefined is not an object (evaluating 'e.name')"
+FAIL Consume response's body as formData with correct urlencoded type promise_test: Unhandled rejection with value: undefined
+FAIL Consume response's body as formData without correct type (error case) promise_test: Unhandled rejection with value: object "TypeError: undefined is not an object (evaluating 'e.name')"
 PASS Consume empty blob response body as arrayBuffer 
 PASS Consume empty text response body as arrayBuffer 
 PASS Consume empty blob response body as text 
index 7535c68..4b9a4b0 100644 (file)
@@ -3,25 +3,25 @@ PASS Consume response's body: from text to text
 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: "Not implemented"
-FAIL Consume response's body: from text without correct multipart type to formData (error case) assert_throws: function "function () { throw e }" threw "Not implemented" (undefined) expected object "TypeError" ("TypeError")
-FAIL Consume response's body: from text with correct urlencoded type to formData promise_test: Unhandled rejection with value: "Not implemented"
-FAIL Consume response's body: from text without correct urlencoded type to formData (error case) assert_throws: function "function () { throw e }" threw "Not implemented" (undefined) expected object "TypeError" ("TypeError")
+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 without correct multipart type to formData (error case) assert_throws: function "function () { throw e }" threw object "NotSupportedError: The operation is not supported." ("NotSupportedError") expected object "TypeError" ("TypeError")
+FAIL Consume response's body: from 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) assert_throws: function "function () { throw e }" threw object "NotSupportedError: The operation is not supported." ("NotSupportedError") expected object "TypeError" ("TypeError")
 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: "Not implemented"
-FAIL Consume response's body: from blob without correct multipart type to formData (error case) assert_throws: function "function () { throw e }" threw "Not implemented" (undefined) expected object "TypeError" ("TypeError")
-FAIL Consume response's body: from blob with correct urlencoded type to formData promise_test: Unhandled rejection with value: "Not implemented"
-FAIL Consume response's body: from blob without correct urlencoded type to formData (error case) assert_throws: function "function () { throw e }" threw "Not implemented" (undefined) expected object "TypeError" ("TypeError")
-FAIL Consume response's body: from FormData to formData promise_test: Unhandled rejection with value: "Not implemented"
-FAIL Consume response's body: from FormData without correct type to formData (error case) assert_throws: function "function () { throw e }" threw "Not implemented" (undefined) expected object "TypeError" ("TypeError")
+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) assert_throws: function "function () { throw e }" threw object "NotSupportedError: The operation is not supported." ("NotSupportedError") expected object "TypeError" ("TypeError")
+FAIL Consume response's body: from 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) assert_throws: function "function () { throw e }" threw object "NotSupportedError: The operation is not supported." ("NotSupportedError") expected object "TypeError" ("TypeError")
+FAIL Consume response's body: from FormData to formData promise_test: Unhandled rejection with value: object "NotSupportedError: The operation is not supported."
+FAIL Consume response's body: from FormData without correct type to formData (error case) assert_throws: function "function () { throw e }" threw object "NotSupportedError: The operation is not supported." ("NotSupportedError") expected object "TypeError" ("TypeError")
 FAIL Consume response's body: from FormData to blob promise_test: Unhandled rejection with value: undefined
 FAIL Consume response's body: from FormData to text promise_test: Unhandled rejection with value: undefined
 FAIL Consume response's body: from FormData to arrayBuffer promise_test: Unhandled rejection with value: undefined
-FAIL Consume response's body: from URLSearchParams to formData promise_test: Unhandled rejection with value: "Not implemented"
-FAIL Consume response's body: from URLSearchParams without correct type to formData (error case) assert_throws: function "function () { throw e }" threw "Not implemented" (undefined) expected object "TypeError" ("TypeError")
+FAIL Consume response's body: from URLSearchParams to formData promise_test: Unhandled rejection with value: object "NotSupportedError: The operation is not supported."
+FAIL Consume response's body: from URLSearchParams without correct type to formData (error case) assert_throws: function "function () { throw e }" threw object "NotSupportedError: The operation is not supported." ("NotSupportedError") expected object "TypeError" ("TypeError")
 FAIL Consume response's body: from URLSearchParams to blob assert_equals: Blob body type should be computed from the response Content-Type expected "application/x-www-form-urlencoded;charset=utf-8" but got "application/x-www-form-urlencoded"
 PASS Consume response's body: from URLSearchParams to text 
 PASS Consume response's body: from URLSearchParams to arrayBuffer 
@@ -29,13 +29,13 @@ PASS Consume response's body: from stream to blob
 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: "Not implemented"
-FAIL Consume response's body: from stream without correct multipart type to formData (error case) assert_throws: function "function () { throw e }" threw "Not implemented" (undefined) expected object "TypeError" ("TypeError")
-FAIL Consume response's body: from stream with correct urlencoded type to formData promise_test: Unhandled rejection with value: "Not implemented"
-FAIL Consume response's body: from stream without correct urlencoded type to formData (error case) assert_throws: function "function () { throw e }" threw "Not implemented" (undefined) expected object "TypeError" ("TypeError")
+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) assert_throws: function "function () { throw e }" threw object "NotSupportedError: The operation is not supported." ("NotSupportedError") expected object "TypeError" ("TypeError")
+FAIL Consume response's body: from 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) assert_throws: function "function () { throw e }" threw object "NotSupportedError: The operation is not supported." ("NotSupportedError") expected object "TypeError" ("TypeError")
 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) assert_throws: function "function () { throw e }" threw "Not implemented" (undefined) expected object "TypeError" ("TypeError")
-FAIL Consume response's body: from multipart form data blob to formData promise_test: Unhandled rejection with value: "Not implemented"
+FAIL Consume response's body: from fetch without correct type to formData (error case) assert_throws: function "function () { throw e }" threw object "NotSupportedError: The operation is not supported." ("NotSupportedError") expected object "TypeError" ("TypeError")
+FAIL Consume response's body: from multipart form data blob to formData promise_test: Unhandled rejection with value: object "NotSupportedError: The operation is not supported."
 
index 22e2281..d54a237 100644 (file)
@@ -1,92 +1,92 @@
 
 PASS URLSearchParams constructed with: test 
-FAIL request.formData() with input: test undefined is not a function (near '...s-1252"} }).formData()...')
-FAIL response.formData() with input: test promise_test: Unhandled rejection with value: "Not implemented"
+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 URLSearchParams constructed with: test= 
-FAIL request.formData() with input: test= undefined is not a function (near '...s-1252"} }).formData()...')
-FAIL response.formData() with input: test= promise_test: Unhandled rejection with value: "Not implemented"
+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 URLSearchParams constructed with: %EF%BB%BFtest=%EF%BB%BF 
-FAIL request.formData() with input: %EF%BB%BFtest=%EF%BB%BF undefined is not a function (near '...s-1252"} }).formData()...')
-FAIL response.formData() with input: %EF%BB%BFtest=%EF%BB%BF promise_test: Unhandled rejection with value: "Not implemented"
+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: property 0, expected "\ufffd\ufffd" but got ""
-FAIL request.formData() with input: %FE%FF undefined is not a function (near '...s-1252"} }).formData()...')
-FAIL response.formData() with input: %FE%FF promise_test: Unhandled rejection with value: "Not implemented"
+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: property 0, expected "\ufffd\ufffd" but got ""
-FAIL request.formData() with input: %FF%FE undefined is not a function (near '...s-1252"} }).formData()...')
-FAIL response.formData() with input: %FF%FE promise_test: Unhandled rejection with value: "Not implemented"
+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 URLSearchParams constructed with: †&†=x 
-FAIL request.formData() with input: †&†=x undefined is not a function (near '...s-1252"} }).formData()...')
-FAIL response.formData() with input: †&†=x promise_test: Unhandled rejection with value: "Not implemented"
+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: property 0, expected "\ufffd" but got ""
-FAIL request.formData() with input: %C2 undefined is not a function (near '...s-1252"} }).formData()...')
-FAIL response.formData() with input: %C2 promise_test: Unhandled rejection with value: "Not implemented"
+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: property 0, expected "\ufffdx" but got ""
-FAIL request.formData() with input: %C2x undefined is not a function (near '...s-1252"} }).formData()...')
-FAIL response.formData() with input: %C2x promise_test: Unhandled rejection with value: "Not implemented"
+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: property 1, expected "\ufffdx" but got ""
-FAIL request.formData() with input: _charset_=windows-1252&test=%C2x undefined is not a function (near '...s-1252"} }).formData()...')
-FAIL response.formData() with input: _charset_=windows-1252&test=%C2x promise_test: Unhandled rejection with value: "Not implemented"
+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 URLSearchParams constructed with:  
-FAIL request.formData() with input:  undefined is not a function (near '...s-1252"} }).formData()...')
-FAIL response.formData() with input:  promise_test: Unhandled rejection with value: "Not implemented"
+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 URLSearchParams constructed with: a 
-FAIL request.formData() with input: a undefined is not a function (near '...s-1252"} }).formData()...')
-FAIL response.formData() with input: a promise_test: Unhandled rejection with value: "Not implemented"
+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 URLSearchParams constructed with: a=b 
-FAIL request.formData() with input: a=b undefined is not a function (near '...s-1252"} }).formData()...')
-FAIL response.formData() with input: a=b promise_test: Unhandled rejection with value: "Not implemented"
+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 URLSearchParams constructed with: a= 
-FAIL request.formData() with input: a= undefined is not a function (near '...s-1252"} }).formData()...')
-FAIL response.formData() with input: a= promise_test: Unhandled rejection with value: "Not implemented"
+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 URLSearchParams constructed with: =b 
-FAIL request.formData() with input: =b undefined is not a function (near '...s-1252"} }).formData()...')
-FAIL response.formData() with input: =b promise_test: Unhandled rejection with value: "Not implemented"
+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 URLSearchParams constructed with: & 
-FAIL request.formData() with input: & undefined is not a function (near '...s-1252"} }).formData()...')
-FAIL response.formData() with input: & promise_test: Unhandled rejection with value: "Not implemented"
+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 URLSearchParams constructed with: &a 
-FAIL request.formData() with input: &a undefined is not a function (near '...s-1252"} }).formData()...')
-FAIL response.formData() with input: &a promise_test: Unhandled rejection with value: "Not implemented"
+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 URLSearchParams constructed with: a& 
-FAIL request.formData() with input: a& undefined is not a function (near '...s-1252"} }).formData()...')
-FAIL response.formData() with input: a& promise_test: Unhandled rejection with value: "Not implemented"
+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 URLSearchParams constructed with: a&a 
-FAIL request.formData() with input: a&a undefined is not a function (near '...s-1252"} }).formData()...')
-FAIL response.formData() with input: a&a promise_test: Unhandled rejection with value: "Not implemented"
+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 URLSearchParams constructed with: a&b&c 
-FAIL request.formData() with input: a&b&c undefined is not a function (near '...s-1252"} }).formData()...')
-FAIL response.formData() with input: a&b&c promise_test: Unhandled rejection with value: "Not implemented"
+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 URLSearchParams constructed with: a=b&c=d 
-FAIL request.formData() with input: a=b&c=d undefined is not a function (near '...s-1252"} }).formData()...')
-FAIL response.formData() with input: a=b&c=d promise_test: Unhandled rejection with value: "Not implemented"
+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 URLSearchParams constructed with: a=b&c=d& 
-FAIL request.formData() with input: a=b&c=d& undefined is not a function (near '...s-1252"} }).formData()...')
-FAIL response.formData() with input: a=b&c=d& promise_test: Unhandled rejection with value: "Not implemented"
+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 URLSearchParams constructed with: &&&a=b&&&&c=d& 
-FAIL request.formData() with input: &&&a=b&&&&c=d& undefined is not a function (near '...s-1252"} }).formData()...')
-FAIL response.formData() with input: &&&a=b&&&&c=d& promise_test: Unhandled rejection with value: "Not implemented"
+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 URLSearchParams constructed with: a=a&a=b&a=c 
-FAIL request.formData() with input: a=a&a=b&a=c undefined is not a function (near '...s-1252"} }).formData()...')
-FAIL response.formData() with input: a=a&a=b&a=c promise_test: Unhandled rejection with value: "Not implemented"
+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 URLSearchParams constructed with: a==a 
-FAIL request.formData() with input: a==a undefined is not a function (near '...s-1252"} }).formData()...')
-FAIL response.formData() with input: a==a promise_test: Unhandled rejection with value: "Not implemented"
+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 URLSearchParams constructed with: a=a+b+c+d 
-FAIL request.formData() with input: a=a+b+c+d undefined is not a function (near '...s-1252"} }).formData()...')
-FAIL response.formData() with input: a=a+b+c+d promise_test: Unhandled rejection with value: "Not implemented"
+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 URLSearchParams constructed with: %=a 
-FAIL request.formData() with input: %=a undefined is not a function (near '...s-1252"} }).formData()...')
-FAIL response.formData() with input: %=a promise_test: Unhandled rejection with value: "Not implemented"
+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 URLSearchParams constructed with: %a=a 
-FAIL request.formData() with input: %a=a undefined is not a function (near '...s-1252"} }).formData()...')
-FAIL response.formData() with input: %a=a promise_test: Unhandled rejection with value: "Not implemented"
+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 URLSearchParams constructed with: %a_=a 
-FAIL request.formData() with input: %a_=a undefined is not a function (near '...s-1252"} }).formData()...')
-FAIL response.formData() with input: %a_=a promise_test: Unhandled rejection with value: "Not implemented"
+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 URLSearchParams constructed with: %61=a 
-FAIL request.formData() with input: %61=a undefined is not a function (near '...s-1252"} }).formData()...')
-FAIL response.formData() with input: %61=a promise_test: Unhandled rejection with value: "Not implemented"
+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 URLSearchParams constructed with: %61+%4d%4D= 
-FAIL request.formData() with input: %61+%4d%4D= undefined is not a function (near '...s-1252"} }).formData()...')
-FAIL response.formData() with input: %61+%4d%4D= promise_test: Unhandled rejection with value: "Not implemented"
+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."
 
index c8a65c0..5d6a49e 100644 (file)
@@ -1,3 +1,69 @@
+2017-09-07  Youenn Fablet  <youenn@apple.com>
+
+        Align FetchResponse and FetchRequest body handling
+        https://bugs.webkit.org/show_bug.cgi?id=176539
+
+        Reviewed by Alex Christensen.
+
+        Covered by rebased tests.
+
+        Removing most of FetchResponse JS Builtins now that ReadableStream has full support.
+        Implementing FetchResponse body cloning through ReadableStream.
+        Cloning a loading FetchResponse still requires to create a ReadableStream for the purpose of teeing.
+
+        Moving exposure of the body from FetchResponse to FetchBodyOwner.
+        This is controlled by a boolean flag set according response tainting.
+
+        Moving handling of body ReadableStream consuming from FetchResponse to FetchBodyConsumer.
+        For that purpose, a loading boolean flag is added to FetchBodyConsumer so that it will resolve consume promises
+        when loading is finished.
+
+        Added support for getting a body in case the request/response body is already consumed.
+        In that case, a locked ReadableStream is returned.
+
+        * Modules/cache/DOMCache.cpp:
+        (WebCore::DOMCache::doMatch):
+        (WebCore::DOMCache::matchAll):
+        * Modules/fetch/FetchBody.h:
+        (WebCore::FetchBody::loadingBody):
+        (WebCore::FetchBody::FetchBody):
+        * Modules/fetch/FetchBody.idl:
+        * Modules/fetch/FetchBodyConsumer.cpp:
+        (WebCore::FetchBodyConsumer::resolve):
+        (WebCore::FetchBodyConsumer::loadingFailed):
+        (WebCore::FetchBodyConsumer::loadingSucceeded):
+        * Modules/fetch/FetchBodyConsumer.h:
+        (WebCore::FetchBodyConsumer::setAsLoading):
+        * Modules/fetch/FetchBodyOwner.cpp:
+        (WebCore::FetchBodyOwner::arrayBuffer):
+        (WebCore::FetchBodyOwner::blob):
+        (WebCore::FetchBodyOwner::formData):
+        (WebCore::FetchBodyOwner::json):
+        (WebCore::FetchBodyOwner::text):
+        (WebCore::FetchBodyOwner::readableStream):
+        * Modules/fetch/FetchBodyOwner.h:
+        (WebCore::FetchBodyOwner::isBodyNullOrOpaque const):
+        (WebCore::FetchBodyOwner::setBodyAsOpaque):
+        (WebCore::FetchBodyOwner::isBodyOpaque const):
+        * Modules/fetch/FetchInternals.js:
+        (fillFetchHeaders):
+        * Modules/fetch/FetchResponse.cpp:
+        (WebCore::FetchResponse::clone):
+        (WebCore::FetchResponse::fetch):
+        (WebCore::FetchResponse::BodyLoader::didReceiveResponse):
+        (WebCore::FetchResponse::setBodyData):
+        (WebCore::FetchResponse::consumeChunk):
+        (WebCore::FetchResponse::consumeBodyAsStream):
+        * Modules/fetch/FetchResponse.h:
+        * Modules/fetch/FetchResponse.idl:
+        * Modules/fetch/FetchResponse.js:
+        (initializeFetchResponse):
+        * bindings/js/ReadableStream.cpp:
+        (WebCore::ReadableStream::create):
+        (WebCore::ReadableStream::lock):
+        * bindings/js/ReadableStream.h:
+        * bindings/js/WebCoreBuiltinNames.h:
+
 2017-09-07  Michael Saboff  <msaboff@apple.com>
 
         Add support for RegExp named capture groups
index eea03a9..5a084e2 100644 (file)
@@ -82,7 +82,7 @@ void DOMCache::doMatch(RequestInfo&& info, CacheQueryOptions&& options, MatchCal
     }
     auto request = requestOrException.releaseReturnValue();
 
-    queryCache(request.get(), WTFMove(options), [callback = WTFMove(callback)](ExceptionOr<Vector<CacheStorageRecord>>&& result) mutable {
+    queryCache(request.get(), WTFMove(options), [this, callback = WTFMove(callback)](ExceptionOr<Vector<CacheStorageRecord>>&& result) mutable {
         if (result.hasException()) {
             callback(result.releaseException());
             return;
@@ -91,7 +91,7 @@ void DOMCache::doMatch(RequestInfo&& info, CacheQueryOptions&& options, MatchCal
             callback(nullptr);
             return;
         }
-        callback(result.returnValue()[0].response->cloneForJS().ptr());
+        callback(result.returnValue()[0].response->clone(*scriptExecutionContext()).releaseReturnValue().ptr());
     });
 }
 
@@ -119,12 +119,12 @@ void DOMCache::matchAll(std::optional<RequestInfo>&& info, CacheQueryOptions&& o
             Vector<Ref<FetchResponse>> responses;
             responses.reserveInitialCapacity(m_records.size());
             for (auto& record : m_records)
-                responses.uncheckedAppend(record.response->cloneForJS());
-            promise.resolve(responses);
+                responses.uncheckedAppend(record.response->clone(*scriptExecutionContext()).releaseReturnValue());
+            promise.resolve(WTFMove(responses));
         });
         return;
     }
-    queryCache(request.releaseNonNull(), WTFMove(options), [promise = WTFMove(promise)](ExceptionOr<Vector<CacheStorageRecord>>&& result) mutable {
+    queryCache(request.releaseNonNull(), WTFMove(options), [this, promise = WTFMove(promise)](ExceptionOr<Vector<CacheStorageRecord>>&& result) mutable {
         if (result.hasException()) {
             promise.reject(result.releaseException());
             return;
@@ -133,7 +133,7 @@ void DOMCache::matchAll(std::optional<RequestInfo>&& info, CacheQueryOptions&& o
         Vector<Ref<FetchResponse>> responses;
         responses.reserveInitialCapacity(records.size());
         for (auto& record : records)
-            responses.uncheckedAppend(record.response->cloneForJS());
+            responses.uncheckedAppend(record.response->clone(*scriptExecutionContext()).releaseReturnValue());
         promise.resolve(responses);
     });
 }
index c3aa7fe..dbaa844 100644 (file)
@@ -64,7 +64,7 @@ public:
 
     using Init = Variant<RefPtr<Blob>, RefPtr<ArrayBufferView>, RefPtr<ArrayBuffer>, RefPtr<DOMFormData>, RefPtr<URLSearchParams>, RefPtr<ReadableStream>, String>;
     static FetchBody extract(ScriptExecutionContext&, Init&&, String&);
-    static FetchBody loadingBody() { return { }; }
+    FetchBody() = default;
 
     void loadingFailed();
     void loadingSucceeded();
@@ -98,7 +98,6 @@ private:
     explicit FetchBody(Ref<const URLSearchParams>&& data) : m_data(WTFMove(data)) { }
     explicit FetchBody(const FetchBodyConsumer& consumer) : m_consumer(consumer) { }
     explicit FetchBody(Ref<ReadableStream>&& stream) : m_readableStream(WTFMove(stream)) { }
-    FetchBody() = default;
 
     void consume(FetchBodyOwner&, Ref<DeferredPromise>&&);
 
index 63c8448..50d89c0 100644 (file)
@@ -37,8 +37,7 @@
     readonly attribute boolean bodyUsed;
     [NewObject] Promise<ArrayBuffer> arrayBuffer();
     [NewObject] Promise<Blob> blob();
-    // FIXME: Add support for form data consumption (https://bugs.webkit.org/show_bug.cgi?id=161190).
-    //[NewObject] Promise<DOMFormData> formData();
+    [NewObject] Promise<DOMFormData> formData();
     [NewObject] Promise<any> json();
     [NewObject] Promise<USVString> text();
 };
index d2d5260..8abf165 100644 (file)
@@ -117,6 +117,11 @@ void FetchBodyConsumer::resolve(Ref<DeferredPromise>&& promise, ReadableStream*
         return;
     }
 
+    if (m_isLoading) {
+        m_consumePromise = WTFMove(promise);
+        return;
+    }
+
     ASSERT(m_type != Type::None);
     switch (m_type) {
     case Type::ArrayBuffer:
@@ -207,6 +212,7 @@ void FetchBodyConsumer::setSource(Ref<FetchBodySource>&& source)
 
 void FetchBodyConsumer::loadingFailed()
 {
+    m_isLoading = false;
     if (m_consumePromise) {
         m_consumePromise->reject();
         m_consumePromise = nullptr;
@@ -219,6 +225,8 @@ void FetchBodyConsumer::loadingFailed()
 
 void FetchBodyConsumer::loadingSucceeded()
 {
+    m_isLoading = false;
+
     if (m_consumePromise)
         resolve(m_consumePromise.releaseNonNull(), nullptr);
     if (m_source) {
index a5e0182..2ceff37 100644 (file)
@@ -73,6 +73,8 @@ public:
     void setConsumePromise(Ref<DeferredPromise>&&);
     void setSource(Ref<FetchBodySource>&&);
 
+    void setAsLoading() { m_isLoading = true; }
+
 private:
     Type m_type;
     String m_contentType;
@@ -80,6 +82,7 @@ private:
     RefPtr<DeferredPromise> m_consumePromise;
     RefPtr<ReadableStreamToSharedBufferSink> m_sink;
     RefPtr<FetchBodySource> m_source;
+    bool m_isLoading { false };
 };
 
 } // namespace WebCore
index bc86ad2..8beb5c5 100644 (file)
@@ -93,7 +93,7 @@ bool FetchBodyOwner::isDisturbedOrLocked() const
 
 void FetchBodyOwner::arrayBuffer(Ref<DeferredPromise>&& promise)
 {
-    if (isBodyNull()) {
+    if (isBodyNullOrOpaque()) {
         fulfillPromiseWithArrayBuffer(WTFMove(promise), nullptr, 0);
         return;
     }
@@ -107,7 +107,7 @@ void FetchBodyOwner::arrayBuffer(Ref<DeferredPromise>&& promise)
 
 void FetchBodyOwner::blob(Ref<DeferredPromise>&& promise)
 {
-    if (isBodyNull()) {
+    if (isBodyNullOrOpaque()) {
         promise->resolve<IDLInterface<Blob>>(Blob::create({ }, Blob::normalizedContentType(extractMIMETypeFromMediaType(m_contentType))));
         return;
     }
@@ -153,30 +153,9 @@ void FetchBodyOwner::consumeOnceLoadingFinished(FetchBodyConsumer::Type type, Re
     m_body->consumeOnceLoadingFinished(type, WTFMove(promise), m_contentType);
 }
 
-void FetchBodyOwner::consumeNullBody(FetchBodyConsumer::Type consumerType, Ref<DeferredPromise>&& promise)
-{
-    switch (consumerType) {
-    case FetchBodyConsumer::Type::ArrayBuffer:
-        fulfillPromiseWithArrayBuffer(WTFMove(promise), nullptr, 0);
-        return;
-    case FetchBodyConsumer::Type::Blob:
-        promise->resolve<IDLInterface<Blob>>(Blob::create({ }, String()));
-        return;
-    case FetchBodyConsumer::Type::JSON:
-        promise->reject(SyntaxError);
-        return;
-    case FetchBodyConsumer::Type::Text:
-        promise->resolve<IDLDOMString>({ });
-        return;
-    case FetchBodyConsumer::Type::None:
-        ASSERT_NOT_REACHED();
-        return;
-    }
-}
-
 void FetchBodyOwner::formData(Ref<DeferredPromise>&& promise)
 {
-    if (isBodyNull()) {
+    if (isBodyNullOrOpaque()) {
         promise->reject();
         return;
     }
@@ -190,7 +169,7 @@ void FetchBodyOwner::formData(Ref<DeferredPromise>&& promise)
 
 void FetchBodyOwner::json(Ref<DeferredPromise>&& promise)
 {
-    if (isBodyNull()) {
+    if (isBodyNullOrOpaque()) {
         promise->reject(SyntaxError);
         return;
     }
@@ -204,7 +183,7 @@ void FetchBodyOwner::json(Ref<DeferredPromise>&& promise)
 
 void FetchBodyOwner::text(Ref<DeferredPromise>&& promise)
 {
-    if (isBodyNull()) {
+    if (isBodyNullOrOpaque()) {
         promise->resolve<IDLDOMString>({ });
         return;
     }
@@ -313,8 +292,13 @@ RefPtr<ReadableStream> FetchBodyOwner::readableStream(JSC::ExecState& state)
 
     if (!m_body->hasReadableStream()) {
         ASSERT(!m_readableStreamSource);
-        m_readableStreamSource = adoptRef(*new FetchBodySource(*this));
-        m_body->setReadableStream(ReadableStream::create(state, m_readableStreamSource));
+        if (isDisturbed()) {
+            m_body->setReadableStream(ReadableStream::create(state, nullptr));
+            m_body->readableStream()->lock();
+        } else {
+            m_readableStreamSource = adoptRef(*new FetchBodySource(*this));
+            m_body->setReadableStream(ReadableStream::create(state, m_readableStreamSource));
+        }
     }
     return m_body->readableStream();
 }
index 3d53564..e4b4afb 100644 (file)
@@ -68,12 +68,12 @@ protected:
     const FetchBody& body() const { return *m_body; }
     FetchBody& body() { return *m_body; }
     bool isBodyNull() const { return !m_body; }
+    bool isBodyNullOrOpaque() const { return !m_body || m_isBodyOpaque; }
     void cloneBody(FetchBodyOwner&);
 
     void extractBody(ScriptExecutionContext&, FetchBody::Init&&);
     void updateContentType();
     void consumeOnceLoadingFinished(FetchBodyConsumer::Type, Ref<DeferredPromise>&&);
-    void consumeNullBody(FetchBodyConsumer::Type, Ref<DeferredPromise>&&);
 
     void setBody(FetchBody&& body) { m_body = WTFMove(body); }
 
@@ -82,6 +82,9 @@ protected:
 
     void setDisturbed() { m_isDisturbed = true; }
 
+    void setBodyAsOpaque() { m_isBodyOpaque = true; }
+    bool isBodyOpaque() const { return m_isBodyOpaque; }
+
 private:
     // Blob loading routines
     void blobChunk(const char*, size_t);
@@ -113,6 +116,7 @@ protected:
 
 private:
     std::optional<BlobLoader> m_blobLoader;
+    bool m_isBodyOpaque { false };
 };
 
 } // namespace WebCore
index 19a5122..a3878f1 100644 (file)
@@ -53,27 +53,3 @@ function fillFetchHeaders(headers, headersInit)
         @Headers.prototype.@appendFromJS.@call(headers, name, headersInit[name]);
     }
 }
-
-function consumeStream(response, type)
-{
-    @assert(response instanceof @Response);
-    @assert(response.@body instanceof @ReadableStream);
-
-    if (@isReadableStreamDisturbed(response.@body))
-        return @Promise.@reject(new @TypeError("Cannot consume a disturbed Response body ReadableStream"));
-
-    try {
-        let reader = new @ReadableStreamDefaultReader(response.@body);
-
-        @Response.prototype.@startConsumingStream.@call(response, type);
-        let pull = (result) => {
-            if (result.done)
-                return @Response.prototype.@finishConsumingStream.@call(response);
-            @Response.prototype.@consumeChunk.@call(response, result.value);
-            return @Promise.prototype.@then.@call(@readableStreamDefaultReaderRead(reader), pull);
-        }
-        return @Promise.prototype.@then.@call(@readableStreamDefaultReaderRead(reader), pull);
-    } catch (e) {
-        return @Promise.@reject(e);
-    }
-}
index 4772051..0897f48 100644 (file)
@@ -85,14 +85,20 @@ FetchResponse::FetchResponse(ScriptExecutionContext& context, std::optional<Fetc
 {
 }
 
-Ref<FetchResponse> FetchResponse::cloneForJS()
+ExceptionOr<Ref<FetchResponse>> FetchResponse::clone(ScriptExecutionContext& context)
 {
+    if (isDisturbedOrLocked())
+        return Exception { TypeError };
+
     ASSERT(scriptExecutionContext());
-    ASSERT(!isDisturbedOrLocked());
 
-    auto clone = adoptRef(*new FetchResponse(*scriptExecutionContext(), std::nullopt, FetchHeaders::create(headers()), ResourceResponse(m_response)));
+    // If loading, let's create a stream so that data is teed on both clones.
+    if (isLoading())
+        readableStream(*context.execState());
+
+    auto clone = adoptRef(*new FetchResponse(context, std::nullopt, FetchHeaders::create(headers()), ResourceResponse(m_response)));
     clone->cloneBody(*this);
-    return clone;
+    return WTFMove(clone);
 }
 
 void FetchResponse::fetch(ScriptExecutionContext& context, FetchRequest& request, NotificationCallback&& responseCallback)
@@ -102,7 +108,9 @@ void FetchResponse::fetch(ScriptExecutionContext& context, FetchRequest& request
             responseCallback(Exception { NotSupportedError, "ReadableStream uploading is not supported" });
         return;
     }
-    auto response = adoptRef(*new FetchResponse(context, FetchBody::loadingBody(), FetchHeaders::create(FetchHeaders::Guard::Immutable), { }));
+    auto response = adoptRef(*new FetchResponse(context, FetchBody { }, FetchHeaders::create(FetchHeaders::Guard::Immutable), { }));
+
+    response->body().consumer().setAsLoading();
 
     response->m_bodyLoader.emplace(response.get(), WTFMove(responseCallback));
     if (!response->m_bodyLoader->start(context, request))
@@ -173,7 +181,8 @@ FetchResponse::BodyLoader::~BodyLoader()
 void FetchResponse::BodyLoader::didReceiveResponse(const ResourceResponse& resourceResponse)
 {
     m_response.m_response = ResourceResponseBase::filter(resourceResponse);
-    m_response.m_shouldExposeBody = resourceResponse.tainting() != ResourceResponse::Tainting::Opaque;
+    if (resourceResponse.tainting() == ResourceResponse::Tainting::Opaque)
+        m_response.setBodyAsOpaque();
 
     m_response.m_headers->filterAndFill(m_response.m_response.httpHeaderFields(), FetchHeaders::Guard::Response);
     m_response.updateContentType();
@@ -222,40 +231,6 @@ void FetchResponse::BodyLoader::stop()
         m_loader->stop();
 }
 
-void FetchResponse::consume(unsigned type, Ref<DeferredPromise>&& wrapper)
-{
-    ASSERT(type <= static_cast<unsigned>(FetchBodyConsumer::Type::Text));
-    auto consumerType = static_cast<FetchBodyConsumer::Type>(type);
-
-    if (!m_shouldExposeBody) {
-        consumeNullBody(consumerType, WTFMove(wrapper));
-        return;
-    }
-
-    if (isLoading()) {
-        consumeOnceLoadingFinished(consumerType, WTFMove(wrapper));
-        return;
-    }
-
-    switch (consumerType) {
-    case FetchBodyConsumer::Type::ArrayBuffer:
-        arrayBuffer(WTFMove(wrapper));
-        return;
-    case FetchBodyConsumer::Type::Blob:
-        blob(WTFMove(wrapper));
-        return;
-    case FetchBodyConsumer::Type::JSON:
-        json(WTFMove(wrapper));
-        return;
-    case FetchBodyConsumer::Type::Text:
-        text(WTFMove(wrapper));
-        return;
-    case FetchBodyConsumer::Type::None:
-        ASSERT_NOT_REACHED();
-        return;
-    }
-}
-
 FetchResponse::ResponseData FetchResponse::consumeBody()
 {
     ASSERT(!isLoading());
@@ -294,38 +269,24 @@ void FetchResponse::setBodyData(ResponseData&& data)
 {
     WTF::switchOn(data, [this](Ref<FormData>& formData) {
         if (isBodyNull())
-            setBody(FetchBody::loadingBody());
+            setBody({ });
         body().setAsFormData(WTFMove(formData));
     }, [this](Ref<SharedBuffer>& buffer) {
         if (isBodyNull())
-            setBody(FetchBody::loadingBody());
+            setBody({ });
         body().consumer().setData(WTFMove(buffer));
     }, [](std::nullptr_t&) { });
 }
 
 #if ENABLE(STREAMS_API)
-void FetchResponse::startConsumingStream(unsigned type)
-{
-    m_isDisturbed = true;
-    auto consumerType = static_cast<FetchBodyConsumer::Type>(type);
-    m_consumer.setType(consumerType);
-    if (consumerType == FetchBodyConsumer::Type::Blob)
-        m_consumer.setContentType(Blob::normalizedContentType(extractMIMETypeFromMediaType(m_contentType)));
-}
-
 void FetchResponse::consumeChunk(Ref<JSC::Uint8Array>&& chunk)
 {
-    m_consumer.append(chunk->data(), chunk->byteLength());
-}
-
-void FetchResponse::finishConsumingStream(Ref<DeferredPromise>&& promise)
-{
-    m_consumer.resolve(WTFMove(promise), nullptr);
+    body().consumer().append(chunk->data(), chunk->byteLength());
 }
 
 void FetchResponse::consumeBodyAsStream()
 {
-    ASSERT(m_shouldExposeBody);
+    ASSERT(!isBodyOpaque());
     ASSERT(m_readableStreamSource);
     if (!isLoading()) {
         FetchBodyOwner::consumeBodyAsStream();
@@ -334,10 +295,6 @@ void FetchResponse::consumeBodyAsStream()
 
     ASSERT(m_bodyLoader);
 
-    if (isBodyNull())
-        setBody(FetchBody::loadingBody());
-    updateContentType();
-
     auto data = m_bodyLoader->startStreaming();
     if (data) {
         if (!m_readableStreamSource->enqueue(data->tryCreateArrayBuffer())) {
@@ -375,14 +332,6 @@ void FetchResponse::feedStream()
     closeStream();
 }
 
-RefPtr<ReadableStream> FetchResponse::createReadableStream(JSC::ExecState& state)
-{
-    if (!m_shouldExposeBody)
-        return nullptr;
-
-    return FetchBodyOwner::readableStream(state);
-}
-
 RefPtr<SharedBuffer> FetchResponse::BodyLoader::startStreaming()
 {
     ASSERT(m_loader);
index 985ee81..ca0a737 100644 (file)
@@ -62,7 +62,6 @@ public:
     using NotificationCallback = WTF::Function<void(ExceptionOr<FetchResponse&>&&)>;
     static void fetch(ScriptExecutionContext&, FetchRequest&, NotificationCallback&&);
 
-    void consume(unsigned, Ref<DeferredPromise>&&);
 #if ENABLE(STREAMS_API)
     void startConsumingStream(unsigned);
     void consumeChunk(Ref<JSC::Uint8Array>&&);
@@ -81,10 +80,9 @@ public:
 
     const FetchHeaders& headers() const { return m_headers; }
     FetchHeaders& headers() { return m_headers; }
-    Ref<FetchResponse> cloneForJS();
+    ExceptionOr<Ref<FetchResponse>> clone(ScriptExecutionContext&);
 
 #if ENABLE(STREAMS_API)
-    RefPtr<ReadableStream> createReadableStream(JSC::ExecState&);
     void consumeBodyAsStream() final;
     void feedStream() final;
     void cancel() final;
@@ -143,9 +141,6 @@ private:
     ResourceResponse m_response;
     std::optional<BodyLoader> m_bodyLoader;
     mutable String m_responseURL;
-    bool m_shouldExposeBody { true };
-
-    FetchBodyConsumer m_consumer { FetchBodyConsumer::Type::ArrayBuffer  };
 };
 
 } // namespace WebCore
index 0c786c8..b551586 100644 (file)
@@ -45,8 +45,6 @@ dictionary FetchResponseInit {
     InterfaceName=Response,
     JSBuiltinConstructor,
     // Constructor(optional BodyInit? body = null, optional FetchResponseInit init)
-    PrivateIdentifier,
-    PublicIdentifier,
 ] interface FetchResponse {
     [CallWith=ScriptExecutionContext, NewObject] static FetchResponse error();
     [CallWith=ScriptExecutionContext, MayThrowException, NewObject] static FetchResponse redirect(USVString url, optional unsigned short status = 302);
@@ -62,27 +60,10 @@ dictionary FetchResponseInit {
     // FIXME: Add support for trailers.
     // readonly attribute Promise<Headers> trailer;
 
-    [JSBuiltin] FetchResponse clone();
+    [CallWith=ScriptExecutionContext, MayThrowException, NewObject] FetchResponse clone();
 
-
-    // FIXME: Copy of FetchBody IDL as we want to implement some of these as built-ins.
-    // This should be a FetchResponse implements FetchBody;
-    [JSBuiltin] readonly attribute ReadableStream? body;
-    [JSBuiltin] readonly attribute boolean bodyUsed;
-    [JSBuiltin] Promise<ArrayBuffer> arrayBuffer();
-    [JSBuiltin] Promise<Blob> blob();
-    [JSBuiltin] Promise<Blob> formData();
-    [JSBuiltin] Promise<any> json();
-    [JSBuiltin] Promise<USVString> text();
-
-    [NewObject, PrivateIdentifier] FetchResponse cloneForJS();
-    [Conditional=STREAMS_API, PrivateIdentifier] void startConsumingStream(unsigned short type);
-    [Conditional=STREAMS_API, PrivateIdentifier] void consumeChunk(Uint8Array chunk);
-    [Conditional=STREAMS_API, PrivateIdentifier] Promise<any> finishConsumingStream();
-    [PrivateIdentifier] Promise<any> consume(unsigned short type);
-    [PrivateIdentifier] boolean isLoading();
     [MayThrowException, PrivateIdentifier] void setStatus(unsigned short status, DOMString statusText);
     [PrivateIdentifier] void initializeWith(BodyInit body);
-    [CallWith=ScriptState, NewObject, PrivateIdentifier] ReadableStream? createReadableStream();
-    [PrivateIdentifier] boolean isDisturbed();
 };
+
+FetchResponse implements FetchBody;
index 74130f4..11b3097 100644 (file)
@@ -47,118 +47,8 @@ function initializeFetchResponse(body, init)
         if (status === 101 || status === 204 || status === 205 || status === 304)
             @throwTypeError("Response cannot have a body with the given status");
 
-        // FIXME: Use @isReadableStream once it is no longer guarded by STREAMS_API compilation guard.
-        let isBodyReadableStream = (@isObject(body) && !!body.@readableStreamController);
-        if (isBodyReadableStream)
-            this.@body = body;
         this.@initializeWith(body);
     }
 
     return this;
 }
-
-@getter
-function bodyUsed()
-{
-   if (!(this instanceof @Response))
-        throw @makeGetterTypeError("Response", "bodyUsed");
-
-    if (this.@body)
-        return @isReadableStreamDisturbed(this.@body);
-
-    return @Response.prototype.@isDisturbed.@call(this);
-}
-
-@getter
-function body()
-{
-    if (!(this instanceof @Response))
-        throw @makeGetterTypeError("Response", "body");
-
-    if (this.@body === @undefined) {
-        if (@Response.prototype.@isDisturbed.@call(this)) {
-            this.@body = new @ReadableStream();
-            // Get reader to lock it.
-            new @ReadableStreamDefaultReader(this.@body);
-        } else
-            this.@body = @Response.prototype.@createReadableStream.@call(this);
-    }
-    return this.@body;
-}
-
-function clone()
-{
-    if (!(this instanceof @Response))
-        throw @makeThisTypeError("Response", "clone");
-
-    if (@Response.prototype.@isDisturbed.@call(this) || (this.@body && @isReadableStreamLocked(this.@body)))
-        @throwTypeError("Cannot clone a disturbed Response");
-
-    // Let's create @body if response body is loading to provide data to both clones.
-    if (@Response.prototype.@isLoading.@call(this) && this.@body === @undefined)
-        this.@body = @Response.prototype.@createReadableStream.@call(this);
-
-    var cloned = @Response.prototype.@cloneForJS.@call(this);
-
-    // Let's refresh @body with the cloned stream.
-    this.@body = @Response.prototype.@createReadableStream.@call(this);
-
-    return cloned;
-}
-
-// consume and consumeStream single parameter should be kept in sync with FetchBodyConsumer::Type.
-function arrayBuffer()
-{
-    if (!(this instanceof @Response))
-        return @Promise.@reject(@makeThisTypeError("Response", "arrayBuffer"));
-
-    const arrayBufferConsumerType = 1;
-    if (!this.@body)
-        return @Response.prototype.@consume.@call(this, arrayBufferConsumerType);
-
-    return @consumeStream(this, arrayBufferConsumerType);
-}
-
-function blob()
-{
-    if (!(this instanceof @Response))
-        return @Promise.@reject(@makeThisTypeError("Response", "blob"));
-
-    const blobConsumerType = 2;
-    if (!this.@body)
-        return @Response.prototype.@consume.@call(this, blobConsumerType);
-
-    return @consumeStream(this, blobConsumerType);
-}
-
-function formData()
-{
-    if (!(this instanceof @Response))
-        return @Promise.@reject(@makeThisTypeError("Response", "formData"));
-
-    return @Promise.@reject("Not implemented");
-}
-
-function json()
-{
-    if (!(this instanceof @Response))
-        return @Promise.@reject(@makeThisTypeError("Response", "json"));
-
-    const jsonConsumerType = 3;
-    if (!this.@body)
-        return @Response.prototype.@consume.@call(this, jsonConsumerType);
-
-    return @consumeStream(this, jsonConsumerType);
-}
-
-function text()
-{
-    if (!(this instanceof @Response))
-        return @Promise.@reject(@makeThisTypeError("Response", "text"));
-
-    const textConsumerType = 4;
-    if (!this.@body)
-        return @Response.prototype.@consume.@call(this, textConsumerType);
-
-    return @consumeStream(this, textConsumerType);
-}
index d16105e..6571f85 100644 (file)
@@ -38,6 +38,8 @@ namespace WebCore {
 Ref<ReadableStream> ReadableStream::create(JSC::ExecState& execState, RefPtr<ReadableStreamSource>&& source)
 {
     VM& vm = execState.vm();
+    auto scope = DECLARE_CATCH_SCOPE(vm);
+
     auto& clientData = *static_cast<JSVMClientData*>(vm.clientData);
     auto& globalObject = *JSC::jsCast<JSDOMGlobalObject*>(execState.lexicalGlobalObject());
 
@@ -51,6 +53,7 @@ Ref<ReadableStream> ReadableStream::create(JSC::ExecState& execState, RefPtr<Rea
     args.append(source ? toJSNewlyCreated(&execState, &globalObject, source.releaseNonNull()) : JSC::jsUndefined());
 
     auto newReadableStream = jsDynamicDowncast<JSReadableStream*>(vm, JSC::construct(&execState, constructor, constructType, constructData, args));
+    scope.assertNoException();
 
     return create(globalObject, *newReadableStream);
 }
@@ -101,6 +104,27 @@ std::pair<Ref<ReadableStream>, Ref<ReadableStream>> ReadableStream::tee()
     return std::make_pair(results[0].releaseNonNull(), results[1].releaseNonNull());
 }
 
+void ReadableStream::lock()
+{
+    auto& state = *m_globalObject->globalExec();
+    VM& vm = state.vm();
+    auto scope = DECLARE_CATCH_SCOPE(vm);
+
+    auto& clientData = *static_cast<JSVMClientData*>(vm.clientData);
+
+    auto* constructor = JSC::asObject(m_globalObject->get(&state, clientData.builtinNames().ReadableStreamDefaultReaderPrivateName()));
+
+    ConstructData constructData;
+    ConstructType constructType = constructor->methodTable(vm)->getConstructData(constructor, constructData);
+    ASSERT(constructType != ConstructType::None);
+
+    MarkedArgumentBuffer args;
+    args.append(readableStream());
+
+    JSC::construct(&state, constructor, constructType, constructData, args);
+    scope.assertNoException();
+}
+
 static inline bool checkReadableStream(JSDOMGlobalObject& globalObject, JSReadableStream* readableStream, JSC::JSValue function)
 {
     auto& state = *globalObject.globalExec();
index 78425cf..0b70d70 100644 (file)
@@ -45,6 +45,7 @@ public:
 
     std::pair<Ref<ReadableStream>, Ref<ReadableStream>> tee();
 
+    void lock();
     void pipeTo(ReadableStreamSink&);
     bool isLocked() const;
     bool isDisturbed() const;
index 5a6424b..374ab7a 100644 (file)
@@ -167,7 +167,6 @@ namespace WebCore {
     macro(caches) \
     macro(cancel) \
     macro(cloneArrayBuffer) \
-    macro(cloneForJS) \
     macro(close) \
     macro(closeRequested) \
     macro(closed) \