diff --git a/Userland/Libraries/LibWeb/DOM/AbortSignal.h b/Userland/Libraries/LibWeb/DOM/AbortSignal.h index 7fb0ad016bf..492e7c02f5b 100644 --- a/Userland/Libraries/LibWeb/DOM/AbortSignal.h +++ b/Userland/Libraries/LibWeb/DOM/AbortSignal.h @@ -49,14 +49,14 @@ public: static WebIDL::ExceptionOr> timeout(JS::VM&, Web::WebIDL::UnsignedLongLong milliseconds); static WebIDL::ExceptionOr> any(JS::VM&, JS::Value signals); + static WebIDL::ExceptionOr> create_dependent_abort_signal(JS::Realm&, Vector> const&); + private: explicit AbortSignal(JS::Realm&); virtual void initialize(JS::Realm&) override; virtual void visit_edges(JS::Cell::Visitor&) override; - static WebIDL::ExceptionOr> create_dependent_abort_signal(JS::Realm&, Vector> const&); - bool dependent() const { return m_dependent; } void set_dependent(bool dependent) { m_dependent = dependent; } diff --git a/Userland/Libraries/LibWeb/Fetch/Request.cpp b/Userland/Libraries/LibWeb/Fetch/Request.cpp index 7bb21874094..bd1287f1a15 100644 --- a/Userland/Libraries/LibWeb/Fetch/Request.cpp +++ b/Userland/Libraries/LibWeb/Fetch/Request.cpp @@ -81,7 +81,7 @@ JS::GCPtr Request::body_impl() } // https://fetch.spec.whatwg.org/#request-create -JS::NonnullGCPtr Request::create(JS::Realm& realm, JS::NonnullGCPtr request, Headers::Guard guard) +JS::NonnullGCPtr Request::create(JS::Realm& realm, JS::NonnullGCPtr request, Headers::Guard guard, JS::NonnullGCPtr signal) { // 1. Let requestObject be a new Request object with realm. // 2. Set requestObject’s request to request. @@ -91,8 +91,8 @@ JS::NonnullGCPtr Request::create(JS::Realm& realm, JS::NonnullGCPtrm_headers = realm.heap().allocate(realm, realm, request->header_list()); request_object->m_headers->set_guard(guard); - // 4. Set requestObject’s signal to a new AbortSignal object with realm. - request_object->m_signal = realm.heap().allocate(realm, realm); + // 4. Set requestObject’s signal to signal. + request_object->m_signal = signal; // 5. Return requestObject. return request_object; @@ -383,23 +383,26 @@ WebIDL::ExceptionOr> Request::construct_impl(JS::Realm if (init.signal.has_value()) input_signal = *init.signal; - // 27. Set this’s request to request. + // FIXME: 27. If init["priority"] exists, then: + + // 28. Set this’s request to request. // NOTE: This is done at the beginning as the 'this' value Request object // cannot exist with a null Infrastructure::Request. - // 28. Set this’s signal to a new AbortSignal object with this’s relevant Realm. + // 29. Let signals be « signal » if signal is non-null; otherwise « ». auto& this_relevant_realm = HTML::relevant_realm(*request_object); - request_object->m_signal = realm.heap().allocate(this_relevant_realm, this_relevant_realm); - - // 29. If signal is not null, then make this’s signal follow signal. + Vector> signals; if (input_signal != nullptr) - request_object->m_signal->follow(*input_signal); + signals.append(*input_signal); - // 30. Set this’s headers to a new Headers object with this’s relevant Realm, whose header list is request’s header list and guard is "request". + // 30. Set this’s signal to the result of creating a dependent abort signal from signals, using AbortSignal and this’s relevant realm. + request_object->m_signal = TRY(DOM::AbortSignal::create_dependent_abort_signal(this_relevant_realm, signals)); + + // 31. Set this’s headers to a new Headers object with this’s relevant Realm, whose header list is request’s header list and guard is "request". request_object->m_headers = realm.heap().allocate(realm, realm, request->header_list()); request_object->m_headers->set_guard(Headers::Guard::Request); - // 31. If this’s request’s mode is "no-cors", then: + // 32. If this’s request’s mode is "no-cors", then: if (request_object->request()->mode() == Infrastructure::Request::Mode::NoCORS) { // 1. If this’s request’s method is not a CORS-safelisted method, then throw a TypeError. if (!Infrastructure::is_cors_safelisted_method(request_object->request()->method())) @@ -409,7 +412,7 @@ WebIDL::ExceptionOr> Request::construct_impl(JS::Realm request_object->headers()->set_guard(Headers::Guard::RequestNoCORS); } - // 32. If init is not empty, then: + // 33. If init is not empty, then: if (!init.is_empty()) { // 1. Let headers be a copy of this’s headers and its associated header list. auto headers = Variant> { request_object->headers()->header_list() }; @@ -432,19 +435,19 @@ WebIDL::ExceptionOr> Request::construct_impl(JS::Realm } } - // 33. Let inputBody be input’s request’s body if input is a Request object; otherwise null. + // 34. Let inputBody be input’s request’s body if input is a Request object; otherwise null. Optional input_body; if (input.has>()) input_body = input.get>()->request()->body(); - // 34. If either init["body"] exists and is non-null or inputBody is non-null, and request’s method is `GET` or `HEAD`, then throw a TypeError. + // 35. If either init["body"] exists and is non-null or inputBody is non-null, and request’s method is `GET` or `HEAD`, then throw a TypeError. if (((init.body.has_value() && (*init.body).has_value()) || (input_body.has_value() && !input_body.value().has())) && StringView { request->method() }.is_one_of("GET"sv, "HEAD"sv)) return WebIDL::SimpleException { WebIDL::SimpleExceptionType::TypeError, "Method must not be GET or HEAD when body is provided"sv }; - // 35. Let initBody be null. + // 36. Let initBody be null. JS::GCPtr init_body; - // 36. If init["body"] exists and is non-null, then: + // 37. If init["body"] exists and is non-null, then: if (init.body.has_value() && (*init.body).has_value()) { // 1. Let bodyWithType be the result of extracting init["body"], with keepalive set to request’s keepalive. auto body_with_type = TRY(extract_body(realm, (*init.body).value(), request->keepalive())); @@ -460,12 +463,12 @@ WebIDL::ExceptionOr> Request::construct_impl(JS::Realm TRY(request_object->headers()->append(TRY_OR_THROW_OOM(vm, Infrastructure::Header::from_string_pair("Content-Type"sv, type->span())))); } - // 37. Let inputOrInitBody be initBody if it is non-null; otherwise inputBody. + // 38. Let inputOrInitBody be initBody if it is non-null; otherwise inputBody. Optional input_or_init_body = init_body ? Infrastructure::Request::BodyType { *init_body } : input_body; - // 38. If inputOrInitBody is non-null and inputOrInitBody’s source is null, then: + // 39. If inputOrInitBody is non-null and inputOrInitBody’s 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>() && input_or_init_body->get>()->source().has()) { // 1. If initBody is non-null and init["duplex"] does not exist, then throw a TypeError. @@ -480,10 +483,10 @@ WebIDL::ExceptionOr> Request::construct_impl(JS::Realm request_object->request()->set_use_cors_preflight(true); } - // 39. Let finalBody be inputOrInitBody. + // 40. Let finalBody be inputOrInitBody. auto const& final_body = input_or_init_body; - // 40. If initBody is null and inputBody is non-null, then: + // 41. If initBody is null and inputBody is non-null, then: if (!init_body && input_body.has_value()) { // 2. If input is unusable, then throw a TypeError. if (input.has>() && input.get>()->is_unusable()) @@ -492,7 +495,7 @@ WebIDL::ExceptionOr> Request::construct_impl(JS::Realm // FIXME: 2. Set finalBody to the result of creating a proxy for inputBody. } - // 41. Set this’s request’s body to finalBody. + // 42. Set this’s request’s body to finalBody. if (final_body.has_value()) request_object->request()->set_body(*final_body); @@ -643,13 +646,17 @@ WebIDL::ExceptionOr> Request::clone() const // 2. Let clonedRequest be the result of cloning this’s request. auto cloned_request = m_request->clone(realm); - // 3. Let clonedRequestObject be the result of creating a Request object, given clonedRequest, this’s headers’s guard, and this’s relevant Realm. - auto cloned_request_object = Request::create(HTML::relevant_realm(*this), cloned_request, m_headers->guard()); + // 3. Assert: this’s signal is non-null. + VERIFY(m_signal); - // 4. Make clonedRequestObject’s signal follow this’s signal. - cloned_request_object->m_signal->follow(*m_signal); + // 4. Let clonedSignal be the result of creating a dependent abort signal from « this’s signal », using AbortSignal and this’s relevant realm. + auto& relevant_realm = HTML::relevant_realm(*this); + auto cloned_signal = TRY(DOM::AbortSignal::create_dependent_abort_signal(relevant_realm, { m_signal })); - // 5. Return clonedRequestObject. + // 5. Let clonedRequestObject be the result of creating a Request object, given clonedRequest, this’s headers’s guard, clonedSignal and this’s relevant realm. + auto cloned_request_object = Request::create(relevant_realm, cloned_request, m_headers->guard(), cloned_signal); + + // 6. Return clonedRequestObject. return cloned_request_object; } diff --git a/Userland/Libraries/LibWeb/Fetch/Request.h b/Userland/Libraries/LibWeb/Fetch/Request.h index 7eb53fa407a..45281777c38 100644 --- a/Userland/Libraries/LibWeb/Fetch/Request.h +++ b/Userland/Libraries/LibWeb/Fetch/Request.h @@ -67,7 +67,7 @@ class Request final JS_DECLARE_ALLOCATOR(Request); public: - [[nodiscard]] static JS::NonnullGCPtr create(JS::Realm&, JS::NonnullGCPtr, Headers::Guard); + [[nodiscard]] static JS::NonnullGCPtr create(JS::Realm&, JS::NonnullGCPtr, Headers::Guard, JS::NonnullGCPtr); static WebIDL::ExceptionOr> construct_impl(JS::Realm&, RequestInfo const& input, RequestInit const& init = {}); virtual ~Request() override;