summaryrefslogtreecommitdiff
path: root/Userland/Libraries
diff options
context:
space:
mode:
authorLinus Groh <mail@linusgroh.de>2022-09-25 19:36:30 +0100
committerLinus Groh <mail@linusgroh.de>2022-09-27 14:56:17 +0100
commit0b52b883af2989cc43b75db4835325a2c31e71d5 (patch)
treeb9a7fb6c90d589dbe4055e9dfb8855da78a0d1b9 /Userland/Libraries
parent9fb672e9815a240fcefbd948806351c7e943922a (diff)
downloadserenity-0b52b883af2989cc43b75db4835325a2c31e71d5.zip
LibWeb: Implement '5.5. Response class' from the Fetch API :^)
Diffstat (limited to 'Userland/Libraries')
-rw-r--r--Userland/Libraries/LibWeb/Bindings/WindowObjectHelper.h3
-rw-r--r--Userland/Libraries/LibWeb/CMakeLists.txt1
-rw-r--r--Userland/Libraries/LibWeb/Fetch/Enums.cpp21
-rw-r--r--Userland/Libraries/LibWeb/Fetch/Enums.h2
-rw-r--r--Userland/Libraries/LibWeb/Fetch/Response.cpp309
-rw-r--r--Userland/Libraries/LibWeb/Fetch/Response.h78
-rw-r--r--Userland/Libraries/LibWeb/Fetch/Response.idl32
-rw-r--r--Userland/Libraries/LibWeb/Forward.h2
-rw-r--r--Userland/Libraries/LibWeb/idl_files.cmake1
9 files changed, 449 insertions, 0 deletions
diff --git a/Userland/Libraries/LibWeb/Bindings/WindowObjectHelper.h b/Userland/Libraries/LibWeb/Bindings/WindowObjectHelper.h
index ec05217967..84f60d0932 100644
--- a/Userland/Libraries/LibWeb/Bindings/WindowObjectHelper.h
+++ b/Userland/Libraries/LibWeb/Bindings/WindowObjectHelper.h
@@ -307,6 +307,8 @@
#include <LibWeb/Bindings/RequestPrototype.h>
#include <LibWeb/Bindings/ResizeObserverConstructor.h>
#include <LibWeb/Bindings/ResizeObserverPrototype.h>
+#include <LibWeb/Bindings/ResponseConstructor.h>
+#include <LibWeb/Bindings/ResponsePrototype.h>
#include <LibWeb/Bindings/SVGAnimatedLengthConstructor.h>
#include <LibWeb/Bindings/SVGAnimatedLengthPrototype.h>
#include <LibWeb/Bindings/SVGCircleElementConstructor.h>
@@ -550,6 +552,7 @@
ADD_WINDOW_OBJECT_INTERFACE(ReadableStream) \
ADD_WINDOW_OBJECT_INTERFACE(Request) \
ADD_WINDOW_OBJECT_INTERFACE(ResizeObserver) \
+ ADD_WINDOW_OBJECT_INTERFACE(Response) \
ADD_WINDOW_OBJECT_INTERFACE(Screen) \
ADD_WINDOW_OBJECT_INTERFACE(Selection) \
ADD_WINDOW_OBJECT_INTERFACE(ShadowRoot) \
diff --git a/Userland/Libraries/LibWeb/CMakeLists.txt b/Userland/Libraries/LibWeb/CMakeLists.txt
index b13e91632c..48d09efd93 100644
--- a/Userland/Libraries/LibWeb/CMakeLists.txt
+++ b/Userland/Libraries/LibWeb/CMakeLists.txt
@@ -132,6 +132,7 @@ set(SOURCES
Fetch/Infrastructure/HTTP/Statuses.cpp
Fetch/Infrastructure/URL.cpp
Fetch/Request.cpp
+ Fetch/Response.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
index 25cf434939..fc9779a83c 100644
--- a/Userland/Libraries/LibWeb/Fetch/Enums.cpp
+++ b/Userland/Libraries/LibWeb/Fetch/Enums.cpp
@@ -5,6 +5,7 @@
*/
#include <LibWeb/Bindings/RequestPrototype.h>
+#include <LibWeb/Bindings/ResponsePrototype.h>
#include <LibWeb/Fetch/Enums.h>
#include <LibWeb/Fetch/Infrastructure/HTTP/Requests.h>
#include <LibWeb/ReferrerPolicy/ReferrerPolicy.h>
@@ -257,4 +258,24 @@ Bindings::RequestRedirect to_bindings_enum(Infrastructure::Request::RedirectMode
}
}
+Bindings::ResponseType to_bindings_enum(Infrastructure::Response::Type type)
+{
+ switch (type) {
+ case Infrastructure::Response::Type::Basic:
+ return Bindings::ResponseType::Basic;
+ case Infrastructure::Response::Type::CORS:
+ return Bindings::ResponseType::Cors;
+ case Infrastructure::Response::Type::Default:
+ return Bindings::ResponseType::Default;
+ case Infrastructure::Response::Type::Error:
+ return Bindings::ResponseType::Error;
+ case Infrastructure::Response::Type::Opaque:
+ return Bindings::ResponseType::Opaque;
+ case Infrastructure::Response::Type::OpaqueRedirect:
+ return Bindings::ResponseType::Opaqueredirect;
+ default:
+ VERIFY_NOT_REACHED();
+ }
+}
+
}
diff --git a/Userland/Libraries/LibWeb/Fetch/Enums.h b/Userland/Libraries/LibWeb/Fetch/Enums.h
index f74921dd1b..2890f97330 100644
--- a/Userland/Libraries/LibWeb/Fetch/Enums.h
+++ b/Userland/Libraries/LibWeb/Fetch/Enums.h
@@ -8,6 +8,7 @@
#include <AK/Forward.h>
#include <LibWeb/Fetch/Infrastructure/HTTP/Requests.h>
+#include <LibWeb/Fetch/Infrastructure/HTTP/Responses.h>
#include <LibWeb/Forward.h>
namespace Web::Fetch {
@@ -24,5 +25,6 @@ namespace Web::Fetch {
[[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);
+[[nodiscard]] Bindings::ResponseType to_bindings_enum(Infrastructure::Response::Type);
}
diff --git a/Userland/Libraries/LibWeb/Fetch/Response.cpp b/Userland/Libraries/LibWeb/Fetch/Response.cpp
new file mode 100644
index 0000000000..8e355f1708
--- /dev/null
+++ b/Userland/Libraries/LibWeb/Fetch/Response.cpp
@@ -0,0 +1,309 @@
+/*
+ * Copyright (c) 2022, Linus Groh <linusg@serenityos.org>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <AK/URLParser.h>
+#include <LibWeb/Bindings/MainThreadVM.h>
+#include <LibWeb/Fetch/Enums.h>
+#include <LibWeb/Fetch/Infrastructure/HTTP/Bodies.h>
+#include <LibWeb/Fetch/Infrastructure/HTTP/Responses.h>
+#include <LibWeb/Fetch/Infrastructure/HTTP/Statuses.h>
+#include <LibWeb/Fetch/Response.h>
+#include <LibWeb/HTML/Scripting/Environments.h>
+#include <LibWeb/HTML/Window.h>
+#include <LibWeb/Infra/JSON.h>
+
+namespace Web::Fetch {
+
+Response::Response(JS::Realm& realm, NonnullOwnPtr<Infrastructure::Response> response)
+ : PlatformObject(realm)
+ , m_response(move(response))
+{
+ auto& window = verify_cast<HTML::Window>(realm.global_object());
+ set_prototype(&window.cached_web_prototype("Response"));
+}
+
+Response::~Response() = default;
+
+void Response::visit_edges(Cell::Visitor& visitor)
+{
+ Base::visit_edges(visitor);
+ visitor.visit(m_headers);
+}
+
+// https://fetch.spec.whatwg.org/#concept-body-mime-type
+// https://fetch.spec.whatwg.org/#ref-for-concept-header-extract-mime-type%E2%91%A7
+Optional<MimeSniff::MimeType> Response::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 Response object’s MIME type is to return the result of extracting a MIME type from its response’s header list.
+ return m_response->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%A8
+Optional<Infrastructure::Body const&> Response::body_impl() const
+{
+ // Objects including the Body interface mixin have an associated body (null or a body).
+ // A Response object’s body is its response’s body.
+ return m_response->body().has_value()
+ ? m_response->body().value()
+ : Optional<Infrastructure::Body 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&> Response::body_impl()
+{
+ // Objects including the Body interface mixin have an associated body (null or a body).
+ // A Response object’s body is its response’s body.
+ return m_response->body().has_value()
+ ? m_response->body().value()
+ : Optional<Infrastructure::Body&> {};
+}
+
+// https://fetch.spec.whatwg.org/#response-create
+JS::NonnullGCPtr<Response> Response::create(NonnullOwnPtr<Infrastructure::Response> response, Headers::Guard guard, JS::Realm& realm)
+{
+ auto& window = verify_cast<HTML::Window>(realm.global_object());
+
+ // Copy a NonnullRefPtr to the response's header list before response is being move()'d.
+ auto response_reader_list = response->header_list();
+
+ // 1. Let responseObject be a new Response object with realm.
+ // 2. Set responseObject’s response to response.
+ auto* response_object = realm.heap().allocate<Response>(realm, realm, move(response));
+
+ // 3. Set responseObject’s headers to a new Headers object with realm, whose headers list is response’s headers list and guard is guard.
+ response_object->m_headers = realm.heap().allocate<Headers>(realm, window);
+ response_object->m_headers->set_header_list(move(response_reader_list));
+ response_object->m_headers->set_guard(guard);
+
+ // 4. Return responseObject.
+ return JS::NonnullGCPtr { *response_object };
+}
+
+// https://fetch.spec.whatwg.org/#initialize-a-response
+WebIDL::ExceptionOr<void> Response::initialize_response(ResponseInit const& init, Optional<Infrastructure::BodyWithType> const& body)
+{
+ auto& vm = Bindings::main_thread_vm();
+ auto& realm = *vm.current_realm();
+ auto& window = verify_cast<HTML::Window>(realm.global_object());
+
+ // 1. If init["status"] is not in the range 200 to 599, inclusive, then throw a RangeError.
+ if (init.status < 200 || init.status > 599)
+ return WebIDL::SimpleException { WebIDL::SimpleExceptionType::RangeError, "Status must be in range 200-599"sv };
+
+ // FIXME: 2. If init["statusText"] does not match the reason-phrase token production, then throw a TypeError.
+
+ // 3. Set response’s response’s status to init["status"].
+ m_response->set_status(init.status);
+
+ // 4. Set response’s response’s status message to init["statusText"].
+ m_response->set_status_message(TRY_OR_RETURN_OOM(window, ByteBuffer::copy(init.status_text.bytes())));
+
+ // 5. If init["headers"] exists, then fill response’s headers with init["headers"].
+ if (init.headers.has_value())
+ m_headers->fill(*init.headers);
+
+ // 6. If body was given, then:
+ if (body.has_value()) {
+ // 1. If response’s status is a null body status, then throw a TypeError.
+ if (Infrastructure::is_null_body_status(m_response->status()))
+ return WebIDL::SimpleException { WebIDL::SimpleExceptionType::TypeError, "Response with null body status cannot have a body"sv };
+
+ // 2. Set response’s body to body’s body.
+ m_response->set_body(body->body);
+
+ // 3. If body’s type is non-null and response’s header list does not contain `Content-Type`, then append (`Content-Type`, body’s type) to response’s header list.
+ if (body->type.has_value() && m_response->header_list()->contains("Content-Type"sv.bytes())) {
+ auto header = Infrastructure::Header {
+ .name = TRY_OR_RETURN_OOM(global_object(), ByteBuffer::copy("Content-Type"sv.bytes())),
+ .value = TRY_OR_RETURN_OOM(global_object(), ByteBuffer::copy(body->type->span())),
+ };
+ TRY_OR_RETURN_OOM(global_object(), m_response->header_list()->append(move(header)));
+ }
+ }
+
+ return {};
+}
+
+// https://fetch.spec.whatwg.org/#dom-response
+WebIDL::ExceptionOr<JS::NonnullGCPtr<Response>> Response::create_with_global_object(HTML::Window& window, Optional<BodyInit> const& body, ResponseInit const& init)
+{
+ auto& realm = window.realm();
+
+ // Referred to as 'this' in the spec.
+ auto response_object = [&] {
+ auto response = adopt_own(*new Infrastructure::Response());
+ return JS::NonnullGCPtr { *realm.heap().allocate<Response>(realm, realm, move(response)) };
+ }();
+
+ // 1. Set this’s response to a new response.
+ // NOTE: This is done at the beginning as the 'this' value Response object
+ // cannot exist with a null Infrastructure::Response.
+
+ // 2. Set this’s headers to a new Headers object with this’s relevant Realm, whose header list is this’s response’s header list and guard is "response".
+ response_object->m_headers = realm.heap().allocate<Headers>(realm, window);
+ response_object->m_headers->set_header_list(response_object->response().header_list());
+ response_object->m_headers->set_guard(Headers::Guard::Response);
+
+ // 3. Let bodyWithType be null.
+ Optional<Infrastructure::BodyWithType> body_with_type;
+
+ // 4. If body is non-null, then set bodyWithType to the result of extracting body.
+ if (body.has_value())
+ body_with_type = TRY(extract_body(realm, *body));
+
+ // 5. Perform initialize a response given this, init, and bodyWithType.
+ TRY(response_object->initialize_response(init, body_with_type));
+
+ return response_object;
+}
+
+// https://fetch.spec.whatwg.org/#dom-response-error
+JS::NonnullGCPtr<Response> Response::error()
+{
+ auto& vm = Bindings::main_thread_vm();
+
+ // The static error() method steps are to return the result of creating a Response object, given a new network error, "immutable", and this’s relevant Realm.
+ // FIXME: How can we reliably get 'this', i.e. the object the function was called on, in IDL-defined functions?
+ return Response::create(Infrastructure::Response::network_error(), Headers::Guard::Immutable, *vm.current_realm());
+}
+
+// https://fetch.spec.whatwg.org/#dom-response-redirect
+WebIDL::ExceptionOr<JS::NonnullGCPtr<Response>> Response::redirect(String const& url, u16 status)
+{
+ auto& vm = Bindings::main_thread_vm();
+ auto& realm = *vm.current_realm();
+ auto& window = verify_cast<HTML::Window>(realm.global_object());
+
+ // 1. Let parsedURL be the result of parsing url with current settings object’s API base URL.
+ auto api_base_url = HTML::current_settings_object().api_base_url();
+ auto parsed_url = URLParser::parse(url, &api_base_url);
+
+ // 2. If parsedURL is failure, then throw a TypeError.
+ if (!parsed_url.is_valid())
+ return WebIDL::SimpleException { WebIDL::SimpleExceptionType::TypeError, "Redirect URL is not valid"sv };
+
+ // 3. If status is not a redirect status, then throw a RangeError.
+ if (!Infrastructure::is_redirect_status(status))
+ return WebIDL::SimpleException { WebIDL::SimpleExceptionType::RangeError, "Status must be one of 301, 302, 303, 307, or 308"sv };
+
+ // 4. Let responseObject be the result of creating a Response object, given a new response, "immutable", and this’s relevant Realm.
+ // FIXME: How can we reliably get 'this', i.e. the object the function was called on, in IDL-defined functions?
+ auto response_object = Response::create(make<Infrastructure::Response>(), Headers::Guard::Immutable, *vm.current_realm());
+
+ // 5. Set responseObject’s response’s status to status.
+ response_object->response().set_status(status);
+
+ // 6. Let value be parsedURL, serialized and isomorphic encoded.
+ auto value = parsed_url.serialize();
+
+ // 7. Append (`Location`, value) to responseObject’s response’s header list.
+ auto header = Infrastructure::Header {
+ .name = TRY_OR_RETURN_OOM(window, ByteBuffer::copy("Location"sv.bytes())),
+ .value = TRY_OR_RETURN_OOM(window, ByteBuffer::copy(value.bytes())),
+ };
+ TRY_OR_RETURN_OOM(window, response_object->response().header_list()->append(move(header)));
+
+ // 8. Return responseObject.
+ return response_object;
+}
+
+// https://fetch.spec.whatwg.org/#dom-response-json
+WebIDL::ExceptionOr<JS::NonnullGCPtr<Response>> Response::json(JS::Value data, ResponseInit const& init)
+{
+ auto& vm = Bindings::main_thread_vm();
+ auto& realm = *vm.current_realm();
+ auto& window = verify_cast<HTML::Window>(realm.global_object());
+
+ // 1. Let bytes the result of running serialize a JavaScript value to JSON bytes on data.
+ auto bytes = TRY(Infra::serialize_javascript_value_to_json_bytes(vm, data));
+
+ // 2. Let body be the result of extracting bytes.
+ auto [body, _] = TRY(extract_body(realm, { bytes.bytes() }));
+
+ // 3. Let responseObject be the result of creating a Response object, given a new response, "response", and this’s relevant Realm.
+ // FIXME: How can we reliably get 'this', i.e. the object the function was called on, in IDL-defined functions?
+ auto response_object = Response::create(make<Infrastructure::Response>(), Headers::Guard::Response, *vm.current_realm());
+
+ // 4. Perform initialize a response given responseObject, init, and (body, "application/json").
+ auto body_with_type = Infrastructure::BodyWithType {
+ .body = move(body),
+ .type = TRY_OR_RETURN_OOM(window, ByteBuffer::copy("application/json"sv.bytes()))
+ };
+ TRY(response_object->initialize_response(init, move(body_with_type)));
+
+ // 5. Return responseObject.
+ return response_object;
+}
+
+// https://fetch.spec.whatwg.org/#dom-response-type
+Bindings::ResponseType Response::type() const
+{
+ // The type getter steps are to return this’s response’s type.
+ return to_bindings_enum(m_response->type());
+}
+
+// https://fetch.spec.whatwg.org/#dom-response-url
+String Response::url() const
+{
+ // The url getter steps are to return the empty string if this’s response’s URL is null; otherwise this’s response’s URL, serialized with exclude fragment set to true.
+ return !m_response->url().has_value()
+ ? String::empty()
+ : m_response->url()->serialize(AK::URL::ExcludeFragment::Yes);
+}
+
+// https://fetch.spec.whatwg.org/#dom-response-redirected
+bool Response::redirected() const
+{
+ // The redirected getter steps are to return true if this’s response’s URL list has more than one item; otherwise false.
+ return m_response->url_list().size() > 1;
+}
+
+// https://fetch.spec.whatwg.org/#dom-response-status
+u16 Response::status() const
+{
+ // The status getter steps are to return this’s response’s status.
+ return m_response->status();
+}
+
+// https://fetch.spec.whatwg.org/#dom-response-ok
+bool Response::ok() const
+{
+ // The ok getter steps are to return true if this’s response’s status is an ok status; otherwise false.
+ return Infrastructure::is_ok_status(m_response->status());
+}
+
+// https://fetch.spec.whatwg.org/#dom-response-statustext
+String Response::status_text() const
+{
+ // The statusText getter steps are to return this’s response’s status message.
+ return String::copy(m_response->status_message());
+}
+
+// https://fetch.spec.whatwg.org/#dom-response-headers
+JS::NonnullGCPtr<Headers> Response::headers() const
+{
+ // The headers getter steps are to return this’s headers.
+ return *m_headers;
+}
+
+// https://fetch.spec.whatwg.org/#dom-response-clone
+WebIDL::ExceptionOr<JS::NonnullGCPtr<Response>> Response::clone() const
+{
+ // 1. If this is unusable, then throw a TypeError.
+ if (is_unusable())
+ return WebIDL::SimpleException { WebIDL::SimpleExceptionType::TypeError, "Response is unusable"sv };
+
+ // 2. Let clonedResponse be the result of cloning this’s response.
+ auto cloned_response = TRY(m_response->clone());
+
+ // 3. Return the result of creating a Response object, given clonedResponse, this’s headers’s guard, and this’s relevant Realm.
+ return Response::create(move(cloned_response), m_headers->guard(), HTML::relevant_realm(*this));
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/Fetch/Response.h b/Userland/Libraries/LibWeb/Fetch/Response.h
new file mode 100644
index 0000000000..e460e87e16
--- /dev/null
+++ b/Userland/Libraries/LibWeb/Fetch/Response.h
@@ -0,0 +1,78 @@
+/*
+ * Copyright (c) 2022, Linus Groh <linusg@serenityos.org>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#pragma once
+
+#include <AK/Forward.h>
+#include <LibJS/Forward.h>
+#include <LibWeb/Bindings/PlatformObject.h>
+#include <LibWeb/Fetch/Body.h>
+#include <LibWeb/Fetch/BodyInit.h>
+#include <LibWeb/Fetch/Headers.h>
+#include <LibWeb/Forward.h>
+
+namespace Web::Fetch {
+
+// https://fetch.spec.whatwg.org/#responseinit
+struct ResponseInit {
+ u16 status;
+ String status_text;
+ Optional<HeadersInit> headers;
+};
+
+// https://fetch.spec.whatwg.org/#response
+class Response final
+ : public Bindings::PlatformObject
+ , public BodyMixin {
+ WEB_PLATFORM_OBJECT(Response, Bindings::PlatformObject);
+
+public:
+ static JS::NonnullGCPtr<Response> create(NonnullOwnPtr<Infrastructure::Response>, Headers::Guard, JS::Realm&);
+ static WebIDL::ExceptionOr<JS::NonnullGCPtr<Response>> create_with_global_object(HTML::Window&, Optional<BodyInit> const& body = {}, ResponseInit const& init = {});
+
+ virtual ~Response() override;
+
+ // ^BodyMixin
+ virtual Optional<MimeSniff::MimeType> mime_type_impl() const override;
+ virtual Optional<Infrastructure::Body&> body_impl() override;
+ virtual Optional<Infrastructure::Body const&> body_impl() const override;
+
+ [[nodiscard]] Infrastructure::Response& response() { return *m_response; }
+ [[nodiscard]] Infrastructure::Response const& response() const { return *m_response; }
+
+ // JS API functions
+ [[nodiscard]] static JS::NonnullGCPtr<Response> error();
+ [[nodiscard]] static WebIDL::ExceptionOr<JS::NonnullGCPtr<Response>> redirect(String const& url, u16 status);
+ [[nodiscard]] static WebIDL::ExceptionOr<JS::NonnullGCPtr<Response>> json(JS::Value data, ResponseInit const& init = {});
+ [[nodiscard]] Bindings::ResponseType type() const;
+ [[nodiscard]] String url() const;
+ [[nodiscard]] bool redirected() const;
+ [[nodiscard]] u16 status() const;
+ [[nodiscard]] bool ok() const;
+ [[nodiscard]] String status_text() const;
+ [[nodiscard]] JS::NonnullGCPtr<Headers> headers() const;
+ [[nodiscard]] WebIDL::ExceptionOr<JS::NonnullGCPtr<Response>> clone() const;
+
+ // Pull in json() from the BodyMixin, which gets lost due to the static json() above
+ using BodyMixin::json;
+
+private:
+ Response(JS::Realm&, NonnullOwnPtr<Infrastructure::Response>);
+
+ virtual void visit_edges(Cell::Visitor&) override;
+
+ WebIDL::ExceptionOr<void> initialize_response(ResponseInit const&, Optional<Infrastructure::BodyWithType> const&);
+
+ // https://fetch.spec.whatwg.org/#concept-response-response
+ // A Response object has an associated response (a response).
+ NonnullOwnPtr<Infrastructure::Response> m_response;
+
+ // https://fetch.spec.whatwg.org/#response-headers
+ // A Response object also has an associated headers (null or a Headers object), initially null.
+ JS::GCPtr<Headers> m_headers;
+};
+
+}
diff --git a/Userland/Libraries/LibWeb/Fetch/Response.idl b/Userland/Libraries/LibWeb/Fetch/Response.idl
new file mode 100644
index 0000000000..467f7f18bb
--- /dev/null
+++ b/Userland/Libraries/LibWeb/Fetch/Response.idl
@@ -0,0 +1,32 @@
+#import <Fetch/Body.idl>
+#import <Fetch/BodyInit.idl>
+#import <Fetch/Headers.idl>
+
+[Exposed=(Window,Worker)]
+interface Response {
+ constructor(optional BodyInit? body = null, optional ResponseInit init = {});
+
+ [NewObject] static Response error();
+ [NewObject] static Response redirect(USVString url, optional unsigned short status = 302);
+ [NewObject] static Response json(any data, optional ResponseInit init = {});
+
+ readonly attribute ResponseType type;
+
+ readonly attribute USVString url;
+ readonly attribute boolean redirected;
+ readonly attribute unsigned short status;
+ readonly attribute boolean ok;
+ readonly attribute ByteString statusText;
+ [SameObject] readonly attribute Headers headers;
+
+ [NewObject] Response clone();
+};
+Response includes Body;
+
+dictionary ResponseInit {
+ unsigned short status = 200;
+ ByteString statusText = "";
+ HeadersInit headers;
+};
+
+enum ResponseType { "basic", "cors", "default", "error", "opaque", "opaqueredirect" };
diff --git a/Userland/Libraries/LibWeb/Forward.h b/Userland/Libraries/LibWeb/Forward.h
index fff7f0e60b..d2f0b3c791 100644
--- a/Userland/Libraries/LibWeb/Forward.h
+++ b/Userland/Libraries/LibWeb/Forward.h
@@ -184,6 +184,7 @@ class BodyMixin;
class Headers;
class HeadersIterator;
class Request;
+class Response;
}
namespace Web::Fetch::Infrastructure {
@@ -489,6 +490,7 @@ enum class RequestCredentials;
enum class RequestCache;
enum class RequestRedirect;
enum class RequestDuplex;
+enum class ResponseType;
enum class ResizeObserverBoxOptions;
enum class XMLHttpRequestResponseType;
}
diff --git a/Userland/Libraries/LibWeb/idl_files.cmake b/Userland/Libraries/LibWeb/idl_files.cmake
index 0f82d23a00..5f293b7a30 100644
--- a/Userland/Libraries/LibWeb/idl_files.cmake
+++ b/Userland/Libraries/LibWeb/idl_files.cmake
@@ -54,6 +54,7 @@ libweb_js_bindings(Encoding/TextDecoder)
libweb_js_bindings(Encoding/TextEncoder)
libweb_js_bindings(Fetch/Headers ITERABLE)
libweb_js_bindings(Fetch/Request)
+libweb_js_bindings(Fetch/Response)
libweb_js_bindings(FileAPI/Blob)
libweb_js_bindings(FileAPI/File)
libweb_js_bindings(Geometry/DOMPoint)