ladybird/Userland/Libraries/LibWeb/Fetch/Request.cpp

643 lines
28 KiB
C++
Raw Normal View History

/*
* Copyright (c) 2022, Linus Groh <linusg@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <AK/ScopeGuard.h>
#include <AK/URLParser.h>
#include <LibWeb/Bindings/Intrinsics.h>
#include <LibWeb/DOM/AbortSignal.h>
#include <LibWeb/Fetch/Enums.h>
#include <LibWeb/Fetch/Headers.h>
#include <LibWeb/Fetch/Infrastructure/HTTP/Bodies.h>
#include <LibWeb/Fetch/Infrastructure/HTTP/Headers.h>
#include <LibWeb/Fetch/Infrastructure/HTTP/Methods.h>
#include <LibWeb/Fetch/Infrastructure/HTTP/Requests.h>
#include <LibWeb/Fetch/Request.h>
#include <LibWeb/HTML/Scripting/Environments.h>
#include <LibWeb/ReferrerPolicy/ReferrerPolicy.h>
namespace Web::Fetch {
Request::Request(JS::Realm& realm, NonnullOwnPtr<Infrastructure::Request> request)
: PlatformObject(realm)
, m_request(move(request))
{
set_prototype(&Bindings::cached_web_prototype(realm, "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<MimeSniff::MimeType> 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 objects MIME type is to return the result of extracting a MIME type from its requests 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<Infrastructure::Body const&> Request::body_impl() const
{
// Objects including the Body interface mixin have an associated body (null or a body).
// A Request objects body is its requests body.
return m_request->body().visit(
[](Infrastructure::Body const& b) -> Optional<Infrastructure::Body const&> { return b; },
[](Empty) -> Optional<Infrastructure::Body const&> { return {}; },
// A byte sequence will be safely extracted into a body early on in fetch.
[](ByteBuffer const&) -> Optional<Infrastructure::Body const&> { VERIFY_NOT_REACHED(); });
}
// https://fetch.spec.whatwg.org/#concept-body-body
// https://fetch.spec.whatwg.org/#ref-for-concept-body-body%E2%91%A7
Optional<Infrastructure::Body&> Request::body_impl()
{
// Objects including the Body interface mixin have an associated body (null or a body).
// A Request objects body is its requests body.
return m_request->body().visit(
[](Infrastructure::Body& b) -> Optional<Infrastructure::Body&> { return b; },
[](Empty) -> Optional<Infrastructure::Body&> { return {}; },
// A byte sequence will be safely extracted into a body early on in fetch.
[](ByteBuffer&) -> Optional<Infrastructure::Body&> { VERIFY_NOT_REACHED(); });
}
// https://fetch.spec.whatwg.org/#request-create
JS::NonnullGCPtr<Request> Request::create(NonnullOwnPtr<Infrastructure::Request> request, Headers::Guard guard, JS::Realm& realm)
{
// 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 requestObjects request to request.
auto* request_object = realm.heap().allocate<Request>(realm, realm, move(request));
// 3. Set requestObjects headers to a new Headers object with realm, whose headers list is requests headers list and guard is guard.
request_object->m_headers = realm.heap().allocate<Headers>(realm, realm);
request_object->m_headers->set_header_list(move(request_reader_list));
request_object->m_headers->set_guard(guard);
// 4. Set requestObjects signal to a new AbortSignal object with realm.
request_object->m_signal = realm.heap().allocate<DOM::AbortSignal>(realm, realm);
// 5. Return requestObject.
return JS::NonnullGCPtr { *request_object };
}
// https://fetch.spec.whatwg.org/#dom-request
WebIDL::ExceptionOr<JS::NonnullGCPtr<Request>> Request::construct_impl(JS::Realm& realm, RequestInfo const& input, RequestInit const& init)
{
// Referred to as 'this' in the spec.
auto request_object = JS::NonnullGCPtr { *realm.heap().allocate<Request>(realm, realm, make<Infrastructure::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<Infrastructure::Request::Mode> fallback_mode;
// 3. Let baseURL be thiss relevant settings objects 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<String>()) {
// 1. Let parsedURL be the result of parsing input with baseURL.
auto parsed_url = URLParser::parse(input.get<String>(), &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<JS::Handle<Request>>());
// 2. Set request to inputs request.
input_request = &input.get<JS::Handle<Request>>()->request();
delete_input_request.disarm();
// 3. Set signal to inputs signal.
input_signal = input.get<JS::Handle<Request>>()->signal();
}
// 7. Let origin be thiss relevant settings objects 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 requests window is an environment settings object and its origin is same origin with origin, then set window to requests window.
if (input_request->window().has<HTML::EnvironmentSettingsObject*>()) {
auto* eso = input_request->window().get<HTML::EnvironmentSettingsObject*>();
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
// requests URL.
request.set_url(input_request->url());
// method
// requests method.
request.set_method(TRY_OR_RETURN_OOM(realm, ByteBuffer::copy(request.method())));
// header list
// A copy of requests header list.
auto header_list_copy = make_ref_counted<Infrastructure::HeaderList>();
for (auto& header : *request.header_list())
TRY_OR_RETURN_OOM(realm, header_list_copy->append(header));
request.set_header_list(move(header_list_copy));
// unsafe-request flag
// Set.
request.set_unsafe_request(true);
// client
// Thiss relevant settings object.
request.set_client(&HTML::relevant_settings_object(*request_object));
// window
// window.
request.set_window(window);
// priority
// requests priority.
request.set_priority(input_request->priority());
// origin
// requests 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
// requests referrer.
request.set_referrer(input_request->referrer());
// referrer policy
// requests referrer policy.
request.set_referrer_policy(input_request->referrer_policy());
// mode
// requests mode.
request.set_mode(input_request->mode());
// credentials mode
// requests credentials mode.
request.set_credentials_mode(input_request->credentials_mode());
// cache mode
// requests cache mode.
request.set_cache_mode(input_request->cache_mode());
// redirect mode
// requests redirect mode.
request.set_redirect_mode(input_request->redirect_mode());
// integrity metadata
// requests integrity metadata.
request.set_integrity_metadata(input_request->integrity_metadata());
// keepalive
// requests keepalive.
request.set_keepalive(input_request->keepalive());
// reload-navigation flag
// requests reload-navigation flag.
request.set_reload_navigation(input_request->reload_navigation());
// history-navigation flag
// requests history-navigation flag.
request.set_history_navigation(input_request->history_navigation());
// URL list
// A clone of requests 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 requests 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 requests reload-navigation flag.
request.set_reload_navigation(false);
// 3. Unset requests history-navigation flag.
request.set_history_navigation(false);
// 4. Set requests origin to "client".
request.set_origin(Infrastructure::Request::Origin::Client);
// 5. Set requests referrer to "client".
request.set_referrer(Infrastructure::Request::Referrer::Client);
// 6. Set requests referrer policy to the empty string.
request.set_referrer_policy({});
// 7. Set requests URL to requests current URL.
request.set_url(request.current_url());
// 8. Set requests URL list to « requests 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 requests 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
// - parsedReferrers scheme is "about" and path is the string "client"
// - parsedReferrers origin is not same origin with origin
// then set requests 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 requests referrer to parsedReferrer.
else {
request.set_referrer(move(parsed_referrer));
}
}
}
// 15. If init["referrerPolicy"] exists, then set requests 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 requests mode to mode.
if (mode.has_value())
request.set_mode(*mode);
// 19. If init["credentials"] exists, then set requests 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 requests cache mode to it.
if (init.cache.has_value())
request.set_cache_mode(from_bindings_enum(*init.cache));
// 21. If requests cache mode is "only-if-cached" and requests 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 requests 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 requests integrity metadata to it.
if (init.integrity.has_value())
request.set_integrity_metadata(*init.integrity);
// 24. If init["keepalive"] exists, then set requests 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(realm, Infrastructure::normalize_method(method.bytes()));
// 4. Set requests 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 thiss 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 thiss signal to a new AbortSignal object with thiss relevant Realm.
auto& this_relevant_realm = HTML::relevant_realm(*request_object);
request_object->m_signal = realm.heap().allocate<DOM::AbortSignal>(this_relevant_realm, this_relevant_realm);
// 29. If signal is not null, then make thiss signal follow signal.
if (input_signal != nullptr) {
// FIXME: Our AbortSignal doesn't support 'following' yet.
}
// 30. Set thiss headers to a new Headers object with thiss relevant Realm, whose header list is requests header list and guard is "request".
request_object->m_headers = realm.heap().allocate<Headers>(realm, realm);
request_object->m_headers->set_header_list(request.header_list());
request_object->m_headers->set_guard(Headers::Guard::Request);
// 31. If thiss requests mode is "no-cors", then:
if (request_object->request().mode() == Infrastructure::Request::Mode::NoCORS) {
// 1. If thiss requests 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 thiss headerss 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 thiss headers and its associated header list.
auto headers = Variant<HeadersInit, NonnullRefPtr<Infrastructure::HeaderList>> { 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 thiss headerss header list.
request_object->headers()->header_list()->clear();
// 4. If headers is a Headers object, then for each header in its header list, append (headers name, headers value) to thiss headers.
if (headers.has<NonnullRefPtr<Infrastructure::HeaderList>>()) {
auto& header_list = *headers.get<NonnullRefPtr<Infrastructure::HeaderList>>();
for (auto& header : header_list)
TRY(request_object->headers()->append(String::copy(header.name), String::copy(header.value)));
}
// 5. Otherwise, fill thiss headers with headers.
else {
request_object->headers()->fill(headers.get<HeadersInit>());
}
}
// 33. Let inputBody be inputs requests body if input is a Request object; otherwise null.
auto const& input_body = input.has<JS::Handle<Request>>()
? input.get<JS::Handle<Request>>()->request().body().get<Infrastructure::Body>()
: Optional<Infrastructure::Body> {};
// 34. If either init["body"] exists and is non-null or inputBody is non-null, and requests 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<Infrastructure::Body> 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 requests keepalive.
auto body_with_type = TRY(extract_body(realm, (*init.body).value(), request.keepalive()));
// 2. Set initBody to bodyWithTypes body.
init_body = move(body_with_type.body);
// 3. Let type be bodyWithTypes type.
auto const& type = body_with_type.type;
// 4. If type is non-null and thiss headerss header list does not contain `Content-Type`, then append (`Content-Type`, type) to thiss 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 inputOrInitBodys source is null, then:
if (input_or_init_body.has_value() && input_or_init_body->source().has<Empty>()) {
// 1. If initBody is non-null and init["duplex"] does not exist, then throw a TypeError.
if (init_body.has_value() && !init.duplex.has_value())
return WebIDL::SimpleException { WebIDL::SimpleExceptionType::TypeError, "Body without source requires 'duplex' value to be set"sv };
// 2. If thiss requests mode is neither "same-origin" nor "cors", then throw a TypeError.
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 thiss requests 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<JS::Handle<Request>>() && input.get<JS::Handle<Request>>()->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 thiss requests 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 thiss requests 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 thiss requests URL, serialized.
return m_request->url().serialize();
}
// https://fetch.spec.whatwg.org/#dom-request-headers
JS::NonnullGCPtr<Headers> Request::headers() const
{
// The headers getter steps are to return thiss headers.
return *m_headers;
}
// https://fetch.spec.whatwg.org/#dom-request-destination
Bindings::RequestDestination Request::destination() const
{
// The destination getter are to return thiss requests 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 thiss requests referrer is "no-referrer", then return the empty string.
case Infrastructure::Request::Referrer::NoReferrer:
return String::empty();
// 2. If thiss requests 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 thiss requests 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 thiss requests 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 thiss requests 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 thiss requests 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 thiss requests 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 thiss requests 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 thiss requests 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 thiss requests 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 thiss requests 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 thiss requests history-navigation flag is set; otherwise false.
return m_request->history_navigation();
}
// https://fetch.spec.whatwg.org/#dom-request-signal
JS::NonnullGCPtr<DOM::AbortSignal> Request::signal() const
{
// The signal getter steps are to return thiss signal.
return *m_signal;
}
// https://fetch.spec.whatwg.org/#dom-request-clone
WebIDL::ExceptionOr<JS::NonnullGCPtr<Request>> 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 thiss request.
auto cloned_request = TRY(m_request->clone());
// 3. Let clonedRequestObject be the result of creating a Request object, given clonedRequest, thiss headerss guard, and thiss relevant Realm.
auto cloned_request_object = Request::create(move(cloned_request), m_headers->guard(), HTML::relevant_realm(*this));
// FIXME: 4. Make clonedRequestObjects signal follow thiss signal.
// 5. Return clonedRequestObject.
return cloned_request_object;
}
}