-
-
Notifications
You must be signed in to change notification settings - Fork 991
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
LibWeb: Implement Web::Fetch::Body::fully_read() closer to spec
By actually using streams, they get marked as disturbed and the `.bodyUsed` API starts to work. Fixes at least 94 subtests in the WPT `fetch/api/request` test suite.
- Loading branch information
Showing
1 changed file
with
24 additions
and
24 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,18 +1,19 @@ | ||
/* | ||
* Copyright (c) 2022-2023, Linus Groh <[email protected]> | ||
* Copyright (c) 2024, Jelle Raaijmakers <[email protected]> | ||
* | ||
* SPDX-License-Identifier: BSD-2-Clause | ||
*/ | ||
|
||
#include <LibJS/Runtime/PromiseCapability.h> | ||
#include <LibWeb/Bindings/ExceptionOrUtils.h> | ||
#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> | ||
#include <LibWeb/WebIDL/Promise.h> | ||
|
||
namespace Web::Fetch::Infrastructure { | ||
|
||
|
@@ -70,38 +71,37 @@ void Body::fully_read(JS::Realm& realm, Web::Fetch::Infrastructure::Body::Proces | |
VERIFY(!task_destination.has<Empty>()); | ||
auto task_destination_object = task_destination.get<JS::NonnullGCPtr<JS::Object>>(); | ||
|
||
// 2. Let successSteps given a byte sequence bytes be to queue a fetch task to run processBody given bytes, with taskDestination. | ||
auto success_steps = [&realm, process_body, task_destination_object = task_destination_object](ReadonlyBytes bytes) -> ErrorOr<void> { | ||
// Make a copy of the bytes, as the source of the bytes may disappear between the time the task is queued and executed. | ||
auto bytes_copy = TRY(ByteBuffer::copy(bytes)); | ||
queue_fetch_task(*task_destination_object, JS::create_heap_function(realm.heap(), [process_body, bytes_copy = move(bytes_copy)]() mutable { | ||
process_body->function()(move(bytes_copy)); | ||
// 2. Let successSteps given a byte sequence bytes be to queue a fetch task to run processBody | ||
// given bytes, with taskDestination. | ||
auto success_steps = [&realm, process_body, task_destination_object = task_destination_object](ByteBuffer bytes) { | ||
queue_fetch_task(*task_destination_object, JS::create_heap_function(realm.heap(), [process_body, bytes]() mutable { | ||
process_body->function()(move(bytes)); | ||
})); | ||
return {}; | ||
}; | ||
|
||
// 3. Let errorSteps optionally given an exception exception be to queue a fetch task to run processBodyError given exception, with taskDestination. | ||
auto error_steps = [&realm, process_body_error, task_destination_object](JS::GCPtr<WebIDL::DOMException> exception) { | ||
// 3. Let errorSteps optionally given an exception exception be to queue a fetch task to run | ||
// processBodyError given exception, with taskDestination. | ||
auto error_steps = [&realm, process_body_error, task_destination_object](JS::Value exception) { | ||
queue_fetch_task(*task_destination_object, JS::create_heap_function(realm.heap(), [process_body_error, exception]() { | ||
process_body_error->function()(exception); | ||
})); | ||
}; | ||
|
||
// 4. Let reader be the result of getting a reader for body’s stream. If that threw an exception, then run errorSteps with that exception and return. | ||
// 4. Let reader be the result of getting a reader for body’s stream. If that threw an | ||
// exception, then run errorSteps with that exception and return. | ||
HTML::TemporaryExecutionContext execution_context { realm, HTML::TemporaryExecutionContext::CallbacksEnabled::Yes }; | ||
auto reader_or_exception = Streams::acquire_readable_stream_default_reader(*m_stream); | ||
if (reader_or_exception.is_exception()) { | ||
auto throw_completion = Bindings::dom_exception_to_throw_completion(realm.vm(), reader_or_exception.release_error()); | ||
error_steps(throw_completion.release_value().value()); | ||
return; | ||
} | ||
auto reader = reader_or_exception.release_value(); | ||
|
||
// 5. Read all bytes from reader, given successSteps and errorSteps. | ||
// FIXME: Use streams for these steps. | ||
m_source.visit( | ||
[&](ByteBuffer const& byte_buffer) { | ||
if (auto result = success_steps(byte_buffer); result.is_error()) | ||
error_steps(WebIDL::UnknownError::create(realm, "Out-of-memory"_string)); | ||
}, | ||
[&](JS::Handle<FileAPI::Blob> const& blob) { | ||
if (auto result = success_steps(blob->raw_bytes()); result.is_error()) | ||
error_steps(WebIDL::UnknownError::create(realm, "Out-of-memory"_string)); | ||
}, | ||
[&](Empty) { | ||
error_steps(WebIDL::DOMException::create(realm, "DOMException"_fly_string, "Reading from Blob, FormData or null source is not yet implemented"_string)); | ||
}); | ||
reader->read_all_bytes( | ||
JS::create_heap_function(realm.heap(), move(success_steps)), | ||
JS::create_heap_function(realm.heap(), move(error_steps))); | ||
} | ||
|
||
// https://fetch.spec.whatwg.org/#body-incrementally-read | ||
|