diff options
Diffstat (limited to 'chromium/third_party/blink/renderer/core/fetch')
49 files changed, 920 insertions, 372 deletions
diff --git a/chromium/third_party/blink/renderer/core/fetch/BUILD.gn b/chromium/third_party/blink/renderer/core/fetch/BUILD.gn index 596433ac5ea..5b2297669c1 100644 --- a/chromium/third_party/blink/renderer/core/fetch/BUILD.gn +++ b/chromium/third_party/blink/renderer/core/fetch/BUILD.gn @@ -14,6 +14,8 @@ blink_core_sources("fetch") { "body_stream_buffer.h", "bytes_consumer_tee.cc", "bytes_consumer_tee.h", + "bytes_uploader.cc", + "bytes_uploader.h", "fetch_data_loader.cc", "fetch_data_loader.h", "fetch_header_list.cc", diff --git a/chromium/third_party/blink/renderer/core/fetch/DEPS b/chromium/third_party/blink/renderer/core/fetch/DEPS index 82e8abc9aab..b43df3036d0 100644 --- a/chromium/third_party/blink/renderer/core/fetch/DEPS +++ b/chromium/third_party/blink/renderer/core/fetch/DEPS @@ -3,8 +3,16 @@ include_rules = [ "+mojo/public/cpp/system/data_pipe.h", "+mojo/public/cpp/system/data_pipe_utils.h", "+mojo/public/cpp/system/simple_watcher.h", + "+net/base/net_errors.h", "+net/base/request_priority.h", + "+net/http/http_response_info.h", "+services/network/public/cpp", "+services/network/public/mojom", "+url/gurl.h", ] + +specific_include_rules = { + "bytes_uploader_test\.cc": [ + "+base/test/mock_callback.h", + ], +} diff --git a/chromium/third_party/blink/renderer/core/fetch/blob_bytes_consumer.cc b/chromium/third_party/blink/renderer/core/fetch/blob_bytes_consumer.cc index 148926a43aa..2c7713ace06 100644 --- a/chromium/third_party/blink/renderer/core/fetch/blob_bytes_consumer.cc +++ b/chromium/third_party/blink/renderer/core/fetch/blob_bytes_consumer.cc @@ -99,7 +99,7 @@ BytesConsumer::PublicState BlobBytesConsumer::GetPublicState() const { return nested_consumer_->GetPublicState(); } -void BlobBytesConsumer::Trace(Visitor* visitor) { +void BlobBytesConsumer::Trace(Visitor* visitor) const { visitor->Trace(execution_context_); visitor->Trace(nested_consumer_); visitor->Trace(client_); diff --git a/chromium/third_party/blink/renderer/core/fetch/blob_bytes_consumer.h b/chromium/third_party/blink/renderer/core/fetch/blob_bytes_consumer.h index 4cc789a8858..6c125ef6d54 100644 --- a/chromium/third_party/blink/renderer/core/fetch/blob_bytes_consumer.h +++ b/chromium/third_party/blink/renderer/core/fetch/blob_bytes_consumer.h @@ -37,7 +37,7 @@ class CORE_EXPORT BlobBytesConsumer final : public BytesConsumer { Error GetError() const override; String DebugName() const override { return "BlobBytesConsumer"; } - void Trace(Visitor*) override; + void Trace(Visitor*) const override; private: Member<ExecutionContext> execution_context_; diff --git a/chromium/third_party/blink/renderer/core/fetch/body.cc b/chromium/third_party/blink/renderer/core/fetch/body.cc index 437db5482c5..28635ccf614 100644 --- a/chromium/third_party/blink/renderer/core/fetch/body.cc +++ b/chromium/third_party/blink/renderer/core/fetch/body.cc @@ -64,7 +64,7 @@ class BodyConsumerBase : public GarbageCollected<BodyConsumerBase>, WrapPersistent(this), object)); } - void Trace(Visitor* visitor) override { + void Trace(Visitor* visitor) const override { visitor->Trace(resolver_); FetchDataLoader::Client::Trace(visitor); } @@ -162,7 +162,7 @@ class BodyJsonConsumer final : public BodyConsumerBase { ScriptPromise Body::arrayBuffer(ScriptState* script_state, ExceptionState& exception_state) { - RejectInvalidConsumption(script_state, exception_state); + RejectInvalidConsumption(exception_state); if (exception_state.HadException()) return ScriptPromise(); @@ -195,7 +195,7 @@ ScriptPromise Body::arrayBuffer(ScriptState* script_state, ScriptPromise Body::blob(ScriptState* script_state, ExceptionState& exception_state) { - RejectInvalidConsumption(script_state, exception_state); + RejectInvalidConsumption(exception_state); if (exception_state.HadException()) return ScriptPromise(); @@ -225,7 +225,7 @@ ScriptPromise Body::blob(ScriptState* script_state, ScriptPromise Body::formData(ScriptState* script_state, ExceptionState& exception_state) { - RejectInvalidConsumption(script_state, exception_state); + RejectInvalidConsumption(exception_state); if (exception_state.HadException()) return ScriptPromise(); @@ -295,7 +295,7 @@ ScriptPromise Body::formData(ScriptState* script_state, ScriptPromise Body::json(ScriptState* script_state, ExceptionState& exception_state) { - RejectInvalidConsumption(script_state, exception_state); + RejectInvalidConsumption(exception_state); if (exception_state.HadException()) return ScriptPromise(); @@ -324,7 +324,7 @@ ScriptPromise Body::json(ScriptState* script_state, ScriptPromise Body::text(ScriptState* script_state, ExceptionState& exception_state) { - RejectInvalidConsumption(script_state, exception_state); + RejectInvalidConsumption(exception_state); if (exception_state.HadException()) return ScriptPromise(); @@ -365,25 +365,14 @@ ReadableStream* Body::body() { return BodyBuffer()->Stream(); } -Body::BodyUsed Body::IsBodyUsed(ExceptionState& exception_state) { +bool Body::IsBodyUsed() const { auto* body_buffer = BodyBuffer(); - if (!body_buffer) - return BodyUsed::kUnused; - base::Optional<bool> stream_disturbed = - body_buffer->IsStreamDisturbed(exception_state); - if (exception_state.HadException()) - return BodyUsed::kBroken; - return stream_disturbed.value() ? BodyUsed::kUsed : BodyUsed::kUnused; + return body_buffer && body_buffer->IsStreamDisturbed(); } -Body::BodyLocked Body::IsBodyLocked(ExceptionState& exception_state) { +bool Body::IsBodyLocked() const { auto* body_buffer = BodyBuffer(); - if (!body_buffer) - return BodyLocked::kUnlocked; - base::Optional<bool> is_locked = body_buffer->IsStreamLocked(exception_state); - if (exception_state.HadException()) - return BodyLocked::kBroken; - return is_locked.value() ? BodyLocked::kLocked : BodyLocked::kUnlocked; + return body_buffer && body_buffer->IsStreamLocked(); } bool Body::HasPendingActivity() const { @@ -395,31 +384,16 @@ bool Body::HasPendingActivity() const { return body_buffer->HasPendingActivity(); } -bool Body::IsBodyUsedForDCheck(ExceptionState& exception_state) { - return BodyBuffer() && - BodyBuffer()->IsStreamDisturbedForDCheck(exception_state); -} - Body::Body(ExecutionContext* context) : ExecutionContextClient(context) {} -void Body::RejectInvalidConsumption(ScriptState* script_state, - ExceptionState& exception_state) { - const auto used = IsBodyUsed(exception_state); - if (exception_state.HadException()) { - DCHECK_EQ(used, BodyUsed::kBroken); - return; - } - DCHECK_NE(used, BodyUsed::kBroken); - - if (IsBodyLocked(exception_state) == BodyLocked::kLocked) { - DCHECK(!exception_state.HadException()); +void Body::RejectInvalidConsumption(ExceptionState& exception_state) const { + if (IsBodyLocked()) { exception_state.ThrowTypeError("body stream is locked"); } - if (exception_state.HadException()) - return; - if (used == BodyUsed::kUsed) + if (IsBodyUsed()) { exception_state.ThrowTypeError("body stream already read"); + } } } // namespace blink diff --git a/chromium/third_party/blink/renderer/core/fetch/body.h b/chromium/third_party/blink/renderer/core/fetch/body.h index fa3449c1273..3db2c704600 100644 --- a/chromium/third_party/blink/renderer/core/fetch/body.h +++ b/chromium/third_party/blink/renderer/core/fetch/body.h @@ -31,9 +31,6 @@ class ScriptState; // implementation. class CORE_EXPORT Body : public ExecutionContextClient { public: - enum class BodyUsed { kUsed, kUnused, kBroken }; - enum class BodyLocked { kLocked, kUnlocked, kBroken }; - explicit Body(ExecutionContext*); ScriptPromise arrayBuffer(ScriptState*, ExceptionState&); @@ -47,25 +44,16 @@ class CORE_EXPORT Body : public ExecutionContextClient { // This should only be called from the generated bindings. All other code // should use IsBodyUsed() instead. - bool bodyUsed(ExceptionState& exception_state) { - return IsBodyUsed(exception_state) == BodyUsed::kUsed; - } + bool bodyUsed() const { return IsBodyUsed(); } - // Returns kUsed, kUnused or kBroken. kBroken implies there is an exception - // pending and the caller should return to JavaScript immediately. - virtual BodyUsed IsBodyUsed(ExceptionState&); + // True if the body has been read from. + virtual bool IsBodyUsed() const; - // Returns kLocked, kUnlocked or kBroken. kBroken implies there is an - // exception pending and the caller should return to JavaScript immediately. - BodyLocked IsBodyLocked(ExceptionState&); + // True if the body is locked. + bool IsBodyLocked() const; bool HasPendingActivity() const; - protected: - // A version of IsBodyUsed() which catches exceptions and returns - // false. Should never be used outside DCHECK(). - virtual bool IsBodyUsedForDCheck(ExceptionState& exception_state); - private: // TODO(e_hakkinen): Fix |MimeType()| to always contain parameters and // remove |ContentType()|. @@ -76,7 +64,7 @@ class CORE_EXPORT Body : public ExecutionContextClient { // error conditions. This method wraps those up into one call which throws // an exception if consumption cannot proceed. The caller must check // |exception_state| on return. - void RejectInvalidConsumption(ScriptState*, ExceptionState& exception_state); + void RejectInvalidConsumption(ExceptionState& exception_state) const; DISALLOW_COPY_AND_ASSIGN(Body); }; diff --git a/chromium/third_party/blink/renderer/core/fetch/body.idl b/chromium/third_party/blink/renderer/core/fetch/body.idl index 79a89529153..1f30a408bc8 100644 --- a/chromium/third_party/blink/renderer/core/fetch/body.idl +++ b/chromium/third_party/blink/renderer/core/fetch/body.idl @@ -7,7 +7,7 @@ [ ActiveScriptWrappable ] interface mixin Body { - [RaisesException] readonly attribute boolean bodyUsed; + readonly attribute boolean bodyUsed; [CallWith=ScriptState, NewObject, RaisesException] Promise<ArrayBuffer> arrayBuffer(); [CallWith=ScriptState, NewObject, RaisesException] Promise<Blob> blob(); [CallWith=ScriptState, NewObject, RaisesException] Promise<FormData> formData(); diff --git a/chromium/third_party/blink/renderer/core/fetch/body_stream_buffer.cc b/chromium/third_party/blink/renderer/core/fetch/body_stream_buffer.cc index 815ad1d51fd..b292b445240 100644 --- a/chromium/third_party/blink/renderer/core/fetch/body_stream_buffer.cc +++ b/chromium/third_party/blink/renderer/core/fetch/body_stream_buffer.cc @@ -9,6 +9,7 @@ #include "third_party/blink/renderer/core/execution_context/execution_context.h" #include "third_party/blink/renderer/core/fetch/body.h" #include "third_party/blink/renderer/core/fetch/bytes_consumer_tee.h" +#include "third_party/blink/renderer/core/fetch/bytes_uploader.h" #include "third_party/blink/renderer/core/fetch/readable_stream_bytes_consumer.h" #include "third_party/blink/renderer/core/streams/readable_stream.h" #include "third_party/blink/renderer/core/streams/readable_stream_default_controller_with_script_scope.h" @@ -84,7 +85,7 @@ class BodyStreamBuffer::LoaderClient final void Abort() override { NOTREACHED(); } - void Trace(Visitor* visitor) override { + void Trace(Visitor* visitor) const override { visitor->Trace(buffer_); visitor->Trace(client_); ExecutionContextLifecycleObserver::Trace(visitor); @@ -169,13 +170,9 @@ BodyStreamBuffer::BodyStreamBuffer(ScriptState* script_state, scoped_refptr<BlobDataHandle> BodyStreamBuffer::DrainAsBlobDataHandle( BytesConsumer::BlobSizePolicy policy, ExceptionState& exception_state) { - DCHECK(!IsStreamLockedForDCheck(exception_state)); - DCHECK(!IsStreamDisturbedForDCheck(exception_state)); - const base::Optional<bool> is_closed = IsStreamClosed(exception_state); - if (exception_state.HadException() || is_closed.value()) - return nullptr; - const base::Optional<bool> is_errored = IsStreamErrored(exception_state); - if (exception_state.HadException() || is_errored.value()) + DCHECK(!IsStreamLocked()); + DCHECK(!IsStreamDisturbed()); + if (IsStreamClosed() || IsStreamErrored()) return nullptr; if (made_from_readable_stream_) @@ -194,13 +191,9 @@ scoped_refptr<BlobDataHandle> BodyStreamBuffer::DrainAsBlobDataHandle( scoped_refptr<EncodedFormData> BodyStreamBuffer::DrainAsFormData( ExceptionState& exception_state) { - DCHECK(!IsStreamLockedForDCheck(exception_state)); - DCHECK(!IsStreamDisturbedForDCheck(exception_state)); - const base::Optional<bool> is_closed = IsStreamClosed(exception_state); - if (exception_state.HadException() || is_closed.value()) - return nullptr; - const base::Optional<bool> is_errored = IsStreamErrored(exception_state); - if (exception_state.HadException() || is_errored.value()) + DCHECK(!IsStreamLocked()); + DCHECK(!IsStreamDisturbed()); + if (IsStreamClosed() || IsStreamErrored()) return nullptr; if (made_from_readable_stream_) @@ -216,6 +209,22 @@ scoped_refptr<EncodedFormData> BodyStreamBuffer::DrainAsFormData( return nullptr; } +void BodyStreamBuffer::DrainAsChunkedDataPipeGetter( + ScriptState* script_state, + mojo::PendingReceiver<network::mojom::blink::ChunkedDataPipeGetter> + pending_receiver, + ExceptionState& exception_state) { + DCHECK(!IsStreamLocked()); + auto* consumer = MakeGarbageCollected<ReadableStreamBytesConsumer>( + script_state, stream_, exception_state); + if (exception_state.HadException()) + return; + stream_uploader_ = MakeGarbageCollected<BytesUploader>( + consumer, std::move(pending_receiver), + ExecutionContext::From(script_state) + ->GetTaskRunner(TaskType::kNetworking)); +} + void BodyStreamBuffer::StartLoading(FetchDataLoader* loader, FetchDataLoader::Client* client, ExceptionState& exception_state) { @@ -241,8 +250,8 @@ void BodyStreamBuffer::StartLoading(FetchDataLoader* loader, void BodyStreamBuffer::Tee(BodyStreamBuffer** branch1, BodyStreamBuffer** branch2, ExceptionState& exception_state) { - DCHECK(!IsStreamLockedForDCheck(exception_state)); - DCHECK(!IsStreamDisturbedForDCheck(exception_state)); + DCHECK(!IsStreamLocked()); + DCHECK(!IsStreamDisturbed()); *branch1 = nullptr; *branch2 = nullptr; scoped_refptr<BlobDataHandle> side_data_blob = TakeSideDataBlob(); @@ -339,41 +348,24 @@ void BodyStreamBuffer::ContextDestroyed() { UnderlyingSourceBase::ContextDestroyed(); } -base::Optional<bool> BodyStreamBuffer::IsStreamReadable( - ExceptionState& exception_state) { - return BooleanStreamOperation(&ReadableStream::IsReadable, exception_state); -} - -base::Optional<bool> BodyStreamBuffer::IsStreamClosed( - ExceptionState& exception_state) { - return BooleanStreamOperation(&ReadableStream::IsClosed, exception_state); -} - -base::Optional<bool> BodyStreamBuffer::IsStreamErrored( - ExceptionState& exception_state) { - return BooleanStreamOperation(&ReadableStream::IsErrored, exception_state); +bool BodyStreamBuffer::IsStreamReadable() const { + return stream_->IsReadable(); } -base::Optional<bool> BodyStreamBuffer::IsStreamLocked( - ExceptionState& exception_state) { - return BooleanStreamOperation(&ReadableStream::IsLocked, exception_state); +bool BodyStreamBuffer::IsStreamClosed() const { + return stream_->IsClosed(); } -bool BodyStreamBuffer::IsStreamLockedForDCheck( - ExceptionState& exception_state) { - auto result = IsStreamLocked(exception_state); - return !result || *result; +bool BodyStreamBuffer::IsStreamErrored() const { + return stream_->IsErrored(); } -base::Optional<bool> BodyStreamBuffer::IsStreamDisturbed( - ExceptionState& exception_state) { - return BooleanStreamOperation(&ReadableStream::IsDisturbed, exception_state); +bool BodyStreamBuffer::IsStreamLocked() const { + return stream_->IsLocked(); } -bool BodyStreamBuffer::IsStreamDisturbedForDCheck( - ExceptionState& exception_state) { - auto result = IsStreamDisturbed(exception_state); - return !result || *result; +bool BodyStreamBuffer::IsStreamDisturbed() const { + return stream_->IsDisturbed(); } void BodyStreamBuffer::CloseAndLockAndDisturb(ExceptionState& exception_state) { @@ -384,25 +376,11 @@ void BodyStreamBuffer::CloseAndLockAndDisturb(ExceptionState& exception_state) { return; } - if (stream_->IsBroken()) { - stream_broken_ = true; - exception_state.ThrowDOMException( - DOMExceptionCode::kInvalidStateError, - "Body stream has been lost and cannot be disturbed"); - return; - } - - base::Optional<bool> is_readable = IsStreamReadable(exception_state); - if (exception_state.HadException()) - return; - - DCHECK(is_readable.has_value()); - if (is_readable.value()) { + if (IsStreamReadable()) { // Note that the stream cannot be "draining", because it doesn't have // the internal buffer. Close(); } - DCHECK(!stream_broken_); stream_->LockAndDisturb(script_state_, exception_state); } @@ -417,9 +395,10 @@ scoped_refptr<BlobDataHandle> BodyStreamBuffer::TakeSideDataBlob() { return std::move(side_data_blob_); } -void BodyStreamBuffer::Trace(Visitor* visitor) { +void BodyStreamBuffer::Trace(Visitor* visitor) const { visitor->Trace(script_state_); visitor->Trace(stream_); + visitor->Trace(stream_uploader_); visitor->Trace(consumer_); visitor->Trace(loader_); visitor->Trace(signal_); @@ -520,40 +499,30 @@ void BodyStreamBuffer::StopLoading() { loader_ = nullptr; } -base::Optional<bool> BodyStreamBuffer::BooleanStreamOperation( - base::Optional<bool> (ReadableStream::*predicate)(ScriptState*, - ExceptionState&) const, +BytesConsumer* BodyStreamBuffer::ReleaseHandle( ExceptionState& exception_state) { + DCHECK(!IsStreamLocked()); + DCHECK(!IsStreamDisturbed()); + if (stream_broken_) { exception_state.ThrowDOMException( DOMExceptionCode::kInvalidStateError, "Body stream has suffered a fatal error and cannot be inspected"); - return base::nullopt; - } - ScriptState::Scope scope(script_state_); - base::Optional<bool> result = - (stream_->*predicate)(script_state_, exception_state); - if (exception_state.HadException()) { - stream_broken_ = true; - return base::nullopt; + return nullptr; } - return result; -} - -BytesConsumer* BodyStreamBuffer::ReleaseHandle( - ExceptionState& exception_state) { - DCHECK(!IsStreamLockedForDCheck(exception_state)); - DCHECK(!IsStreamDisturbedForDCheck(exception_state)); - - side_data_blob_.reset(); - if (stream_broken_) { + if (!GetExecutionContext()) { + // Avoid crashing if ContextDestroyed() has been called. exception_state.ThrowDOMException( DOMExceptionCode::kInvalidStateError, - "Body stream has suffered a fatal error and cannot be inspected"); + "Cannot release body in a window or worker than has been detached"); return nullptr; } + // Do this after state checks to avoid side-effects when the method does + // nothing. + side_data_blob_.reset(); + if (made_from_readable_stream_) { ScriptState::Scope scope(script_state_); auto* consumer = MakeGarbageCollected<ReadableStreamBytesConsumer>( @@ -565,12 +534,8 @@ BytesConsumer* BodyStreamBuffer::ReleaseHandle( return consumer; } // We need to call these before calling CloseAndLockAndDisturb. - const base::Optional<bool> is_closed = IsStreamClosed(exception_state); - if (exception_state.HadException()) - return nullptr; - const base::Optional<bool> is_errored = IsStreamErrored(exception_state); - if (exception_state.HadException()) - return nullptr; + const bool is_closed = IsStreamClosed(); + const bool is_errored = IsStreamErrored(); BytesConsumer* consumer = consumer_.Release(); @@ -578,12 +543,12 @@ BytesConsumer* BodyStreamBuffer::ReleaseHandle( if (exception_state.HadException()) return nullptr; - if (is_closed.value()) { + if (is_closed) { // Note that the stream cannot be "draining", because it doesn't have // the internal buffer. return BytesConsumer::CreateClosed(); } - if (is_errored.value()) + if (is_errored) return BytesConsumer::CreateErrored(BytesConsumer::Error("error")); DCHECK(consumer); diff --git a/chromium/third_party/blink/renderer/core/fetch/body_stream_buffer.h b/chromium/third_party/blink/renderer/core/fetch/body_stream_buffer.h index bac00bcb6b1..38a45519c06 100644 --- a/chromium/third_party/blink/renderer/core/fetch/body_stream_buffer.h +++ b/chromium/third_party/blink/renderer/core/fetch/body_stream_buffer.h @@ -6,8 +6,9 @@ #define THIRD_PARTY_BLINK_RENDERER_CORE_FETCH_BODY_STREAM_BUFFER_H_ #include <memory> -#include "base/optional.h" #include "base/util/type_safety/pass_key.h" +#include "mojo/public/cpp/bindings/pending_remote.h" +#include "services/network/public/mojom/chunked_data_pipe_getter.mojom-blink.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/core/core_export.h" @@ -21,6 +22,7 @@ namespace blink { +class BytesUploader; class EncodedFormData; class ExceptionState; class ReadableStream; @@ -62,6 +64,10 @@ class CORE_EXPORT BodyStreamBuffer final : public UnderlyingSourceBase, BytesConsumer::BlobSizePolicy, ExceptionState&); scoped_refptr<EncodedFormData> DrainAsFormData(ExceptionState&); + void DrainAsChunkedDataPipeGetter( + ScriptState*, + mojo::PendingReceiver<network::mojom::blink::ChunkedDataPipeGetter>, + ExceptionState&); void StartLoading(FetchDataLoader*, FetchDataLoader::Client* /* client */, ExceptionState&); @@ -77,13 +83,11 @@ class CORE_EXPORT BodyStreamBuffer final : public UnderlyingSourceBase, void OnStateChange() override; String DebugName() const override { return "BodyStreamBuffer"; } - base::Optional<bool> IsStreamReadable(ExceptionState&); - base::Optional<bool> IsStreamClosed(ExceptionState&); - base::Optional<bool> IsStreamErrored(ExceptionState&); - base::Optional<bool> IsStreamLocked(ExceptionState&); - bool IsStreamLockedForDCheck(ExceptionState&); - base::Optional<bool> IsStreamDisturbed(ExceptionState&); - bool IsStreamDisturbedForDCheck(ExceptionState&); + bool IsStreamReadable() const; + bool IsStreamClosed() const; + bool IsStreamErrored() const; + bool IsStreamLocked() const; + bool IsStreamDisturbed() const; void CloseAndLockAndDisturb(ExceptionState&); ScriptState* GetScriptState() { return script_state_; } @@ -97,7 +101,9 @@ class CORE_EXPORT BodyStreamBuffer final : public UnderlyingSourceBase, return side_data_blob_; } - void Trace(Visitor*) override; + bool IsMadeFromReadableStream() const { return made_from_readable_stream_; } + + void Trace(Visitor*) const override; private: class LoaderClient; @@ -116,17 +122,9 @@ class CORE_EXPORT BodyStreamBuffer final : public UnderlyingSourceBase, void EndLoading(); void StopLoading(); - // Implementation of IsStream*() methods. Delegates to |predicate|, one of the - // methods defined in ReadableStream. Sets |stream_broken_| and throws if - // |predicate| throws. Throws an exception if called when |stream_broken_| - // is already true. - base::Optional<bool> BooleanStreamOperation( - base::Optional<bool> (ReadableStream::*predicate)(ScriptState*, - ExceptionState&) const, - ExceptionState& exception_state); - Member<ScriptState> script_state_; Member<ReadableStream> stream_; + Member<BytesUploader> stream_uploader_; Member<BytesConsumer> consumer_; // We need this member to keep it alive while loading. Member<FetchDataLoader> loader_; @@ -140,6 +138,8 @@ class CORE_EXPORT BodyStreamBuffer final : public UnderlyingSourceBase, bool stream_needs_more_ = false; bool made_from_readable_stream_; bool in_process_data_ = false; + + // TODO(ricea): Remove remaining uses of |stream_broken_|. bool stream_broken_ = false; DISALLOW_COPY_AND_ASSIGN(BodyStreamBuffer); diff --git a/chromium/third_party/blink/renderer/core/fetch/body_stream_buffer_test.cc b/chromium/third_party/blink/renderer/core/fetch/body_stream_buffer_test.cc index 821593825d6..d198c1ca8e8 100644 --- a/chromium/third_party/blink/renderer/core/fetch/body_stream_buffer_test.cc +++ b/chromium/third_party/blink/renderer/core/fetch/body_stream_buffer_test.cc @@ -133,8 +133,8 @@ TEST_F(BodyStreamBufferTest, Tee) { BodyStreamBuffer* new2; buffer->Tee(&new1, &new2, exception_state); - EXPECT_TRUE(buffer->IsStreamLocked(exception_state).value_or(false)); - EXPECT_TRUE(buffer->IsStreamDisturbed(exception_state).value_or(false)); + EXPECT_TRUE(buffer->IsStreamLocked()); + EXPECT_TRUE(buffer->IsStreamDisturbed()); EXPECT_FALSE(buffer->HasPendingActivity()); EXPECT_EQ(nullptr, buffer->GetSideDataBlobForTest()); @@ -199,18 +199,18 @@ TEST_F(BodyStreamBufferTest, TeeFromHandleMadeFromStream) { BodyStreamBuffer* new2; buffer->Tee(&new1, &new2, exception_state); - EXPECT_TRUE(buffer->IsStreamLocked(exception_state).value_or(false)); + EXPECT_TRUE(buffer->IsStreamLocked()); // Note that this behavior is slightly different from for the behavior of // a BodyStreamBuffer made from a BytesConsumer. See the above test. In this // test, the stream will get disturbed when the microtask is performed. // TODO(yhirano): A uniformed behavior is preferred. - EXPECT_FALSE(buffer->IsStreamDisturbed(exception_state).value_or(true)); + EXPECT_FALSE(buffer->IsStreamDisturbed()); EXPECT_FALSE(buffer->HasPendingActivity()); v8::MicrotasksScope::PerformCheckpoint(scope.GetScriptState()->GetIsolate()); - EXPECT_TRUE(buffer->IsStreamLocked(exception_state).value_or(false)); - EXPECT_TRUE(buffer->IsStreamDisturbed(exception_state).value_or(false)); + EXPECT_TRUE(buffer->IsStreamLocked()); + EXPECT_TRUE(buffer->IsStreamDisturbed()); EXPECT_FALSE(buffer->HasPendingActivity()); new1->StartLoading(FetchDataLoader::CreateLoaderAsString( @@ -238,8 +238,8 @@ TEST_F(BodyStreamBufferTest, DrainAsBlobDataHandle) { blob_data_handle), /* abort_signal = */ nullptr, side_data_blob); - EXPECT_FALSE(buffer->IsStreamLocked(ASSERT_NO_EXCEPTION).value_or(true)); - EXPECT_FALSE(buffer->IsStreamDisturbed(ASSERT_NO_EXCEPTION).value_or(true)); + EXPECT_FALSE(buffer->IsStreamLocked()); + EXPECT_FALSE(buffer->IsStreamDisturbed()); EXPECT_FALSE(buffer->HasPendingActivity()); EXPECT_EQ(side_data_blob, buffer->GetSideDataBlobForTest()); scoped_refptr<BlobDataHandle> output_blob_data_handle = @@ -247,8 +247,8 @@ TEST_F(BodyStreamBufferTest, DrainAsBlobDataHandle) { BytesConsumer::BlobSizePolicy::kAllowBlobWithInvalidSize, ASSERT_NO_EXCEPTION); - EXPECT_TRUE(buffer->IsStreamLocked(ASSERT_NO_EXCEPTION).value_or(false)); - EXPECT_TRUE(buffer->IsStreamDisturbed(ASSERT_NO_EXCEPTION).value_or(false)); + EXPECT_TRUE(buffer->IsStreamLocked()); + EXPECT_TRUE(buffer->IsStreamDisturbed()); EXPECT_FALSE(buffer->HasPendingActivity()); EXPECT_EQ(nullptr, buffer->GetSideDataBlobForTest()); EXPECT_EQ(blob_data_handle, output_blob_data_handle); @@ -264,8 +264,8 @@ TEST_F(BodyStreamBufferTest, DrainAsBlobDataHandleReturnsNull) { BodyStreamBuffer::Create(scope.GetScriptState(), src, /* abort_signal = */ nullptr, side_data_blob); - EXPECT_FALSE(buffer->IsStreamLocked(ASSERT_NO_EXCEPTION).value_or(true)); - EXPECT_FALSE(buffer->IsStreamDisturbed(ASSERT_NO_EXCEPTION).value_or(true)); + EXPECT_FALSE(buffer->IsStreamLocked()); + EXPECT_FALSE(buffer->IsStreamDisturbed()); EXPECT_FALSE(buffer->HasPendingActivity()); EXPECT_EQ(side_data_blob, buffer->GetSideDataBlobForTest()); @@ -273,8 +273,8 @@ TEST_F(BodyStreamBufferTest, DrainAsBlobDataHandleReturnsNull) { BytesConsumer::BlobSizePolicy::kAllowBlobWithInvalidSize, ASSERT_NO_EXCEPTION)); - EXPECT_FALSE(buffer->IsStreamLocked(ASSERT_NO_EXCEPTION).value_or(true)); - EXPECT_FALSE(buffer->IsStreamDisturbed(ASSERT_NO_EXCEPTION).value_or(true)); + EXPECT_FALSE(buffer->IsStreamLocked()); + EXPECT_FALSE(buffer->IsStreamDisturbed()); EXPECT_FALSE(buffer->HasPendingActivity()); EXPECT_EQ(side_data_blob, buffer->GetSideDataBlobForTest()); } @@ -290,18 +290,18 @@ TEST_F(BodyStreamBufferTest, MakeGarbageCollected<BodyStreamBuffer>(scope.GetScriptState(), stream); EXPECT_FALSE(buffer->HasPendingActivity()); - EXPECT_FALSE(buffer->IsStreamLocked(exception_state).value_or(true)); - EXPECT_FALSE(buffer->IsStreamDisturbed(exception_state).value_or(true)); - EXPECT_TRUE(buffer->IsStreamReadable(exception_state).value_or(false)); + EXPECT_FALSE(buffer->IsStreamLocked()); + EXPECT_FALSE(buffer->IsStreamDisturbed()); + EXPECT_TRUE(buffer->IsStreamReadable()); EXPECT_FALSE(buffer->DrainAsBlobDataHandle( BytesConsumer::BlobSizePolicy::kAllowBlobWithInvalidSize, exception_state)); EXPECT_FALSE(buffer->HasPendingActivity()); - EXPECT_FALSE(buffer->IsStreamLocked(exception_state).value_or(true)); - EXPECT_FALSE(buffer->IsStreamDisturbed(exception_state).value_or(true)); - EXPECT_TRUE(buffer->IsStreamReadable(exception_state).value_or(false)); + EXPECT_FALSE(buffer->IsStreamLocked()); + EXPECT_FALSE(buffer->IsStreamDisturbed()); + EXPECT_TRUE(buffer->IsStreamReadable()); } TEST_F(BodyStreamBufferTest, DrainAsFormData) { @@ -319,15 +319,15 @@ TEST_F(BodyStreamBufferTest, DrainAsFormData) { input_form_data), /* abort_signal = */ nullptr, side_data_blob); - EXPECT_FALSE(buffer->IsStreamLocked(ASSERT_NO_EXCEPTION).value_or(true)); - EXPECT_FALSE(buffer->IsStreamDisturbed(ASSERT_NO_EXCEPTION).value_or(true)); + EXPECT_FALSE(buffer->IsStreamLocked()); + EXPECT_FALSE(buffer->IsStreamDisturbed()); EXPECT_FALSE(buffer->HasPendingActivity()); EXPECT_EQ(side_data_blob, buffer->GetSideDataBlobForTest()); scoped_refptr<EncodedFormData> output_form_data = buffer->DrainAsFormData(ASSERT_NO_EXCEPTION); - EXPECT_TRUE(buffer->IsStreamLocked(ASSERT_NO_EXCEPTION).value_or(false)); - EXPECT_TRUE(buffer->IsStreamDisturbed(ASSERT_NO_EXCEPTION).value_or(false)); + EXPECT_TRUE(buffer->IsStreamLocked()); + EXPECT_TRUE(buffer->IsStreamDisturbed()); EXPECT_FALSE(buffer->HasPendingActivity()); EXPECT_EQ(nullptr, buffer->GetSideDataBlobForTest()); EXPECT_EQ(output_form_data->FlattenToString(), @@ -344,15 +344,15 @@ TEST_F(BodyStreamBufferTest, DrainAsFormDataReturnsNull) { BodyStreamBuffer::Create(scope.GetScriptState(), src, /* abort_signal = */ nullptr, side_data_blob); - EXPECT_FALSE(buffer->IsStreamLocked(ASSERT_NO_EXCEPTION).value_or(true)); - EXPECT_FALSE(buffer->IsStreamDisturbed(ASSERT_NO_EXCEPTION).value_or(true)); + EXPECT_FALSE(buffer->IsStreamLocked()); + EXPECT_FALSE(buffer->IsStreamDisturbed()); EXPECT_FALSE(buffer->HasPendingActivity()); EXPECT_EQ(side_data_blob, buffer->GetSideDataBlobForTest()); EXPECT_FALSE(buffer->DrainAsFormData(ASSERT_NO_EXCEPTION)); - EXPECT_FALSE(buffer->IsStreamLocked(ASSERT_NO_EXCEPTION).value_or(true)); - EXPECT_FALSE(buffer->IsStreamDisturbed(ASSERT_NO_EXCEPTION).value_or(true)); + EXPECT_FALSE(buffer->IsStreamLocked()); + EXPECT_FALSE(buffer->IsStreamDisturbed()); EXPECT_FALSE(buffer->HasPendingActivity()); EXPECT_EQ(side_data_blob, buffer->GetSideDataBlobForTest()); } @@ -367,16 +367,16 @@ TEST_F(BodyStreamBufferTest, MakeGarbageCollected<BodyStreamBuffer>(scope.GetScriptState(), stream); EXPECT_FALSE(buffer->HasPendingActivity()); - EXPECT_FALSE(buffer->IsStreamLocked(exception_state).value_or(true)); - EXPECT_FALSE(buffer->IsStreamDisturbed(exception_state).value_or(true)); - EXPECT_TRUE(buffer->IsStreamReadable(exception_state).value_or(false)); + EXPECT_FALSE(buffer->IsStreamLocked()); + EXPECT_FALSE(buffer->IsStreamDisturbed()); + EXPECT_TRUE(buffer->IsStreamReadable()); EXPECT_FALSE(buffer->DrainAsFormData(exception_state)); EXPECT_FALSE(buffer->HasPendingActivity()); - EXPECT_FALSE(buffer->IsStreamLocked(exception_state).value_or(true)); - EXPECT_FALSE(buffer->IsStreamDisturbed(exception_state).value_or(true)); - EXPECT_TRUE(buffer->IsStreamReadable(exception_state).value_or(false)); + EXPECT_FALSE(buffer->IsStreamLocked()); + EXPECT_FALSE(buffer->IsStreamDisturbed()); + EXPECT_TRUE(buffer->IsStreamReadable()); } TEST_F(BodyStreamBufferTest, LoadBodyStreamBufferAsArrayBuffer) { @@ -405,16 +405,16 @@ TEST_F(BodyStreamBufferTest, LoadBodyStreamBufferAsArrayBuffer) { ASSERT_NO_EXCEPTION); EXPECT_EQ(nullptr, buffer->GetSideDataBlobForTest()); - EXPECT_TRUE(buffer->IsStreamLocked(ASSERT_NO_EXCEPTION).value_or(false)); - EXPECT_TRUE(buffer->IsStreamDisturbed(ASSERT_NO_EXCEPTION).value_or(false)); + EXPECT_TRUE(buffer->IsStreamLocked()); + EXPECT_TRUE(buffer->IsStreamDisturbed()); EXPECT_TRUE(buffer->HasPendingActivity()); checkpoint.Call(1); test::RunPendingTasks(); checkpoint.Call(2); - EXPECT_TRUE(buffer->IsStreamLocked(ASSERT_NO_EXCEPTION).value_or(false)); - EXPECT_TRUE(buffer->IsStreamDisturbed(ASSERT_NO_EXCEPTION).value_or(true)); + EXPECT_TRUE(buffer->IsStreamLocked()); + EXPECT_TRUE(buffer->IsStreamDisturbed()); EXPECT_FALSE(buffer->HasPendingActivity()); ASSERT_TRUE(array_buffer); EXPECT_EQ("hello", String(static_cast<const char*>(array_buffer->Data()), @@ -447,16 +447,16 @@ TEST_F(BodyStreamBufferTest, LoadBodyStreamBufferAsBlob) { client, ASSERT_NO_EXCEPTION); EXPECT_EQ(nullptr, buffer->GetSideDataBlobForTest()); - EXPECT_TRUE(buffer->IsStreamLocked(ASSERT_NO_EXCEPTION).value_or(false)); - EXPECT_TRUE(buffer->IsStreamDisturbed(ASSERT_NO_EXCEPTION).value_or(false)); + EXPECT_TRUE(buffer->IsStreamLocked()); + EXPECT_TRUE(buffer->IsStreamDisturbed()); EXPECT_TRUE(buffer->HasPendingActivity()); checkpoint.Call(1); test::RunPendingTasks(); checkpoint.Call(2); - EXPECT_TRUE(buffer->IsStreamLocked(ASSERT_NO_EXCEPTION).value_or(false)); - EXPECT_TRUE(buffer->IsStreamDisturbed(ASSERT_NO_EXCEPTION).value_or(false)); + EXPECT_TRUE(buffer->IsStreamLocked()); + EXPECT_TRUE(buffer->IsStreamDisturbed()); EXPECT_FALSE(buffer->HasPendingActivity()); EXPECT_EQ(5u, blob_data_handle->size()); } @@ -486,16 +486,16 @@ TEST_F(BodyStreamBufferTest, LoadBodyStreamBufferAsString) { client, ASSERT_NO_EXCEPTION); EXPECT_EQ(nullptr, buffer->GetSideDataBlobForTest()); - EXPECT_TRUE(buffer->IsStreamLocked(ASSERT_NO_EXCEPTION).value_or(false)); - EXPECT_TRUE(buffer->IsStreamDisturbed(ASSERT_NO_EXCEPTION).value_or(false)); + EXPECT_TRUE(buffer->IsStreamLocked()); + EXPECT_TRUE(buffer->IsStreamDisturbed()); EXPECT_TRUE(buffer->HasPendingActivity()); checkpoint.Call(1); test::RunPendingTasks(); checkpoint.Call(2); - EXPECT_TRUE(buffer->IsStreamLocked(ASSERT_NO_EXCEPTION).value_or(false)); - EXPECT_TRUE(buffer->IsStreamDisturbed(ASSERT_NO_EXCEPTION).value_or(false)); + EXPECT_TRUE(buffer->IsStreamLocked()); + EXPECT_TRUE(buffer->IsStreamDisturbed()); EXPECT_FALSE(buffer->HasPendingActivity()); } @@ -514,10 +514,10 @@ TEST_F(BodyStreamBufferTest, LoadClosedHandle) { scope.GetScriptState(), BytesConsumer::CreateClosed(), /* abort_signal = */ nullptr, side_data_blob); - EXPECT_TRUE(buffer->IsStreamClosed(ASSERT_NO_EXCEPTION).value_or(false)); + EXPECT_TRUE(buffer->IsStreamClosed()); - EXPECT_FALSE(buffer->IsStreamLocked(ASSERT_NO_EXCEPTION).value_or(true)); - EXPECT_FALSE(buffer->IsStreamDisturbed(ASSERT_NO_EXCEPTION).value_or(true)); + EXPECT_FALSE(buffer->IsStreamLocked()); + EXPECT_FALSE(buffer->IsStreamDisturbed()); EXPECT_FALSE(buffer->HasPendingActivity()); EXPECT_EQ(nullptr, buffer->GetSideDataBlobForTest()); @@ -527,8 +527,8 @@ TEST_F(BodyStreamBufferTest, LoadClosedHandle) { client, ASSERT_NO_EXCEPTION); checkpoint.Call(2); - EXPECT_TRUE(buffer->IsStreamLocked(ASSERT_NO_EXCEPTION).value_or(false)); - EXPECT_TRUE(buffer->IsStreamDisturbed(ASSERT_NO_EXCEPTION).value_or(false)); + EXPECT_TRUE(buffer->IsStreamLocked()); + EXPECT_TRUE(buffer->IsStreamDisturbed()); EXPECT_FALSE(buffer->HasPendingActivity()); } @@ -548,10 +548,10 @@ TEST_F(BodyStreamBufferTest, LoadErroredHandle) { BytesConsumer::CreateErrored(BytesConsumer::Error()), /* abort_signal = */ nullptr, side_data_blob); - EXPECT_TRUE(buffer->IsStreamErrored(ASSERT_NO_EXCEPTION).value_or(false)); + EXPECT_TRUE(buffer->IsStreamErrored()); - EXPECT_FALSE(buffer->IsStreamLocked(ASSERT_NO_EXCEPTION).value_or(true)); - EXPECT_FALSE(buffer->IsStreamDisturbed(ASSERT_NO_EXCEPTION).value_or(true)); + EXPECT_FALSE(buffer->IsStreamLocked()); + EXPECT_FALSE(buffer->IsStreamDisturbed()); EXPECT_FALSE(buffer->HasPendingActivity()); EXPECT_EQ(nullptr, buffer->GetSideDataBlobForTest()); @@ -561,8 +561,8 @@ TEST_F(BodyStreamBufferTest, LoadErroredHandle) { client, ASSERT_NO_EXCEPTION); checkpoint.Call(2); - EXPECT_TRUE(buffer->IsStreamLocked(ASSERT_NO_EXCEPTION).value_or(false)); - EXPECT_TRUE(buffer->IsStreamDisturbed(ASSERT_NO_EXCEPTION).value_or(false)); + EXPECT_TRUE(buffer->IsStreamLocked()); + EXPECT_TRUE(buffer->IsStreamDisturbed()); EXPECT_FALSE(buffer->HasPendingActivity()); } diff --git a/chromium/third_party/blink/renderer/core/fetch/bytes_consumer_tee.cc b/chromium/third_party/blink/renderer/core/fetch/bytes_consumer_tee.cc index 1492d51b897..37ded968847 100644 --- a/chromium/third_party/blink/renderer/core/fetch/bytes_consumer_tee.cc +++ b/chromium/third_party/blink/renderer/core/fetch/bytes_consumer_tee.cc @@ -113,7 +113,7 @@ class TeeHelper final : public GarbageCollected<TeeHelper>, BytesConsumer* Destination1() const { return destination1_; } BytesConsumer* Destination2() const { return destination2_; } - void Trace(Visitor* visitor) override { + void Trace(Visitor* visitor) const override { visitor->Trace(src_); visitor->Trace(destination1_); visitor->Trace(destination2_); @@ -138,7 +138,7 @@ class TeeHelper final : public GarbageCollected<TeeHelper>, const char* data() const { return buffer_.data(); } wtf_size_t size() const { return buffer_.size(); } - void Trace(Visitor* visitor) {} + void Trace(Visitor* visitor) const {} private: Vector<char> buffer_; @@ -268,7 +268,7 @@ class TeeHelper final : public GarbageCollected<TeeHelper>, bool IsCancelled() const { return is_cancelled_; } - void Trace(Visitor* visitor) override { + void Trace(Visitor* visitor) const override { visitor->Trace(execution_context_); visitor->Trace(tee_); visitor->Trace(client_); diff --git a/chromium/third_party/blink/renderer/core/fetch/bytes_consumer_test_util.h b/chromium/third_party/blink/renderer/core/fetch/bytes_consumer_test_util.h index 41ade535ece..f3aae804144 100644 --- a/chromium/third_party/blink/renderer/core/fetch/bytes_consumer_test_util.h +++ b/chromium/third_party/blink/renderer/core/fetch/bytes_consumer_test_util.h @@ -42,7 +42,7 @@ class BytesConsumerTestUtil { USING_GARBAGE_COLLECTED_MIXIN(MockFetchDataLoaderClient); public: - void Trace(Visitor* visitor) override { + void Trace(Visitor* visitor) const override { FetchDataLoader::Client::Trace(visitor); } diff --git a/chromium/third_party/blink/renderer/core/fetch/bytes_uploader.cc b/chromium/third_party/blink/renderer/core/fetch/bytes_uploader.cc new file mode 100644 index 00000000000..1cb306e41fc --- /dev/null +++ b/chromium/third_party/blink/renderer/core/fetch/bytes_uploader.cc @@ -0,0 +1,169 @@ +// Copyright 2020 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/fetch/bytes_uploader.h" + +#include "base/numerics/checked_math.h" +#include "base/numerics/safe_conversions.h" +#include "base/single_thread_task_runner.h" +#include "net/base/net_errors.h" +#include "third_party/blink/public/platform/task_type.h" +#include "third_party/blink/renderer/core/execution_context/execution_context.h" +#include "third_party/blink/renderer/platform/heap/persistent.h" +#include "third_party/blink/renderer/platform/loader/fetch/bytes_consumer.h" + +namespace blink { + +BytesUploader::BytesUploader( + BytesConsumer* consumer, + mojo::PendingReceiver<network::mojom::blink::ChunkedDataPipeGetter> + pending_receiver, + scoped_refptr<base::SingleThreadTaskRunner> task_runner) + : consumer_(consumer), + receiver_(this, std::move(pending_receiver)), + upload_pipe_watcher_(FROM_HERE, + mojo::SimpleWatcher::ArmingPolicy::MANUAL, + std::move(task_runner)) { + DCHECK(consumer_); + DCHECK_EQ(consumer_->GetPublicState(), + BytesConsumer::PublicState::kReadableOrWaiting); +} + +BytesUploader::~BytesUploader() = default; + +void BytesUploader::Trace(blink::Visitor* visitor) const { + visitor->Trace(consumer_); + BytesConsumer::Client::Trace(visitor); +} + +void BytesUploader::GetSize(GetSizeCallback get_size_callback) { + DCHECK(!get_size_callback_); + get_size_callback_ = std::move(get_size_callback); +} + +void BytesUploader::StartReading( + mojo::ScopedDataPipeProducerHandle upload_pipe) { + DVLOG(3) << this << " StartReading()"; + DCHECK(get_size_callback_); + DCHECK(upload_pipe); + if (upload_pipe_) { + // Replay was asked by net/ service. + CloseOnError(); + return; + } + upload_pipe_ = std::move(upload_pipe); + upload_pipe_watcher_.Watch(upload_pipe_.get(), MOJO_HANDLE_SIGNAL_WRITABLE, + WTF::BindRepeating(&BytesUploader::OnPipeWriteable, + WrapWeakPersistent(this))); + consumer_->SetClient(this); + if (consumer_->GetPublicState() == + BytesConsumer::PublicState::kReadableOrWaiting) { + WriteDataOnPipe(); + } +} + +void BytesUploader::OnStateChange() { + DVLOG(3) << this << " OnStateChange(). consumer_->GetPublicState()=" + << consumer_->GetPublicState(); + DCHECK(get_size_callback_); + switch (consumer_->GetPublicState()) { + case BytesConsumer::PublicState::kReadableOrWaiting: + WriteDataOnPipe(); + return; + case BytesConsumer::PublicState::kClosed: + Close(); + return; + case BytesConsumer::PublicState::kErrored: + CloseOnError(); + return; + } + NOTREACHED(); +} + +void BytesUploader::OnPipeWriteable(MojoResult unused) { + WriteDataOnPipe(); +} + +void BytesUploader::WriteDataOnPipe() { + DVLOG(3) << this << " WriteDataOnPipe(). consumer_->GetPublicState()=" + << consumer_->GetPublicState(); + DCHECK(upload_pipe_); + DCHECK(get_size_callback_); + if (!upload_pipe_.is_valid()) + return; + + while (true) { + const char* buffer; + size_t available; + auto consumer_result = consumer_->BeginRead(&buffer, &available); + DVLOG(3) << " consumer_->BeginRead()=" << consumer_result + << ", available=" << available; + switch (consumer_result) { + case BytesConsumer::Result::kError: + CloseOnError(); + return; + case BytesConsumer::Result::kShouldWait: + return; + case BytesConsumer::Result::kDone: + Close(); + return; + case BytesConsumer::Result::kOk: + break; + } + DCHECK_EQ(consumer_result, BytesConsumer::Result::kOk); + uint32_t written_bytes = base::saturated_cast<uint32_t>(available); + const MojoResult mojo_result = upload_pipe_->WriteData( + buffer, &written_bytes, MOJO_WRITE_DATA_FLAG_NONE); + DVLOG(3) << " upload_pipe_->WriteData()=" << mojo_result + << ", mojo_written=" << written_bytes + << ", consumer_->EndRead()=" << consumer_result; + if (mojo_result == MOJO_RESULT_SHOULD_WAIT) { + // Wait for the pipe to have more capacity available + consumer_result = consumer_->EndRead(0); + upload_pipe_watcher_.ArmOrNotify(); + return; + } + if (mojo_result != MOJO_RESULT_OK) { + CloseOnError(); + return; + } + + consumer_result = consumer_->EndRead(written_bytes); + if (!base::CheckAdd(total_size_, written_bytes) + .AssignIfValid(&total_size_)) { + CloseOnError(); + return; + } + + switch (consumer_result) { + case BytesConsumer::Result::kError: + CloseOnError(); + return; + case BytesConsumer::Result::kShouldWait: + NOTREACHED(); + return; + case BytesConsumer::Result::kDone: + Close(); + return; + case BytesConsumer::Result::kOk: + break; + } + } +} + +void BytesUploader::Close() { + DVLOG(3) << this << " Close(). total_size=" << total_size_; + DCHECK(get_size_callback_); + std::move(get_size_callback_).Run(net::OK, total_size_); +} + +void BytesUploader::CloseOnError() { + DVLOG(3) << this << " CloseOnError(). total_size=" << total_size_; + DCHECK(consumer_); + consumer_->Cancel(); + DCHECK(get_size_callback_); + std::move(get_size_callback_).Run(net::ERR_FAILED, total_size_); +} + +} // namespace blink diff --git a/chromium/third_party/blink/renderer/core/fetch/bytes_uploader.h b/chromium/third_party/blink/renderer/core/fetch/bytes_uploader.h new file mode 100644 index 00000000000..304814b39b9 --- /dev/null +++ b/chromium/third_party/blink/renderer/core/fetch/bytes_uploader.h @@ -0,0 +1,67 @@ +// Copyright 2020 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_FETCH_BYTES_UPLOADER_H_ +#define THIRD_PARTY_BLINK_RENDERER_CORE_FETCH_BYTES_UPLOADER_H_ + +#include "mojo/public/cpp/bindings/pending_remote.h" +#include "mojo/public/cpp/bindings/receiver.h" +#include "mojo/public/cpp/system/data_pipe.h" +#include "mojo/public/cpp/system/simple_watcher.h" +#include "services/network/public/mojom/chunked_data_pipe_getter.mojom-blink.h" +#include "third_party/blink/renderer/core/core_export.h" +#include "third_party/blink/renderer/platform/heap/handle.h" +#include "third_party/blink/renderer/platform/loader/fetch/bytes_consumer.h" + +namespace base { +class SingleThreadTaskRunner; +} // namespace base + +namespace blink { + +class CORE_EXPORT BytesUploader + : public GarbageCollected<BytesUploader>, + public BytesConsumer::Client, + public network::mojom::blink::ChunkedDataPipeGetter { + USING_GARBAGE_COLLECTED_MIXIN(BytesUploader); + + public: + BytesUploader( + BytesConsumer* consumer, + mojo::PendingReceiver<network::mojom::blink::ChunkedDataPipeGetter> + pending_receiver, + scoped_refptr<base::SingleThreadTaskRunner> task_runner); + ~BytesUploader() override; + BytesUploader(const BytesUploader&) = delete; + BytesUploader& operator=(const BytesUploader&) = delete; + + void Trace(blink::Visitor*) const override; + + private: + // BytesConsumer::Client implementation + void OnStateChange() override; + String DebugName() const override { return "BytesUploader"; } + + // mojom::ChunkedDataPipeGetter implementation: + void GetSize(GetSizeCallback get_size_callback) override; + void StartReading(mojo::ScopedDataPipeProducerHandle upload_pipe) override; + + void OnPipeWriteable(MojoResult unused); + void WriteDataOnPipe(); + + void Close(); + // TODO(yoichio): Add a string parameter and show it on console. + void CloseOnError(); + + Member<BytesConsumer> consumer_; + mojo::Receiver<network::mojom::blink::ChunkedDataPipeGetter> receiver_; + mojo::ScopedDataPipeProducerHandle upload_pipe_; + mojo::SimpleWatcher upload_pipe_watcher_; + GetSizeCallback get_size_callback_; + uint64_t total_size_ = 0; +}; + +} // namespace blink + +#endif // THIRD_PARTY_BLINK_RENDERER_CORE_FETCH_BYTES_UPLOADER_H_ diff --git a/chromium/third_party/blink/renderer/core/fetch/bytes_uploader_test.cc b/chromium/third_party/blink/renderer/core/fetch/bytes_uploader_test.cc new file mode 100644 index 00000000000..61eed927154 --- /dev/null +++ b/chromium/third_party/blink/renderer/core/fetch/bytes_uploader_test.cc @@ -0,0 +1,238 @@ +// Copyright 2020 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/fetch/bytes_uploader.h" + +#include "base/test/mock_callback.h" +#include "mojo/public/cpp/bindings/remote.h" +#include "net/base/net_errors.h" +#include "services/network/public/mojom/chunked_data_pipe_getter.mojom-blink.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "third_party/blink/renderer/platform/scheduler/public/thread.h" +#include "third_party/blink/renderer/platform/testing/unit_test_helpers.h" + +using network::mojom::blink::ChunkedDataPipeGetter; +using testing::_; +using testing::InSequence; +using testing::Invoke; +using testing::Return; +namespace blink { + +typedef testing::StrictMock<testing::MockFunction<void(int)>> Checkpoint; + +class MockBytesConsumer : public BytesConsumer { + public: + MockBytesConsumer() = default; + ~MockBytesConsumer() override = default; + + MOCK_METHOD2(BeginRead, Result(const char**, size_t*)); + MOCK_METHOD1(EndRead, Result(size_t)); + MOCK_METHOD1(SetClient, void(Client*)); + MOCK_METHOD0(ClearClient, void()); + MOCK_METHOD0(Cancel, void()); + MOCK_CONST_METHOD0(GetPublicState, PublicState()); + MOCK_CONST_METHOD0(GetError, Error()); + MOCK_CONST_METHOD0(DebugName, String()); +}; + +class BytesUploaderTest : public ::testing::Test { + public: + void InitializeBytesUploader(uint32_t capacity = 100u) { + mock_bytes_consumer_ = MakeGarbageCollected<MockBytesConsumer>(); + EXPECT_CALL(*mock_bytes_consumer_, GetPublicState()) + .WillRepeatedly(Return(BytesConsumer::PublicState::kReadableOrWaiting)); + + bytes_uploader_ = MakeGarbageCollected<BytesUploader>( + mock_bytes_consumer_, remote_.BindNewPipeAndPassReceiver(), + Thread::Current()->GetTaskRunner()); + + const MojoCreateDataPipeOptions data_pipe_options{ + sizeof(MojoCreateDataPipeOptions), MOJO_CREATE_DATA_PIPE_FLAG_NONE, 1, + capacity}; + ASSERT_EQ(MOJO_RESULT_OK, + mojo::CreateDataPipe(&data_pipe_options, &writable_, &readable_)); + } + + MockBytesConsumer& Mock() const { return *mock_bytes_consumer_; } + mojo::ScopedDataPipeProducerHandle& Writable() { return writable_; } + mojo::ScopedDataPipeConsumerHandle& Readable() { return readable_; } + mojo::Remote<ChunkedDataPipeGetter>& Remote() { return remote_; } + + private: + Persistent<BytesUploader> bytes_uploader_; + Persistent<MockBytesConsumer> mock_bytes_consumer_; + + mojo::ScopedDataPipeProducerHandle writable_; + mojo::ScopedDataPipeConsumerHandle readable_; + mojo::Remote<ChunkedDataPipeGetter> remote_; +}; + +TEST_F(BytesUploaderTest, Create) { + MockBytesConsumer* mock_bytes_consumer = + MakeGarbageCollected<MockBytesConsumer>(); + Checkpoint checkpoint; + { + InSequence s; + EXPECT_CALL(checkpoint, Call(1)); + EXPECT_CALL(*mock_bytes_consumer, GetPublicState()) + .WillRepeatedly(Return(BytesConsumer::PublicState::kReadableOrWaiting)); + } + + checkpoint.Call(1); + mojo::PendingRemote<ChunkedDataPipeGetter> pending_remote; + BytesUploader* bytes_uploader_ = MakeGarbageCollected<BytesUploader>( + mock_bytes_consumer, pending_remote.InitWithNewPipeAndPassReceiver(), + Thread::Current()->GetTaskRunner()); + ASSERT_TRUE(bytes_uploader_); +} + +// TODO(yoichio): Needs BytesConsumer state tests. + +TEST_F(BytesUploaderTest, ReadEmpty) { + InitializeBytesUploader(); + + base::MockCallback<ChunkedDataPipeGetter::GetSizeCallback> get_size_callback; + Checkpoint checkpoint; + { + InSequence s; + + EXPECT_CALL(checkpoint, Call(1)); + EXPECT_CALL(checkpoint, Call(2)); + EXPECT_CALL(Mock(), SetClient(_)); + EXPECT_CALL(Mock(), GetPublicState()) + .WillRepeatedly(Return(BytesConsumer::PublicState::kReadableOrWaiting)); + EXPECT_CALL(Mock(), BeginRead(_, _)) + .WillOnce(Return(BytesConsumer::Result::kDone)); + EXPECT_CALL(get_size_callback, Run(net::OK, 0u)); + + EXPECT_CALL(checkpoint, Call(3)); + } + + checkpoint.Call(1); + Remote()->GetSize(get_size_callback.Get()); + Remote()->StartReading(std::move(Writable())); + + checkpoint.Call(2); + test::RunPendingTasks(); + + checkpoint.Call(3); + char buffer[20] = {}; + uint32_t num_bytes = sizeof(buffer); + MojoResult rv = + Readable()->ReadData(buffer, &num_bytes, MOJO_READ_DATA_FLAG_NONE); + EXPECT_EQ(MOJO_RESULT_SHOULD_WAIT, rv); +} + +TEST_F(BytesUploaderTest, ReadSmall) { + InitializeBytesUploader(); + + base::MockCallback<ChunkedDataPipeGetter::GetSizeCallback> get_size_callback; + Checkpoint checkpoint; + { + InSequence s; + EXPECT_CALL(checkpoint, Call(1)); + EXPECT_CALL(checkpoint, Call(2)); + EXPECT_CALL(Mock(), SetClient(_)); + EXPECT_CALL(Mock(), GetPublicState()) + .WillRepeatedly(Return(BytesConsumer::PublicState::kReadableOrWaiting)); + EXPECT_CALL(Mock(), BeginRead(_, _)) + .WillOnce(Invoke([](const char** buffer, size_t* size) { + *size = 6; + *buffer = "foobar"; + return BytesConsumer::Result::kOk; + })); + EXPECT_CALL(Mock(), EndRead(6u)) + .WillOnce(Return(BytesConsumer::Result::kDone)); + EXPECT_CALL(get_size_callback, Run(net::OK, 6u)); + + EXPECT_CALL(checkpoint, Call(3)); + } + + checkpoint.Call(1); + Remote()->GetSize(get_size_callback.Get()); + Remote()->StartReading(std::move(Writable())); + + checkpoint.Call(2); + test::RunPendingTasks(); + + checkpoint.Call(3); + char buffer[20] = {}; + uint32_t num_bytes = sizeof(buffer); + EXPECT_EQ(MOJO_RESULT_OK, + Readable()->ReadData(buffer, &num_bytes, MOJO_READ_DATA_FLAG_NONE)); + EXPECT_EQ(6u, num_bytes); + EXPECT_STREQ("foobar", buffer); +} + +TEST_F(BytesUploaderTest, ReadOverPipeCapacity) { + InitializeBytesUploader(10u); + + base::MockCallback<ChunkedDataPipeGetter::GetSizeCallback> get_size_callback; + Checkpoint checkpoint; + { + InSequence s; + + EXPECT_CALL(checkpoint, Call(1)); + EXPECT_CALL(checkpoint, Call(2)); + EXPECT_CALL(Mock(), SetClient(_)); + EXPECT_CALL(Mock(), GetPublicState()) + .WillRepeatedly(Return(BytesConsumer::PublicState::kReadableOrWaiting)); + EXPECT_CALL(Mock(), BeginRead(_, _)) + .WillOnce(Invoke([](const char** buffer, size_t* size) { + *size = 12; + *buffer = "foobarFOOBAR"; + return BytesConsumer::Result::kOk; + })); + EXPECT_CALL(Mock(), EndRead(10u)) + .WillOnce(Return(BytesConsumer::Result::kOk)); + + EXPECT_CALL(Mock(), BeginRead(_, _)) + .WillOnce(Invoke([](const char** buffer, size_t* size) { + *size = 2; + *buffer = "AR"; + return BytesConsumer::Result::kOk; + })); + EXPECT_CALL(Mock(), EndRead(0u)) + .WillOnce(Return(BytesConsumer::Result::kOk)); + + EXPECT_CALL(checkpoint, Call(3)); + EXPECT_CALL(checkpoint, Call(4)); + EXPECT_CALL(Mock(), BeginRead(_, _)) + .WillOnce(Invoke([](const char** buffer, size_t* size) { + *size = 2; + *buffer = "AR"; + return BytesConsumer::Result::kOk; + })); + EXPECT_CALL(Mock(), EndRead(2u)) + .WillOnce(Return(BytesConsumer::Result::kDone)); + EXPECT_CALL(get_size_callback, Run(net::OK, 12u)); + } + + checkpoint.Call(1); + Remote()->GetSize(get_size_callback.Get()); + Remote()->StartReading(std::move(Writable())); + + checkpoint.Call(2); + test::RunPendingTasks(); + + checkpoint.Call(3); + char buffer[20] = {}; + uint32_t num_bytes = sizeof(buffer); + EXPECT_EQ(MOJO_RESULT_OK, + Readable()->ReadData(buffer, &num_bytes, MOJO_READ_DATA_FLAG_NONE)); + EXPECT_EQ(10u, num_bytes); + EXPECT_STREQ("foobarFOOB", buffer); + + checkpoint.Call(4); + test::RunPendingTasks(); + char buffer2[20] = {}; + num_bytes = sizeof(buffer2); + EXPECT_EQ(MOJO_RESULT_OK, Readable()->ReadData(buffer2, &num_bytes, + MOJO_READ_DATA_FLAG_NONE)); + EXPECT_EQ(2u, num_bytes); + EXPECT_STREQ("AR", buffer2); +} + +} // namespace blink diff --git a/chromium/third_party/blink/renderer/core/fetch/fetch_data_loader.cc b/chromium/third_party/blink/renderer/core/fetch/fetch_data_loader.cc index 50cfd7c5141..99cbd31de9a 100644 --- a/chromium/third_party/blink/renderer/core/fetch/fetch_data_loader.cc +++ b/chromium/third_party/blink/renderer/core/fetch/fetch_data_loader.cc @@ -104,7 +104,7 @@ class FetchDataLoaderAsBlobHandle final : public FetchDataLoader, String DebugName() const override { return "FetchDataLoaderAsBlobHandle"; } - void Trace(Visitor* visitor) override { + void Trace(Visitor* visitor) const override { visitor->Trace(consumer_); visitor->Trace(client_); FetchDataLoader::Trace(visitor); @@ -182,7 +182,7 @@ class FetchDataLoaderAsArrayBuffer final : public FetchDataLoader, String DebugName() const override { return "FetchDataLoaderAsArrayBuffer"; } - void Trace(Visitor* visitor) override { + void Trace(Visitor* visitor) const override { visitor->Trace(consumer_); visitor->Trace(client_); FetchDataLoader::Trace(visitor); @@ -266,7 +266,7 @@ class FetchDataLoaderAsFailure final : public FetchDataLoader, void Cancel() override { consumer_->Cancel(); } - void Trace(Visitor* visitor) override { + void Trace(Visitor* visitor) const override { visitor->Trace(consumer_); visitor->Trace(client_); FetchDataLoader::Trace(visitor); @@ -352,7 +352,7 @@ class FetchDataLoaderAsFormData final : public FetchDataLoader, multipart_parser_->Cancel(); } - void Trace(Visitor* visitor) override { + void Trace(Visitor* visitor) const override { visitor->Trace(consumer_); visitor->Trace(client_); visitor->Trace(form_data_); @@ -507,7 +507,7 @@ class FetchDataLoaderAsString final : public FetchDataLoader, void Cancel() override { consumer_->Cancel(); } - void Trace(Visitor* visitor) override { + void Trace(Visitor* visitor) const override { visitor->Trace(consumer_); visitor->Trace(client_); FetchDataLoader::Trace(visitor); @@ -644,7 +644,7 @@ class FetchDataLoaderAsDataPipe final : public FetchDataLoader, void Cancel() override { StopInternal(); } - void Trace(Visitor* visitor) override { + void Trace(Visitor* visitor) const override { visitor->Trace(consumer_); visitor->Trace(client_); FetchDataLoader::Trace(visitor); diff --git a/chromium/third_party/blink/renderer/core/fetch/fetch_data_loader.h b/chromium/third_party/blink/renderer/core/fetch/fetch_data_loader.h index 070f97a1d98..75a9bc80693 100644 --- a/chromium/third_party/blink/renderer/core/fetch/fetch_data_loader.h +++ b/chromium/third_party/blink/renderer/core/fetch/fetch_data_loader.h @@ -66,7 +66,7 @@ class CORE_EXPORT FetchDataLoader : public GarbageCollected<FetchDataLoader> { // This function is called when an abort has been signalled. virtual void Abort() = 0; - void Trace(Visitor* visitor) override {} + void Trace(Visitor* visitor) const override {} }; static FetchDataLoader* CreateLoaderAsBlobHandle(const String& mime_type); @@ -91,7 +91,7 @@ class CORE_EXPORT FetchDataLoader : public GarbageCollected<FetchDataLoader> { virtual void Cancel() = 0; - virtual void Trace(Visitor* visitor) {} + virtual void Trace(Visitor* visitor) const {} }; } // namespace blink diff --git a/chromium/third_party/blink/renderer/core/fetch/fetch_data_loader_test.cc b/chromium/third_party/blink/renderer/core/fetch/fetch_data_loader_test.cc index 69aa8773da3..acb6e795a05 100644 --- a/chromium/third_party/blink/renderer/core/fetch/fetch_data_loader_test.cc +++ b/chromium/third_party/blink/renderer/core/fetch/fetch_data_loader_test.cc @@ -94,7 +94,7 @@ class FetchDataLoaderTest : public testing::Test { BytesConsumer* GetDestination() { return destination_; } - void Trace(Visitor* visitor) override { + void Trace(Visitor* visitor) const override { visitor->Trace(destination_); visitor->Trace(completion_notifier_); FetchDataLoader::Client::Trace(visitor); diff --git a/chromium/third_party/blink/renderer/core/fetch/fetch_header_list.h b/chromium/third_party/blink/renderer/core/fetch/fetch_header_list.h index d1a561786f6..3a8199c994f 100644 --- a/chromium/third_party/blink/renderer/core/fetch/fetch_header_list.h +++ b/chromium/third_party/blink/renderer/core/fetch/fetch_header_list.h @@ -51,7 +51,7 @@ class CORE_EXPORT FetchHeaderList final static bool IsValidHeaderName(const String&); static bool IsValidHeaderValue(const String&); - void Trace(Visitor* visitor) {} + void Trace(Visitor* visitor) const {} private: // While using STL data structures in Blink is not very common or diff --git a/chromium/third_party/blink/renderer/core/fetch/fetch_manager.cc b/chromium/third_party/blink/renderer/core/fetch/fetch_manager.cc index 7bae19a506f..e9b0475fe6a 100644 --- a/chromium/third_party/blink/renderer/core/fetch/fetch_manager.cc +++ b/chromium/third_party/blink/renderer/core/fetch/fetch_manager.cc @@ -102,7 +102,7 @@ class FetchManager::Loader final bool is_isolated_world, AbortSignal*); ~Loader() override; - void Trace(Visitor*) override; + void Trace(Visitor*) const override; // ThreadableLoaderClient implementation. bool WillFollowRedirect(const KURL&, const ResourceResponse&) override; @@ -203,7 +203,7 @@ class FetchManager::Loader final bool IsFinished() const { return finished_; } - void Trace(Visitor* visitor) override { + void Trace(Visitor* visitor) const override { visitor->Trace(body_); visitor->Trace(updater_); visitor->Trace(response_); @@ -274,7 +274,7 @@ FetchManager::Loader::~Loader() { DCHECK(!threadable_loader_); } -void FetchManager::Loader::Trace(Visitor* visitor) { +void FetchManager::Loader::Trace(Visitor* visitor) const { visitor->Trace(fetch_manager_); visitor->Trace(resolver_); visitor->Trace(fetch_request_data_); @@ -407,6 +407,20 @@ void FetchManager::Loader::DidReceiveResponse( } } + // TODO(crbug.com/1072350): Remove this once enough data is collected. + if (fetch_request_data_->Method() != http_names::kGET && + fetch_request_data_->Method() != http_names::kHEAD && + fetch_request_data_->Mode() == network::mojom::RequestMode::kNoCors && + tainting == FetchRequestData::kOpaqueTainting) { + UseCounter::Count(execution_context_, + WebFeature::kFetchAPINonGetOrHeadOpaqueResponse); + if (url_list_.size() > 1) { + UseCounter::Count( + execution_context_, + WebFeature::kFetchAPINonGetOrHeadOpaqueResponseWithRedirect); + } + } + place_holder_body_ = MakeGarbageCollected<PlaceHolderBytesConsumer>(); FetchResponseData* response_data = FetchResponseData::CreateWithBuffer( BodyStreamBuffer::Create(script_state, place_holder_body_, signal_)); @@ -535,7 +549,9 @@ void FetchManager::Loader::Start(ExceptionState& exception_state) { // "- should fetching |request| be blocked as content security returns // blocked" if (!execution_context_->GetContentSecurityPolicyForWorld() - ->AllowConnectToSource(fetch_request_data_->Url())) { + ->AllowConnectToSource(fetch_request_data_->Url(), + fetch_request_data_->Url(), + RedirectStatus::kNoRedirect)) { // "A network error." PerformNetworkError( "Refused to connect to '" + fetch_request_data_->Url().ElidedString() + @@ -713,10 +729,29 @@ void FetchManager::Loader::PerformHTTPFetch(ExceptionState& exception_state) { if (fetch_request_data_->Method() != http_names::kGET && fetch_request_data_->Method() != http_names::kHEAD) { if (fetch_request_data_->Buffer()) { - request.SetHttpBody( - fetch_request_data_->Buffer()->DrainAsFormData(exception_state)); + scoped_refptr<EncodedFormData> form_data = + fetch_request_data_->Buffer()->DrainAsFormData(exception_state); + if (exception_state.HadException()) return; + if (form_data) { + request.SetHttpBody(form_data); + } else if (RuntimeEnabledFeatures::OutOfBlinkCorsEnabled() && + RuntimeEnabledFeatures::FetchUploadStreamingEnabled( + execution_context_)) { + UseCounter::Count(execution_context_, + WebFeature::kFetchUploadStreaming); + mojo::PendingRemote<network::mojom::blink::ChunkedDataPipeGetter> + pending_remote; + fetch_request_data_->Buffer()->DrainAsChunkedDataPipeGetter( + resolver_->GetScriptState(), + pending_remote.InitWithNewPipeAndPassReceiver(), exception_state); + if (exception_state.HadException()) + return; + request.MutableBody().SetStreamBody(std::move(pending_remote)); + request.SetAllowHTTP1ForStreamingUpload( + fetch_request_data_->AllowHTTP1ForStreamingUpload()); + } } } request.SetCacheMode(fetch_request_data_->CacheMode()); @@ -878,7 +913,7 @@ void FetchManager::OnLoaderFinished(Loader* loader) { loader->Dispose(); } -void FetchManager::Trace(Visitor* visitor) { +void FetchManager::Trace(Visitor* visitor) const { visitor->Trace(loaders_); ExecutionContextLifecycleObserver::Trace(visitor); } diff --git a/chromium/third_party/blink/renderer/core/fetch/fetch_manager.h b/chromium/third_party/blink/renderer/core/fetch/fetch_manager.h index 6b5c87c25ff..e97080faf3e 100644 --- a/chromium/third_party/blink/renderer/core/fetch/fetch_manager.h +++ b/chromium/third_party/blink/renderer/core/fetch/fetch_manager.h @@ -32,7 +32,7 @@ class CORE_EXPORT FetchManager final ExceptionState&); void ContextDestroyed() override; - void Trace(Visitor*) override; + void Trace(Visitor*) const override; private: class Loader; diff --git a/chromium/third_party/blink/renderer/core/fetch/fetch_request_data.cc b/chromium/third_party/blink/renderer/core/fetch/fetch_request_data.cc index 2bc52ecc3a0..ea2c33a39eb 100644 --- a/chromium/third_party/blink/renderer/core/fetch/fetch_request_data.cc +++ b/chromium/third_party/blink/renderer/core/fetch/fetch_request_data.cc @@ -18,6 +18,7 @@ #include "third_party/blink/renderer/platform/bindings/script_state.h" #include "third_party/blink/renderer/platform/heap/heap.h" #include "third_party/blink/renderer/platform/loader/fetch/bytes_consumer.h" +#include "third_party/blink/renderer/platform/loader/fetch/data_pipe_bytes_consumer.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/network/http_names.h" @@ -66,17 +67,31 @@ bool IsExcludedHeaderForServiceWorkerFetchEvent(const String& header_name) { return false; } +void SignalSize( + std::unique_ptr<mojo::Remote<network::mojom::blink::ChunkedDataPipeGetter>>, + Persistent<DataPipeBytesConsumer::CompletionNotifier> notifier, + int32_t status, + uint64_t size) { + if (status != 0) { + // error case + notifier->SignalError(BytesConsumer::Error()); + return; + } + notifier->SignalSize(size); +} + } // namespace FetchRequestData* FetchRequestData::Create( ScriptState* script_state, - const mojom::blink::FetchAPIRequest& fetch_api_request, + mojom::blink::FetchAPIRequestPtr fetch_api_request, ForServiceWorkerFetchEvent for_service_worker_fetch_event) { + DCHECK(fetch_api_request); FetchRequestData* request = MakeGarbageCollected<FetchRequestData>( script_state ? ExecutionContext::From(script_state) : nullptr); - request->url_ = fetch_api_request.url; - request->method_ = AtomicString(fetch_api_request.method); - for (const auto& pair : fetch_api_request.headers) { + request->url_ = fetch_api_request->url; + request->method_ = AtomicString(fetch_api_request->method); + for (const auto& pair : fetch_api_request->headers) { // TODO(leonhsl): Check sources of |fetch_api_request.headers| to make clear // whether we really need this filter. if (EqualIgnoringASCIICase(pair.key, "referer")) @@ -88,19 +103,56 @@ FetchRequestData* FetchRequestData::Create( request->header_list_->Append(pair.key, pair.value); } - if (fetch_api_request.blob) { - DCHECK(!fetch_api_request.body); + if (fetch_api_request->blob) { + DCHECK(fetch_api_request->body.IsEmpty()); request->SetBuffer(BodyStreamBuffer::Create( script_state, MakeGarbageCollected<BlobBytesConsumer>( - ExecutionContext::From(script_state), fetch_api_request.blob), - nullptr /* AbortSignal */)); - } else if (fetch_api_request.body) { - request->SetBuffer(BodyStreamBuffer::Create( - script_state, - MakeGarbageCollected<FormDataBytesConsumer>( - ExecutionContext::From(script_state), fetch_api_request.body), + ExecutionContext::From(script_state), fetch_api_request->blob), nullptr /* AbortSignal */)); + } else if (fetch_api_request->body.FormBody()) { + request->SetBuffer( + BodyStreamBuffer::Create(script_state, + MakeGarbageCollected<FormDataBytesConsumer>( + ExecutionContext::From(script_state), + fetch_api_request->body.FormBody()), + nullptr /* AbortSignal */)); + } else if (fetch_api_request->body.StreamBody()) { + mojo::ScopedDataPipeConsumerHandle readable; + mojo::ScopedDataPipeProducerHandle writable; + MojoCreateDataPipeOptions options{sizeof(MojoCreateDataPipeOptions), + MOJO_CREATE_DATA_PIPE_FLAG_NONE, 1, 0}; + const MojoResult result = + mojo::CreateDataPipe(&options, &writable, &readable); + if (result == MOJO_RESULT_OK) { + DataPipeBytesConsumer::CompletionNotifier* completion_notifier = nullptr; + // Explicitly creating a ReadableStream here in order to remember + // that the request is created from a ReadableStream. + auto* stream = BodyStreamBuffer::Create( + script_state, + MakeGarbageCollected<DataPipeBytesConsumer>( + ExecutionContext::From(script_state) + ->GetTaskRunner(TaskType::kNetworking), + std::move(readable), &completion_notifier), + /*AbortSignal=*/nullptr) + ->Stream(); + request->SetBuffer( + MakeGarbageCollected<BodyStreamBuffer>(script_state, stream, + /*AbortSignal=*/nullptr)); + + auto body_remote = std::make_unique< + mojo::Remote<network::mojom::blink::ChunkedDataPipeGetter>>( + fetch_api_request->body.TakeStreamBody()); + auto* body_remote_raw = body_remote.get(); + (*body_remote_raw) + ->GetSize(WTF::Bind(SignalSize, std::move(body_remote), + WrapPersistent(completion_notifier))); + (*body_remote_raw)->StartReading(std::move(writable)); + } else { + request->SetBuffer(BodyStreamBuffer::Create( + script_state, BytesConsumer::CreateErrored(BytesConsumer::Error()), + nullptr /* AbortSignal */)); + } } // Context is always set to FETCH later, so we don't copy it @@ -108,25 +160,27 @@ FetchRequestData* FetchRequestData::Create( // TODO(crbug.com/1045925): Remove this comment too when // we deprecate SetContext. - request->SetDestination(fetch_api_request.destination); + request->SetDestination(fetch_api_request->destination); request->SetReferrerString(AtomicString(Referrer::NoReferrer())); - if (fetch_api_request.referrer) { - if (!fetch_api_request.referrer->url.IsEmpty()) - request->SetReferrerString(AtomicString(fetch_api_request.referrer->url)); - request->SetReferrerPolicy(fetch_api_request.referrer->policy); + if (fetch_api_request->referrer) { + if (!fetch_api_request->referrer->url.IsEmpty()) { + request->SetReferrerString( + AtomicString(fetch_api_request->referrer->url)); + } + request->SetReferrerPolicy(fetch_api_request->referrer->policy); } - request->SetMode(fetch_api_request.mode); - request->SetCredentials(fetch_api_request.credentials_mode); - request->SetCacheMode(fetch_api_request.cache_mode); - request->SetRedirect(fetch_api_request.redirect_mode); + request->SetMode(fetch_api_request->mode); + request->SetCredentials(fetch_api_request->credentials_mode); + request->SetCacheMode(fetch_api_request->cache_mode); + request->SetRedirect(fetch_api_request->redirect_mode); request->SetMimeType(request->header_list_->ExtractMIMEType()); - request->SetIntegrity(fetch_api_request.integrity); - request->SetKeepalive(fetch_api_request.keepalive); - request->SetIsHistoryNavigation(fetch_api_request.is_history_navigation); - request->SetPriority( - ConvertRequestPriorityToResourceLoadPriority(fetch_api_request.priority)); - if (fetch_api_request.fetch_window_id) - request->SetWindowId(fetch_api_request.fetch_window_id.value()); + request->SetIntegrity(fetch_api_request->integrity); + request->SetKeepalive(fetch_api_request->keepalive); + request->SetIsHistoryNavigation(fetch_api_request->is_history_navigation); + request->SetPriority(ConvertRequestPriorityToResourceLoadPriority( + fetch_api_request->priority)); + if (fetch_api_request->fetch_window_id) + request->SetWindowId(fetch_api_request->fetch_window_id.value()); return request; } @@ -154,6 +208,8 @@ FetchRequestData* FetchRequestData::CloneExceptBody() { request->is_history_navigation_ = is_history_navigation_; request->window_id_ = window_id_; request->trust_token_params_ = trust_token_params_; + request->allow_http1_for_streaming_upload_ = + allow_http1_for_streaming_upload_; return request; } @@ -213,7 +269,7 @@ FetchRequestData::FetchRequestData(ExecutionContext* execution_context) url_loader_factory_(execution_context), execution_context_(execution_context) {} -void FetchRequestData::Trace(Visitor* visitor) { +void FetchRequestData::Trace(Visitor* visitor) const { visitor->Trace(buffer_); visitor->Trace(header_list_); visitor->Trace(url_loader_factory_); diff --git a/chromium/third_party/blink/renderer/core/fetch/fetch_request_data.h b/chromium/third_party/blink/renderer/core/fetch/fetch_request_data.h index cf2e86c21b8..2bf540d0ccc 100644 --- a/chromium/third_party/blink/renderer/core/fetch/fetch_request_data.h +++ b/chromium/third_party/blink/renderer/core/fetch/fetch_request_data.h @@ -40,7 +40,7 @@ class CORE_EXPORT FetchRequestData final enum class ForServiceWorkerFetchEvent { kFalse, kTrue }; static FetchRequestData* Create(ScriptState*, - const mojom::blink::FetchAPIRequest&, + mojom::blink::FetchAPIRequestPtr, ForServiceWorkerFetchEvent); FetchRequestData* Clone(ScriptState*, ExceptionState&); FetchRequestData* Pass(ScriptState*, ExceptionState&); @@ -138,7 +138,14 @@ class CORE_EXPORT FetchRequestData final trust_token_params_ = std::move(trust_token_params); } - void Trace(Visitor*); + void SetAllowHTTP1ForStreamingUpload(bool allow) { + allow_http1_for_streaming_upload_ = allow; + } + bool AllowHTTP1ForStreamingUpload() const { + return allow_http1_for_streaming_upload_; + } + + void Trace(Visitor*) const; private: FetchRequestData* CloneExceptBody(); @@ -183,6 +190,7 @@ class CORE_EXPORT FetchRequestData final url_loader_factory_; base::UnguessableToken window_id_; Member<ExecutionContext> execution_context_; + bool allow_http1_for_streaming_upload_ = false; DISALLOW_COPY_AND_ASSIGN(FetchRequestData); }; diff --git a/chromium/third_party/blink/renderer/core/fetch/fetch_request_data_test.cc b/chromium/third_party/blink/renderer/core/fetch/fetch_request_data_test.cc index 57ab1b71c1b..78efa87e618 100644 --- a/chromium/third_party/blink/renderer/core/fetch/fetch_request_data_test.cc +++ b/chromium/third_party/blink/renderer/core/fetch/fetch_request_data_test.cc @@ -40,7 +40,7 @@ class FetchRequestDataTestingPlatformSupport : public TestingPlatformSupport { TEST(FetchRequestDataTest, For_ServiceWorkerFetchEvent_Headers) { { FetchRequestData* request_data = FetchRequestData::Create( - nullptr /* script_state */, *PrepareFetchAPIRequest(), + nullptr /* script_state */, PrepareFetchAPIRequest(), FetchRequestData::ForServiceWorkerFetchEvent::kTrue); EXPECT_EQ(2U, request_data->HeaderList()->size()); EXPECT_TRUE(request_data->HeaderList()->Has("x-hi-hi")); @@ -54,7 +54,7 @@ TEST(FetchRequestDataTest, For_ServiceWorkerFetchEvent_Headers) { platform; FetchRequestData* request_data = FetchRequestData::Create( - nullptr /* script_state */, *PrepareFetchAPIRequest(), + nullptr /* script_state */, PrepareFetchAPIRequest(), FetchRequestData::ForServiceWorkerFetchEvent::kTrue); EXPECT_EQ(1U, request_data->HeaderList()->size()); EXPECT_TRUE(request_data->HeaderList()->Has("x-hi-hi")); @@ -67,7 +67,7 @@ TEST(FetchRequestDataTest, For_ServiceWorkerFetchEvent_Headers) { TEST(FetchRequestDataTest, Not_For_ServiceWorkerFetchEvent_Headers) { { FetchRequestData* request_data = FetchRequestData::Create( - nullptr /* script_state */, *PrepareFetchAPIRequest(), + nullptr /* script_state */, PrepareFetchAPIRequest(), FetchRequestData::ForServiceWorkerFetchEvent::kFalse); EXPECT_EQ(4U, request_data->HeaderList()->size()); EXPECT_TRUE(request_data->HeaderList()->Has("x-hi-hi")); @@ -81,7 +81,7 @@ TEST(FetchRequestDataTest, Not_For_ServiceWorkerFetchEvent_Headers) { platform; FetchRequestData* request_data = FetchRequestData::Create( - nullptr /* script_state */, *PrepareFetchAPIRequest(), + nullptr /* script_state */, PrepareFetchAPIRequest(), FetchRequestData::ForServiceWorkerFetchEvent::kFalse); EXPECT_EQ(4U, request_data->HeaderList()->size()); EXPECT_TRUE(request_data->HeaderList()->Has("x-hi-hi")); diff --git a/chromium/third_party/blink/renderer/core/fetch/fetch_response_data.cc b/chromium/third_party/blink/renderer/core/fetch/fetch_response_data.cc index 7c96506830c..c595b241206 100644 --- a/chromium/third_party/blink/renderer/core/fetch/fetch_response_data.cc +++ b/chromium/third_party/blink/renderer/core/fetch/fetch_response_data.cc @@ -194,6 +194,10 @@ FetchResponseData* FetchResponseData::Clone(ScriptState* script_state, new_response->response_time_ = response_time_; new_response->cache_storage_cache_name_ = cache_storage_cache_name_; new_response->cors_exposed_header_names_ = cors_exposed_header_names_; + new_response->connection_info_ = connection_info_; + new_response->alpn_negotiated_protocol_ = alpn_negotiated_protocol_; + new_response->loaded_with_credentials_ = loaded_with_credentials_; + new_response->was_fetched_via_spdy_ = was_fetched_via_spdy_; switch (type_) { case Type::kBasic: @@ -257,11 +261,15 @@ mojom::blink::FetchAPIResponsePtr FetchResponseData::PopulateFetchAPIResponse( response->status_text = status_message_; response->response_type = type_; response->response_source = response_source_; + response->mime_type = mime_type_; response->response_time = response_time_; response->cache_storage_cache_name = cache_storage_cache_name_; response->cors_exposed_header_names = HeaderSetToVector(cors_exposed_header_names_); + response->connection_info = connection_info_; + response->alpn_negotiated_protocol = alpn_negotiated_protocol_; response->loaded_with_credentials = loaded_with_credentials_; + response->was_fetched_via_spdy = was_fetched_via_spdy_; for (const auto& header : HeaderList()->List()) response->headers.insert(header.first, header.second); response->parsed_headers = ParseHeaders( @@ -308,6 +316,16 @@ void FetchResponseData::InitFromResourceResponse( SetResponseSource(network::mojom::FetchResponseSource::kNetwork); } + SetConnectionInfo(response.ConnectionInfo()); + + // Some non-http responses, like data: url responses, will have a null + // |alpn_negotiated_protocol|. In these cases we leave the default + // value of "unknown". + if (!response.AlpnNegotiatedProtocol().IsNull()) + SetAlpnNegotiatedProtocol(response.AlpnNegotiatedProtocol()); + + SetWasFetchedViaSpdy(response.WasFetchedViaSPDY()); + // TODO(wanderview): Remove |tainting| and use |response.GetType()| // instead once the OOR-CORS disabled path is removed. SetLoadedWithCredentials( @@ -326,7 +344,10 @@ FetchResponseData::FetchResponseData(Type type, status_message_(status_message), header_list_(MakeGarbageCollected<FetchHeaderList>()), response_time_(base::Time::Now()), - loaded_with_credentials_(false) {} + connection_info_(net::HttpResponseInfo::CONNECTION_INFO_UNKNOWN), + alpn_negotiated_protocol_("unknown"), + loaded_with_credentials_(false), + was_fetched_via_spdy_(false) {} void FetchResponseData::ReplaceBodyStreamBuffer(BodyStreamBuffer* buffer) { if (type_ == Type::kBasic || type_ == Type::kCors) { @@ -339,7 +360,7 @@ void FetchResponseData::ReplaceBodyStreamBuffer(BodyStreamBuffer* buffer) { } } -void FetchResponseData::Trace(Visitor* visitor) { +void FetchResponseData::Trace(Visitor* visitor) const { visitor->Trace(header_list_); visitor->Trace(internal_response_); visitor->Trace(buffer_); diff --git a/chromium/third_party/blink/renderer/core/fetch/fetch_response_data.h b/chromium/third_party/blink/renderer/core/fetch/fetch_response_data.h index 1eb7454afb0..0c4bfcb335e 100644 --- a/chromium/third_party/blink/renderer/core/fetch/fetch_response_data.h +++ b/chromium/third_party/blink/renderer/core/fetch/fetch_response_data.h @@ -9,6 +9,7 @@ #include "base/macros.h" #include "base/memory/scoped_refptr.h" +#include "net/http/http_response_info.h" #include "services/network/public/mojom/fetch_api.mojom-blink-forward.h" #include "third_party/blink/public/mojom/fetch/fetch_api_request.mojom-blink-forward.h" #include "third_party/blink/public/mojom/fetch/fetch_api_response.mojom-blink-forward.h" @@ -103,10 +104,19 @@ class CORE_EXPORT FetchResponseData final void SetCorsExposedHeaderNames(const HTTPHeaderSet& header_names) { cors_exposed_header_names_ = header_names; } - bool LoadedWithCredentials() const { return loaded_with_credentials_; } + void SetConnectionInfo( + net::HttpResponseInfo::ConnectionInfo connection_info) { + connection_info_ = connection_info; + } + void SetAlpnNegotiatedProtocol(AtomicString alpn_negotiated_protocol) { + alpn_negotiated_protocol_ = alpn_negotiated_protocol; + } void SetLoadedWithCredentials(bool loaded_with_credentials) { loaded_with_credentials_ = loaded_with_credentials; } + void SetWasFetchedViaSpdy(bool was_fetched_via_spdy) { + was_fetched_via_spdy_ = was_fetched_via_spdy; + } // If the type is Default, replaces |buffer_|. // If the type is Basic or CORS, replaces |buffer_| and @@ -125,7 +135,7 @@ class CORE_EXPORT FetchResponseData final FetchRequestData::Tainting tainting, const ResourceResponse& response); - void Trace(Visitor*); + void Trace(Visitor*) const; private: network::mojom::FetchResponseType type_; @@ -141,7 +151,10 @@ class CORE_EXPORT FetchResponseData final base::Time response_time_; String cache_storage_cache_name_; HTTPHeaderSet cors_exposed_header_names_; + net::HttpResponseInfo::ConnectionInfo connection_info_; + AtomicString alpn_negotiated_protocol_; bool loaded_with_credentials_; + bool was_fetched_via_spdy_; DISALLOW_COPY_AND_ASSIGN(FetchResponseData); }; diff --git a/chromium/third_party/blink/renderer/core/fetch/fetch_response_data_test.cc b/chromium/third_party/blink/renderer/core/fetch/fetch_response_data_test.cc index d7f8d314135..ac21aeea0c9 100644 --- a/chromium/third_party/blink/renderer/core/fetch/fetch_response_data_test.cc +++ b/chromium/third_party/blink/renderer/core/fetch/fetch_response_data_test.cc @@ -267,8 +267,6 @@ TEST_F(FetchResponseDataTest, DefaultResponseTime) { TEST_F(FetchResponseDataTest, ContentSecurityPolicy) { base::test::ScopedFeatureList scoped_feature_list; - scoped_feature_list.InitAndEnableFeature( - network::features::kOutOfBlinkFrameAncestors); FetchResponseData* internal_response = CreateInternalResponse(); internal_response->HeaderList()->Append("content-security-policy", "frame-ancestors 'none'"); diff --git a/chromium/third_party/blink/renderer/core/fetch/form_data_bytes_consumer.cc b/chromium/third_party/blink/renderer/core/fetch/form_data_bytes_consumer.cc index 80ce8b92387..71d540e5326 100644 --- a/chromium/third_party/blink/renderer/core/fetch/form_data_bytes_consumer.cc +++ b/chromium/third_party/blink/renderer/core/fetch/form_data_bytes_consumer.cc @@ -313,7 +313,7 @@ class DataPipeAndDataBytesConsumer final : public BytesConsumer { String DebugName() const override { return "DataPipeAndDataBytesConsumer"; } - void Trace(Visitor* visitor) override { + void Trace(Visitor* visitor) const override { visitor->Trace(execution_context_); visitor->Trace(client_); visitor->Trace(simple_consumer_); @@ -479,7 +479,7 @@ class ComplexFormDataBytesConsumer final : public BytesConsumer { Error GetError() const override { return blob_bytes_consumer_->GetError(); } String DebugName() const override { return "ComplexFormDataBytesConsumer"; } - void Trace(Visitor* visitor) override { + void Trace(Visitor* visitor) const override { visitor->Trace(blob_bytes_consumer_); BytesConsumer::Trace(visitor); } diff --git a/chromium/third_party/blink/renderer/core/fetch/form_data_bytes_consumer.h b/chromium/third_party/blink/renderer/core/fetch/form_data_bytes_consumer.h index fd06b7d51cd..35f4eba6473 100644 --- a/chromium/third_party/blink/renderer/core/fetch/form_data_bytes_consumer.h +++ b/chromium/third_party/blink/renderer/core/fetch/form_data_bytes_consumer.h @@ -54,7 +54,7 @@ class FormDataBytesConsumer final : public BytesConsumer { Error GetError() const override { return impl_->GetError(); } String DebugName() const override { return impl_->DebugName(); } - void Trace(Visitor* visitor) override { + void Trace(Visitor* visitor) const override { visitor->Trace(impl_); BytesConsumer::Trace(visitor); } diff --git a/chromium/third_party/blink/renderer/core/fetch/form_data_bytes_consumer_test.cc b/chromium/third_party/blink/renderer/core/fetch/form_data_bytes_consumer_test.cc index 56cf200312c..31a95c200fb 100644 --- a/chromium/third_party/blink/renderer/core/fetch/form_data_bytes_consumer_test.cc +++ b/chromium/third_party/blink/renderer/core/fetch/form_data_bytes_consumer_test.cc @@ -103,7 +103,7 @@ scoped_refptr<EncodedFormData> DataPipeFormData() { new SimpleDataPipeGetter( String(" hello world"), data_pipe_getter_remote.InitWithNewPipeAndPassReceiver()); - body.AppendDataPipe(data_pipe_getter_remote.PassPipe()); + body.AppendDataPipe(std::move(data_pipe_getter_remote)); // Add another data pipe. mojo::PendingRemote<network::mojom::blink::DataPipeGetter> @@ -112,7 +112,7 @@ scoped_refptr<EncodedFormData> DataPipeFormData() { new SimpleDataPipeGetter( String(" here's another data pipe "), data_pipe_getter_remote2.InitWithNewPipeAndPassReceiver()); - body.AppendDataPipe(data_pipe_getter_remote2.PassPipe()); + body.AppendDataPipe(std::move(data_pipe_getter_remote2)); // Add some more data. body.AppendData(WebData("bar baz", 7)); diff --git a/chromium/third_party/blink/renderer/core/fetch/global_fetch.cc b/chromium/third_party/blink/renderer/core/fetch/global_fetch.cc index 80d8de09a20..aed0c0976b8 100644 --- a/chromium/third_party/blink/renderer/core/fetch/global_fetch.cc +++ b/chromium/third_party/blink/renderer/core/fetch/global_fetch.cc @@ -88,7 +88,7 @@ class GlobalFetchImpl final : public GarbageCollected<GlobalFetchImpl<T>>, return promise; } - void Trace(Visitor* visitor) override { + void Trace(Visitor* visitor) const override { visitor->Trace(fetch_manager_); ScopedFetcher::Trace(visitor); Supplement<T>::Trace(visitor); @@ -118,7 +118,7 @@ GlobalFetch::ScopedFetcher* GlobalFetch::ScopedFetcher::From( worker.GetExecutionContext()); } -void GlobalFetch::ScopedFetcher::Trace(Visitor* visitor) {} +void GlobalFetch::ScopedFetcher::Trace(Visitor* visitor) const {} ScriptPromise GlobalFetch::fetch(ScriptState* script_state, LocalDOMWindow& window, diff --git a/chromium/third_party/blink/renderer/core/fetch/global_fetch.h b/chromium/third_party/blink/renderer/core/fetch/global_fetch.h index f401074a8ec..8f9afe857de 100644 --- a/chromium/third_party/blink/renderer/core/fetch/global_fetch.h +++ b/chromium/third_party/blink/renderer/core/fetch/global_fetch.h @@ -33,7 +33,7 @@ class CORE_EXPORT GlobalFetch { static ScopedFetcher* From(LocalDOMWindow&); static ScopedFetcher* From(WorkerGlobalScope&); - void Trace(Visitor*) override; + void Trace(Visitor*) const override; }; static ScriptPromise fetch(ScriptState*, diff --git a/chromium/third_party/blink/renderer/core/fetch/headers.cc b/chromium/third_party/blink/renderer/core/fetch/headers.cc index 893ecc97fe9..ae3e5e8062b 100644 --- a/chromium/third_party/blink/renderer/core/fetch/headers.cc +++ b/chromium/third_party/blink/renderer/core/fetch/headers.cc @@ -292,7 +292,7 @@ Headers::Headers() Headers::Headers(FetchHeaderList* header_list) : header_list_(header_list), guard_(kNoneGuard) {} -void Headers::Trace(Visitor* visitor) { +void Headers::Trace(Visitor* visitor) const { visitor->Trace(header_list_); ScriptWrappable::Trace(visitor); } diff --git a/chromium/third_party/blink/renderer/core/fetch/headers.h b/chromium/third_party/blink/renderer/core/fetch/headers.h index 045bec08cdf..34c4be0f4b5 100644 --- a/chromium/third_party/blink/renderer/core/fetch/headers.h +++ b/chromium/third_party/blink/renderer/core/fetch/headers.h @@ -60,7 +60,7 @@ class CORE_EXPORT Headers final : public ScriptWrappable, void FillWith(const HeadersInit&, ExceptionState&); FetchHeaderList* HeaderList() const { return header_list_; } - void Trace(Visitor*) override; + void Trace(Visitor*) const override; private: // These methods should only be called when size() would return 0. diff --git a/chromium/third_party/blink/renderer/core/fetch/multipart_parser.cc b/chromium/third_party/blink/renderer/core/fetch/multipart_parser.cc index 2f3383b1201..43024b42f90 100644 --- a/chromium/third_party/blink/renderer/core/fetch/multipart_parser.cc +++ b/chromium/third_party/blink/renderer/core/fetch/multipart_parser.cc @@ -333,7 +333,7 @@ void MultipartParser::ParseTransportPadding(const char** bytes_pointer, ++(*bytes_pointer); } -void MultipartParser::Trace(Visitor* visitor) { +void MultipartParser::Trace(Visitor* visitor) const { visitor->Trace(client_); } diff --git a/chromium/third_party/blink/renderer/core/fetch/multipart_parser.h b/chromium/third_party/blink/renderer/core/fetch/multipart_parser.h index 210efffac61..0cc92c076a9 100644 --- a/chromium/third_party/blink/renderer/core/fetch/multipart_parser.h +++ b/chromium/third_party/blink/renderer/core/fetch/multipart_parser.h @@ -40,7 +40,7 @@ class CORE_EXPORT MultipartParser final virtual void PartDataInMultipartReceived(const char* bytes, size_t) = 0; // The method is called whenever all data of a complete part is parsed. virtual void PartDataInMultipartFullyReceived() = 0; - void Trace(Visitor* visitor) override {} + void Trace(Visitor* visitor) const override {} }; MultipartParser(Vector<char> boundary, Client*); @@ -50,7 +50,7 @@ class CORE_EXPORT MultipartParser final bool IsCancelled() const { return state_ == State::kCancelled; } - void Trace(Visitor*); + void Trace(Visitor*) const; private: class Matcher { diff --git a/chromium/third_party/blink/renderer/core/fetch/place_holder_bytes_consumer.cc b/chromium/third_party/blink/renderer/core/fetch/place_holder_bytes_consumer.cc index a5c44e81103..f0cba0e09bf 100644 --- a/chromium/third_party/blink/renderer/core/fetch/place_holder_bytes_consumer.cc +++ b/chromium/third_party/blink/renderer/core/fetch/place_holder_bytes_consumer.cc @@ -91,7 +91,7 @@ void PlaceHolderBytesConsumer::Update(BytesConsumer* consumer) { } } -void PlaceHolderBytesConsumer::Trace(Visitor* visitor) { +void PlaceHolderBytesConsumer::Trace(Visitor* visitor) const { visitor->Trace(underlying_); visitor->Trace(client_); BytesConsumer::Trace(visitor); diff --git a/chromium/third_party/blink/renderer/core/fetch/place_holder_bytes_consumer.h b/chromium/third_party/blink/renderer/core/fetch/place_holder_bytes_consumer.h index 09ef3a30d6a..c2c4ec025c9 100644 --- a/chromium/third_party/blink/renderer/core/fetch/place_holder_bytes_consumer.h +++ b/chromium/third_party/blink/renderer/core/fetch/place_holder_bytes_consumer.h @@ -31,7 +31,7 @@ class CORE_EXPORT PlaceHolderBytesConsumer final : public BytesConsumer { // This function can be called at most once. void Update(BytesConsumer* consumer); - void Trace(Visitor* visitor) override; + void Trace(Visitor* visitor) const override; private: Member<BytesConsumer> underlying_; diff --git a/chromium/third_party/blink/renderer/core/fetch/readable_stream_bytes_consumer.cc b/chromium/third_party/blink/renderer/core/fetch/readable_stream_bytes_consumer.cc index 383f9d4bde4..293826bee32 100644 --- a/chromium/third_party/blink/renderer/core/fetch/readable_stream_bytes_consumer.cc +++ b/chromium/third_party/blink/renderer/core/fetch/readable_stream_bytes_consumer.cc @@ -58,7 +58,7 @@ class ReadableStreamBytesConsumer::OnFulfilled final : public ScriptFunction { return v; } - void Trace(Visitor* visitor) override { + void Trace(Visitor* visitor) const override { visitor->Trace(consumer_); ScriptFunction::Trace(visitor); } @@ -84,7 +84,7 @@ class ReadableStreamBytesConsumer::OnRejected final : public ScriptFunction { return v; } - void Trace(Visitor* visitor) override { + void Trace(Visitor* visitor) const override { visitor->Trace(consumer_); ScriptFunction::Trace(visitor); } @@ -177,7 +177,7 @@ BytesConsumer::Error ReadableStreamBytesConsumer::GetError() const { return Error("Failed to read from a ReadableStream."); } -void ReadableStreamBytesConsumer::Trace(Visitor* visitor) { +void ReadableStreamBytesConsumer::Trace(Visitor* visitor) const { visitor->Trace(reader_); visitor->Trace(client_); visitor->Trace(pending_buffer_); diff --git a/chromium/third_party/blink/renderer/core/fetch/readable_stream_bytes_consumer.h b/chromium/third_party/blink/renderer/core/fetch/readable_stream_bytes_consumer.h index 8a74a038f90..246b4292395 100644 --- a/chromium/third_party/blink/renderer/core/fetch/readable_stream_bytes_consumer.h +++ b/chromium/third_party/blink/renderer/core/fetch/readable_stream_bytes_consumer.h @@ -38,7 +38,7 @@ class CORE_EXPORT ReadableStreamBytesConsumer final : public BytesConsumer { Error GetError() const override; String DebugName() const override { return "ReadableStreamBytesConsumer"; } - void Trace(Visitor*) override; + void Trace(Visitor*) const override; private: class OnFulfilled; diff --git a/chromium/third_party/blink/renderer/core/fetch/readable_stream_bytes_consumer_test.cc b/chromium/third_party/blink/renderer/core/fetch/readable_stream_bytes_consumer_test.cc index fd8affae8f9..eeeeae9434d 100644 --- a/chromium/third_party/blink/renderer/core/fetch/readable_stream_bytes_consumer_test.cc +++ b/chromium/third_party/blink/renderer/core/fetch/readable_stream_bytes_consumer_test.cc @@ -39,7 +39,7 @@ class MockClient : public GarbageCollected<MockClient>, MOCK_METHOD0(OnStateChange, void()); String DebugName() const override { return "MockClient"; } - void Trace(Visitor* visitor) override {} + void Trace(Visitor* visitor) const override {} }; TEST(ReadableStreamBytesConsumerTest, Create) { diff --git a/chromium/third_party/blink/renderer/core/fetch/request.cc b/chromium/third_party/blink/renderer/core/fetch/request.cc index a6bec95916d..9fdf4da73a9 100644 --- a/chromium/third_party/blink/renderer/core/fetch/request.cc +++ b/chromium/third_party/blink/renderer/core/fetch/request.cc @@ -175,6 +175,14 @@ static BodyStreamBuffer* ExtractBody(ScriptState* script_state, execution_context, std::move(form_data)), nullptr /* AbortSignal */); content_type = "application/x-www-form-urlencoded;charset=UTF-8"; + } else if (RuntimeEnabledFeatures::OutOfBlinkCorsEnabled() && + RuntimeEnabledFeatures::FetchUploadStreamingEnabled( + execution_context) && + V8ReadableStream::HasInstance(body, isolate)) { + ReadableStream* readable_stream = + V8ReadableStream::ToImpl(body.As<v8::Object>()); + return_buffer = + MakeGarbageCollected<BodyStreamBuffer>(script_state, readable_stream); } else { String string = NativeValueTraits<IDLUSVString>::NativeValue( isolate, body, exception_state); @@ -199,16 +207,13 @@ Request* Request::CreateRequestWithRequestOrString( // Setup RequestInit's body first // - "If |input| is a Request object and it is disturbed, throw a // TypeError." - if (input_request && - input_request->IsBodyUsed(exception_state) == BodyUsed::kUsed) { - DCHECK(!exception_state.HadException()); + if (input_request && input_request->IsBodyUsed()) { exception_state.ThrowTypeError( "Cannot construct a Request with a Request object that has already " "been used."); return nullptr; } - if (exception_state.HadException()) - return nullptr; + // - "Let |temporaryBody| be |input|'s request's body if |input| is a // Request object, and null otherwise." BodyStreamBuffer* temporary_body = @@ -543,8 +548,6 @@ Request* Request::CreateRequestWithRequestOrString( return nullptr; } - VLOG(1) << "a"; - if (params.type == TrustTokenOperationType::kIssuance && !IsTrustTokenIssuanceAvailableInExecutionContext(*execution_context)) { exception_state.ThrowTypeError( @@ -555,6 +558,10 @@ Request* Request::CreateRequestWithRequestOrString( request->SetTrustTokenParams(std::move(params)); } + if (init->hasAllowHTTP1ForStreamingUpload()) { + request->SetAllowHTTP1ForStreamingUpload( + init->allowHTTP1ForStreamingUpload()); + } // "Let |r| be a new Request object associated with |request| and a new // Headers object whose guard is "request"." @@ -645,6 +652,21 @@ Request* Request::CreateRequestWithRequestOrString( return nullptr; } + // If body is non-null and body’s source is null, then: + if (temporary_body && temporary_body->IsMadeFromReadableStream()) { + // If r’s request’s mode is neither "same-origin" nor "cors", then throw a + // TypeError. + if (request->Mode() != network::mojom::RequestMode::kSameOrigin && + request->Mode() != network::mojom::RequestMode::kCors) { + exception_state.ThrowTypeError( + "If request is made from ReadableStream, mode should be" + "\"same-origin\" or \"cors\""); + return nullptr; + } + // Set r’s request’s use-CORS-preflight flag. + request->SetMode(network::mojom::RequestMode::kCorsWithForcedPreflight); + } + // "Set |r|'s request's body to |temporaryBody|. if (temporary_body) r->request_->SetBuffer(temporary_body); @@ -717,10 +739,11 @@ Request* Request::Create(ScriptState* script_state, FetchRequestData* request) { Request* Request::Create( ScriptState* script_state, - const mojom::blink::FetchAPIRequest& fetch_api_request, + mojom::blink::FetchAPIRequestPtr fetch_api_request, ForServiceWorkerFetchEvent for_service_worker_fetch_event) { - FetchRequestData* data = FetchRequestData::Create( - script_state, fetch_api_request, for_service_worker_fetch_event); + FetchRequestData* data = + FetchRequestData::Create(script_state, std::move(fetch_api_request), + for_service_worker_fetch_event); return MakeGarbageCollected<Request>(script_state, data); } @@ -827,6 +850,7 @@ String Request::credentials() const { // mode:" switch (request_->Credentials()) { case network::mojom::CredentialsMode::kOmit: + case network::mojom::CredentialsMode::kOmitBug_775438_Workaround: return "omit"; case network::mojom::CredentialsMode::kSameOrigin: return "same-origin"; @@ -889,14 +913,10 @@ bool Request::isHistoryNavigation() const { Request* Request::clone(ScriptState* script_state, ExceptionState& exception_state) { - if (IsBodyLocked(exception_state) == BodyLocked::kLocked || - IsBodyUsed(exception_state) == BodyUsed::kUsed) { - DCHECK(!exception_state.HadException()); + if (IsBodyLocked() || IsBodyUsed()) { exception_state.ThrowTypeError("Request body is already used"); return nullptr; } - if (exception_state.HadException()) - return nullptr; FetchRequestData* request = request_->Clone(script_state, exception_state); if (exception_state.HadException()) @@ -911,7 +931,7 @@ Request* Request::clone(ScriptState* script_state, FetchRequestData* Request::PassRequestData(ScriptState* script_state, ExceptionState& exception_state) { - DCHECK(!IsBodyUsedForDCheck(exception_state)); + DCHECK(!IsBodyUsed()); FetchRequestData* data = request_->Pass(script_state, exception_state); if (exception_state.HadException()) return nullptr; @@ -994,7 +1014,7 @@ network::mojom::RequestDestination Request::GetRequestDestination() const { return request_->Destination(); } -void Request::Trace(Visitor* visitor) { +void Request::Trace(Visitor* visitor) const { ScriptWrappable::Trace(visitor); ActiveScriptWrappable<Request>::Trace(visitor); Body::Trace(visitor); diff --git a/chromium/third_party/blink/renderer/core/fetch/request.h b/chromium/third_party/blink/renderer/core/fetch/request.h index 23147aebd4f..6fa038826a5 100644 --- a/chromium/third_party/blink/renderer/core/fetch/request.h +++ b/chromium/third_party/blink/renderer/core/fetch/request.h @@ -59,7 +59,7 @@ class CORE_EXPORT Request final : public ScriptWrappable, ExceptionState&); static Request* Create(ScriptState*, FetchRequestData*); static Request* Create(ScriptState*, - const mojom::blink::FetchAPIRequest&, + mojom::blink::FetchAPIRequestPtr, ForServiceWorkerFetchEvent); Request(ScriptState*, FetchRequestData*, Headers*, AbortSignal*); @@ -103,7 +103,7 @@ class CORE_EXPORT Request final : public ScriptWrappable, mojom::RequestContextType GetRequestContextType() const; network::mojom::RequestDestination GetRequestDestination() const; - void Trace(Visitor*) override; + void Trace(Visitor*) const override; private: const FetchRequestData* GetRequest() const { return request_; } diff --git a/chromium/third_party/blink/renderer/core/fetch/request_init.idl b/chromium/third_party/blink/renderer/core/fetch/request_init.idl index 3f4f4ba2177..d59ac05c1c4 100644 --- a/chromium/third_party/blink/renderer/core/fetch/request_init.idl +++ b/chromium/third_party/blink/renderer/core/fetch/request_init.idl @@ -27,7 +27,7 @@ dictionary RequestInit { // contexts, this has to be enforced after the fact because the // SecureContext IDL attribute doesn't affect dictionary members. [RuntimeEnabled=TrustTokens] TrustToken trustToken; - + [RuntimeEnabled=FetchUploadStreaming] boolean allowHTTP1ForStreamingUpload; // TODO(domfarolino): add support for RequestInit window member. //any window; // can only be set to null }; diff --git a/chromium/third_party/blink/renderer/core/fetch/request_test.cc b/chromium/third_party/blink/renderer/core/fetch/request_test.cc index 6e05308c72c..0b294198d88 100644 --- a/chromium/third_party/blink/renderer/core/fetch/request_test.cc +++ b/chromium/third_party/blink/renderer/core/fetch/request_test.cc @@ -82,9 +82,10 @@ TEST(ServiceWorkerRequestTest, FromAndToFetchAPIRequest) { } fetch_api_request->referrer = mojom::blink::Referrer::New(KURL(NullURL(), referrer), kReferrerPolicy); + const auto fetch_api_request_headers = fetch_api_request->headers; Request* request = - Request::Create(scope.GetScriptState(), *fetch_api_request, + Request::Create(scope.GetScriptState(), std::move(fetch_api_request), Request::ForServiceWorkerFetchEvent::kFalse); DCHECK(request); EXPECT_EQ(url, request->url()); @@ -118,7 +119,7 @@ TEST(ServiceWorkerRequestTest, FromAndToFetchAPIRequest) { EXPECT_EQ(referrer, second_fetch_api_request->referrer->url); EXPECT_EQ(network::mojom::ReferrerPolicy::kAlways, second_fetch_api_request->referrer->policy); - EXPECT_EQ(fetch_api_request->headers, second_fetch_api_request->headers); + EXPECT_EQ(fetch_api_request_headers, second_fetch_api_request->headers); } TEST(ServiceWorkerRequestTest, ToFetchAPIRequestStripsURLFragment) { diff --git a/chromium/third_party/blink/renderer/core/fetch/response.cc b/chromium/third_party/blink/renderer/core/fetch/response.cc index f3bd7020c3f..619232c20d5 100644 --- a/chromium/third_party/blink/renderer/core/fetch/response.cc +++ b/chromium/third_party/blink/renderer/core/fetch/response.cc @@ -293,12 +293,9 @@ Response* Response::Create(ScriptState* script_state, // then IsStreamLocked and IsStreamDisturbed will always be false. // So we don't have to check BodyStreamBuffer is a ReadableStream // or not. - if (body->IsStreamLocked(exception_state).value_or(true) || - body->IsStreamDisturbed(exception_state).value_or(true)) { - if (!exception_state.HadException()) { - exception_state.ThrowTypeError( - "Response body object should not be disturbed or locked"); - } + if (body->IsStreamLocked() || body->IsStreamDisturbed()) { + exception_state.ThrowTypeError( + "Response body object should not be disturbed or locked"); return nullptr; } @@ -384,19 +381,24 @@ FetchResponseData* Response::CreateUnfilteredFetchResponseDataWithoutBody( response->SetResponseTime(fetch_api_response.response_time); response->SetCacheStorageCacheName( fetch_api_response.cache_storage_cache_name); + response->SetConnectionInfo(fetch_api_response.connection_info); + response->SetAlpnNegotiatedProtocol( + WTF::AtomicString(fetch_api_response.alpn_negotiated_protocol)); response->SetLoadedWithCredentials( fetch_api_response.loaded_with_credentials); + response->SetWasFetchedViaSpdy(fetch_api_response.was_fetched_via_spdy); for (const auto& header : fetch_api_response.headers) response->HeaderList()->Append(header.key, header.value); - // TODO(wanderview): This sets the mime type of the Response based on the - // current headers. This should be correct for most cases, but technically - // the mime type should really be frozen at the initial Response - // construction. We should plumb the value through the cache_storage - // persistence layer and include the explicit mime type in FetchAPIResponse - // to set here. See: crbug.com/938939 - response->SetMimeType(response->HeaderList()->ExtractMIMEType()); + // Use the |mime_type| provided by the FetchAPIResponse if its set. + // Otherwise fall back to extracting the mime type from the headers. This + // can happen when the response is loaded from an older cache_storage + // instance that did not yet store the mime_type value. + if (!fetch_api_response.mime_type.IsNull()) + response->SetMimeType(fetch_api_response.mime_type); + else + response->SetMimeType(response->HeaderList()->ExtractMIMEType()); return response; } @@ -469,16 +471,11 @@ Headers* Response::headers() const { Response* Response::clone(ScriptState* script_state, ExceptionState& exception_state) { - if (IsBodyLocked(exception_state) == BodyLocked::kLocked || - IsBodyUsed(exception_state) == BodyUsed::kUsed) { - DCHECK(!exception_state.HadException()); + if (IsBodyLocked() || IsBodyUsed()) { exception_state.ThrowTypeError("Response body is already used"); return nullptr; } - if (exception_state.HadException()) - return nullptr; - FetchResponseData* response = response_->Clone(script_state, exception_state); if (exception_state.HadException()) return nullptr; @@ -520,16 +517,9 @@ bool Response::HasBody() const { return response_->InternalBuffer(); } -Body::BodyUsed Response::IsBodyUsed(ExceptionState& exception_state) { +bool Response::IsBodyUsed() const { auto* body_buffer = InternalBodyBuffer(); - if (!body_buffer) - return BodyUsed::kUnused; - base::Optional<bool> stream_disturbed = - body_buffer->IsStreamDisturbed(exception_state); - if (exception_state.HadException()) - return BodyUsed::kBroken; - DCHECK(stream_disturbed.has_value()); - return stream_disturbed.value() ? BodyUsed::kUsed : BodyUsed::kUnused; + return body_buffer && body_buffer->IsStreamDisturbed(); } String Response::MimeType() const { @@ -554,7 +544,7 @@ FetchHeaderList* Response::InternalHeaderList() const { return response_->InternalHeaderList(); } -void Response::Trace(Visitor* visitor) { +void Response::Trace(Visitor* visitor) const { ScriptWrappable::Trace(visitor); ActiveScriptWrappable<Response>::Trace(visitor); Body::Trace(visitor); @@ -562,9 +552,4 @@ void Response::Trace(Visitor* visitor) { visitor->Trace(headers_); } -bool Response::IsBodyUsedForDCheck(ExceptionState& exception_state) { - return InternalBodyBuffer() && - InternalBodyBuffer()->IsStreamDisturbedForDCheck(exception_state); -} - } // namespace blink diff --git a/chromium/third_party/blink/renderer/core/fetch/response.h b/chromium/third_party/blink/renderer/core/fetch/response.h index 9d0987f51a7..2899fe168e8 100644 --- a/chromium/third_party/blink/renderer/core/fetch/response.h +++ b/chromium/third_party/blink/renderer/core/fetch/response.h @@ -114,7 +114,7 @@ class CORE_EXPORT Response final : public ScriptWrappable, return response_->InternalBuffer(); } - BodyUsed IsBodyUsed(ExceptionState&) override; + bool IsBodyUsed() const override; String ContentType() const override; String MimeType() const override; @@ -124,12 +124,7 @@ class CORE_EXPORT Response final : public ScriptWrappable, FetchHeaderList* InternalHeaderList() const; - void Trace(Visitor*) override; - - protected: - // A version of IsBodyUsed() which catches exceptions and returns - // false. Should never be used outside DCHECK(). - bool IsBodyUsedForDCheck(ExceptionState&) override; + void Trace(Visitor*) const override; private: const Member<FetchResponseData> response_; diff --git a/chromium/third_party/blink/renderer/core/fetch/trust_token.idl b/chromium/third_party/blink/renderer/core/fetch/trust_token.idl index a2b595ec9ac..e4014edc3c7 100644 --- a/chromium/third_party/blink/renderer/core/fetch/trust_token.idl +++ b/chromium/third_party/blink/renderer/core/fetch/trust_token.idl @@ -25,10 +25,12 @@ dictionary TrustToken { // 2. |additionalSignedHeaders| // 3. |includeTimestampHeader| // 4. |signRequestData| + // 5. |additionalSigningData| // // Additionally, |issuer| is required when |type| is "send-srr". USVString issuer; sequence<USVString> additionalSignedHeaders; boolean includeTimestampHeader = false; SignRequestData signRequestData = "omit"; + DOMString additionalSigningData; }; diff --git a/chromium/third_party/blink/renderer/core/fetch/trust_token_to_mojom.cc b/chromium/third_party/blink/renderer/core/fetch/trust_token_to_mojom.cc index c0064cafe44..d7af058e35d 100644 --- a/chromium/third_party/blink/renderer/core/fetch/trust_token_to_mojom.cc +++ b/chromium/third_party/blink/renderer/core/fetch/trust_token_to_mojom.cc @@ -85,6 +85,9 @@ bool ConvertTrustTokenToMojom(const TrustToken& in, return false; } + if (in.hasAdditionalSigningData()) + out->possibly_unsafe_additional_signing_data = in.additionalSigningData(); + return true; } |