mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-11-22 07:30:19 +00:00
LibWeb: Implement '5.4. Request class' from the Fetch API :^)
This commit is contained in:
parent
5ad6283331
commit
9fb672e981
Notes:
sideshowbarker
2024-07-17 06:35:29 +09:00
Author: https://github.com/linusg Commit: https://github.com/SerenityOS/serenity/commit/9fb672e981 Pull-request: https://github.com/SerenityOS/serenity/pull/15359 Reviewed-by: https://github.com/alimpfard Reviewed-by: https://github.com/davidot ✅ Reviewed-by: https://github.com/kennethmyhra
13 changed files with 1146 additions and 3 deletions
|
@ -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;
|
||||
|
|
|
@ -303,6 +303,8 @@
|
|||
#include <LibWeb/Bindings/RangePrototype.h>
|
||||
#include <LibWeb/Bindings/ReadableStreamConstructor.h>
|
||||
#include <LibWeb/Bindings/ReadableStreamPrototype.h>
|
||||
#include <LibWeb/Bindings/RequestConstructor.h>
|
||||
#include <LibWeb/Bindings/RequestPrototype.h>
|
||||
#include <LibWeb/Bindings/ResizeObserverConstructor.h>
|
||||
#include <LibWeb/Bindings/ResizeObserverPrototype.h>
|
||||
#include <LibWeb/Bindings/SVGAnimatedLengthConstructor.h>
|
||||
|
@ -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) \
|
||||
|
|
|
@ -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
|
||||
|
|
260
Userland/Libraries/LibWeb/Fetch/Enums.cpp
Normal file
260
Userland/Libraries/LibWeb/Fetch/Enums.cpp
Normal file
|
@ -0,0 +1,260 @@
|
|||
/*
|
||||
* Copyright (c) 2022, Linus Groh <linusg@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <LibWeb/Bindings/RequestPrototype.h>
|
||||
#include <LibWeb/Fetch/Enums.h>
|
||||
#include <LibWeb/Fetch/Infrastructure/HTTP/Requests.h>
|
||||
#include <LibWeb/ReferrerPolicy/ReferrerPolicy.h>
|
||||
|
||||
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<ReferrerPolicy::ReferrerPolicy> 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<ReferrerPolicy::ReferrerPolicy> 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<Infrastructure::Request::Destination> 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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
28
Userland/Libraries/LibWeb/Fetch/Enums.h
Normal file
28
Userland/Libraries/LibWeb/Fetch/Enums.h
Normal file
|
@ -0,0 +1,28 @@
|
|||
/*
|
||||
* Copyright (c) 2022, Linus Groh <linusg@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/Forward.h>
|
||||
#include <LibWeb/Fetch/Infrastructure/HTTP/Requests.h>
|
||||
#include <LibWeb/Forward.h>
|
||||
|
||||
namespace Web::Fetch {
|
||||
|
||||
[[nodiscard]] Optional<ReferrerPolicy::ReferrerPolicy> 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<ReferrerPolicy::ReferrerPolicy> const&);
|
||||
[[nodiscard]] Bindings::RequestDestination to_bindings_enum(Optional<Infrastructure::Request::Destination> 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);
|
||||
|
||||
}
|
|
@ -42,6 +42,8 @@ public:
|
|||
[[nodiscard]] Guard guard() const { return m_guard; }
|
||||
void set_guard(Guard guard) { m_guard = guard; }
|
||||
|
||||
WebIDL::ExceptionOr<void> fill(HeadersInit const&);
|
||||
|
||||
// JS API functions
|
||||
WebIDL::ExceptionOr<void> append(Infrastructure::Header);
|
||||
WebIDL::ExceptionOr<void> append(String const& name, String const& value);
|
||||
|
@ -58,7 +60,6 @@ private:
|
|||
|
||||
explicit Headers(HTML::Window&);
|
||||
|
||||
WebIDL::ExceptionOr<void> fill(HeadersInit const&);
|
||||
void remove_privileged_no_cors_headers();
|
||||
|
||||
// https://fetch.spec.whatwg.org/#concept-headers-header-list
|
||||
|
|
|
@ -31,6 +31,7 @@ class HeaderList final
|
|||
, Vector<Header> {
|
||||
public:
|
||||
using Vector::begin;
|
||||
using Vector::clear;
|
||||
using Vector::end;
|
||||
|
||||
[[nodiscard]] bool contains(ReadonlyBytes) const;
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
||||
|
|
649
Userland/Libraries/LibWeb/Fetch/Request.cpp
Normal file
649
Userland/Libraries/LibWeb/Fetch/Request.cpp
Normal file
|
@ -0,0 +1,649 @@
|
|||
/*
|
||||
* Copyright (c) 2022, Linus Groh <linusg@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <AK/ScopeGuard.h>
|
||||
#include <AK/URLParser.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/HTML/Window.h>
|
||||
#include <LibWeb/ReferrerPolicy/ReferrerPolicy.h>
|
||||
|
||||
namespace Web::Fetch {
|
||||
|
||||
Request::Request(JS::Realm& realm, NonnullOwnPtr<Infrastructure::Request> request)
|
||||
: PlatformObject(realm)
|
||||
, m_request(move(request))
|
||||
{
|
||||
auto& window = verify_cast<HTML::Window>(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<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 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<Infrastructure::Body const&> 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<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 object’s body is its request’s 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)
|
||||
{
|
||||
auto& window = verify_cast<HTML::Window>(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<Request>(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<Headers>(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<DOM::AbortSignal>(realm, window);
|
||||
|
||||
// 5. Return requestObject.
|
||||
return JS::NonnullGCPtr { *request_object };
|
||||
}
|
||||
|
||||
// https://fetch.spec.whatwg.org/#dom-request
|
||||
WebIDL::ExceptionOr<JS::NonnullGCPtr<Request>> 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<Request>(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<Infrastructure::Request::Mode> 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<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 input’s request.
|
||||
input_request = &input.get<JS::Handle<Request>>()->request();
|
||||
delete_input_request.disarm();
|
||||
|
||||
// 3. Set signal to input’s signal.
|
||||
input_signal = input.get<JS::Handle<Request>>()->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<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
|
||||
// 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<Infrastructure::HeaderList>();
|
||||
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<DOM::AbortSignal>(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<Headers>(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<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 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<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 this’s headers with headers.
|
||||
else {
|
||||
request_object->headers()->fill(headers.get<HeadersInit>());
|
||||
}
|
||||
}
|
||||
|
||||
// 33. Let inputBody be input’s request’s 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 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<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 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<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 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<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 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<Headers> 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<DOM::AbortSignal> 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<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 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;
|
||||
}
|
||||
|
||||
}
|
115
Userland/Libraries/LibWeb/Fetch/Request.h
Normal file
115
Userland/Libraries/LibWeb/Fetch/Request.h
Normal file
|
@ -0,0 +1,115 @@
|
|||
/*
|
||||
* 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/#requestinfo
|
||||
using RequestInfo = Variant<JS::Handle<Request>, String>;
|
||||
|
||||
// https://fetch.spec.whatwg.org/#requestinit
|
||||
struct RequestInit {
|
||||
Optional<String> method;
|
||||
Optional<HeadersInit> headers;
|
||||
Optional<Optional<BodyInit>> body;
|
||||
Optional<String> referrer;
|
||||
Optional<Bindings::ReferrerPolicy> referrer_policy;
|
||||
Optional<Bindings::RequestMode> mode;
|
||||
Optional<Bindings::RequestCredentials> credentials;
|
||||
Optional<Bindings::RequestCache> cache;
|
||||
Optional<Bindings::RequestRedirect> redirect;
|
||||
Optional<String> integrity;
|
||||
Optional<bool> keepalive;
|
||||
Optional<JS::GCPtr<DOM::AbortSignal>> signal;
|
||||
Optional<Bindings::RequestDuplex> duplex;
|
||||
Optional<JS::Value> 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<Request> create(NonnullOwnPtr<Infrastructure::Request>, Headers::Guard, JS::Realm&);
|
||||
static WebIDL::ExceptionOr<JS::NonnullGCPtr<Request>> create_with_global_object(HTML::Window&, RequestInfo const& input, RequestInit const& init = {});
|
||||
|
||||
virtual ~Request() 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::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> 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<DOM::AbortSignal> signal() const;
|
||||
[[nodiscard]] WebIDL::ExceptionOr<JS::NonnullGCPtr<Request>> clone() const;
|
||||
|
||||
private:
|
||||
Request(JS::Realm&, NonnullOwnPtr<Infrastructure::Request>);
|
||||
|
||||
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<Infrastructure::Request> 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<Headers> 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<DOM::AbortSignal> m_signal;
|
||||
};
|
||||
|
||||
}
|
69
Userland/Libraries/LibWeb/Fetch/Request.idl
Normal file
69
Userland/Libraries/LibWeb/Fetch/Request.idl
Normal file
|
@ -0,0 +1,69 @@
|
|||
#import <DOM/AbortSignal.idl>
|
||||
#import <Fetch/Body.idl>
|
||||
#import <Fetch/BodyInit.idl>
|
||||
#import <Fetch/Headers.idl>
|
||||
|
||||
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"
|
||||
};
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in a new issue