blob: 07a7aca7e52b4652872fa0a4ccd8b3170f2e01d3 [file] [log] [blame]
// Copyright 2014 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 "config.h"
#include "modules/serviceworkers/Body.h"
#include "bindings/core/v8/ScriptPromiseResolver.h"
#include "bindings/core/v8/ScriptState.h"
#include "bindings/core/v8/V8ThrowException.h"
#include "core/dom/DOMArrayBuffer.h"
#include "core/fileapi/Blob.h"
#include "core/fileapi/FileReaderLoader.h"
#include "core/fileapi/FileReaderLoaderClient.h"
namespace blink {
ScriptPromise Body::readAsync(ScriptState* scriptState, ResponseType type)
{
if (m_bodyUsed)
return ScriptPromise::reject(scriptState, V8ThrowException::createTypeError(scriptState->isolate(), "Already read"));
// When the main thread sends a V8::TerminateExecution() signal to a worker
// thread, any V8 API on the worker thread starts returning an empty
// handle. This can happen in Body::readAsync. To avoid the situation, we
// first check the ExecutionContext and return immediately if it's already
// gone (which means that the V8::TerminateExecution() signal has been sent
// to this worker thread).
ExecutionContext* executionContext = scriptState->executionContext();
if (!executionContext)
return ScriptPromise();
m_bodyUsed = true;
m_responseType = type;
ASSERT(!m_resolver);
m_resolver = ScriptPromiseResolver::create(scriptState);
ScriptPromise promise = m_resolver->promise();
FileReaderLoader::ReadType readType = FileReaderLoader::ReadAsText;
RefPtr<BlobDataHandle> blobHandle = blobDataHandle();
if (!blobHandle.get()) {
blobHandle = BlobDataHandle::create(BlobData::create(), 0);
}
switch (type) {
case ResponseAsArrayBuffer:
readType = FileReaderLoader::ReadAsArrayBuffer;
break;
case ResponseAsBlob:
if (blobHandle->size() != kuint64max) {
// If the size of |blobHandle| is set correctly, creates Blob from
// it.
m_resolver->resolve(Blob::create(blobHandle));
m_resolver.clear();
return promise;
}
// If the size is not set, read as ArrayBuffer and create a new blob to
// get the size.
// FIXME: This workaround is not good for performance.
// When we will stop using Blob as a base system of Body to support
// stream, this problem should be solved.
readType = FileReaderLoader::ReadAsArrayBuffer;
break;
case ResponseAsFormData:
// FIXME: Implement this.
ASSERT_NOT_REACHED();
break;
case ResponseAsJSON:
case ResponseAsText:
break;
default:
ASSERT_NOT_REACHED();
}
m_loader = adoptPtr(new FileReaderLoader(readType, this));
m_loader->start(executionContext, blobHandle);
return promise;
}
ScriptPromise Body::arrayBuffer(ScriptState* scriptState)
{
return readAsync(scriptState, ResponseAsArrayBuffer);
}
ScriptPromise Body::blob(ScriptState* scriptState)
{
return readAsync(scriptState, ResponseAsBlob);
}
ScriptPromise Body::formData(ScriptState* scriptState)
{
return readAsync(scriptState, ResponseAsFormData);
}
ScriptPromise Body::json(ScriptState* scriptState)
{
return readAsync(scriptState, ResponseAsJSON);
}
ScriptPromise Body::text(ScriptState* scriptState)
{
return readAsync(scriptState, ResponseAsText);
}
bool Body::bodyUsed() const
{
return m_bodyUsed;
}
void Body::setBodyUsed()
{
m_bodyUsed = true;
}
void Body::stop()
{
// Canceling the load will call didFail which will remove the resolver.
if (m_resolver)
m_loader->cancel();
}
bool Body::hasPendingActivity() const
{
return m_resolver;
}
Body::Body(ExecutionContext* context)
: ActiveDOMObject(context)
, m_bodyUsed(false)
, m_responseType(ResponseType::ResponseUnknown)
{
}
Body::Body(const Body& copy_from)
: ActiveDOMObject(copy_from.lifecycleContext())
, m_bodyUsed(copy_from.bodyUsed())
, m_responseType(ResponseType::ResponseUnknown)
{
}
void Body::resolveJSON()
{
ASSERT(m_responseType == ResponseAsJSON);
ScriptState::Scope scope(m_resolver->scriptState());
v8::Isolate* isolate = m_resolver->scriptState()->isolate();
v8::Local<v8::String> inputString = v8String(isolate, m_loader->stringResult());
v8::TryCatch trycatch;
v8::Local<v8::Value> parsed = v8::JSON::Parse(inputString);
if (parsed.IsEmpty()) {
if (trycatch.HasCaught())
m_resolver->reject(trycatch.Exception());
else
m_resolver->reject(v8::Exception::Error(v8::String::NewFromUtf8(isolate, "JSON parse error")));
return;
}
m_resolver->resolve(parsed);
}
// FileReaderLoaderClient functions.
void Body::didStartLoading() { }
void Body::didReceiveData() { }
void Body::didFinishLoading()
{
if (!m_resolver->executionContext() || m_resolver->executionContext()->activeDOMObjectsAreStopped())
return;
switch (m_responseType) {
case ResponseAsArrayBuffer:
m_resolver->resolve(DOMArrayBuffer::create(m_loader->arrayBufferResult()));
break;
case ResponseAsBlob: {
ASSERT(blobDataHandle()->size() == kuint64max);
OwnPtr<BlobData> blobData = BlobData::create();
RefPtr<ArrayBuffer> buffer = m_loader->arrayBufferResult();
blobData->appendArrayBuffer(buffer.get());
const size_t length = blobData->length();
m_resolver->resolve(Blob::create(BlobDataHandle::create(blobData.release(), length)));
break;
}
case ResponseAsFormData:
ASSERT_NOT_REACHED();
break;
case ResponseAsJSON:
resolveJSON();
break;
case ResponseAsText:
m_resolver->resolve(m_loader->stringResult());
break;
default:
ASSERT_NOT_REACHED();
}
m_resolver.clear();
}
void Body::didFail(FileError::ErrorCode code)
{
ASSERT(m_resolver);
if (!m_resolver->executionContext() || m_resolver->executionContext()->activeDOMObjectsAreStopped())
return;
m_resolver->resolve("");
m_resolver.clear();
}
} // namespace blink