1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-05-20 14:25:08 +00:00

LibWeb: Propagate Realm instead of VM more through Fetch

This makes Fetch rely less on using main_thread_vm().current_realm(),
which relies on the dummy execution context if no JavaScript is
currently running.
This commit is contained in:
Luke Wilde 2023-02-28 17:45:49 +00:00 committed by Linus Groh
parent f7ff1fd985
commit 9acc542059
19 changed files with 62 additions and 49 deletions

View file

@ -132,7 +132,7 @@ WebIDL::ExceptionOr<JS::Value> package_data(JS::Realm& realm, ByteBuffer bytes,
} }
case PackageDataType::JSON: case PackageDataType::JSON:
// Return the result of running parse JSON from bytes on bytes. // Return the result of running parse JSON from bytes on bytes.
return Infra::parse_json_bytes_to_javascript_value(vm, bytes); return Infra::parse_json_bytes_to_javascript_value(realm, bytes);
case PackageDataType::Text: case PackageDataType::Text:
// Return the result of running UTF-8 decode on bytes. // Return the result of running UTF-8 decode on bytes.
return JS::PrimitiveString::create(vm, TRY_OR_THROW_OOM(vm, String::from_utf8(bytes))); return JS::PrimitiveString::create(vm, TRY_OR_THROW_OOM(vm, String::from_utf8(bytes)));

View file

