Changeset 203767 in webkit


Ignore:
Timestamp:
Jul 26, 2016 11:38:15 PM (8 years ago)
Author:
commit-queue@webkit.org
Message:

[Fetch API] Response constructor should be able to take a ReadableStream as body
https://bugs.webkit.org/show_bug.cgi?id=159804

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

LayoutTests/imported/w3c:

  • web-platform-tests/fetch/api/response/response-consume-empty-expected.txt:
  • web-platform-tests/fetch/api/response/response-consume-expected.txt:
  • web-platform-tests/fetch/api/response/response-consume.html: Updating test to exercice Response coonstructor with a ReadableStream.

Source/WebCore:

Covered by existing and updated tests.

Introduced FetchBodyConsumer to encapsulate the code responsible to adapt FetchBody internal data to the requests made by user scripts.
This refactoring eases the handling of internal data coming from ReadableStream.

FetchLoader is now delegating conversion from the data to its m_consumer field.
If m_consumer is null, FetchLoader calls FetchLoaderClient::didReceiveData (streaming reception mode).
Clients of FetchLoader needs to pass a FetchBodyConsumer to the FetchLoader to do the data adaptation at loader creation time.

Added support for body data passed as a ReadableStream to Response.
This requires to set @body internal slot of the Response object in the constructor initialization JS built-in.

To actually use that data, Body accessors are also implemented as JS built-in for Response.
Since there is no need to do so for Request, FetchResponse is no longer marked as implementing FetchBody but all
FetchBody IDL description is inlined in FetchResponse.idl.

For each body accessor (arrayBuffer, blob, json, text), the corresponding JS built-in checks whether @body internal slot is set.
If that is not the case, regular handling is done through a new private method called @consume.
If @body internal slot is set, chunks are pumped from the ReadableStream using ReadableStream internal built-ins functions.
Data handling is done in C++ through the private methods @startConsumingStream, @consumeChunk and @finishConsumingStream.

To support cloning of Response with bodies, clone method is also implemented as a JS built-in.
Main clone is done through @cloneFoJS private method implemented in C++.
JS built-in clone code does the teeing of the ReadableStream using ReadableStream JS built-in internal functions.

Introducing a new ReadableStream type for FetchBody to cope with body being stored in a ReadableStream.

Introducing a new Loaded type for FetchBody to cope with body being stored in the FetchBodyConsumer owned by FetchBody.
This allows removing the conversion of data to JSC::ArrayBuffer which was done by defaut at the end of Fetch loading if user script did not request data before.
At the end of a load, the data remains in FetchBodyConsumer and the body is marked as Loaded.
If the user wants to get the data after data finished being loaded, FetchBodyConsumer will do the required conversions.

Introducing DeferredWrapper::resolveWithNewValue to handle the case of resolving promises with new objects.
This allows directly using toJSNewlyCreated instead of toJS.

  • CMakeLists.txt: Adding FetchBodyConsumer.cpp. Removing WebCoreJSBuiltins.cpp from CMake list as it is not needed.
  • Modules/fetch/FetchBody.cpp: Moving data adaptation code to FetchBodyConsumer.

(WebCore::FetchBody::extract):
(WebCore::FetchBody::arrayBuffer):
(WebCore::FetchBody::blob): Setting contentType in FetchBodyConsumer to handle proper creation of blob.
(WebCore::FetchBody::json):
(WebCore::FetchBody::text):
(WebCore::FetchBody::consume): Refactoring and added case of Loaded bodies.
(WebCore::FetchBody::consumeAsStream): Ditto.
(WebCore::FetchBody::consumeArrayBuffer):
(WebCore::FetchBody::consumeText):
(WebCore::FetchBody::consumeBlob):
(WebCore::FetchBody::loadingFailed):
(WebCore::FetchBody::loadingSucceeded):
(WebCore::FetchBody::loadingType): Deleted.
(WebCore::blobFromArrayBuffer): Deleted.
(WebCore::FetchBody::fulfillTextPromise): Deleted.
(WebCore::FetchBody::loadedAsArrayBuffer): Deleted.
(WebCore::FetchBody::loadedAsText): Deleted.

  • Modules/fetch/FetchBody.h:

(WebCore::FetchBody::consumer):

  • Modules/fetch/FetchBodyConsumer.cpp: Added, responsible of data adaptation.

(WebCore::blobFromData):
(WebCore::FetchBodyConsumer::resolveWithData):
(WebCore::FetchBodyConsumer::resolve):
(WebCore::FetchBodyConsumer::append):
(WebCore::FetchBodyConsumer::takeData):
(WebCore::FetchBodyConsumer::takeAsArrayBuffer):
(WebCore::FetchBodyConsumer::takeAsBlob):
(WebCore::FetchBodyConsumer::takeAsText):

  • Modules/fetch/FetchBodyConsumer.h: Added.

(WebCore::FetchBodyConsumer::FetchBodyConsumer):
(WebCore::FetchBodyConsumer::setContentType):
(WebCore::FetchBodyConsumer::setType):
(WebCore::FetchBodyConsumer::type):
(WebCore::FetchBodyConsumer::clean):
(WebCore::FetchBodyConsumer::hasData):

  • Modules/fetch/FetchBodyOwner.cpp:

(WebCore::FetchBodyOwner::loadBlob):
(WebCore::FetchBodyOwner::blobLoadingSucceeded):
(WebCore::FetchBodyOwner::loadedBlobAsText): Deleted.

  • Modules/fetch/FetchBodyOwner.h:

(WebCore::FetchBodyOwner::loadedBlobAsArrayBuffer): Deleted.

  • Modules/fetch/FetchInternals.js:

(consumeStream): Pump ReadableStream data and send it to FetchResponse to be converted according user need.

  • Modules/fetch/FetchLoader.cpp:

(WebCore::FetchLoader::FetchLoader): FetchLoader is simplified as it has two nodes: consumer mode in which case
data is sent to the consumer and no-consumer mode in which case data is passed to loader client. The second mode
is used to conveyy data to ReadableStream source.
(WebCore::FetchLoader::stop):
(WebCore::FetchLoader::startStreaming):
(WebCore::FetchLoader::didReceiveData):
(WebCore::FetchLoader::didFinishLoading): Deleted.

  • Modules/fetch/FetchLoader.h:
  • Modules/fetch/FetchLoaderClient.h:

(WebCore::FetchLoaderClient::didFinishLoadingAsText): Deleted.
(WebCore::FetchLoaderClient::didFinishLoadingAsArrayBuffer): Deleted.

  • Modules/fetch/FetchResponse.cpp:

(WebCore::FetchResponse::cloneForJS):
(WebCore::FetchResponse::BodyLoader::didSucceed):
(WebCore::FetchResponse::BodyLoader::start):
(WebCore::FetchResponse::consume): Introduced to consume data stored in body and not in ReadableStream.
(WebCore::FetchResponse::startConsumingStream): Introduced to start process of consuming data stored in the ReadableStream.
(WebCore::FetchResponse::consumeChunk): Reception of ReadableStream data.
(WebCore::FetchResponse::finishConsumingStream): Doing the final conversion.
(WebCore::FetchResponse::clone): Deleted.
(WebCore::FetchResponse::BodyLoader::didFinishLoadingAsArrayBuffer): Deleted.

  • Modules/fetch/FetchResponse.h:
  • Modules/fetch/FetchResponse.idl: Inlining of FetchBody methods/attributes and adding private methods.
  • Modules/fetch/FetchResponse.js:

(initializeFetchResponse):
(clone):
(arrayBuffer):
(blob):
(formData):
(json):
(text):

  • WebCore.xcodeproj/project.pbxproj:
  • bindings/js/JSDOMPromise.h:

