Pārlūkot izejas kodu

LibWeb/HTML: Port Window.fetch() to IDL

Linus Groh 2 gadi atpakaļ
vecāks
revīzija
5cc6b1c4db

+ 0 - 367
Userland/Libraries/LibWeb/Bindings/FetchMethod.cpp

@@ -1,367 +0,0 @@
-/*
- * Copyright (c) 2022-2023, Linus Groh <linusg@serenityos.org>
- *
- * SPDX-License-Identifier: BSD-2-Clause
- */
-
-#include <AK/TypeCasts.h>
-#include <LibJS/Runtime/ArrayBuffer.h>
-#include <LibJS/Runtime/Error.h>
-#include <LibJS/Runtime/FunctionObject.h>
-#include <LibJS/Runtime/IteratorOperations.h>
-#include <LibJS/Runtime/VM.h>
-#include <LibWeb/Bindings/ExceptionOrUtils.h>
-#include <LibWeb/Bindings/FetchMethod.h>
-#include <LibWeb/Bindings/RequestPrototype.h>
-#include <LibWeb/DOM/AbortSignal.h>
-#include <LibWeb/Fetch/FetchMethod.h>
-#include <LibWeb/Fetch/Request.h>
-#include <LibWeb/FileAPI/Blob.h>
-#include <LibWeb/Streams/ReadableStream.h>
-#include <LibWeb/URL/URLSearchParams.h>
-
-// NOTE: This file contains code generated by BindingsGenerator from the following input:
-// ```idl
-// #import <Fetch/Request.idl>
-// #import <Fetch/Response.idl>
-// [Exposed=Window, UseNewAKString]
-// interface Dummy {
-//     static Promise<Response> fetch(RequestInfo input, optional RequestInit init = {});
-// };
-// ```
-// This is because the spec defines the fetch() method as a 'partial interface mixin' on
-// WindowOrWorkerGlobalScope, which we don't support yet - and even if we did, the Window object is
-// not generated from IDL currently, so we couldn't add a mixin to it that way. The generated code
-// has _not_ been cleaned up manually, the only changes are:
-// - Adding only the necessary includes and 'using namespace' declarations
-// - Deferring to 'Fetch::fetch_impl()' at the very end instead of 'Fetch::Dummy::fetch()'
-// - Removing all empty lines, there's an excessive amount of them and this isn't supposed to be
-//   readable code anyway
-// - Running clang-format :^)
-// Don't hesitate to sync it with updated output when making changes to BindingsGenerator!
-
-using namespace Web::DOM;
-using namespace Web::Fetch;
-using namespace Web::FileAPI;
-using namespace Web::Streams;
-using namespace Web::URL;
-
-namespace Web::Bindings {
-
-// NOLINTBEGIN
-JS::ThrowCompletionOr<JS::Value> fetch(JS::VM& vm)
-{
-    [[maybe_unused]] auto& realm = *vm.current_realm();
-    if (vm.argument_count() < 1)
-        return vm.throw_completion<JS::TypeError>(JS::ErrorType::BadArgCountOne, "fetch");
-    auto arg0 = vm.argument(0);
-    auto arg0_to_variant = [&vm, &realm](JS::Value arg0) -> JS::ThrowCompletionOr<Variant<JS::Handle<Request>, String>> {
-        // These might be unused.
-        (void)vm;
-        (void)realm;
-        if (arg0.is_object()) {
-            [[maybe_unused]] auto& arg0_object = arg0.as_object();
-            if (is<PlatformObject>(arg0_object)) {
-                if (is<Request>(arg0_object))
-                    return JS::make_handle(static_cast<Request&>(arg0_object));
-            }
-        }
-        return TRY(arg0.to_string(vm));
-    };
-    Variant<JS::Handle<Request>, String> input = TRY(arg0_to_variant(arg0));
-    auto arg1 = vm.argument(1);
-    if (!arg1.is_nullish() && !arg1.is_object())
-        return vm.throw_completion<JS::TypeError>(JS::ErrorType::NotAnObjectOfType, "RequestInit");
-    RequestInit init {};
-    auto body_property_value = JS::js_undefined();
-    if (arg1.is_object())
-        body_property_value = TRY(arg1.as_object().get("body"));
-    if (!body_property_value.is_undefined()) {
-        auto body_property_value_to_variant = [&vm, &realm](JS::Value body_property_value) -> JS::ThrowCompletionOr<Variant<JS::Handle<ReadableStream>, JS::Handle<Blob>, JS::Handle<JS::Object>, JS::Handle<URLSearchParams>, String>> {
-            // These might be unused.
-            (void)vm;
-            (void)realm;
-            if (body_property_value.is_object()) {
-                [[maybe_unused]] auto& body_property_value_object = body_property_value.as_object();
-                if (is<PlatformObject>(body_property_value_object)) {
-                    if (is<ReadableStream>(body_property_value_object))
-                        return JS::make_handle(static_cast<ReadableStream&>(body_property_value_object));
-                    if (is<Blob>(body_property_value_object))
-                        return JS::make_handle(static_cast<Blob&>(body_property_value_object));
-                    if (is<URLSearchParams>(body_property_value_object))
-                        return JS::make_handle(static_cast<URLSearchParams&>(body_property_value_object));
-                }
-                if (is<JS::ArrayBuffer>(body_property_value_object))
-                    return JS::make_handle(body_property_value_object);
-            }
-            return TRY(body_property_value.to_string(vm));
-        };
-        Optional<Variant<JS::Handle<ReadableStream>, JS::Handle<Blob>, JS::Handle<JS::Object>, JS::Handle<URLSearchParams>, String>> body_value;
-        if (!body_property_value.is_nullish())
-            body_value = TRY(body_property_value_to_variant(body_property_value));
-        init.body = body_value;
-    }
-    auto cache_property_value = JS::js_undefined();
-    if (arg1.is_object())
-        cache_property_value = TRY(arg1.as_object().get("cache"));
-    if (!cache_property_value.is_undefined()) {
-        RequestCache cache_value { RequestCache::Default };
-        if (!cache_property_value.is_undefined()) {
-            auto cache_property_value_string = TRY(cache_property_value.to_string(vm));
-            if (cache_property_value_string == "default"sv)
-                cache_value = RequestCache::Default;
-            else if (cache_property_value_string == "no-store"sv)
-                cache_value = RequestCache::NoStore;
-            else if (cache_property_value_string == "reload"sv)
-                cache_value = RequestCache::Reload;
-            else if (cache_property_value_string == "no-cache"sv)
-                cache_value = RequestCache::NoCache;
-            else if (cache_property_value_string == "force-cache"sv)
-                cache_value = RequestCache::ForceCache;
-            else if (cache_property_value_string == "only-if-cached"sv)
-                cache_value = RequestCache::OnlyIfCached;
-            else
-                return vm.throw_completion<JS::TypeError>(JS::ErrorType::InvalidEnumerationValue, cache_property_value_string, "RequestCache");
-        }
-        init.cache = cache_value;
-    }
-    auto credentials_property_value = JS::js_undefined();
-    if (arg1.is_object())
-        credentials_property_value = TRY(arg1.as_object().get("credentials"));
-    if (!credentials_property_value.is_undefined()) {
-        RequestCredentials credentials_value { RequestCredentials::Omit };
-        if (!credentials_property_value.is_undefined()) {
-            auto credentials_property_value_string = TRY(credentials_property_value.to_string(vm));
-            if (credentials_property_value_string == "omit"sv)
-                credentials_value = RequestCredentials::Omit;
-            else if (credentials_property_value_string == "same-origin"sv)
-                credentials_value = RequestCredentials::SameOrigin;
-            else if (credentials_property_value_string == "include"sv)
-                credentials_value = RequestCredentials::Include;
-            else
-                return vm.throw_completion<JS::TypeError>(JS::ErrorType::InvalidEnumerationValue, credentials_property_value_string, "RequestCredentials");
-        }
-        init.credentials = credentials_value;
-    }
-    auto duplex_property_value = JS::js_undefined();
-    if (arg1.is_object())
-        duplex_property_value = TRY(arg1.as_object().get("duplex"));
-    if (!duplex_property_value.is_undefined()) {
-        RequestDuplex duplex_value { RequestDuplex::Half };
-        if (!duplex_property_value.is_undefined()) {
-            auto duplex_property_value_string = TRY(duplex_property_value.to_string(vm));
-            if (duplex_property_value_string == "half"sv)
-                duplex_value = RequestDuplex::Half;
-            else
-                return vm.throw_completion<JS::TypeError>(JS::ErrorType::InvalidEnumerationValue, duplex_property_value_string, "RequestDuplex");
-        }
-        init.duplex = duplex_value;
-    }
-    auto headers_property_value = JS::js_undefined();
-    if (arg1.is_object())
-        headers_property_value = TRY(arg1.as_object().get("headers"));
-    if (!headers_property_value.is_undefined()) {
-        auto headers_property_value_to_variant = [&vm, &realm](JS::Value headers_property_value) -> JS::ThrowCompletionOr<Variant<Vector<Vector<String>>, OrderedHashMap<String, String>>> {
-            // These might be unused.
-            (void)vm;
-            (void)realm;
-            if (headers_property_value.is_object()) {
-                [[maybe_unused]] auto& headers_property_value_object = headers_property_value.as_object();
-                auto* method = TRY(headers_property_value.get_method(vm, *vm.well_known_symbol_iterator()));
-                if (method) {
-                    auto iterator1 = TRY(JS::get_iterator(vm, headers_property_value, JS::IteratorHint::Sync, method));
-                    Vector<Vector<String>> headers_value;
-                    for (;;) {
-                        auto* next1 = TRY(JS::iterator_step(vm, iterator1));
-                        if (!next1)
-                            break;
-                        auto next_item1 = TRY(JS::iterator_value(vm, *next1));
-                        if (!next_item1.is_object())
-                            return vm.throw_completion<JS::TypeError>(JS::ErrorType::NotAnObject, TRY_OR_THROW_OOM(vm, next_item1.to_string_without_side_effects()));
-                        auto* iterator_method1 = TRY(next_item1.get_method(vm, *vm.well_known_symbol_iterator()));
-                        if (!iterator_method1)
-                            return vm.throw_completion<JS::TypeError>(JS::ErrorType::NotIterable, TRY_OR_THROW_OOM(vm, next_item1.to_string_without_side_effects()));
-                        auto iterator2 = TRY(JS::get_iterator(vm, next_item1, JS::IteratorHint::Sync, iterator_method1));
-                        Vector<String> sequence_item1;
-                        for (;;) {
-                            auto* next2 = TRY(JS::iterator_step(vm, iterator2));
-                            if (!next2)
-                                break;
-                            auto next_item2 = TRY(JS::iterator_value(vm, *next2));
-                            String sequence_item2;
-                            if (!false || !next_item2.is_null()) {
-                                sequence_item2 = TRY(next_item2.to_string(vm));
-                            }
-                            sequence_item1.append(sequence_item2);
-                        }
-                        headers_value.append(sequence_item1);
-                    }
-                    return headers_value;
-                }
-                OrderedHashMap<String, String> record_union_type;
-                auto record_keys1 = TRY(headers_property_value_object.internal_own_property_keys());
-                for (auto& key1 : record_keys1) {
-                    auto property_key1 = MUST(JS::PropertyKey::from_value(vm, key1));
-                    auto descriptor1 = TRY(headers_property_value_object.internal_get_own_property(property_key1));
-                    if (!descriptor1.has_value() || !descriptor1->enumerable.has_value() || !descriptor1->enumerable.value())
-                        continue;
-                    String typed_key1;
-                    if (!false || !key1.is_null()) {
-                        typed_key1 = TRY(key1.to_string(vm));
-                    }
-                    auto value1 = TRY(headers_property_value_object.get(property_key1));
-                    String typed_value1;
-                    if (!false || !value1.is_null()) {
-                        typed_value1 = TRY(value1.to_string(vm));
-                    }
-                    record_union_type.set(typed_key1, typed_value1);
-                }
-                return record_union_type;
-            }
-            return vm.throw_completion<JS::TypeError>("No union types matched"sv);
-        };
-        Optional<Variant<Vector<Vector<String>>, OrderedHashMap<String, String>>> headers_value;
-        if (!headers_property_value.is_nullish())
-            headers_value = TRY(headers_property_value_to_variant(headers_property_value));
-        init.headers = headers_value;
-    }
-    auto integrity_property_value = JS::js_undefined();
-    if (arg1.is_object())
-        integrity_property_value = TRY(arg1.as_object().get("integrity"));
-    if (!integrity_property_value.is_undefined()) {
-        Optional<String> integrity_value;
-        if (!integrity_property_value.is_undefined()) {
-            if (!false || !integrity_property_value.is_null())
-                integrity_value = TRY(integrity_property_value.to_string(vm));
-        }
-        if (integrity_value.has_value())
-            init.integrity = integrity_value.release_value();
-    }
-    auto keepalive_property_value = JS::js_undefined();
-    if (arg1.is_object())
-        keepalive_property_value = TRY(arg1.as_object().get("keepalive"));
-    if (!keepalive_property_value.is_undefined()) {
-        Optional<bool> keepalive_value;
-        if (!keepalive_property_value.is_undefined())
-            keepalive_value = keepalive_property_value.to_boolean();
-        init.keepalive = keepalive_value;
-    }
-    auto method_property_value = JS::js_undefined();
-    if (arg1.is_object())
-        method_property_value = TRY(arg1.as_object().get("method"));
-    if (!method_property_value.is_undefined()) {
-        Optional<String> method_value;
-        if (!method_property_value.is_undefined()) {
-            if (!false || !method_property_value.is_null())
-                method_value = TRY(method_property_value.to_string(vm));
-        }
-        if (method_value.has_value())
-            init.method = method_value.release_value();
-    }
-    auto mode_property_value = JS::js_undefined();
-    if (arg1.is_object())
-        mode_property_value = TRY(arg1.as_object().get("mode"));
-    if (!mode_property_value.is_undefined()) {
-        RequestMode mode_value { RequestMode::Navigate };
-        if (!mode_property_value.is_undefined()) {
-            auto mode_property_value_string = TRY(mode_property_value.to_string(vm));
-            if (mode_property_value_string == "navigate"sv)
-                mode_value = RequestMode::Navigate;
-            else if (mode_property_value_string == "same-origin"sv)
-                mode_value = RequestMode::SameOrigin;
-            else if (mode_property_value_string == "no-cors"sv)
-                mode_value = RequestMode::NoCors;
-            else if (mode_property_value_string == "cors"sv)
-                mode_value = RequestMode::Cors;
-            else
-                return vm.throw_completion<JS::TypeError>(JS::ErrorType::InvalidEnumerationValue, mode_property_value_string, "RequestMode");
-        }
-        init.mode = mode_value;
-    }
-    auto redirect_property_value = JS::js_undefined();
-    if (arg1.is_object())
-        redirect_property_value = TRY(arg1.as_object().get("redirect"));
-    if (!redirect_property_value.is_undefined()) {
-        RequestRedirect redirect_value { RequestRedirect::Follow };
-        if (!redirect_property_value.is_undefined()) {
-            auto redirect_property_value_string = TRY(redirect_property_value.to_string(vm));
-            if (redirect_property_value_string == "follow"sv)
-                redirect_value = RequestRedirect::Follow;
-            else if (redirect_property_value_string == "error"sv)
-                redirect_value = RequestRedirect::Error;
-            else if (redirect_property_value_string == "manual"sv)
-                redirect_value = RequestRedirect::Manual;
-            else
-                return vm.throw_completion<JS::TypeError>(JS::ErrorType::InvalidEnumerationValue, redirect_property_value_string, "RequestRedirect");
-        }
-        init.redirect = redirect_value;
-    }
-    auto referrer_property_value = JS::js_undefined();
-    if (arg1.is_object())
-        referrer_property_value = TRY(arg1.as_object().get("referrer"));
-    if (!referrer_property_value.is_undefined()) {
-        Optional<String> referrer_value;
-        if (!referrer_property_value.is_undefined()) {
-            if (!false || !referrer_property_value.is_null())
-                referrer_value = TRY(referrer_property_value.to_string(vm));
-        }
-        if (referrer_value.has_value())
-            init.referrer = referrer_value.release_value();
-    }
-    auto referrer_policy_property_value = JS::js_undefined();
-    if (arg1.is_object())
-        referrer_policy_property_value = TRY(arg1.as_object().get("referrerPolicy"));
-    if (!referrer_policy_property_value.is_undefined()) {
-        ReferrerPolicy referrer_policy_value { ReferrerPolicy::Empty };
-        if (!referrer_policy_property_value.is_undefined()) {
-            auto referrer_policy_property_value_string = TRY(referrer_policy_property_value.to_string(vm));
-            if (referrer_policy_property_value_string == ""sv)
-                referrer_policy_value = ReferrerPolicy::Empty;
-            else if (referrer_policy_property_value_string == "no-referrer"sv)
-                referrer_policy_value = ReferrerPolicy::NoReferrer;
-            else if (referrer_policy_property_value_string == "no-referrer-when-downgrade"sv)
-                referrer_policy_value = ReferrerPolicy::NoReferrerWhenDowngrade;
-            else if (referrer_policy_property_value_string == "same-origin"sv)
-                referrer_policy_value = ReferrerPolicy::SameOrigin;
-            else if (referrer_policy_property_value_string == "origin"sv)
-                referrer_policy_value = ReferrerPolicy::Origin;
-            else if (referrer_policy_property_value_string == "strict-origin"sv)
-                referrer_policy_value = ReferrerPolicy::StrictOrigin;
-            else if (referrer_policy_property_value_string == "origin-when-cross-origin"sv)
-                referrer_policy_value = ReferrerPolicy::OriginWhenCrossOrigin;
-            else if (referrer_policy_property_value_string == "strict-origin-when-cross-origin"sv)
-                referrer_policy_value = ReferrerPolicy::StrictOriginWhenCrossOrigin;
-            else if (referrer_policy_property_value_string == "unsafe-url"sv)
-                referrer_policy_value = ReferrerPolicy::UnsafeUrl;
-            else
-                return vm.throw_completion<JS::TypeError>(JS::ErrorType::InvalidEnumerationValue, referrer_policy_property_value_string, "ReferrerPolicy");
-        }
-        init.referrer_policy = referrer_policy_value;
-    }
-    auto signal_property_value = JS::js_undefined();
-    if (arg1.is_object())
-        signal_property_value = TRY(arg1.as_object().get("signal"));
-    if (!signal_property_value.is_undefined()) {
-        AbortSignal* signal_value = nullptr;
-        if (!signal_property_value.is_nullish()) {
-            if (!signal_property_value.is_object() || !is<AbortSignal>(signal_property_value.as_object()))
-                return vm.throw_completion<JS::TypeError>(JS::ErrorType::NotAnObjectOfType, "AbortSignal");
-            signal_value = &static_cast<AbortSignal&>(signal_property_value.as_object());
-        }
-        init.signal = signal_value;
-    }
-    auto window_property_value = JS::js_undefined();
-    if (arg1.is_object())
-        window_property_value = TRY(arg1.as_object().get("window"));
-    if (!window_property_value.is_undefined()) {
-        JS::Value window_value = JS::js_undefined();
-        if (!window_property_value.is_undefined())
-            window_value = window_property_value;
-        init.window = window_value;
-    }
-    [[maybe_unused]] auto retval = TRY(throw_dom_exception_if_needed(vm, [&] { return Fetch::fetch_impl(vm, input, init); }));
-    return retval;
-}
-// NOLINTEND
-
-}

