blob: 308a01729b6fc7fdbb018dee7fecc229f29b0e63 [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 "wtf/text/StringBuilder.h"
#include "wtf/text/WTFString.h"
#include "wtf/typed_arrays/ArrayBufferBuilder.h"
namespace blink {
namespace {
class FetchDataLoaderAsBlobHandle
: public FetchDataLoader
, public WebDataConsumerHandle::Client {
public:
explicit FetchDataLoaderAsBlobHandle(const String& mimeType)
: m_client(nullptr)
, m_mimeType(mimeType) { }
DEFINE_INLINE_VIRTUAL_TRACE()
{
FetchDataLoader::trace(visitor);
visitor->trace(m_client);
}
private:
void start(FetchDataConsumerHandle* handle, FetchDataLoader::Client* client) override
{
ASSERT(!m_client);
ASSERT(!m_reader);
m_client = client;
// Passing |this| here is safe because |this| owns |m_reader|.
m_reader = handle->obtainReader(this);
RefPtr<BlobDataHandle> blobHandle = m_reader->drainAsBlobDataHandle();
if (blobHandle) {
ASSERT(blobHandle->size() != UINT64_MAX);
m_reader.clear();
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(blobHandle);
}
m_client.clear();
return;
}
// We read data from |m_reader| and create a new blob.
m_blobData = BlobData::create();
m_blobData->setContentType(m_mimeType);
}
void didGetReadable() override
{
ASSERT(m_client);
ASSERT(m_reader);
while (true) {
const void* buffer;
size_t available;
WebDataConsumerHandle::Result result = m_reader->beginRead(&buffer, WebDataConsumerHandle::FlagNone, &available);
switch (result) {
case WebDataConsumerHandle::Ok:
m_blobData->appendBytes(buffer, available);
m_reader->endRead(available);
break;
case WebDataConsumerHandle::Done: {
m_reader.clear();
long long size = m_blobData->length();
m_client->didFetchDataLoadedBlobHandle(BlobDataHandle::create(m_blobData.release(), size));
m_client.clear();
return;
}
case WebDataConsumerHandle::ShouldWait:
return;
case WebDataConsumerHandle::Busy:
case WebDataConsumerHandle::ResourceExhausted:
case WebDataConsumerHandle::UnexpectedError:
m_reader.clear();
m_blobData.clear();
m_client->didFetchDataLoadFailed();
m_client.clear();
return;
}
}
}
void cancel() override
{
m_reader.clear();
m_blobData.clear();
m_client.clear();
}
OwnPtr<FetchDataConsumerHandle::Reader> m_reader;
Member<FetchDataLoader::Client> m_client;
String m_mimeType;
OwnPtr<BlobData> m_blobData;
};
class FetchDataLoaderAsArrayBuffer
: public FetchDataLoader
, public WebDataConsumerHandle::Client {
public:
FetchDataLoaderAsArrayBuffer()
: m_client(nullptr) { }
DEFINE_INLINE_VIRTUAL_TRACE()
{
FetchDataLoader::trace(visitor);
visitor->trace(m_client);
}
protected:
void start(FetchDataConsumerHandle* handle, FetchDataLoader::Client* client) override
{
ASSERT(!m_client);
ASSERT(!m_rawData);
ASSERT(!m_reader);
m_client = client;
m_rawData = adoptPtr(new ArrayBufferBuilder());
m_reader = handle->obtainReader(this);
}
void didGetReadable() override
{
ASSERT(m_client);
ASSERT(m_rawData);
ASSERT(m_reader);
while (true) {
const void* buffer;
size_t available;
WebDataConsumerHandle::Result result = m_reader->beginRead(&buffer, WebDataConsumerHandle::FlagNone, &available);
switch (result) {
case WebDataConsumerHandle::Ok:
if (available > 0) {
unsigned bytesAppended = m_rawData->append(static_cast<const char*>(buffer), available);
if (!bytesAppended) {
m_reader->endRead(0);
error();
return;
}
ASSERT(bytesAppended == available);
}
m_reader->endRead(available);
break;
case WebDataConsumerHandle::Done:
m_reader.clear();
m_client->didFetchDataLoadedArrayBuffer(DOMArrayBuffer::create(m_rawData->toArrayBuffer()));
m_rawData.clear();
m_client.clear();
return;
case WebDataConsumerHandle::ShouldWait:
return;
case WebDataConsumerHandle::Busy:
case WebDataConsumerHandle::ResourceExhausted:
case WebDataConsumerHandle::UnexpectedError:
error();
return;
}
}
}
void error()
{
m_reader.clear();
m_rawData.clear();
m_client->didFetchDataLoadFailed();
m_client.clear();
}
void cancel() override
{
m_reader.clear();
m_rawData.clear();
m_client.clear();
}
OwnPtr<FetchDataConsumerHandle::Reader> m_reader;
Member<FetchDataLoader::Client> m_client;
OwnPtr<ArrayBufferBuilder> m_rawData;
};
class FetchDataLoaderAsString
: public FetchDataLoader
, public WebDataConsumerHandle::Client {
public:
FetchDataLoaderAsString()
: m_client(nullptr) { }
DEFINE_INLINE_VIRTUAL_TRACE()
{
FetchDataLoader::trace(visitor);
visitor->trace(m_client);
}
protected:
void start(FetchDataConsumerHandle* handle, FetchDataLoader::Client* client) override
{
ASSERT(!m_client);
ASSERT(!m_decoder);
ASSERT(!m_reader);
m_client = client;
m_decoder = TextResourceDecoder::createAlwaysUseUTF8ForText();
m_reader = handle->obtainReader(this);
}
void didGetReadable() override
{
ASSERT(m_client);
ASSERT(m_decoder);
ASSERT(m_reader);
while (true) {
const void* buffer;
size_t available;
WebDataConsumerHandle::Result result = m_reader->beginRead(&buffer, WebDataConsumerHandle::FlagNone, &available);
switch (result) {
case WebDataConsumerHandle::Ok:
if (available > 0)
m_builder.append(m_decoder->decode(static_cast<const char*>(buffer), available));
m_reader->endRead(available);
break;
case WebDataConsumerHandle::Done:
m_reader.clear();
m_builder.append(m_decoder->flush());
m_client->didFetchDataLoadedString(m_builder.toString());
m_builder.clear();
m_decoder.clear();
m_client.clear();
return;
case WebDataConsumerHandle::ShouldWait:
return;
case WebDataConsumerHandle::Busy:
case WebDataConsumerHandle::ResourceExhausted:
case WebDataConsumerHandle::UnexpectedError:
error();
return;
}
}
}
void error()
{
m_reader.clear();
m_builder.clear();
m_decoder.clear();
m_client->didFetchDataLoadFailed();
m_client.clear();
}
void cancel() override
{
m_reader.clear();
m_builder.clear();
m_decoder.clear();
m_client.clear();
}
OwnPtr<FetchDataConsumerHandle::Reader> m_reader;
Member<FetchDataLoader::Client> m_client;
OwnPtr<TextResourceDecoder> m_decoder;
StringBuilder m_builder;
};
class FetchDataLoaderAsStream
: public FetchDataLoader
, public WebDataConsumerHandle::Client {
public:
explicit FetchDataLoaderAsStream(Stream* outStream)
: m_client(nullptr)
, m_outStream(outStream) { }
DEFINE_INLINE_VIRTUAL_TRACE()
{
FetchDataLoader::trace(visitor);
visitor->trace(m_client);
visitor->trace(m_outStream);
}
protected:
void start(FetchDataConsumerHandle* handle, FetchDataLoader::Client* client) override
{
ASSERT(!m_client);
ASSERT(!m_reader);
m_client = client;
m_reader = handle->obtainReader(this);
}
void didGetReadable() override
{
ASSERT(m_client);
ASSERT(m_reader);
bool needToFlush = false;
while (true) {
const void* buffer;
size_t available;
WebDataConsumerHandle::Result result = m_reader->beginRead(&buffer, WebDataConsumerHandle::FlagNone, &available);
switch (result) {
case WebDataConsumerHandle::Ok:
m_outStream->addData(static_cast<const char*>(buffer), available);
m_reader->endRead(available);
needToFlush = true;
break;
case WebDataConsumerHandle::Done:
m_reader.clear();
if (needToFlush)
m_outStream->flush();
m_outStream->finalize();
m_client->didFetchDataLoadedStream();
cleanup();
return;
case WebDataConsumerHandle::ShouldWait:
if (needToFlush)
m_outStream->flush();
return;
case WebDataConsumerHandle::Busy:
case WebDataConsumerHandle::ResourceExhausted:
case WebDataConsumerHandle::UnexpectedError:
// If the stream is aborted soon after the stream is registered
// to the StreamRegistry, ServiceWorkerURLRequestJob may not
// notice the error and continue waiting forever.
// FIXME: Add new message to report the error to the browser
// process.
m_reader.clear();
m_outStream->abort();
m_client->didFetchDataLoadFailed();
cleanup();
return;
}
}
}
void cancel() override
{
cleanup();
}
void cleanup()
{
m_reader.clear();
m_client.clear();
m_outStream.clear();
}
OwnPtr<FetchDataConsumerHandle::Reader> m_reader;
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