1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-05-19 19:45:08 +00:00
serenity/Userland/Libraries/LibWeb/Bindings/FetchMethod.cpp
Linus Groh 1de1d6423b LibWeb: Implement the fetch() method :^)
With so much infrastructure implemented, we can finally add the last
piece of this puzzle - the fetch() method itself!

This contains a few hundred lines of generated code as handling the
RequestInfo and RequestInfo parameter types manually is not feasible,
but we can't use the IDL definition as the Window object is handwritten
code at the moment.
It's neatly tucked away in Bindings/ and will be removed eventually.
2022-10-30 20:10:29 +00:00

371 lines
19 KiB
C++

/*
* Copyright (c) 2022, 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:
// interface Dummy {
// static Promise<Response> fetch(RequestInfo input, optional RequestInfo 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 == "only-if-cached"sv)
cache_value = RequestCache::OnlyIfCached;
else 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 == "force-cache"sv)
cache_value = RequestCache::ForceCache;
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
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 == "same-origin"sv)
credentials_value = RequestCredentials::SameOrigin;
else if (credentials_property_value_string == "include"sv)
credentials_value = RequestCredentials::Include;
else if (credentials_property_value_string == "omit"sv)
credentials_value = RequestCredentials::Omit;
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, 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, 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 (next_item2.is_null() && false) {
sequence_item2 = String::empty();
} else {
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 (key1.is_null() && false) {
typed_key1 = String::empty();
} else {
typed_key1 = TRY(key1.to_string(vm));
}
auto value1 = TRY(headers_property_value_object.get(property_key1));
String typed_value1;
if (value1.is_null() && false) {
typed_value1 = String::empty();
} else {
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");
};
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()) {
String integrity_value;
if (!integrity_property_value.is_undefined()) {
if (integrity_property_value.is_null() && false)
integrity_value = String::empty();
else
integrity_value = TRY(integrity_property_value.to_string(vm));
}
init.integrity = integrity_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()) {
String method_value;
if (!method_property_value.is_undefined()) {
if (method_property_value.is_null() && false)
method_value = String::empty();
else
method_value = TRY(method_property_value.to_string(vm));
}
init.method = method_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 == "manual"sv)
redirect_value = RequestRedirect::Manual;
else if (redirect_property_value_string == "error"sv)
redirect_value = RequestRedirect::Error;
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()) {
String referrer_value;
if (!referrer_property_value.is_undefined()) {
if (referrer_property_value.is_null() && false)
referrer_value = String::empty();
else
referrer_value = TRY(referrer_property_value.to_string(vm));
}
init.referrer = referrer_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 == "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 == "origin-when-cross-origin"sv)
referrer_policy_value = ReferrerPolicy::OriginWhenCrossOrigin;
else if (referrer_policy_property_value_string == "strict-origin"sv)
referrer_policy_value = ReferrerPolicy::StrictOrigin;
else if (referrer_policy_property_value_string == "no-referrer"sv)
referrer_policy_value = ReferrerPolicy::NoReferrer;
else if (referrer_policy_property_value_string == "unsafe-url"sv)
referrer_policy_value = ReferrerPolicy::UnsafeUrl;
else if (referrer_policy_property_value_string == "no-referrer-when-downgrade"sv)
referrer_policy_value = ReferrerPolicy::NoReferrerWhenDowngrade;
else if (referrer_policy_property_value_string == "strict-origin-when-cross-origin"sv)
referrer_policy_value = ReferrerPolicy::StrictOriginWhenCrossOrigin;
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
}