LibWeb: Make Fetch::Infrastructure::Body be GC allocated

Making the body GC-allocated allows us to avoid using `JS::Handle`
for `m_stream` in its members.
This commit is contained in:
Aliaksandr Kalenik 2023-08-18 19:38:13 +02:00 committed by Andreas Kling
parent 953c19bdb7
commit bdd3a16b16
Notes: sideshowbarker 2024-07-17 20:19:08 +09:00
21 changed files with 117 additions and 91 deletions

View file

@ -233,7 +233,7 @@ JS::GCPtr<DOM::Document> load_document(Optional<HTML::NavigationParams> navigati
auto& realm = document->realm();
if (navigation_params->response->body().has_value()) {
if (navigation_params->response->body()) {
auto process_body = [navigation_params, document](ByteBuffer bytes) {
if (!parse_document(*document, bytes)) {
// FIXME: Load html page with an error if parsing failed.

View file

@ -31,7 +31,7 @@ bool BodyMixin::is_unusable() const
{
// An object including the Body interface mixin is said to be unusable if its body is non-null and its bodys stream is disturbed or locked.
auto const& body = body_impl();
return body.has_value() && (body->stream()->is_disturbed() || body->stream()->is_locked());
return body && (body->stream()->is_disturbed() || body->stream()->is_locked());
}
// https://fetch.spec.whatwg.org/#dom-body-body
@ -39,7 +39,7 @@ JS::GCPtr<Streams::ReadableStream> BodyMixin::body() const
{
// The body getter steps are to return null if thiss body is null; otherwise thiss bodys stream.
auto const& body = body_impl();
return body.has_value() ? body->stream().ptr() : nullptr;
return body ? body->stream().ptr() : nullptr;
}
// https://fetch.spec.whatwg.org/#dom-body-bodyused
@ -47,7 +47,7 @@ bool BodyMixin::body_used() const
{
// The bodyUsed getter steps are to return true if thiss body is non-null and thiss bodys stream is disturbed; otherwise false.
auto const& body = body_impl();
return body.has_value() && body->stream()->is_disturbed();
return body && body->stream()->is_disturbed();
}
// https://fetch.spec.whatwg.org/#dom-body-arraybuffer
@ -197,7 +197,7 @@ WebIDL::ExceptionOr<JS::NonnullGCPtr<JS::Promise>> consume_body(JS::Realm& realm
// 5. If objects body is null, then run successSteps with an empty byte sequence.
auto const& body = object.body_impl();
if (!body.has_value()) {
if (!body) {
success_steps(ByteBuffer {});
}
// 6. Otherwise, fully read objects body given successSteps, errorSteps, and objects relevant global object.

View file

@ -27,8 +27,8 @@ public:
virtual ~BodyMixin();
virtual ErrorOr<Optional<MimeSniff::MimeType>> mime_type_impl() const = 0;
virtual Optional<Infrastructure::Body&> body_impl() = 0;
virtual Optional<Infrastructure::Body const&> body_impl() const = 0;
virtual JS::GCPtr<Infrastructure::Body> body_impl() = 0;
virtual JS::GCPtr<Infrastructure::Body const> body_impl() const = 0;
virtual Bindings::PlatformObject& as_platform_object() = 0;
virtual Bindings::PlatformObject const& as_platform_object() const = 0;

View file

@ -136,7 +136,7 @@ WebIDL::ExceptionOr<Infrastructure::BodyWithType> extract_body(JS::Realm& realm,
// FIXME: 12. If action is non-null, then run these steps in parallel:
// 13. Let body be a body whose stream is stream, source is source, and length is length.
auto body = Infrastructure::Body { JS::make_handle(*stream), move(source), move(length) };
auto body = Infrastructure::Body::create(vm, *stream, move(source), move(length));
// 14. Return (body, type).
return Infrastructure::BodyWithType { .body = move(body), .type = move(type) };

View file

@ -165,7 +165,7 @@ void abort_fetch(JS::Realm& realm, WebIDL::Promise const& promise, JS::NonnullGC
WebIDL::reject_promise(realm, promise, error);
// 2. If requests body is non-null and is readable, then cancel requests body with error.
if (auto* body = request->body().get_pointer<Infrastructure::Body>(); body != nullptr && body->stream()->is_readable()) {
if (auto* body = request->body().get_pointer<JS::NonnullGCPtr<Infrastructure::Body>>(); body != nullptr && (*body)->stream()->is_readable()) {
// TODO: Implement cancelling streams
(void)error;
}
@ -178,7 +178,7 @@ void abort_fetch(JS::Realm& realm, WebIDL::Promise const& promise, JS::NonnullGC
auto response = response_object->response();
// 5. If responses body is non-null and is readable, then error responses body with error.
if (response->body().has_value()) {
if (response->body()) {
auto stream = response->body()->stream();
if (stream->is_readable()) {
// TODO: Implement erroring streams

View file

@ -498,7 +498,7 @@ WebIDL::ExceptionOr<Optional<JS::NonnullGCPtr<PendingResponse>>> main_fetch(JS::
};
// 2. If responses body is null, then run processBodyError and abort these steps.
if (!response->body().has_value()) {
if (!response->body()) {
process_body_error({});
return;
}
@ -644,7 +644,7 @@ WebIDL::ExceptionOr<void> fetch_response_handover(JS::Realm& realm, Infrastructu
auto internal_response = response.is_network_error() ? JS::NonnullGCPtr { response } : response.unsafe_response();
// 6. If internalResponses body is null, then run processResponseEndOfBody.
if (!internal_response->body().has_value()) {
if (!internal_response->body()) {
process_response_end_of_body();
}
// 7. Otherwise:
@ -672,7 +672,7 @@ WebIDL::ExceptionOr<void> fetch_response_handover(JS::Realm& realm, Infrastructu
// 3. If internalResponse's body is null, then queue a fetch task to run processBody given null, with
// fetchParamss task destination.
if (!internal_response->body().has_value()) {
if (!internal_response->body()) {
Infrastructure::queue_fetch_task(task_destination, [process_body = move(process_body)]() {
process_body({});
});
@ -1118,7 +1118,7 @@ WebIDL::ExceptionOr<Optional<JS::NonnullGCPtr<PendingResponse>>> http_redirect_f
// return a network error.
if (actual_response->status() != 303
&& !request->body().has<Empty>()
&& request->body().get<Infrastructure::Body>().source().has<Empty>()) {
&& request->body().get<JS::NonnullGCPtr<Infrastructure::Body>>()->source().has<Empty>()) {
return PendingResponse::create(vm, request, Infrastructure::Response::network_error(vm, "Request has body but no body source"sv));
}
@ -1160,7 +1160,7 @@ WebIDL::ExceptionOr<Optional<JS::NonnullGCPtr<PendingResponse>>> http_redirect_f
// requests bodys source.
// NOTE: requests bodys sources nullity has already been checked.
if (!request->body().has<Empty>()) {
auto const& source = request->body().get<Infrastructure::Body>().source();
auto const& source = request->body().get<JS::NonnullGCPtr<Infrastructure::Body>>()->source();
// NOTE: BodyInitOrReadableBytes is a superset of Body::SourceType
auto converted_source = source.has<ByteBuffer>()
? BodyInitOrReadableBytes { source.get<ByteBuffer>() }
@ -1292,8 +1292,8 @@ WebIDL::ExceptionOr<JS::NonnullGCPtr<PendingResponse>> http_network_or_cache_fet
include_credentials = IncludeCredentials::No;
// 5. Let contentLength be httpRequests bodys length, if httpRequests body is non-null; otherwise null.
auto content_length = http_request->body().has<Infrastructure::Body>()
? http_request->body().get<Infrastructure::Body>().length()
auto content_length = http_request->body().has<JS::NonnullGCPtr<Infrastructure::Body>>()
? http_request->body().get<JS::NonnullGCPtr<Infrastructure::Body>>()->length()
: Optional<u64> {};
// 6. Let contentLengthHeaderValue be null.
@ -1580,13 +1580,13 @@ WebIDL::ExceptionOr<JS::NonnullGCPtr<PendingResponse>> http_network_or_cache_fet
// 2. If requests body is non-null, then:
if (!request->body().has<Empty>()) {
// 1. If requests bodys source is null, then return a network error.
if (request->body().get<Infrastructure::Body>().source().has<Empty>()) {
if (request->body().get<JS::NonnullGCPtr<Infrastructure::Body>>()->source().has<Empty>()) {
returned_pending_response->resolve(Infrastructure::Response::network_error(vm, "Request has body but no body source"_string));
return;
}
// 2. Set requests body to the body of the result of safely extracting requests bodys source.
auto const& source = request->body().get<Infrastructure::Body>().source();
auto const& source = request->body().get<JS::NonnullGCPtr<Infrastructure::Body>>()->source();
// NOTE: BodyInitOrReadableBytes is a superset of Body::SourceType
auto converted_source = source.has<ByteBuffer>()
? BodyInitOrReadableBytes { source.get<ByteBuffer>() }
@ -1657,7 +1657,7 @@ WebIDL::ExceptionOr<JS::NonnullGCPtr<PendingResponse>> http_network_or_cache_fet
// - isNewConnectionFetch is false
&& is_new_connection_fetch == IsNewConnectionFetch::No
// - requests body is null, or requests body is non-null and requests bodys source is non-null
&& (request->body().has<Empty>() || !request->body().get<Infrastructure::Body>().source().has<Empty>())
&& (request->body().has<Empty>() || !request->body().get<JS::NonnullGCPtr<Infrastructure::Body>>()->source().has<Empty>())
// then:
) {
// 1. If fetchParams is canceled, then return the appropriate network error for fetchParams.
@ -1738,8 +1738,8 @@ WebIDL::ExceptionOr<JS::NonnullGCPtr<PendingResponse>> nonstandard_resource_load
load_request.set_method(DeprecatedString::copy(request->method()));
for (auto const& header : *request->header_list())
load_request.set_header(DeprecatedString::copy(header.name), DeprecatedString::copy(header.value));
if (auto const* body = request->body().get_pointer<Infrastructure::Body>()) {
TRY(body->source().visit(
if (auto const* body = request->body().get_pointer<JS::NonnullGCPtr<Infrastructure::Body>>()) {
TRY((*body)->source().visit(
[&](ByteBuffer const& byte_buffer) -> WebIDL::ExceptionOr<void> {
load_request.set_body(TRY_OR_THROW_OOM(vm, ByteBuffer::copy(byte_buffer)));
return {};

View file

@ -13,20 +13,36 @@
namespace Web::Fetch::Infrastructure {
Body::Body(JS::Handle<Streams::ReadableStream> stream)
JS::NonnullGCPtr<Body> Body::create(JS::VM& vm, JS::NonnullGCPtr<Streams::ReadableStream> stream)
{
return vm.heap().allocate_without_realm<Body>(stream);
}
JS::NonnullGCPtr<Body> Body::create(JS::VM& vm, JS::NonnullGCPtr<Streams::ReadableStream> stream, SourceType source, Optional<u64> length)
{
return vm.heap().allocate_without_realm<Body>(stream, source, length);
}
Body::Body(JS::NonnullGCPtr<Streams::ReadableStream> stream)
: m_stream(move(stream))
{
}
Body::Body(JS::Handle<Streams::ReadableStream> stream, SourceType source, Optional<u64> length)
Body::Body(JS::NonnullGCPtr<Streams::ReadableStream> stream, SourceType source, Optional<u64> length)
: m_stream(move(stream))
, m_source(move(source))
, m_length(move(length))
{
}
void Body::visit_edges(Cell::Visitor& visitor)
{
Base::visit_edges(visitor);
visitor.visit(m_stream);
}
// https://fetch.spec.whatwg.org/#concept-body-clone
Body Body::clone(JS::Realm& realm) const
JS::NonnullGCPtr<Body> Body::clone(JS::Realm& realm) const
{
// To clone a body body, run these steps:
// FIXME: 1. Let « out1, out2 » be the result of teeing bodys stream.
@ -34,7 +50,7 @@ Body Body::clone(JS::Realm& realm) const
auto out2 = realm.heap().allocate<Streams::ReadableStream>(realm, realm);
// 3. Return a body whose stream is out2 and other members are copied from body.
return Body { JS::make_handle(out2), m_source, m_length };
return Body::create(realm.vm(), out2, m_source, m_length);
}
// https://fetch.spec.whatwg.org/#body-fully-read
@ -80,7 +96,7 @@ WebIDL::ExceptionOr<void> Body::fully_read(JS::Realm& realm, Web::Fetch::Infrast
}
// https://fetch.spec.whatwg.org/#byte-sequence-as-a-body
WebIDL::ExceptionOr<Body> byte_sequence_as_body(JS::Realm& realm, ReadonlyBytes bytes)
WebIDL::ExceptionOr<JS::NonnullGCPtr<Body>> byte_sequence_as_body(JS::Realm& realm, ReadonlyBytes bytes)
{
// To get a byte sequence bytes as a body, return the body of the result of safely extracting bytes.
auto [body, _] = TRY(safely_extract_body(realm, bytes));

View file

@ -21,7 +21,9 @@
namespace Web::Fetch::Infrastructure {
// https://fetch.spec.whatwg.org/#concept-body
class Body final {
class Body final : public JS::Cell {
JS_CELL(Body, JS::Cell);
public:
using SourceType = Variant<Empty, ByteBuffer, JS::Handle<FileAPI::Blob>>;
// processBody must be an algorithm accepting a byte sequence.
@ -29,21 +31,26 @@ public:
// processBodyError must be an algorithm optionally accepting an exception.
using ProcessBodyErrorCallback = JS::SafeFunction<void(JS::GCPtr<WebIDL::DOMException>)>;
explicit Body(JS::Handle<Streams::ReadableStream>);
Body(JS::Handle<Streams::ReadableStream>, SourceType, Optional<u64>);
[[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>);
[[nodiscard]] JS::NonnullGCPtr<Streams::ReadableStream> stream() const { return *m_stream; }
[[nodiscard]] SourceType const& source() const { return m_source; }
[[nodiscard]] Optional<u64> const& length() const { return m_length; }
[[nodiscard]] Body clone(JS::Realm&) const;
[[nodiscard]] JS::NonnullGCPtr<Body> clone(JS::Realm&) const;
WebIDL::ExceptionOr<void> fully_read(JS::Realm&, ProcessBodyCallback process_body, ProcessBodyErrorCallback process_body_error, TaskDestination task_destination) const;
virtual void visit_edges(JS::Cell::Visitor&) override;
private:
explicit Body(JS::NonnullGCPtr<Streams::ReadableStream>);
Body(JS::NonnullGCPtr<Streams::ReadableStream>, SourceType, Optional<u64>);
// https://fetch.spec.whatwg.org/#concept-body-stream
// A stream (a ReadableStream object).
JS::Handle<Streams::ReadableStream> m_stream;
JS::NonnullGCPtr<Streams::ReadableStream> m_stream;
// https://fetch.spec.whatwg.org/#concept-body-source
// A source (null, a byte sequence, a Blob object, or a FormData object), initially null.
@ -57,10 +64,10 @@ private:
// https://fetch.spec.whatwg.org/#body-with-type
// A body with type is a tuple that consists of a body (a body) and a type (a header value or null).
struct BodyWithType {
Body body;
JS::NonnullGCPtr<Body> body;
Optional<ByteBuffer> type;
};
WebIDL::ExceptionOr<Body> byte_sequence_as_body(JS::Realm&, ReadonlyBytes);
WebIDL::ExceptionOr<JS::NonnullGCPtr<Body>> byte_sequence_as_body(JS::Realm&, ReadonlyBytes);
}

View file

@ -23,6 +23,9 @@ void Request::visit_edges(JS::Cell::Visitor& visitor)
Base::visit_edges(visitor);
visitor.visit(m_header_list);
visitor.visit(m_client);
m_body.visit(
[&](JS::GCPtr<Body>& body) { visitor.visit(body); },
[](auto&) {});
m_reserved_client.visit(
[&](JS::GCPtr<HTML::EnvironmentSettingsObject> const& value) { visitor.visit(value); },
[](auto const&) {});
@ -249,8 +252,8 @@ JS::NonnullGCPtr<Request> Request::clone(JS::Realm& realm) const
new_request->set_timing_allow_failed(m_timing_allow_failed);
// 2. If requests body is non-null, set newRequests body to the result of cloning requests body.
if (auto const* body = m_body.get_pointer<Body>())
new_request->set_body(body->clone(realm));
if (auto const* body = m_body.get_pointer<JS::NonnullGCPtr<Body>>())
new_request->set_body((*body)->clone(realm));
// 3. Return newRequest.
return new_request;

View file

@ -153,7 +153,7 @@ public:
// Members are implementation-defined
struct Priority { };
using BodyType = Variant<Empty, ByteBuffer, Body>;
using BodyType = Variant<Empty, ByteBuffer, JS::NonnullGCPtr<Body>>;
using OriginType = Variant<Origin, HTML::Origin>;
using PolicyContainerType = Variant<PolicyContainer, HTML::PolicyContainer>;
using ReferrerType = Variant<Referrer, AK::URL>;

View file

@ -26,6 +26,7 @@ void Response::visit_edges(JS::Cell::Visitor& visitor)
{
Base::visit_edges(visitor);
visitor.visit(m_header_list);
visitor.visit(m_body);
}
JS::NonnullGCPtr<Response> Response::create(JS::VM& vm)
@ -50,7 +51,7 @@ JS::NonnullGCPtr<Response> Response::network_error(JS::VM& vm, Variant<String, S
auto response = Response::create(vm);
response->set_status(0);
response->set_type(Type::Error);
VERIFY(!response->body().has_value());
VERIFY(!response->body());
response->m_network_error_message = move(message);
return response;
}
@ -163,7 +164,7 @@ WebIDL::ExceptionOr<JS::NonnullGCPtr<Response>> Response::clone(JS::Realm& realm
// FIXME: service worker timing info
// 3. If responses body is non-null, then set newResponses body to the result of cloning responses body.
if (m_body.has_value())
if (m_body)
new_response->set_body(m_body->clone(realm));
// 4. Return newResponse.
@ -283,6 +284,7 @@ void OpaqueFilteredResponse::visit_edges(JS::Cell::Visitor& visitor)
{
Base::visit_edges(visitor);
visitor.visit(m_header_list);
visitor.visit(m_body);
}
JS::NonnullGCPtr<OpaqueRedirectFilteredResponse> OpaqueRedirectFilteredResponse::create(JS::VM& vm, JS::NonnullGCPtr<Response> internal_response)
@ -302,6 +304,7 @@ void OpaqueRedirectFilteredResponse::visit_edges(JS::Cell::Visitor& visitor)
{
Base::visit_edges(visitor);
visitor.visit(m_header_list);
visitor.visit(m_body);
}
}

View file

@ -75,9 +75,9 @@ public:
[[nodiscard]] virtual JS::NonnullGCPtr<HeaderList> header_list() const { return m_header_list; }
void set_header_list(JS::NonnullGCPtr<HeaderList> header_list) { m_header_list = header_list; }
[[nodiscard]] virtual Optional<Body> const& body() const { return m_body; }
[[nodiscard]] virtual Optional<Body>& body() { return m_body; }
void set_body(Optional<Body> body) { m_body = move(body); }
[[nodiscard]] virtual JS::GCPtr<Body> const& body() const { return m_body; }
[[nodiscard]] virtual JS::GCPtr<Body>& body() { return m_body; }
void set_body(JS::GCPtr<Body> body) { m_body = move(body); }
[[nodiscard]] virtual Optional<CacheState> const& cache_state() const { return m_cache_state; }
void set_cache_state(Optional<CacheState> cache_state) { m_cache_state = move(cache_state); }
@ -147,7 +147,7 @@ private:
// https://fetch.spec.whatwg.org/#concept-response-body
// A response has an associated body (null or a body). Unless stated otherwise it is null.
Optional<Body> m_body;
JS::GCPtr<Body> m_body;
// https://fetch.spec.whatwg.org/#concept-response-cache-state
// A response has an associated cache state (the empty string, "local", or "validated"). Unless stated otherwise, it is the empty string.
@ -199,8 +199,8 @@ public:
[[nodiscard]] virtual Status status() const override { return m_internal_response->status(); }
[[nodiscard]] virtual ReadonlyBytes status_message() const override { return m_internal_response->status_message(); }
[[nodiscard]] virtual JS::NonnullGCPtr<HeaderList> header_list() const override { return m_internal_response->header_list(); }
[[nodiscard]] virtual Optional<Body> const& body() const override { return m_internal_response->body(); }
[[nodiscard]] virtual Optional<Body>& body() override { return m_internal_response->body(); }
[[nodiscard]] virtual JS::GCPtr<Body> const& body() const override { return m_internal_response->body(); }
[[nodiscard]] virtual JS::GCPtr<Body>& body() override { return m_internal_response->body(); }
[[nodiscard]] virtual Optional<CacheState> const& cache_state() const override { return m_internal_response->cache_state(); }
[[nodiscard]] virtual Vector<ByteBuffer> const& cors_exposed_header_name_list() const override { return m_internal_response->cors_exposed_header_name_list(); }
[[nodiscard]] virtual bool range_requested() const override { return m_internal_response->range_requested(); }
@ -267,8 +267,8 @@ public:
[[nodiscard]] virtual Status status() const override { return 0; }
[[nodiscard]] virtual ReadonlyBytes status_message() const override { return {}; }
[[nodiscard]] virtual JS::NonnullGCPtr<HeaderList> header_list() const override { return m_header_list; }
[[nodiscard]] virtual Optional<Body> const& body() const override { return m_body; }
[[nodiscard]] virtual Optional<Body>& body() override { return m_body; }
[[nodiscard]] virtual JS::GCPtr<Body> const& body() const override { return m_body; }
[[nodiscard]] virtual JS::GCPtr<Body>& body() override { return m_body; }
private:
OpaqueFilteredResponse(JS::NonnullGCPtr<Response>, JS::NonnullGCPtr<HeaderList>);
@ -277,7 +277,7 @@ private:
Vector<AK::URL> m_url_list;
JS::NonnullGCPtr<HeaderList> m_header_list;
Optional<Body> m_body;
JS::GCPtr<Body> m_body;
};
// https://fetch.spec.whatwg.org/#concept-filtered-response-opaque-redirect
@ -291,8 +291,8 @@ public:
[[nodiscard]] virtual Status status() const override { return 0; }
[[nodiscard]] virtual ReadonlyBytes status_message() const override { return {}; }
[[nodiscard]] virtual JS::NonnullGCPtr<HeaderList> header_list() const override { return m_header_list; }
[[nodiscard]] virtual Optional<Body> const& body() const override { return m_body; }
[[nodiscard]] virtual Optional<Body>& body() override { return m_body; }
[[nodiscard]] virtual JS::GCPtr<Body> const& body() const override { return m_body; }
[[nodiscard]] virtual JS::GCPtr<Body>& body() override { return m_body; }
private:
OpaqueRedirectFilteredResponse(JS::NonnullGCPtr<Response>, JS::NonnullGCPtr<HeaderList>);
@ -300,6 +300,6 @@ private:
virtual void visit_edges(JS::Cell::Visitor&) override;
JS::NonnullGCPtr<HeaderList> m_header_list;
Optional<Body> m_body;
JS::GCPtr<Body> m_body;
};
}

View file

@ -54,28 +54,28 @@ ErrorOr<Optional<MimeSniff::MimeType>> Request::mime_type_impl() const
// https://fetch.spec.whatwg.org/#concept-body-body
// https://fetch.spec.whatwg.org/#ref-for-concept-body-body%E2%91%A7
Optional<Infrastructure::Body const&> Request::body_impl() const
JS::GCPtr<Infrastructure::Body const> Request::body_impl() const
{
// Objects including the Body interface mixin have an associated body (null or a body).
// A Request objects body is its requests body.
return m_request->body().visit(
[](Infrastructure::Body const& b) -> Optional<Infrastructure::Body const&> { return b; },
[](Empty) -> Optional<Infrastructure::Body const&> { return {}; },
[](JS::NonnullGCPtr<Infrastructure::Body> const& b) -> JS::GCPtr<Infrastructure::Body const> { return b; },
[](Empty) -> JS::GCPtr<Infrastructure::Body const> { return nullptr; },
// A byte sequence will be safely extracted into a body early on in fetch.
[](ByteBuffer const&) -> Optional<Infrastructure::Body const&> { VERIFY_NOT_REACHED(); });
[](ByteBuffer const&) -> JS::GCPtr<Infrastructure::Body const> { VERIFY_NOT_REACHED(); });
}
// https://fetch.spec.whatwg.org/#concept-body-body
// https://fetch.spec.whatwg.org/#ref-for-concept-body-body%E2%91%A7
Optional<Infrastructure::Body&> Request::body_impl()
JS::GCPtr<Infrastructure::Body> Request::body_impl()
{
// Objects including the Body interface mixin have an associated body (null or a body).
// A Request objects body is its requests body.
return m_request->body().visit(
[](Infrastructure::Body& b) -> Optional<Infrastructure::Body&> { return b; },
[](Empty) -> Optional<Infrastructure::Body&> { return {}; },
[](JS::NonnullGCPtr<Infrastructure::Body>& b) -> JS::GCPtr<Infrastructure::Body> { return b; },
[](Empty) -> JS::GCPtr<Infrastructure::Body> { return {}; },
// A byte sequence will be safely extracted into a body early on in fetch.
[](ByteBuffer&) -> Optional<Infrastructure::Body&> { VERIFY_NOT_REACHED(); });
[](ByteBuffer&) -> JS::GCPtr<Infrastructure::Body> { VERIFY_NOT_REACHED(); });
}
// https://fetch.spec.whatwg.org/#request-create
@ -440,7 +440,7 @@ WebIDL::ExceptionOr<JS::NonnullGCPtr<Request>> Request::construct_impl(JS::Realm
return WebIDL::SimpleException { WebIDL::SimpleExceptionType::TypeError, "Method must not be GET or HEAD when body is provided"sv };
// 35. Let initBody be null.
Optional<Infrastructure::Body> init_body;
JS::GCPtr<Infrastructure::Body> init_body;
// 36. If init["body"] exists and is non-null, then:
if (init.body.has_value() && (*init.body).has_value()) {
@ -448,7 +448,7 @@ WebIDL::ExceptionOr<JS::NonnullGCPtr<Request>> Request::construct_impl(JS::Realm
auto body_with_type = TRY(extract_body(realm, (*init.body).value(), request->keepalive()));
// 2. Set initBody to bodyWithTypes body.
init_body = move(body_with_type.body);
init_body = body_with_type.body;
// 3. Let type be bodyWithTypes type.
auto const& type = body_with_type.type;
@ -459,15 +459,15 @@ WebIDL::ExceptionOr<JS::NonnullGCPtr<Request>> Request::construct_impl(JS::Realm
}
// 37. Let inputOrInitBody be initBody if it is non-null; otherwise inputBody.
Optional<Infrastructure::Request::BodyType> input_or_init_body = init_body.has_value()
? Infrastructure::Request::BodyType { init_body.value() }
Optional<Infrastructure::Request::BodyType> input_or_init_body = init_body
? Infrastructure::Request::BodyType { *init_body }
: input_body;
// 38. If inputOrInitBody is non-null and inputOrInitBodys source is null, then:
// FIXME: The spec doesn't check if inputOrInitBody is a body before accessing source.
if (input_or_init_body.has_value() && input_or_init_body->has<Infrastructure::Body>() && input_or_init_body->get<Infrastructure::Body>().source().has<Empty>()) {
if (input_or_init_body.has_value() && input_or_init_body->has<JS::NonnullGCPtr<Infrastructure::Body>>() && input_or_init_body->get<JS::NonnullGCPtr<Infrastructure::Body>>()->source().has<Empty>()) {
// 1. If initBody is non-null and init["duplex"] does not exist, then throw a TypeError.
if (init_body.has_value() && !init.duplex.has_value())
if (init_body && !init.duplex.has_value())
return WebIDL::SimpleException { WebIDL::SimpleExceptionType::TypeError, "Body without source requires 'duplex' value to be set"sv };
// 2. If thiss requests mode is neither "same-origin" nor "cors", then throw a TypeError.
@ -482,7 +482,7 @@ WebIDL::ExceptionOr<JS::NonnullGCPtr<Request>> Request::construct_impl(JS::Realm
auto const& final_body = input_or_init_body;
// 40. If initBody is null and inputBody is non-null, then:
if (!init_body.has_value() && input_body.has_value()) {
if (!init_body && input_body.has_value()) {
// 2. If input is unusable, then throw a TypeError.
if (input.has<JS::Handle<Request>>() && input.get<JS::Handle<Request>>()->is_unusable())
return WebIDL::SimpleException { WebIDL::SimpleExceptionType::TypeError, "Request is unusable"sv };

View file

@ -73,8 +73,8 @@ public:
// ^BodyMixin
virtual ErrorOr<Optional<MimeSniff::MimeType>> mime_type_impl() const override;
virtual Optional<Infrastructure::Body&> body_impl() override;
virtual Optional<Infrastructure::Body const&> body_impl() const override;
virtual JS::GCPtr<Infrastructure::Body> body_impl() override;
virtual JS::GCPtr<Infrastructure::Body const> body_impl() const override;
virtual Bindings::PlatformObject& as_platform_object() override { return *this; }
virtual Bindings::PlatformObject const& as_platform_object() const override { return *this; }

View file

@ -50,24 +50,20 @@ ErrorOr<Optional<MimeSniff::MimeType>> Response::mime_type_impl() const
// https://fetch.spec.whatwg.org/#concept-body-body
// https://fetch.spec.whatwg.org/#ref-for-concept-body-body%E2%91%A8
Optional<Infrastructure::Body const&> Response::body_impl() const
JS::GCPtr<Infrastructure::Body const> Response::body_impl() const
{
// Objects including the Body interface mixin have an associated body (null or a body).
// A Response objects body is its responses body.
return m_response->body().has_value()
? m_response->body().value()
: Optional<Infrastructure::Body const&> {};
return m_response->body() ? m_response->body() : nullptr;
}
// https://fetch.spec.whatwg.org/#concept-body-body
// https://fetch.spec.whatwg.org/#ref-for-concept-body-body%E2%91%A8
Optional<Infrastructure::Body&> Response::body_impl()
JS::GCPtr<Infrastructure::Body> Response::body_impl()
{
// Objects including the Body interface mixin have an associated body (null or a body).
// A Response objects body is its responses body.
return m_response->body().has_value()
? m_response->body().value()
: Optional<Infrastructure::Body&> {};
return m_response->body() ? m_response->body() : nullptr;
}
// https://fetch.spec.whatwg.org/#response-create

View file

@ -40,8 +40,8 @@ public:
// ^BodyMixin
virtual ErrorOr<Optional<MimeSniff::MimeType>> mime_type_impl() const override;
virtual Optional<Infrastructure::Body&> body_impl() override;
virtual Optional<Infrastructure::Body const&> body_impl() const override;
virtual JS::GCPtr<Infrastructure::Body> body_impl() override;
virtual JS::GCPtr<Infrastructure::Body const> body_impl() const override;
virtual Bindings::PlatformObject& as_platform_object() override { return *this; }
virtual Bindings::PlatformObject const& as_platform_object() const override { return *this; }

View file

@ -1003,7 +1003,7 @@ WebIDL::ExceptionOr<void> HTMLMediaElement::fetch_resource(AK::URL const& url_re
// 5. Otherwise, incrementally read response's body given updateMedia, processEndOfMedia, an empty algorithm, and global.
VERIFY(response->body().has_value());
VERIFY(response->body());
auto empty_algorithm = [](auto) {};
// FIXME: We are "fully" reading the response here, rather than "incrementally". Memory concerns aside, this should be okay for now as we are

View file

@ -189,7 +189,7 @@ WebIDL::ExceptionOr<void> HTMLVideoElement::determine_element_poster_frame(Optio
m_poster_frame = move(image.release_value().frames[0].bitmap);
};
VERIFY(response->body().has_value());
VERIFY(response->body());
auto empty_algorithm = [](auto) {};
response->body()->fully_read(realm, move(on_image_data_read), move(empty_algorithm), JS::NonnullGCPtr { global }).release_value_but_fixme_should_propagate_errors();

View file

@ -82,8 +82,8 @@ void SharedImageRequest::fetch_image(JS::Realm& realm, JS::NonnullGCPtr<Fetch::I
handle_failed_fetch();
};
if (response->body().has_value())
response->body().value().fully_read(realm, move(process_body), move(process_body_error), JS::NonnullGCPtr { realm.global_object() }).release_value_but_fixme_should_propagate_errors();
if (response->body())
response->body()->fully_read(realm, move(process_body), move(process_body_error), JS::NonnullGCPtr { realm.global_object() }).release_value_but_fixme_should_propagate_errors();
};
m_state = State::Fetching;

View file

@ -82,6 +82,7 @@ void XMLHttpRequest::visit_edges(Cell::Visitor& visitor)
Base::visit_edges(visitor);
visitor.visit(m_upload_object);
visitor.visit(m_author_request_headers);
visitor.visit(m_request_body);
visitor.visit(m_response);
visitor.visit(m_fetch_controller);
@ -194,7 +195,7 @@ WebIDL::ExceptionOr<JS::Value> XMLHttpRequest::response()
// Note: Automatically done by the layers above us.
// 2. If thiss responses body is null, then return null.
if (!m_response->body().has_value())
if (!m_response->body())
return JS::js_null();
// 3. Let jsonObject be the result of running parse JSON from bytes on thiss received bytes. If that threw an exception, then return null.
@ -214,7 +215,7 @@ WebIDL::ExceptionOr<JS::Value> XMLHttpRequest::response()
String XMLHttpRequest::get_text_response() const
{
// 1. If xhrs responses body is null, then return the empty string.
if (!m_response->body().has_value())
if (!m_response->body())
return String {};
// 2. Let charset be the result of get a final encoding for xhr.
@ -559,8 +560,8 @@ WebIDL::ExceptionOr<void> XMLHttpRequest::send(Optional<DocumentOrXMLHttpRequest
// body
// Thiss request body.
if (m_request_body.has_value())
request->set_body(m_request_body.value());
if (m_request_body)
request->set_body(JS::NonnullGCPtr { *m_request_body });
// client
// Thiss relevant settings object.
@ -594,7 +595,7 @@ WebIDL::ExceptionOr<void> XMLHttpRequest::send(Optional<DocumentOrXMLHttpRequest
// 9. If reqs body is null, then set thiss upload complete flag.
// NOTE: req's body is always m_request_body here, see step 6.
if (!m_request_body.has_value())
if (!m_request_body)
m_upload_complete = true;
// 10. Set thiss send() flag.
@ -615,11 +616,11 @@ WebIDL::ExceptionOr<void> XMLHttpRequest::send(Optional<DocumentOrXMLHttpRequest
// NOTE: req's body is always m_request_body here, see step 6.
// 4. Assert: requestBodyLength is an integer.
// NOTE: This is done to provide a better assertion failure message, whereas below the message would be "m_has_value"
if (m_request_body.has_value())
if (m_request_body)
VERIFY(m_request_body->length().has_value());
// NOTE: This is const to allow the callback functions to take a copy of it and know it won't change.
auto const request_body_length = m_request_body.has_value() ? m_request_body->length().value() : 0;
auto const request_body_length = m_request_body ? m_request_body->length().value() : 0;
// 5. If thiss upload complete flag is unset and thiss upload listener flag is set, then fire a progress event named loadstart at thiss upload object with requestBodyTransmitted and requestBodyLength.
if (!m_upload_complete && m_upload_listener)
@ -691,7 +692,7 @@ WebIDL::ExceptionOr<void> XMLHttpRequest::send(Optional<DocumentOrXMLHttpRequest
return;
// 7. If thiss responses body is null, then run handle response end-of-body for this and return.
if (!m_response->body().has_value()) {
if (!m_response->body()) {
// NOTE: This cannot throw, as `handle_response_end_of_body` only throws in a synchronous context.
// FIXME: However, we can receive allocation failures, but we can't propagate them anywhere currently.
handle_response_end_of_body().release_value_but_fixme_should_propagate_errors();

View file

@ -136,7 +136,7 @@ private:
// https://xhr.spec.whatwg.org/#request-body
// request body
// Initially null.
Optional<Fetch::Infrastructure::Body> m_request_body;
JS::GCPtr<Fetch::Infrastructure::Body> m_request_body;
// https://xhr.spec.whatwg.org/#synchronous-flag
// synchronous flag