| Index: Source/modules/fetch/BodyStreamBuffer.cpp | 
| diff --git a/Source/modules/fetch/BodyStreamBuffer.cpp b/Source/modules/fetch/BodyStreamBuffer.cpp | 
| index 252101ef8b713a89382e2cc9966db86adac1e316..715f515030f32e6a64ecd7a29adaaf93991006ce 100644 | 
| --- a/Source/modules/fetch/BodyStreamBuffer.cpp | 
| +++ b/Source/modules/fetch/BodyStreamBuffer.cpp | 
| @@ -6,161 +6,221 @@ | 
| #include "modules/fetch/BodyStreamBuffer.h" | 
|  | 
| #include "core/dom/DOMArrayBuffer.h" | 
| +#include "core/dom/DOMTypedArray.h" | 
| #include "core/dom/ExceptionCode.h" | 
| +#include "modules/fetch/DataConsumerHandleUtil.h" | 
| +#include "platform/blob/BlobData.h" | 
|  | 
| namespace blink { | 
|  | 
| -BodyStreamBuffer* BodyStreamBuffer::createEmpty() | 
| -{ | 
| -    return BodyStreamBuffer::create(createFetchDataConsumerHandleFromWebHandle(createDoneDataConsumerHandle())); | 
| -} | 
| - | 
| -FetchDataConsumerHandle* BodyStreamBuffer::handle() const | 
| -{ | 
| -    ASSERT(!m_fetchDataLoader); | 
| -    ASSERT(!m_drainingStreamNotificationClient); | 
| -    return m_handle.get(); | 
| -} | 
| - | 
| -PassOwnPtr<FetchDataConsumerHandle> BodyStreamBuffer::releaseHandle() | 
| -{ | 
| -    ASSERT(!m_fetchDataLoader); | 
| -    ASSERT(!m_drainingStreamNotificationClient); | 
| -    return m_handle.release(); | 
| -} | 
| - | 
| -class ClientWithFinishNotification final : public GarbageCollectedFinalized<ClientWithFinishNotification>, public FetchDataLoader::Client { | 
| -    USING_GARBAGE_COLLECTED_MIXIN(ClientWithFinishNotification); | 
| +class BodyStreamBuffer::LoaderHolder final : public GarbageCollectedFinalized<LoaderHolder>, public FetchDataLoader::Client { | 
| +    WTF_MAKE_NONCOPYABLE(LoaderHolder); | 
| +    USING_GARBAGE_COLLECTED_MIXIN(LoaderHolder); | 
| public: | 
| -    ClientWithFinishNotification(BodyStreamBuffer* buffer, FetchDataLoader::Client* client) | 
| -        : m_buffer(buffer) | 
| -        , m_client(client) | 
| -    { | 
| -    } | 
| +    LoaderHolder(BodyStreamBuffer* buffer, FetchDataLoader* loader, FetchDataLoader::Client* client) : m_buffer(buffer), m_loader(loader), m_client(client) {} | 
|  | 
| -    DEFINE_INLINE_VIRTUAL_TRACE() | 
| -    { | 
| -        visitor->trace(m_buffer); | 
| -        visitor->trace(m_client); | 
| -        FetchDataLoader::Client::trace(visitor); | 
| -    } | 
| +    void start(PassOwnPtr<FetchDataConsumerHandle> handle) { m_loader->start(handle.get(), this); } | 
|  | 
| -private: | 
| void didFetchDataLoadedBlobHandle(PassRefPtr<BlobDataHandle> blobDataHandle) override | 
| { | 
| -        if (m_client) | 
| -            m_client->didFetchDataLoadedBlobHandle(blobDataHandle); | 
| -        m_buffer->didFetchDataLoadFinished(); | 
| +        m_buffer->endLoading(this, EndLoadingDone); | 
| +        m_client->didFetchDataLoadedBlobHandle(blobDataHandle); | 
| } | 
| + | 
| void didFetchDataLoadedArrayBuffer(PassRefPtr<DOMArrayBuffer> arrayBuffer) override | 
| { | 
| -        if (m_client) | 
| -            m_client->didFetchDataLoadedArrayBuffer(arrayBuffer); | 
| -        m_buffer->didFetchDataLoadFinished(); | 
| +        m_buffer->endLoading(this, EndLoadingDone); | 
| +        m_client->didFetchDataLoadedArrayBuffer(arrayBuffer); | 
| } | 
| -    void didFetchDataLoadedString(const String& str) override | 
| + | 
| +    void didFetchDataLoadedString(const String& string) override | 
| { | 
| -        if (m_client) | 
| -            m_client->didFetchDataLoadedString(str); | 
| -        m_buffer->didFetchDataLoadFinished(); | 
| +        m_buffer->endLoading(this, EndLoadingDone); | 
| +        m_client->didFetchDataLoadedString(string); | 
| } | 
| + | 
| void didFetchDataLoadedStream() override | 
| { | 
| -        if (m_client) | 
| -            m_client->didFetchDataLoadedStream(); | 
| -        m_buffer->didFetchDataLoadFinished(); | 
| +        m_buffer->endLoading(this, EndLoadingDone); | 
| +        m_client->didFetchDataLoadedStream(); | 
| } | 
| + | 
| void didFetchDataLoadFailed() override | 
| { | 
| -        if (m_client) | 
| -            m_client->didFetchDataLoadFailed(); | 
| -        m_buffer->didFetchDataLoadFinished(); | 
| +        m_buffer->endLoading(this, EndLoadingErrored); | 
| +        m_client->didFetchDataLoadFailed(); | 
| +    } | 
| + | 
| +    DEFINE_INLINE_TRACE() | 
| +    { | 
| +        visitor->trace(m_buffer); | 
| +        visitor->trace(m_loader); | 
| +        visitor->trace(m_client); | 
| +        FetchDataLoader::Client::trace(visitor); | 
| } | 
|  | 
| +private: | 
| Member<BodyStreamBuffer> m_buffer; | 
| +    Member<FetchDataLoader> m_loader; | 
| Member<FetchDataLoader::Client> m_client; | 
| }; | 
|  | 
| -void BodyStreamBuffer::setDrainingStreamNotificationClient(DrainingStreamNotificationClient* client) | 
| +BodyStreamBuffer::BodyStreamBuffer(PassOwnPtr<FetchDataConsumerHandle> handle) | 
| +    : m_handle(handle) | 
| +    , m_reader(m_handle ? m_handle->obtainReader(this) : nullptr) | 
| +    , m_stream(new ReadableByteStream(this, new ReadableByteStream::StrictStrategy)) | 
| +    , m_lockLevel(0) | 
| +    , m_hasBody(m_handle) | 
| +    , m_streamNeedsMore(false) | 
| { | 
| -    ASSERT(!m_fetchDataLoader); | 
| -    ASSERT(!m_drainingStreamNotificationClient); | 
| -    m_drainingStreamNotificationClient = client; | 
| +    if (m_hasBody) { | 
| +        m_stream->didSourceStart(); | 
| +    } else { | 
| +        // a null body corresponds to an empty stream. | 
| +        close(); | 
| +    } | 
| } | 
|  | 
| -void BodyStreamBuffer::startLoading(FetchDataLoader* fetchDataLoader, FetchDataLoader::Client* client) | 
| +PassRefPtr<BlobDataHandle> BodyStreamBuffer::drainAsBlobDataHandle(FetchDataConsumerHandle::Reader::BlobSizePolicy policy) | 
| { | 
| -    ASSERT(!m_fetchDataLoader); | 
| -    m_fetchDataLoader = fetchDataLoader; | 
| -    m_fetchDataLoader->start(m_handle.get(), new ClientWithFinishNotification(this, client)); | 
| +    ASSERT(!isLocked()); | 
| +    if (ReadableStream::Closed == m_stream->stateInternal() || ReadableStream::Errored == m_stream->stateInternal()) | 
| +        return nullptr; | 
| + | 
| +    RefPtr<BlobDataHandle> blobDataHandle = m_reader->drainAsBlobDataHandle(policy); | 
| +    if (blobDataHandle) { | 
| +        close(); | 
| +        return blobDataHandle.release(); | 
| +    } | 
| +    return nullptr; | 
| } | 
|  | 
| -void BodyStreamBuffer::doDrainingStreamNotification() | 
| +PassOwnPtr<FetchDataConsumerHandle> BodyStreamBuffer::lock(ExecutionContext* executionContext) | 
| { | 
| -    ASSERT(!m_fetchDataLoader); | 
| -    DrainingStreamNotificationClient* client = m_drainingStreamNotificationClient; | 
| -    m_drainingStreamNotificationClient.clear(); | 
| -    if (client) | 
| -        client->didFetchDataLoadFinishedFromDrainingStream(); | 
| +    ASSERT(!isLocked()); | 
| +    ++m_lockLevel; | 
| +    m_reader = nullptr; | 
| +    OwnPtr<FetchDataConsumerHandle> handle = m_handle.release(); | 
| +    if (ReadableStream::Closed == m_stream->stateInternal() || !m_hasBody) | 
| +        return createFetchDataConsumerHandleFromWebHandle(createDoneDataConsumerHandle()); | 
| +    if (ReadableStream::Errored == m_stream->stateInternal()) | 
| +        return createFetchDataConsumerHandleFromWebHandle(createUnexpectedErrorDataConsumerHandle()); | 
| + | 
| +    TrackExceptionState exceptionState; | 
| +    m_streamReader = m_stream->getBytesReader(executionContext, exceptionState); | 
| +    return handle.release(); | 
| } | 
|  | 
| -void BodyStreamBuffer::clearDrainingStreamNotification() | 
| +void BodyStreamBuffer::startLoading(ExecutionContext* executionContext, FetchDataLoader* loader, FetchDataLoader::Client* client) | 
| { | 
| -    ASSERT(!m_fetchDataLoader); | 
| -    m_drainingStreamNotificationClient.clear(); | 
| +    OwnPtr<FetchDataConsumerHandle> handle = lock(executionContext); | 
| +    auto holder = new LoaderHolder(this, loader, client); | 
| +    m_loaders.add(holder); | 
| +    holder->start(handle.release()); | 
| } | 
|  | 
| -void BodyStreamBuffer::didFetchDataLoadFinished() | 
| +void BodyStreamBuffer::pullSource() | 
| { | 
| -    ASSERT(m_fetchDataLoader); | 
| -    m_fetchDataLoader.clear(); | 
| -    doDrainingStreamNotification(); | 
| +    ASSERT(!m_streamNeedsMore); | 
| +    m_streamNeedsMore = true; | 
| +    processData(); | 
| } | 
|  | 
| -DrainingBodyStreamBuffer::~DrainingBodyStreamBuffer() | 
| +ScriptPromise BodyStreamBuffer::cancelSource(ScriptState* scriptState, ScriptValue) | 
| { | 
| -    if (m_buffer) | 
| -        m_buffer->doDrainingStreamNotification(); | 
| +    close(); | 
| +    return ScriptPromise::cast(scriptState, v8::Undefined(scriptState->isolate())); | 
| } | 
|  | 
| -void DrainingBodyStreamBuffer::startLoading(FetchDataLoader* fetchDataLoader, FetchDataLoader::Client* client) | 
| +void BodyStreamBuffer::didGetReadable() | 
| { | 
| -    if (!m_buffer) | 
| +    if (!m_reader) | 
| return; | 
|  | 
| -    m_buffer->startLoading(fetchDataLoader, client); | 
| -    m_buffer.clear(); | 
| +    if (!m_streamNeedsMore) { | 
| +        // Perform zero-length read to call close()/error() early. | 
| +        size_t readSize; | 
| +        WebDataConsumerHandle::Result result = m_reader->read(nullptr, 0, WebDataConsumerHandle::FlagNone, &readSize); | 
| +        switch (result) { | 
| +        case WebDataConsumerHandle::Ok: | 
| +        case WebDataConsumerHandle::ShouldWait: | 
| +            return; | 
| +        case WebDataConsumerHandle::Done: | 
| +            close(); | 
| +            return; | 
| +        case WebDataConsumerHandle::Busy: | 
| +        case WebDataConsumerHandle::ResourceExhausted: | 
| +        case WebDataConsumerHandle::UnexpectedError: | 
| +            error(); | 
| +            return; | 
| +        } | 
| +        return; | 
| +    } | 
| +    processData(); | 
| } | 
|  | 
| -BodyStreamBuffer* DrainingBodyStreamBuffer::leakBuffer() | 
| +void BodyStreamBuffer::close() | 
| { | 
| -    if (!m_buffer) | 
| -        return nullptr; | 
| +    m_reader = nullptr; | 
| +    m_stream->close(); | 
| +} | 
|  | 
| -    m_buffer->clearDrainingStreamNotification(); | 
| -    BodyStreamBuffer* buffer = m_buffer; | 
| -    m_buffer.clear(); | 
| -    return buffer; | 
| +void BodyStreamBuffer::error() | 
| +{ | 
| +    m_reader = nullptr; | 
| +    m_stream->error(DOMException::create(NetworkError, "network error")); | 
| } | 
|  | 
| -PassRefPtr<BlobDataHandle> DrainingBodyStreamBuffer::drainAsBlobDataHandle(FetchDataConsumerHandle::Reader::BlobSizePolicy blobSizePolicy) | 
| +void BodyStreamBuffer::processData() | 
| { | 
| -    if (!m_buffer) | 
| -        return nullptr; | 
| +    ASSERT(m_reader); | 
| +    while (m_streamNeedsMore) { | 
| +        const void* buffer; | 
| +        size_t available; | 
| +        WebDataConsumerHandle::Result result = m_reader->beginRead(&buffer, WebDataConsumerHandle::FlagNone, &available); | 
| +        switch (result) { | 
| +        case WebDataConsumerHandle::Ok: | 
| +            m_streamNeedsMore = m_stream->enqueue(DOMUint8Array::create(static_cast<const unsigned char*>(buffer), available)); | 
| +            m_reader->endRead(available); | 
| +            break; | 
| + | 
| +        case WebDataConsumerHandle::Done: | 
| +            close(); | 
| +            return; | 
| + | 
| +        case WebDataConsumerHandle::ShouldWait: | 
| +            return; | 
| + | 
| +        case WebDataConsumerHandle::Busy: | 
| +        case WebDataConsumerHandle::ResourceExhausted: | 
| +        case WebDataConsumerHandle::UnexpectedError: | 
| +            error(); | 
| +            return; | 
| +        } | 
| +    } | 
| +} | 
|  | 
| -    RefPtr<BlobDataHandle> blobDataHandle = m_buffer->m_handle->obtainReader(nullptr)->drainAsBlobDataHandle(blobSizePolicy); | 
| -    if (!blobDataHandle) | 
| -        return nullptr; | 
| -    m_buffer->doDrainingStreamNotification(); | 
| -    m_buffer.clear(); | 
| -    return blobDataHandle.release(); | 
| +void BodyStreamBuffer::unlock() | 
| +{ | 
| +    ASSERT(m_lockLevel > 0); | 
| +    if (m_streamReader) { | 
| +        m_streamReader->releaseLock(); | 
| +        m_streamReader = nullptr; | 
| +    } | 
| +    --m_lockLevel; | 
| } | 
|  | 
| -DrainingBodyStreamBuffer::DrainingBodyStreamBuffer(BodyStreamBuffer* buffer, BodyStreamBuffer::DrainingStreamNotificationClient* client) | 
| -    : m_buffer(buffer) | 
| +void BodyStreamBuffer::endLoading(FetchDataLoader::Client* client, EndLoadingMode mode) | 
| { | 
| -    ASSERT(client); | 
| -    m_buffer->setDrainingStreamNotificationClient(client); | 
| +    ASSERT(m_loaders.contains(client)); | 
| +    m_loaders.remove(client); | 
| +    unlock(); | 
| +    if (mode == EndLoadingDone) { | 
| +        close(); | 
| +    } else { | 
| +        ASSERT(mode == EndLoadingErrored); | 
| +        error(); | 
| +    } | 
| } | 
|  | 
| } // namespace blink | 
|  |