blob: cb76438f1b76c3acc2f85c3bd824085cf010fa9e [file] [log] [blame]
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "modules/fetch/FetchDataLoader.h"
#include "core/html/parser/TextResourceDecoder.h"
#include "modules/fetch/BytesConsumer.h"
#include "wtf/PtrUtil.h"
#include "wtf/text/StringBuilder.h"
#include "wtf/text/WTFString.h"
#include "wtf/typed_arrays/ArrayBufferBuilder.h"
#include <memory>
namespace blink {
namespace {
class FetchDataLoaderAsBlobHandle final : public FetchDataLoader,
public BytesConsumer::Client {
USING_GARBAGE_COLLECTED_MIXIN(FetchDataLoaderAsBlobHandle);
public:
explicit FetchDataLoaderAsBlobHandle(const String& mimeType)
: m_mimeType(mimeType) {}
void start(BytesConsumer* consumer,
FetchDataLoader::Client* client) override {
DCHECK(!m_client);
DCHECK(!m_consumer);
m_client = client;
m_consumer = consumer;
RefPtr<BlobDataHandle> blobHandle = m_consumer->drainAsBlobDataHandle();
if (blobHandle) {
DCHECK_NE(UINT64_MAX, blobHandle->size());
if (blobHandle->type() != m_mimeType) {
// A new BlobDataHandle is created to override the Blob's type.
m_client->didFetchDataLoadedBlobHandle(BlobDataHandle::create(
blobHandle->uuid(), m_mimeType, blobHandle->size()));
} else {
m_client->didFetchDataLoadedBlobHandle(std::move(blobHandle));
}
return;
}
m_blobData = BlobData::create();
m_blobData->setContentType(m_mimeType);
m_consumer->setClient(this);
onStateChange();
}
void cancel() override { m_consumer->cancel(); }
void onStateChange() override {
while (true) {
const char* buffer;
size_t available;
auto result = m_consumer->beginRead(&buffer, &available);
if (result == BytesConsumer::Result::ShouldWait)
return;
if (result == BytesConsumer::Result::Ok) {
m_blobData->appendBytes(buffer, available);
result = m_consumer->endRead(available);
}
switch (result) {
case BytesConsumer::Result::Ok:
break;
case BytesConsumer::Result::ShouldWait:
NOTREACHED();
return;
case BytesConsumer::Result::Done: {
auto size = m_blobData->length();
m_client->didFetchDataLoadedBlobHandle(
BlobDataHandle::create(std::move(m_blobData), size));
return;
}
case BytesConsumer::Result::Error:
m_client->didFetchDataLoadFailed();
return;
}
}
}
DEFINE_INLINE_TRACE() {
visitor->trace(m_consumer);
visitor->trace(m_client);
FetchDataLoader::trace(visitor);
BytesConsumer::Client::trace(visitor);
}
private:
Member<BytesConsumer> m_consumer;
Member<FetchDataLoader::Client> m_client;
String m_mimeType;
std::unique_ptr<BlobData> m_blobData;
};
class FetchDataLoaderAsArrayBuffer final : public FetchDataLoader,
public BytesConsumer::Client {
USING_GARBAGE_COLLECTED_MIXIN(FetchDataLoaderAsArrayBuffer)
public:
void start(BytesConsumer* consumer,
FetchDataLoader::Client* client) override {
DCHECK(!m_client);
DCHECK(!m_rawData);
DCHECK(!m_consumer);
m_client = client;
m_rawData = makeUnique<ArrayBufferBuilder>();
m_consumer = consumer;
m_consumer->setClient(this);
onStateChange();
}
void cancel() override { m_consumer->cancel(); }
void onStateChange() override {
while (true) {
const char* buffer;
size_t available;
auto result = m_consumer->beginRead(&buffer, &available);
if (result == BytesConsumer::Result::ShouldWait)
return;
if (result == BytesConsumer::Result::Ok) {
if (available > 0) {
unsigned bytesAppended = m_rawData->append(buffer, available);
if (!bytesAppended) {
auto unused = m_consumer->endRead(0);
ALLOW_UNUSED_LOCAL(unused);
m_consumer->cancel();
m_client->didFetchDataLoadFailed();
return;
}
DCHECK_EQ(bytesAppended, available);
}
result = m_consumer->endRead(available);
}
switch (result) {
case BytesConsumer::Result::Ok:
break;
case BytesConsumer::Result::ShouldWait:
NOTREACHED();
return;
case BytesConsumer::Result::Done:
m_client->didFetchDataLoadedArrayBuffer(
DOMArrayBuffer::create(m_rawData->toArrayBuffer()));
return;
case BytesConsumer::Result::Error:
m_client->didFetchDataLoadFailed();
return;
}
}
}
DEFINE_INLINE_TRACE() {
visitor->trace(m_consumer);
visitor->trace(m_client);
FetchDataLoader::trace(visitor);
BytesConsumer::Client::trace(visitor);
}
private:
Member<BytesConsumer> m_consumer;
Member<FetchDataLoader::Client> m_client;
std::unique_ptr<ArrayBufferBuilder> m_rawData;
};
class FetchDataLoaderAsString final : public FetchDataLoader,
public BytesConsumer::Client {
USING_GARBAGE_COLLECTED_MIXIN(FetchDataLoaderAsString);
public:
void start(BytesConsumer* consumer,
FetchDataLoader::Client* client) override {
DCHECK(!m_client);
DCHECK(!m_decoder);
DCHECK(!m_consumer);
m_client = client;
m_decoder = TextResourceDecoder::createAlwaysUseUTF8ForText();
m_consumer = consumer;
m_consumer->setClient(this);
onStateChange();
}
void onStateChange() override {
while (true) {
const char* buffer;
size_t available;
auto result = m_consumer->beginRead(&buffer, &available);
if (result == BytesConsumer::Result::ShouldWait)
return;
if (result == BytesConsumer::Result::Ok) {
if (available > 0)
m_builder.append(m_decoder->decode(buffer, available));
result = m_consumer->endRead(available);
}
switch (result) {
case BytesConsumer::Result::Ok:
break;
case BytesConsumer::Result::ShouldWait:
NOTREACHED();
return;
case BytesConsumer::Result::Done:
m_builder.append(m_decoder->flush());
m_client->didFetchDataLoadedString(m_builder.toString());
return;
case BytesConsumer::Result::Error:
m_client->didFetchDataLoadFailed();
return;
}
}
}
void cancel() override { m_consumer->cancel(); }
DEFINE_INLINE_TRACE() {
visitor->trace(m_consumer);
visitor->trace(m_client);
FetchDataLoader::trace(visitor);
BytesConsumer::Client::trace(visitor);
}
private:
Member<BytesConsumer> m_consumer;
Member<FetchDataLoader::Client> m_client;
std::unique_ptr<TextResourceDecoder> m_decoder;
StringBuilder m_builder;
};
class FetchDataLoaderAsStream final : public FetchDataLoader,
public BytesConsumer::Client {
USING_GARBAGE_COLLECTED_MIXIN(FetchDataLoaderAsStream);
public:
explicit FetchDataLoaderAsStream(Stream* outStream)
: m_outStream(outStream) {}
void start(BytesConsumer* consumer,
FetchDataLoader::Client* client) override {
DCHECK(!m_client);
DCHECK(!m_consumer);
m_client = client;
m_consumer = consumer;
m_consumer->setClient(this);
onStateChange();
}
void onStateChange() override {
bool needToFlush = false;
while (true) {
const char* buffer;
size_t available;
auto result = m_consumer->beginRead(&buffer, &available);
if (result == BytesConsumer::Result::ShouldWait) {
if (needToFlush)
m_outStream->flush();
return;
}
if (result == BytesConsumer::Result::Ok) {
m_outStream->addData(buffer, available);
needToFlush = true;
result = m_consumer->endRead(available);
}
switch (result) {
case BytesConsumer::Result::Ok:
break;
case BytesConsumer::Result::ShouldWait:
NOTREACHED();
return;
case BytesConsumer::Result::Done:
if (needToFlush)
m_outStream->flush();
m_outStream->finalize();
m_client->didFetchDataLoadedStream();
return;
case BytesConsumer::Result::Error:
// If the stream is aborted soon after the stream is registered
// to the StreamRegistry, ServiceWorkerURLRequestJob may not
// notice the error and continue waiting forever.
// TODO(yhirano): Add new message to report the error to the
// browser process.
m_outStream->abort();
m_client->didFetchDataLoadFailed();
return;
}
}
}
void cancel() override { m_consumer->cancel(); }
DEFINE_INLINE_TRACE() {
visitor->trace(m_consumer);
visitor->trace(m_client);
visitor->trace(m_outStream);
FetchDataLoader::trace(visitor);
BytesConsumer::Client::trace(visitor);
}
Member<BytesConsumer> m_consumer;
Member<FetchDataLoader::Client> m_client;
Member<Stream> m_outStream;
};
} // namespace
FetchDataLoader* FetchDataLoader::createLoaderAsBlobHandle(
const String& mimeType) {
return new FetchDataLoaderAsBlobHandle(mimeType);
}
FetchDataLoader* FetchDataLoader::createLoaderAsArrayBuffer() {
return new FetchDataLoaderAsArrayBuffer();
}
FetchDataLoader* FetchDataLoader::createLoaderAsString() {
return new FetchDataLoaderAsString();
}
FetchDataLoader* FetchDataLoader::createLoaderAsStream(Stream* outStream) {
return new FetchDataLoaderAsStream(outStream);
}
} // namespace blink