(WebCore::DeferredWrapper::resolveWithNewValue):

  • bindings/js/WebCoreBuiltinNames.h:
Location:
trunk
Files:
1 added
21 edited
1 copied

Legend:

Unmodified
Added
Removed
  • trunk/LayoutTests/imported/w3c/ChangeLog

    r203761 r203767  
     12016-07-26  Youenn Fablet  <youenn@apple.com>
     2
     3        [Fetch API] Response constructor should be able to take a ReadableStream as body
     4        https://bugs.webkit.org/show_bug.cgi?id=159804
     5
     6        Reviewed by Alex Christensen.
     7
     8        * web-platform-tests/fetch/api/response/response-consume-empty-expected.txt:
     9        * web-platform-tests/fetch/api/response/response-consume-expected.txt:
     10        * web-platform-tests/fetch/api/response/response-consume.html: Updating test to exercice Response coonstructor with a ReadableStream.
     11
    1122016-07-26  Chris Dumez  <cdumez@apple.com>
    213
  • trunk/LayoutTests/imported/w3c/web-platform-tests/fetch/api/response/response-consume-empty-expected.txt

    r203726 r203767  
    44PASS Consume response's body as arrayBuffer
    55PASS Consume response's body as json
    6 FAIL Consume response's body as formData promise_test: Unhandled rejection with value: undefined
     6FAIL Consume response's body as formData promise_test: Unhandled rejection with value: "Not implemented"
    77PASS Consume empty blob response body as arrayBuffer
    88PASS Consume empty text response body as arrayBuffer
  • trunk/LayoutTests/imported/w3c/web-platform-tests/fetch/api/response/response-consume-expected.txt

    r203726 r203767  
    44PASS Consume response's body as arrayBuffer
    55PASS Consume response's body as json
    6 FAIL Consume response's body as formData promise_test: Unhandled rejection with value: undefined
     6FAIL Consume response's body as formData promise_test: Unhandled rejection with value: "Not implemented"
    77PASS Consume blob response's body as blob
    88PASS Consume blob response's body as text
    99PASS Consume blob response's body as json
    1010PASS Consume blob response's body as arrayBuffer
     11PASS Consume stream response's body as blob
     12PASS Consume stream response's body as text
     13PASS Consume stream response's body as json
     14PASS Consume stream response's body as arrayBuffer
    1115
  • trunk/LayoutTests/imported/w3c/web-platform-tests/fetch/api/response/response-consume.html

    r203726 r203767  
    9393    checkBlobResponseBody(blob, textData, "arrayBuffer", checkBodyArrayBuffer);
    9494
     95    function checkReadableStreamResponseBody(streamData, bodyType, checkFunction) {
     96      promise_test(function(test) {
     97        var stream = new ReadableStream({
     98          start: function(controller) {
     99            controller.enqueue((stringToArray(streamData)));
     100            controller.close();
     101          }
     102        });
     103        var response = new Response(stream);
     104        assert_false(response.bodyUsed, "bodyUsed is false at init");
     105        return checkFunction(response, streamData);
     106      }, "Consume stream response's body as " + bodyType);
     107    }
     108
     109    checkReadableStreamResponseBody(textData, "blob", checkBodyBlob);
     110    checkReadableStreamResponseBody(textData, "text", checkBodyText);
     111    checkReadableStreamResponseBody(textData, "json", checkBodyJSON);
     112    checkReadableStreamResponseBody(textData, "arrayBuffer", checkBodyArrayBuffer);
     113
    95114    </script>
    96115  </body>
  • trunk/Source/WebCore/CMakeLists.txt

    r203726 r203767  
    819819    Modules/fetch/DOMWindowFetch.cpp
    820820    Modules/fetch/FetchBody.cpp
     821    Modules/fetch/FetchBodyConsumer.cpp
    821822    Modules/fetch/FetchBodyOwner.cpp
    822823    Modules/fetch/FetchHeaders.cpp
     
    37743775    COMMAND ${PYTHON_EXECUTABLE} ${JavaScriptCore_SCRIPTS_DIR}/generate-js-builtins.py --wrappers-only --framework WebCore --output-directory ${DERIVED_SOURCES_WEBCORE_DIR} ${WebCore_BUILTINS_SOURCES}
    37753776    VERBATIM)
    3776 list(APPEND WebCore_DERIVED_SOURCES ${DERIVED_SOURCES_WEBCORE_DIR}/WebCoreJSBuiltins.cpp)
    37773777list(APPEND WebCore_DERIVED_SOURCES ${DERIVED_SOURCES_WEBCORE_DIR}/WebCoreJSBuiltinInternals.cpp)
    37783778list(APPEND WebCore_DERIVED_SOURCES ${DERIVED_SOURCES_WEBCORE_DIR}/WebCoreJSBuiltins.h)
  • trunk/Source/WebCore/ChangeLog

    r203766 r203767  
     12016-07-26  Youenn Fablet  <youenn@apple.com>
     2
     3        [Fetch API] Response constructor should be able to take a ReadableStream as body
     4        https://bugs.webkit.org/show_bug.cgi?id=159804
     5
     6        Reviewed by Alex Christensen.
     7
     8        Covered by existing and updated tests.
     9
     10        Introduced FetchBodyConsumer to encapsulate the code responsible to adapt FetchBody internal data to the requests made by user scripts.
     11        This refactoring eases the handling of internal data coming from ReadableStream.
     12
     13        FetchLoader is now delegating conversion from the data to its m_consumer field.
     14        If m_consumer is null, FetchLoader calls FetchLoaderClient::didReceiveData (streaming reception mode).
     15        Clients of FetchLoader needs to pass a FetchBodyConsumer to the FetchLoader to do the data adaptation at loader creation time.
     16
     17        Added support for body data passed as a ReadableStream to Response.
     18        This requires to set @body internal slot of the Response object in the constructor initialization JS built-in.
     19
     20        To actually use that data, Body accessors are also implemented as JS built-in for Response.
     21        Since there is no need to do so for Request, FetchResponse is no longer marked as implementing FetchBody but all
     22        FetchBody IDL description is inlined in FetchResponse.idl.
     23
     24        For each body accessor (arrayBuffer, blob, json, text), the corresponding JS built-in checks whether @body internal slot is set.
     25        If that is not the case, regular handling is done through a new private method called @consume.
     26        If @body internal slot is set, chunks are pumped from the ReadableStream using ReadableStream internal built-ins functions.
     27        Data handling is done in C++ through the private methods @startConsumingStream, @consumeChunk and @finishConsumingStream.
     28
     29        To support cloning of Response with bodies, clone method is also implemented as a JS built-in.
     30        Main clone is done through @cloneFoJS private method implemented in C++.
     31        JS built-in clone code does the teeing of the ReadableStream using ReadableStream JS built-in internal functions.
     32
     33        Introducing a new ReadableStream type for FetchBody to cope with body being stored in a ReadableStream.
     34
     35        Introducing a new Loaded type for FetchBody to cope with body being stored in the FetchBodyConsumer owned by FetchBody.
     36        This allows removing the conversion of data to JSC::ArrayBuffer which was done by defaut at the end of Fetch loading if user script did not request data before.
     37        At the end of a load, the data remains in FetchBodyConsumer and the body is marked as Loaded.
     38        If the user wants to get the data after data finished being loaded, FetchBodyConsumer will do the required conversions.
     39
     40        Introducing DeferredWrapper::resolveWithNewValue to handle the case of resolving promises with new objects.
     41        This allows directly using toJSNewlyCreated instead of toJS.
     42
     43        * CMakeLists.txt: Adding FetchBodyConsumer.cpp. Removing WebCoreJSBuiltins.cpp from CMake list as it is not needed.
     44        * Modules/fetch/FetchBody.cpp: Moving data adaptation code to FetchBodyConsumer.
     45        (WebCore::FetchBody::extract):
     46        (WebCore::FetchBody::arrayBuffer):
     47        (WebCore::FetchBody::blob): Setting contentType in FetchBodyConsumer to handle proper creation of blob.
     48        (WebCore::FetchBody::json):
     49        (WebCore::FetchBody::text):
     50        (WebCore::FetchBody::consume): Refactoring and added case of Loaded bodies.
     51        (WebCore::FetchBody::consumeAsStream): Ditto.
     52        (WebCore::FetchBody::consumeArrayBuffer):
     53        (WebCore::FetchBody::consumeText):
     54        (WebCore::FetchBody::consumeBlob):
     55        (WebCore::FetchBody::loadingFailed):
     56        (WebCore::FetchBody::loadingSucceeded):
     57        (WebCore::FetchBody::loadingType): Deleted.
     58        (WebCore::blobFromArrayBuffer): Deleted.
     59        (WebCore::FetchBody::fulfillTextPromise): Deleted.
     60        (WebCore::FetchBody::loadedAsArrayBuffer): Deleted.
     61        (WebCore::FetchBody::loadedAsText): Deleted.
     62        * Modules/fetch/FetchBody.h:
     63        (WebCore::FetchBody::consumer):
     64        * Modules/fetch/FetchBodyConsumer.cpp: Added, responsible of data adaptation.
     65        (WebCore::blobFromData):
     66        (WebCore::FetchBodyConsumer::resolveWithData):
     67        (WebCore::FetchBodyConsumer::resolve):
     68        (WebCore::FetchBodyConsumer::append):
     69        (WebCore::FetchBodyConsumer::takeData):
     70        (WebCore::FetchBodyConsumer::takeAsArrayBuffer):
     71        (WebCore::FetchBodyConsumer::takeAsBlob):
     72        (WebCore::FetchBodyConsumer::takeAsText):
     73        * Modules/fetch/FetchBodyConsumer.h: Added.
     74        (WebCore::FetchBodyConsumer::FetchBodyConsumer):
     75        (WebCore::FetchBodyConsumer::setContentType):
     76        (WebCore::FetchBodyConsumer::setType):
     77        (WebCore::FetchBodyConsumer::type):
     78        (WebCore::FetchBodyConsumer::clean):
     79        (WebCore::FetchBodyConsumer::hasData):
     80        * Modules/fetch/FetchBodyOwner.cpp:
     81        (WebCore::FetchBodyOwner::loadBlob):
     82        (WebCore::FetchBodyOwner::blobLoadingSucceeded):
     83        (WebCore::FetchBodyOwner::loadedBlobAsText): Deleted.
     84        * Modules/fetch/FetchBodyOwner.h:
     85        (WebCore::FetchBodyOwner::loadedBlobAsArrayBuffer): Deleted.
     86        * Modules/fetch/FetchInternals.js:
     87        (consumeStream): Pump ReadableStream data and send it to FetchResponse to be converted according user need.
     88        * Modules/fetch/FetchLoader.cpp:
     89        (WebCore::FetchLoader::FetchLoader): FetchLoader is simplified as it has two nodes: consumer mode in which case
     90        data is sent to the consumer and no-consumer mode in which case data is passed to loader client. The second mode
     91        is used to conveyy data to ReadableStream source.
     92        (WebCore::FetchLoader::stop):
     93        (WebCore::FetchLoader::startStreaming):
     94        (WebCore::FetchLoader::didReceiveData):
     95        (WebCore::FetchLoader::didFinishLoading): Deleted.
     96        * Modules/fetch/FetchLoader.h:
     97        * Modules/fetch/FetchLoaderClient.h:
     98        (WebCore::FetchLoaderClient::didFinishLoadingAsText): Deleted.
     99        (WebCore::FetchLoaderClient::didFinishLoadingAsArrayBuffer): Deleted.
     100        * Modules/fetch/FetchResponse.cpp:
     101        (WebCore::FetchResponse::cloneForJS):
     102        (WebCore::FetchResponse::BodyLoader::didSucceed):
     103        (WebCore::FetchResponse::BodyLoader::start):
     104        (WebCore::FetchResponse::consume): Introduced to consume data stored in body and not in ReadableStream.
     105        (WebCore::FetchResponse::startConsumingStream): Introduced to start process of consuming data stored in the ReadableStream.
     106        (WebCore::FetchResponse::consumeChunk): Reception of ReadableStream data.
     107        (WebCore::FetchResponse::finishConsumingStream): Doing the final conversion.
     108        (WebCore::FetchResponse::clone): Deleted.
     109        (WebCore::FetchResponse::BodyLoader::didFinishLoadingAsArrayBuffer): Deleted.
     110        * Modules/fetch/FetchResponse.h:
     111        * Modules/fetch/FetchResponse.idl: Inlining of FetchBody methods/attributes and adding private methods.
     112        * Modules/fetch/FetchResponse.js:
     113        (initializeFetchResponse):
     114        (clone):
     115        (arrayBuffer):
     116        (blob):
     117        (formData):
     118        (json):
     119        (text):
     120        * WebCore.xcodeproj/project.pbxproj:
     121        * bindings/js/JSDOMPromise.h:
     122        (WebCore::DeferredWrapper::resolveWithNewValue):
     123        * bindings/js/WebCoreBuiltinNames.h:
     124
    11252016-07-26  Youenn Fablet  <youennf@gmail.com>
    2126
  • trunk/Source/WebCore/Modules/fetch/FetchBody.cpp

    r203726 r203767  
    4040#include "JSBlob.h"
    4141#include "JSDOMFormData.h"
     42#include "JSReadableStream.h"
    4243#include "ReadableStreamSource.h"
    4344
    4445namespace WebCore {
    45 
    46 static Ref<Blob> blobFromArrayBuffer(ArrayBuffer*, const String&);
    4746
    4847FetchBody::FetchBody(Ref<Blob>&& blob)
     
    7675    if (value.isString())
    7776        return FetchBody(value.toWTFString(&state));
     77    if (value.inherits(JSReadableStream::info()))
     78        return { Type::ReadableStream };
    7879    return { };
    7980}
     
    9091{
    9192    ASSERT(m_type != Type::None);
    92     consume(owner, Consumer::Type::ArrayBuffer, WTFMove(promise));
     93    m_consumer.setType(FetchBodyConsumer::Type::ArrayBuffer);
     94    consume(owner, WTFMove(promise));
    9395}
    9496
     
    9698{
    9799    ASSERT(m_type != Type::None);
    98     consume(owner, Consumer::Type::Blob, WTFMove(promise));
     100    m_consumer.setType(FetchBodyConsumer::Type::Blob);
     101    m_consumer.setContentType(Blob::normalizedContentType(extractMIMETypeFromMediaType(m_mimeType)));
     102    consume(owner, WTFMove(promise));
    99103}
    100104
     
    107111        return;
    108112    }
    109     consume(owner, Consumer::Type::JSON, WTFMove(promise));
     113    m_consumer.setType(FetchBodyConsumer::Type::JSON);
     114    consume(owner, WTFMove(promise));
    110115}
    111116
     
    118123        return;
    119124    }
    120     consume(owner, Consumer::Type::Text, WTFMove(promise));
    121 }
    122 
    123 void FetchBody::consume(FetchBodyOwner& owner, Consumer::Type type, DeferredWrapper&& promise)
    124 {
    125     if (m_type == Type::ArrayBuffer) {
    126         consumeArrayBuffer(type, promise);
    127         return;
    128     }
    129     if (m_type == Type::Text) {
    130         consumeText(type, promise);
    131         return;
    132     }
    133     if (m_type == Type::Blob) {
    134         consumeBlob(owner, type, WTFMove(promise));
    135         return;
    136     }
    137     if (m_type == Type::Loading) {
    138         // FIXME: We should be able to change the loading type to text if consumer type is JSON or Text.
    139         m_consumer = Consumer({type, WTFMove(promise)});
    140         return;
    141     }
    142 
    143     // FIXME: Support other types.
    144     promise.reject(0);
     125    m_consumer.setType(FetchBodyConsumer::Type::Text);
     126    consume(owner, WTFMove(promise));
     127}
     128
     129void FetchBody::consume(FetchBodyOwner& owner, DeferredWrapper&& promise)
     130{
     131    // This should be handled by FetchBodyOwner
     132    ASSERT(m_type != Type::None);
     133    // This should be handled by JS built-ins
     134    ASSERT(m_type != Type::ReadableStream);
     135
     136    switch (m_type) {
     137    case Type::ArrayBuffer:
     138        consumeArrayBuffer(promise);
     139        return;
     140    case Type::Text:
     141        consumeText(promise);
     142        return;
     143    case Type::Blob:
     144        consumeBlob(owner, WTFMove(promise));
     145        return;
     146    case Type::Loading:
     147        m_consumePromise = WTFMove(promise);
     148        return;
     149    case Type::Loaded:
     150        m_consumer.resolve(promise);
     151        return;
     152    default:
     153        // FIXME: Support other types.
     154        promise.reject(0);
     155    }
    145156}
    146157
     
    148159void FetchBody::consumeAsStream(FetchBodyOwner& owner, FetchResponseSource& source)
    149160{
     161    // This should be handled by FetchResponse
    150162    ASSERT(m_type != Type::Loading);
    151 
     163    // This should be handled by JS built-ins
     164    ASSERT(m_type != Type::ReadableStream);
     165
     166    bool closeStream = false;
    152167    switch (m_type) {
    153168    case Type::ArrayBuffer:
    154169        ASSERT(m_data);
    155         if (source.enqueue(RefPtr<JSC::ArrayBuffer>(m_data)))
    156             source.close();
    157         return;
     170        closeStream = source.enqueue(RefPtr<JSC::ArrayBuffer>(m_data));
     171        break;
    158172    case Type::Text: {
    159173        Vector<uint8_t> data = extractFromText();
    160         if (source.enqueue(ArrayBuffer::tryCreate(data.data(), data.size())))
    161             source.close();
    162         return;
     174        closeStream = source.enqueue(ArrayBuffer::tryCreate(data.data(), data.size()));
     175        break;
    163176    }
    164177    case Type::Blob:
    165178        ASSERT(m_blob);
    166         owner.loadBlob(*m_blob, FetchLoader::Type::Stream);
    167         return;
     179        owner.loadBlob(*m_blob, nullptr);
     180        break;
    168181    case Type::None:
    169         source.close();
    170         return;
     182        closeStream = true;
     183        break;
     184    case Type::Loaded: {
     185        closeStream = source.enqueue(m_consumer.takeAsArrayBuffer());
     186        break;
     187    }
    171188    default:
    172189        source.error(ASCIILiteral("not implemented"));
    173190    }
     191
     192    if (closeStream)
     193        source.close();
    174194}
    175195#endif
    176196
    177 void FetchBody::consumeArrayBuffer(Consumer::Type type, DeferredWrapper& promise)
    178 {
    179     if (type == Consumer::Type::ArrayBuffer) {
    180         fulfillPromiseWithArrayBuffer(promise, m_data.get());
    181         return;
    182     }
    183     if (type == Consumer::Type::Blob) {
    184         promise.resolve(blobFromArrayBuffer(m_data.get(), Blob::normalizedContentType(extractMIMETypeFromMediaType(m_mimeType))));
    185         return;
    186     }
    187 
    188     ASSERT(type == Consumer::Type::Text || type == Consumer::Type::JSON);
    189     // FIXME: Do we need TextResourceDecoder to create a String to decode UTF-8 data.
    190     fulfillTextPromise(type, TextResourceDecoder::create(ASCIILiteral("text/plain"), "UTF-8")->decodeAndFlush(static_cast<const char*>(m_data->data()), m_data->byteLength()), promise);
    191 }
    192 
    193 void FetchBody::consumeText(Consumer::Type type, DeferredWrapper& promise)
    194 {
    195     ASSERT(type == Consumer::Type::ArrayBuffer || type == Consumer::Type::Blob);
    196 
    197     if (type == Consumer::Type::ArrayBuffer) {
    198         Vector<uint8_t> data = extractFromText();
    199         fulfillPromiseWithArrayBuffer(promise, data.data(), data.size());
    200         return;
    201     }
    202     String contentType = Blob::normalizedContentType(extractMIMETypeFromMediaType(m_mimeType));
    203     promise.resolve(Blob::create(extractFromText(), contentType));
    204 }
    205 
    206 FetchLoader::Type FetchBody::loadingType(Consumer::Type type)
    207 {
    208     switch (type) {
    209     case Consumer::Type::JSON:
    210     case Consumer::Type::Text:
    211         return FetchLoader::Type::Text;
    212     case Consumer::Type::Blob:
    213     case Consumer::Type::ArrayBuffer:
    214         return FetchLoader::Type::ArrayBuffer;
    215     default:
    216         ASSERT_NOT_REACHED();
    217         return FetchLoader::Type::ArrayBuffer;
    218     };
    219 }
    220 
    221 void FetchBody::consumeBlob(FetchBodyOwner& owner, Consumer::Type type, DeferredWrapper&& promise)
     197void FetchBody::consumeArrayBuffer(DeferredWrapper& promise)
     198{
     199    m_consumer.resolveWithData(promise, static_cast<const uint8_t*>(m_data->data()), m_data->byteLength());
     200}
     201
     202void FetchBody::consumeText(DeferredWrapper& promise)
     203{
     204    Vector<uint8_t> data = extractFromText();
     205    m_consumer.resolveWithData(promise, data.data(), data.size());
     206}
     207
     208void FetchBody::consumeBlob(FetchBodyOwner& owner, DeferredWrapper&& promise)
    222209{
    223210    ASSERT(m_blob);
    224211
    225     m_consumer = Consumer({type, WTFMove(promise)});
    226     owner.loadBlob(*m_blob, loadingType(type));
     212    m_consumePromise = WTFMove(promise);
     213    owner.loadBlob(*m_blob, &m_consumer);
    227214}
    228215
     
    237224}
    238225
    239 static inline Ref<Blob> blobFromArrayBuffer(ArrayBuffer* buffer, const String& contentType)
    240 {
    241     if (!buffer)
    242         return Blob::create(Vector<uint8_t>(), contentType);
    243 
    244     // FIXME: We should try to move buffer to Blob without doing this copy.
    245     Vector<uint8_t> value(buffer->byteLength());
    246     memcpy(value.data(), buffer->data(), buffer->byteLength());
    247     return Blob::create(WTFMove(value), contentType);
    248 }
    249 
    250 void FetchBody::fulfillTextPromise(FetchBody::Consumer::Type type, const String& text, DeferredWrapper& promise)
    251 {
    252     ASSERT(type == Consumer::Type::Text || type == Consumer::Type::JSON);
    253     if (type == FetchBody::Consumer::Type::Text)
    254         promise.resolve(text);
    255     else
    256         fulfillPromiseWithJSON(promise, text);
    257 }
    258 
    259226void FetchBody::loadingFailed()
    260227{
    261     ASSERT(m_consumer);
    262     m_consumer->promise.reject(0);
    263     m_consumer = Nullopt;
    264 }
    265 
    266 void FetchBody::loadedAsArrayBuffer(RefPtr<ArrayBuffer>&& buffer)
    267 {
    268     if (m_type == Type::Loading) {
    269         m_type = Type::ArrayBuffer;
    270         m_data = buffer;
    271         if (m_consumer) {
    272             consumeArrayBuffer(m_consumer->type, m_consumer->promise);
    273             m_consumer = Nullopt;
    274         }
    275         return;
    276     }
    277 
    278     ASSERT(m_consumer);
    279     ASSERT(m_consumer->type == Consumer::Type::Blob || m_consumer->type == Consumer::Type::ArrayBuffer);
    280     if (m_consumer->type == Consumer::Type::ArrayBuffer)
    281         fulfillPromiseWithArrayBuffer(m_consumer->promise, buffer.get());
    282     else {
    283         ASSERT(m_blob);
    284         m_consumer->promise.resolve(blobFromArrayBuffer(buffer.get(), m_blob->type()));
    285     }
    286     m_consumer = Nullopt;
    287 }
    288 
    289 void FetchBody::loadedAsText(String&& text)
    290 {
    291     ASSERT(m_consumer);
    292     ASSERT(m_consumer->type == Consumer::Type::Text || m_consumer->type == Consumer::Type::JSON);
    293 
    294     fulfillTextPromise(m_consumer->type, text, m_consumer->promise);
    295     m_consumer = Nullopt;
     228    if (m_consumePromise) {
     229        m_consumePromise->reject(0);
     230        m_consumePromise = Nullopt;
     231    }
     232}
     233
     234void FetchBody::loadingSucceeded()
     235{
     236    m_type = m_consumer.hasData() ? Type::Loaded : Type::None;
     237    if (m_consumePromise) {
     238        m_consumer.resolve(*m_consumePromise);
     239        m_consumePromise = Nullopt;
     240    }
    296241}
    297242
  • trunk/Source/WebCore/Modules/fetch/FetchBody.h

    r203726 r203767  
    3434#include "Blob.h"
    3535#include "DOMFormData.h"
     36#include "FetchBodyConsumer.h"
    3637#include "FetchLoader.h"
    3738#include "JSDOMPromise.h"
     
    7172
    7273    void loadingFailed();
    73     void loadedAsArrayBuffer(RefPtr<ArrayBuffer>&&);
    74     void loadedAsText(String&&);
     74    void loadingSucceeded();
    7575
    7676    RefPtr<FormData> bodyForInternalRequest() const;
    7777
    78     enum class Type { None, ArrayBuffer, Loading, Text, Blob, FormData };
     78    enum class Type { None, ArrayBuffer, Blob, FormData, Text, Loading, Loaded, ReadableStream };
    7979    Type type() const { return m_type; }
     80
     81    FetchBodyConsumer& consumer() { return m_consumer; }
    8082
    8183private:
     
    8587    FetchBody(Type type) : m_type(type) { }
    8688
    87     struct Consumer {
    88         enum class Type { Text, Blob, JSON, ArrayBuffer };
    89 
    90         Type type;
    91         DeferredWrapper promise;
    92     };
    93     void consume(FetchBodyOwner&, Consumer::Type, DeferredWrapper&&);
     89    void consume(FetchBodyOwner&, DeferredWrapper&&);
    9490
    9591    Vector<uint8_t> extractFromText() const;
    96     void consumeArrayBuffer(Consumer::Type, DeferredWrapper&);
    97     void consumeText(Consumer::Type, DeferredWrapper&);
    98     void consumeBlob(FetchBodyOwner&, Consumer::Type, DeferredWrapper&&);
    99     static FetchLoader::Type loadingType(Consumer::Type);
    100     static void fulfillTextPromise(FetchBody::Consumer::Type, const String&, DeferredWrapper&);
    101     static void fulfillArrayBufferPromise(FetchBody::Consumer::Type, const String&, DeferredWrapper&);
     92    void consumeArrayBuffer(DeferredWrapper&);
     93    void consumeText(DeferredWrapper&);
     94    void consumeBlob(FetchBodyOwner&, DeferredWrapper&&);
    10295
    10396    Type m_type { Type::None };
     
    110103    String m_text;
    111104
    112     Optional<Consumer> m_consumer;
     105    FetchBodyConsumer m_consumer { FetchBodyConsumer::Type::None };
     106    Optional<DeferredWrapper> m_consumePromise;
    113107};
    114108
  • trunk/Source/WebCore/Modules/fetch/FetchBodyConsumer.h

    r203766 r203767  
    11/*
    2  * Copyright (C) 2016 Canon Inc.
     2 * Copyright (C) 2016 Apple Inc.
    33 *
    44 * Redistribution and use in source and binary forms, with or without
     
    1111 *     notice, this list of conditions and the following disclaimer in the
    1212 *     documentation and/or other materials provided with the distribution.
    13  * 3.  Neither the name of Canon Inc. nor the names of
     13 * 3.  Neither the name of Apple Inc. nor the names of
    1414 *     its contributors may be used to endorse or promote products derived
    1515 *     from this software without specific prior written permission.
    1616 *
    17  * THIS SOFTWARE IS PROVIDED BY CANON INC. AND ITS CONTRIBUTORS "AS IS" AND ANY
     17 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS "AS IS" AND ANY
    1818 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
    1919 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
    20  * DISCLAIMED. IN NO EVENT SHALL CANON INC. AND ITS CONTRIBUTORS BE LIABLE FOR
     20 * DISCLAIMED. IN NO EVENT SHALL APPLE INC. AND ITS CONTRIBUTORS BE LIABLE FOR
    2121 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
    2222 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
     
    2727 */
    2828
    29 #ifndef FetchLoaderClient_h
    30 #define FetchLoaderClient_h
     29#pragma once
    3130
    3231#if ENABLE(FETCH_API)
    3332
    34 #include <wtf/Forward.h>
    35 
    36 namespace JSC {
    37 class ArrayBuffer;
    38 }
     33#include "SharedBuffer.h"
    3934
    4035namespace WebCore {
    4136
    42 class ResourceResponse;
     37class Blob;
     38class DeferredWrapper;
    4339
    44 class FetchLoaderClient {
     40class FetchBodyConsumer {
    4541public:
    46     virtual ~FetchLoaderClient() { }
     42    // Type is used in FetchResponse.js and should be kept synchronized with it.
     43    enum class Type { None, ArrayBuffer, Blob, JSON, Text };
    4744
    48     virtual void didReceiveResponse(const ResourceResponse&) { }
     45    FetchBodyConsumer(Type type) : m_type(type) { }
    4946
    50     virtual void didFinishLoadingAsText(String&&) { }
    51     virtual void didFinishLoadingAsArrayBuffer(RefPtr<JSC::ArrayBuffer>&&) { }
    52     virtual void didReceiveData(const char*, size_t) { }
     47    void append(const char* data, unsigned);
     48    void append(const unsigned char* data, unsigned);
    5349
    54     virtual void didSucceed() = 0;
    55     virtual void didFail() = 0;
     50    RefPtr<SharedBuffer> takeData();
     51    RefPtr<JSC::ArrayBuffer> takeAsArrayBuffer();
     52    Ref<Blob> takeAsBlob();
     53    String takeAsText();
     54
     55    void setContentType(const String& contentType) { m_contentType = contentType; }
     56    void setType(Type type) { m_type = type; }
     57
     58    void clean() { m_buffer = nullptr; }
     59
     60    void resolve(DeferredWrapper&);
     61    void resolveWithData(DeferredWrapper&, const unsigned char*, unsigned);
     62
     63    bool hasData() const { return !!m_buffer; }
     64
     65private:
     66    Type m_type;
     67    String m_contentType;
     68    RefPtr<SharedBuffer> m_buffer;
    5669};
    5770
     
    5972
    6073#endif // ENABLE(FETCH_API)
    61 
    62 #endif // FetchLoaderClient_h
  • trunk/Source/WebCore/Modules/fetch/FetchBodyOwner.cpp

    r203726 r203767  
    139139}
    140140
    141 void FetchBodyOwner::loadBlob(Blob& blob, FetchLoader::Type type)
     141void FetchBodyOwner::loadBlob(Blob& blob, FetchBodyConsumer* consumer)
    142142{
    143143    // Can only be called once for a body instance.
     
    151151
    152152    m_blobLoader = { *this };
    153     m_blobLoader->loader = std::make_unique<FetchLoader>(type, *m_blobLoader);
     153    m_blobLoader->loader = std::make_unique<FetchLoader>(*m_blobLoader, consumer);
    154154
    155155    m_blobLoader->loader->start(*scriptExecutionContext(), blob);
     
    170170}
    171171
    172 void FetchBodyOwner::loadedBlobAsText(String&& text)
    173 {
    174     m_body.loadedAsText(WTFMove(text));
    175 }
    176 
    177172void FetchBodyOwner::blobLoadingSucceeded()
    178173{
     
    185180    }
    186181#endif
    187 
     182    m_body.loadingSucceeded();
    188183    finishBlobLoading();
    189184}
  • trunk/Source/WebCore/Modules/fetch/FetchBodyOwner.h

    r203726 r203767  
    5353    void text(DeferredWrapper&&);
    5454
    55     void loadBlob(Blob&, FetchLoader::Type);
     55    void loadBlob(Blob&, FetchBodyConsumer*);
    5656
    5757    bool isActive() const { return !!m_blobLoader; }
     
    6868private:
    6969    // Blob loading routines
    70     void loadedBlobAsText(String&&);
    71     void loadedBlobAsArrayBuffer(RefPtr<ArrayBuffer>&& buffer) { m_body.loadedAsArrayBuffer(WTFMove(buffer)); }
    7270    void blobChunk(const char*, size_t);
    7371    void blobLoadingSucceeded();
     
    7977
    8078        // FetchLoaderClient API
    81         void didFinishLoadingAsText(String&& text) final { owner.loadedBlobAsText(WTFMove(text)); }
    82         void didFinishLoadingAsArrayBuffer(RefPtr<ArrayBuffer>&& buffer) final { owner.loadedBlobAsArrayBuffer(WTFMove(buffer)); }
    8379        void didReceiveResponse(const ResourceResponse&) final;
    8480        void didReceiveData(const char* data, size_t size) final { owner.blobChunk(data, size); }
  • trunk/Source/WebCore/Modules/fetch/FetchInternals.js

    r203726 r203767  
    5252    }
    5353}
     54
     55function consumeStream(response, type)
     56{
     57    @assert(response instanceof @Response);
     58    @assert(response.@body instanceof @ReadableStream);
     59
     60    if (@isReadableStreamDisturbed(response.@body))
     61        return @Promise.@reject(new @TypeError("Cannot consume a disturbed Response body ReadableStream"));
     62
     63    try {
     64        let reader = new @ReadableStreamReader(response.@body);
     65
     66        @Response.prototype.@startConsumingStream.@call(response, type);
     67        let pull = (result) => {
     68            if (result.done)
     69                return @Response.prototype.@finishConsumingStream.@call(response);
     70            @Response.prototype.@consumeChunk.@call(response, result.value);
     71            return @Promise.prototype.@then.@call(@readFromReadableStreamReader(reader), pull);
     72        }
     73        return @Promise.prototype.@then.@call(@readFromReadableStreamReader(reader), pull);
     74    } catch(e) {
     75        return @Promise.@reject(e);
     76    }
     77}
  • trunk/Source/WebCore/Modules/fetch/FetchLoader.cpp

    r203732 r203767  
    9292}
    9393
    94 FetchLoader::FetchLoader(Type type, FetchLoaderClient& client)
    95     : m_type(type)
    96     , m_client(client)
     94FetchLoader::FetchLoader(FetchLoaderClient& client, FetchBodyConsumer* consumer)
     95    : m_client(client)
     96    , m_consumer(consumer)
    9797{
    9898}
     
    100100void FetchLoader::stop()
    101101{
    102     m_data = nullptr;
     102    if (m_consumer)
     103        m_consumer->clean();
    103104    if (m_loader)
    104105        m_loader->cancel();
     
    107108RefPtr<SharedBuffer> FetchLoader::startStreaming()
    108109{
    109     ASSERT(m_type == Type::ArrayBuffer);
    110     m_type = Type::Stream;
    111     return WTFMove(m_data);
     110    ASSERT(m_consumer);
     111    auto firstChunk = m_consumer->takeData();
     112    m_consumer = nullptr;
     113    return firstChunk;
    112114}
    113115
     
    117119}
    118120
    119 // FIXME: We should make text and blob creation more efficient.
    120 // We might also want to merge this class with FileReaderLoader.
    121121void FetchLoader::didReceiveData(const char* value, int size)
    122122{
    123     if (m_type == Type::Stream) {
     123    if (!m_consumer) {
    124124        m_client.didReceiveData(value, size);
    125125        return;
    126126    }
    127     if (!m_data) {
    128         m_data = SharedBuffer::create(value, size);
    129         return;
    130     }
    131     m_data->append(value, size);
     127    m_consumer->append(value, size);
    132128}
    133129
    134130void FetchLoader::didFinishLoading(unsigned long, double)
    135131{
    136     if (m_type == Type::ArrayBuffer)
    137         m_client.didFinishLoadingAsArrayBuffer(m_data ? m_data->createArrayBuffer() : ArrayBuffer::tryCreate(nullptr, 0));
    138     else if (m_type == Type::Text)
    139         m_client.didFinishLoadingAsText(m_data ? TextResourceDecoder::create(ASCIILiteral("text/plain"), "UTF-8")->decodeAndFlush(m_data->data(), m_data->size()): String());
    140     m_data = nullptr;
    141 
    142132    m_client.didSucceed();
    143133}
  • trunk/Source/WebCore/Modules/fetch/FetchLoader.h

    r203726 r203767  
    2727 */
    2828
    29 #ifndef FetchLoader_h
    30 #define FetchLoader_h
     29#pragma once
    3130
    3231#if ENABLE(FETCH_API)
    3332
    34 #include "SharedBuffer.h"
     33#include "FetchBodyConsumer.h"
    3534#include "ThreadableLoader.h"
    3635#include "ThreadableLoaderClient.h"
     
    4544class FetchLoader final : public ThreadableLoaderClient {
    4645public:
    47     enum class Type { ArrayBuffer, Stream, Text };
    48 
    49     FetchLoader(Type, FetchLoaderClient&);
     46    FetchLoader(FetchLoaderClient&, FetchBodyConsumer*);
    5047
    5148    RefPtr<SharedBuffer> startStreaming();
     
    6461    void didFail(const ResourceError&) final;
    6562
    66     Type type() const { return m_type; }
    67 
    6863private:
    69     Type m_type { Type::ArrayBuffer };
    7064    FetchLoaderClient& m_client;
    7165    RefPtr<ThreadableLoader> m_loader;
    72     RefPtr<SharedBuffer> m_data;
     66    FetchBodyConsumer* m_consumer;
    7367    bool m_isStarted { false };
    7468};
     
    7771
    7872#endif // ENABLE(FETCH_API)
    79 
    80 #endif // FetchLoader_h
  • trunk/Source/WebCore/Modules/fetch/FetchLoaderClient.h

    r203726 r203767  
    4848    virtual void didReceiveResponse(const ResourceResponse&) { }
    4949
    50     virtual void didFinishLoadingAsText(String&&) { }
    51     virtual void didFinishLoadingAsArrayBuffer(RefPtr<JSC::ArrayBuffer>&&) { }
    5250    virtual void didReceiveData(const char*, size_t) { }
    5351
  • trunk/Source/WebCore/Modules/fetch/FetchResponse.cpp

    r203726 r203767  
    3535#include "FetchRequest.h"
    3636#include "HTTPParsers.h"
     37#include "JSBlob.h"
    3738#include "JSFetchResponse.h"
    3839#include "ScriptExecutionContext.h"
     
    9495}
    9596
    96 RefPtr<FetchResponse> FetchResponse::clone(ScriptExecutionContext& context, ExceptionCode& ec)
    97 {
    98     if (isDisturbed()) {
    99         ec = TypeError;
    100         return nullptr;
    101     }
    102     return adoptRef(*new FetchResponse(context, FetchBody(m_body), FetchHeaders::create(headers()), ResourceResponse(m_response)));
     97Ref<FetchResponse> FetchResponse::cloneForJS()
     98{
     99    ASSERT(scriptExecutionContext());
     100    ASSERT(!isDisturbed());
     101    return adoptRef(*new FetchResponse(*scriptExecutionContext(), FetchBody(m_body), FetchHeaders::create(headers()), ResourceResponse(m_response)));
    103102}
    104103
     
    131130    }
    132131#endif
     132    m_response.m_body.loadingSucceeded();
     133
    133134    if (m_loader->isStarted())
    134135        m_response.m_bodyLoader = Nullopt;
     
    188189}
    189190
    190 void FetchResponse::BodyLoader::didFinishLoadingAsArrayBuffer(RefPtr<ArrayBuffer>&& buffer)
    191 {
    192     m_response.body().loadedAsArrayBuffer(WTFMove(buffer));
    193 }
    194 
    195191bool FetchResponse::BodyLoader::start(ScriptExecutionContext& context, const FetchRequest& request)
    196192{
    197     m_loader = std::make_unique<FetchLoader>(FetchLoader::Type::ArrayBuffer, *this);
     193    m_loader = std::make_unique<FetchLoader>(*this, &m_response.m_body.consumer());
    198194    m_loader->start(context, request);
    199195    return m_loader->isStarted();
     
    206202}
    207203
     204void FetchResponse::consume(unsigned type, DeferredWrapper&& wrapper)
     205{
     206    ASSERT(type <= static_cast<unsigned>(FetchBodyConsumer::Type::Text));
     207
     208    switch (static_cast<FetchBodyConsumer::Type>(type)) {
     209    case FetchBodyConsumer::Type::ArrayBuffer:
     210        arrayBuffer(WTFMove(wrapper));
     211        return;
     212    case FetchBodyConsumer::Type::Blob:
     213        blob(WTFMove(wrapper));
     214        return;
     215    case FetchBodyConsumer::Type::JSON:
     216        json(WTFMove(wrapper));
     217        return;
     218    case FetchBodyConsumer::Type::Text:
     219        text(WTFMove(wrapper));
     220        return;
     221    case FetchBodyConsumer::Type::None:
     222        ASSERT_NOT_REACHED();
     223        return;
     224    }
     225}
     226
    208227#if ENABLE(STREAMS_API)
     228void FetchResponse::startConsumingStream(unsigned type)
     229{
     230    m_isDisturbed = true;
     231    m_consumer.setType(static_cast<FetchBodyConsumer::Type>(type));
     232}
     233
     234void FetchResponse::consumeChunk(Ref<JSC::Uint8Array>&& chunk)
     235{
     236    m_consumer.append(chunk->data(), chunk->byteLength());
     237}
     238
     239void FetchResponse::finishConsumingStream(DeferredWrapper&& promise)
     240{
     241    m_consumer.resolve(promise);
     242}
     243
    209244void FetchResponse::consumeBodyAsStream()
    210245{
  • trunk/Source/WebCore/Modules/fetch/FetchResponse.h

    r203726 r203767  
    3434#include "FetchHeaders.h"
    3535#include "ResourceResponse.h"
     36#include <runtime/TypedArrays.h>
    3637
    3738namespace JSC {
     
    6061    static void fetch(ScriptExecutionContext&, FetchRequest&, FetchPromise&&);
    6162
     63    void consume(unsigned, DeferredWrapper&&);
     64#if ENABLE(STREAMS_API)
     65    void startConsumingStream(unsigned);
     66    void consumeChunk(Ref<JSC::Uint8Array>&&);
     67    void finishConsumingStream(DeferredWrapper&&);
     68#endif
     69
    6270    void setStatus(int, const String&, ExceptionCode&);
    6371    void initializeWith(JSC::ExecState&, JSC::JSValue);
     
    7179
    7280    FetchHeaders& headers() { return m_headers; }
    73     RefPtr<FetchResponse> clone(ScriptExecutionContext&, ExceptionCode&);
     81    Ref<FetchResponse> cloneForJS();
    7482
    7583#if ENABLE(STREAMS_API)
     
    106114        void didReceiveResponse(const ResourceResponse&) final;
    107115        void didReceiveData(const char*, size_t) final;
    108         void didFinishLoadingAsArrayBuffer(RefPtr<ArrayBuffer>&&) final;
    109116
    110117        FetchResponse& m_response;
     
    117124    Optional<BodyLoader> m_bodyLoader;
    118125    mutable String m_responseURL;
     126
     127    FetchBodyConsumer m_consumer { FetchBodyConsumer::Type::ArrayBuffer  };
    119128};
    120129
  • trunk/Source/WebCore/Modules/fetch/FetchResponse.idl

    r203726 r203767  
    5454    [JSBuiltin] readonly attribute ReadableStream? body;
    5555
    56     [NewObject, CallWith=ScriptExecutionContext, RaisesException] FetchResponse clone();
     56    // Copy of FetchBody IDL as we want to implement some of these as built-ins.
     57    [ImplementedAs=isDisturbed] readonly attribute boolean bodyUsed;
     58    [JSBuiltin] Promise arrayBuffer();
     59    [JSBuiltin] Promise blob();
     60    [JSBuiltin] Promise formData();
     61    [JSBuiltin] Promise json();
     62    [JSBuiltin] Promise text();
    5763
     64    [JSBuiltin] FetchResponse clone();
     65
     66    [PrivateIdentifier, NewObject] FetchResponse cloneForJS();
     67
     68    [Conditional=STREAMS_API, PrivateIdentifier] void startConsumingStream(unsigned short type);
     69    [Conditional=STREAMS_API, PrivateIdentifier] void consumeChunk(Uint8Array chunk);
     70    [Conditional=STREAMS_API, PrivateIdentifier] Promise finishConsumingStream();
     71
     72    [PrivateIdentifier] Promise consume(unsigned short type);
    5873    [PrivateIdentifier, RaisesException] void setStatus(unsigned short status, DOMString statusText);
    5974    [CallWith=ScriptState, PrivateIdentifier] void initializeWith(any body);
     
    6176    [PrivateIdentifier] boolean isDisturbed();
    6277};
    63 FetchResponse implements FetchBody;
  • trunk/Source/WebCore/Modules/fetch/FetchResponse.js

    r203766 r203767  
    4949        if (status == 101 || status == 204 || status == 205 || status == 304)
    5050            throw new @TypeError("Response cannot have a body with the given status");
     51
     52        // FIXME: Use @isReadableStream once it is no longer guarded by STREAMS_API guard.
     53        let isBodyReadableStream = (@isObject(body) && !!body.@underlyingSource);
     54        if (isBodyReadableStream)
     55          this.@body = body;
     56
    5157        this.@initializeWith(body);
    5258    }
     
    7278    return this.@body;
    7379}
     80
     81function clone()
     82{
     83    if (!this instanceof @Response)
     84        throw new @TypeError("Function should be called on a Response");
     85
     86    if (@Response.prototype.@isDisturbed.@call(this))
     87        throw new @TypeError("Cannot clone a disturbed Response");
     88
     89    var cloned = @Response.prototype.@cloneForJS.@call(this);
     90    if (this.@body) {
     91        var teedReadableStreams = @teeReadableStream(this.@body, false);
     92        this.@body = teedReadableStreams[0];
     93        cloned.@body = teedReadableStreams[1];
     94    }
     95    return cloned;
     96}
     97
     98// consume and consumeStream single parameter should be kept in sync with FetchBodyConsumer::Type.
     99function arrayBuffer()
     100{
     101    if (!this instanceof @Response)
     102        throw new @TypeError("Function should be called on a Response");
     103
     104    const arrayBufferConsumerType = 1;
     105    if (!this.@body)
     106        return @Response.prototype.@consume.@call(this, arrayBufferConsumerType);
     107
     108    return @consumeStream(this, arrayBufferConsumerType);
     109}
     110
     111function blob()
     112{
     113    if (!this instanceof @Response)
     114        throw new @TypeError("Function should be called on a Response");
     115
     116    const blobConsumerType = 2;
     117    if (!this.@body)
     118        return @Response.prototype.@consume.@call(this, blobConsumerType);
     119
     120    return @consumeStream(this, blobConsumerType);
     121}
     122
     123function formData()
     124{
     125    if (!this instanceof @Response)
     126        throw new @TypeError("Function should be called on a Response");
     127
     128    return @Promise.@reject("Not implemented");
     129}
     130
     131function json()
     132{
     133    if (!this instanceof @Response)
     134        throw new @TypeError("Function should be called on a Response");
     135
     136    const jsonConsumerType = 3;
     137    if (!this.@body)
     138        return @Response.prototype.@consume.@call(this, jsonConsumerType);
     139
     140    return @consumeStream(this, jsonConsumerType);
     141}
     142
     143function text()
     144{
     145    if (!this instanceof @Response)
     146        throw new @TypeError("Function should be called on a Response");
     147
     148    const textConsumerType = 4;
     149    if (!this.@body)
     150        return @Response.prototype.@consume.@call(this, textConsumerType);
     151
     152    return @consumeStream(this, textConsumerType);
     153}
  • trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj

    r203754 r203767  
    16351635                41BF70100FE86F61005E8DEC /* PlatformMessagePortChannel.h in Headers */ = {isa = PBXBuildFile; fileRef = 41BF700E0FE86F61005E8DEC /* PlatformMessagePortChannel.h */; };
    16361636                41C760B10EDE03D300C1655F /* ScriptState.h in Headers */ = {isa = PBXBuildFile; fileRef = 41C760B00EDE03D300C1655F /* ScriptState.h */; settings = {ATTRIBUTES = (Private, ); }; };
     1637                41CF8BE71D46226700707DC9 /* FetchBodyConsumer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 41CF8BE41D46222000707DC9 /* FetchBodyConsumer.cpp */; };
    16371638                41D015CA0F4B5C71004A662F /* ContentType.h in Headers */ = {isa = PBXBuildFile; fileRef = 41D015C80F4B5C71004A662F /* ContentType.h */; };
    16381639                41D015CB0F4B5C71004A662F /* ContentType.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 41D015C90F4B5C71004A662F /* ContentType.cpp */; };
     
    92249225                41BF700E0FE86F61005E8DEC /* PlatformMessagePortChannel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PlatformMessagePortChannel.h; path = default/PlatformMessagePortChannel.h; sourceTree = "<group>"; };
    92259226                41C760B00EDE03D300C1655F /* ScriptState.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ScriptState.h; sourceTree = "<group>"; };
     9227                41CF8BE41D46222000707DC9 /* FetchBodyConsumer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FetchBodyConsumer.cpp; sourceTree = "<group>"; };
     9228                41CF8BE51D46222000707DC9 /* FetchBodyConsumer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FetchBodyConsumer.h; sourceTree = "<group>"; };
     9229                41CF8BE61D46222C00707DC9 /* FetchInternals.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = FetchInternals.js; sourceTree = "<group>"; };
    92269230                41D015C80F4B5C71004A662F /* ContentType.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ContentType.h; sourceTree = "<group>"; };
    92279231                41D015C90F4B5C71004A662F /* ContentType.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ContentType.cpp; sourceTree = "<group>"; };
     
    1710417108                                41F54F7E1C50C4F600338488 /* FetchBody.h */,
    1710517109                                41F54F7F1C50C4F600338488 /* FetchBody.idl */,
     17110                                41CF8BE41D46222000707DC9 /* FetchBodyConsumer.cpp */,
     17111                                41CF8BE51D46222000707DC9 /* FetchBodyConsumer.h */,
    1710617112                                4147E2B31C89912600A7E715 /* FetchBodyOwner.cpp */,
    1710717113                                4147E2B21C88337F00A7E715 /* FetchBodyOwner.h */,
     
    1711017116                                41F54F841C50C4F600338488 /* FetchHeaders.idl */,
    1711117117                                41F54F851C50C4F600338488 /* FetchHeaders.js */,
     17118                                41CF8BE61D46222C00707DC9 /* FetchInternals.js */,
    1711217119                                4147E2B41C89912600A7E715 /* FetchLoader.cpp */,
    1711317120                                4147E2B51C89912600A7E715 /* FetchLoader.h */,
     
    3078430791                                BC06ED9D0BFD660600856E9D /* JSHTMLTableColElement.cpp in Sources */,
    3078530792                                BC06EE040BFD71AA00856E9D /* JSHTMLTableElement.cpp in Sources */,
     30793                                41CF8BE71D46226700707DC9 /* FetchBodyConsumer.cpp in Sources */,
    3078630794                                BC06ED9F0BFD660600856E9D /* JSHTMLTableRowElement.cpp in Sources */,
    3078730795                                BC06ED060BFD5BAE00856E9D /* JSHTMLTableSectionElement.cpp in Sources */,
  • trunk/Source/WebCore/bindings/js/JSDOMPromise.h

    r203726 r203767  
    119119    reject(const RejectResultType& result) { rejectWithValue(result); }
    120120
     121    template<class ResolveResultType> void resolveWithNewlyCreated(Ref<ResolveResultType>&&);
     122
    121123    void reject(ExceptionCode, const String& = { });
    122124
     
    182184}
    183185
     186template<class ResolveResultType>
     187inline void DeferredWrapper::resolveWithNewlyCreated(Ref<ResolveResultType>&& result)
     188{
     189    ASSERT(m_deferred);
     190    ASSERT(m_globalObject);
     191    JSC::ExecState* exec = m_globalObject->globalExec();
     192    JSC::JSLockHolder locker(exec);
     193    resolve(*exec, toJSNewlyCreated(exec, m_globalObject.get(), WTFMove(result)));
     194}
     195
    184196template<class RejectResultType>
    185197inline void DeferredWrapper::rejectWithValue(RejectResultType&& result)
  • trunk/Source/WebCore/bindings/js/WebCoreBuiltinNames.h

    r203766 r203767  
    3636    macro(appendFromJS) \
    3737    macro(body) \
     38    macro(cloneForJS) \
    3839    macro(closeRequested) \
    3940    macro(closedPromiseCapability) \
     41    macro(consume) \
     42    macro(consumeChunk) \
    4043    macro(controlledReadableStream) \
    4144    macro(controller) \
     
    4447    macro(fetchRequest) \
    4548    macro(fillFromJS) \
     49    macro(finishConsumingStream) \
    4650    macro(firstReadCallback) \
    4751    macro(getUserMediaFromJS) \
     
    7579    macro(setStatus) \
    7680    macro(state) \
     81    macro(startConsumingStream) \
    7782    macro(started) \
    7883    macro(startedPromise) \
Note: See TracChangeset for help on using the changeset viewer.