diff options
Diffstat (limited to 'chromium/third_party/blink/renderer/core/streams')
44 files changed, 1635 insertions, 262 deletions
diff --git a/chromium/third_party/blink/renderer/core/streams/BUILD.gn b/chromium/third_party/blink/renderer/core/streams/BUILD.gn index 24a5a0577bc..e747d5e73ac 100644 --- a/chromium/third_party/blink/renderer/core/streams/BUILD.gn +++ b/chromium/third_party/blink/renderer/core/streams/BUILD.gn @@ -6,12 +6,18 @@ import("//third_party/blink/renderer/core/core.gni") blink_core_sources("streams") { sources = [ + "byte_length_queuing_strategy.cc", + "byte_length_queuing_strategy.h", + "count_queuing_strategy.cc", + "count_queuing_strategy.h", "miscellaneous_operations.cc", "miscellaneous_operations.h", "promise_handler.cc", "promise_handler.h", "queue_with_sizes.cc", "queue_with_sizes.h", + "queuing_strategy_common.cc", + "queuing_strategy_common.h", "readable_stream.cc", "readable_stream.h", "readable_stream_default_controller.cc", @@ -30,6 +36,8 @@ blink_core_sources("streams") { "stream_algorithms.h", "stream_promise_resolver.cc", "stream_promise_resolver.h", + "transferable_streams.cc", + "transferable_streams.h", "transform_stream.cc", "transform_stream.h", "transform_stream_default_controller.cc", diff --git a/chromium/third_party/blink/renderer/core/streams/README.md b/chromium/third_party/blink/renderer/core/streams/README.md index 68569248bdf..1b845c20ec1 100644 --- a/chromium/third_party/blink/renderer/core/streams/README.md +++ b/chromium/third_party/blink/renderer/core/streams/README.md @@ -26,6 +26,8 @@ feature. The following files are part of the new implementation: writable_stream_default_writer.idl writable_stream_native.cc writable_stream_native.h + transferable_streams.cc + transferable_streams.h transform_stream_default_controller.cc transform_stream_default_controller.h transform_stream_native.cc diff --git a/chromium/third_party/blink/renderer/core/streams/ReadableStream.js b/chromium/third_party/blink/renderer/core/streams/ReadableStream.js index 797fe38d9b5..b34ad33e09d 100644 --- a/chromium/third_party/blink/renderer/core/streams/ReadableStream.js +++ b/chromium/third_party/blink/renderer/core/streams/ReadableStream.js @@ -38,7 +38,6 @@ const internalReadableStreamSymbol = v8.createPrivateSymbol( 'internal ReadableStream in exposed ReadableStream interface'); // Remove this once C++ code has been updated to use CreateReadableStream. - const _lockNotifyTarget = v8.createPrivateSymbol('[[lockNotifyTarget]]'); const _strategySizeAlgorithm = v8.createPrivateSymbol( '[[strategySizeAlgorithm]]'); const _pullAlgorithm = v8.createPrivateSymbol('[[pullAlgorithm]]'); @@ -47,9 +46,6 @@ const CLOSE_REQUESTED = 0b10; const PULLING = 0b100; const PULL_AGAIN = 0b1000; - // TODO(ricea): Remove this once blink::UnderlyingSourceBase no longer needs - // it. - const BLINK_LOCK_NOTIFICATIONS = 0b10000; const ObjectCreate = global.Object.create; @@ -127,10 +123,10 @@ // CreateReadableStream. constructor(underlyingSource = {}, strategy = {}, internalArgument = undefined) { - const enableBlinkLockNotifications = + const createdByUA = internalArgument === createWithExternalControllerSentinel; - if (!useCounted && !enableBlinkLockNotifications) { + if (!useCounted && !createdByUA) { binding.countUse('ReadableStreamConstructor'); useCounted = true; } @@ -157,8 +153,7 @@ highWaterMark = ValidateAndNormalizeHighWaterMark(highWaterMark); SetUpReadableStreamDefaultControllerFromUnderlyingSource( - this, underlyingSource, highWaterMark, sizeAlgorithm, - enableBlinkLockNotifications); + this, underlyingSource, highWaterMark, sizeAlgorithm); } } @@ -381,11 +376,8 @@ return new ReadableStreamDefaultReader(stream); } - // The non-standard boolean |enableBlinkLockNotifications| argument indicates - // whether the stream is being created from C++. function CreateReadableStream(startAlgorithm, pullAlgorithm, cancelAlgorithm, - highWaterMark, sizeAlgorithm, - enableBlinkLockNotifications) { + highWaterMark, sizeAlgorithm) { if (highWaterMark === undefined) { highWaterMark = 1; } @@ -399,7 +391,7 @@ const controller = ObjectCreate(ReadableStreamDefaultController_prototype); SetUpReadableStreamDefaultController( stream, controller, startAlgorithm, pullAlgorithm, cancelAlgorithm, - highWaterMark, sizeAlgorithm, enableBlinkLockNotifications); + highWaterMark, sizeAlgorithm); return stream; } @@ -490,11 +482,9 @@ const startAlgorithm = () => undefined; const branch1Stream = CreateReadableStream( - startAlgorithm, pullAlgorithm, cancel1Algorithm, undefined, undefined, - false); + startAlgorithm, pullAlgorithm, cancel1Algorithm); const branch2Stream = CreateReadableStream( - startAlgorithm, pullAlgorithm, cancel2Algorithm, undefined, undefined, - false); + startAlgorithm, pullAlgorithm, cancel2Algorithm); const branch1controller = branch1Stream[_controller]; const branch2controller = branch2Stream[_controller]; @@ -695,17 +685,6 @@ } function ReadableStreamReaderGenericInitialize(reader, stream) { - // TODO(yhirano): Remove this when we don't need hasPendingActivity in - // blink::UnderlyingSourceBase. - const controller = stream[_controller]; - if (controller[_readableStreamDefaultControllerBits] & - BLINK_LOCK_NOTIFICATIONS) { - // The stream is created with an external controller (i.e. made in - // Blink). - const lockNotifyTarget = controller[_lockNotifyTarget]; - callFunction(lockNotifyTarget.notifyLockAcquired, lockNotifyTarget); - } - reader[_ownerReadableStream] = stream; stream[_reader] = reader; @@ -724,17 +703,6 @@ } function ReadableStreamReaderGenericRelease(reader) { - // TODO(yhirano): Remove this when we don't need hasPendingActivity in - // blink::UnderlyingSourceBase. - const controller = reader[_ownerReadableStream][_controller]; - if (controller[_readableStreamDefaultControllerBits] & - BLINK_LOCK_NOTIFICATIONS) { - // The stream is created with an external controller (i.e. made in - // Blink). - const lockNotifyTarget = controller[_lockNotifyTarget]; - callFunction(lockNotifyTarget.notifyLockReleased, lockNotifyTarget); - } - if (ReadableStreamGetState(reader[_ownerReadableStream]) === STATE_READABLE) { rejectPromise( @@ -1000,12 +968,10 @@ function SetUpReadableStreamDefaultController( stream, controller, startAlgorithm, pullAlgorithm, cancelAlgorithm, - highWaterMark, sizeAlgorithm, enableBlinkLockNotifications) { + highWaterMark, sizeAlgorithm) { controller[_controlledReadableStream] = stream; controller[_queue] = new binding.SimpleQueue(); controller[_queueTotalSize] = 0; - controller[_readableStreamDefaultControllerBits] = - enableBlinkLockNotifications ? BLINK_LOCK_NOTIFICATIONS : 0b0; controller[_strategySizeAlgorithm] = sizeAlgorithm; controller[_strategyHWM] = highWaterMark; controller[_pullAlgorithm] = pullAlgorithm; @@ -1019,8 +985,7 @@ } function SetUpReadableStreamDefaultControllerFromUnderlyingSource( - stream, underlyingSource, highWaterMark, sizeAlgorithm, - enableBlinkLockNotifications) { + stream, underlyingSource, highWaterMark, sizeAlgorithm) { const controller = ObjectCreate(ReadableStreamDefaultController_prototype); const startAlgorithm = () => CallOrNoop1(underlyingSource, 'start', controller, @@ -1029,13 +994,9 @@ underlyingSource, 'pull', 0, controller, 'underlyingSource.pull'); const cancelAlgorithm = CreateAlgorithmFromUnderlyingMethod( underlyingSource, 'cancel', 1, 'underlyingSource.cancel'); - // TODO(ricea): Remove this once C++ API has been updated. - if (enableBlinkLockNotifications) { - controller[_lockNotifyTarget] = underlyingSource; - } SetUpReadableStreamDefaultController( stream, controller, startAlgorithm, pullAlgorithm, cancelAlgorithm, - highWaterMark, sizeAlgorithm, enableBlinkLockNotifications); + highWaterMark, sizeAlgorithm); } // diff --git a/chromium/third_party/blink/renderer/core/streams/TransformStream.js b/chromium/third_party/blink/renderer/core/streams/TransformStream.js index 4b7449584a9..639be9b30f1 100644 --- a/chromium/third_party/blink/renderer/core/streams/TransformStream.js +++ b/chromium/third_party/blink/renderer/core/streams/TransformStream.js @@ -193,7 +193,7 @@ }; stream[_readable] = binding.CreateReadableStream( startAlgorithm, pullAlgorithm, cancelAlgorithm, readableHighWaterMark, - readableSizeAlgorithm, false); + readableSizeAlgorithm); stream[_backpressure] = undefined; stream[_backpressureChangePromise] = undefined; TransformStreamSetBackpressure(stream, true); diff --git a/chromium/third_party/blink/renderer/core/streams/byte_length_queuing_strategy.cc b/chromium/third_party/blink/renderer/core/streams/byte_length_queuing_strategy.cc new file mode 100644 index 00000000000..aae8fb8bc29 --- /dev/null +++ b/chromium/third_party/blink/renderer/core/streams/byte_length_queuing_strategy.cc @@ -0,0 +1,101 @@ +// Copyright 2019 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/byte_length_queuing_strategy.h" + +#include "third_party/blink/renderer/bindings/core/v8/script_function.h" +#include "third_party/blink/renderer/bindings/core/v8/script_value.h" +#include "third_party/blink/renderer/core/streams/queuing_strategy_common.h" +#include "third_party/blink/renderer/core/streams/queuing_strategy_init.h" +#include "third_party/blink/renderer/platform/bindings/script_state.h" +#include "third_party/blink/renderer/platform/bindings/v8_binding.h" +#include "third_party/blink/renderer/platform/heap/visitor.h" + +namespace blink { + +namespace { + +class ByteLengthQueuingStrategySizeFunction final : public ScriptFunction { + public: + static v8::Local<v8::Function> CreateFunction(ScriptState* script_state) { + ByteLengthQueuingStrategySizeFunction* self = + MakeGarbageCollected<ByteLengthQueuingStrategySizeFunction>( + script_state); + return self->BindToV8Function(); + } + + explicit ByteLengthQueuingStrategySizeFunction(ScriptState* script_state) + : ScriptFunction(script_state) {} + + private: + void CallRaw(const v8::FunctionCallbackInfo<v8::Value>& args) override { + auto* isolate = args.GetIsolate(); + DCHECK_EQ(isolate, GetScriptState()->GetIsolate()); + auto context = GetScriptState()->GetContext(); + v8::Local<v8::Value> chunk; + if (args.Length() < 1) { + chunk = v8::Undefined(isolate); + } else { + chunk = args[0]; + } + + // https://streams.spec.whatwg.org/#blqs-size + // 1. Return ? GetV(chunk, "byteLength"). + + // https://tc39.es/ecma262/#sec-getv + // 1. Assert: IsPropertyKey(P) is true. + // 2. Let O be ? ToObject(V). + v8::Local<v8::Object> chunk_as_object; + if (!chunk->ToObject(context).ToLocal(&chunk_as_object)) { + // Should have thrown an exception, which will be caught further up the + // stack. + return; + } + // 3. Return ? O.[[Get]](P, V). + v8::Local<v8::Value> byte_length; + if (!chunk_as_object->Get(context, V8AtomicString(isolate, "byteLength")) + .ToLocal(&byte_length)) { + // Should have thrown an exception. + return; + } + args.GetReturnValue().Set(byte_length); + } +}; + +} // namespace + +ByteLengthQueuingStrategy* ByteLengthQueuingStrategy::Create( + ScriptState* script_state, + const QueuingStrategyInit* init) { + return MakeGarbageCollected<ByteLengthQueuingStrategy>(script_state, init); +} + +ByteLengthQueuingStrategy::ByteLengthQueuingStrategy( + ScriptState* script_state, + const QueuingStrategyInit* init) + : high_water_mark_(script_state->GetIsolate(), + HighWaterMarkOrUndefined(script_state, init)) {} + +ByteLengthQueuingStrategy::~ByteLengthQueuingStrategy() = default; + +ScriptValue ByteLengthQueuingStrategy::highWaterMark( + ScriptState* script_state) const { + return ScriptValue(script_state, + high_water_mark_.NewLocal(script_state->GetIsolate())); +} + +ScriptValue ByteLengthQueuingStrategy::size(ScriptState* script_state) const { + // We don't cache the result because normally this method will only be called + // once anyway. + return ScriptValue( + script_state, + ByteLengthQueuingStrategySizeFunction::CreateFunction(script_state)); +} + +void ByteLengthQueuingStrategy::Trace(Visitor* visitor) { + visitor->Trace(high_water_mark_); + ScriptWrappable::Trace(visitor); +} + +} // namespace blink diff --git a/chromium/third_party/blink/renderer/core/streams/byte_length_queuing_strategy.h b/chromium/third_party/blink/renderer/core/streams/byte_length_queuing_strategy.h new file mode 100644 index 00000000000..c1544212c8a --- /dev/null +++ b/chromium/third_party/blink/renderer/core/streams/byte_length_queuing_strategy.h @@ -0,0 +1,41 @@ +// Copyright 2019 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 THIRD_PARTY_BLINK_RENDERER_CORE_STREAMS_BYTE_LENGTH_QUEUING_STRATEGY_H_ +#define THIRD_PARTY_BLINK_RENDERER_CORE_STREAMS_BYTE_LENGTH_QUEUING_STRATEGY_H_ + +#include "third_party/blink/renderer/platform/bindings/script_wrappable.h" +#include "third_party/blink/renderer/platform/bindings/trace_wrapper_v8_reference.h" +#include "v8/include/v8.h" + +namespace blink { + +class QueuingStrategyInit; +class ScriptState; +class ScriptValue; +class Visitor; + +// https://streams.spec.whatwg.org/#blqs-class +class ByteLengthQueuingStrategy final : public ScriptWrappable { + DEFINE_WRAPPERTYPEINFO(); + + public: + static ByteLengthQueuingStrategy* Create(ScriptState*, + const QueuingStrategyInit*); + + ByteLengthQueuingStrategy(ScriptState*, const QueuingStrategyInit*); + ~ByteLengthQueuingStrategy() override; + + ScriptValue highWaterMark(ScriptState*) const; + ScriptValue size(ScriptState*) const; + + void Trace(Visitor*) override; + + private: + const TraceWrapperV8Reference<v8::Value> high_water_mark_; +}; + +} // namespace blink + +#endif // THIRD_PARTY_BLINK_RENDERER_CORE_STREAMS_BYTE_LENGTH_QUEUING_STRATEGY_H_ diff --git a/chromium/third_party/blink/renderer/core/streams/byte_length_queuing_strategy.idl b/chromium/third_party/blink/renderer/core/streams/byte_length_queuing_strategy.idl new file mode 100644 index 00000000000..af68396eb0e --- /dev/null +++ b/chromium/third_party/blink/renderer/core/streams/byte_length_queuing_strategy.idl @@ -0,0 +1,17 @@ +// Copyright 2019 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. + +// https://streams.spec.whatwg.org/#blqs-class + +[ + Exposed=(Window,Worker,Worklet), + RuntimeEnabled=StreamsNative, + ConstructorCallWith=ScriptState, + Constructor([PermissiveDictionaryConversion] QueuingStrategyInit init) +] interface ByteLengthQueuingStrategy { + [CallWith=ScriptState] readonly attribute any highWaterMark; + + // size is an accessor that returns a function. + [CallWith=ScriptState] readonly attribute any size; +}; diff --git a/chromium/third_party/blink/renderer/core/streams/count_queuing_strategy.cc b/chromium/third_party/blink/renderer/core/streams/count_queuing_strategy.cc new file mode 100644 index 00000000000..2b1aa3b9c24 --- /dev/null +++ b/chromium/third_party/blink/renderer/core/streams/count_queuing_strategy.cc @@ -0,0 +1,72 @@ +// Copyright 2019 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/count_queuing_strategy.h" + +#include "third_party/blink/renderer/bindings/core/v8/script_function.h" +#include "third_party/blink/renderer/bindings/core/v8/script_value.h" +#include "third_party/blink/renderer/core/streams/queuing_strategy_common.h" +#include "third_party/blink/renderer/core/streams/queuing_strategy_init.h" +#include "third_party/blink/renderer/platform/bindings/script_state.h" +#include "third_party/blink/renderer/platform/heap/visitor.h" + +namespace blink { + +namespace { + +class CountQueuingStrategySizeFunction final : public ScriptFunction { + public: + static v8::Local<v8::Function> CreateFunction(ScriptState* script_state) { + CountQueuingStrategySizeFunction* self = + MakeGarbageCollected<CountQueuingStrategySizeFunction>(script_state); + return self->BindToV8Function(); + } + + explicit CountQueuingStrategySizeFunction(ScriptState* script_state) + : ScriptFunction(script_state) {} + + private: + void CallRaw(const v8::FunctionCallbackInfo<v8::Value>& args) override { + // https://streams.spec.whatwg.org/#cqs-size + // 1. Return 1. + args.GetReturnValue().Set( + v8::Integer::New(GetScriptState()->GetIsolate(), 1)); + } +}; + +} // namespace + +CountQueuingStrategy* CountQueuingStrategy::Create( + ScriptState* script_state, + const QueuingStrategyInit* init) { + return MakeGarbageCollected<CountQueuingStrategy>(script_state, init); +} + +CountQueuingStrategy::CountQueuingStrategy(ScriptState* script_state, + const QueuingStrategyInit* init) + : high_water_mark_(script_state->GetIsolate(), + HighWaterMarkOrUndefined(script_state, init)) {} + +CountQueuingStrategy::~CountQueuingStrategy() = default; + +ScriptValue CountQueuingStrategy::highWaterMark( + ScriptState* script_state) const { + return ScriptValue(script_state, + high_water_mark_.NewLocal(script_state->GetIsolate())); +} + +ScriptValue CountQueuingStrategy::size(ScriptState* script_state) const { + // We don't cache the result because normally this method will only be called + // once anyway. + return ScriptValue( + script_state, + CountQueuingStrategySizeFunction::CreateFunction(script_state)); +} + +void CountQueuingStrategy::Trace(Visitor* visitor) { + visitor->Trace(high_water_mark_); + ScriptWrappable::Trace(visitor); +} + +} // namespace blink diff --git a/chromium/third_party/blink/renderer/core/streams/count_queuing_strategy.h b/chromium/third_party/blink/renderer/core/streams/count_queuing_strategy.h new file mode 100644 index 00000000000..246c4599d14 --- /dev/null +++ b/chromium/third_party/blink/renderer/core/streams/count_queuing_strategy.h @@ -0,0 +1,40 @@ +// Copyright 2019 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 THIRD_PARTY_BLINK_RENDERER_CORE_STREAMS_COUNT_QUEUING_STRATEGY_H_ +#define THIRD_PARTY_BLINK_RENDERER_CORE_STREAMS_COUNT_QUEUING_STRATEGY_H_ + +#include "third_party/blink/renderer/platform/bindings/script_wrappable.h" +#include "third_party/blink/renderer/platform/bindings/trace_wrapper_v8_reference.h" +#include "v8/include/v8.h" + +namespace blink { + +class QueuingStrategyInit; +class ScriptState; +class ScriptValue; +class Visitor; + +// https://streams.spec.whatwg.org/#blqs-class +class CountQueuingStrategy final : public ScriptWrappable { + DEFINE_WRAPPERTYPEINFO(); + + public: + static CountQueuingStrategy* Create(ScriptState*, const QueuingStrategyInit*); + + CountQueuingStrategy(ScriptState*, const QueuingStrategyInit*); + ~CountQueuingStrategy() override; + + ScriptValue highWaterMark(ScriptState*) const; + ScriptValue size(ScriptState*) const; + + void Trace(Visitor*) override; + + private: + const TraceWrapperV8Reference<v8::Value> high_water_mark_; +}; + +} // namespace blink + +#endif // THIRD_PARTY_BLINK_RENDERER_CORE_STREAMS_COUNT_QUEUING_STRATEGY_H_ diff --git a/chromium/third_party/blink/renderer/core/streams/count_queuing_strategy.idl b/chromium/third_party/blink/renderer/core/streams/count_queuing_strategy.idl new file mode 100644 index 00000000000..02cfe7a9715 --- /dev/null +++ b/chromium/third_party/blink/renderer/core/streams/count_queuing_strategy.idl @@ -0,0 +1,17 @@ +// Copyright 2019 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. + +// https://streams.spec.whatwg.org/#cqs-class + +[ + Exposed=(Window,Worker,Worklet), + RuntimeEnabled=StreamsNative, + ConstructorCallWith=ScriptState, + Constructor([PermissiveDictionaryConversion] QueuingStrategyInit init) +] interface CountQueuingStrategy { + [CallWith=ScriptState] readonly attribute any highWaterMark; + + // size is an accessor that returns a function. + [CallWith=ScriptState] readonly attribute any size; +}; diff --git a/chromium/third_party/blink/renderer/core/streams/miscellaneous_operations.h b/chromium/third_party/blink/renderer/core/streams/miscellaneous_operations.h index f490157e2e2..8d715102122 100644 --- a/chromium/third_party/blink/renderer/core/streams/miscellaneous_operations.h +++ b/chromium/third_party/blink/renderer/core/streams/miscellaneous_operations.h @@ -7,7 +7,7 @@ #include "third_party/blink/renderer/bindings/core/v8/script_value.h" #include "third_party/blink/renderer/core/core_export.h" -#include "third_party/blink/renderer/platform/wtf/allocator.h" +#include "third_party/blink/renderer/platform/wtf/allocator/allocator.h" #include "v8/include/v8.h" namespace blink { diff --git a/chromium/third_party/blink/renderer/core/streams/miscellaneous_operations_test.cc b/chromium/third_party/blink/renderer/core/streams/miscellaneous_operations_test.cc index c21a934ed4a..9a8e28b4895 100644 --- a/chromium/third_party/blink/renderer/core/streams/miscellaneous_operations_test.cc +++ b/chromium/third_party/blink/renderer/core/streams/miscellaneous_operations_test.cc @@ -97,7 +97,8 @@ v8::Local<v8::Value> CreateFromFunctionAndGetResult( int argc = 0, v8::Local<v8::Value> argv[] = nullptr) { String js = String("({start: ") + function_definition + "})" + '\0'; - ScriptValue underlying_value = EvalWithPrintingError(scope, js.Utf8().data()); + ScriptValue underlying_value = + EvalWithPrintingError(scope, js.Utf8().c_str()); auto underlying_object = underlying_value.V8Value().As<v8::Object>(); auto* algo = CreateAlgorithmFromUnderlyingMethod( scope->GetScriptState(), underlying_object, "start", diff --git a/chromium/third_party/blink/renderer/core/streams/queuing_strategy_common.cc b/chromium/third_party/blink/renderer/core/streams/queuing_strategy_common.cc new file mode 100644 index 00000000000..4441a896a38 --- /dev/null +++ b/chromium/third_party/blink/renderer/core/streams/queuing_strategy_common.cc @@ -0,0 +1,23 @@ +// Copyright 2019 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/queuing_strategy_common.h" + +#include "third_party/blink/renderer/core/streams/queuing_strategy_init.h" +#include "third_party/blink/renderer/platform/bindings/script_state.h" + +namespace blink { + +v8::Local<v8::Value> HighWaterMarkOrUndefined(ScriptState* script_state, + const QueuingStrategyInit* init) { + v8::Local<v8::Value> high_water_mark; + if (init->hasHighWaterMark()) { + high_water_mark = init->highWaterMark().V8Value(); + } else { + high_water_mark = v8::Undefined(script_state->GetIsolate()); + } + return high_water_mark; +} + +} // namespace blink diff --git a/chromium/third_party/blink/renderer/core/streams/queuing_strategy_common.h b/chromium/third_party/blink/renderer/core/streams/queuing_strategy_common.h new file mode 100644 index 00000000000..d62e6a866f1 --- /dev/null +++ b/chromium/third_party/blink/renderer/core/streams/queuing_strategy_common.h @@ -0,0 +1,20 @@ +// Copyright 2019 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 THIRD_PARTY_BLINK_RENDERER_CORE_STREAMS_QUEUING_STRATEGY_COMMON_H_ +#define THIRD_PARTY_BLINK_RENDERER_CORE_STREAMS_QUEUING_STRATEGY_COMMON_H_ + +#include "v8/include/v8.h" + +namespace blink { + +class ScriptState; +class QueuingStrategyInit; + +v8::Local<v8::Value> HighWaterMarkOrUndefined(ScriptState* script_state, + const QueuingStrategyInit* init); + +} // namespace blink + +#endif // THIRD_PARTY_BLINK_RENDERER_CORE_STREAMS_QUEUING_STRATEGY_COMMON_H_ diff --git a/chromium/third_party/blink/renderer/core/streams/queuing_strategy_init.idl b/chromium/third_party/blink/renderer/core/streams/queuing_strategy_init.idl new file mode 100644 index 00000000000..7d5bd0c511e --- /dev/null +++ b/chromium/third_party/blink/renderer/core/streams/queuing_strategy_init.idl @@ -0,0 +1,10 @@ +// Copyright 2019 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. + +// https://streams.spec.whatwg.org/#cqs-class + +[PermissiveDictionaryConversion] +dictionary QueuingStrategyInit { + any highWaterMark; +}; diff --git a/chromium/third_party/blink/renderer/core/streams/readable_stream.cc b/chromium/third_party/blink/renderer/core/streams/readable_stream.cc index 8b787d62af1..e1088481c4a 100644 --- a/chromium/third_party/blink/renderer/core/streams/readable_stream.cc +++ b/chromium/third_party/blink/renderer/core/streams/readable_stream.cc @@ -60,11 +60,9 @@ ReadableStream* ReadableStream::CreateWithCountQueueingStrategy( ReadableStream* ReadableStream::Deserialize(ScriptState* script_state, MessagePort* port, ExceptionState& exception_state) { - // TODO(ricea): Implementation serialization for the native implementation. if (RuntimeEnabledFeatures::StreamsNativeEnabled()) { - exception_state.ThrowTypeError( - "serialization disabled because StreamsNative feature is enabled"); - return nullptr; + return ReadableStreamNative::Deserialize(script_state, port, + exception_state); } return ReadableStreamWrapper::Deserialize(script_state, port, diff --git a/chromium/third_party/blink/renderer/core/streams/readable_stream_default_controller.cc b/chromium/third_party/blink/renderer/core/streams/readable_stream_default_controller.cc index b5fca428366..653cab070a3 100644 --- a/chromium/third_party/blink/renderer/core/streams/readable_stream_default_controller.cc +++ b/chromium/third_party/blink/renderer/core/streams/readable_stream_default_controller.cc @@ -271,7 +271,6 @@ void ReadableStreamDefaultController::Trace(Visitor* visitor) { visitor->Trace(pull_algorithm_); visitor->Trace(queue_); visitor->Trace(strategy_size_algorithm_); - visitor->Trace(lock_notify_target_); ScriptWrappable::Trace(visitor); } @@ -494,7 +493,6 @@ void ReadableStreamDefaultController::SetUp( StreamAlgorithm* cancel_algorithm, double high_water_mark, StrategySizeAlgorithm* size_algorithm, - bool enable_blink_lock_notifications, ExceptionState& exception_state) { // https://streams.spec.whatwg.org/#set-up-readable-stream-default-controller // 1. Assert: stream.[[readableStreamController]] is undefined. @@ -510,10 +508,6 @@ void ReadableStreamDefaultController::SetUp( DCHECK(controller->queue_->IsEmpty()); DCHECK_EQ(controller->queue_->TotalSize(), 0); - // Not part of the standard. - controller->enable_blink_lock_notifications_ = - enable_blink_lock_notifications; - // 5. Set controller.[[strategySizeAlgorithm]] to sizeAlgorithm and // controller.[[strategyHWM]] to highWaterMark. controller->strategy_size_algorithm_ = size_algorithm; @@ -610,7 +604,6 @@ void ReadableStreamDefaultController::SetUpFromUnderlyingSource( v8::Local<v8::Object> underlying_source, double high_water_mark, StrategySizeAlgorithm* size_algorithm, - bool enable_blink_lock_notifications, ExceptionState& exception_state) { // https://streams.spec.whatwg.org/#set-up-readable-stream-default-controller-from-underlying-source // 2. Let controller be ObjectCreate(the original value of @@ -646,18 +639,11 @@ void ReadableStreamDefaultController::SetUpFromUnderlyingSource( return; } - // TODO(ricea): Remove this once C++ API has been updated. - if (enable_blink_lock_notifications) { - controller->lock_notify_target_.Set(script_state->GetIsolate(), - underlying_source); - } - // 6. Perform ? SetUpReadableStreamDefaultController(stream, controller, // startAlgorithm, pullAlgorithm, cancelAlgorithm, highWaterMark, // sizeAlgorithm). SetUp(script_state, stream, controller, start_algorithm, pull_algorithm, - cancel_algorithm, high_water_mark, size_algorithm, - enable_blink_lock_notifications, exception_state); + cancel_algorithm, high_water_mark, size_algorithm, exception_state); } } // namespace blink diff --git a/chromium/third_party/blink/renderer/core/streams/readable_stream_default_controller.h b/chromium/third_party/blink/renderer/core/streams/readable_stream_default_controller.h index 14e8becff8f..0a14d943b82 100644 --- a/chromium/third_party/blink/renderer/core/streams/readable_stream_default_controller.h +++ b/chromium/third_party/blink/renderer/core/streams/readable_stream_default_controller.h @@ -100,7 +100,6 @@ class ReadableStreamDefaultController : public ScriptWrappable { StreamAlgorithm* cancel_algorithm, double high_water_mark, StrategySizeAlgorithm* size_algorithm, - bool enable_blink_lock_notifications, ExceptionState&); // https://streams.spec.whatwg.org/#set-up-readable-stream-default-controller-from-underlying-source @@ -109,7 +108,6 @@ class ReadableStreamDefaultController : public ScriptWrappable { v8::Local<v8::Object> underlying_source, double high_water_mark, StrategySizeAlgorithm* size_algorithm, - bool enable_blink_lock_notifications, ExceptionState&); // Boolean flags are grouped together to reduce object size. Verbs have been @@ -118,14 +116,12 @@ class ReadableStreamDefaultController : public ScriptWrappable { bool will_pull_again_ = false; bool is_pulling_ = false; bool is_started_ = false; - bool enable_blink_lock_notifications_ = false; Member<StreamAlgorithm> cancel_algorithm_; Member<ReadableStreamNative> controlled_readable_stream_; Member<StreamAlgorithm> pull_algorithm_; Member<QueueWithSizes> queue_; double strategy_high_water_mark_ = 0.0; Member<StrategySizeAlgorithm> strategy_size_algorithm_; - TraceWrapperV8Reference<v8::Object> lock_notify_target_; }; } // namespace blink diff --git a/chromium/third_party/blink/renderer/core/streams/readable_stream_default_controller_interface.cc b/chromium/third_party/blink/renderer/core/streams/readable_stream_default_controller_interface.cc index 6abdedafe86..91fe704295a 100644 --- a/chromium/third_party/blink/renderer/core/streams/readable_stream_default_controller_interface.cc +++ b/chromium/third_party/blink/renderer/core/streams/readable_stream_default_controller_interface.cc @@ -31,8 +31,6 @@ class ReadableStreamDefaultControllerWrapper final // (close/desiredSize/enqueue/error will become no-ops afterward.) void NoteHasBeenCanceled() override { js_controller_.Clear(); } - bool IsActive() const override { return !js_controller_.IsEmpty(); } - void Close() override { ScriptState* script_state = script_state_; // This will assert that the context is valid; do not call this method when @@ -129,8 +127,6 @@ class ReadableStreamDefaultControllerNative final void NoteHasBeenCanceled() override { controller_ = nullptr; } - bool IsActive() const override { return controller_; } - void Close() override { if (!controller_) return; @@ -138,6 +134,7 @@ class ReadableStreamDefaultControllerNative final ScriptState::Scope scope(script_state_); ReadableStreamDefaultController::Close(script_state_, controller_); + controller_ = nullptr; } double DesiredSize() const override { @@ -173,6 +170,7 @@ class ReadableStreamDefaultControllerNative final ReadableStreamDefaultController::Error(script_state_, controller_, js_error); + controller_ = nullptr; } void Trace(Visitor* visitor) override { diff --git a/chromium/third_party/blink/renderer/core/streams/readable_stream_default_controller_interface.h b/chromium/third_party/blink/renderer/core/streams/readable_stream_default_controller_interface.h index bfdb80447cb..7d859463d8c 100644 --- a/chromium/third_party/blink/renderer/core/streams/readable_stream_default_controller_interface.h +++ b/chromium/third_party/blink/renderer/core/streams/readable_stream_default_controller_interface.h @@ -33,7 +33,6 @@ class CORE_EXPORT ReadableStreamDefaultControllerInterface // (Close/DesiredSize/Enqueue/Error will become no-ops afterward.) virtual void NoteHasBeenCanceled() = 0; - virtual bool IsActive() const = 0; virtual void Close() = 0; virtual double DesiredSize() const = 0; virtual void Enqueue(v8::Local<v8::Value> js_chunk) const = 0; diff --git a/chromium/third_party/blink/renderer/core/streams/readable_stream_native.cc b/chromium/third_party/blink/renderer/core/streams/readable_stream_native.cc index 4567ec4b712..632ea367d68 100644 --- a/chromium/third_party/blink/renderer/core/streams/readable_stream_native.cc +++ b/chromium/third_party/blink/renderer/core/streams/readable_stream_native.cc @@ -8,7 +8,6 @@ #include "third_party/blink/renderer/bindings/core/v8/script_function.h" #include "third_party/blink/renderer/bindings/core/v8/v8_iterator_result_value.h" #include "third_party/blink/renderer/core/execution_context/execution_context.h" -#include "third_party/blink/renderer/core/frame/use_counter.h" #include "third_party/blink/renderer/core/frame/web_feature.h" #include "third_party/blink/renderer/core/streams/miscellaneous_operations.h" #include "third_party/blink/renderer/core/streams/promise_handler.h" @@ -16,6 +15,7 @@ #include "third_party/blink/renderer/core/streams/readable_stream_reader.h" #include "third_party/blink/renderer/core/streams/stream_algorithms.h" #include "third_party/blink/renderer/core/streams/stream_promise_resolver.h" +#include "third_party/blink/renderer/core/streams/transferable_streams.h" #include "third_party/blink/renderer/core/streams/underlying_source_base.h" #include "third_party/blink/renderer/core/streams/writable_stream_default_controller.h" #include "third_party/blink/renderer/core/streams/writable_stream_default_writer.h" @@ -25,18 +25,12 @@ #include "third_party/blink/renderer/platform/bindings/v8_binding.h" #include "third_party/blink/renderer/platform/heap/garbage_collected.h" #include "third_party/blink/renderer/platform/heap/visitor.h" +#include "third_party/blink/renderer/platform/instrumentation/use_counter.h" #include "third_party/blink/renderer/platform/wtf/assertions.h" #include "third_party/blink/renderer/platform/wtf/deque.h" namespace blink { -struct ReadableStreamNative::PipeOptions { - PipeOptions() = default; - bool prevent_close = false; - bool prevent_abort = false; - bool prevent_cancel = false; -}; - // PipeToEngine implements PipeTo(). All standard steps in this class come from // https://streams.spec.whatwg.org/#readable-stream-pipe-to // @@ -1053,8 +1047,7 @@ ReadableStreamNative* ReadableStreamNative::Create( // sizeAlgorithm). ReadableStreamDefaultController::SetUp( script_state, stream, controller, start_algorithm, pull_algorithm, - cancel_algorithm, high_water_mark, size_algorithm, false, - exception_state); + cancel_algorithm, high_water_mark, size_algorithm, exception_state); if (exception_state.HadException()) { return nullptr; } @@ -1065,14 +1058,12 @@ ReadableStreamNative* ReadableStreamNative::Create( ReadableStreamNative::ReadableStreamNative() = default; -// TODO(ricea): Remove |enable_blink_lock_notifications| once -// blink::ReadableStreamOperations has been updated to use CreateReadableStream. ReadableStreamNative::ReadableStreamNative(ScriptState* script_state, ScriptValue raw_underlying_source, ScriptValue raw_strategy, - bool enable_blink_lock_notifications, + bool created_by_ua, ExceptionState& exception_state) { - if (!enable_blink_lock_notifications) { + if (!created_by_ua) { // TODO(ricea): Move this to IDL once blink::ReadableStreamOperations is // no longer using the public constructor. UseCounter::Count(ExecutionContext::From(script_state), @@ -1157,7 +1148,7 @@ ReadableStreamNative::ReadableStreamNative(ScriptState* script_state, // (this, underlyingSource, highWaterMark, sizeAlgorithm). ReadableStreamDefaultController::SetUpFromUnderlyingSource( script_state, this, underlying_source, high_water_mark, size_algorithm, - enable_blink_lock_notifications, exception_state); + exception_state); } ReadableStreamNative::~ReadableStreamNative() = default; @@ -1394,7 +1385,43 @@ void ReadableStreamNative::LockAndDisturb(ScriptState* script_state, void ReadableStreamNative::Serialize(ScriptState* script_state, MessagePort* port, ExceptionState& exception_state) { - // TODO(ricea): Implement this. + if (IsLocked(this)) { + exception_state.ThrowTypeError("Cannot transfer a locked stream"); + return; + } + + auto* writable = + CreateCrossRealmTransformWritable(script_state, port, exception_state); + if (exception_state.HadException()) { + return; + } + + auto promise = PipeTo(script_state, this, writable, PipeOptions()); + promise.MarkAsHandled(); +} + +ReadableStreamNative* ReadableStreamNative::Deserialize( + ScriptState* script_state, + MessagePort* port, + ExceptionState& exception_state) { + // We need to execute JavaScript to call "Then" on v8::Promises. We will not + // run author code. + v8::Isolate::AllowJavascriptExecutionScope allow_js( + script_state->GetIsolate()); + auto* readable = + CreateCrossRealmTransformReadable(script_state, port, exception_state); + if (exception_state.HadException()) { + return nullptr; + } + return readable; +} + +ScriptPromise ReadableStreamNative::PipeTo(ScriptState* script_state, + ReadableStreamNative* readable, + WritableStreamNative* destination, + PipeOptions pipe_options) { + auto* engine = MakeGarbageCollected<PipeToEngine>(script_state, pipe_options); + return engine->Start(readable, destination); } v8::Local<v8::Value> ReadableStreamNative::GetStoredError( @@ -1409,14 +1436,6 @@ void ReadableStreamNative::Trace(Visitor* visitor) { ReadableStream::Trace(visitor); } -ScriptPromise ReadableStreamNative::PipeTo(ScriptState* script_state, - ReadableStreamNative* readable, - WritableStreamNative* destination, - PipeOptions pipe_options) { - auto* engine = MakeGarbageCollected<PipeToEngine>(script_state, pipe_options); - return engine->Start(readable, destination); -} - // // Abstract Operations Used By Controllers // diff --git a/chromium/third_party/blink/renderer/core/streams/readable_stream_native.h b/chromium/third_party/blink/renderer/core/streams/readable_stream_native.h index b2af17d437e..ee1b87d9d9b 100644 --- a/chromium/third_party/blink/renderer/core/streams/readable_stream_native.h +++ b/chromium/third_party/blink/renderer/core/streams/readable_stream_native.h @@ -30,6 +30,13 @@ class WritableStreamNative; // See https://streams.spec.whatwg.org/#rs-model for background. class ReadableStreamNative : public ReadableStream { public: + struct PipeOptions { + PipeOptions() = default; + bool prevent_close = false; + bool prevent_abort = false; + bool prevent_cancel = false; + }; + enum State : uint8_t { kReadable, kClosed, kErrored }; // Implements ReadableStream::Create() when this implementation is enabled. @@ -60,14 +67,11 @@ class ReadableStreamNative : public ReadableStream { ReadableStreamNative(); - // TODO(ricea): Remove |enable_blink_lock_notifications| once - // blink::ReadableStreamOperations has been updated to use - // CreateReadableStream. // https://streams.spec.whatwg.org/#rs-constructor ReadableStreamNative(ScriptState*, ScriptValue raw_underlying_source, ScriptValue raw_strategy, - bool enable_blink_lock_notifications, + bool created_by_ua, ExceptionState&); ~ReadableStreamNative() override; @@ -146,6 +150,10 @@ class ReadableStreamNative : public ReadableStream { void Serialize(ScriptState*, MessagePort* port, ExceptionState&) override; + static ReadableStreamNative* Deserialize(ScriptState*, + MessagePort* port, + ExceptionState&); + bool IsBroken() const override { return false; } // @@ -162,6 +170,12 @@ class ReadableStreamNative : public ReadableStream { return stream->reader_; } + // https://streams.spec.whatwg.org/#readable-stream-pipe-to + static ScriptPromise PipeTo(ScriptState*, + ReadableStreamNative*, + WritableStreamNative*, + PipeOptions); + // // Functions exported for use by TransformStream. Not part of the standard. // @@ -190,7 +204,6 @@ class ReadableStreamNative : public ReadableStream { friend class ReadableStreamDefaultController; friend class ReadableStreamReader; - struct PipeOptions; class PipeToEngine; class ReadHandleImpl; class TeeEngine; @@ -204,12 +217,6 @@ class ReadableStreamNative : public ReadableStream { bool for_author_code, ExceptionState&); - // https://streams.spec.whatwg.org/#readable-stream-pipe-to - static ScriptPromise PipeTo(ScriptState*, - ReadableStreamNative*, - WritableStreamNative*, - PipeOptions); - // https://streams.spec.whatwg.org/#readable-stream-add-read-request static StreamPromiseResolver* AddReadRequest(ScriptState*, ReadableStreamNative*); diff --git a/chromium/third_party/blink/renderer/core/streams/readable_stream_operations_test.cc b/chromium/third_party/blink/renderer/core/streams/readable_stream_operations_test.cc index 605a1b46c80..8f77a2bfc43 100644 --- a/chromium/third_party/blink/renderer/core/streams/readable_stream_operations_test.cc +++ b/chromium/third_party/blink/renderer/core/streams/readable_stream_operations_test.cc @@ -24,6 +24,7 @@ #include "third_party/blink/renderer/platform/bindings/v8_throw_exception.h" #include "third_party/blink/renderer/platform/heap/handle.h" #include "third_party/blink/renderer/platform/runtime_enabled_features.h" +#include "third_party/blink/renderer/platform/testing/runtime_enabled_features_test_helpers.h" #include "third_party/blink/renderer/platform/testing/unit_test_helpers.h" #include "v8/include/v8.h" @@ -131,6 +132,8 @@ ScriptValue CheckedGetInternalStream(ScriptState* script_state, } TEST(ReadableStreamOperationsTest, IsReadableStream) { + ScopedStreamsNativeForTest enabled(false); + V8TestingScope scope; TryCatchScope try_catch_scope(scope.GetIsolate()); EXPECT_FALSE(ReadableStreamOperations::IsReadableStream( @@ -167,6 +170,8 @@ TEST(ReadableStreamOperationsTest, IsReadableStream) { } TEST(ReadableStreamOperationsTest, IsReadableStreamDefaultReaderInvalid) { + ScopedStreamsNativeForTest enabled(false); + V8TestingScope scope; TryCatchScope try_catch_scope(scope.GetIsolate()); EXPECT_FALSE(ReadableStreamOperations::IsReadableStreamDefaultReader( @@ -195,6 +200,8 @@ TEST(ReadableStreamOperationsTest, IsReadableStreamDefaultReaderInvalid) { } TEST(ReadableStreamOperationsTest, GetReader) { + ScopedStreamsNativeForTest enabled(false); + V8TestingScope scope; TryCatchScope try_catch_scope(scope.GetIsolate()); auto* stream = @@ -232,6 +239,8 @@ TEST(ReadableStreamOperationsTest, GetReader) { } TEST(ReadableStreamOperationsTest, IsDisturbed) { + ScopedStreamsNativeForTest enabled(false); + V8TestingScope scope; TryCatchScope try_catch_scope(scope.GetIsolate()); auto* stream = @@ -252,6 +261,8 @@ TEST(ReadableStreamOperationsTest, IsDisturbed) { } TEST(ReadableStreamOperationsTest, Read) { + ScopedStreamsNativeForTest enabled(false); + V8TestingScope scope; TryCatchScope try_catch_scope(scope.GetIsolate()); ScriptValue reader = @@ -301,6 +312,8 @@ TEST(ReadableStreamOperationsTest, Read) { TEST(ReadableStreamOperationsTest, CreateReadableStreamWithCustomUnderlyingSourceAndStrategy) { + ScopedStreamsNativeForTest enabled(false); + V8TestingScope scope; TryCatchScope try_catch_scope(scope.GetIsolate()); auto* underlying_source = @@ -367,47 +380,9 @@ TEST(ReadableStreamOperationsTest, EXPECT_TRUE(it3->IsDone()); } -TEST(ReadableStreamOperationsTest, - UnderlyingSourceShouldHavePendingActivityWhenLockedAndControllerIsActive) { - V8TestingScope scope; - TryCatchScope try_catch_scope(scope.GetIsolate()); - auto* underlying_source = - MakeGarbageCollected<TestUnderlyingSource>(scope.GetScriptState()); - - ScriptValue strategy = ReadableStreamOperations::CreateCountQueuingStrategy( - scope.GetScriptState(), 10); - ASSERT_FALSE(strategy.IsEmpty()); - - ScriptValue internal_stream = ReadableStreamOperations::CreateReadableStream( - scope.GetScriptState(), underlying_source, strategy); - ASSERT_FALSE(internal_stream.IsEmpty()); - - CHECK(!RuntimeEnabledFeatures::StreamsNativeEnabled()); - auto* stream = ReadableStreamWrapper::CreateFromInternalStream( - scope.GetScriptState(), internal_stream, ASSERT_NO_EXCEPTION); - ASSERT_TRUE(stream); - - v8::Local<v8::Object> global = scope.GetScriptState()->GetContext()->Global(); - ASSERT_TRUE(global - ->Set(scope.GetContext(), - V8String(scope.GetIsolate(), "stream"), - ToV8(stream, scope.GetScriptState())) - .IsJust()); - - EXPECT_FALSE(underlying_source->HasPendingActivity()); - EvalWithPrintingError(&scope, "let reader = stream.getReader();"); - EXPECT_TRUE(underlying_source->HasPendingActivity()); - EvalWithPrintingError(&scope, "reader.releaseLock();"); - EXPECT_FALSE(underlying_source->HasPendingActivity()); - EvalWithPrintingError(&scope, "reader = stream.getReader();"); - EXPECT_TRUE(underlying_source->HasPendingActivity()); - underlying_source->Enqueue( - ScriptValue(scope.GetScriptState(), v8::Undefined(scope.GetIsolate()))); - underlying_source->Close(); - EXPECT_FALSE(underlying_source->HasPendingActivity()); -} - TEST(ReadableStreamOperationsTest, IsReadable) { + ScopedStreamsNativeForTest enabled(false); + V8TestingScope scope; TryCatchScope try_catch_scope(scope.GetIsolate()); @@ -448,6 +423,8 @@ TEST(ReadableStreamOperationsTest, IsReadable) { } TEST(ReadableStreamOperationsTest, IsClosed) { + ScopedStreamsNativeForTest enabled(false); + V8TestingScope scope; TryCatchScope try_catch_scope(scope.GetIsolate()); @@ -488,6 +465,8 @@ TEST(ReadableStreamOperationsTest, IsClosed) { } TEST(ReadableStreamOperationsTest, IsErrored) { + ScopedStreamsNativeForTest enabled(false); + V8TestingScope scope; TryCatchScope try_catch_scope(scope.GetIsolate()); @@ -528,6 +507,8 @@ TEST(ReadableStreamOperationsTest, IsErrored) { } TEST(ReadableStreamOperationsTest, Tee) { + ScopedStreamsNativeForTest enabled(false); + V8TestingScope scope; TryCatchScope try_catch_scope(scope.GetIsolate()); v8::Local<v8::Context> context = scope.GetScriptState()->GetContext(); @@ -599,7 +580,8 @@ TEST(ReadableStreamOperationsTest, Tee) { } TEST(ReadableStreamOperationsTest, Serialize) { - RuntimeEnabledFeatures::SetTransferableStreamsEnabled(true); + ScopedStreamsNativeForTest streams_native_enabled(false); + ScopedTransferableStreamsForTest transferable_streams_enabled(true); V8TestingScope scope; TryCatchScope try_catch_scope(scope.GetIsolate()); diff --git a/chromium/third_party/blink/renderer/core/streams/readable_stream_reader.cc b/chromium/third_party/blink/renderer/core/streams/readable_stream_reader.cc index 7396853b466..66b5a1a999b 100644 --- a/chromium/third_party/blink/renderer/core/streams/readable_stream_reader.cc +++ b/chromium/third_party/blink/renderer/core/streams/readable_stream_reader.cc @@ -170,16 +170,6 @@ void ReadableStreamReader::GenericRelease(ScriptState* script_state, DCHECK_EQ(reader->owner_readable_stream_->reader_, reader); auto* isolate = script_state->GetIsolate(); - // TODO(yhirano): Remove this when we don"t need hasPendingActivity in - // blink::UnderlyingSourceBase. - ReadableStreamDefaultController* controller = - reader->owner_readable_stream_->readable_stream_controller_; - if (controller->enable_blink_lock_notifications_) { - // The stream is created with an external controller (i.e. made in - // Blink). - auto lock_notify_target = controller->lock_notify_target_.NewLocal(isolate); - CallNullaryMethod(script_state, lock_notify_target, "notifyLockReleased"); - } // 3. If reader.[[ownerReadableStream]].[[state]] is "readable", reject // reader.[[closedPromise]] with a TypeError exception. @@ -237,17 +227,6 @@ void ReadableStreamReader::GenericInitialize(ScriptState* script_state, ReadableStreamReader* reader, ReadableStreamNative* stream) { auto* isolate = script_state->GetIsolate(); - // TODO(yhirano): Remove this when we don't need hasPendingActivity in - // blink::UnderlyingSourceBase. - ReadableStreamDefaultController* controller = - stream->readable_stream_controller_; - if (controller->enable_blink_lock_notifications_) { - // The stream is created with an external controller (i.e. made in - // Blink). - v8::Local<v8::Object> lock_notify_target = - controller->lock_notify_target_.NewLocal(isolate); - CallNullaryMethod(script_state, lock_notify_target, "notifyLockAcquired"); - } // https://streams.spec.whatwg.org/#readable-stream-reader-generic-initialize // 1. Set reader.[[forAuthorCode]] to true. @@ -290,32 +269,4 @@ void ReadableStreamReader::GenericInitialize(ScriptState* script_state, } } -void ReadableStreamReader::CallNullaryMethod(ScriptState* script_state, - v8::Local<v8::Object> object, - const char* method_name) { - auto* isolate = script_state->GetIsolate(); - auto context = script_state->GetContext(); - v8::TryCatch try_catch(isolate); - v8::Local<v8::Value> method; - if (!object->Get(context, V8AtomicString(isolate, method_name)) - .ToLocal(&method)) { - DLOG(WARNING) << "Ignored failed lookup of '" << method_name - << "' in CallNullaryMethod"; - return; - } - - if (!method->IsFunction()) { - DLOG(WARNING) << "Didn't call '" << method_name - << "' in CallNullaryMethod because it was the wrong type"; - return; - } - - v8::MaybeLocal<v8::Value> result = - method.As<v8::Function>()->Call(context, object, 0, nullptr); - if (result.IsEmpty()) { - DLOG(WARNING) << "Ignored failure of '" << method_name - << "' in CallNullaryMethod"; - } -} - } // namespace blink diff --git a/chromium/third_party/blink/renderer/core/streams/readable_stream_reader.h b/chromium/third_party/blink/renderer/core/streams/readable_stream_reader.h index 3d5f8d3e7c5..ba10befa61e 100644 --- a/chromium/third_party/blink/renderer/core/streams/readable_stream_reader.h +++ b/chromium/third_party/blink/renderer/core/streams/readable_stream_reader.h @@ -6,6 +6,7 @@ #define THIRD_PARTY_BLINK_RENDERER_CORE_STREAMS_READABLE_STREAM_READER_H_ #include "third_party/blink/renderer/bindings/core/v8/script_value.h" +#include "third_party/blink/renderer/core/core_export.h" #include "third_party/blink/renderer/platform/bindings/script_wrappable.h" #include "v8/include/v8.h" @@ -25,7 +26,7 @@ class Visitor; // with the standard, ReadableStreamDefaultReader is implemented by the // ReadableStreamReader class. // TODO(ricea): Refactor this when implementing ReadableStreamBYOBReader. -class ReadableStreamReader : public ScriptWrappable { +class CORE_EXPORT ReadableStreamReader : public ScriptWrappable { DEFINE_WRAPPERTYPEINFO(); public: @@ -81,12 +82,6 @@ class ReadableStreamReader : public ScriptWrappable { ReadableStreamReader*, ReadableStreamNative*); - // Calls method |method_name| on |object|, passing no arguments, and ignoring - // errors. Used for Blink lock notifications. - static void CallNullaryMethod(ScriptState*, - v8::Local<v8::Object> object, - const char* method_name); - Member<StreamPromiseResolver> closed_promise_; bool for_author_code_ = true; Member<ReadableStreamNative> owner_readable_stream_; diff --git a/chromium/third_party/blink/renderer/core/streams/readable_stream_test.cc b/chromium/third_party/blink/renderer/core/streams/readable_stream_test.cc index 8347364aea2..2217f829919 100644 --- a/chromium/third_party/blink/renderer/core/streams/readable_stream_test.cc +++ b/chromium/third_party/blink/renderer/core/streams/readable_stream_test.cc @@ -410,13 +410,7 @@ TEST_P(ReadableStreamTest, LockAndDisturb) { } TEST_P(ReadableStreamTest, Serialize) { - if (GetParam()) { - // Serialize() is not yet supported in the C++ implementation. - return; - } - ScopedTransferableStreamsForTest enabled(true); - RuntimeEnabledFeatures::SetTransferableStreamsEnabled(true); V8TestingScope scope; auto* script_state = scope.GetScriptState(); diff --git a/chromium/third_party/blink/renderer/core/streams/transferable_streams.cc b/chromium/third_party/blink/renderer/core/streams/transferable_streams.cc new file mode 100644 index 00000000000..82efecbc340 --- /dev/null +++ b/chromium/third_party/blink/renderer/core/streams/transferable_streams.cc @@ -0,0 +1,878 @@ +// Copyright 2019 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. + +// Functions for transferable streams. See design doc +// https://docs.google.com/document/d/1_KuZzg5c3pncLJPFa8SuVm23AP4tft6mzPCL5at3I9M/edit + +#include "third_party/blink/renderer/core/streams/transferable_streams.h" + +#include "base/stl_util.h" +#include "third_party/blink/renderer/bindings/core/v8/to_v8_for_core.h" +#include "third_party/blink/renderer/bindings/core/v8/v8_dom_exception.h" +#include "third_party/blink/renderer/core/dom/dom_exception.h" +#include "third_party/blink/renderer/core/dom/events/native_event_listener.h" +#include "third_party/blink/renderer/core/events/message_event.h" +#include "third_party/blink/renderer/core/messaging/message_port.h" +#include "third_party/blink/renderer/core/messaging/post_message_options.h" +#include "third_party/blink/renderer/core/streams/miscellaneous_operations.h" +#include "third_party/blink/renderer/core/streams/promise_handler.h" +#include "third_party/blink/renderer/core/streams/readable_stream_default_controller.h" +#include "third_party/blink/renderer/core/streams/readable_stream_native.h" +#include "third_party/blink/renderer/core/streams/stream_algorithms.h" +#include "third_party/blink/renderer/core/streams/stream_promise_resolver.h" +#include "third_party/blink/renderer/core/streams/writable_stream_default_controller.h" +#include "third_party/blink/renderer/core/streams/writable_stream_native.h" +#include "third_party/blink/renderer/platform/bindings/exception_state.h" +#include "third_party/blink/renderer/platform/bindings/script_state.h" +#include "third_party/blink/renderer/platform/bindings/v8_binding.h" +#include "third_party/blink/renderer/platform/heap/heap.h" +#include "third_party/blink/renderer/platform/heap/visitor.h" +#include "third_party/blink/renderer/platform/wtf/assertions.h" +#include "v8/include/v8.h" + +// See the design doc at +// https://docs.google.com/document/d/1_KuZzg5c3pncLJPFa8SuVm23AP4tft6mzPCL5at3I9M/edit +// for explanation of how transferable streams are constructed from the "cross +// realm identity transform" implemented in this file. + +// The peer (the other end of the MessagePort) is untrusted as it may be +// compromised. This means we have to be very careful in unpacking the messages +// from the peer. LOG(WARNING) is used for cases where a message from the peer +// appears to be invalid. If this appears during ordinary testing it indicates a +// bug. +// +// The -vmodule=transferable_streams=3 command-line argument can be used for +// debugging of the protocol. + +namespace blink { + +namespace { + +// These are the types of messages that are sent between peers. +enum class MessageType { kPull, kCancel, kChunk, kClose, kAbort, kError }; + +// These are the different ways an error reason can be encoded. +enum class ErrorType { kTypeError, kJson, kDomException, kUndefined }; + +bool IsATypeError(ScriptState* script_state, v8::Local<v8::Object> object) { + // There isn't a 100% reliable way to identify a TypeError. + return object->IsNativeError() && + object->GetConstructorName() + ->Equals(script_state->GetContext(), + V8AtomicString(script_state->GetIsolate(), "TypeError")) + .ToChecked(); +} + +bool IsADOMException(v8::Isolate* isolate, v8::Local<v8::Object> object) { + return V8DOMException::HasInstance(object, isolate); +} + +// Creates a JavaScript object with a null prototype structured like {key1: +// value2, key2: value2}. This is used to create objects to be serialized by +// postMessage. +v8::Local<v8::Object> CreateKeyValueObject(v8::Isolate* isolate, + const char* key1, + v8::Local<v8::Value> value1, + const char* key2, + v8::Local<v8::Value> value2) { + v8::Local<v8::Name> names[] = {V8AtomicString(isolate, key1), + V8AtomicString(isolate, key2)}; + v8::Local<v8::Value> values[] = {value1, value2}; + static_assert(base::size(names) == base::size(values), + "names and values arrays must be the same size"); + return v8::Object::New(isolate, v8::Null(isolate), names, values, + base::size(names)); +} + +// Unpacks an object created by CreateKeyValueObject(). |value1| and |value2| +// are out parameters. Returns false on failure. +bool UnpackKeyValueObject(ScriptState* script_state, + v8::Local<v8::Object> object, + const char* key1, + v8::Local<v8::Value>* value1, + const char* key2, + v8::Local<v8::Value>* value2) { + auto* isolate = script_state->GetIsolate(); + v8::TryCatch try_catch(isolate); + auto context = script_state->GetContext(); + if (!object->Get(context, V8AtomicString(isolate, key1)).ToLocal(value1)) { + DLOG(WARNING) << "Error reading key: '" << key1 << "'"; + return false; + } + if (!object->Get(context, V8AtomicString(isolate, key2)).ToLocal(value2)) { + DLOG(WARNING) << "Error reading key: '" << key2 << "'"; + return false; + } + return true; +} + +// Send a message with type |type| and contents |value| over |port|. The type +// will be packed as a number with key "t", and the value will be packed with +// key "v". +void PackAndPostMessage(ScriptState* script_state, + MessagePort* port, + MessageType type, + v8::Local<v8::Value> value, + ExceptionState& exception_state) { + DVLOG(3) << "PackAndPostMessage sending message type " + << static_cast<int>(type); + auto* isolate = script_state->GetIsolate(); + v8::Local<v8::Object> packed = CreateKeyValueObject( + isolate, "t", v8::Number::New(isolate, static_cast<int>(type)), "v", + value); + port->postMessage(script_state, ScriptValue(script_state, packed), + PostMessageOptions::Create(), exception_state); +} + +// Packs an error into an {e: number, s: string} object for transmission by +// postMessage. Serializing the resulting object should never fail. +v8::Local<v8::Object> PackErrorType(v8::Isolate* isolate, + ErrorType type, + v8::Local<v8::String> string) { + auto error_as_number = v8::Number::New(isolate, static_cast<int>(type)); + return CreateKeyValueObject(isolate, "e", error_as_number, "s", string); +} + +// Overload for the common case where |string| is a compile-time constant. +v8::Local<v8::Object> PackErrorType(v8::Isolate* isolate, + ErrorType type, + const char* string) { + return PackErrorType(isolate, type, V8String(isolate, string)); +} + +// We'd like to able to transfer TypeError exceptions, but we can't, so we hack +// around it. PackReason() is guaranteed to succeed and the object produced is +// guaranteed to be serializable by postMessage(), however data may be lost. It +// is not very efficient, and has fairly arbitrary semantics. +// TODO(ricea): Replace once Errors are serializable. +v8::Local<v8::Value> PackReason(ScriptState* script_state, + v8::Local<v8::Value> reason) { + auto* isolate = script_state->GetIsolate(); + auto context = script_state->GetContext(); + if (reason->IsString() || reason->IsNumber() || reason->IsBoolean()) { + v8::TryCatch try_catch(isolate); + v8::Local<v8::String> stringified; + if (!v8::JSON::Stringify(context, reason).ToLocal(&stringified)) { + return PackErrorType(isolate, ErrorType::kTypeError, + "Cannot transfer message"); + } + + return PackErrorType(isolate, ErrorType::kJson, stringified); + } + + if (reason->IsNull()) { + return PackErrorType(isolate, ErrorType::kJson, "null"); + } + + if (reason->IsFunction() || reason->IsSymbol() || !reason->IsObject()) { + // Squash to undefined + return PackErrorType(isolate, ErrorType::kUndefined, ""); + } + + if (IsATypeError(script_state, reason.As<v8::Object>())) { + v8::TryCatch try_catch(isolate); + // "message" on TypeError is a normal property, meaning that if it + // is set, it is set on the object itself. We can take advantage of + // this to avoid executing user JavaScript in the case when the + // TypeError was generated internally. + v8::Local<v8::Value> descriptor; + if (!reason.As<v8::Object>() + ->GetOwnPropertyDescriptor(context, + V8AtomicString(isolate, "message")) + .ToLocal(&descriptor)) { + return PackErrorType(isolate, ErrorType::kTypeError, + "Cannot transfer message"); + } + if (descriptor->IsUndefined()) { + return PackErrorType(isolate, ErrorType::kTypeError, ""); + } + v8::Local<v8::Value> message; + CHECK(descriptor->IsObject()); + if (!descriptor.As<v8::Object>() + ->Get(context, V8AtomicString(isolate, "value")) + .ToLocal(&message)) { + message = V8String(isolate, "Cannot transfer message"); + } else if (!message->IsString()) { + message = V8String(isolate, ""); + } + return PackErrorType(isolate, ErrorType::kTypeError, + message.As<v8::String>()); + } + + if (IsADOMException(isolate, reason.As<v8::Object>())) { + DOMException* dom_exception = + V8DOMException::ToImpl(reason.As<v8::Object>()); + String message = dom_exception->message(); + String name = dom_exception->name(); + v8::Local<v8::Value> packed = CreateKeyValueObject( + isolate, "m", V8String(isolate, message), "n", V8String(isolate, name)); + // It should be impossible for this to fail, except for out-of-memory. + v8::Local<v8::String> packed_string = + v8::JSON::Stringify(context, packed).ToLocalChecked(); + return PackErrorType(isolate, ErrorType::kDomException, packed_string); + } + + v8::TryCatch try_catch(isolate); + v8::Local<v8::Value> json; + if (!v8::JSON::Stringify(context, reason).ToLocal(&json)) { + return PackErrorType(isolate, ErrorType::kTypeError, + "Cannot transfer message"); + } + + return PackErrorType(isolate, ErrorType::kJson, json.As<v8::String>()); +} + +// Converts an object created by PackReason() back into a clone of the original +// object, minus any data that was discarded by PackReason(). +bool UnpackReason(ScriptState* script_state, + v8::Local<v8::Value> packed_reason, + v8::Local<v8::Value>* reason) { + // We need to be robust against malformed input because it could come from a + // compromised renderer. + if (!packed_reason->IsObject()) { + DLOG(WARNING) << "packed_reason is not an object"; + return false; + } + + v8::Local<v8::Value> encoder_value; + v8::Local<v8::Value> string_value; + if (!UnpackKeyValueObject(script_state, packed_reason.As<v8::Object>(), "e", + &encoder_value, "s", &string_value)) { + return false; + } + + if (!encoder_value->IsNumber()) { + DLOG(WARNING) << "encoder_value is not a number"; + return false; + } + + int encoder = encoder_value.As<v8::Number>()->Value(); + if (!string_value->IsString()) { + DLOG(WARNING) << "string_value is not a string"; + return false; + } + + v8::Local<v8::String> string = string_value.As<v8::String>(); + auto* isolate = script_state->GetIsolate(); + auto context = script_state->GetContext(); + switch (static_cast<ErrorType>(encoder)) { + case ErrorType::kJson: { + v8::TryCatch try_catch(isolate); + if (!v8::JSON::Parse(context, string).ToLocal(reason)) { + DLOG(WARNING) << "JSON Parse failed. Content: " << ToCoreString(string); + return false; + } + return true; + } + + case ErrorType::kTypeError: + *reason = v8::Exception::TypeError(string); + return true; + + case ErrorType::kDomException: { + v8::TryCatch try_catch(isolate); + v8::Local<v8::Value> packed_exception; + if (!v8::JSON::Parse(context, string).ToLocal(&packed_exception)) { + DLOG(WARNING) << "Packed DOMException JSON parse failed"; + return false; + } + + if (!packed_exception->IsObject()) { + DLOG(WARNING) << "Packed DOMException is not an object"; + return false; + } + + v8::Local<v8::Value> message; + v8::Local<v8::Value> name; + if (!UnpackKeyValueObject(script_state, packed_exception.As<v8::Object>(), + "m", &message, "n", &name)) { + DLOG(WARNING) << "Failed unpacking packed DOMException"; + return false; + } + + if (!message->IsString()) { + DLOG(WARNING) << "DOMException message is not a string"; + return false; + } + + if (!name->IsString()) { + DLOG(WARNING) << "DOMException name is not a string"; + return false; + } + + auto ToBlink = [](v8::Local<v8::Value> value) { + return ToBlinkString<String>(value.As<v8::String>(), kDoNotExternalize); + }; + *reason = ToV8(DOMException::Create(ToBlink(message), ToBlink(name)), + script_state); + return true; + } + + case ErrorType::kUndefined: + *reason = v8::Undefined(isolate); + return true; + + default: + DLOG(WARNING) << "Invalid ErrorType: " << encoder; + return false; + } +} + +// Base class for CrossRealmTransformWritable and CrossRealmTransformReadable. +// Contains common methods that are used when handling MessagePort events. +class CrossRealmTransformStream + : public GarbageCollected<CrossRealmTransformStream> { + public: + // Neither of the subclasses require finalization, so no destructor. + + virtual ScriptState* GetScriptState() const = 0; + virtual MessagePort* GetMessagePort() const = 0; + + // HandleMessage() is called by CrossRealmTransformMessageListener to handle + // an incoming message from the MessagePort. + virtual void HandleMessage(MessageType type, v8::Local<v8::Value> value) = 0; + + // HandleError() is called by CrossRealmTransformErrorListener when an error + // event is fired on the message port. It should error the stream. + virtual void HandleError(v8::Local<v8::Value> error) = 0; + + virtual void Trace(Visitor*) {} +}; + +// Handles MessageEvents from the MessagePort. +class CrossRealmTransformMessageListener final : public NativeEventListener { + public: + explicit CrossRealmTransformMessageListener(CrossRealmTransformStream* target) + : target_(target) {} + + void Invoke(ExecutionContext*, Event* event) override { + // TODO(ricea): Find a way to guarantee this cast is safe. + MessageEvent* message = static_cast<MessageEvent*>(event); + ScriptState* script_state = target_->GetScriptState(); + // The deserializer code called by message->data() looks up the ScriptState + // from the current context, so we need to make sure it is set. + ScriptState::Scope scope(script_state); + v8::Local<v8::Value> data = message->data(script_state).V8Value(); + if (!data->IsObject()) { + DLOG(WARNING) << "Invalid message from peer ignored (not object)"; + return; + } + + v8::Local<v8::Value> type; + v8::Local<v8::Value> value; + if (!UnpackKeyValueObject(script_state, data.As<v8::Object>(), "t", &type, + "v", &value)) { + DLOG(WARNING) << "Invalid message from peer ignored"; + return; + } + + if (!type->IsNumber()) { + DLOG(WARNING) << "Invalid message from peer ignored (type is not number)"; + return; + } + + int type_value = type.As<v8::Number>()->Value(); + DVLOG(3) << "MessageListener saw message type " << type_value; + target_->HandleMessage(static_cast<MessageType>(type_value), value); + } + + void Trace(Visitor* visitor) override { + visitor->Trace(target_); + NativeEventListener::Trace(visitor); + } + + private: + const Member<CrossRealmTransformStream> target_; +}; + +// Handles "error" events from the MessagePort. +class CrossRealmTransformErrorListener final : public NativeEventListener { + public: + explicit CrossRealmTransformErrorListener(CrossRealmTransformStream* target) + : target_(target) {} + + void Invoke(ExecutionContext*, Event*) override { + ScriptState* script_state = target_->GetScriptState(); + const auto* error = + DOMException::Create("chunk could not be cloned", "DataCloneError"); + auto* message_port = target_->GetMessagePort(); + v8::Local<v8::Value> error_value = ToV8(error, script_state); + ExceptionState exception_state(script_state->GetIsolate(), + ExceptionState::kUnknownContext, "", ""); + + PackAndPostMessage(script_state, message_port, MessageType::kError, + PackReason(script_state, error_value), exception_state); + if (exception_state.HadException()) { + DLOG(WARNING) << "Ignoring postMessage failure in error listener"; + exception_state.ClearException(); + } + + message_port->close(); + target_->HandleError(error_value); + } + + void Trace(Visitor* visitor) override { + visitor->Trace(target_); + NativeEventListener::Trace(visitor); + } + + private: + const Member<CrossRealmTransformStream> target_; +}; + +// Class for data associated with the writable side of the cross realm transform +// stream. +class CrossRealmTransformWritable final : public CrossRealmTransformStream { + public: + CrossRealmTransformWritable(ScriptState* script_state, MessagePort* port) + : script_state_(script_state), + message_port_(port), + backpressure_promise_( + MakeGarbageCollected<StreamPromiseResolver>(script_state)) {} + + WritableStreamNative* CreateWritableStream(ExceptionState&); + + ScriptState* GetScriptState() const override { return script_state_; } + MessagePort* GetMessagePort() const override { return message_port_; } + void HandleMessage(MessageType type, v8::Local<v8::Value> value) override; + void HandleError(v8::Local<v8::Value> error) override; + + void Trace(Visitor* visitor) override { + visitor->Trace(script_state_); + visitor->Trace(message_port_); + visitor->Trace(backpressure_promise_); + visitor->Trace(controller_); + CrossRealmTransformStream::Trace(visitor); + } + + private: + class WriteAlgorithm; + class CloseAlgorithm; + class AbortAlgorithm; + + const Member<ScriptState> script_state_; + const Member<MessagePort> message_port_; + Member<StreamPromiseResolver> backpressure_promise_; + Member<WritableStreamDefaultController> controller_; +}; + +class CrossRealmTransformWritable::WriteAlgorithm final + : public StreamAlgorithm { + public: + explicit WriteAlgorithm(CrossRealmTransformWritable* writable) + : writable_(writable) {} + + // Sends the chunk to the readable side, possibly after waiting for + // backpressure. + v8::Local<v8::Promise> Run(ScriptState* script_state, + int argc, + v8::Local<v8::Value> argv[]) override { + DCHECK_EQ(argc, 1); + auto chunk = argv[0]; + + if (!writable_->backpressure_promise_) { + return DoWrite(script_state, chunk); + } + + auto* isolate = script_state->GetIsolate(); + return StreamThenPromise( + script_state->GetContext(), + writable_->backpressure_promise_->V8Promise(isolate), + MakeGarbageCollected<DoWriteOnResolve>(script_state, chunk, this)); + } + + void Trace(Visitor* visitor) override { + visitor->Trace(writable_); + StreamAlgorithm::Trace(visitor); + } + + private: + // A promise handler which calls DoWrite() when the promise resolves. + class DoWriteOnResolve final : public PromiseHandlerWithValue { + public: + DoWriteOnResolve(ScriptState* script_state, + v8::Local<v8::Value> chunk, + WriteAlgorithm* target) + : PromiseHandlerWithValue(script_state), + chunk_(script_state->GetIsolate(), chunk), + target_(target) {} + + v8::Local<v8::Value> CallWithLocal(v8::Local<v8::Value>) override { + ScriptState* script_state = GetScriptState(); + return target_->DoWrite(script_state, + chunk_.NewLocal(script_state->GetIsolate())); + } + + void Trace(Visitor* visitor) override { + visitor->Trace(chunk_); + visitor->Trace(target_); + PromiseHandlerWithValue::Trace(visitor); + } + + private: + const TraceWrapperV8Reference<v8::Value> chunk_; + const Member<WriteAlgorithm> target_; + }; + + // Sends a chunk over the message port to the readable side. + v8::Local<v8::Promise> DoWrite(ScriptState* script_state, + v8::Local<v8::Value> chunk) { + writable_->backpressure_promise_ = + MakeGarbageCollected<StreamPromiseResolver>(script_state); + ExceptionState exception_state(script_state->GetIsolate(), + ExceptionState::kUnknownContext, "", ""); + PackAndPostMessage(script_state, writable_->message_port_, + MessageType::kChunk, chunk, exception_state); + if (exception_state.HadException()) { + auto exception = exception_state.GetException(); + exception_state.ClearException(); + + PackAndPostMessage( + script_state, writable_->message_port_, MessageType::kError, + PackReason(writable_->script_state_, exception), exception_state); + if (exception_state.HadException()) { + DLOG(WARNING) << "Disregarding exception while sending error"; + exception_state.ClearException(); + } + + writable_->message_port_->close(); + return PromiseReject(script_state, exception); + } + + return PromiseResolveWithUndefined(script_state); + } + + const Member<CrossRealmTransformWritable> writable_; +}; + +class CrossRealmTransformWritable::CloseAlgorithm final + : public StreamAlgorithm { + public: + explicit CloseAlgorithm(CrossRealmTransformWritable* writable) + : writable_(writable) {} + + // Sends a close message to the readable side and closes the message port. + v8::Local<v8::Promise> Run(ScriptState* script_state, + int argc, + v8::Local<v8::Value> argv[]) override { + DCHECK_EQ(argc, 0); + ExceptionState exception_state(script_state->GetIsolate(), + ExceptionState::kUnknownContext, "", ""); + PackAndPostMessage( + script_state, writable_->message_port_, MessageType::kClose, + v8::Undefined(script_state->GetIsolate()), exception_state); + if (exception_state.HadException()) { + DLOG(WARNING) << "Ignoring exception from PackAndPostMessage kClose"; + exception_state.ClearException(); + } + + writable_->message_port_->close(); + return PromiseResolveWithUndefined(script_state); + } + + void Trace(Visitor* visitor) override { + visitor->Trace(writable_); + StreamAlgorithm::Trace(visitor); + } + + private: + const Member<CrossRealmTransformWritable> writable_; +}; + +class CrossRealmTransformWritable::AbortAlgorithm final + : public StreamAlgorithm { + public: + explicit AbortAlgorithm(CrossRealmTransformWritable* writable) + : writable_(writable) {} + + // Sends an abort message to the readable side and closes the message port. + v8::Local<v8::Promise> Run(ScriptState* script_state, + int argc, + v8::Local<v8::Value> argv[]) override { + DCHECK_EQ(argc, 1); + auto reason = argv[0]; + ExceptionState exception_state(script_state->GetIsolate(), + ExceptionState::kUnknownContext, "", ""); + PackAndPostMessage( + script_state, writable_->message_port_, MessageType::kAbort, + PackReason(writable_->script_state_, reason), exception_state); + if (exception_state.HadException()) { + DLOG(WARNING) << "Ignoring exception from PackAndPostMessage kAbort"; + exception_state.ClearException(); + } + writable_->message_port_->close(); + return PromiseResolveWithUndefined(script_state); + } + + void Trace(Visitor* visitor) override { + visitor->Trace(writable_); + StreamAlgorithm::Trace(visitor); + } + + private: + const Member<CrossRealmTransformWritable> writable_; +}; + +WritableStreamNative* CrossRealmTransformWritable::CreateWritableStream( + ExceptionState& exception_state) { + DCHECK(!controller_) << "CreateWritableStream() can only be called once"; + + message_port_->setOnmessage( + MakeGarbageCollected<CrossRealmTransformMessageListener>(this)); + message_port_->setOnmessageerror( + MakeGarbageCollected<CrossRealmTransformErrorListener>(this)); + + auto* stream = WritableStreamNative::Create( + script_state_, CreateTrivialStartAlgorithm(), + MakeGarbageCollected<WriteAlgorithm>(this), + MakeGarbageCollected<CloseAlgorithm>(this), + MakeGarbageCollected<AbortAlgorithm>(this), 1, + CreateDefaultSizeAlgorithm(), exception_state); + + if (exception_state.HadException()) { + return nullptr; + } + + controller_ = stream->Controller(); + return stream; +} + +void CrossRealmTransformWritable::HandleMessage(MessageType type, + v8::Local<v8::Value> value) { + switch (type) { + case MessageType::kPull: + DCHECK(backpressure_promise_); + backpressure_promise_->ResolveWithUndefined(script_state_); + backpressure_promise_ = nullptr; + return; + + case MessageType::kCancel: + case MessageType::kError: { + v8::Local<v8::Value> reason; + if (!UnpackReason(script_state_, value, &reason)) { + DLOG(WARNING) + << "Invalid message from peer ignored (unable to unpack value)"; + return; + } + WritableStreamDefaultController::ErrorIfNeeded(script_state_, controller_, + reason); + if (backpressure_promise_) { + backpressure_promise_->ResolveWithUndefined(script_state_); + backpressure_promise_ = nullptr; + } + return; + } + + default: + DLOG(WARNING) << "Invalid message from peer ignored (invalid type): " + << static_cast<int>(type); + return; + } +} + +void CrossRealmTransformWritable::HandleError(v8::Local<v8::Value> error) { + WritableStreamDefaultController::ErrorIfNeeded(script_state_, controller_, + error); +} + +// Class for data associated with the readable side of the cross realm transform +// stream. +class CrossRealmTransformReadable final : public CrossRealmTransformStream { + public: + CrossRealmTransformReadable(ScriptState* script_state, MessagePort* port) + : script_state_(script_state), + message_port_(port), + backpressure_promise_( + MakeGarbageCollected<StreamPromiseResolver>(script_state)) {} + + ReadableStreamNative* CreateReadableStream(ExceptionState&); + + ScriptState* GetScriptState() const override { return script_state_; } + MessagePort* GetMessagePort() const override { return message_port_; } + void HandleMessage(MessageType type, v8::Local<v8::Value> value) override; + void HandleError(v8::Local<v8::Value> error) override; + + void Trace(Visitor* visitor) override { + visitor->Trace(script_state_); + visitor->Trace(message_port_); + visitor->Trace(backpressure_promise_); + visitor->Trace(controller_); + CrossRealmTransformStream::Trace(visitor); + } + + private: + class PullAlgorithm; + class CancelAlgorithm; + + const Member<ScriptState> script_state_; + const Member<MessagePort> message_port_; + Member<StreamPromiseResolver> backpressure_promise_; + Member<ReadableStreamDefaultController> controller_; + bool finished_ = false; +}; + +class CrossRealmTransformReadable::PullAlgorithm final + : public StreamAlgorithm { + public: + explicit PullAlgorithm(CrossRealmTransformReadable* readable) + : readable_(readable) {} + + // Sends a pull message to the writable side and then waits for backpressure + // to clear. + v8::Local<v8::Promise> Run(ScriptState* script_state, + int argc, + v8::Local<v8::Value> argv[]) override { + DCHECK_EQ(argc, 0); + auto* isolate = script_state->GetIsolate(); + ExceptionState exception_state(isolate, ExceptionState::kUnknownContext, "", + ""); + + PackAndPostMessage( + script_state, readable_->message_port_, MessageType::kPull, + v8::Undefined(script_state->GetIsolate()), exception_state); + if (exception_state.HadException()) { + DLOG(WARNING) << "Ignoring exception from PackAndPostMessage kClose"; + exception_state.ClearException(); + } + + return readable_->backpressure_promise_->V8Promise(isolate); + } + + void Trace(Visitor* visitor) override { + visitor->Trace(readable_); + StreamAlgorithm::Trace(visitor); + } + + private: + const Member<CrossRealmTransformReadable> readable_; +}; + +class CrossRealmTransformReadable::CancelAlgorithm final + : public StreamAlgorithm { + public: + explicit CancelAlgorithm(CrossRealmTransformReadable* readable) + : readable_(readable) {} + + // Sends a cancel message to the writable side and closes the message port. + v8::Local<v8::Promise> Run(ScriptState* script_state, + int argc, + v8::Local<v8::Value> argv[]) override { + DCHECK_EQ(argc, 1); + auto reason = argv[0]; + readable_->finished_ = true; + ExceptionState exception_state(script_state->GetIsolate(), + ExceptionState::kUnknownContext, "", ""); + + PackAndPostMessage(script_state, readable_->message_port_, + MessageType::kCancel, PackReason(script_state, reason), + exception_state); + if (exception_state.HadException()) { + DLOG(WARNING) << "Ignoring exception from PackAndPostMessage kClose"; + exception_state.ClearException(); + } + + readable_->message_port_->close(); + return PromiseResolveWithUndefined(script_state); + } + + void Trace(Visitor* visitor) override { + visitor->Trace(readable_); + StreamAlgorithm::Trace(visitor); + } + + private: + const Member<CrossRealmTransformReadable> readable_; +}; + +ReadableStreamNative* CrossRealmTransformReadable::CreateReadableStream( + ExceptionState& exception_state) { + DCHECK(!controller_) << "CreateReadableStream can only be called once"; + + message_port_->setOnmessage( + MakeGarbageCollected<CrossRealmTransformMessageListener>(this)); + message_port_->setOnmessageerror( + MakeGarbageCollected<CrossRealmTransformErrorListener>(this)); + + auto* stream = ReadableStreamNative::Create( + script_state_, CreateTrivialStartAlgorithm(), + MakeGarbageCollected<PullAlgorithm>(this), + MakeGarbageCollected<CancelAlgorithm>(this), + /* highWaterMark = */ 0, CreateDefaultSizeAlgorithm(), exception_state); + + if (exception_state.HadException()) { + return nullptr; + } + + controller_ = stream->GetController(); + return stream; +} + +void CrossRealmTransformReadable::HandleMessage(MessageType type, + v8::Local<v8::Value> value) { + switch (type) { + case MessageType::kChunk: { + // This can't throw because we always use the default strategy size + // algorithm, which doesn't throw, and always returns a valid value of + // 1.0. + ReadableStreamDefaultController::Enqueue(script_state_, controller_, + value, ASSERT_NO_EXCEPTION); + + backpressure_promise_->ResolveWithUndefined(script_state_); + backpressure_promise_ = + MakeGarbageCollected<StreamPromiseResolver>(script_state_); + return; + } + + case MessageType::kClose: + finished_ = true; + ReadableStreamDefaultController::Close(script_state_, controller_); + message_port_->close(); + return; + + case MessageType::kAbort: + case MessageType::kError: { + finished_ = true; + v8::Local<v8::Value> reason; + if (!UnpackReason(script_state_, value, &reason)) { + DLOG(WARNING) + << "Invalid message from peer ignored (unable to unpack value)"; + return; + } + + ReadableStreamDefaultController::Error(script_state_, controller_, + reason); + message_port_->close(); + return; + } + + default: + DLOG(WARNING) << "Invalid message from peer ignored (invalid type): " + << static_cast<int>(type); + return; + } +} + +void CrossRealmTransformReadable::HandleError(v8::Local<v8::Value> error) { + ReadableStreamDefaultController::Error(script_state_, controller_, error); +} + +} // namespace + +CORE_EXPORT WritableStreamNative* CreateCrossRealmTransformWritable( + ScriptState* script_state, + MessagePort* port, + ExceptionState& exception_state) { + return MakeGarbageCollected<CrossRealmTransformWritable>(script_state, port) + ->CreateWritableStream(exception_state); +} + +CORE_EXPORT ReadableStreamNative* CreateCrossRealmTransformReadable( + ScriptState* script_state, + MessagePort* port, + ExceptionState& exception_state) { + return MakeGarbageCollected<CrossRealmTransformReadable>(script_state, port) + ->CreateReadableStream(exception_state); +} + +} // namespace blink diff --git a/chromium/third_party/blink/renderer/core/streams/transferable_streams.h b/chromium/third_party/blink/renderer/core/streams/transferable_streams.h new file mode 100644 index 00000000000..62883f1d69c --- /dev/null +++ b/chromium/third_party/blink/renderer/core/streams/transferable_streams.h @@ -0,0 +1,37 @@ +// Copyright 2019 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. + +// Functions used to build transferable streams. + +#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_STREAMS_TRANSFERABLE_STREAMS_H_ +#define THIRD_PARTY_BLINK_RENDERER_CORE_STREAMS_TRANSFERABLE_STREAMS_H_ + +#include "third_party/blink/renderer/core/core_export.h" + +namespace blink { + +class ExceptionState; +class MessagePort; +class ReadableStreamNative; +class ScriptState; +class WritableStreamNative; + +// Creates the writable side of a cross-realm identity transform stream, using +// |port| for communication. |port| must be entangled with another MessagePort +// which is passed to CreateCrossRealmTransformReadable(). +CORE_EXPORT WritableStreamNative* CreateCrossRealmTransformWritable( + ScriptState*, + MessagePort* port, + ExceptionState&); + +// Creates the readable side of a cross-realm identity transform stream. |port| +// is used symmetrically with CreateCrossRealmTransformWritable(). +CORE_EXPORT ReadableStreamNative* CreateCrossRealmTransformReadable( + ScriptState*, + MessagePort* port, + ExceptionState&); + +} // namespace blink + +#endif // THIRD_PARTY_BLINK_RENDERER_CORE_STREAMS_TRANSFERABLE_STREAMS_H_ diff --git a/chromium/third_party/blink/renderer/core/streams/transferable_streams_test.cc b/chromium/third_party/blink/renderer/core/streams/transferable_streams_test.cc new file mode 100644 index 00000000000..3811b0541e5 --- /dev/null +++ b/chromium/third_party/blink/renderer/core/streams/transferable_streams_test.cc @@ -0,0 +1,129 @@ +// Copyright 2019 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/transferable_streams.h" + +#include "testing/gtest/include/gtest/gtest.h" +#include "third_party/blink/renderer/bindings/core/v8/script_function.h" +#include "third_party/blink/renderer/bindings/core/v8/script_promise.h" +#include "third_party/blink/renderer/bindings/core/v8/script_value.h" +#include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_testing.h" +#include "third_party/blink/renderer/bindings/core/v8/v8_iterator_result_value.h" +#include "third_party/blink/renderer/bindings/core/v8/v8_readable_stream_default_reader.h" +#include "third_party/blink/renderer/bindings/core/v8/v8_writable_stream_default_writer.h" +#include "third_party/blink/renderer/core/messaging/message_channel.h" +#include "third_party/blink/renderer/core/streams/readable_stream_default_reader.h" +#include "third_party/blink/renderer/core/streams/readable_stream_native.h" +#include "third_party/blink/renderer/core/streams/writable_stream_default_writer.h" +#include "third_party/blink/renderer/core/streams/writable_stream_native.h" +#include "third_party/blink/renderer/platform/bindings/exception_state.h" +#include "third_party/blink/renderer/platform/heap/heap.h" +#include "third_party/blink/renderer/platform/testing/unit_test_helpers.h" + +namespace blink { + +namespace { + +// We only do minimal testing here. The functionality of transferable streams is +// tested in the layout tests. +TEST(TransferableStreamsTest, SmokeTest) { + V8TestingScope scope; + + auto* channel = + MakeGarbageCollected<MessageChannel>(scope.GetExecutionContext()); + auto* script_state = scope.GetScriptState(); + auto* writable = CreateCrossRealmTransformWritable( + script_state, channel->port1(), ASSERT_NO_EXCEPTION); + ASSERT_TRUE(writable); + auto* readable = CreateCrossRealmTransformReadable( + script_state, channel->port2(), ASSERT_NO_EXCEPTION); + ASSERT_TRUE(readable); + + auto* writer = V8WritableStreamDefaultWriter::ToImpl( + writable->getWriter(script_state, ASSERT_NO_EXCEPTION) + .V8Value() + .As<v8::Object>()); + auto* reader = V8ReadableStreamDefaultReader::ToImpl( + readable->getReader(script_state, ASSERT_NO_EXCEPTION) + .V8Value() + .As<v8::Object>()); + + writer->write(script_state, ScriptValue::CreateNull(script_state)); + + class ExpectNullResponse : public ScriptFunction { + public: + static v8::Local<v8::Function> Create(ScriptState* script_state, + bool* got_response) { + auto* self = + MakeGarbageCollected<ExpectNullResponse>(script_state, got_response); + return self->BindToV8Function(); + } + + ExpectNullResponse(ScriptState* script_state, bool* got_response) + : ScriptFunction(script_state), got_response_(got_response) {} + + private: + ScriptValue Call(ScriptValue value) override { + *got_response_ = true; + if (!value.IsObject()) { + ADD_FAILURE() << "iterator must be an object"; + return ScriptValue(); + } + bool done = false; + auto* script_state = GetScriptState(); + auto chunk_maybe = + V8UnpackIteratorResult(script_state, + value.V8Value() + ->ToObject(script_state->GetContext()) + .ToLocalChecked(), + &done); + EXPECT_FALSE(done); + v8::Local<v8::Value> chunk; + if (!chunk_maybe.ToLocal(&chunk)) { + ADD_FAILURE() << "V8UnpackIteratorResult failed"; + return ScriptValue(); + } + EXPECT_TRUE(chunk->IsNull()); + return ScriptValue(); + } + + bool* got_response_; + }; + + // TODO(ricea): This is copy-and-pasted from transform_stream_test.cc. Put it + // in a shared location. + class ExpectNotReached : public ScriptFunction { + public: + static v8::Local<v8::Function> Create(ScriptState* script_state) { + auto* self = MakeGarbageCollected<ExpectNotReached>(script_state); + return self->BindToV8Function(); + } + + explicit ExpectNotReached(ScriptState* script_state) + : ScriptFunction(script_state) {} + + private: + ScriptValue Call(ScriptValue) override { + ADD_FAILURE() << "ExpectNotReached was reached"; + return ScriptValue(); + } + }; + + bool got_response = false; + reader->read(script_state) + .Then(ExpectNullResponse::Create(script_state, &got_response), + ExpectNotReached::Create(script_state)); + + // Need to run the event loop to pass messages through the MessagePort. + test::RunPendingTasks(); + + // Resolve promises. + v8::MicrotasksScope::PerformCheckpoint(scope.GetIsolate()); + + EXPECT_TRUE(got_response); +} + +} // namespace + +} // namespace blink diff --git a/chromium/third_party/blink/renderer/core/streams/transform_stream_default_controller_interface.h b/chromium/third_party/blink/renderer/core/streams/transform_stream_default_controller_interface.h index 3ce613810ae..d87de7fea31 100644 --- a/chromium/third_party/blink/renderer/core/streams/transform_stream_default_controller_interface.h +++ b/chromium/third_party/blink/renderer/core/streams/transform_stream_default_controller_interface.h @@ -7,7 +7,7 @@ #include "base/macros.h" #include "third_party/blink/renderer/core/core_export.h" -#include "third_party/blink/renderer/platform/wtf/allocator.h" +#include "third_party/blink/renderer/platform/wtf/allocator/allocator.h" #include "v8/include/v8.h" namespace blink { diff --git a/chromium/third_party/blink/renderer/core/streams/transform_stream_native.cc b/chromium/third_party/blink/renderer/core/streams/transform_stream_native.cc index 79040dae29f..825cece3098 100644 --- a/chromium/third_party/blink/renderer/core/streams/transform_stream_native.cc +++ b/chromium/third_party/blink/renderer/core/streams/transform_stream_native.cc @@ -5,7 +5,6 @@ #include "third_party/blink/renderer/core/streams/transform_stream_native.h" #include "third_party/blink/renderer/core/execution_context/execution_context.h" -#include "third_party/blink/renderer/core/frame/use_counter.h" #include "third_party/blink/renderer/core/streams/miscellaneous_operations.h" #include "third_party/blink/renderer/core/streams/promise_handler.h" #include "third_party/blink/renderer/core/streams/readable_stream_default_controller.h" @@ -22,7 +21,8 @@ #include "third_party/blink/renderer/platform/bindings/to_v8.h" #include "third_party/blink/renderer/platform/bindings/v8_binding.h" #include "third_party/blink/renderer/platform/heap/visitor.h" -#include "third_party/blink/renderer/platform/wtf/allocator.h" +#include "third_party/blink/renderer/platform/instrumentation/use_counter.h" +#include "third_party/blink/renderer/platform/wtf/allocator/allocator.h" #include "third_party/blink/renderer/platform/wtf/assertions.h" namespace blink { diff --git a/chromium/third_party/blink/renderer/core/streams/transform_stream_wrapper.h b/chromium/third_party/blink/renderer/core/streams/transform_stream_wrapper.h index 022a175c8be..f0668dffaad 100644 --- a/chromium/third_party/blink/renderer/core/streams/transform_stream_wrapper.h +++ b/chromium/third_party/blink/renderer/core/streams/transform_stream_wrapper.h @@ -7,7 +7,7 @@ #include "third_party/blink/renderer/bindings/core/v8/script_value.h" #include "third_party/blink/renderer/platform/heap/member.h" -#include "third_party/blink/renderer/platform/wtf/allocator.h" +#include "third_party/blink/renderer/platform/wtf/allocator/allocator.h" #include "v8/include/v8.h" namespace blink { diff --git a/chromium/third_party/blink/renderer/core/streams/underlying_sink_base.h b/chromium/third_party/blink/renderer/core/streams/underlying_sink_base.h index 515b64cd552..9dd9e0bf5ee 100644 --- a/chromium/third_party/blink/renderer/core/streams/underlying_sink_base.h +++ b/chromium/third_party/blink/renderer/core/streams/underlying_sink_base.h @@ -5,6 +5,7 @@ #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_STREAMS_UNDERLYING_SINK_BASE_H_ #define THIRD_PARTY_BLINK_RENDERER_CORE_STREAMS_UNDERLYING_SINK_BASE_H_ +#include "third_party/blink/renderer/bindings/core/v8/script_promise.h" #include "third_party/blink/renderer/core/core_export.h" #include "third_party/blink/renderer/core/streams/writable_stream_default_controller_interface.h" #include "third_party/blink/renderer/platform/bindings/script_wrappable.h" @@ -12,7 +13,6 @@ namespace blink { -class ScriptPromise; class ScriptValue; class ScriptState; diff --git a/chromium/third_party/blink/renderer/core/streams/underlying_source_base.cc b/chromium/third_party/blink/renderer/core/streams/underlying_source_base.cc index 6e4ccc9f619..9efc0fe8ad5 100644 --- a/chromium/third_party/blink/renderer/core/streams/underlying_source_base.cc +++ b/chromium/third_party/blink/renderer/core/streams/underlying_source_base.cc @@ -47,22 +47,6 @@ ScriptValue UnderlyingSourceBase::type(ScriptState* script_state) const { return ScriptValue(script_state, v8::Undefined(script_state->GetIsolate())); } -void UnderlyingSourceBase::notifyLockAcquired() { - is_stream_locked_ = true; -} - -void UnderlyingSourceBase::notifyLockReleased() { - is_stream_locked_ = false; -} - -bool UnderlyingSourceBase::HasPendingActivity() const { - // This will return false within a finite time period _assuming_ that - // consumers use the controller to close or error the stream. - // Browser-created readable streams should always close or error within a - // finite time period, due to timeouts etc. - return controller_ && controller_->IsActive() && is_stream_locked_; -} - void UnderlyingSourceBase::ContextDestroyed(ExecutionContext*) { if (controller_) { controller_->NoteHasBeenCanceled(); diff --git a/chromium/third_party/blink/renderer/core/streams/underlying_source_base.h b/chromium/third_party/blink/renderer/core/streams/underlying_source_base.h index 55abd8e3dfb..664f30e87d5 100644 --- a/chromium/third_party/blink/renderer/core/streams/underlying_source_base.h +++ b/chromium/third_party/blink/renderer/core/streams/underlying_source_base.h @@ -22,7 +22,6 @@ class ReadableStreamDefaultControllerInterface; class CORE_EXPORT UnderlyingSourceBase : public ScriptWrappable, - public ActiveScriptWrappable<UnderlyingSourceBase>, public ContextLifecycleObserver { DEFINE_WRAPPERTYPEINFO(); USING_GARBAGE_COLLECTED_MIXIN(UnderlyingSourceBase); @@ -41,13 +40,8 @@ class CORE_EXPORT UnderlyingSourceBase ScriptValue type(ScriptState*) const; - void notifyLockAcquired(); - void notifyLockReleased(); - - // ScriptWrappable - bool HasPendingActivity() const override; - // ContextLifecycleObserver + // TODO(ricea): Is this still useful? void ContextDestroyed(ExecutionContext*) override; protected: @@ -60,7 +54,6 @@ class CORE_EXPORT UnderlyingSourceBase private: Member<ReadableStreamDefaultControllerInterface> controller_; - bool is_stream_locked_ = false; }; } // namespace blink diff --git a/chromium/third_party/blink/renderer/core/streams/underlying_source_base.idl b/chromium/third_party/blink/renderer/core/streams/underlying_source_base.idl index d943658bcc2..bdf11311634 100644 --- a/chromium/third_party/blink/renderer/core/streams/underlying_source_base.idl +++ b/chromium/third_party/blink/renderer/core/streams/underlying_source_base.idl @@ -8,7 +8,6 @@ // automatically for use in initializing a ReadableStream. [ - ActiveScriptWrappable, NoInterfaceObject ] interface UnderlyingSourceBase { @@ -18,7 +17,4 @@ interface UnderlyingSourceBase { // This only exists to prevent Object.prototype.type being accessed. [CallWith=ScriptState] readonly attribute any type; - - void notifyLockAcquired(); - void notifyLockReleased(); }; diff --git a/chromium/third_party/blink/renderer/core/streams/writable_stream.cc b/chromium/third_party/blink/renderer/core/streams/writable_stream.cc index da3db6e90c5..f0817bf8400 100644 --- a/chromium/third_party/blink/renderer/core/streams/writable_stream.cc +++ b/chromium/third_party/blink/renderer/core/streams/writable_stream.cc @@ -53,11 +53,27 @@ WritableStream* WritableStream::Create(ScriptState* script_state, return stream; } +WritableStream* WritableStream::CreateWithCountQueueingStrategy( + ScriptState* script_state, + UnderlyingSinkBase* underlying_sink, + size_t high_water_mark) { + if (RuntimeEnabledFeatures::StreamsNativeEnabled()) { + return WritableStreamNative::CreateWithCountQueueingStrategy( + script_state, underlying_sink, high_water_mark); + } + + return WritableStreamWrapper::CreateWithCountQueueingStrategy( + script_state, underlying_sink, high_water_mark); +} + // static WritableStream* WritableStream::Deserialize(ScriptState* script_state, MessagePort* port, ExceptionState& exception_state) { - // TODO(ricea): Switch on Blink feature. + if (RuntimeEnabledFeatures::StreamsNativeEnabled()) { + return WritableStreamNative::Deserialize(script_state, port, + exception_state); + } return WritableStreamWrapper::Deserialize(script_state, port, exception_state); } diff --git a/chromium/third_party/blink/renderer/core/streams/writable_stream.h b/chromium/third_party/blink/renderer/core/streams/writable_stream.h index 2d57e2195b9..cbc4f61654b 100644 --- a/chromium/third_party/blink/renderer/core/streams/writable_stream.h +++ b/chromium/third_party/blink/renderer/core/streams/writable_stream.h @@ -15,6 +15,7 @@ namespace blink { class MessagePort; +class UnderlyingSinkBase; // This is an implementation of the corresponding IDL interface. class CORE_EXPORT WritableStream : public ScriptWrappable { @@ -31,6 +32,10 @@ class CORE_EXPORT WritableStream : public ScriptWrappable { ScriptValue underlying_sink, ScriptValue strategy, ExceptionState&); + static WritableStream* CreateWithCountQueueingStrategy( + ScriptState*, + UnderlyingSinkBase*, + size_t high_water_mark); // IDL defined functions virtual bool locked(ScriptState*, ExceptionState&) const = 0; diff --git a/chromium/third_party/blink/renderer/core/streams/writable_stream_default_writer.h b/chromium/third_party/blink/renderer/core/streams/writable_stream_default_writer.h index 1bb5bea8112..fbe239eac20 100644 --- a/chromium/third_party/blink/renderer/core/streams/writable_stream_default_writer.h +++ b/chromium/third_party/blink/renderer/core/streams/writable_stream_default_writer.h @@ -6,6 +6,7 @@ #define THIRD_PARTY_BLINK_RENDERER_CORE_STREAMS_WRITABLE_STREAM_DEFAULT_WRITER_H_ #include "base/optional.h" +#include "third_party/blink/renderer/core/core_export.h" #include "third_party/blink/renderer/platform/bindings/script_wrappable.h" #include "v8/include/v8.h" @@ -21,7 +22,7 @@ class WritableStream; class WritableStreamNative; // https://streams.spec.whatwg.org/#default-writer-class -class WritableStreamDefaultWriter final : public ScriptWrappable { +class CORE_EXPORT WritableStreamDefaultWriter final : public ScriptWrappable { DEFINE_WRAPPERTYPEINFO(); public: diff --git a/chromium/third_party/blink/renderer/core/streams/writable_stream_native.cc b/chromium/third_party/blink/renderer/core/streams/writable_stream_native.cc index 262a9833c1b..4c8477811ac 100644 --- a/chromium/third_party/blink/renderer/core/streams/writable_stream_native.cc +++ b/chromium/third_party/blink/renderer/core/streams/writable_stream_native.cc @@ -7,7 +7,11 @@ #include "third_party/blink/renderer/bindings/core/v8/script_value.h" #include "third_party/blink/renderer/core/streams/miscellaneous_operations.h" #include "third_party/blink/renderer/core/streams/promise_handler.h" +#include "third_party/blink/renderer/core/streams/readable_stream_native.h" +#include "third_party/blink/renderer/core/streams/readable_stream_operations.h" #include "third_party/blink/renderer/core/streams/stream_promise_resolver.h" +#include "third_party/blink/renderer/core/streams/transferable_streams.h" +#include "third_party/blink/renderer/core/streams/underlying_sink_base.h" #include "third_party/blink/renderer/core/streams/writable_stream_default_controller.h" #include "third_party/blink/renderer/core/streams/writable_stream_default_writer.h" #include "third_party/blink/renderer/platform/bindings/exception_state.h" @@ -215,6 +219,67 @@ WritableStreamNative* WritableStreamNative::Create( return stream; } +// static +WritableStreamNative* WritableStreamNative::CreateWithCountQueueingStrategy( + ScriptState* script_state, + UnderlyingSinkBase* underlying_sink, + size_t high_water_mark) { + // TODO(crbug.com/902633): This method of constructing a WritableStream + // introduces unnecessary trips through the V8. Perhaps we should implement + // algorithms based on an UnderlyingSinkBase, or C++ stream implementations + // should provide the algorithms directly. + ScriptValue strategy = ReadableStreamOperations::CreateCountQueuingStrategy( + script_state, high_water_mark); + if (strategy.IsEmpty()) + return nullptr; + + auto underlying_sink_value = ScriptValue::From(script_state, underlying_sink); + + ExceptionState exception_state(script_state->GetIsolate(), + ExceptionState::kConstructionContext, + "WritableStream"); + auto* stream = MakeGarbageCollected<WritableStreamNative>( + script_state, underlying_sink_value, strategy, exception_state); + if (exception_state.HadException()) + return nullptr; + return stream; +} + +void WritableStreamNative::Serialize(ScriptState* script_state, + MessagePort* port, + ExceptionState& exception_state) { + if (IsLocked(this)) { + exception_state.ThrowTypeError("Cannot transfer a locked stream"); + return; + } + + auto* readable = + CreateCrossRealmTransformReadable(script_state, port, exception_state); + if (exception_state.HadException()) { + return; + } + + auto promise = ReadableStreamNative::PipeTo( + script_state, readable, this, ReadableStreamNative::PipeOptions()); + promise.MarkAsHandled(); +} + +WritableStreamNative* WritableStreamNative::Deserialize( + ScriptState* script_state, + MessagePort* port, + ExceptionState& exception_state) { + // We need to execute JavaScript to call "Then" on v8::Promises. We will not + // run author code. + v8::Isolate::AllowJavascriptExecutionScope allow_js( + script_state->GetIsolate()); + auto* writable = + CreateCrossRealmTransformWritable(script_state, port, exception_state); + if (exception_state.HadException()) { + return nullptr; + } + return writable; +} + WritableStreamDefaultWriter* WritableStreamNative::AcquireDefaultWriter( ScriptState* script_state, WritableStreamNative* stream, diff --git a/chromium/third_party/blink/renderer/core/streams/writable_stream_native.h b/chromium/third_party/blink/renderer/core/streams/writable_stream_native.h index 64f80fc2fdd..e37cac9a68c 100644 --- a/chromium/third_party/blink/renderer/core/streams/writable_stream_native.h +++ b/chromium/third_party/blink/renderer/core/streams/writable_stream_native.h @@ -20,6 +20,7 @@ class StrategySizeAlgorithm; class StreamAlgorithm; class StreamPromiseResolver; class StreamStartAlgorithm; +class UnderlyingSinkBase; class WritableStreamDefaultController; class WritableStreamDefaultWriter; @@ -44,6 +45,11 @@ class CORE_EXPORT WritableStreamNative : public WritableStream { StrategySizeAlgorithm* size_algorithm, ExceptionState&); + static WritableStreamNative* CreateWithCountQueueingStrategy( + ScriptState*, + UnderlyingSinkBase*, + size_t high_water_mark); + // Used by Create(). WritableStreamNative(); @@ -83,9 +89,11 @@ class CORE_EXPORT WritableStreamNative : public WritableStream { return stream->writer_; } - void Serialize(ScriptState*, MessagePort*, ExceptionState&) override { - // TODO(ricea): Implement this. - } + void Serialize(ScriptState*, MessagePort*, ExceptionState&) override; + + static WritableStreamNative* Deserialize(ScriptState*, + MessagePort*, + ExceptionState&); // // Methods used by ReadableStreamNative::PipeTo diff --git a/chromium/third_party/blink/renderer/core/streams/writable_stream_test.cc b/chromium/third_party/blink/renderer/core/streams/writable_stream_test.cc index 5401cc714fc..e87946d1063 100644 --- a/chromium/third_party/blink/renderer/core/streams/writable_stream_test.cc +++ b/chromium/third_party/blink/renderer/core/streams/writable_stream_test.cc @@ -60,12 +60,6 @@ TEST_P(WritableStreamTest, GetWriter) { } TEST_P(WritableStreamTest, Serialize) { - // Disable the test when StreamsNative is enabled as WritableStreamNative - // doesn't support serialization yet. - // TODO(ricea): Re-enable this test when serialization is supported. - if (GetParam()) - return; - ScopedTransferableStreamsForTest enable_transferable_streams(true); V8TestingScope scope; diff --git a/chromium/third_party/blink/renderer/core/streams/writable_stream_wrapper.cc b/chromium/third_party/blink/renderer/core/streams/writable_stream_wrapper.cc index 277aa68d67a..6e6ffcd7ab3 100644 --- a/chromium/third_party/blink/renderer/core/streams/writable_stream_wrapper.cc +++ b/chromium/third_party/blink/renderer/core/streams/writable_stream_wrapper.cc @@ -7,6 +7,8 @@ #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/readable_stream_operations.h" +#include "third_party/blink/renderer/core/streams/underlying_sink_base.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" @@ -46,6 +48,27 @@ WritableStreamWrapper* WritableStreamWrapper::CreateFromInternalStream( return stream; } +WritableStreamWrapper* WritableStreamWrapper::CreateWithCountQueueingStrategy( + ScriptState* script_state, + UnderlyingSinkBase* underlying_sink, + size_t high_water_mark) { + ScriptValue strategy = ReadableStreamOperations::CreateCountQueuingStrategy( + script_state, high_water_mark); + if (strategy.IsEmpty()) + return nullptr; + + auto underlying_sink_value = ScriptValue::From(script_state, underlying_sink); + auto* stream = MakeGarbageCollected<WritableStreamWrapper>(); + + ExceptionState exception_state(script_state->GetIsolate(), + ExceptionState::kConstructionContext, + "WritableStream"); + stream->Init(script_state, underlying_sink_value, strategy, exception_state); + if (exception_state.HadException()) + return nullptr; + return stream; +} + bool WritableStreamWrapper::InitInternal( ScriptState* script_state, v8::Local<v8::Object> internal_stream) { diff --git a/chromium/third_party/blink/renderer/core/streams/writable_stream_wrapper.h b/chromium/third_party/blink/renderer/core/streams/writable_stream_wrapper.h index 4a24ad85ae6..658c6d10225 100644 --- a/chromium/third_party/blink/renderer/core/streams/writable_stream_wrapper.h +++ b/chromium/third_party/blink/renderer/core/streams/writable_stream_wrapper.h @@ -16,6 +16,7 @@ namespace blink { class MessagePort; +class WritableStreamWrapper; // This is an implementation of the WritableStream interface that delegates to // the V8 Extras implementation. @@ -46,6 +47,11 @@ class CORE_EXPORT WritableStreamWrapper final : public WritableStream { v8::Local<v8::Object> internal_stream, ExceptionState&); + static WritableStreamWrapper* CreateWithCountQueueingStrategy( + ScriptState*, + UnderlyingSinkBase*, + size_t high_water_mark); + void Trace(Visitor* visitor) override; // IDL defined functions |