blob: 21edc0cefabfd9e74f7af09051734743ac4d7822 [file] [log] [blame]
// Copyright 2016 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.
#ifndef ReadableStreamController_h
#define ReadableStreamController_h
#include "bindings/core/v8/ScopedPersistent.h"
#include "bindings/core/v8/ScriptValue.h"
#include "bindings/core/v8/ToV8.h"
#include "bindings/core/v8/V8ScriptRunner.h"
#include "bindings/core/v8/WorkerOrWorkletScriptController.h"
#include "core/CoreExport.h"
#include "core/workers/WorkerGlobalScope.h"
#include "platform/heap/Handle.h"
#include "wtf/RefPtr.h"
#include <v8.h>
namespace blink {
// TODO(tyoshino): Rename this to ReadableStreamDefaultControllerWrapper.
class CORE_EXPORT ReadableStreamController final : public GarbageCollectedFinalized<ReadableStreamController> {
public:
DEFINE_INLINE_TRACE() {}
explicit ReadableStreamController(ScriptValue controller)
: m_scriptState(controller.getScriptState())
, m_jsController(controller.isolate(), controller.v8Value())
{
m_jsController.setWeak(&m_jsController, ReadableStreamController::controllerWeakCallback);
}
// Users of the ReadableStreamController can call this to note that the stream has been canceled and thus they
// don't anticipate using the ReadableStreamController anymore. (close/desiredSize/enqueue/error will become no-ops
// afterward.)
void noteHasBeenCanceled()
{
m_jsController.clear();
}
bool isActive() const
{
return !m_jsController.isEmpty();
}
void close()
{
if (isTerminating(m_scriptState.get())) {
m_jsController.clear();
return;
}
ScriptState* scriptState = m_scriptState.get();
ScriptState::Scope scope(scriptState); // will assert context is valid; do not call this method when the context is invalidated
v8::Isolate* isolate = scriptState->isolate();
v8::Local<v8::Value> controller = m_jsController.newLocal(isolate);
if (controller.IsEmpty())
return;
v8::Local<v8::Value> args[] = { controller };
v8::MaybeLocal<v8::Value> result = V8ScriptRunner::callExtra(scriptState, "ReadableStreamDefaultControllerClose", args);
m_jsController.clear();
if (isTerminating(m_scriptState.get()))
return;
v8CallOrCrash(result);
}
double desiredSize() const
{
if (isTerminating(m_scriptState.get()))
return 0;
ScriptState* scriptState = m_scriptState.get();
ScriptState::Scope scope(scriptState); // will assert context is valid; do not call this method when the context is invalidated
v8::Isolate* isolate = scriptState->isolate();
v8::Local<v8::Value> controller = m_jsController.newLocal(isolate);
if (controller.IsEmpty())
return 0;
v8::Local<v8::Value> args[] = { controller };
v8::MaybeLocal<v8::Value> result = V8ScriptRunner::callExtra(scriptState, "ReadableStreamDefaultControllerGetDesiredSize", args);
if (isTerminating(m_scriptState.get()))
return 0;
return v8CallOrCrash(result).As<v8::Number>()->Value();
}
template <typename ChunkType>
void enqueue(ChunkType chunk) const
{
if (isTerminating(m_scriptState.get()))
return;
ScriptState* scriptState = m_scriptState.get();
ScriptState::Scope scope(scriptState); // will assert context is valid; do not call this method when the context is invalidated
v8::Isolate* isolate = scriptState->isolate();
v8::Local<v8::Value> controller = m_jsController.newLocal(isolate);
if (controller.IsEmpty())
return;
v8::Local<v8::Value> jsChunk = toV8(chunk, scriptState);
v8::Local<v8::Value> args[] = { controller, jsChunk };
v8::MaybeLocal<v8::Value> result = V8ScriptRunner::callExtra(scriptState, "ReadableStreamDefaultControllerEnqueue", args);
if (isTerminating(m_scriptState.get()))
return;
v8CallOrCrash(result);
}
template <typename ErrorType>
void error(ErrorType error)
{
if (isTerminating(m_scriptState.get())) {
m_jsController.clear();
return;
}
ScriptState* scriptState = m_scriptState.get();
ScriptState::Scope scope(scriptState); // will assert context is valid; do not call this method when the context is invalidated
v8::Isolate* isolate = scriptState->isolate();
v8::Local<v8::Value> controller = m_jsController.newLocal(isolate);
if (controller.IsEmpty())
return;
v8::Local<v8::Value> jsError = toV8(error, scriptState);
v8::Local<v8::Value> args[] = { controller, jsError };
v8::MaybeLocal<v8::Value> result = V8ScriptRunner::callExtra(scriptState, "ReadableStreamDefaultControllerError", args);
m_jsController.clear();
if (isTerminating(m_scriptState.get()))
return;
v8CallOrCrash(result);
}
private:
static void controllerWeakCallback(const v8::WeakCallbackInfo<ScopedPersistent<v8::Value>>& weakInfo)
{
weakInfo.GetParameter()->clear();
}
static bool isTerminating(ScriptState* scriptState)
{
ExecutionContext* executionContext = scriptState->getExecutionContext();
if (!executionContext)
return true;
if (!executionContext->isWorkerGlobalScope())
return false;
return toWorkerGlobalScope(executionContext)->scriptController()->isExecutionTerminating();
}
RefPtr<ScriptState> m_scriptState;
ScopedPersistent<v8::Value> m_jsController;
};
} // namespace blink
#endif // ReadableStreamController_h