blob: e8c7bd1d3d267f97825a2a7bc6f8e0af930993ed [file] [log] [blame]
// Copyright 2018 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 "third_party/blink/renderer/core/streams/writable_stream_wrapper.h"
#include "third_party/blink/renderer/bindings/core/v8/v8_script_runner.h"
#include "third_party/blink/renderer/bindings/core/v8/v8_writable_stream.h"
#include "third_party/blink/renderer/core/messaging/message_port.h"
#include "third_party/blink/renderer/core/streams/retain_wrapper_during_construction.h"
#include "third_party/blink/renderer/platform/bindings/exception_state.h"
#include "third_party/blink/renderer/platform/bindings/v8_binding.h"
#include "third_party/blink/renderer/platform/runtime_enabled_features.h"
namespace blink {
void WritableStreamWrapper::Init(ScriptState* script_state,
ScriptValue underlying_sink,
ScriptValue strategy,
ExceptionState& exception_state) {
v8::Local<v8::Object> internal_stream;
v8::TryCatch block(script_state->GetIsolate());
if (!CreateInternalStream(script_state, underlying_sink.V8Value(),
strategy.V8Value())
.ToLocal(&internal_stream)) {
exception_state.RethrowV8Exception(block.Exception());
return;
}
if (!InitInternal(script_state, internal_stream)) {
exception_state.RethrowV8Exception(block.Exception());
return;
}
}
WritableStreamWrapper* WritableStreamWrapper::CreateFromInternalStream(
ScriptState* script_state,
v8::Local<v8::Object> internal_stream,
ExceptionState& exception_state) {
v8::TryCatch block(script_state->GetIsolate());
auto* stream = MakeGarbageCollected<WritableStreamWrapper>();
if (!stream->InitInternal(script_state, internal_stream)) {
exception_state.RethrowV8Exception(block.Exception());
return nullptr;
}
return stream;
}
bool WritableStreamWrapper::InitInternal(
ScriptState* script_state,
v8::Local<v8::Object> internal_stream) {
v8::Isolate* isolate = script_state->GetIsolate();
#if DCHECK_IS_ON()
v8::Local<v8::Value> args[] = {internal_stream};
v8::Local<v8::Value> result_value;
if (!V8ScriptRunner::CallExtra(script_state, "IsWritableStream", args)
.ToLocal(&result_value)) {
DLOG(FATAL) << "Failing to call IsWritableStream for DCHECK.";
return false;
}
DCHECK(result_value->BooleanValue(isolate));
#endif // DCHECK_IS_ON()
internal_stream_.Set(isolate, internal_stream);
v8::Local<v8::Value> wrapper = ToV8(this, script_state);
if (wrapper.IsEmpty())
return false;
v8::Local<v8::Context> context = script_state->GetContext();
v8::Local<v8::Object> bindings =
context->GetExtrasBindingObject().As<v8::Object>();
v8::Local<v8::Value> symbol_value;
if (!bindings->Get(context, V8String(isolate, "internalWritableStreamSymbol"))
.ToLocal(&symbol_value)) {
return false;
}
if (wrapper.As<v8::Object>()
->Set(context, symbol_value.As<v8::Symbol>(),
internal_stream_.NewLocal(isolate))
.IsNothing()) {
return false;
}
return RetainWrapperDuringConstruction(this, script_state);
}
v8::MaybeLocal<v8::Object> WritableStreamWrapper::CreateInternalStream(
ScriptState* script_state,
v8::Local<v8::Value> underlying_sink,
v8::Local<v8::Value> strategy) {
v8::Local<v8::Value> args[] = {underlying_sink, strategy};
v8::Local<v8::Value> stream;
if (!V8ScriptRunner::CallExtra(script_state, "createWritableStream", args)
.ToLocal(&stream)) {
return v8::MaybeLocal<v8::Object>();
}
DCHECK(stream->IsObject());
return v8::MaybeLocal<v8::Object>(stream.As<v8::Object>());
}
void WritableStreamWrapper::Trace(Visitor* visitor) {
visitor->Trace(internal_stream_);
ScriptWrappable::Trace(visitor);
}
bool WritableStreamWrapper::locked(ScriptState* script_state,
ExceptionState& exception_state) const {
auto result = IsLocked(script_state, exception_state);
return !result || *result;
}
ScriptPromise WritableStreamWrapper::abort(ScriptState* script_state,
ExceptionState& exception_state) {
return abort(
script_state,
ScriptValue(script_state, v8::Undefined(script_state->GetIsolate())),
exception_state);
}
ScriptPromise WritableStreamWrapper::abort(ScriptState* script_state,
ScriptValue reason,
ExceptionState& exception_state) {
if (locked(script_state, exception_state) &&
!exception_state.HadException()) {
exception_state.ThrowTypeError("Cannot abort a locked stream");
}
v8::Local<v8::Value> args[] = {
internal_stream_.NewLocal(script_state->GetIsolate()), reason.V8Value()};
v8::TryCatch block(script_state->GetIsolate());
v8::Local<v8::Value> result;
if (!V8ScriptRunner::CallExtra(script_state, "WritableStreamAbort", args)
.ToLocal(&result)) {
exception_state.RethrowV8Exception(block.Exception());
return ScriptPromise();
}
return ScriptPromise(script_state, result);
}
ScriptValue WritableStreamWrapper::getWriter(ScriptState* script_state,
ExceptionState& exception_state) {
v8::TryCatch block(script_state->GetIsolate());
v8::Local<v8::Value> args[] = {
internal_stream_.NewLocal(script_state->GetIsolate())};
v8::Local<v8::Value> result;
if (!V8ScriptRunner::CallExtra(script_state,
"AcquireWritableStreamDefaultWriter", args)
.ToLocal(&result)) {
exception_state.RethrowV8Exception(block.Exception());
return ScriptValue();
}
return ScriptValue(script_state, result);
}
base::Optional<bool> WritableStreamWrapper::IsLocked(
ScriptState* script_state,
ExceptionState& exception_state) const {
v8::TryCatch block(script_state->GetIsolate());
v8::Local<v8::Value> args[] = {
internal_stream_.NewLocal(script_state->GetIsolate())};
v8::Local<v8::Value> result_value;
if (!V8ScriptRunner::CallExtra(script_state, "IsWritableStreamLocked", args)
.ToLocal(&result_value)) {
exception_state.RethrowV8Exception(block.Exception());
return base::nullopt;
}
return result_value->BooleanValue(script_state->GetIsolate());
}
void WritableStreamWrapper::Serialize(ScriptState* script_state,
MessagePort* port,
ExceptionState& exception_state) {
DCHECK(port);
DCHECK(RuntimeEnabledFeatures::TransferableStreamsEnabled());
v8::TryCatch block(script_state->GetIsolate());
v8::Local<v8::Value> port_v8_value = ToV8(port, script_state);
DCHECK(!port_v8_value.IsEmpty());
v8::Local<v8::Value> args[] = {GetInternalStream(script_state).V8Value(),
port_v8_value};
V8ScriptRunner::CallExtra(script_state, "WritableStreamSerialize", args);
if (block.HasCaught()) {
exception_state.RethrowV8Exception(block.Exception());
}
}
// static
WritableStreamWrapper* WritableStreamWrapper::Deserialize(
ScriptState* script_state,
MessagePort* port,
ExceptionState& exception_state) {
// We need to execute V8 Extras JavaScript to create the new WritableStream.
// We will not run author code.
auto* isolate = script_state->GetIsolate();
v8::Isolate::AllowJavascriptExecutionScope allow_js(isolate);
DCHECK(port);
DCHECK(RuntimeEnabledFeatures::TransferableStreamsEnabled());
v8::TryCatch block(isolate);
v8::Local<v8::Value> port_v8 = ToV8(port, script_state);
DCHECK(!port_v8.IsEmpty());
v8::Local<v8::Value> args[] = {port_v8};
ScriptValue internal_stream(
script_state, V8ScriptRunner::CallExtra(
script_state, "WritableStreamDeserialize", args));
if (block.HasCaught()) {
exception_state.RethrowV8Exception(block.Exception());
return nullptr;
}
DCHECK(!internal_stream.IsEmpty());
return CreateFromInternalStream(script_state, internal_stream,
exception_state);
}
ScriptValue WritableStreamWrapper::GetInternalStream(
ScriptState* script_state) const {
return ScriptValue(script_state,
internal_stream_.NewLocal(script_state->GetIsolate()));
}
} // namespace blink