blob: 5f7912d26821451de70959d847d2d1fb0e9c6676 [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 "config.h"
#include "core/streams/ReadableStreamReader.h"
#include "bindings/core/v8/ExceptionState.h"
#include "bindings/core/v8/ScriptPromiseResolver.h"
#include "bindings/core/v8/V8IteratorResultValue.h"
#include "core/dom/DOMException.h"
#include "core/dom/ExceptionCode.h"
#include "core/streams/ReadableStream.h"
namespace blink {
ReadableStreamReader::ReadableStreamReader(ExecutionContext* executionContext, ReadableStream* stream)
: ActiveDOMObject(executionContext)
, m_stream(stream)
, m_stateAfterRelease(ReadableStream::Closed)
, m_closed(new ClosedPromise(executionContext, this, ClosedPromise::Closed))
{
suspendIfNeeded();
ASSERT(m_stream->isLockedTo(nullptr));
m_stream->setReader(this);
if (m_stream->stateInternal() == ReadableStream::Closed || m_stream->stateInternal() == ReadableStream::Errored) {
// If the stream is already closed or errored the created reader
// should be closed or errored respectively.
releaseLock();
}
}
ScriptPromise ReadableStreamReader::closed(ScriptState* scriptState)
{
return m_closed->promise(scriptState->world());
}
bool ReadableStreamReader::isActive() const
{
return m_stream->isLockedTo(this);
}
ScriptPromise ReadableStreamReader::cancel(ScriptState* scriptState)
{
return cancel(scriptState, ScriptValue(scriptState, v8::Undefined(scriptState->isolate())));
}
ScriptPromise ReadableStreamReader::cancel(ScriptState* scriptState, ScriptValue reason)
{
if (isActive())
return m_stream->cancelInternal(scriptState, reason);
// A method should return a different promise on each call.
ScriptPromiseResolver* resolver = ScriptPromiseResolver::create(scriptState);
ScriptPromise promise = resolver->promise();
resolver->resolve(closed(scriptState).v8Value());
return promise;
}
ScriptPromise ReadableStreamReader::read(ScriptState* scriptState)
{
if (!isActive()) {
ASSERT(m_stateAfterRelease == ReadableStream::Closed || m_stateAfterRelease == ReadableStream::Errored);
if (m_stateAfterRelease == ReadableStream::Closed) {
// {value: undefined, done: true}
return ScriptPromise::cast(scriptState, v8IteratorResultDone(scriptState));
}
// A method should return a different promise on each call.
ScriptPromiseResolver* resolver = ScriptPromiseResolver::create(scriptState);
ScriptPromise promise = resolver->promise();
resolver->resolve(closed(scriptState).v8Value());
return promise;
}
return m_stream->read(scriptState);
}
void ReadableStreamReader::releaseLock(ExceptionState& es)
{
if (!isActive())
return;
if (m_stream->hasPendingReads()) {
es.throwTypeError("The stream has pending read operations.");
return;
}
releaseLock();
}
void ReadableStreamReader::releaseLock()
{
if (!isActive())
return;
ASSERT(!m_stream->hasPendingReads());
if (m_stream->stateInternal() == ReadableStream::Closed) {
m_stateAfterRelease = ReadableStream::Closed;
m_closed->resolve(ToV8UndefinedGenerator());
} else if (m_stream->stateInternal() == ReadableStream::Errored) {
m_stateAfterRelease = ReadableStream::Errored;
m_closed->reject(m_stream->storedException());
} else {
m_stateAfterRelease = ReadableStream::Closed;
m_closed->resolve(ToV8UndefinedGenerator());
}
// We call setReader(nullptr) after resolving / rejecting |m_closed|
// because it affects hasPendingActivity.
m_stream->setReader(nullptr);
ASSERT(!isActive());
}
bool ReadableStreamReader::hasPendingActivity() const
{
// We need to extend ReadableStreamReader's wrapper's life while it is
// active in order to call resolve / reject on ScriptPromiseProperties.
return isActive();
}
void ReadableStreamReader::stop()
{
if (isActive()) {
// Calling |error| will release the lock.
m_stream->error(DOMException::create(AbortError, "The frame stops working."));
}
ActiveDOMObject::stop();
}
DEFINE_TRACE(ReadableStreamReader)
{
visitor->trace(m_stream);
visitor->trace(m_closed);
ActiveDOMObject::trace(visitor);
}
} // namespace blink