Commit e385ffd3 authored by Giovanni Ortuño Urquidi's avatar Giovanni Ortuño Urquidi Committed by Commit Bot

Revert "Remove blink::CallExtraOrCrash"

This reverts commit 1977b793.

Reason for revert: breaks WebKit Linux Trusty MSAN

Tests seem to time out since this CL:

https://test-results.appspot.com/data/layout_results/WebKit_Linux_Trusty_MSAN/8393/layout-test-results/results.html

https://ci.chromium.org/buildbot/chromium.webkit/WebKit%20Linux%20Trusty%20MSAN/8393

Original change's description:
> Remove blink::CallExtraOrCrash
> 
> CallExtraOrCrash is a function to call into V8 Extras and crash if it
> fails. Unfortunately, calling into V8 Extras can fail under normal
> conditions such as stack overflow and Worker destruction, causing a lot
> of unnecessary crashes. Remove CallExtraOrCrash.
> 
> This is a stopgap change designed to be mergable to version 68. It
> avoids major code restructuring but as a result lacks robust
> encapsulation.
> 
> The general strategy is to pass a boolean pointer to functions in
> ReadableStreamOperations that call into V8 Extras. If the internal call
> fails, the boolean is set to true, and the call returns a default value.
> 
> BodyStreamBuffer keeps the boolean as member variable. Once a call to a
> V8 extra function fails, the boolean is set and no more calls will be
> made. This is to avoid us taking action based on information that is
> incorrect.
> 
> For Tee() and the from-stream version of the BodyStreamBuffer
> constructor, we use an ExceptionState object instead. In these cases, it
> is straightforward to pass through ExceptionState object from the Blink
> bindings. This allows the failure to be propagated safely back to
> Javascript.
> 
> Conversely, DefaultReaderRead() has been switched from using
> ExceptionState to using an error-signalling bool. This is because its
> callers do not have access to a real ExceptionState object. Previously
> callers used a NonThrowableExceptionState, but this was not suitable as
> DefaultReaderRead() can throw.
> 
> BodyStreamBuffer mostly wraps access to ReadableStreamOperations, so
> some degree of encapsulation has been retained.
> 
> 7 new layout tests exercise each of the deterministically reachable
> crashes, using stack overflows. Because they depend on stack layout and
> whether DCHECKs are enabled, they only hit the crashes in particular
> environments.
> 
> Bug: 829790, 849312
> Change-Id: I47481b33a47b418dc6916e3b4311e60b5fd89e3d
> Reviewed-on: https://chromium-review.googlesource.com/1097047
> Commit-Queue: Adam Rice <ricea@chromium.org>
> Reviewed-by: Yuki Shiino <yukishiino@chromium.org>
> Reviewed-by: Yutaka Hirano <yhirano@chromium.org>
> Cr-Commit-Position: refs/heads/master@{#568009}

TBR=ricea@chromium.org,yukishiino@chromium.org,yhirano@chromium.org

Change-Id: I4234c9a5ebb7bed44f3f0a66b266b65a80efb295
No-Presubmit: true
No-Tree-Checks: true
No-Try: true
Bug: 829790, 849312
Reviewed-on: https://chromium-review.googlesource.com/1105577Reviewed-by: default avatarGiovanni Ortuño Urquidi <ortuno@chromium.org>
Commit-Queue: Giovanni Ortuño Urquidi <ortuno@chromium.org>
Cr-Commit-Position: refs/heads/master@{#568279}
parent c008bf65
<!doctype html>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="resources/stack-overflow.js"></script>
<script>
test(() => {
const rs = new ReadableStream();
fillStackAndRun(() => new Response(rs));
}, 'stack overflow in Response construction from stream should not crash the browser');
</script>
<!doctype html>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="resources/stack-overflow.js"></script>
<script>
test(() => {
fillStackAndRun(() => new Response('hi'));
}, 'stack overflow in Response constructor should not crash the browser');
</script>
<!doctype html>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="resources/stack-overflow.js"></script>
<script>
test(() => {
const rs = new ReadableStream();
const response = new Response(rs);
fillStackAndRun(() => response.bodyUsed);
}, 'stack overflow in bodyUsed should not crash the browser');
</script>
<!doctype html>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script>
// Running put several times is necessary to trigger the crash, but running too
// many times causes the test to timeout on windows.
let putRunsLeft = 64;
// fillStackAndRun() doesn't not cause a stack overflow in this test. This is
// probably because put() takes two arguments.
function fillStackThenCallPut(foo, request, response) {
try {
fillStackThenCallPut(foo, request, response);
} finally {
// This runs thousands of times, causing the console to spew "Uncaught
// (in promise) TypeError: Response body is already used" messages, but
// it's harmless.
if (putRunsLeft > 0) {
--putRunsLeft;
foo.put(request, response);
}
}
}
promise_test(async t => {
const request = new Request('/');
const response = new Response('foo');
const foo = await caches.open('foo');
t.add_cleanup(() => caches.delete('foo'));
await foo.put(request, response);
assert_throws(
new RangeError(), () => fillStackThenCallPut(foo, request, response),
'fillStackThenCallPut should throw');
}, 'stack overflow in IsLocked() should not crash the browser');
</script>
<!doctype html>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="resources/stack-overflow.js"></script>
<script>
test(() => {
const request = new Request('/', {method: 'POST', body: 'hi'});
fillStackAndRun(() => fetch(request));
}, 'stack overflow during fetch should not crash the browser');
</script>
<!doctype html>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="resources/stack-overflow.js"></script>
<script>
// This test unhelpfully crashes in IsReadableStream when compiled with DCHECKs
// enabled. For best coverage, it should be compiled without DCHECKs.
test(() => {
const rs = new ReadableStream();
const response = new Response(rs);
try {
// This throws different errors depending on how Blink is built, so
// assert_throws() cannot be used.
fillStackAndRun(() => response.clone(), 784);
assert_unreached('stack should overflow');
} catch (e) {
}
}, 'stack overflow in clone() should not crash the browser');
</script>
<!doctype html>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="resources/stack-overflow.js"></script>
<script>
// Trigger the following crash:
// #0 0x7ffff7d8092c base::debug::StackTrace::StackTrace()
// #1 0x7ffff7d80491 base::debug::(anonymous namespace)::StackDumpSignalHandler()
// #2 0x7ffff00130c0 <unknown>
// #3 0x7ffff41c113d v8::internal::GlobalHandles::MakeWeak()
// #4 0x7ffff308198e blink::ReadableStreamBytesConsumer::ReadableStreamBytesConsumer()
// #5 0x7ffff306bc6f blink::BodyStreamBuffer::ReleaseHandle()
// #6 0x7ffff306b883 blink::BodyStreamBuffer::StartLoading()
// #7 0x7ffff306914d blink::Body::blob()
// #8 0x7ffff37a9d20 blink::V8Response::blobMethodCallback()
// #9 0x7ffff3f02097 v8::internal::FunctionCallbackArguments::Call()
// #10 0x7ffff3f015a3 v8::internal::(anonymous namespace)::HandleApiCallHelper<>()
// #11 0x7ffff3f00ca6 v8::internal::Builtin_Impl_HandleApiCall()
// This happens on Linux release build at 70ddf623, no DCHECK. It may be
// possible to reproduce in other environments by bisecting the second argument
// to fillStackAndRun() until you get the above crash.
// See https://bugs.chromium.org/p/chromium/issues/detail?id=829790#c5
test(() => {
const rs = new ReadableStream();
const response = new Response(rs);
try {
// This throws an exception in debug builds but not in release builds.
// If the process doesn't crash, the test passed.
fillStackAndRun(() => response.blob(), 784);
} catch (e) {
}
}, 'stack overflow in response.blob() should not crash the browser');
</script>
// The bugs in this file are necessary to reproduce the crashes. Don't fix them.
// Run |func| with a full stack, so that any Javascript running inside it is hit
// with a stack overflow exception. |extraPadLevels| can be set to a non-zero
// value to add a bit more stack space. Sometimes this makes it possible to
// avoid crashing inside a small uninteresting function and instead crash inside
// the function of interest which is called later and requires more stack
// space. Useful |extraPadLevels| values are not portable and can only be
// determined by experiment.
function fillStackAndRun(func, extraPadLevels = 0) {
try {
padStack(extraPadLevels);
fillStackAndRun(func, extraPadLevels);
} catch (e) {
return func();
}
}
// Recurse |n| levels and then return. If it doesn't throw then an amount of
// stack space proportional to |n| is available.
function padStack(n) {
if (n > 0) padStack(n - 1);
}
...@@ -95,7 +95,8 @@ class CORE_EXPORT V8ScriptRunner final { ...@@ -95,7 +95,8 @@ class CORE_EXPORT V8ScriptRunner final {
const String& file_name, const String& file_name,
const TextPosition&); const TextPosition&);
// Calls a function on the V8 extras binding object. // Utilities for calling functions added to the V8 extras binding object.
template <size_t N> template <size_t N>
static v8::MaybeLocal<v8::Value> CallExtra(ScriptState* script_state, static v8::MaybeLocal<v8::Value> CallExtra(ScriptState* script_state,
const char* name, const char* name,
...@@ -103,6 +104,14 @@ class CORE_EXPORT V8ScriptRunner final { ...@@ -103,6 +104,14 @@ class CORE_EXPORT V8ScriptRunner final {
return CallExtraHelper(script_state, name, N, args); return CallExtraHelper(script_state, name, N, args);
} }
template <size_t N>
static v8::Local<v8::Value> CallExtraOrCrash(
ScriptState* script_state,
const char* name,
v8::Local<v8::Value> (&args)[N]) {
return CallExtraHelper(script_state, name, N, args).ToLocalChecked();
}
// Reports an exception to the message handler, as if it were an uncaught // Reports an exception to the message handler, as if it were an uncaught
// exception. Can only be called on the main thread. // exception. Can only be called on the main thread.
// //
......
...@@ -13,7 +13,6 @@ ...@@ -13,7 +13,6 @@
#include "third_party/blink/renderer/core/streams/readable_stream_operations.h" #include "third_party/blink/renderer/core/streams/readable_stream_operations.h"
#include "third_party/blink/renderer/core/typed_arrays/dom_array_buffer.h" #include "third_party/blink/renderer/core/typed_arrays/dom_array_buffer.h"
#include "third_party/blink/renderer/core/typed_arrays/dom_typed_array.h" #include "third_party/blink/renderer/core/typed_arrays/dom_typed_array.h"
#include "third_party/blink/renderer/platform/bindings/exception_code.h"
#include "third_party/blink/renderer/platform/bindings/exception_state.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/script_state.h"
#include "third_party/blink/renderer/platform/bindings/v8_private_property.h" #include "third_party/blink/renderer/platform/bindings/v8_private_property.h"
...@@ -104,27 +103,12 @@ BodyStreamBuffer::BodyStreamBuffer(ScriptState* script_state, ...@@ -104,27 +103,12 @@ BodyStreamBuffer::BodyStreamBuffer(ScriptState* script_state,
DCHECK(body_value->IsObject()); DCHECK(body_value->IsObject());
v8::Local<v8::Object> body = body_value.As<v8::Object>(); v8::Local<v8::Object> body = body_value.As<v8::Object>();
{ ScriptValue readable_stream = ReadableStreamOperations::CreateReadableStream(
// Leaving an exception pending will cause Blink to crash in the bindings script_state, this,
// code later, so catch instead. ReadableStreamOperations::CreateCountQueuingStrategy(script_state, 0));
v8::TryCatch try_catch(script_state->GetIsolate()); DCHECK(!readable_stream.IsEmpty());
ScriptValue strategy =
ReadableStreamOperations::CreateCountQueuingStrategy(script_state, 0);
if (!strategy.IsEmpty()) {
ScriptValue readable_stream =
ReadableStreamOperations::CreateReadableStream(script_state, this,
strategy);
if (!readable_stream.IsEmpty()) {
V8PrivateProperty::GetInternalBodyStream(script_state->GetIsolate()) V8PrivateProperty::GetInternalBodyStream(script_state->GetIsolate())
.Set(body, readable_stream.V8Value()); .Set(body, readable_stream.V8Value());
} else {
stream_broken_ = true;
}
} else {
stream_broken_ = true;
}
DCHECK_EQ(stream_broken_, try_catch.HasCaught());
}
consumer_->SetClient(this); consumer_->SetClient(this);
if (signal) { if (signal) {
if (signal->aborted()) { if (signal->aborted()) {
...@@ -138,19 +122,12 @@ BodyStreamBuffer::BodyStreamBuffer(ScriptState* script_state, ...@@ -138,19 +122,12 @@ BodyStreamBuffer::BodyStreamBuffer(ScriptState* script_state,
} }
BodyStreamBuffer::BodyStreamBuffer(ScriptState* script_state, BodyStreamBuffer::BodyStreamBuffer(ScriptState* script_state,
ScriptValue stream, ScriptValue stream)
ExceptionState& exception_state)
: UnderlyingSourceBase(script_state), : UnderlyingSourceBase(script_state),
script_state_(script_state), script_state_(script_state),
signal_(nullptr), signal_(nullptr),
made_from_readable_stream_(true) { made_from_readable_stream_(true) {
DCHECK(ReadableStreamOperations::IsReadableStream(script_state, stream, DCHECK(ReadableStreamOperations::IsReadableStream(script_state, stream));
exception_state)
.value_or(true));
if (exception_state.HadException())
return;
v8::Local<v8::Value> body_value = ToV8(this, script_state); v8::Local<v8::Value> body_value = ToV8(this, script_state);
DCHECK(!body_value.IsEmpty()); DCHECK(!body_value.IsEmpty());
DCHECK(body_value->IsObject()); DCHECK(body_value->IsObject());
...@@ -161,10 +138,6 @@ BodyStreamBuffer::BodyStreamBuffer(ScriptState* script_state, ...@@ -161,10 +138,6 @@ BodyStreamBuffer::BodyStreamBuffer(ScriptState* script_state,
} }
ScriptValue BodyStreamBuffer::Stream() { ScriptValue BodyStreamBuffer::Stream() {
// Since this is the implementation of response.body, we return the stream
// even if |stream_broken_| is true, so that expected Javascript attribute
// behaviour is not changed. User code is still permitted to access the
// stream even when it has thrown an exception.
ScriptState::Scope scope(script_state_.get()); ScriptState::Scope scope(script_state_.get());
v8::Local<v8::Value> body_value = ToV8(this, script_state_.get()); v8::Local<v8::Value> body_value = ToV8(this, script_state_.get());
DCHECK(!body_value.IsEmpty()); DCHECK(!body_value.IsEmpty());
...@@ -231,42 +204,18 @@ void BodyStreamBuffer::StartLoading(FetchDataLoader* loader, ...@@ -231,42 +204,18 @@ void BodyStreamBuffer::StartLoading(FetchDataLoader* loader,
} }
void BodyStreamBuffer::Tee(BodyStreamBuffer** branch1, void BodyStreamBuffer::Tee(BodyStreamBuffer** branch1,
BodyStreamBuffer** branch2, BodyStreamBuffer** branch2) {
ExceptionState& exception_state) {
DCHECK(!IsStreamLocked()); DCHECK(!IsStreamLocked());
DCHECK(!IsStreamDisturbed()); DCHECK(!IsStreamDisturbed());
*branch1 = nullptr; *branch1 = nullptr;
*branch2 = nullptr; *branch2 = nullptr;
if (made_from_readable_stream_) { if (made_from_readable_stream_) {
if (stream_broken_) {
// We don't really know what state the stream is in, so throw an exception
// rather than making things worse.
exception_state.ThrowDOMException(
DOMExceptionCode::kInvalidStateError,
"Unsafe to tee stream in unknown state");
return;
}
ScriptValue stream1, stream2; ScriptValue stream1, stream2;
ReadableStreamOperations::Tee(script_state_.get(), Stream(), &stream1, ReadableStreamOperations::Tee(script_state_.get(), Stream(), &stream1,
&stream2, exception_state); &stream2);
if (exception_state.HadException()) { *branch1 = new BodyStreamBuffer(script_state_.get(), stream1);
stream_broken_ = true; *branch2 = new BodyStreamBuffer(script_state_.get(), stream2);
return;
}
// Exceptions here imply that |stream1| and/or |stream2| are broken, not the
// stream owned by this object, so we shouldn't set |stream_broken_|.
auto* tmp1 =
new BodyStreamBuffer(script_state_.get(), stream1, exception_state);
if (exception_state.HadException())
return;
auto* tmp2 =
new BodyStreamBuffer(script_state_.get(), stream2, exception_state);
if (exception_state.HadException())
return;
*branch1 = tmp1;
*branch2 = tmp2;
return; return;
} }
BytesConsumer* dest1 = nullptr; BytesConsumer* dest1 = nullptr;
...@@ -333,28 +282,28 @@ void BodyStreamBuffer::ContextDestroyed(ExecutionContext* destroyed_context) { ...@@ -333,28 +282,28 @@ void BodyStreamBuffer::ContextDestroyed(ExecutionContext* destroyed_context) {
} }
bool BodyStreamBuffer::IsStreamReadable() { bool BodyStreamBuffer::IsStreamReadable() {
return BooleanStreamOperationOrFallback(ReadableStreamOperations::IsReadable, ScriptState::Scope scope(script_state_.get());
false); return ReadableStreamOperations::IsReadable(script_state_.get(), Stream());
} }
bool BodyStreamBuffer::IsStreamClosed() { bool BodyStreamBuffer::IsStreamClosed() {
return BooleanStreamOperationOrFallback(ReadableStreamOperations::IsClosed, ScriptState::Scope scope(script_state_.get());
true); return ReadableStreamOperations::IsClosed(script_state_.get(), Stream());
} }
bool BodyStreamBuffer::IsStreamErrored() { bool BodyStreamBuffer::IsStreamErrored() {
return BooleanStreamOperationOrFallback(ReadableStreamOperations::IsErrored, ScriptState::Scope scope(script_state_.get());
true); return ReadableStreamOperations::IsErrored(script_state_.get(), Stream());
} }
bool BodyStreamBuffer::IsStreamLocked() { bool BodyStreamBuffer::IsStreamLocked() {
return BooleanStreamOperationOrFallback(ReadableStreamOperations::IsLocked, ScriptState::Scope scope(script_state_.get());
true); return ReadableStreamOperations::IsLocked(script_state_.get(), Stream());
} }
bool BodyStreamBuffer::IsStreamDisturbed() { bool BodyStreamBuffer::IsStreamDisturbed() {
return BooleanStreamOperationOrFallback(ReadableStreamOperations::IsDisturbed, ScriptState::Scope scope(script_state_.get());
true); return ReadableStreamOperations::IsDisturbed(script_state_.get(), Stream());
} }
void BodyStreamBuffer::CloseAndLockAndDisturb() { void BodyStreamBuffer::CloseAndLockAndDisturb() {
...@@ -363,14 +312,11 @@ void BodyStreamBuffer::CloseAndLockAndDisturb() { ...@@ -363,14 +312,11 @@ void BodyStreamBuffer::CloseAndLockAndDisturb() {
// the internal buffer. // the internal buffer.
Close(); Close();
} }
if (stream_broken_)
return;
ScriptState::Scope scope(script_state_.get()); ScriptState::Scope scope(script_state_.get());
ScriptValue reader = NonThrowableExceptionState exception_state;
ReadableStreamOperations::GetReader(script_state_.get(), Stream()); ScriptValue reader = ReadableStreamOperations::GetReader(
if (reader.IsEmpty()) script_state_.get(), Stream(), exception_state);
return;
ReadableStreamOperations::DefaultReaderRead(script_state_.get(), reader); ReadableStreamOperations::DefaultReaderRead(script_state_.get(), reader);
} }
...@@ -463,29 +409,10 @@ void BodyStreamBuffer::StopLoading() { ...@@ -463,29 +409,10 @@ void BodyStreamBuffer::StopLoading() {
loader_ = nullptr; loader_ = nullptr;
} }
bool BodyStreamBuffer::BooleanStreamOperationOrFallback(
base::Optional<bool> (*predicate)(ScriptState*, ScriptValue),
bool fallback_value) {
if (stream_broken_)
return fallback_value;
ScriptState::Scope scope(script_state_.get());
const base::Optional<bool> result = predicate(script_state_.get(), Stream());
if (!result.has_value()) {
stream_broken_ = true;
return fallback_value;
}
return result.value();
}
BytesConsumer* BodyStreamBuffer::ReleaseHandle() { BytesConsumer* BodyStreamBuffer::ReleaseHandle() {
DCHECK(!IsStreamLocked()); DCHECK(!IsStreamLocked());
DCHECK(!IsStreamDisturbed()); DCHECK(!IsStreamDisturbed());
if (stream_broken_) {
return BytesConsumer::CreateErrored(
BytesConsumer::Error("ReleaseHandle called with broken stream"));
}
if (made_from_readable_stream_) { if (made_from_readable_stream_) {
ScriptState::Scope scope(script_state_.get()); ScriptState::Scope scope(script_state_.get());
// We need to have |reader| alive by some means (as written in // We need to have |reader| alive by some means (as written in
...@@ -494,13 +421,9 @@ BytesConsumer* BodyStreamBuffer::ReleaseHandle() { ...@@ -494,13 +421,9 @@ BytesConsumer* BodyStreamBuffer::ReleaseHandle() {
// - This branch cannot be taken when called from tee. // - This branch cannot be taken when called from tee.
// - startLoading makes hasPendingActivity return true while loading. // - startLoading makes hasPendingActivity return true while loading.
// , we don't need to keep the reader explicitly. // , we don't need to keep the reader explicitly.
ScriptValue reader = NonThrowableExceptionState exception_state;
ReadableStreamOperations::GetReader(script_state_.get(), Stream()); ScriptValue reader = ReadableStreamOperations::GetReader(
if (reader.IsEmpty()) { script_state_.get(), Stream(), exception_state);
stream_broken_ = true;
return BytesConsumer::CreateErrored(
BytesConsumer::Error("Failed to GetReader in ReleaseHandle"));
}
return new ReadableStreamBytesConsumer(script_state_.get(), reader); return new ReadableStreamBytesConsumer(script_state_.get(), reader);
} }
// We need to call these before calling closeAndLockAndDisturb. // We need to call these before calling closeAndLockAndDisturb.
......
...@@ -6,7 +6,6 @@ ...@@ -6,7 +6,6 @@
#define THIRD_PARTY_BLINK_RENDERER_CORE_FETCH_BODY_STREAM_BUFFER_H_ #define THIRD_PARTY_BLINK_RENDERER_CORE_FETCH_BODY_STREAM_BUFFER_H_
#include <memory> #include <memory>
#include "base/optional.h"
#include "third_party/blink/public/platform/web_data_consumer_handle.h" #include "third_party/blink/public/platform/web_data_consumer_handle.h"
#include "third_party/blink/renderer/bindings/core/v8/script_promise.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/script_value.h"
...@@ -21,7 +20,6 @@ ...@@ -21,7 +20,6 @@
namespace blink { namespace blink {
class EncodedFormData; class EncodedFormData;
class ExceptionState;
class ScriptState; class ScriptState;
class CORE_EXPORT BodyStreamBuffer final : public UnderlyingSourceBase, class CORE_EXPORT BodyStreamBuffer final : public UnderlyingSourceBase,
...@@ -38,7 +36,7 @@ class CORE_EXPORT BodyStreamBuffer final : public UnderlyingSourceBase, ...@@ -38,7 +36,7 @@ class CORE_EXPORT BodyStreamBuffer final : public UnderlyingSourceBase,
AbortSignal* /* signal */); AbortSignal* /* signal */);
// |ReadableStreamOperations::isReadableStream(stream)| must hold. // |ReadableStreamOperations::isReadableStream(stream)| must hold.
// This function must be called with entering an appropriate V8 context. // This function must be called with entering an appropriate V8 context.
BodyStreamBuffer(ScriptState*, ScriptValue stream, ExceptionState&); BodyStreamBuffer(ScriptState*, ScriptValue stream);
ScriptValue Stream(); ScriptValue Stream();
...@@ -47,7 +45,7 @@ class CORE_EXPORT BodyStreamBuffer final : public UnderlyingSourceBase, ...@@ -47,7 +45,7 @@ class CORE_EXPORT BodyStreamBuffer final : public UnderlyingSourceBase,
BytesConsumer::BlobSizePolicy); BytesConsumer::BlobSizePolicy);
scoped_refptr<EncodedFormData> DrainAsFormData(); scoped_refptr<EncodedFormData> DrainAsFormData();
void StartLoading(FetchDataLoader*, FetchDataLoader::Client* /* client */); void StartLoading(FetchDataLoader*, FetchDataLoader::Client* /* client */);
void Tee(BodyStreamBuffer**, BodyStreamBuffer**, ExceptionState&); void Tee(BodyStreamBuffer**, BodyStreamBuffer**);
// UnderlyingSourceBase // UnderlyingSourceBase
ScriptPromise pull(ScriptState*) override; ScriptPromise pull(ScriptState*) override;
...@@ -88,13 +86,6 @@ class CORE_EXPORT BodyStreamBuffer final : public UnderlyingSourceBase, ...@@ -88,13 +86,6 @@ class CORE_EXPORT BodyStreamBuffer final : public UnderlyingSourceBase,
void EndLoading(); void EndLoading();
void StopLoading(); void StopLoading();
// Implementation of IsStream*() methods. Sets |stream_broken_| if |predicate|
// returns empty. Returns |fallback_value| if |stream_broken_| is or becomes
// true.
bool BooleanStreamOperationOrFallback(
base::Optional<bool> (*predicate)(ScriptState*, ScriptValue),
bool fallback_value);
scoped_refptr<ScriptState> script_state_; scoped_refptr<ScriptState> script_state_;
Member<BytesConsumer> consumer_; Member<BytesConsumer> consumer_;
// We need this member to keep it alive while loading. // We need this member to keep it alive while loading.
...@@ -105,7 +96,6 @@ class CORE_EXPORT BodyStreamBuffer final : public UnderlyingSourceBase, ...@@ -105,7 +96,6 @@ class CORE_EXPORT BodyStreamBuffer final : public UnderlyingSourceBase,
bool stream_needs_more_ = false; bool stream_needs_more_ = false;
bool made_from_readable_stream_; bool made_from_readable_stream_;
bool in_process_data_ = false; bool in_process_data_ = false;
bool stream_broken_ = false;
DISALLOW_COPY_AND_ASSIGN(BodyStreamBuffer); DISALLOW_COPY_AND_ASSIGN(BodyStreamBuffer);
}; };
......
...@@ -14,7 +14,6 @@ ...@@ -14,7 +14,6 @@
#include "third_party/blink/renderer/core/fetch/bytes_consumer_test_util.h" #include "third_party/blink/renderer/core/fetch/bytes_consumer_test_util.h"
#include "third_party/blink/renderer/core/fetch/form_data_bytes_consumer.h" #include "third_party/blink/renderer/core/fetch/form_data_bytes_consumer.h"
#include "third_party/blink/renderer/core/html/forms/form_data.h" #include "third_party/blink/renderer/core/html/forms/form_data.h"
#include "third_party/blink/renderer/platform/bindings/exception_state.h"
#include "third_party/blink/renderer/platform/blob/blob_data.h" #include "third_party/blink/renderer/platform/blob/blob_data.h"
#include "third_party/blink/renderer/platform/blob/blob_url.h" #include "third_party/blink/renderer/platform/blob/blob_url.h"
#include "third_party/blink/renderer/platform/heap/garbage_collected.h" #include "third_party/blink/renderer/platform/heap/garbage_collected.h"
...@@ -88,7 +87,6 @@ class MockFetchDataLoader : public FetchDataLoader { ...@@ -88,7 +87,6 @@ class MockFetchDataLoader : public FetchDataLoader {
TEST_F(BodyStreamBufferTest, Tee) { TEST_F(BodyStreamBufferTest, Tee) {
V8TestingScope scope; V8TestingScope scope;
NonThrowableExceptionState exception_state;
Checkpoint checkpoint; Checkpoint checkpoint;
MockFetchDataLoaderClient* client1 = MockFetchDataLoaderClient::Create(); MockFetchDataLoaderClient* client1 = MockFetchDataLoaderClient::Create();
MockFetchDataLoaderClient* client2 = MockFetchDataLoaderClient::Create(); MockFetchDataLoaderClient* client2 = MockFetchDataLoaderClient::Create();
...@@ -112,7 +110,7 @@ TEST_F(BodyStreamBufferTest, Tee) { ...@@ -112,7 +110,7 @@ TEST_F(BodyStreamBufferTest, Tee) {
BodyStreamBuffer* new1; BodyStreamBuffer* new1;
BodyStreamBuffer* new2; BodyStreamBuffer* new2;
buffer->Tee(&new1, &new2, exception_state); buffer->Tee(&new1, &new2);
EXPECT_TRUE(buffer->IsStreamLocked()); EXPECT_TRUE(buffer->IsStreamLocked());
EXPECT_TRUE(buffer->IsStreamDisturbed()); EXPECT_TRUE(buffer->IsStreamDisturbed());
...@@ -132,7 +130,6 @@ TEST_F(BodyStreamBufferTest, Tee) { ...@@ -132,7 +130,6 @@ TEST_F(BodyStreamBufferTest, Tee) {
TEST_F(BodyStreamBufferTest, TeeFromHandleMadeFromStream) { TEST_F(BodyStreamBufferTest, TeeFromHandleMadeFromStream) {
V8TestingScope scope; V8TestingScope scope;
NonThrowableExceptionState exception_state;
ScriptValue stream = EvalWithPrintingError( ScriptValue stream = EvalWithPrintingError(
scope.GetScriptState(), scope.GetScriptState(),
"stream = new ReadableStream({start: c => controller = c});" "stream = new ReadableStream({start: c => controller = c});"
...@@ -153,11 +150,11 @@ TEST_F(BodyStreamBufferTest, TeeFromHandleMadeFromStream) { ...@@ -153,11 +150,11 @@ TEST_F(BodyStreamBufferTest, TeeFromHandleMadeFromStream) {
EXPECT_CALL(checkpoint, Call(4)); EXPECT_CALL(checkpoint, Call(4));
BodyStreamBuffer* buffer = BodyStreamBuffer* buffer =
new BodyStreamBuffer(scope.GetScriptState(), stream, exception_state); new BodyStreamBuffer(scope.GetScriptState(), stream);
BodyStreamBuffer* new1; BodyStreamBuffer* new1;
BodyStreamBuffer* new2; BodyStreamBuffer* new2;
buffer->Tee(&new1, &new2, exception_state); buffer->Tee(&new1, &new2);
EXPECT_TRUE(buffer->IsStreamLocked()); EXPECT_TRUE(buffer->IsStreamLocked());
// Note that this behavior is slightly different from for the behavior of // Note that this behavior is slightly different from for the behavior of
...@@ -231,11 +228,10 @@ TEST_F(BodyStreamBufferTest, DrainAsBlobDataHandleReturnsNull) { ...@@ -231,11 +228,10 @@ TEST_F(BodyStreamBufferTest, DrainAsBlobDataHandleReturnsNull) {
TEST_F(BodyStreamBufferTest, TEST_F(BodyStreamBufferTest,
DrainAsBlobFromBufferMadeFromBufferMadeFromStream) { DrainAsBlobFromBufferMadeFromBufferMadeFromStream) {
V8TestingScope scope; V8TestingScope scope;
NonThrowableExceptionState exception_state;
ScriptValue stream = ScriptValue stream =
EvalWithPrintingError(scope.GetScriptState(), "new ReadableStream()"); EvalWithPrintingError(scope.GetScriptState(), "new ReadableStream()");
BodyStreamBuffer* buffer = BodyStreamBuffer* buffer =
new BodyStreamBuffer(scope.GetScriptState(), stream, exception_state); new BodyStreamBuffer(scope.GetScriptState(), stream);
EXPECT_FALSE(buffer->HasPendingActivity()); EXPECT_FALSE(buffer->HasPendingActivity());
EXPECT_FALSE(buffer->IsStreamLocked()); EXPECT_FALSE(buffer->IsStreamLocked());
...@@ -297,11 +293,10 @@ TEST_F(BodyStreamBufferTest, DrainAsFormDataReturnsNull) { ...@@ -297,11 +293,10 @@ TEST_F(BodyStreamBufferTest, DrainAsFormDataReturnsNull) {
TEST_F(BodyStreamBufferTest, TEST_F(BodyStreamBufferTest,
DrainAsFormDataFromBufferMadeFromBufferMadeFromStream) { DrainAsFormDataFromBufferMadeFromBufferMadeFromStream) {
V8TestingScope scope; V8TestingScope scope;
NonThrowableExceptionState exception_state;
ScriptValue stream = ScriptValue stream =
EvalWithPrintingError(scope.GetScriptState(), "new ReadableStream()"); EvalWithPrintingError(scope.GetScriptState(), "new ReadableStream()");
BodyStreamBuffer* buffer = BodyStreamBuffer* buffer =
new BodyStreamBuffer(scope.GetScriptState(), stream, exception_state); new BodyStreamBuffer(scope.GetScriptState(), stream);
EXPECT_FALSE(buffer->HasPendingActivity()); EXPECT_FALSE(buffer->HasPendingActivity());
EXPECT_FALSE(buffer->IsStreamLocked()); EXPECT_FALSE(buffer->IsStreamLocked());
......
...@@ -13,7 +13,6 @@ ...@@ -13,7 +13,6 @@
#include "third_party/blink/renderer/core/fetch/fetch_header_list.h" #include "third_party/blink/renderer/core/fetch/fetch_header_list.h"
#include "third_party/blink/renderer/core/fetch/form_data_bytes_consumer.h" #include "third_party/blink/renderer/core/fetch/form_data_bytes_consumer.h"
#include "third_party/blink/renderer/core/loader/threadable_loader.h" #include "third_party/blink/renderer/core/loader/threadable_loader.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/script_state.h"
#include "third_party/blink/renderer/platform/loader/fetch/resource_loader_options.h" #include "third_party/blink/renderer/platform/loader/fetch/resource_loader_options.h"
#include "third_party/blink/renderer/platform/loader/fetch/resource_request.h" #include "third_party/blink/renderer/platform/loader/fetch/resource_request.h"
...@@ -84,15 +83,12 @@ FetchRequestData* FetchRequestData::CloneExceptBody() { ...@@ -84,15 +83,12 @@ FetchRequestData* FetchRequestData::CloneExceptBody() {
return request; return request;
} }
FetchRequestData* FetchRequestData::Clone(ScriptState* script_state, FetchRequestData* FetchRequestData::Clone(ScriptState* script_state) {
ExceptionState& exception_state) {
FetchRequestData* request = FetchRequestData::CloneExceptBody(); FetchRequestData* request = FetchRequestData::CloneExceptBody();
if (buffer_) { if (buffer_) {
BodyStreamBuffer* new1 = nullptr; BodyStreamBuffer* new1 = nullptr;
BodyStreamBuffer* new2 = nullptr; BodyStreamBuffer* new2 = nullptr;
buffer_->Tee(&new1, &new2, exception_state); buffer_->Tee(&new1, &new2);
if (exception_state.HadException())
return nullptr;
buffer_ = new1; buffer_ = new1;
request->buffer_ = new2; request->buffer_ = new2;
} }
......
...@@ -22,7 +22,6 @@ ...@@ -22,7 +22,6 @@
namespace blink { namespace blink {
class BodyStreamBuffer; class BodyStreamBuffer;
class ExceptionState;
class FetchHeaderList; class FetchHeaderList;
class SecurityOrigin; class SecurityOrigin;
class ScriptState; class ScriptState;
...@@ -36,7 +35,7 @@ class FetchRequestData final ...@@ -36,7 +35,7 @@ class FetchRequestData final
static FetchRequestData* Create(); static FetchRequestData* Create();
static FetchRequestData* Create(ScriptState*, const WebServiceWorkerRequest&); static FetchRequestData* Create(ScriptState*, const WebServiceWorkerRequest&);
// Call Request::refreshBody() after calling clone() or pass(). // Call Request::refreshBody() after calling clone() or pass().
FetchRequestData* Clone(ScriptState*, ExceptionState&); FetchRequestData* Clone(ScriptState*);
FetchRequestData* Pass(ScriptState*); FetchRequestData* Pass(ScriptState*);
~FetchRequestData(); ~FetchRequestData();
......
...@@ -8,7 +8,6 @@ ...@@ -8,7 +8,6 @@
#include "third_party/blink/renderer/core/fetch/body_stream_buffer.h" #include "third_party/blink/renderer/core/fetch/body_stream_buffer.h"
#include "third_party/blink/renderer/core/fetch/fetch_header_list.h" #include "third_party/blink/renderer/core/fetch/fetch_header_list.h"
#include "third_party/blink/renderer/core/typed_arrays/dom_array_buffer.h" #include "third_party/blink/renderer/core/typed_arrays/dom_array_buffer.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/script_state.h"
#include "third_party/blink/renderer/platform/loader/fetch/fetch_utils.h" #include "third_party/blink/renderer/platform/loader/fetch/fetch_utils.h"
#include "third_party/blink/renderer/platform/network/http_names.h" #include "third_party/blink/renderer/platform/network/http_names.h"
...@@ -172,8 +171,7 @@ const Vector<KURL>& FetchResponseData::InternalURLList() const { ...@@ -172,8 +171,7 @@ const Vector<KURL>& FetchResponseData::InternalURLList() const {
return url_list_; return url_list_;
} }
FetchResponseData* FetchResponseData::Clone(ScriptState* script_state, FetchResponseData* FetchResponseData::Clone(ScriptState* script_state) {
ExceptionState& exception_state) {
FetchResponseData* new_response = Create(); FetchResponseData* new_response = Create();
new_response->type_ = type_; new_response->type_ = type_;
if (termination_reason_) { if (termination_reason_) {
...@@ -196,9 +194,7 @@ FetchResponseData* FetchResponseData::Clone(ScriptState* script_state, ...@@ -196,9 +194,7 @@ FetchResponseData* FetchResponseData::Clone(ScriptState* script_state,
DCHECK_EQ(buffer_, internal_response_->buffer_); DCHECK_EQ(buffer_, internal_response_->buffer_);
DCHECK_EQ(internal_response_->type_, Type::kDefault); DCHECK_EQ(internal_response_->type_, Type::kDefault);
new_response->internal_response_ = new_response->internal_response_ =
internal_response_->Clone(script_state, exception_state); internal_response_->Clone(script_state);
if (exception_state.HadException())
return nullptr;
buffer_ = internal_response_->buffer_; buffer_ = internal_response_->buffer_;
new_response->buffer_ = new_response->internal_response_->buffer_; new_response->buffer_ = new_response->internal_response_->buffer_;
break; break;
...@@ -207,9 +203,7 @@ FetchResponseData* FetchResponseData::Clone(ScriptState* script_state, ...@@ -207,9 +203,7 @@ FetchResponseData* FetchResponseData::Clone(ScriptState* script_state,
if (buffer_) { if (buffer_) {
BodyStreamBuffer* new1 = nullptr; BodyStreamBuffer* new1 = nullptr;
BodyStreamBuffer* new2 = nullptr; BodyStreamBuffer* new2 = nullptr;
buffer_->Tee(&new1, &new2, exception_state); buffer_->Tee(&new1, &new2);
if (exception_state.HadException())
return nullptr;
buffer_ = new1; buffer_ = new1;
new_response->buffer_ = new2; new_response->buffer_ = new2;
} }
...@@ -225,9 +219,7 @@ FetchResponseData* FetchResponseData::Clone(ScriptState* script_state, ...@@ -225,9 +219,7 @@ FetchResponseData* FetchResponseData::Clone(ScriptState* script_state,
DCHECK(!buffer_); DCHECK(!buffer_);
DCHECK_EQ(internal_response_->type_, Type::kDefault); DCHECK_EQ(internal_response_->type_, Type::kDefault);
new_response->internal_response_ = new_response->internal_response_ =
internal_response_->Clone(script_state, exception_state); internal_response_->Clone(script_state);
if (exception_state.HadException())
return nullptr;
break; break;
} }
return new_response; return new_response;
......
...@@ -24,7 +24,6 @@ ...@@ -24,7 +24,6 @@
namespace blink { namespace blink {
class BodyStreamBuffer; class BodyStreamBuffer;
class ExceptionState;
class FetchHeaderList; class FetchHeaderList;
class ScriptState; class ScriptState;
class WebServiceWorkerResponse; class WebServiceWorkerResponse;
...@@ -55,7 +54,7 @@ class CORE_EXPORT FetchResponseData final ...@@ -55,7 +54,7 @@ class CORE_EXPORT FetchResponseData final
return internal_response_; return internal_response_;
} }
FetchResponseData* Clone(ScriptState*, ExceptionState& exception_state); FetchResponseData* Clone(ScriptState*);
network::mojom::FetchResponseType GetType() const { return type_; } network::mojom::FetchResponseType GetType() const { return type_; }
const KURL* Url() const; const KURL* Url() const;
......
...@@ -83,8 +83,9 @@ class ReadableStreamBytesConsumerTest : public testing::Test { ...@@ -83,8 +83,9 @@ class ReadableStreamBytesConsumerTest : public testing::Test {
} }
ReadableStreamBytesConsumer* CreateConsumer(ScriptValue stream) { ReadableStreamBytesConsumer* CreateConsumer(ScriptValue stream) {
NonThrowableExceptionState es;
ScriptValue reader = ScriptValue reader =
ReadableStreamOperations::GetReader(GetScriptState(), stream); ReadableStreamOperations::GetReader(GetScriptState(), stream, es);
DCHECK(!reader.IsEmpty()); DCHECK(!reader.IsEmpty());
DCHECK(reader.V8Value()->IsObject()); DCHECK(reader.V8Value()->IsObject());
return new ReadableStreamBytesConsumer(GetScriptState(), reader); return new ReadableStreamBytesConsumer(GetScriptState(), reader);
......
...@@ -26,7 +26,6 @@ ...@@ -26,7 +26,6 @@
#include "third_party/blink/renderer/core/html/forms/form_data.h" #include "third_party/blink/renderer/core/html/forms/form_data.h"
#include "third_party/blink/renderer/core/loader/threadable_loader.h" #include "third_party/blink/renderer/core/loader/threadable_loader.h"
#include "third_party/blink/renderer/core/url/url_search_params.h" #include "third_party/blink/renderer/core/url/url_search_params.h"
#include "third_party/blink/renderer/platform/bindings/exception_state.h"
#include "third_party/blink/renderer/platform/bindings/v8_private_property.h" #include "third_party/blink/renderer/platform/bindings/v8_private_property.h"
#include "third_party/blink/renderer/platform/blob/blob_data.h" #include "third_party/blink/renderer/platform/blob/blob_data.h"
#include "third_party/blink/renderer/platform/loader/cors/cors.h" #include "third_party/blink/renderer/platform/loader/cors/cors.h"
...@@ -831,9 +830,7 @@ Request* Request::clone(ScriptState* script_state, ...@@ -831,9 +830,7 @@ Request* Request::clone(ScriptState* script_state,
return nullptr; return nullptr;
} }
FetchRequestData* request = request_->Clone(script_state, exception_state); FetchRequestData* request = request_->Clone(script_state);
if (exception_state.HadException())
return nullptr;
RefreshBody(script_state); RefreshBody(script_state);
Headers* headers = Headers::Create(request->HeaderList()); Headers* headers = Headers::Create(request->HeaderList());
headers->SetGuard(headers_->GetGuard()); headers->SetGuard(headers_->GetGuard());
......
...@@ -22,7 +22,6 @@ namespace blink { ...@@ -22,7 +22,6 @@ namespace blink {
class AbortSignal; class AbortSignal;
class BodyStreamBuffer; class BodyStreamBuffer;
class ExceptionState;
class RequestInit; class RequestInit;
class WebServiceWorkerRequest; class WebServiceWorkerRequest;
......
...@@ -236,17 +236,11 @@ Response* Response::Create(ScriptState* script_state, ...@@ -236,17 +236,11 @@ Response* Response::Create(ScriptState* script_state,
new FormDataBytesConsumer(execution_context, std::move(form_data)), new FormDataBytesConsumer(execution_context, std::move(form_data)),
nullptr /* AbortSignal */); nullptr /* AbortSignal */);
content_type = "application/x-www-form-urlencoded;charset=UTF-8"; content_type = "application/x-www-form-urlencoded;charset=UTF-8";
} else if (ReadableStreamOperations::IsReadableStream( } else if (ReadableStreamOperations::IsReadableStream(script_state,
script_state, body_value, exception_state) body_value)) {
.value_or(true)) {
if (exception_state.HadException())
return nullptr;
UseCounter::Count(execution_context, UseCounter::Count(execution_context,
WebFeature::kFetchResponseConstructionWithStream); WebFeature::kFetchResponseConstructionWithStream);
body_buffer = body_buffer = new BodyStreamBuffer(script_state, body_value);
new BodyStreamBuffer(script_state, body_value, exception_state);
if (exception_state.HadException())
return nullptr;
} else { } else {
String string = NativeValueTraits<IDLUSVString>::NativeValue( String string = NativeValueTraits<IDLUSVString>::NativeValue(
isolate, body, exception_state); isolate, body, exception_state);
...@@ -456,9 +450,7 @@ Response* Response::clone(ScriptState* script_state, ...@@ -456,9 +450,7 @@ Response* Response::clone(ScriptState* script_state,
return nullptr; return nullptr;
} }
FetchResponseData* response = response_->Clone(script_state, exception_state); FetchResponseData* response = response_->Clone(script_state);
if (exception_state.HadException())
return nullptr;
RefreshBody(script_state); RefreshBody(script_state);
Headers* headers = Headers::Create(response->HeaderList()); Headers* headers = Headers::Create(response->HeaderList());
headers->SetGuard(headers_->GetGuard()); headers->SetGuard(headers_->GetGuard());
......
...@@ -4,102 +4,14 @@ ...@@ -4,102 +4,14 @@
#include "third_party/blink/renderer/core/streams/readable_stream_operations.h" #include "third_party/blink/renderer/core/streams/readable_stream_operations.h"
#include <utility>
#include "third_party/blink/renderer/bindings/core/v8/to_v8_for_core.h" #include "third_party/blink/renderer/bindings/core/v8/to_v8_for_core.h"
#include "third_party/blink/renderer/bindings/core/v8/v8_script_runner.h" #include "third_party/blink/renderer/bindings/core/v8/v8_script_runner.h"
#include "third_party/blink/renderer/core/streams/underlying_source_base.h" #include "third_party/blink/renderer/core/streams/underlying_source_base.h"
#include "third_party/blink/renderer/platform/bindings/exception_state.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/script_state.h"
#include "third_party/blink/renderer/platform/wtf/assertions.h"
namespace blink { namespace blink {
namespace {
base::Optional<bool> BooleanOperation(ScriptState* script_state,
ScriptValue value,
const char* operation) {
v8::Local<v8::Value> args[] = {value.V8Value()};
v8::Local<v8::Value> result_value;
bool result_bool = false;
if (V8ScriptRunner::CallExtra(script_state, operation, args)
.ToLocal(&result_value) &&
result_value->BooleanValue(script_state->GetContext()).To(&result_bool)) {
return result_bool;
}
return base::nullopt;
}
base::Optional<bool> BooleanOperationWithRethrow(
ScriptState* script_state,
ScriptValue value,
const char* operation,
ExceptionState& exception_state) {
DCHECK(!value.IsEmpty());
if (!value.IsObject())
return false;
v8::TryCatch block(script_state->GetIsolate());
v8::Local<v8::Value> args[] = {value.V8Value()};
v8::Local<v8::Value> local_value;
bool result_bool = false;
if (!V8ScriptRunner::CallExtra(script_state, operation, args)
.ToLocal(&local_value) ||
!local_value->BooleanValue(script_state->GetContext()).To(&result_bool)) {
DCHECK(block.HasCaught());
exception_state.RethrowV8Exception(block.Exception());
return base::nullopt;
}
DCHECK(!block.HasCaught());
return result_bool;
}
// Performs |operation| on |value|, catching any exceptions. This is for use in
// DCHECK(). It is unsafe for general use because it ignores errors. Returns
// |fallback_value|, which must be chosen so that the DCHECK() passed if an
// exception was thrown, so that the behaviour matches a release build.
bool BooleanOperationForDCheck(ScriptState* script_state,
ScriptValue value,
const char* operation,
bool fallback_value) {
v8::TryCatch block(script_state->GetIsolate());
base::Optional<bool> result =
BooleanOperation(script_state, value, operation);
if (block.HasCaught()) {
DCHECK(!result.has_value());
return fallback_value;
}
DCHECK(result.has_value());
return result.value();
}
// Performs IsReadableStream(value), catching exceptions. Should only be used in
// DCHECK(). Returns true on exception.
bool IsReadableStreamForDCheck(ScriptState* script_state, ScriptValue value) {
return BooleanOperationForDCheck(script_state, value, "IsReadableStream",
true);
}
// Performs IsReadableStreamLocked(stream), catching exceptions. Should only be
// used in DCHECK(). Returns false on exception.
bool IsLockedForDCheck(ScriptState* script_state, ScriptValue stream) {
return BooleanOperationForDCheck(script_state, stream,
"IsReadableStreamLocked", false);
}
// Performs IsReadableStreamDefaultReader(value), catching exceptions. Should
// only be used in DCHECK(). Returns true on exception.
bool IsDefaultReaderForDCheck(ScriptState* script_state, ScriptValue value) {
return BooleanOperationForDCheck(script_state, value,
"IsReadableStreamDefaultReader", true);
}
} // namespace
ScriptValue ReadableStreamOperations::CreateReadableStream( ScriptValue ReadableStreamOperations::CreateReadableStream(
ScriptState* script_state, ScriptState* script_state,
UnderlyingSourceBase* underlying_source, UnderlyingSourceBase* underlying_source,
...@@ -112,7 +24,7 @@ ScriptValue ReadableStreamOperations::CreateReadableStream( ...@@ -112,7 +24,7 @@ ScriptValue ReadableStreamOperations::CreateReadableStream(
v8::Local<v8::Value> args[] = {js_underlying_source, js_strategy}; v8::Local<v8::Value> args[] = {js_underlying_source, js_strategy};
return ScriptValue( return ScriptValue(
script_state, script_state,
V8ScriptRunner::CallExtra( V8ScriptRunner::CallExtraOrCrash(
script_state, "createReadableStreamWithExternalController", args)); script_state, "createReadableStreamWithExternalController", args));
} }
...@@ -125,13 +37,14 @@ ScriptValue ReadableStreamOperations::CreateCountQueuingStrategy( ...@@ -125,13 +37,14 @@ ScriptValue ReadableStreamOperations::CreateCountQueuingStrategy(
v8::Number::New(script_state->GetIsolate(), high_water_mark)}; v8::Number::New(script_state->GetIsolate(), high_water_mark)};
return ScriptValue( return ScriptValue(
script_state, script_state,
V8ScriptRunner::CallExtra(script_state, V8ScriptRunner::CallExtraOrCrash(
"createBuiltInCountQueuingStrategy", args)); script_state, "createBuiltInCountQueuingStrategy", args));
} }
ScriptValue ReadableStreamOperations::GetReader(ScriptState* script_state, ScriptValue ReadableStreamOperations::GetReader(ScriptState* script_state,
ScriptValue stream) { ScriptValue stream,
DCHECK(IsReadableStreamForDCheck(script_state, stream)); ExceptionState& es) {
DCHECK(IsReadableStream(script_state, stream));
v8::TryCatch block(script_state->GetIsolate()); v8::TryCatch block(script_state->GetIsolate());
v8::Local<v8::Value> args[] = {stream.V8Value()}; v8::Local<v8::Value> args[] = {stream.V8Value()};
...@@ -139,122 +52,131 @@ ScriptValue ReadableStreamOperations::GetReader(ScriptState* script_state, ...@@ -139,122 +52,131 @@ ScriptValue ReadableStreamOperations::GetReader(ScriptState* script_state,
script_state, script_state,
V8ScriptRunner::CallExtra(script_state, V8ScriptRunner::CallExtra(script_state,
"AcquireReadableStreamDefaultReader", args)); "AcquireReadableStreamDefaultReader", args));
DCHECK(block.HasCaught() || !result.IsEmpty()); if (block.HasCaught())
es.RethrowV8Exception(block.Exception());
return result; return result;
} }
base::Optional<bool> ReadableStreamOperations::IsReadableStream( bool ReadableStreamOperations::IsReadableStream(ScriptState* script_state,
ScriptState* script_state,
ScriptValue value) { ScriptValue value) {
return BooleanOperation(script_state, value, "IsReadableStream"); DCHECK(!value.IsEmpty());
}
base::Optional<bool> ReadableStreamOperations::IsReadableStream( if (!value.IsObject())
ScriptState* script_state, return false;
ScriptValue value,
ExceptionState& exception_state) { v8::Local<v8::Value> args[] = {value.V8Value()};
return BooleanOperationWithRethrow(script_state, value, "IsReadableStream", return V8ScriptRunner::CallExtraOrCrash(script_state, "IsReadableStream",
exception_state); args)
->ToBoolean()
->Value();
} }
base::Optional<bool> ReadableStreamOperations::IsDisturbed( bool ReadableStreamOperations::IsDisturbed(ScriptState* script_state,
ScriptState* script_state,
ScriptValue stream) { ScriptValue stream) {
DCHECK(IsReadableStreamForDCheck(script_state, stream)); DCHECK(IsReadableStream(script_state, stream));
return BooleanOperation(script_state, stream, "IsReadableStreamDisturbed");
v8::Local<v8::Value> args[] = {stream.V8Value()};
return V8ScriptRunner::CallExtraOrCrash(script_state,
"IsReadableStreamDisturbed", args)
->ToBoolean()
->Value();
} }
base::Optional<bool> ReadableStreamOperations::IsLocked( bool ReadableStreamOperations::IsLocked(ScriptState* script_state,
ScriptState* script_state,
ScriptValue stream) { ScriptValue stream) {
DCHECK(IsReadableStreamForDCheck(script_state, stream)); DCHECK(IsReadableStream(script_state, stream));
return BooleanOperation(script_state, stream, "IsReadableStreamLocked");
v8::Local<v8::Value> args[] = {stream.V8Value()};
return V8ScriptRunner::CallExtraOrCrash(script_state,
"IsReadableStreamLocked", args)
->ToBoolean()
->Value();
} }
base::Optional<bool> ReadableStreamOperations::IsReadable( bool ReadableStreamOperations::IsReadable(ScriptState* script_state,
ScriptState* script_state,
ScriptValue stream) { ScriptValue stream) {
DCHECK(IsReadableStreamForDCheck(script_state, stream)); DCHECK(IsReadableStream(script_state, stream));
return BooleanOperation(script_state, stream, "IsReadableStreamReadable");
v8::Local<v8::Value> args[] = {stream.V8Value()};
return V8ScriptRunner::CallExtraOrCrash(script_state,
"IsReadableStreamReadable", args)
->ToBoolean()
->Value();
} }
base::Optional<bool> ReadableStreamOperations::IsClosed( bool ReadableStreamOperations::IsClosed(ScriptState* script_state,
ScriptState* script_state,
ScriptValue stream) { ScriptValue stream) {
DCHECK(IsReadableStreamForDCheck(script_state, stream)); DCHECK(IsReadableStream(script_state, stream));
return BooleanOperation(script_state, stream, "IsReadableStreamClosed");
v8::Local<v8::Value> args[] = {stream.V8Value()};
return V8ScriptRunner::CallExtraOrCrash(script_state,
"IsReadableStreamClosed", args)
->ToBoolean()
->Value();
} }
base::Optional<bool> ReadableStreamOperations::IsErrored( bool ReadableStreamOperations::IsErrored(ScriptState* script_state,
ScriptState* script_state,
ScriptValue stream) { ScriptValue stream) {
DCHECK(IsReadableStreamForDCheck(script_state, stream)); DCHECK(IsReadableStream(script_state, stream));
return BooleanOperation(script_state, stream, "IsReadableStreamErrored");
v8::Local<v8::Value> args[] = {stream.V8Value()};
return V8ScriptRunner::CallExtraOrCrash(script_state,
"IsReadableStreamErrored", args)
->ToBoolean()
->Value();
} }
base::Optional<bool> ReadableStreamOperations::IsReadableStreamDefaultReader( bool ReadableStreamOperations::IsReadableStreamDefaultReader(
ScriptState* script_state, ScriptState* script_state,
ScriptValue value) { ScriptValue value) {
return BooleanOperation(script_state, value, "IsReadableStreamDefaultReader"); DCHECK(!value.IsEmpty());
if (!value.IsObject())
return false;
v8::Local<v8::Value> args[] = {value.V8Value()};
return V8ScriptRunner::CallExtraOrCrash(script_state,
"IsReadableStreamDefaultReader", args)
->ToBoolean()
->Value();
} }
ScriptPromise ReadableStreamOperations::DefaultReaderRead( ScriptPromise ReadableStreamOperations::DefaultReaderRead(
ScriptState* script_state, ScriptState* script_state,
ScriptValue reader) { ScriptValue reader) {
DCHECK(IsDefaultReaderForDCheck(script_state, reader)); DCHECK(IsReadableStreamDefaultReader(script_state, reader));
v8::TryCatch block(script_state->GetIsolate());
v8::Local<v8::Value> args[] = {reader.V8Value()}; v8::Local<v8::Value> args[] = {reader.V8Value()};
v8::MaybeLocal<v8::Value> maybe_result = V8ScriptRunner::CallExtra( return ScriptPromise::Cast(
script_state, "ReadableStreamDefaultReaderRead", args); script_state, V8ScriptRunner::CallExtraOrCrash(
if (maybe_result.IsEmpty()) { script_state, "ReadableStreamDefaultReaderRead", args));
DCHECK(block.HasCaught());
return ScriptPromise::Reject(script_state, block.Exception());
}
return ScriptPromise::Cast(script_state, maybe_result.ToLocalChecked());
} }
void ReadableStreamOperations::Tee(ScriptState* script_state, void ReadableStreamOperations::Tee(ScriptState* script_state,
ScriptValue stream, ScriptValue stream,
ScriptValue* new_stream1, ScriptValue* new_stream1,
ScriptValue* new_stream2, ScriptValue* new_stream2) {
ExceptionState& exception_state) { DCHECK(IsReadableStream(script_state, stream));
DCHECK(IsReadableStreamForDCheck(script_state, stream)); DCHECK(!IsLocked(script_state, stream));
DCHECK(!IsLockedForDCheck(script_state, stream));
v8::Local<v8::Value> args[] = {stream.V8Value()}; v8::Local<v8::Value> args[] = {stream.V8Value()};
v8::TryCatch block(script_state->GetIsolate()); ScriptValue result(script_state,
v8::Local<v8::Value> result; V8ScriptRunner::CallExtraOrCrash(
if (!V8ScriptRunner::CallExtra(script_state, "ReadableStreamTee", args) script_state, "ReadableStreamTee", args));
.ToLocal(&result)) { DCHECK(result.V8Value()->IsArray());
DCHECK(block.HasCaught()); v8::Local<v8::Array> branches = result.V8Value().As<v8::Array>();
exception_state.RethrowV8Exception(block.Exception());
return;
}
DCHECK(result->IsArray());
v8::Local<v8::Array> branches = result.As<v8::Array>();
DCHECK_EQ(2u, branches->Length()); DCHECK_EQ(2u, branches->Length());
ScriptValue result1(script_state, *new_stream1 = ScriptValue(
branches->Get(script_state->GetContext(), 0)); script_state,
DCHECK(!result1.IsEmpty()); branches->Get(script_state->GetContext(), 0).ToLocalChecked());
DCHECK( *new_stream2 = ScriptValue(
IsReadableStream(script_state, result1, exception_state).value_or(true)); script_state,
if (exception_state.HadException()) branches->Get(script_state->GetContext(), 1).ToLocalChecked());
return;
DCHECK(IsReadableStream(script_state, *new_stream1));
ScriptValue result2(script_state, DCHECK(IsReadableStream(script_state, *new_stream2));
branches->Get(script_state->GetContext(), 1));
DCHECK(!result2.IsEmpty());
DCHECK(
IsReadableStream(script_state, result2, exception_state).value_or(true));
if (exception_state.HadException())
return;
*new_stream1 = std::move(result1);
*new_stream2 = std::move(result2);
} }
} // namespace blink } // namespace blink
...@@ -5,7 +5,6 @@ ...@@ -5,7 +5,6 @@
#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_STREAMS_READABLE_STREAM_OPERATIONS_H_ #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_STREAMS_READABLE_STREAM_OPERATIONS_H_
#define THIRD_PARTY_BLINK_RENDERER_CORE_STREAMS_READABLE_STREAM_OPERATIONS_H_ #define THIRD_PARTY_BLINK_RENDERER_CORE_STREAMS_READABLE_STREAM_OPERATIONS_H_
#include "base/optional.h"
#include "third_party/blink/renderer/bindings/core/v8/script_promise.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/script_value.h"
#include "third_party/blink/renderer/core/core_export.h" #include "third_party/blink/renderer/core/core_export.h"
...@@ -21,82 +20,65 @@ class ScriptState; ...@@ -21,82 +20,65 @@ class ScriptState;
// V8 Extras. // V8 Extras.
// All methods should be called in an appropriate V8 context. All ScriptValue // All methods should be called in an appropriate V8 context. All ScriptValue
// arguments must not be empty. // arguments must not be empty.
//
// Boolean methods return an optional bool, where an empty value indicates that
// Javascript failed to return a value (ie. an exception occurred). Exceptions
// are not caught, so that they can be handled by user Javascript. This implicit
// exception passing is error-prone and bad.
//
// TODO(ricea): Add ExceptionState arguments and make exception passing
// explicit. https://crbug.com/853189.
class CORE_EXPORT ReadableStreamOperations { class CORE_EXPORT ReadableStreamOperations {
STATIC_ONLY(ReadableStreamOperations); STATIC_ONLY(ReadableStreamOperations);
public: public:
// createReadableStreamWithExternalController // createReadableStreamWithExternalController
// If the caller supplies an invalid strategy (e.g. one that returns // If the caller supplies an invalid strategy (e.g. one that returns
// negative sizes, or doesn't have appropriate properties), or an exception // negative sizes, or doesn't have appropriate properties), this will crash.
// occurs for another reason, this will return an empty value.
static ScriptValue CreateReadableStream(ScriptState*, static ScriptValue CreateReadableStream(ScriptState*,
UnderlyingSourceBase*, UnderlyingSourceBase*,
ScriptValue strategy); ScriptValue strategy);
// createBuiltInCountQueuingStrategy // createBuiltInCountQueuingStrategy
// If the constructor throws, this will return an empty value.
static ScriptValue CreateCountQueuingStrategy(ScriptState*, static ScriptValue CreateCountQueuingStrategy(ScriptState*,
size_t high_water_mark); size_t high_water_mark);
// AcquireReadableStreamDefaultReader // AcquireReadableStreamDefaultReader
// This function assumes |IsReadableStream(stream)|. // This function assumes |isReadableStream(stream)|.
static ScriptValue GetReader(ScriptState*, ScriptValue stream); // Returns an empty value and throws an error via the ExceptionState when
// errored.
// IsReadableStream. Exceptions are not caught. static ScriptValue GetReader(ScriptState*,
static base::Optional<bool> IsReadableStream(ScriptState*, ScriptValue); ScriptValue stream,
ExceptionState&);
// IsReadableStream, exception-catching version. Exceptions will be passed to // IsReadableStream
// |exception_state|. static bool IsReadableStream(ScriptState*, ScriptValue);
static base::Optional<bool> IsReadableStream(ScriptState*,
ScriptValue,
ExceptionState& exception_state);
// IsReadableStreamDisturbed. // IsReadableStreamDisturbed
// This function assumes |IsReadableStream(stream)|. // This function assumes |isReadableStream(stream)|.
static base::Optional<bool> IsDisturbed(ScriptState*, ScriptValue stream); static bool IsDisturbed(ScriptState*, ScriptValue stream);
// IsReadableStreamLocked. // IsReadableStreamLocked
// This function assumes |IsReadableStream(stream)|. // This function assumes |isReadableStream(stream)|.
static base::Optional<bool> IsLocked(ScriptState*, ScriptValue stream); static bool IsLocked(ScriptState*, ScriptValue stream);
// IsReadableStreamReadable. // IsReadableStreamReadable
// This function assumes |IsReadableStream(stream)|. // This function assumes |isReadableStream(stream)|.
static base::Optional<bool> IsReadable(ScriptState*, ScriptValue stream); static bool IsReadable(ScriptState*, ScriptValue stream);
// IsReadableStreamClosed. // IsReadableStreamClosed
// This function assumes |IsReadableStream(stream)|. // This function assumes |isReadableStream(stream)|.
static base::Optional<bool> IsClosed(ScriptState*, ScriptValue stream); static bool IsClosed(ScriptState*, ScriptValue stream);
// IsReadableStreamErrored. // IsReadableStreamErrored
// This function assumes |IsReadableStream(stream)|. // This function assumes |isReadableStream(stream)|.
static base::Optional<bool> IsErrored(ScriptState*, ScriptValue stream); static bool IsErrored(ScriptState*, ScriptValue stream);
// IsReadableStreamDefaultReader. // IsReadableStreamDefaultReader
static base::Optional<bool> IsReadableStreamDefaultReader(ScriptState*, static bool IsReadableStreamDefaultReader(ScriptState*, ScriptValue);
ScriptValue);
// ReadableStreamDefaultReaderRead // ReadableStreamDefaultReaderRead
// This function assumes |IsReadableStreamDefaultReader(reader)|. // This function assumes |isReadableStreamDefaultReader(reader)|.
// If an exception occurs, returns a rejected promise.
static ScriptPromise DefaultReaderRead(ScriptState*, ScriptValue reader); static ScriptPromise DefaultReaderRead(ScriptState*, ScriptValue reader);
// ReadableStreamTee // ReadableStreamTee
// This function assumes |IsReadableStream(stream)| and |!IsLocked(stream)| // This function assumes |isReadableStream(stream)| and |!isLocked(stream)|
// Returns without setting new_stream1 or new_stream2 if an error occurs.
// Exceptions are caught and rethrown on |exception_state|.
static void Tee(ScriptState*, static void Tee(ScriptState*,
ScriptValue stream, ScriptValue stream,
ScriptValue* new_stream1, ScriptValue* new_stream1,
ScriptValue* new_stream2, ScriptValue* new_stream2);
ExceptionState&);
}; };
} // namespace blink } // namespace blink
......
...@@ -121,23 +121,17 @@ TEST(ReadableStreamOperationsTest, IsReadableStream) { ...@@ -121,23 +121,17 @@ TEST(ReadableStreamOperationsTest, IsReadableStream) {
TryCatchScope try_catch_scope(scope.GetIsolate()); TryCatchScope try_catch_scope(scope.GetIsolate());
EXPECT_FALSE(ReadableStreamOperations::IsReadableStream( EXPECT_FALSE(ReadableStreamOperations::IsReadableStream(
scope.GetScriptState(), scope.GetScriptState(),
ScriptValue(scope.GetScriptState(), ScriptValue(scope.GetScriptState(), v8::Undefined(scope.GetIsolate()))));
v8::Undefined(scope.GetIsolate())))
.value_or(true));
EXPECT_FALSE(ReadableStreamOperations::IsReadableStream( EXPECT_FALSE(ReadableStreamOperations::IsReadableStream(
scope.GetScriptState(), scope.GetScriptState(), ScriptValue::CreateNull(scope.GetScriptState())));
ScriptValue::CreateNull(scope.GetScriptState()))
.value_or(true));
EXPECT_FALSE(ReadableStreamOperations::IsReadableStream( EXPECT_FALSE(ReadableStreamOperations::IsReadableStream(
scope.GetScriptState(), scope.GetScriptState(),
ScriptValue(scope.GetScriptState(), ScriptValue(scope.GetScriptState(),
v8::Object::New(scope.GetIsolate()))) v8::Object::New(scope.GetIsolate()))));
.value_or(true));
ScriptValue stream = EvalWithPrintingError(&scope, "new ReadableStream()"); ScriptValue stream = EvalWithPrintingError(&scope, "new ReadableStream()");
EXPECT_FALSE(stream.IsEmpty()); EXPECT_FALSE(stream.IsEmpty());
EXPECT_TRUE( EXPECT_TRUE(ReadableStreamOperations::IsReadableStream(scope.GetScriptState(),
ReadableStreamOperations::IsReadableStream(scope.GetScriptState(), stream) stream));
.value_or(false));
} }
TEST(ReadableStreamOperationsTest, IsReadableStreamDefaultReaderInvalid) { TEST(ReadableStreamOperationsTest, IsReadableStreamDefaultReaderInvalid) {
...@@ -145,24 +139,18 @@ TEST(ReadableStreamOperationsTest, IsReadableStreamDefaultReaderInvalid) { ...@@ -145,24 +139,18 @@ TEST(ReadableStreamOperationsTest, IsReadableStreamDefaultReaderInvalid) {
TryCatchScope try_catch_scope(scope.GetIsolate()); TryCatchScope try_catch_scope(scope.GetIsolate());
EXPECT_FALSE(ReadableStreamOperations::IsReadableStreamDefaultReader( EXPECT_FALSE(ReadableStreamOperations::IsReadableStreamDefaultReader(
scope.GetScriptState(), scope.GetScriptState(),
ScriptValue(scope.GetScriptState(), ScriptValue(scope.GetScriptState(), v8::Undefined(scope.GetIsolate()))));
v8::Undefined(scope.GetIsolate())))
.value_or(true));
EXPECT_FALSE(ReadableStreamOperations::IsReadableStreamDefaultReader( EXPECT_FALSE(ReadableStreamOperations::IsReadableStreamDefaultReader(
scope.GetScriptState(), scope.GetScriptState(), ScriptValue::CreateNull(scope.GetScriptState())));
ScriptValue::CreateNull(scope.GetScriptState()))
.value_or(true));
EXPECT_FALSE(ReadableStreamOperations::IsReadableStreamDefaultReader( EXPECT_FALSE(ReadableStreamOperations::IsReadableStreamDefaultReader(
scope.GetScriptState(), scope.GetScriptState(),
ScriptValue(scope.GetScriptState(), ScriptValue(scope.GetScriptState(),
v8::Object::New(scope.GetIsolate()))) v8::Object::New(scope.GetIsolate()))));
.value_or(true));
ScriptValue stream = EvalWithPrintingError(&scope, "new ReadableStream()"); ScriptValue stream = EvalWithPrintingError(&scope, "new ReadableStream()");
ASSERT_FALSE(stream.IsEmpty()); EXPECT_FALSE(stream.IsEmpty());
EXPECT_FALSE(ReadableStreamOperations::IsReadableStreamDefaultReader( EXPECT_FALSE(ReadableStreamOperations::IsReadableStreamDefaultReader(
scope.GetScriptState(), stream) scope.GetScriptState(), stream));
.value_or(true));
} }
TEST(ReadableStreamOperationsTest, GetReader) { TEST(ReadableStreamOperationsTest, GetReader) {
...@@ -172,24 +160,31 @@ TEST(ReadableStreamOperationsTest, GetReader) { ...@@ -172,24 +160,31 @@ TEST(ReadableStreamOperationsTest, GetReader) {
EXPECT_FALSE(stream.IsEmpty()); EXPECT_FALSE(stream.IsEmpty());
EXPECT_FALSE( EXPECT_FALSE(
ReadableStreamOperations::IsLocked(scope.GetScriptState(), stream) ReadableStreamOperations::IsLocked(scope.GetScriptState(), stream));
.value_or(true));
ScriptValue reader; ScriptValue reader;
reader = ReadableStreamOperations::GetReader(scope.GetScriptState(), stream); {
EXPECT_TRUE(ReadableStreamOperations::IsLocked(scope.GetScriptState(), stream) DummyExceptionStateForTesting es;
.value_or(false)); reader =
ReadableStreamOperations::GetReader(scope.GetScriptState(), stream, es);
ASSERT_FALSE(es.HadException());
}
EXPECT_TRUE(
ReadableStreamOperations::IsLocked(scope.GetScriptState(), stream));
ASSERT_FALSE(reader.IsEmpty()); ASSERT_FALSE(reader.IsEmpty());
EXPECT_FALSE( EXPECT_FALSE(ReadableStreamOperations::IsReadableStream(
ReadableStreamOperations::IsReadableStream(scope.GetScriptState(), reader) scope.GetScriptState(), reader));
.value_or(true));
EXPECT_TRUE(ReadableStreamOperations::IsReadableStreamDefaultReader( EXPECT_TRUE(ReadableStreamOperations::IsReadableStreamDefaultReader(
scope.GetScriptState(), reader) scope.GetScriptState(), reader));
.value_or(false));
// Already locked! // Already locked!
reader = ReadableStreamOperations::GetReader(scope.GetScriptState(), stream); {
EXPECT_TRUE(reader.IsEmpty()); DummyExceptionStateForTesting es;
reader =
ReadableStreamOperations::GetReader(scope.GetScriptState(), stream, es);
ASSERT_TRUE(es.HadException());
}
ASSERT_TRUE(reader.IsEmpty());
} }
TEST(ReadableStreamOperationsTest, IsDisturbed) { TEST(ReadableStreamOperationsTest, IsDisturbed) {
...@@ -200,14 +195,12 @@ TEST(ReadableStreamOperationsTest, IsDisturbed) { ...@@ -200,14 +195,12 @@ TEST(ReadableStreamOperationsTest, IsDisturbed) {
EXPECT_FALSE(stream.IsEmpty()); EXPECT_FALSE(stream.IsEmpty());
EXPECT_FALSE( EXPECT_FALSE(
ReadableStreamOperations::IsDisturbed(scope.GetScriptState(), stream) ReadableStreamOperations::IsDisturbed(scope.GetScriptState(), stream));
.value_or(true));
ASSERT_FALSE(EvalWithPrintingError(&scope, "stream.cancel()").IsEmpty()); ASSERT_FALSE(EvalWithPrintingError(&scope, "stream.cancel()").IsEmpty());
EXPECT_TRUE( EXPECT_TRUE(
ReadableStreamOperations::IsDisturbed(scope.GetScriptState(), stream) ReadableStreamOperations::IsDisturbed(scope.GetScriptState(), stream));
.value_or(false));
} }
TEST(ReadableStreamOperationsTest, Read) { TEST(ReadableStreamOperationsTest, Read) {
...@@ -220,8 +213,7 @@ TEST(ReadableStreamOperationsTest, Read) { ...@@ -220,8 +213,7 @@ TEST(ReadableStreamOperationsTest, Read) {
"new ReadableStream({start}).getReader()"); "new ReadableStream({start}).getReader()");
EXPECT_FALSE(reader.IsEmpty()); EXPECT_FALSE(reader.IsEmpty());
ASSERT_TRUE(ReadableStreamOperations::IsReadableStreamDefaultReader( ASSERT_TRUE(ReadableStreamOperations::IsReadableStreamDefaultReader(
scope.GetScriptState(), reader) scope.GetScriptState(), reader));
.value_or(false));
Iteration* it1 = new Iteration(); Iteration* it1 = new Iteration();
Iteration* it2 = new Iteration(); Iteration* it2 = new Iteration();
...@@ -281,7 +273,12 @@ TEST(ReadableStreamOperationsTest, ...@@ -281,7 +273,12 @@ TEST(ReadableStreamOperationsTest,
EXPECT_EQ(8, underlying_source->DesiredSize()); EXPECT_EQ(8, underlying_source->DesiredSize());
ScriptValue reader; ScriptValue reader;
reader = ReadableStreamOperations::GetReader(scope.GetScriptState(), stream); {
DummyExceptionStateForTesting es;
reader =
ReadableStreamOperations::GetReader(scope.GetScriptState(), stream, es);
ASSERT_FALSE(es.HadException());
}
ASSERT_FALSE(reader.IsEmpty()); ASSERT_FALSE(reader.IsEmpty());
Iteration* it1 = new Iteration(); Iteration* it1 = new Iteration();
...@@ -371,14 +368,11 @@ TEST(ReadableStreamOperationsTest, IsReadable) { ...@@ -371,14 +368,11 @@ TEST(ReadableStreamOperationsTest, IsReadable) {
ASSERT_FALSE(errored.IsEmpty()); ASSERT_FALSE(errored.IsEmpty());
EXPECT_TRUE( EXPECT_TRUE(
ReadableStreamOperations::IsReadable(scope.GetScriptState(), readable) ReadableStreamOperations::IsReadable(scope.GetScriptState(), readable));
.value_or(false));
EXPECT_FALSE( EXPECT_FALSE(
ReadableStreamOperations::IsReadable(scope.GetScriptState(), closed) ReadableStreamOperations::IsReadable(scope.GetScriptState(), closed));
.value_or(true));
EXPECT_FALSE( EXPECT_FALSE(
ReadableStreamOperations::IsReadable(scope.GetScriptState(), errored) ReadableStreamOperations::IsReadable(scope.GetScriptState(), errored));
.value_or(true));
} }
TEST(ReadableStreamOperationsTest, IsClosed) { TEST(ReadableStreamOperationsTest, IsClosed) {
...@@ -394,13 +388,11 @@ TEST(ReadableStreamOperationsTest, IsClosed) { ...@@ -394,13 +388,11 @@ TEST(ReadableStreamOperationsTest, IsClosed) {
ASSERT_FALSE(errored.IsEmpty()); ASSERT_FALSE(errored.IsEmpty());
EXPECT_FALSE( EXPECT_FALSE(
ReadableStreamOperations::IsClosed(scope.GetScriptState(), readable) ReadableStreamOperations::IsClosed(scope.GetScriptState(), readable));
.value_or(true)); EXPECT_TRUE(
EXPECT_TRUE(ReadableStreamOperations::IsClosed(scope.GetScriptState(), closed) ReadableStreamOperations::IsClosed(scope.GetScriptState(), closed));
.value_or(false));
EXPECT_FALSE( EXPECT_FALSE(
ReadableStreamOperations::IsClosed(scope.GetScriptState(), errored) ReadableStreamOperations::IsClosed(scope.GetScriptState(), errored));
.value_or(true));
} }
TEST(ReadableStreamOperationsTest, IsErrored) { TEST(ReadableStreamOperationsTest, IsErrored) {
...@@ -416,39 +408,29 @@ TEST(ReadableStreamOperationsTest, IsErrored) { ...@@ -416,39 +408,29 @@ TEST(ReadableStreamOperationsTest, IsErrored) {
ASSERT_FALSE(errored.IsEmpty()); ASSERT_FALSE(errored.IsEmpty());
EXPECT_FALSE( EXPECT_FALSE(
ReadableStreamOperations::IsErrored(scope.GetScriptState(), readable) ReadableStreamOperations::IsErrored(scope.GetScriptState(), readable));
.value_or(true));
EXPECT_FALSE( EXPECT_FALSE(
ReadableStreamOperations::IsErrored(scope.GetScriptState(), closed) ReadableStreamOperations::IsErrored(scope.GetScriptState(), closed));
.value_or(true));
EXPECT_TRUE( EXPECT_TRUE(
ReadableStreamOperations::IsErrored(scope.GetScriptState(), errored) ReadableStreamOperations::IsErrored(scope.GetScriptState(), errored));
.value_or(false));
} }
TEST(ReadableStreamOperationsTest, Tee) { TEST(ReadableStreamOperationsTest, Tee) {
V8TestingScope scope; V8TestingScope scope;
TryCatchScope try_catch_scope(scope.GetIsolate()); TryCatchScope try_catch_scope(scope.GetIsolate());
NonThrowableExceptionState exception_state;
ScriptValue original = ScriptValue original =
EvalWithPrintingError(&scope, EvalWithPrintingError(&scope,
"var controller;" "var controller;"
"new ReadableStream({start: c => controller = c})"); "new ReadableStream({start: c => controller = c})");
ASSERT_FALSE(original.IsEmpty()); ASSERT_FALSE(original.IsEmpty());
ScriptValue new1, new2; ScriptValue new1, new2;
ReadableStreamOperations::Tee(scope.GetScriptState(), original, &new1, &new2, ReadableStreamOperations::Tee(scope.GetScriptState(), original, &new1, &new2);
exception_state);
ASSERT_FALSE(new1.IsEmpty());
ASSERT_FALSE(new2.IsEmpty());
NonThrowableExceptionState ec;
ScriptValue reader1 = ScriptValue reader1 =
ReadableStreamOperations::GetReader(scope.GetScriptState(), new1); ReadableStreamOperations::GetReader(scope.GetScriptState(), new1, ec);
ScriptValue reader2 = ScriptValue reader2 =
ReadableStreamOperations::GetReader(scope.GetScriptState(), new2); ReadableStreamOperations::GetReader(scope.GetScriptState(), new2, ec);
ASSERT_FALSE(reader1.IsEmpty());
ASSERT_FALSE(reader2.IsEmpty());
Iteration* it1 = new Iteration(); Iteration* it1 = new Iteration();
Iteration* it2 = new Iteration(); Iteration* it2 = new Iteration();
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment