From 9fb672e9815a240fcefbd948806351c7e943922a Mon Sep 17 00:00:00 2001 From: Linus Groh Date: Sun, 25 Sep 2022 19:33:13 +0100 Subject: [PATCH] LibWeb: Implement '5.4. Request class' from the Fetch API :^) --- .../BindingsGenerator/IDLGenerators.cpp | 5 + .../LibWeb/Bindings/WindowObjectHelper.h | 3 + Userland/Libraries/LibWeb/CMakeLists.txt | 4 +- Userland/Libraries/LibWeb/Fetch/Enums.cpp | 260 +++++++ Userland/Libraries/LibWeb/Fetch/Enums.h | 28 + Userland/Libraries/LibWeb/Fetch/Headers.h | 3 +- .../Fetch/Infrastructure/HTTP/Headers.h | 1 + .../Fetch/Infrastructure/HTTP/Requests.cpp | 3 +- Userland/Libraries/LibWeb/Fetch/Request.cpp | 649 ++++++++++++++++++ Userland/Libraries/LibWeb/Fetch/Request.h | 115 ++++ Userland/Libraries/LibWeb/Fetch/Request.idl | 69 ++ Userland/Libraries/LibWeb/Forward.h | 8 + Userland/Libraries/LibWeb/idl_files.cmake | 1 + 13 files changed, 1146 insertions(+), 3 deletions(-) create mode 100644 Userland/Libraries/LibWeb/Fetch/Enums.cpp create mode 100644 Userland/Libraries/LibWeb/Fetch/Enums.h create mode 100644 Userland/Libraries/LibWeb/Fetch/Request.cpp create mode 100644 Userland/Libraries/LibWeb/Fetch/Request.h create mode 100644 Userland/Libraries/LibWeb/Fetch/Request.idl diff --git a/Meta/Lagom/Tools/CodeGenerators/LibWeb/BindingsGenerator/IDLGenerators.cpp b/Meta/Lagom/Tools/CodeGenerators/LibWeb/BindingsGenerator/IDLGenerators.cpp index 1e286076238..c913ba8bd2a 100644 --- a/Meta/Lagom/Tools/CodeGenerators/LibWeb/BindingsGenerator/IDLGenerators.cpp +++ b/Meta/Lagom/Tools/CodeGenerators/LibWeb/BindingsGenerator/IDLGenerators.cpp @@ -36,6 +36,8 @@ static bool is_platform_object(Type const& type) "Node"sv, "Path2D"sv, "Range"sv, + "ReadableStream"sv, + "Request"sv, "Selection"sv, "Text"sv, "TextMetrics"sv, @@ -2097,7 +2099,9 @@ using namespace Web::IntersectionObserver; using namespace Web::RequestIdleCallback; using namespace Web::ResizeObserver; using namespace Web::Selection; +using namespace Web::Streams; using namespace Web::UIEvents; +using namespace Web::URL; using namespace Web::XHR; using namespace Web::WebGL; using namespace Web::WebIDL; @@ -2410,6 +2414,7 @@ using namespace Web::NavigationTiming; using namespace Web::RequestIdleCallback; using namespace Web::ResizeObserver; using namespace Web::Selection; +using namespace Web::Streams; using namespace Web::SVG; using namespace Web::UIEvents; using namespace Web::URL; diff --git a/Userland/Libraries/LibWeb/Bindings/WindowObjectHelper.h b/Userland/Libraries/LibWeb/Bindings/WindowObjectHelper.h index 9944790de69..ec052179678 100644 --- a/Userland/Libraries/LibWeb/Bindings/WindowObjectHelper.h +++ b/Userland/Libraries/LibWeb/Bindings/WindowObjectHelper.h @@ -303,6 +303,8 @@ #include #include #include +#include +#include #include #include #include @@ -546,6 +548,7 @@ ADD_WINDOW_OBJECT_INTERFACE(PromiseRejectionEvent) \ ADD_WINDOW_OBJECT_INTERFACE(Range) \ ADD_WINDOW_OBJECT_INTERFACE(ReadableStream) \ + ADD_WINDOW_OBJECT_INTERFACE(Request) \ ADD_WINDOW_OBJECT_INTERFACE(ResizeObserver) \ ADD_WINDOW_OBJECT_INTERFACE(Screen) \ ADD_WINDOW_OBJECT_INTERFACE(Selection) \ diff --git a/Userland/Libraries/LibWeb/CMakeLists.txt b/Userland/Libraries/LibWeb/CMakeLists.txt index fece527fc90..b13e91632c8 100644 --- a/Userland/Libraries/LibWeb/CMakeLists.txt +++ b/Userland/Libraries/LibWeb/CMakeLists.txt @@ -120,16 +120,18 @@ set(SOURCES Encoding/TextEncoder.cpp Fetch/Body.cpp Fetch/BodyInit.cpp + Fetch/Enums.cpp Fetch/Headers.cpp Fetch/HeadersIterator.cpp Fetch/Infrastructure/HTTP.cpp - Fetch/Infrastructure/URL.cpp Fetch/Infrastructure/HTTP/Bodies.cpp Fetch/Infrastructure/HTTP/Headers.cpp Fetch/Infrastructure/HTTP/Methods.cpp Fetch/Infrastructure/HTTP/Requests.cpp Fetch/Infrastructure/HTTP/Responses.cpp Fetch/Infrastructure/HTTP/Statuses.cpp + Fetch/Infrastructure/URL.cpp + Fetch/Request.cpp FileAPI/Blob.cpp FileAPI/File.cpp FontCache.cpp diff --git a/Userland/Libraries/LibWeb/Fetch/Enums.cpp b/Userland/Libraries/LibWeb/Fetch/Enums.cpp new file mode 100644 index 00000000000..25cf434939c --- /dev/null +++ b/Userland/Libraries/LibWeb/Fetch/Enums.cpp @@ -0,0 +1,260 @@ +/* + * Copyright (c) 2022, Linus Groh + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include +#include +#include + +namespace Web::Fetch { + +// We have a handful of enums that have both a generated and a handwritten version, and need to +// convert between some of them. This has three reasons: +// - Some enums have more internal values in the spec than what is exposed to JS. An example of +// this is Request::Destination's ServiceWorker member and Request::Mode's WebSocket member, +// both of which are not present in the IDL-defined enums. +// - The generated enums are not perfect, e.g. "no-cors" becomes NoCors, not NoCORS. This is fine +// for the generated constructor/prototype code, but not great for the remaining handwritten +// code. +// - Fetch has use-cases beyond its JS interface, so having to refer to the 'Bindings' namespace +// constantly is irritating. + +Optional from_bindings_enum(Bindings::ReferrerPolicy referrer_policy) +{ + switch (referrer_policy) { + case Bindings::ReferrerPolicy::Empty: + return {}; + case Bindings::ReferrerPolicy::NoReferrer: + return ReferrerPolicy::ReferrerPolicy::NoReferrer; + case Bindings::ReferrerPolicy::NoReferrerWhenDowngrade: + return ReferrerPolicy::ReferrerPolicy::NoReferrerWhenDowngrade; + case Bindings::ReferrerPolicy::SameOrigin: + return ReferrerPolicy::ReferrerPolicy::SameOrigin; + case Bindings::ReferrerPolicy::Origin: + return ReferrerPolicy::ReferrerPolicy::Origin; + case Bindings::ReferrerPolicy::StrictOrigin: + return ReferrerPolicy::ReferrerPolicy::StrictOrigin; + case Bindings::ReferrerPolicy::OriginWhenCrossOrigin: + return ReferrerPolicy::ReferrerPolicy::OriginWhenCrossOrigin; + case Bindings::ReferrerPolicy::StrictOriginWhenCrossOrigin: + return ReferrerPolicy::ReferrerPolicy::StrictOriginWhenCrossOrigin; + case Bindings::ReferrerPolicy::UnsafeUrl: + return ReferrerPolicy::ReferrerPolicy::UnsafeURL; + default: + VERIFY_NOT_REACHED(); + } +} + +Infrastructure::Request::Mode from_bindings_enum(Bindings::RequestMode mode) +{ + switch (mode) { + case Bindings::RequestMode::SameOrigin: + return Infrastructure::Request::Mode::SameOrigin; + case Bindings::RequestMode::Cors: + return Infrastructure::Request::Mode::CORS; + case Bindings::RequestMode::NoCors: + return Infrastructure::Request::Mode::NoCORS; + case Bindings::RequestMode::Navigate: + return Infrastructure::Request::Mode::Navigate; + default: + VERIFY_NOT_REACHED(); + } +} + +Infrastructure::Request::CredentialsMode from_bindings_enum(Bindings::RequestCredentials request_credentials) +{ + switch (request_credentials) { + case Bindings::RequestCredentials::Omit: + return Infrastructure::Request::CredentialsMode::Omit; + case Bindings::RequestCredentials::SameOrigin: + return Infrastructure::Request::CredentialsMode::SameOrigin; + case Bindings::RequestCredentials::Include: + return Infrastructure::Request::CredentialsMode::Include; + default: + VERIFY_NOT_REACHED(); + } +} + +Infrastructure::Request::CacheMode from_bindings_enum(Bindings::RequestCache request_cache) +{ + switch (request_cache) { + case Bindings::RequestCache::Default: + return Infrastructure::Request::CacheMode::Default; + case Bindings::RequestCache::NoStore: + return Infrastructure::Request::CacheMode::NoStore; + case Bindings::RequestCache::Reload: + return Infrastructure::Request::CacheMode::Reload; + case Bindings::RequestCache::NoCache: + return Infrastructure::Request::CacheMode::NoCache; + case Bindings::RequestCache::ForceCache: + return Infrastructure::Request::CacheMode::ForceCache; + case Bindings::RequestCache::OnlyIfCached: + return Infrastructure::Request::CacheMode::OnlyIfCached; + default: + VERIFY_NOT_REACHED(); + } +} + +Infrastructure::Request::RedirectMode from_bindings_enum(Bindings::RequestRedirect request_redirect) +{ + switch (request_redirect) { + case Bindings::RequestRedirect::Follow: + return Infrastructure::Request::RedirectMode::Follow; + case Bindings::RequestRedirect::Error: + return Infrastructure::Request::RedirectMode::Error; + case Bindings::RequestRedirect::Manual: + return Infrastructure::Request::RedirectMode::Manual; + default: + VERIFY_NOT_REACHED(); + } +} + +Bindings::ReferrerPolicy to_bindings_enum(Optional const& referrer_policy) +{ + if (!referrer_policy.has_value()) + return Bindings::ReferrerPolicy::Empty; + switch (*referrer_policy) { + case ReferrerPolicy::ReferrerPolicy::NoReferrer: + return Bindings::ReferrerPolicy::NoReferrer; + case ReferrerPolicy::ReferrerPolicy::NoReferrerWhenDowngrade: + return Bindings::ReferrerPolicy::NoReferrerWhenDowngrade; + case ReferrerPolicy::ReferrerPolicy::SameOrigin: + return Bindings::ReferrerPolicy::SameOrigin; + case ReferrerPolicy::ReferrerPolicy::Origin: + return Bindings::ReferrerPolicy::Origin; + case ReferrerPolicy::ReferrerPolicy::StrictOrigin: + return Bindings::ReferrerPolicy::StrictOrigin; + case ReferrerPolicy::ReferrerPolicy::OriginWhenCrossOrigin: + return Bindings::ReferrerPolicy::OriginWhenCrossOrigin; + case ReferrerPolicy::ReferrerPolicy::StrictOriginWhenCrossOrigin: + return Bindings::ReferrerPolicy::StrictOriginWhenCrossOrigin; + case ReferrerPolicy::ReferrerPolicy::UnsafeURL: + return Bindings::ReferrerPolicy::UnsafeUrl; + default: + VERIFY_NOT_REACHED(); + } +} + +Bindings::RequestDestination to_bindings_enum(Optional const& destination) +{ + if (!destination.has_value()) + return Bindings::RequestDestination::Empty; + switch (*destination) { + case Infrastructure::Request::Destination::Audio: + return Bindings::RequestDestination::Audio; + case Infrastructure::Request::Destination::AudioWorklet: + return Bindings::RequestDestination::Audioworklet; + case Infrastructure::Request::Destination::Document: + return Bindings::RequestDestination::Document; + case Infrastructure::Request::Destination::Embed: + return Bindings::RequestDestination::Embed; + case Infrastructure::Request::Destination::Font: + return Bindings::RequestDestination::Font; + case Infrastructure::Request::Destination::Frame: + return Bindings::RequestDestination::Frame; + case Infrastructure::Request::Destination::IFrame: + return Bindings::RequestDestination::Iframe; + case Infrastructure::Request::Destination::Image: + return Bindings::RequestDestination::Image; + case Infrastructure::Request::Destination::Manifest: + return Bindings::RequestDestination::Manifest; + case Infrastructure::Request::Destination::Object: + return Bindings::RequestDestination::Object; + case Infrastructure::Request::Destination::PaintWorklet: + return Bindings::RequestDestination::Paintworklet; + case Infrastructure::Request::Destination::Report: + return Bindings::RequestDestination::Report; + case Infrastructure::Request::Destination::Script: + return Bindings::RequestDestination::Script; + case Infrastructure::Request::Destination::ServiceWorker: + // NOTE: "serviceworker" is omitted from RequestDestination as it cannot be observed from JavaScript. + // Implementations will still need to support it as a destination. + VERIFY_NOT_REACHED(); + case Infrastructure::Request::Destination::SharedWorker: + return Bindings::RequestDestination::Sharedworker; + case Infrastructure::Request::Destination::Style: + return Bindings::RequestDestination::Style; + case Infrastructure::Request::Destination::Track: + return Bindings::RequestDestination::Track; + case Infrastructure::Request::Destination::Video: + return Bindings::RequestDestination::Video; + case Infrastructure::Request::Destination::Worker: + return Bindings::RequestDestination::Worker; + case Infrastructure::Request::Destination::XSLT: + return Bindings::RequestDestination::Xslt; + default: + VERIFY_NOT_REACHED(); + } +} + +Bindings::RequestMode to_bindings_enum(Infrastructure::Request::Mode mode) +{ + switch (mode) { + case Infrastructure::Request::Mode::SameOrigin: + return Bindings::RequestMode::SameOrigin; + case Infrastructure::Request::Mode::CORS: + return Bindings::RequestMode::Cors; + case Infrastructure::Request::Mode::NoCORS: + return Bindings::RequestMode::NoCors; + case Infrastructure::Request::Mode::Navigate: + return Bindings::RequestMode::Navigate; + case Infrastructure::Request::Mode::WebSocket: + // NOTE: "websocket" is omitted from RequestMode as it cannot be used nor observed from JavaScript. + VERIFY_NOT_REACHED(); + default: + VERIFY_NOT_REACHED(); + } +} + +Bindings::RequestCredentials to_bindings_enum(Infrastructure::Request::CredentialsMode credentials_mode) +{ + switch (credentials_mode) { + case Infrastructure::Request::CredentialsMode::Omit: + return Bindings::RequestCredentials::Omit; + case Infrastructure::Request::CredentialsMode::SameOrigin: + return Bindings::RequestCredentials::SameOrigin; + case Infrastructure::Request::CredentialsMode::Include: + return Bindings::RequestCredentials::Include; + default: + VERIFY_NOT_REACHED(); + } +} + +Bindings::RequestCache to_bindings_enum(Infrastructure::Request::CacheMode cache_mode) +{ + switch (cache_mode) { + case Infrastructure::Request::CacheMode::Default: + return Bindings::RequestCache::Default; + case Infrastructure::Request::CacheMode::NoStore: + return Bindings::RequestCache::NoStore; + case Infrastructure::Request::CacheMode::Reload: + return Bindings::RequestCache::Reload; + case Infrastructure::Request::CacheMode::NoCache: + return Bindings::RequestCache::NoCache; + case Infrastructure::Request::CacheMode::ForceCache: + return Bindings::RequestCache::ForceCache; + case Infrastructure::Request::CacheMode::OnlyIfCached: + return Bindings::RequestCache::OnlyIfCached; + default: + VERIFY_NOT_REACHED(); + } +} + +Bindings::RequestRedirect to_bindings_enum(Infrastructure::Request::RedirectMode redirect_mode) +{ + switch (redirect_mode) { + case Infrastructure::Request::RedirectMode::Follow: + return Bindings::RequestRedirect::Follow; + case Infrastructure::Request::RedirectMode::Error: + return Bindings::RequestRedirect::Error; + case Infrastructure::Request::RedirectMode::Manual: + return Bindings::RequestRedirect::Manual; + default: + VERIFY_NOT_REACHED(); + } +} + +} diff --git a/Userland/Libraries/LibWeb/Fetch/Enums.h b/Userland/Libraries/LibWeb/Fetch/Enums.h new file mode 100644 index 00000000000..f74921dd1b1 --- /dev/null +++ b/Userland/Libraries/LibWeb/Fetch/Enums.h @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2022, Linus Groh + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include +#include + +namespace Web::Fetch { + +[[nodiscard]] Optional from_bindings_enum(Bindings::ReferrerPolicy); +[[nodiscard]] Infrastructure::Request::Mode from_bindings_enum(Bindings::RequestMode); +[[nodiscard]] Infrastructure::Request::CredentialsMode from_bindings_enum(Bindings::RequestCredentials); +[[nodiscard]] Infrastructure::Request::CacheMode from_bindings_enum(Bindings::RequestCache); +[[nodiscard]] Infrastructure::Request::RedirectMode from_bindings_enum(Bindings::RequestRedirect); + +[[nodiscard]] Bindings::ReferrerPolicy to_bindings_enum(Optional const&); +[[nodiscard]] Bindings::RequestDestination to_bindings_enum(Optional const&); +[[nodiscard]] Bindings::RequestMode to_bindings_enum(Infrastructure::Request::Mode); +[[nodiscard]] Bindings::RequestCredentials to_bindings_enum(Infrastructure::Request::CredentialsMode); +[[nodiscard]] Bindings::RequestCache to_bindings_enum(Infrastructure::Request::CacheMode); +[[nodiscard]] Bindings::RequestRedirect to_bindings_enum(Infrastructure::Request::RedirectMode); + +} diff --git a/Userland/Libraries/LibWeb/Fetch/Headers.h b/Userland/Libraries/LibWeb/Fetch/Headers.h index 98c5914538e..339a4ac2eea 100644 --- a/Userland/Libraries/LibWeb/Fetch/Headers.h +++ b/Userland/Libraries/LibWeb/Fetch/Headers.h @@ -42,6 +42,8 @@ public: [[nodiscard]] Guard guard() const { return m_guard; } void set_guard(Guard guard) { m_guard = guard; } + WebIDL::ExceptionOr fill(HeadersInit const&); + // JS API functions WebIDL::ExceptionOr append(Infrastructure::Header); WebIDL::ExceptionOr append(String const& name, String const& value); @@ -58,7 +60,6 @@ private: explicit Headers(HTML::Window&); - WebIDL::ExceptionOr fill(HeadersInit const&); void remove_privileged_no_cors_headers(); // https://fetch.spec.whatwg.org/#concept-headers-header-list diff --git a/Userland/Libraries/LibWeb/Fetch/Infrastructure/HTTP/Headers.h b/Userland/Libraries/LibWeb/Fetch/Infrastructure/HTTP/Headers.h index e1461120aa2..dda8bcbc954 100644 --- a/Userland/Libraries/LibWeb/Fetch/Infrastructure/HTTP/Headers.h +++ b/Userland/Libraries/LibWeb/Fetch/Infrastructure/HTTP/Headers.h @@ -31,6 +31,7 @@ class HeaderList final , Vector
{ public: using Vector::begin; + using Vector::clear; using Vector::end; [[nodiscard]] bool contains(ReadonlyBytes) const; diff --git a/Userland/Libraries/LibWeb/Fetch/Infrastructure/HTTP/Requests.cpp b/Userland/Libraries/LibWeb/Fetch/Infrastructure/HTTP/Requests.cpp index 793f1ed1bbe..78afd24d79d 100644 --- a/Userland/Libraries/LibWeb/Fetch/Infrastructure/HTTP/Requests.cpp +++ b/Userland/Libraries/LibWeb/Fetch/Infrastructure/HTTP/Requests.cpp @@ -36,7 +36,8 @@ void Request::set_url(AK::URL url) // Sometimes setting the URL and URL list are done as two distinct steps in the spec, // but since we know the URL is always the URL list's first item and doesn't change later // on, we can combine them. - VERIFY(m_url_list.is_empty()); + if (!m_url_list.is_empty()) + m_url_list.clear(); m_url_list.append(move(url)); } diff --git a/Userland/Libraries/LibWeb/Fetch/Request.cpp b/Userland/Libraries/LibWeb/Fetch/Request.cpp new file mode 100644 index 00000000000..dd6c52d4a11 --- /dev/null +++ b/Userland/Libraries/LibWeb/Fetch/Request.cpp @@ -0,0 +1,649 @@ +/* + * Copyright (c) 2022, Linus Groh + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Web::Fetch { + +Request::Request(JS::Realm& realm, NonnullOwnPtr request) + : PlatformObject(realm) + , m_request(move(request)) +{ + auto& window = verify_cast(realm.global_object()); + set_prototype(&window.cached_web_prototype("Request")); +} + +Request::~Request() = default; + +void Request::visit_edges(Cell::Visitor& visitor) +{ + Base::visit_edges(visitor); + visitor.visit(m_headers); + visitor.visit(m_signal); +} + +// https://fetch.spec.whatwg.org/#concept-body-mime-type +// https://fetch.spec.whatwg.org/#ref-for-concept-body-mime-type%E2%91%A0 +Optional Request::mime_type_impl() const +{ + // Objects including the Body interface mixin need to define an associated MIME type algorithm which takes no arguments and returns failure or a MIME type. + // A Request object’s MIME type is to return the result of extracting a MIME type from its request’s header list. + return m_request->header_list()->extract_mime_type(); +} + +// https://fetch.spec.whatwg.org/#concept-body-body +// https://fetch.spec.whatwg.org/#ref-for-concept-body-body%E2%91%A7 +Optional Request::body_impl() const +{ + // Objects including the Body interface mixin have an associated body (null or a body). + // A Request object’s body is its request’s body. + return m_request->body().visit( + [](Infrastructure::Body const& b) -> Optional { return b; }, + [](Empty) -> Optional { return {}; }, + // A byte sequence will be safely extracted into a body early on in fetch. + [](ByteBuffer const&) -> Optional { 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 Request::body_impl() +{ + // Objects including the Body interface mixin have an associated body (null or a body). + // A Request object’s body is its request’s body. + return m_request->body().visit( + [](Infrastructure::Body& b) -> Optional { return b; }, + [](Empty) -> Optional { return {}; }, + // A byte sequence will be safely extracted into a body early on in fetch. + [](ByteBuffer&) -> Optional { VERIFY_NOT_REACHED(); }); +} + +// https://fetch.spec.whatwg.org/#request-create +JS::NonnullGCPtr Request::create(NonnullOwnPtr request, Headers::Guard guard, JS::Realm& realm) +{ + auto& window = verify_cast(realm.global_object()); + + // Copy a NonnullRefPtr to the request's header list before request is being move()'d. + auto request_reader_list = request->header_list(); + + // 1. Let requestObject be a new Request object with realm. + // 2. Set requestObject’s request to request. + auto* request_object = realm.heap().allocate(realm, realm, move(request)); + + // 3. Set requestObject’s headers to a new Headers object with realm, whose headers list is request’s headers list and guard is guard. + request_object->m_headers = realm.heap().allocate(realm, window); + request_object->m_headers->set_header_list(move(request_reader_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, window); + + // 5. Return requestObject. + return JS::NonnullGCPtr { *request_object }; +} + +// https://fetch.spec.whatwg.org/#dom-request +WebIDL::ExceptionOr> Request::create_with_global_object(HTML::Window& html_window, RequestInfo const& input, RequestInit const& init) +{ + auto& realm = html_window.realm(); + + // Referred to as 'this' in the spec. + auto request_object = [&] { + auto request = adopt_own(*new Infrastructure::Request()); + return JS::NonnullGCPtr { *realm.heap().allocate(realm, realm, move(request)) }; + }(); + + // 1. Let request be null. + Infrastructure::Request const* input_request = nullptr; + + // Cleanup for the special case where we create a temporary Request ourselves + // instead of just taking a pointer from something else. + ArmedScopeGuard delete_input_request { [&] { + delete input_request; + } }; + + // 2. Let fallbackMode be null. + Optional fallback_mode; + + // 3. Let baseURL be this’s relevant settings object’s API base URL. + auto base_url = HTML::relevant_settings_object(*request_object).api_base_url(); + + // 4. Let signal be null. + DOM::AbortSignal const* input_signal = nullptr; + + // 5. If input is a string, then: + if (input.has()) { + // 1. Let parsedURL be the result of parsing input with baseURL. + auto parsed_url = URLParser::parse(input.get(), &base_url); + + // 2. If parsedURL is failure, then throw a TypeError. + if (!parsed_url.is_valid()) + return WebIDL::SimpleException { WebIDL::SimpleExceptionType::TypeError, "Input URL is not valid"sv }; + + // 3. If parsedURL includes credentials, then throw a TypeError. + if (parsed_url.includes_credentials()) + return WebIDL::SimpleException { WebIDL::SimpleExceptionType::TypeError, "Input URL must not include credentials"sv }; + + // 4. Set request to a new request whose URL is parsedURL. + auto* new_request = new Infrastructure::Request(); + new_request->set_url(move(parsed_url)); + input_request = new_request; + + // 5. Set fallbackMode to "cors". + fallback_mode = Infrastructure::Request::Mode::CORS; + } + // 6. Otherwise: + else { + // 1. Assert: input is a Request object. + VERIFY(input.has>()); + + // 2. Set request to input’s request. + input_request = &input.get>()->request(); + delete_input_request.disarm(); + + // 3. Set signal to input’s signal. + input_signal = input.get>()->signal(); + } + + // 7. Let origin be this’s relevant settings object’s origin. + auto const& origin = HTML::relevant_settings_object(*request_object).origin(); + + // 8. Let window be "client". + auto window = Infrastructure::Request::WindowType { Infrastructure::Request::Window::Client }; + + // 9. If request’s window is an environment settings object and its origin is same origin with origin, then set window to request’s window. + if (input_request->window().has()) { + auto* eso = input_request->window().get(); + if (eso->origin().is_same_origin(origin)) + window = input_request->window(); + } + + // 10. If init["window"] exists and is non-null, then throw a TypeError. + if (init.window.has_value() && !init.window->is_null()) + return WebIDL::SimpleException { WebIDL::SimpleExceptionType::TypeError, "The 'window' property must be omitted or null"sv }; + + // 11. If init["window"] exists, then set window to "no-window". + if (init.window.has_value()) + window = Infrastructure::Request::Window::NoWindow; + + // 12. Set request to a new request with the following properties: + // NOTE: This is done at the beginning as the 'this' value Request object + // cannot exist with a null Infrastructure::Request. + auto& request = request_object->request(); + + // URL + // request’s URL. + request.set_url(input_request->url()); + + // method + // request’s method. + request.set_method(TRY_OR_RETURN_OOM(html_window, ByteBuffer::copy(request.method()))); + + // header list + // A copy of request’s header list. + auto header_list_copy = make_ref_counted(); + for (auto& header : *request.header_list()) + TRY_OR_RETURN_OOM(html_window, header_list_copy->append(header)); + request.set_header_list(move(header_list_copy)); + + // unsafe-request flag + // Set. + request.set_unsafe_request(true); + + // client + // This’s relevant settings object. + request.set_client(HTML::relevant_settings_object(*request_object)); + + // window + // window. + request.set_window(window); + + // priority + // request’s priority. + request.set_priority(input_request->priority()); + + // origin + // request’s origin. The propagation of the origin is only significant for navigation requests being handled by a service worker. In this scenario a request can have an origin that is different from the current client. + request.set_origin(input_request->origin()); + + // referrer + // request’s referrer. + request.set_referrer(input_request->referrer()); + + // referrer policy + // request’s referrer policy. + request.set_referrer_policy(input_request->referrer_policy()); + + // mode + // request’s mode. + request.set_mode(input_request->mode()); + + // credentials mode + // request’s credentials mode. + request.set_credentials_mode(input_request->credentials_mode()); + + // cache mode + // request’s cache mode. + request.set_cache_mode(input_request->cache_mode()); + + // redirect mode + // request’s redirect mode. + request.set_redirect_mode(input_request->redirect_mode()); + + // integrity metadata + // request’s integrity metadata. + request.set_integrity_metadata(input_request->integrity_metadata()); + + // keepalive + // request’s keepalive. + request.set_keepalive(input_request->keepalive()); + + // reload-navigation flag + // request’s reload-navigation flag. + request.set_reload_navigation(input_request->reload_navigation()); + + // history-navigation flag + // request’s history-navigation flag. + request.set_history_navigation(input_request->history_navigation()); + + // URL list + // A clone of request’s URL list. + request.set_url_list(input_request->url_list()); + + // initiator type + // "fetch". + request.set_initiator_type(Infrastructure::Request::InitiatorType::Fetch); + + // 13. If init is not empty, then: + if (!init.is_empty()) { + // 1. If request’s mode is "navigate", then set it to "same-origin". + if (request.mode() == Infrastructure::Request::Mode::Navigate) + request.set_mode(Infrastructure::Request::Mode::SameOrigin); + + // 2. Unset request’s reload-navigation flag. + request.set_reload_navigation(false); + + // 3. Unset request’s history-navigation flag. + request.set_history_navigation(false); + + // 4. Set request’s origin to "client". + request.set_origin(Infrastructure::Request::Origin::Client); + + // 5. Set request’s referrer to "client". + request.set_referrer(Infrastructure::Request::Referrer::Client); + + // 6. Set request’s referrer policy to the empty string. + request.set_referrer_policy({}); + + // 7. Set request’s URL to request’s current URL. + request.set_url(request.current_url()); + + // 8. Set request’s URL list to « request’s URL ». + // NOTE: This is done implicitly by assigning the initial URL above. + } + + // 14. If init["referrer"] exists, then: + if (init.referrer.has_value()) { + // 1. Let referrer be init["referrer"]. + auto const& referrer = *init.referrer; + + // 2. If referrer is the empty string, then set request’s referrer to "no-referrer". + if (referrer.is_empty()) { + request.set_referrer(Infrastructure::Request::Referrer::NoReferrer); + } + // 3. Otherwise: + else { + // 1. Let parsedReferrer be the result of parsing referrer with baseURL. + auto parsed_referrer = URLParser::parse(referrer, &base_url); + + // 2. If parsedReferrer is failure, then throw a TypeError. + if (!parsed_referrer.is_valid()) + return WebIDL::SimpleException { WebIDL::SimpleExceptionType::TypeError, "Referrer must be a valid URL"sv }; + + // 3. If one of the following is true + // - parsedReferrer’s scheme is "about" and path is the string "client" + // - parsedReferrer’s origin is not same origin with origin + // then set request’s referrer to "client". + // FIXME: Actually use the given origin once we have https://url.spec.whatwg.org/#concept-url-origin. + if ((parsed_referrer.scheme() == "about"sv && parsed_referrer.path() == "client"sv) || !HTML::Origin().is_same_origin(origin)) { + request.set_referrer(Infrastructure::Request::Referrer::Client); + } + // 4. Otherwise, set request’s referrer to parsedReferrer. + else { + request.set_referrer(move(parsed_referrer)); + } + } + } + + // 15. If init["referrerPolicy"] exists, then set request’s referrer policy to it. + if (init.referrer_policy.has_value()) + request.set_referrer_policy(from_bindings_enum(*init.referrer_policy)); + + // 16. Let mode be init["mode"] if it exists, and fallbackMode otherwise. + auto mode = init.mode.has_value() + ? from_bindings_enum(*init.mode) + : fallback_mode; + + // 17. If mode is "navigate", then throw a TypeError. + if (mode == Infrastructure::Request::Mode::Navigate) + return WebIDL::SimpleException { WebIDL::SimpleExceptionType::TypeError, "Mode must not be 'navigate"sv }; + + // 18. If mode is non-null, set request’s mode to mode. + if (mode.has_value()) + request.set_mode(*mode); + + // 19. If init["credentials"] exists, then set request’s credentials mode to it. + if (init.credentials.has_value()) + request.set_credentials_mode(from_bindings_enum(*init.credentials)); + + // 20. If init["cache"] exists, then set request’s cache mode to it. + if (init.cache.has_value()) + request.set_cache_mode(from_bindings_enum(*init.cache)); + + // 21. If request’s cache mode is "only-if-cached" and request’s mode is not "same-origin", then throw a TypeError. + if (request.cache_mode() == Infrastructure::Request::CacheMode::OnlyIfCached && request.mode() != Infrastructure::Request::Mode::SameOrigin) + return WebIDL::SimpleException { WebIDL::SimpleExceptionType::TypeError, "Mode must be 'same-origin' when cache mode is 'only-if-cached'"sv }; + + // 22. If init["redirect"] exists, then set request’s redirect mode to it. + if (init.redirect.has_value()) + request.set_redirect_mode(from_bindings_enum(*init.redirect)); + + // 23. If init["integrity"] exists, then set request’s integrity metadata to it. + if (init.integrity.has_value()) + request.set_integrity_metadata(*init.integrity); + + // 24. If init["keepalive"] exists, then set request’s keepalive to it. + if (init.keepalive.has_value()) + request.set_keepalive(*init.keepalive); + + // 25. If init["method"] exists, then: + if (init.method.has_value()) { + // 1. Let method be init["method"]. + auto method = *init.method; + + // 2. If method is not a method or method is a forbidden method, then throw a TypeError. + if (!Infrastructure::is_method(method.bytes())) + return WebIDL::SimpleException { WebIDL::SimpleExceptionType::TypeError, "Method has invalid value"sv }; + if (Infrastructure::is_forbidden_method(method.bytes())) + return WebIDL::SimpleException { WebIDL::SimpleExceptionType::TypeError, "Method must not be one of CONNECT, TRACE, or TRACK"sv }; + + // 3. Normalize method. + method = TRY_OR_RETURN_OOM(html_window, Infrastructure::normalize_method(method.bytes())); + + // 4. Set request’s method to method. + request.set_method(MUST(ByteBuffer::copy(method.bytes()))); + } + + // 26. If init["signal"] exists, then set signal to it. + if (init.signal.has_value()) + input_signal = *init.signal; + + // 27. 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. + request_object->m_signal = realm.heap().allocate(HTML::relevant_realm(*request_object), html_window); + + // 29. If signal is not null, then make this’s signal follow signal. + if (input_signal != nullptr) { + // FIXME: Our AbortSignal doesn't support 'following' yet. + } + + // 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". + request_object->m_headers = realm.heap().allocate(realm, html_window); + request_object->m_headers->set_header_list(request.header_list()); + request_object->m_headers->set_guard(Headers::Guard::Request); + + // 31. 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())) + return WebIDL::SimpleException { WebIDL::SimpleExceptionType::TypeError, "Method must be one of GET, HEAD, or POST"sv }; + + // 2. Set this’s headers’s guard to "request-no-cors". + request_object->headers()->set_guard(Headers::Guard::RequestNoCORS); + } + + // 32. 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() }; + + // 2. If init["headers"] exists, then set headers to init["headers"]. + if (init.headers.has_value()) + headers = *init.headers; + + // 3. Empty this’s headers’s header list. + request_object->headers()->header_list()->clear(); + + // 4. If headers is a Headers object, then for each header in its header list, append (header’s name, header’s value) to this’s headers. + if (headers.has>()) { + auto& header_list = *headers.get>(); + for (auto& header : header_list) + TRY(request_object->headers()->append(String::copy(header.name), String::copy(header.value))); + } + // 5. Otherwise, fill this’s headers with headers. + else { + request_object->headers()->fill(headers.get()); + } + } + + // 33. Let inputBody be input’s request’s body if input is a Request object; otherwise null. + auto const& input_body = input.has>() + ? input.get>()->request().body().get() + : Optional {}; + + // 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. + if (((init.body.has_value() && (*init.body).has_value()) || input_body.has_value()) && 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. + Optional init_body; + + // 36. 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())); + + // 2. Set initBody to bodyWithType’s body. + init_body = move(body_with_type.body); + + // 3. Let type be bodyWithType’s type. + auto const& type = body_with_type.type; + + // 4. If type is non-null and this’s headers’s header list does not contain `Content-Type`, then append (`Content-Type`, type) to this’s headers. + if (type.has_value() && !request_object->headers()->header_list()->contains("Content-Type"sv.bytes())) + TRY(request_object->headers()->append("Content-Type"sv, String::copy(type->span()))); + } + + // 37. Let inputOrInitBody be initBody if it is non-null; otherwise inputBody. + auto const& input_or_init_body = init_body.has_value() ? init_body : input_body; + + // 38. If inputOrInitBody is non-null and inputOrInitBody’s source is null, then: + if (input_or_init_body.has_value() && input_or_init_body->source().has()) { + // 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()) + return WebIDL::SimpleException { WebIDL::SimpleExceptionType::TypeError, "Body without source requires 'duplex' value to be set"sv }; + + // 2. If this’s request’s mode is neither "same-origin" nor "cors", then throw a TypeError. + if (request_object->request().mode() != Infrastructure::Request::Mode::SameOrigin && request_object->request().mode() != Infrastructure::Request::Mode::CORS) + return WebIDL::SimpleException { WebIDL::SimpleExceptionType::TypeError, "Request mode must be 'same-origin' or 'cors'"sv }; + + // 3. Set this’s request’s use-CORS-preflight flag. + request_object->request().set_use_cors_preflight(true); + } + + // 39. Let finalBody be inputOrInitBody. + 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()) { + // 2. If input is unusable, then throw a TypeError. + if (input.has>() && input.get>()->is_unusable()) + return WebIDL::SimpleException { WebIDL::SimpleExceptionType::TypeError, "Request is unusable"sv }; + + // FIXME: 2. Set finalBody to the result of creating a proxy for inputBody. + } + + // 41. Set this’s request’s body to finalBody. + if (final_body.has_value()) + request_object->request().set_body(*final_body); + + return JS::NonnullGCPtr { *request_object }; +} + +// https://fetch.spec.whatwg.org/#dom-request-method +String Request::method() const +{ + // The method getter steps are to return this’s request’s method. + return String::copy(m_request->method()); +} + +// https://fetch.spec.whatwg.org/#dom-request-url +String Request::url() const +{ + // The url getter steps are to return this’s request’s URL, serialized. + return m_request->url().serialize(); +} + +// https://fetch.spec.whatwg.org/#dom-request-headers +JS::NonnullGCPtr Request::headers() const +{ + // The headers getter steps are to return this’s headers. + return *m_headers; +} + +// https://fetch.spec.whatwg.org/#dom-request-destination +Bindings::RequestDestination Request::destination() const +{ + // The destination getter are to return this’s request’s destination. + return to_bindings_enum(m_request->destination()); +} + +// https://fetch.spec.whatwg.org/#dom-request-referrer +String Request::referrer() const +{ + return m_request->referrer().visit( + [&](Infrastructure::Request::Referrer const& referrer) { + switch (referrer) { + // 1. If this’s request’s referrer is "no-referrer", then return the empty string. + case Infrastructure::Request::Referrer::NoReferrer: + return String::empty(); + // 2. If this’s request’s referrer is "client", then return "about:client". + case Infrastructure::Request::Referrer::Client: + return String { "about:client"sv }; + default: + VERIFY_NOT_REACHED(); + } + }, + [&](AK::URL const& url) { + // 3. Return this’s request’s referrer, serialized. + return url.serialize(); + }); +} + +// https://fetch.spec.whatwg.org/#dom-request-referrerpolicy +Bindings::ReferrerPolicy Request::referrer_policy() const +{ + // The referrerPolicy getter steps are to return this’s request’s referrer policy. + return to_bindings_enum(m_request->referrer_policy()); +} + +// https://fetch.spec.whatwg.org/#dom-request-mode +Bindings::RequestMode Request::mode() const +{ + // The mode getter steps are to return this’s request’s mode. + return to_bindings_enum(m_request->mode()); +} + +// https://fetch.spec.whatwg.org/#dom-request-credentials +Bindings::RequestCredentials Request::credentials() const +{ + // The credentials getter steps are to return this’s request’s credentials mode. + return to_bindings_enum(m_request->credentials_mode()); +} + +// https://fetch.spec.whatwg.org/#dom-request-cache +Bindings::RequestCache Request::cache() const +{ + // The cache getter steps are to return this’s request’s cache mode. + return to_bindings_enum(m_request->cache_mode()); +} + +// https://fetch.spec.whatwg.org/#dom-request-redirect +Bindings::RequestRedirect Request::redirect() const +{ + // The redirect getter steps are to return this’s request’s redirect mode. + return to_bindings_enum(m_request->redirect_mode()); +} + +// https://fetch.spec.whatwg.org/#dom-request-integrity +String Request::integrity() const +{ + // The integrity getter steps are to return this’s request’s integrity metadata. + return m_request->integrity_metadata(); +} + +// https://fetch.spec.whatwg.org/#dom-request-keepalive +bool Request::keepalive() const +{ + // The keepalive getter steps are to return this’s request’s keepalive. + return m_request->keepalive(); +} + +// https://fetch.spec.whatwg.org/#dom-request-isreloadnavigation +bool Request::is_reload_navigation() const +{ + // The isReloadNavigation getter steps are to return true if this’s request’s reload-navigation flag is set; otherwise false. + return m_request->reload_navigation(); +} + +// https://fetch.spec.whatwg.org/#dom-request-ishistorynavigation +bool Request::is_history_navigation() const +{ + // The isHistoryNavigation getter steps are to return true if this’s request’s history-navigation flag is set; otherwise false. + return m_request->history_navigation(); +} + +// https://fetch.spec.whatwg.org/#dom-request-signal +JS::NonnullGCPtr Request::signal() const +{ + // The signal getter steps are to return this’s signal. + return *m_signal; +} + +// https://fetch.spec.whatwg.org/#dom-request-clone +WebIDL::ExceptionOr> Request::clone() const +{ + // 1. If this is unusable, then throw a TypeError. + if (is_unusable()) + return WebIDL::SimpleException { WebIDL::SimpleExceptionType::TypeError, "Request is unusable"sv }; + + // 2. Let clonedRequest be the result of cloning this’s request. + auto cloned_request = TRY(m_request->clone()); + + // 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(move(cloned_request), m_headers->guard(), HTML::relevant_realm(*this)); + + // FIXME: 4. Make clonedRequestObject’s signal follow this’s signal. + + // 5. Return clonedRequestObject. + return cloned_request_object; +} + +} diff --git a/Userland/Libraries/LibWeb/Fetch/Request.h b/Userland/Libraries/LibWeb/Fetch/Request.h new file mode 100644 index 00000000000..a1236c57ed2 --- /dev/null +++ b/Userland/Libraries/LibWeb/Fetch/Request.h @@ -0,0 +1,115 @@ +/* + * Copyright (c) 2022, Linus Groh + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +namespace Web::Fetch { + +// https://fetch.spec.whatwg.org/#requestinfo +using RequestInfo = Variant, String>; + +// https://fetch.spec.whatwg.org/#requestinit +struct RequestInit { + Optional method; + Optional headers; + Optional> body; + Optional referrer; + Optional referrer_policy; + Optional mode; + Optional credentials; + Optional cache; + Optional redirect; + Optional integrity; + Optional keepalive; + Optional> signal; + Optional duplex; + Optional window; + + // https://infra.spec.whatwg.org/#map-is-empty + bool is_empty() const + { + return !(method.has_value() + || headers.has_value() + || body.has_value() + || referrer.has_value() + || referrer_policy.has_value() + || mode.has_value() + || credentials.has_value() + || cache.has_value() + || redirect.has_value() + || integrity.has_value() + || keepalive.has_value() + || signal.has_value() + || duplex.has_value() + || window.has_value()); + } +}; + +// https://fetch.spec.whatwg.org/#request +class Request final + : public Bindings::PlatformObject + , public BodyMixin { + WEB_PLATFORM_OBJECT(Request, Bindings::PlatformObject); + +public: + static JS::NonnullGCPtr create(NonnullOwnPtr, Headers::Guard, JS::Realm&); + static WebIDL::ExceptionOr> create_with_global_object(HTML::Window&, RequestInfo const& input, RequestInit const& init = {}); + + virtual ~Request() override; + + // ^BodyMixin + virtual Optional mime_type_impl() const override; + virtual Optional body_impl() override; + virtual Optional body_impl() const override; + + [[nodiscard]] Infrastructure::Request& request() { return *m_request; } + [[nodiscard]] Infrastructure::Request const& request() const { return *m_request; } + + // JS API functions + [[nodiscard]] String method() const; + [[nodiscard]] String url() const; + [[nodiscard]] JS::NonnullGCPtr headers() const; + [[nodiscard]] Bindings::RequestDestination destination() const; + [[nodiscard]] String referrer() const; + [[nodiscard]] Bindings::ReferrerPolicy referrer_policy() const; + [[nodiscard]] Bindings::RequestMode mode() const; + [[nodiscard]] Bindings::RequestCredentials credentials() const; + [[nodiscard]] Bindings::RequestCache cache() const; + [[nodiscard]] Bindings::RequestRedirect redirect() const; + [[nodiscard]] String integrity() const; + [[nodiscard]] bool keepalive() const; + [[nodiscard]] bool is_reload_navigation() const; + [[nodiscard]] bool is_history_navigation() const; + [[nodiscard]] JS::NonnullGCPtr signal() const; + [[nodiscard]] WebIDL::ExceptionOr> clone() const; + +private: + Request(JS::Realm&, NonnullOwnPtr); + + virtual void visit_edges(Cell::Visitor&) override; + + // https://fetch.spec.whatwg.org/#concept-request-request + // A Request object has an associated request (a request). + NonnullOwnPtr m_request; + + // https://fetch.spec.whatwg.org/#request-headers + // A Request object also has an associated headers (null or a Headers object), initially null. + JS::GCPtr m_headers; + + // https://fetch.spec.whatwg.org/#request-signal + // A Request object has an associated signal (null or an AbortSignal object), initially null. + JS::GCPtr m_signal; +}; + +} diff --git a/Userland/Libraries/LibWeb/Fetch/Request.idl b/Userland/Libraries/LibWeb/Fetch/Request.idl new file mode 100644 index 00000000000..e0f6bb7049d --- /dev/null +++ b/Userland/Libraries/LibWeb/Fetch/Request.idl @@ -0,0 +1,69 @@ +#import +#import +#import +#import + +typedef (Request or USVString) RequestInfo; + +// https://fetch.spec.whatwg.org/#request +[Exposed=(Window,Worker)] +interface Request { + constructor(RequestInfo input, optional RequestInit init = {}); + + readonly attribute ByteString method; + readonly attribute USVString url; + [SameObject] readonly attribute Headers headers; + + readonly attribute RequestDestination destination; + readonly attribute USVString referrer; + readonly attribute ReferrerPolicy referrerPolicy; + readonly attribute RequestMode mode; + readonly attribute RequestCredentials credentials; + readonly attribute RequestCache cache; + readonly attribute RequestRedirect redirect; + readonly attribute DOMString integrity; + readonly attribute boolean keepalive; + readonly attribute boolean isReloadNavigation; + readonly attribute boolean isHistoryNavigation; + readonly attribute AbortSignal signal; + + [NewObject] Request clone(); +}; +Request includes Body; + +dictionary RequestInit { + ByteString method; + HeadersInit headers; + BodyInit? body; + USVString referrer; + ReferrerPolicy referrerPolicy; + RequestMode mode; + RequestCredentials credentials; + RequestCache cache; + RequestRedirect redirect; + DOMString integrity; + boolean keepalive; + AbortSignal? signal; + RequestDuplex duplex; + any window; // can only be set to null +}; + +enum RequestDestination { "", "audio", "audioworklet", "document", "embed", "font", "frame", "iframe", "image", "manifest", "object", "paintworklet", "report", "script", "sharedworker", "style", "track", "video", "worker", "xslt" }; +enum RequestMode { "navigate", "same-origin", "no-cors", "cors" }; +enum RequestCredentials { "omit", "same-origin", "include" }; +enum RequestCache { "default", "no-store", "reload", "no-cache", "force-cache", "only-if-cached" }; +enum RequestRedirect { "follow", "error", "manual" }; +enum RequestDuplex { "half" }; + +// https://w3c.github.io/webappsec-referrer-policy/#enumdef-referrerpolicy +enum ReferrerPolicy { + "", + "no-referrer", + "no-referrer-when-downgrade", + "same-origin", + "origin", + "strict-origin", + "origin-when-cross-origin", + "strict-origin-when-cross-origin", + "unsafe-url" +}; diff --git a/Userland/Libraries/LibWeb/Forward.h b/Userland/Libraries/LibWeb/Forward.h index 27a27848214..fff7f0e60be 100644 --- a/Userland/Libraries/LibWeb/Forward.h +++ b/Userland/Libraries/LibWeb/Forward.h @@ -183,6 +183,7 @@ namespace Web::Fetch { class BodyMixin; class Headers; class HeadersIterator; +class Request; } namespace Web::Fetch::Infrastructure { @@ -481,6 +482,13 @@ enum class CanPlayTypeResult; enum class CanvasFillRule; enum class EndingType; enum class DOMParserSupportedType; +enum class ReferrerPolicy; +enum class RequestDestination; +enum class RequestMode; +enum class RequestCredentials; +enum class RequestCache; +enum class RequestRedirect; +enum class RequestDuplex; enum class ResizeObserverBoxOptions; enum class XMLHttpRequestResponseType; } diff --git a/Userland/Libraries/LibWeb/idl_files.cmake b/Userland/Libraries/LibWeb/idl_files.cmake index 815b9dd435c..0f82d23a000 100644 --- a/Userland/Libraries/LibWeb/idl_files.cmake +++ b/Userland/Libraries/LibWeb/idl_files.cmake @@ -53,6 +53,7 @@ libweb_js_bindings(DOMParsing/XMLSerializer) libweb_js_bindings(Encoding/TextDecoder) libweb_js_bindings(Encoding/TextEncoder) libweb_js_bindings(Fetch/Headers ITERABLE) +libweb_js_bindings(Fetch/Request) libweb_js_bindings(FileAPI/Blob) libweb_js_bindings(FileAPI/File) libweb_js_bindings(Geometry/DOMPoint)