mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-11-25 09:00:22 +00:00
LibWeb: Implement the concept incrementally read a body
This commit is contained in:
parent
34ecc59508
commit
76418f3ffa
Notes:
sideshowbarker
2024-07-18 00:41:35 +09:00
Author: https://github.com/kennethmyhra Commit: https://github.com/SerenityOS/serenity/commit/76418f3ffa Pull-request: https://github.com/SerenityOS/serenity/pull/24132 Reviewed-by: https://github.com/shannonbooth Reviewed-by: https://github.com/trflynn89
6 changed files with 161 additions and 0 deletions
|
@ -221,6 +221,7 @@ set(SOURCES
|
|||
Fetch/Infrastructure/HTTP/Requests.cpp
|
||||
Fetch/Infrastructure/HTTP/Responses.cpp
|
||||
Fetch/Infrastructure/HTTP/Statuses.cpp
|
||||
Fetch/Infrastructure/IncrementalReadLoopReadRequest.cpp
|
||||
Fetch/Infrastructure/MimeTypeBlocking.cpp
|
||||
Fetch/Infrastructure/NoSniffBlocking.cpp
|
||||
Fetch/Infrastructure/PortBlocking.cpp
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
#include <LibWeb/Bindings/MainThreadVM.h>
|
||||
#include <LibWeb/Fetch/BodyInit.h>
|
||||
#include <LibWeb/Fetch/Infrastructure/HTTP/Bodies.h>
|
||||
#include <LibWeb/Fetch/Infrastructure/IncrementalReadLoopReadRequest.h>
|
||||
#include <LibWeb/Fetch/Infrastructure/Task.h>
|
||||
#include <LibWeb/HTML/Scripting/TemporaryExecutionContext.h>
|
||||
#include <LibWeb/Streams/AbstractOperations.h>
|
||||
|
@ -103,6 +104,35 @@ void Body::fully_read(JS::Realm& realm, Web::Fetch::Infrastructure::Body::Proces
|
|||
});
|
||||
}
|
||||
|
||||
// https://fetch.spec.whatwg.org/#body-incrementally-read
|
||||
void Body::incrementally_read(ProcessBodyChunkCallback process_body_chunk, ProcessEndOfBodyCallback process_end_of_body, ProcessBodyErrorCallback process_body_error, TaskDestination task_destination)
|
||||
{
|
||||
HTML::TemporaryExecutionContext const execution_context { Bindings::host_defined_environment_settings_object(m_stream->realm()), HTML::TemporaryExecutionContext::CallbacksEnabled::Yes };
|
||||
|
||||
VERIFY(task_destination.has<JS::NonnullGCPtr<JS::Object>>());
|
||||
// FIXME: 1. If taskDestination is null, then set taskDestination to the result of starting a new parallel queue.
|
||||
// FIXME: Handle 'parallel queue' task destination
|
||||
|
||||
// 2. Let reader be the result of getting a reader for body’s stream.
|
||||
// NOTE: This operation will not throw an exception.
|
||||
auto reader = MUST(Streams::acquire_readable_stream_default_reader(m_stream));
|
||||
|
||||
// 3. Perform the incrementally-read loop given reader, taskDestination, processBodyChunk, processEndOfBody, and processBodyError.
|
||||
incrementally_read_loop(reader, task_destination.get<JS::NonnullGCPtr<JS::Object>>(), process_body_chunk, process_end_of_body, process_body_error);
|
||||
}
|
||||
|
||||
// https://fetch.spec.whatwg.org/#incrementally-read-loop
|
||||
void Body::incrementally_read_loop(Streams::ReadableStreamDefaultReader& reader, JS::NonnullGCPtr<JS::Object> task_destination, ProcessBodyChunkCallback process_body_chunk, ProcessEndOfBodyCallback process_end_of_body, ProcessBodyErrorCallback process_body_error)
|
||||
|
||||
{
|
||||
auto& realm = reader.realm();
|
||||
// 1. Let readRequest be the following read request:
|
||||
auto read_request = realm.heap().allocate<IncrementalReadLoopReadRequest>(realm, *this, reader, task_destination, process_body_chunk, process_end_of_body, process_body_error);
|
||||
|
||||
// 2. Read a chunk from reader given readRequest.
|
||||
reader.read_a_chunk(read_request);
|
||||
}
|
||||
|
||||
// https://fetch.spec.whatwg.org/#byte-sequence-as-a-body
|
||||
WebIDL::ExceptionOr<JS::NonnullGCPtr<Body>> byte_sequence_as_body(JS::Realm& realm, ReadonlyBytes bytes)
|
||||
{
|
||||
|
|
|
@ -31,6 +31,10 @@ public:
|
|||
using ProcessBodyCallback = JS::NonnullGCPtr<JS::HeapFunction<void(ByteBuffer)>>;
|
||||
// processBodyError must be an algorithm optionally accepting an exception.
|
||||
using ProcessBodyErrorCallback = JS::NonnullGCPtr<JS::HeapFunction<void(JS::Value)>>;
|
||||
// processBodyChunk must be an algorithm accepting a byte sequence.
|
||||
using ProcessBodyChunkCallback = JS::NonnullGCPtr<JS::HeapFunction<void(ByteBuffer)>>;
|
||||
// processEndOfBody must be an algorithm accepting no arguments
|
||||
using ProcessEndOfBodyCallback = JS::NonnullGCPtr<JS::HeapFunction<void()>>;
|
||||
|
||||
[[nodiscard]] static JS::NonnullGCPtr<Body> create(JS::VM&, JS::NonnullGCPtr<Streams::ReadableStream>);
|
||||
[[nodiscard]] static JS::NonnullGCPtr<Body> create(JS::VM&, JS::NonnullGCPtr<Streams::ReadableStream>, SourceType, Optional<u64>);
|
||||
|
@ -43,6 +47,8 @@ public:
|
|||
[[nodiscard]] JS::NonnullGCPtr<Body> clone(JS::Realm&);
|
||||
|
||||
void fully_read(JS::Realm&, ProcessBodyCallback process_body, ProcessBodyErrorCallback process_body_error, TaskDestination task_destination) const;
|
||||
void incrementally_read(ProcessBodyChunkCallback process_body_chunk, ProcessEndOfBodyCallback process_end_of_body, ProcessBodyErrorCallback process_body_error, TaskDestination task_destination);
|
||||
void incrementally_read_loop(Streams::ReadableStreamDefaultReader& reader, JS::NonnullGCPtr<JS::Object> task_destination, ProcessBodyChunkCallback process_body_chunk, ProcessEndOfBodyCallback process_end_of_body, ProcessBodyErrorCallback process_body_error);
|
||||
|
||||
virtual void visit_edges(JS::Cell::Visitor&) override;
|
||||
|
||||
|
|
|
@ -0,0 +1,86 @@
|
|||
/*
|
||||
* Copyright (c) 2024, Kenneth Myhra <kennethmyhra@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <LibJS/Runtime/TypedArray.h>
|
||||
#include <LibWeb/Bindings/HostDefined.h>
|
||||
#include <LibWeb/Fetch/Infrastructure/IncrementalReadLoopReadRequest.h>
|
||||
#include <LibWeb/HTML/Scripting/TemporaryExecutionContext.h>
|
||||
|
||||
namespace Web::Fetch::Infrastructure {
|
||||
|
||||
JS_DEFINE_ALLOCATOR(IncrementalReadLoopReadRequest);
|
||||
|
||||
void IncrementalReadLoopReadRequest::on_chunk(JS::Value chunk)
|
||||
{
|
||||
auto& realm = m_reader->realm();
|
||||
// 1. Let continueAlgorithm be null.
|
||||
JS::GCPtr<JS::HeapFunction<void()>> continue_algorithm;
|
||||
|
||||
// 2. If chunk is not a Uint8Array object, then set continueAlgorithm to this step: run processBodyError given a TypeError.
|
||||
if (!chunk.is_object() || !is<JS::Uint8Array>(chunk.as_object())) {
|
||||
continue_algorithm = JS::create_heap_function(realm.heap(), [&realm, process_body_error = m_process_body_error] {
|
||||
process_body_error->function()(JS::TypeError::create(realm, "Chunk data is not Uint8Array"sv));
|
||||
});
|
||||
}
|
||||
// 3. Otherwise:
|
||||
else {
|
||||
// 1. Let bytes be a copy of chunk.
|
||||
// NOTE: Implementations are strongly encouraged to use an implementation strategy that avoids this copy where possible.
|
||||
auto& uint8_array = static_cast<JS::Uint8Array&>(chunk.as_object());
|
||||
auto bytes = MUST(ByteBuffer::copy(uint8_array.data()));
|
||||
// 2. Set continueAlgorithm to these steps:
|
||||
continue_algorithm = JS::create_heap_function(realm.heap(), [bytes = move(bytes), body = m_body, reader = m_reader, task_destination = m_task_destination, process_body_chunk = m_process_body_chunk, process_end_of_body = m_process_end_of_body, process_body_error = m_process_body_error] {
|
||||
HTML::TemporaryExecutionContext execution_context { Bindings::host_defined_environment_settings_object(reader->realm()), HTML::TemporaryExecutionContext::CallbacksEnabled::Yes };
|
||||
// 1. Run processBodyChunk given bytes.
|
||||
process_body_chunk->function()(move(bytes));
|
||||
|
||||
// 2. Perform the incrementally-read loop given reader, taskDestination, processBodyChunk, processEndOfBody, and processBodyError.
|
||||
body->incrementally_read_loop(reader, task_destination, process_body_chunk, process_end_of_body, process_body_error);
|
||||
});
|
||||
}
|
||||
|
||||
// 4. Queue a fetch task given continueAlgorithm and taskDestination.
|
||||
Fetch::Infrastructure::queue_fetch_task(m_task_destination, *continue_algorithm);
|
||||
}
|
||||
|
||||
void IncrementalReadLoopReadRequest::on_close()
|
||||
{
|
||||
// 1. Queue a fetch task given processEndOfBody and taskDestination.
|
||||
Fetch::Infrastructure::queue_fetch_task(m_task_destination, JS::create_heap_function(m_reader->heap(), [this] {
|
||||
m_process_end_of_body->function()();
|
||||
}));
|
||||
}
|
||||
|
||||
void IncrementalReadLoopReadRequest::on_error(JS::Value error)
|
||||
{
|
||||
// 1. Queue a fetch task to run processBodyError given e, with taskDestination.
|
||||
Fetch::Infrastructure::queue_fetch_task(m_task_destination, JS::create_heap_function(m_reader->heap(), [this, error = move(error)] {
|
||||
m_process_body_error->function()(error);
|
||||
}));
|
||||
}
|
||||
|
||||
IncrementalReadLoopReadRequest::IncrementalReadLoopReadRequest(JS::NonnullGCPtr<Body> body, JS::NonnullGCPtr<Streams::ReadableStreamDefaultReader> reader, JS::NonnullGCPtr<JS::Object> task_destination, Body::ProcessBodyChunkCallback process_body_chunk, Body::ProcessEndOfBodyCallback process_end_of_body, Body::ProcessBodyErrorCallback process_body_error)
|
||||
: m_body(body)
|
||||
, m_reader(reader)
|
||||
, m_task_destination(task_destination)
|
||||
, m_process_body_chunk(process_body_chunk)
|
||||
, m_process_end_of_body(process_end_of_body)
|
||||
, m_process_body_error(process_body_error)
|
||||
{
|
||||
}
|
||||
|
||||
void IncrementalReadLoopReadRequest::visit_edges(Visitor& visitor)
|
||||
{
|
||||
Base::visit_edges(visitor);
|
||||
visitor.visit(m_body);
|
||||
visitor.visit(m_reader);
|
||||
visitor.visit(m_task_destination);
|
||||
visitor.visit(m_process_body_chunk);
|
||||
visitor.visit(m_process_end_of_body);
|
||||
visitor.visit(m_process_body_error);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
/*
|
||||
* Copyright (c) 2024, Kenneth Myhra <kennethmyhra@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <LibWeb/Fetch/Infrastructure/HTTP/Bodies.h>
|
||||
#include <LibWeb/Streams/ReadableStreamDefaultReader.h>
|
||||
|
||||
namespace Web::Fetch::Infrastructure {
|
||||
|
||||
// https://fetch.spec.whatwg.org/#incrementally-read-loop
|
||||
class IncrementalReadLoopReadRequest : public Streams::ReadRequest {
|
||||
JS_CELL(IncrementalReadLoopReadRequest, JS::Cell);
|
||||
JS_DECLARE_ALLOCATOR(IncrementalReadLoopReadRequest);
|
||||
|
||||
public:
|
||||
IncrementalReadLoopReadRequest(JS::NonnullGCPtr<Body>, JS::NonnullGCPtr<Streams::ReadableStreamDefaultReader>, JS::NonnullGCPtr<JS::Object> task_destination, Body::ProcessBodyChunkCallback, Body::ProcessEndOfBodyCallback, Body::ProcessBodyErrorCallback);
|
||||
|
||||
virtual void on_chunk(JS::Value chunk) override;
|
||||
virtual void on_close() override;
|
||||
virtual void on_error(JS::Value error) override;
|
||||
|
||||
private:
|
||||
virtual void visit_edges(Visitor&) override;
|
||||
|
||||
JS::NonnullGCPtr<Body> m_body;
|
||||
JS::NonnullGCPtr<Streams::ReadableStreamDefaultReader> m_reader;
|
||||
JS::NonnullGCPtr<JS::Object> m_task_destination;
|
||||
Body::ProcessBodyChunkCallback m_process_body_chunk;
|
||||
Body::ProcessEndOfBodyCallback m_process_end_of_body;
|
||||
Body::ProcessBodyErrorCallback m_process_body_error;
|
||||
};
|
||||
|
||||
}
|
|
@ -305,6 +305,7 @@ class FetchController;
|
|||
class FetchParams;
|
||||
class FetchTimingInfo;
|
||||
class HeaderList;
|
||||
class IncrementalReadLoopReadRequest;
|
||||
class Request;
|
||||
class Response;
|
||||
|
||||
|
|
Loading…
Reference in a new issue