@ -8,6 +8,7 @@
#include <AK/TypeCasts.h> #include <AK/TypeCasts.h>
#include <LibJS/Runtime/PromiseCapability.h> #include <LibJS/Runtime/PromiseCapability.h>
#include <LibWeb/Bindings/ExceptionOrUtils.h> #include <LibWeb/Bindings/ExceptionOrUtils.h>
#include <LibWeb/Bindings/HostDefined.h>
#include <LibWeb/DOM/AbortSignal.h> #include <LibWeb/DOM/AbortSignal.h>
#include <LibWeb/Fetch/FetchMethod.h> #include <LibWeb/Fetch/FetchMethod.h>
#include <LibWeb/Fetch/Fetching/Fetching.h> #include <LibWeb/Fetch/Fetching/Fetching.h>
@ -36,7 +37,7 @@ JS::NonnullGCPtr<JS::Promise> fetch_impl(JS::VM& vm, RequestInfo const& input, R
if (exception_or_request_object.is_exception()) { if (exception_or_request_object.is_exception()) {
// FIXME: We should probably make this a public API? // FIXME: We should probably make this a public API?
auto throw_completion = Bindings::Detail::dom_exception_to_throw_completion(vm, exception_or_request_object.release_error()); auto throw_completion = Bindings::Detail::dom_exception_to_throw_completion(vm, exception_or_request_object.release_error());
WebIDL::reject_promise(vm, promise_capability, *throw_completion.value()); WebIDL::reject_promise(realm, promise_capability, *throw_completion.value());
return verify_cast<JS::Promise>(*promise_capability->promise().ptr()); return verify_cast<JS::Promise>(*promise_capability->promise().ptr());
} }
auto request_object = exception_or_request_object.release_value(); auto request_object = exception_or_request_object.release_value();
@ -47,7 +48,7 @@ JS::NonnullGCPtr<JS::Promise> fetch_impl(JS::VM& vm, RequestInfo const& input, R
// 4. If requestObjects signal is aborted, then: // 4. If requestObjects signal is aborted, then:
if (request_object->signal()->aborted()) { if (request_object->signal()->aborted()) {
// 1. Abort the fetch() call with p, request, null, and requestObjects signals abort reason. // 1. Abort the fetch() call with p, request, null, and requestObjects signals abort reason.
abort_fetch(vm, promise_capability, request, nullptr, request_object->signal()->reason()); abort_fetch(realm, promise_capability, request, nullptr, request_object->signal()->reason());
// 2. Return p. // 2. Return p.
return verify_cast<JS::Promise>(*promise_capability->promise().ptr()); return verify_cast<JS::Promise>(*promise_capability->promise().ptr());
@ -79,11 +80,21 @@ JS::NonnullGCPtr<JS::Promise> fetch_impl(JS::VM& vm, RequestInfo const& input, R
// 12. Set controller to the result of calling fetch given request and processResponse given response being these // 12. Set controller to the result of calling fetch given request and processResponse given response being these
// steps: // steps:
auto process_response = [&vm, locally_aborted, promise_capability, request, response_object_handle, &relevant_realm](JS::NonnullGCPtr<Infrastructure::Response> response) mutable { auto process_response = [locally_aborted, promise_capability, request, response_object_handle, &relevant_realm](JS::NonnullGCPtr<Infrastructure::Response> response) mutable {
// 1. If locallyAborted is true, then abort these steps. // 1. If locallyAborted is true, then abort these steps.
if (locally_aborted->value()) if (locally_aborted->value())
return; return;
// NOTE: Not part of the spec, but we need to have an execution context on the stack to call native functions.
// (In this case, Promise functions)
auto& environment_settings_object = Bindings::host_defined_environment_settings_object(relevant_realm);
environment_settings_object.prepare_to_run_script();
ScopeGuard guard = [&]() {
// See above NOTE.
environment_settings_object.clean_up_after_running_script();
};
// 2. If responses aborted flag is set, then: // 2. If responses aborted flag is set, then:
if (response->aborted()) { if (response->aborted()) {
// FIXME: 1. Let deserializedError be the result of deserialize a serialized abort reason given controllers // FIXME: 1. Let deserializedError be the result of deserialize a serialized abort reason given controllers
@ -91,7 +102,7 @@ JS::NonnullGCPtr<JS::Promise> fetch_impl(JS::VM& vm, RequestInfo const& input, R
auto deserialized_error = JS::js_undefined(); auto deserialized_error = JS::js_undefined();
// 2. Abort the fetch() call with p, request, responseObject, and deserializedError. // 2. Abort the fetch() call with p, request, responseObject, and deserializedError.
abort_fetch(vm, promise_capability, request, response_object_handle.cell(), deserialized_error); abort_fetch(relevant_realm, promise_capability, request, response_object_handle.cell(), deserialized_error);
// 3. Abort these steps. // 3. Abort these steps.
return; return;
@ -100,7 +111,7 @@ JS::NonnullGCPtr<JS::Promise> fetch_impl(JS::VM& vm, RequestInfo const& input, R
// 3. If response is a network error, then reject p with a TypeError and abort these steps. // 3. If response is a network error, then reject p with a TypeError and abort these steps.
if (response->is_network_error()) { if (response->is_network_error()) {
auto message = response->network_error_message().value_or("Response is a network error"sv); auto message = response->network_error_message().value_or("Response is a network error"sv);
WebIDL::reject_promise(vm, promise_capability, JS::TypeError::create(relevant_realm, message).release_allocated_value_but_fixme_should_propagate_errors()); WebIDL::reject_promise(relevant_realm, promise_capability, JS::TypeError::create(relevant_realm, message).release_allocated_value_but_fixme_should_propagate_errors());
return; return;
} }
@ -110,7 +121,7 @@ JS::NonnullGCPtr<JS::Promise> fetch_impl(JS::VM& vm, RequestInfo const& input, R
response_object_handle = JS::make_handle(response_object); response_object_handle = JS::make_handle(response_object);
// 5. Resolve p with responseObject. // 5. Resolve p with responseObject.
WebIDL::resolve_promise(vm, promise_capability, response_object); WebIDL::resolve_promise(relevant_realm, promise_capability, response_object);
}; };
controller = MUST(Fetching::fetch( controller = MUST(Fetching::fetch(
realm, realm,
@ -126,7 +137,7 @@ JS::NonnullGCPtr<JS::Promise> fetch_impl(JS::VM& vm, RequestInfo const& input, R
}))); })));
// 11. Add the following abort steps to requestObjects signal: // 11. Add the following abort steps to requestObjects signal:
request_object->signal()->add_abort_algorithm([&vm, locally_aborted, request, controller, promise_capability_handle = JS::make_handle(*promise_capability), request_object_handle = JS::make_handle(*request_object), response_object_handle] { request_object->signal()->add_abort_algorithm([locally_aborted, request, controller, promise_capability_handle = JS::make_handle(*promise_capability), request_object_handle = JS::make_handle(*request_object), response_object_handle, &relevant_realm] {
dbgln_if(WEB_FETCH_DEBUG, "Fetch: Request object signal's abort algorithm called"); dbgln_if(WEB_FETCH_DEBUG, "Fetch: Request object signal's abort algorithm called");
auto& promise_capability = *promise_capability_handle; auto& promise_capability = *promise_capability_handle;
@ -140,10 +151,10 @@ JS::NonnullGCPtr<JS::Promise> fetch_impl(JS::VM& vm, RequestInfo const& input, R
VERIFY(controller); VERIFY(controller);
// 3. Abort controller with requestObjects signals abort reason. // 3. Abort controller with requestObjects signals abort reason.
controller->abort(vm, request_object.signal()->reason()); controller->abort(relevant_realm, request_object.signal()->reason());
// 4. Abort the fetch() call with p, request, responseObject, and requestObjects signals abort reason. // 4. Abort the fetch() call with p, request, responseObject, and requestObjects signals abort reason.
abort_fetch(vm, promise_capability, request, response_object, request_object.signal()->reason()); abort_fetch(relevant_realm, promise_capability, request, response_object, request_object.signal()->reason());
}); });
// 13. Return p. // 13. Return p.
@ -151,13 +162,13 @@ JS::NonnullGCPtr<JS::Promise> fetch_impl(JS::VM& vm, RequestInfo const& input, R
} }
// https://fetch.spec.whatwg.org/#abort-fetch // https://fetch.spec.whatwg.org/#abort-fetch
void abort_fetch(JS::VM& vm, WebIDL::Promise const& promise, JS::NonnullGCPtr<Infrastructure::Request> request, JS::GCPtr<Response> response_object, JS::Value error) void abort_fetch(JS::Realm& realm, WebIDL::Promise const& promise, JS::NonnullGCPtr<Infrastructure::Request> request, JS::GCPtr<Response> response_object, JS::Value error)
{ {
dbgln_if(WEB_FETCH_DEBUG, "Fetch: Aborting fetch with: request @ {}, error = {}", request.ptr(), error); dbgln_if(WEB_FETCH_DEBUG, "Fetch: Aborting fetch with: request @ {}, error = {}", request.ptr(), error);
// 1. Reject promise with error. // 1. Reject promise with error.
// NOTE: This is a no-op if promise has already fulfilled. // NOTE: This is a no-op if promise has already fulfilled.
WebIDL::reject_promise(vm, promise, error); WebIDL::reject_promise(realm, promise, error);
// 2. If requests body is non-null and is readable, then cancel requests body with 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<Infrastructure::Body>(); body != nullptr && body->stream()->is_readable()) {

View file

@ -15,6 +15,6 @@
namespace Web::Fetch { namespace Web::Fetch {
JS::NonnullGCPtr<JS::Promise> fetch_impl(JS::VM&, RequestInfo const& input, RequestInit const& init = {}); JS::NonnullGCPtr<JS::Promise> fetch_impl(JS::VM&, RequestInfo const& input, RequestInit const& init = {});
void abort_fetch(JS::VM&, WebIDL::Promise const&, JS::NonnullGCPtr<Infrastructure::Request>, JS::GCPtr<Response>, JS::Value error); void abort_fetch(JS::Realm&, WebIDL::Promise const&, JS::NonnullGCPtr<Infrastructure::Request>, JS::GCPtr<Response>, JS::Value error);
} }

View file

@ -759,7 +759,7 @@ WebIDL::ExceptionOr<JS::NonnullGCPtr<PendingResponse>> http_fetch(JS::Realm& rea
// 4. If requests service-workers mode is "all", then: // 4. If requests service-workers mode is "all", then:
if (request->service_workers_mode() == Infrastructure::Request::ServiceWorkersMode::All) { if (request->service_workers_mode() == Infrastructure::Request::ServiceWorkersMode::All) {
// 1. Let requestForServiceWorker be a clone of request. // 1. Let requestForServiceWorker be a clone of request.
auto request_for_service_worker = TRY(request->clone(vm)); auto request_for_service_worker = TRY(request->clone(realm));
// 2. If requestForServiceWorkers body is non-null, then: // 2. If requestForServiceWorkers body is non-null, then:
if (!request_for_service_worker->body().has<Empty>()) { if (!request_for_service_worker->body().has<Empty>()) {
@ -1147,7 +1147,7 @@ WebIDL::ExceptionOr<JS::NonnullGCPtr<PendingResponse>> http_network_or_cache_fet
// NOTE: Implementations are encouraged to avoid teeing requests bodys stream when requests bodys // NOTE: Implementations are encouraged to avoid teeing requests bodys stream when requests bodys
// source is null as only a single body is needed in that case. E.g., when requests bodys source // source is null as only a single body is needed in that case. E.g., when requests bodys source
// is null, redirects and authentication will end up failing the fetch. // is null, redirects and authentication will end up failing the fetch.
http_request = TRY(request->clone(vm)); http_request = TRY(request->clone(realm));
// 2. Set httpFetchParams to a copy of fetchParams. // 2. Set httpFetchParams to a copy of fetchParams.
// 3. Set httpFetchParamss request to httpRequest. // 3. Set httpFetchParamss request to httpRequest.

View file

@ -55,10 +55,8 @@ JS::NonnullGCPtr<FetchTimingInfo> FetchController::extract_full_timing_info() co
} }
// https://fetch.spec.whatwg.org/#fetch-controller-abort // https://fetch.spec.whatwg.org/#fetch-controller-abort
void FetchController::abort(JS::VM& vm, Optional<JS::Value> error) void FetchController::abort(JS::Realm& realm, Optional<JS::Value> error)
{ {
auto& realm = *vm.current_realm();
// 1. Set controllers state to "aborted". // 1. Set controllers state to "aborted".
m_state = State::Aborted; m_state = State::Aborted;

View file

@ -36,7 +36,7 @@ public:
void report_timing(JS::Object const&) const; void report_timing(JS::Object const&) const;
void process_next_manual_redirect() const; void process_next_manual_redirect() const;
[[nodiscard]] JS::NonnullGCPtr<FetchTimingInfo> extract_full_timing_info() const; [[nodiscard]] JS::NonnullGCPtr<FetchTimingInfo> extract_full_timing_info() const;
void abort(JS::VM&, Optional<JS::Value>); void abort(JS::Realm&, Optional<JS::Value>);
void terminate(); void terminate();
private: private:

View file

@ -25,16 +25,12 @@ Body::Body(JS::Handle<Streams::ReadableStream> stream, SourceType source, Option
} }
// https://fetch.spec.whatwg.org/#concept-body-clone // https://fetch.spec.whatwg.org/#concept-body-clone
WebIDL::ExceptionOr<Body> Body::clone() const WebIDL::ExceptionOr<Body> Body::clone(JS::Realm& realm) const
{ {
// To clone a body body, run these steps: // To clone a body body, run these steps:
auto& vm = Bindings::main_thread_vm();
auto& realm = *vm.current_realm();
// FIXME: 1. Let « out1, out2 » be the result of teeing bodys stream. // FIXME: 1. Let « out1, out2 » be the result of teeing bodys stream.
// FIXME: 2. Set bodys stream to out1. // FIXME: 2. Set bodys stream to out1.
auto out2 = MUST_OR_THROW_OOM(vm.heap().allocate<Streams::ReadableStream>(realm, realm)); auto out2 = MUST_OR_THROW_OOM(realm.heap().allocate<Streams::ReadableStream>(realm, realm));
// 3. Return a body whose stream is out2 and other members are copied from body. // 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 { JS::make_handle(out2), m_source, m_length };

View file

@ -31,7 +31,7 @@ public:
[[nodiscard]] SourceType const& source() const { return m_source; } [[nodiscard]] SourceType const& source() const { return m_source; }
[[nodiscard]] Optional<u64> const& length() const { return m_length; } [[nodiscard]] Optional<u64> const& length() const { return m_length; }
WebIDL::ExceptionOr<Body> clone() const; WebIDL::ExceptionOr<Body> clone(JS::Realm&) const;
WebIDL::ExceptionOr<JS::NonnullGCPtr<WebIDL::Promise>> fully_read_as_promise() const; WebIDL::ExceptionOr<JS::NonnullGCPtr<WebIDL::Promise>> fully_read_as_promise() const;

View file

@ -195,9 +195,10 @@ ErrorOr<ByteBuffer> Request::byte_serialize_origin() const
} }
// https://fetch.spec.whatwg.org/#concept-request-clone // https://fetch.spec.whatwg.org/#concept-request-clone
WebIDL::ExceptionOr<JS::NonnullGCPtr<Request>> Request::clone(JS::VM& vm) const WebIDL::ExceptionOr<JS::NonnullGCPtr<Request>> Request::clone(JS::Realm& realm) const
{ {
// To clone a request request, run these steps: // To clone a request request, run these steps:
auto& vm = realm.vm();
// 1. Let newRequest be a copy of request, except for its body. // 1. Let newRequest be a copy of request, except for its body.
auto new_request = Infrastructure::Request::create(vm); auto new_request = Infrastructure::Request::create(vm);
@ -242,7 +243,7 @@ WebIDL::ExceptionOr<JS::NonnullGCPtr<Request>> Request::clone(JS::VM& vm) const
// 2. If requests body is non-null, set newRequests body to the result of cloning requests body. // 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>()) if (auto const* body = m_body.get_pointer<Body>())
new_request->set_body(TRY(body->clone())); new_request->set_body(TRY(body->clone(realm)));
// 3. Return newRequest. // 3. Return newRequest.
return new_request; return new_request;

View file

@ -297,7 +297,7 @@ public:
[[nodiscard]] ErrorOr<String> serialize_origin() const; [[nodiscard]] ErrorOr<String> serialize_origin() const;
[[nodiscard]] ErrorOr<ByteBuffer> byte_serialize_origin() const; [[nodiscard]] ErrorOr<ByteBuffer> byte_serialize_origin() const;
[[nodiscard]] WebIDL::ExceptionOr<JS::NonnullGCPtr<Request>> clone(JS::VM&) const; [[nodiscard]] WebIDL::ExceptionOr<JS::NonnullGCPtr<Request>> clone(JS::Realm&) const;
[[nodiscard]] ErrorOr<void> add_range_header(u64 first, Optional<u64> const& last); [[nodiscard]] ErrorOr<void> add_range_header(u64 first, Optional<u64> const& last);
[[nodiscard]] ErrorOr<void> add_origin_header(); [[nodiscard]] ErrorOr<void> add_origin_header();

View file

@ -127,13 +127,14 @@ ErrorOr<Optional<AK::URL>> Response::location_url(Optional<String> const& reques
} }
// https://fetch.spec.whatwg.org/#concept-response-clone // https://fetch.spec.whatwg.org/#concept-response-clone
WebIDL::ExceptionOr<JS::NonnullGCPtr<Response>> Response::clone(JS::VM& vm) const WebIDL::ExceptionOr<JS::NonnullGCPtr<Response>> Response::clone(JS::Realm& realm) const
{ {
// To clone a response response, run these steps: // To clone a response response, run these steps:
auto& vm = realm.vm();
// 1. If response is a filtered response, then return a new identical filtered response whose internal response is a clone of responses internal response. // 1. If response is a filtered response, then return a new identical filtered response whose internal response is a clone of responses internal response.
if (is<FilteredResponse>(*this)) { if (is<FilteredResponse>(*this)) {
auto internal_response = TRY(static_cast<FilteredResponse const&>(*this).internal_response()->clone(vm)); auto internal_response = TRY(static_cast<FilteredResponse const&>(*this).internal_response()->clone(realm));
if (is<BasicFilteredResponse>(*this)) if (is<BasicFilteredResponse>(*this))
return TRY_OR_THROW_OOM(vm, BasicFilteredResponse::create(vm, internal_response)); return TRY_OR_THROW_OOM(vm, BasicFilteredResponse::create(vm, internal_response));
if (is<CORSFilteredResponse>(*this)) if (is<CORSFilteredResponse>(*this))
@ -164,7 +165,7 @@ WebIDL::ExceptionOr<JS::NonnullGCPtr<Response>> Response::clone(JS::VM& vm) cons
// 3. If responses body is non-null, then set newResponses body to the result of cloning responses body. // 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.has_value())
new_response->set_body(TRY(m_body->clone())); new_response->set_body(TRY(m_body->clone(realm)));
// 4. Return newResponse. // 4. Return newResponse.
return new_response; return new_response;

View file

@ -106,7 +106,7 @@ public:
[[nodiscard]] Optional<AK::URL const&> url() const; [[nodiscard]] Optional<AK::URL const&> url() const;
[[nodiscard]] ErrorOr<Optional<AK::URL>> location_url(Optional<String> const& request_fragment) const; [[nodiscard]] ErrorOr<Optional<AK::URL>> location_url(Optional<String> const& request_fragment) const;
[[nodiscard]] WebIDL::ExceptionOr<JS::NonnullGCPtr<Response>> clone(JS::VM&) const; [[nodiscard]] WebIDL::ExceptionOr<JS::NonnullGCPtr<Response>> clone(JS::Realm&) const;
// Non-standard // Non-standard
[[nodiscard]] Optional<StringView> network_error_message() const; [[nodiscard]] Optional<StringView> network_error_message() const;

View file

@ -634,14 +634,14 @@ Bindings::RequestDuplex Request::duplex() const
// https://fetch.spec.whatwg.org/#dom-request-clone // https://fetch.spec.whatwg.org/#dom-request-clone
WebIDL::ExceptionOr<JS::NonnullGCPtr<Request>> Request::clone() const WebIDL::ExceptionOr<JS::NonnullGCPtr<Request>> Request::clone() const
{ {
auto& vm = this->vm(); auto& realm = this->realm();
// 1. If this is unusable, then throw a TypeError. // 1. If this is unusable, then throw a TypeError.
if (is_unusable()) if (is_unusable())
return WebIDL::SimpleException { WebIDL::SimpleExceptionType::TypeError, "Request is unusable"sv }; return WebIDL::SimpleException { WebIDL::SimpleExceptionType::TypeError, "Request is unusable"sv };
// 2. Let clonedRequest be the result of cloning thiss request. // 2. Let clonedRequest be the result of cloning thiss request.
auto cloned_request = TRY(m_request->clone(vm)); auto cloned_request = TRY(m_request->clone(realm));
// 3. Let clonedRequestObject be the result of creating a Request object, given clonedRequest, thiss headerss guard, and thiss relevant Realm. // 3. Let clonedRequestObject be the result of creating a Request object, given clonedRequest, thiss headerss guard, and thiss relevant Realm.
auto cloned_request_object = TRY(Request::create(HTML::relevant_realm(*this), cloned_request, m_headers->guard())); auto cloned_request_object = TRY(Request::create(HTML::relevant_realm(*this), cloned_request, m_headers->guard()));

View file

@ -286,14 +286,14 @@ JS::NonnullGCPtr<Headers> Response::headers() const
// https://fetch.spec.whatwg.org/#dom-response-clone // https://fetch.spec.whatwg.org/#dom-response-clone
WebIDL::ExceptionOr<JS::NonnullGCPtr<Response>> Response::clone() const WebIDL::ExceptionOr<JS::NonnullGCPtr<Response>> Response::clone() const
{ {
auto& vm = this->vm(); auto& realm = this->realm();
// 1. If this is unusable, then throw a TypeError. // 1. If this is unusable, then throw a TypeError.
if (is_unusable()) if (is_unusable())
return WebIDL::SimpleException { WebIDL::SimpleExceptionType::TypeError, "Response is unusable"sv }; return WebIDL::SimpleException { WebIDL::SimpleExceptionType::TypeError, "Response is unusable"sv };
// 2. Let clonedResponse be the result of cloning thiss response. // 2. Let clonedResponse be the result of cloning thiss response.
auto cloned_response = TRY(m_response->clone(vm)); auto cloned_response = TRY(m_response->clone(realm));
// 3. Return the result of creating a Response object, given clonedResponse, thiss headerss guard, and thiss relevant Realm. // 3. Return the result of creating a Response object, given clonedResponse, thiss headerss guard, and thiss relevant Realm.
return TRY(Response::create(HTML::relevant_realm(*this), cloned_response, m_headers->guard())); return TRY(Response::create(HTML::relevant_realm(*this), cloned_response, m_headers->guard()));

View file

@ -15,23 +15,25 @@
namespace Web::Infra { namespace Web::Infra {
// https://infra.spec.whatwg.org/#parse-a-json-string-to-a-javascript-value // https://infra.spec.whatwg.org/#parse-a-json-string-to-a-javascript-value
WebIDL::ExceptionOr<JS::Value> parse_json_string_to_javascript_value(JS::VM& vm, StringView string) WebIDL::ExceptionOr<JS::Value> parse_json_string_to_javascript_value(JS::Realm& realm, StringView string)
{ {
auto& realm = *vm.current_realm(); auto& vm = realm.vm();
// 1. Return ? Call(%JSON.parse%, undefined, « string »). // 1. Return ? Call(%JSON.parse%, undefined, « string »).
return TRY(JS::call(vm, realm.intrinsics().json_parse_function(), JS::js_undefined(), MUST_OR_THROW_OOM(JS::PrimitiveString::create(vm, string)))); return TRY(JS::call(vm, realm.intrinsics().json_parse_function(), JS::js_undefined(), MUST_OR_THROW_OOM(JS::PrimitiveString::create(vm, string))));
} }
// https://infra.spec.whatwg.org/#parse-json-bytes-to-a-javascript-value // https://infra.spec.whatwg.org/#parse-json-bytes-to-a-javascript-value
WebIDL::ExceptionOr<JS::Value> parse_json_bytes_to_javascript_value(JS::VM& vm, ReadonlyBytes bytes) WebIDL::ExceptionOr<JS::Value> parse_json_bytes_to_javascript_value(JS::Realm& realm, ReadonlyBytes bytes)
{ {
auto& vm = realm.vm();
// 1. Let string be the result of running UTF-8 decode on bytes. // 1. Let string be the result of running UTF-8 decode on bytes.
TextCodec::UTF8Decoder decoder; TextCodec::UTF8Decoder decoder;
auto string = TRY_OR_THROW_OOM(vm, decoder.to_utf8(bytes)); auto string = TRY_OR_THROW_OOM(vm, decoder.to_utf8(bytes));
// 2. Return the result of parsing a JSON string to an Infra value given string. // 2. Return the result of parsing a JSON string to an Infra value given string.
return parse_json_string_to_javascript_value(vm, string); return parse_json_string_to_javascript_value(realm, string);
} }
// https://infra.spec.whatwg.org/#serialize-a-javascript-value-to-a-json-string // https://infra.spec.whatwg.org/#serialize-a-javascript-value-to-a-json-string

View file

@ -12,8 +12,8 @@
namespace Web::Infra { namespace Web::Infra {
WebIDL::ExceptionOr<JS::Value> parse_json_string_to_javascript_value(JS::VM&, StringView); WebIDL::ExceptionOr<JS::Value> parse_json_string_to_javascript_value(JS::Realm&, StringView);
WebIDL::ExceptionOr<JS::Value> parse_json_bytes_to_javascript_value(JS::VM&, ReadonlyBytes); WebIDL::ExceptionOr<JS::Value> parse_json_bytes_to_javascript_value(JS::Realm&, ReadonlyBytes);
WebIDL::ExceptionOr<String> serialize_javascript_value_to_json_string(JS::VM&, JS::Value); WebIDL::ExceptionOr<String> serialize_javascript_value_to_json_string(JS::VM&, JS::Value);
WebIDL::ExceptionOr<ByteBuffer> serialize_javascript_value_to_json_bytes(JS::VM&, JS::Value); WebIDL::ExceptionOr<ByteBuffer> serialize_javascript_value_to_json_bytes(JS::VM&, JS::Value);

View file

@ -10,6 +10,7 @@
#include <LibJS/Runtime/PromiseConstructor.h> #include <LibJS/Runtime/PromiseConstructor.h>
#include <LibJS/Runtime/Realm.h> #include <LibJS/Runtime/Realm.h>
#include <LibWeb/Bindings/ExceptionOrUtils.h> #include <LibWeb/Bindings/ExceptionOrUtils.h>
#include <LibWeb/Bindings/HostDefined.h>
#include <LibWeb/WebIDL/ExceptionOr.h> #include <LibWeb/WebIDL/ExceptionOr.h>
#include <LibWeb/WebIDL/Promise.h> #include <LibWeb/WebIDL/Promise.h>
@ -69,20 +70,23 @@ JS::NonnullGCPtr<Promise> create_rejected_promise(JS::Realm& realm, JS::Value re
} }
// https://webidl.spec.whatwg.org/#resolve // https://webidl.spec.whatwg.org/#resolve
void resolve_promise(JS::VM& vm, Promise const& promise, JS::Value value) void resolve_promise(JS::Realm& realm, Promise const& promise, JS::Value value)
{ {
auto& vm = realm.vm();
// 1. If x is not given, then let it be the undefined value. // 1. If x is not given, then let it be the undefined value.
// NOTE: This is done via the default argument. // NOTE: This is done via the default argument.
// 2. Let value be the result of converting x to an ECMAScript value. // 2. Let value be the result of converting x to an ECMAScript value.
// 3. Perform ! Call(p.[[Resolve]], undefined, « value »). // 3. Perform ! Call(p.[[Resolve]], undefined, « value »).
MUST(JS::call(vm, *promise.resolve(), JS::js_undefined(), value)); MUST(JS::call(vm, *promise.resolve(), JS::js_undefined(), value));
} }
// https://webidl.spec.whatwg.org/#reject // https://webidl.spec.whatwg.org/#reject
void reject_promise(JS::VM& vm, Promise const& promise, JS::Value reason) void reject_promise(JS::Realm& realm, Promise const& promise, JS::Value reason)
{ {
auto& vm = realm.vm();
// 1. Perform ! Call(p.[[Reject]], undefined, « r »). // 1. Perform ! Call(p.[[Reject]], undefined, « r »).
MUST(JS::call(vm, *promise.reject(), JS::js_undefined(), reason)); MUST(JS::call(vm, *promise.reject(), JS::js_undefined(), reason));
} }

View file

@ -22,8 +22,8 @@ using Promise = JS::PromiseCapability;
JS::NonnullGCPtr<Promise> create_promise(JS::Realm&); JS::NonnullGCPtr<Promise> create_promise(JS::Realm&);
JS::NonnullGCPtr<Promise> create_resolved_promise(JS::Realm&, JS::Value); JS::NonnullGCPtr<Promise> create_resolved_promise(JS::Realm&, JS::Value);
JS::NonnullGCPtr<Promise> create_rejected_promise(JS::Realm&, JS::Value); JS::NonnullGCPtr<Promise> create_rejected_promise(JS::Realm&, JS::Value);
void resolve_promise(JS::VM&, Promise const&, JS::Value = JS::js_undefined()); void resolve_promise(JS::Realm&, Promise const&, JS::Value = JS::js_undefined());
void reject_promise(JS::VM&, Promise const&, JS::Value); void reject_promise(JS::Realm&, Promise const&, JS::Value);
JS::NonnullGCPtr<JS::Promise> react_to_promise(Promise const&, Optional<ReactionSteps> on_fulfilled_callback, Optional<ReactionSteps> on_rejected_callback); JS::NonnullGCPtr<JS::Promise> react_to_promise(Promise const&, Optional<ReactionSteps> on_fulfilled_callback, Optional<ReactionSteps> on_rejected_callback);
JS::NonnullGCPtr<JS::Promise> upon_fulfillment(Promise const&, ReactionSteps); JS::NonnullGCPtr<JS::Promise> upon_fulfillment(Promise const&, ReactionSteps);
JS::NonnullGCPtr<JS::Promise> upon_rejection(Promise const&, ReactionSteps); JS::NonnullGCPtr<JS::Promise> upon_rejection(Promise const&, ReactionSteps);

View file

@ -182,7 +182,7 @@ WebIDL::ExceptionOr<JS::Value> XMLHttpRequest::response()
return JS::js_null(); 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. // 3. Let jsonObject be the result of running parse JSON from bytes on thiss received bytes. If that threw an exception, then return null.
auto json_object_result = Infra::parse_json_bytes_to_javascript_value(vm, m_received_bytes); auto json_object_result = Infra::parse_json_bytes_to_javascript_value(realm(), m_received_bytes);
if (json_object_result.is_error()) if (json_object_result.is_error())
return JS::js_null(); return JS::js_null();