+ 0 - 15
Userland/Libraries/LibWeb/Bindings/FetchMethod.h

@@ -1,15 +0,0 @@
-/*
- * Copyright (c) 2022, Linus Groh <linusg@serenityos.org>
- *
- * SPDX-License-Identifier: BSD-2-Clause
- */
-
-#pragma once
-
-#include <LibJS/Forward.h>
-
-namespace Web::Bindings {
-
-JS::ThrowCompletionOr<JS::Value> fetch(JS::VM&);
-
-}

+ 0 - 1
Userland/Libraries/LibWeb/CMakeLists.txt

@@ -6,7 +6,6 @@ set(SOURCES
     ARIA/Roles.cpp
     Bindings/AudioConstructor.cpp
     Bindings/CSSNamespace.cpp
-    Bindings/FetchMethod.cpp
     Bindings/HostDefined.cpp
     Bindings/ImageConstructor.cpp
     Bindings/Intrinsics.cpp

+ 2 - 1
Userland/Libraries/LibWeb/Fetch/FetchMethod.cpp

@@ -19,12 +19,13 @@
 #include <LibWeb/Fetch/Infrastructure/HTTP/Responses.h>
 #include <LibWeb/Fetch/Request.h>
 #include <LibWeb/Fetch/Response.h>
+#include <LibWeb/WebIDL/ExceptionOr.h>
 #include <LibWeb/WebIDL/Promise.h>
 
 namespace Web::Fetch {
 
 // https://fetch.spec.whatwg.org/#dom-global-fetch
-JS::NonnullGCPtr<JS::Promise> fetch_impl(JS::VM& vm, RequestInfo const& input, RequestInit const& init)
+JS::NonnullGCPtr<JS::Promise> fetch(JS::VM& vm, RequestInfo const& input, RequestInit const& init)
 {
     auto& realm = *vm.current_realm();
 

+ 2 - 2
Userland/Libraries/LibWeb/Fetch/FetchMethod.h

@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2022, Linus Groh <linusg@serenityos.org>
+ * Copyright (c) 2022-2023, Linus Groh <linusg@serenityos.org>
  *
  * SPDX-License-Identifier: BSD-2-Clause
  */
@@ -14,7 +14,7 @@
 
 namespace Web::Fetch {
 
-JS::NonnullGCPtr<JS::Promise> fetch_impl(JS::VM&, RequestInfo const& input, RequestInit const& init = {});
+JS::NonnullGCPtr<JS::Promise> fetch(JS::VM&, RequestInfo const& input, RequestInit const& init = {});
 void abort_fetch(JS::Realm&, WebIDL::Promise const&, JS::NonnullGCPtr<Infrastructure::Request>, JS::GCPtr<Response>, JS::Value error);
 
 }

+ 1 - 0
Userland/Libraries/LibWeb/Fetch/Request.h

@@ -10,6 +10,7 @@
 #include <LibJS/Forward.h>
 #include <LibJS/Heap/GCPtr.h>
 #include <LibWeb/Bindings/PlatformObject.h>
+#include <LibWeb/Bindings/RequestPrototype.h>
 #include <LibWeb/Fetch/Body.h>
 #include <LibWeb/Fetch/BodyInit.h>
 #include <LibWeb/Fetch/Headers.h>

+ 1 - 0
Userland/Libraries/LibWeb/Fetch/Response.h

@@ -10,6 +10,7 @@
 #include <LibJS/Forward.h>
 #include <LibJS/Heap/GCPtr.h>
 #include <LibWeb/Bindings/PlatformObject.h>
+#include <LibWeb/Bindings/RequestPrototype.h>
 #include <LibWeb/Fetch/Body.h>
 #include <LibWeb/Fetch/BodyInit.h>
 #include <LibWeb/Fetch/Headers.h>

+ 0 - 3
Userland/Libraries/LibWeb/HTML/Window.cpp

@@ -21,7 +21,6 @@
 #include <LibTextCodec/Decoder.h>
 #include <LibWeb/Bindings/CSSNamespace.h>
 #include <LibWeb/Bindings/ExceptionOrUtils.h>
-#include <LibWeb/Bindings/FetchMethod.h>
 #include <LibWeb/Bindings/Replaceable.h>
 #include <LibWeb/Bindings/WindowExposedInterfaces.h>
 #include <LibWeb/Bindings/WindowPrototype.h>
@@ -896,8 +895,6 @@ WebIDL::ExceptionOr<void> Window::initialize_web_interfaces(Badge<WindowEnvironm
 
     define_native_function(realm, "structuredClone", structured_clone, 1, attr);
 
-    define_native_function(realm, "fetch", Bindings::fetch, 1, attr);
-
     define_direct_property("CSS", MUST_OR_THROW_OOM(heap().allocate<Bindings::CSSNamespace>(realm, realm)), 0);
 
     define_native_accessor(realm, "localStorage", local_storage_getter, {}, attr);

+ 1 - 0
Userland/Libraries/LibWeb/HTML/Window.h

@@ -60,6 +60,7 @@ public:
 
     using WindowOrWorkerGlobalScopeMixin::atob;
     using WindowOrWorkerGlobalScopeMixin::btoa;
+    using WindowOrWorkerGlobalScopeMixin::fetch;
 
     // ^DOM::EventTarget
     virtual bool dispatch_event(DOM::Event&) override;

+ 7 - 0
Userland/Libraries/LibWeb/HTML/WindowOrWorkerGlobalScope.cpp

@@ -10,6 +10,7 @@
 #include <AK/Utf8View.h>
 #include <AK/Vector.h>
 #include <LibTextCodec/Decoder.h>
+#include <LibWeb/Fetch/FetchMethod.h>
 #include <LibWeb/Forward.h>
 #include <LibWeb/HTML/WindowOrWorkerGlobalScope.h>
 #include <LibWeb/Infra/Base64.h>
@@ -83,4 +84,10 @@ WebIDL::ExceptionOr<String> WindowOrWorkerGlobalScopeMixin::atob(String const& d
     return TRY_OR_THROW_OOM(vm, decoder->to_utf8(decoded_data.value()));
 }
 
+JS::NonnullGCPtr<JS::Promise> WindowOrWorkerGlobalScopeMixin::fetch(Fetch::RequestInfo const& input, Fetch::RequestInit const& init) const
+{
+    auto& vm = this_impl().vm();
+    return Fetch::fetch(vm, input, init);
+}
+
 }

+ 2 - 0
Userland/Libraries/LibWeb/HTML/WindowOrWorkerGlobalScope.h

@@ -8,6 +8,7 @@
 
 #include <AK/Forward.h>
 #include <LibWeb/Bindings/PlatformObject.h>
+#include <LibWeb/Fetch/Request.h>
 #include <LibWeb/Forward.h>
 
 namespace Web::HTML {
@@ -26,6 +27,7 @@ public:
     bool cross_origin_isolated() const;
     WebIDL::ExceptionOr<String> btoa(String const& data) const;
     WebIDL::ExceptionOr<String> atob(String const& data) const;
+    JS::NonnullGCPtr<JS::Promise> fetch(Fetch::RequestInfo const&, Fetch::RequestInit const&) const;
 };
 
 }

+ 6 - 0
Userland/Libraries/LibWeb/HTML/WindowOrWorkerGlobalScope.idl

@@ -1,3 +1,6 @@
+#import <Fetch/Request.idl>
+#import <Fetch/Response.idl>
+
 // https://html.spec.whatwg.org/multipage/webappapis.html#windoworworkerglobalscope
 interface mixin WindowOrWorkerGlobalScope {
     [Replaceable] readonly attribute USVString origin;
@@ -25,4 +28,7 @@ interface mixin WindowOrWorkerGlobalScope {
 
     // structured cloning
     // FIXME: any structuredClone(any value, optional StructuredSerializeOptions options = {});
+
+    // https://fetch.spec.whatwg.org/#fetch-method
+    [NewObject] Promise<Response> fetch(RequestInfo input, optional RequestInit init = {});
 };