#include <LibJS/Runtime/Error.h>
#include <LibJS/Runtime/Object.h>
#include <LibJS/Runtime/Value.h>
#include <LibJS/Runtime/ValueInlines.h>
#include <LibWeb/Bindings/ExceptionOrUtils.h>
#include <LibWeb/Bindings/Intrinsics.h>
#include <LibWeb/Bindings/PerformanceTiming.h>
#include <LibWeb/NavigationTiming/PerformanceTiming.h>
#include <LibWeb/WebIDL/Tracing.h>
#include <LibWeb/WebIDL/Types.h>

namespace Web::Bindings {

void PerformanceTimingConstructor::initialize(JS::Realm& realm, JS::NativeFunction& object)
{
    auto& vm = realm.vm();
    [[maybe_unused]] u8 default_attributes = JS::Attribute::Enumerable;

    
    object.define_direct_property(vm.names.length, JS::Value(0), JS::Attribute::Configurable);
    object.define_direct_property(vm.names.name, JS::PrimitiveString::create(vm, "PerformanceTiming"_utf16), JS::Attribute::Configurable);
    object.define_direct_property(vm.names.prototype, &ensure_web_prototype<PerformanceTimingPrototype>(realm, "PerformanceTiming"_fly_string), 0);
}

JS::ThrowCompletionOr<GC::Ref<JS::Object>> PerformanceTimingConstructor::construct([[maybe_unused]] InterfaceConstructor& constructor, [[maybe_unused]] JS::FunctionObject& new_target)
{
    WebIDL::log_trace(constructor.vm(), "PerformanceTimingConstructor::construct");
    return constructor.vm().throw_completion<JS::TypeError>(JS::ErrorType::NotAConstructor, "PerformanceTiming");
}

void PerformanceTimingPrototype::initialize(JS::Realm& realm, JS::Object& object)
{
    [[maybe_unused]] auto& vm = realm.vm();
    [[maybe_unused]] u8 default_attributes = JS::Attribute::Enumerable | JS::Attribute::Configurable | JS::Attribute::Writable;

    object.set_prototype(realm.intrinsics().object_prototype());

    auto navigation_start_id = "navigationStart"_utf16_fly_string;
    auto native_navigation_start_getter = JS::NativeFunction::create(realm, navigation_start_getter, 0, navigation_start_id, &realm, "get"sv);
    GC::Ptr<JS::NativeFunction> native_navigation_start_setter;

    // 4. Let configurable be false if attr is unforgeable and true otherwise.
    auto navigation_start_attributes = default_attributes;

    // 5. Let desc be the PropertyDescriptor{[[Get]]: getter, [[Set]]: setter, [[Enumerable]]: true, [[Configurable]]: configurable}.

    // 7. Perform ! DefinePropertyOrThrow(target, id, desc).
    object.define_direct_accessor(navigation_start_id, native_navigation_start_getter, native_navigation_start_setter, navigation_start_attributes);

    // 8. FIXME: If attr’s type is an observable array type with type argument T, then set target’s backing observable array exotic object for attr to the result of creating an observable array exotic object in realm, given T, attr’s set an indexed value algorithm, and attr’s delete an indexed value algorithm.
    auto unload_event_start_id = "unloadEventStart"_utf16_fly_string;
    auto native_unload_event_start_getter = JS::NativeFunction::create(realm, unload_event_start_getter, 0, unload_event_start_id, &realm, "get"sv);
    GC::Ptr<JS::NativeFunction> native_unload_event_start_setter;

    // 4. Let configurable be false if attr is unforgeable and true otherwise.
    auto unload_event_start_attributes = default_attributes;

    // 5. Let desc be the PropertyDescriptor{[[Get]]: getter, [[Set]]: setter, [[Enumerable]]: true, [[Configurable]]: configurable}.

    // 7. Perform ! DefinePropertyOrThrow(target, id, desc).
    object.define_direct_accessor(unload_event_start_id, native_unload_event_start_getter, native_unload_event_start_setter, unload_event_start_attributes);

    // 8. FIXME: If attr’s type is an observable array type with type argument T, then set target’s backing observable array exotic object for attr to the result of creating an observable array exotic object in realm, given T, attr’s set an indexed value algorithm, and attr’s delete an indexed value algorithm.
    auto unload_event_end_id = "unloadEventEnd"_utf16_fly_string;
    auto native_unload_event_end_getter = JS::NativeFunction::create(realm, unload_event_end_getter, 0, unload_event_end_id, &realm, "get"sv);
    GC::Ptr<JS::NativeFunction> native_unload_event_end_setter;

    // 4. Let configurable be false if attr is unforgeable and true otherwise.
    auto unload_event_end_attributes = default_attributes;

    // 5. Let desc be the PropertyDescriptor{[[Get]]: getter, [[Set]]: setter, [[Enumerable]]: true, [[Configurable]]: configurable}.

    // 7. Perform ! DefinePropertyOrThrow(target, id, desc).
    object.define_direct_accessor(unload_event_end_id, native_unload_event_end_getter, native_unload_event_end_setter, unload_event_end_attributes);

    // 8. FIXME: If attr’s type is an observable array type with type argument T, then set target’s backing observable array exotic object for attr to the result of creating an observable array exotic object in realm, given T, attr’s set an indexed value algorithm, and attr’s delete an indexed value algorithm.
    auto redirect_start_id = "redirectStart"_utf16_fly_string;
    auto native_redirect_start_getter = JS::NativeFunction::create(realm, redirect_start_getter, 0, redirect_start_id, &realm, "get"sv);
    GC::Ptr<JS::NativeFunction> native_redirect_start_setter;

    // 4. Let configurable be false if attr is unforgeable and true otherwise.
    auto redirect_start_attributes = default_attributes;

    // 5. Let desc be the PropertyDescriptor{[[Get]]: getter, [[Set]]: setter, [[Enumerable]]: true, [[Configurable]]: configurable}.

    // 7. Perform ! DefinePropertyOrThrow(target, id, desc).
    object.define_direct_accessor(redirect_start_id, native_redirect_start_getter, native_redirect_start_setter, redirect_start_attributes);

    // 8. FIXME: If attr’s type is an observable array type with type argument T, then set target’s backing observable array exotic object for attr to the result of creating an observable array exotic object in realm, given T, attr’s set an indexed value algorithm, and attr’s delete an indexed value algorithm.
    auto redirect_end_id = "redirectEnd"_utf16_fly_string;
    auto native_redirect_end_getter = JS::NativeFunction::create(realm, redirect_end_getter, 0, redirect_end_id, &realm, "get"sv);
    GC::Ptr<JS::NativeFunction> native_redirect_end_setter;

    // 4. Let configurable be false if attr is unforgeable and true otherwise.
    auto redirect_end_attributes = default_attributes;

    // 5. Let desc be the PropertyDescriptor{[[Get]]: getter, [[Set]]: setter, [[Enumerable]]: true, [[Configurable]]: configurable}.

    // 7. Perform ! DefinePropertyOrThrow(target, id, desc).
    object.define_direct_accessor(redirect_end_id, native_redirect_end_getter, native_redirect_end_setter, redirect_end_attributes);

    // 8. FIXME: If attr’s type is an observable array type with type argument T, then set target’s backing observable array exotic object for attr to the result of creating an observable array exotic object in realm, given T, attr’s set an indexed value algorithm, and attr’s delete an indexed value algorithm.
    auto fetch_start_id = "fetchStart"_utf16_fly_string;
    auto native_fetch_start_getter = JS::NativeFunction::create(realm, fetch_start_getter, 0, fetch_start_id, &realm, "get"sv);
    GC::Ptr<JS::NativeFunction> native_fetch_start_setter;

    // 4. Let configurable be false if attr is unforgeable and true otherwise.
    auto fetch_start_attributes = default_attributes;

    // 5. Let desc be the PropertyDescriptor{[[Get]]: getter, [[Set]]: setter, [[Enumerable]]: true, [[Configurable]]: configurable}.

    // 7. Perform ! DefinePropertyOrThrow(target, id, desc).
    object.define_direct_accessor(fetch_start_id, native_fetch_start_getter, native_fetch_start_setter, fetch_start_attributes);

    // 8. FIXME: If attr’s type is an observable array type with type argument T, then set target’s backing observable array exotic object for attr to the result of creating an observable array exotic object in realm, given T, attr’s set an indexed value algorithm, and attr’s delete an indexed value algorithm.
    auto domain_lookup_start_id = "domainLookupStart"_utf16_fly_string;
    auto native_domain_lookup_start_getter = JS::NativeFunction::create(realm, domain_lookup_start_getter, 0, domain_lookup_start_id, &realm, "get"sv);
    GC::Ptr<JS::NativeFunction> native_domain_lookup_start_setter;

    // 4. Let configurable be false if attr is unforgeable and true otherwise.
    auto domain_lookup_start_attributes = default_attributes;

    // 5. Let desc be the PropertyDescriptor{[[Get]]: getter, [[Set]]: setter, [[Enumerable]]: true, [[Configurable]]: configurable}.

    // 7. Perform ! DefinePropertyOrThrow(target, id, desc).
    object.define_direct_accessor(domain_lookup_start_id, native_domain_lookup_start_getter, native_domain_lookup_start_setter, domain_lookup_start_attributes);

    // 8. FIXME: If attr’s type is an observable array type with type argument T, then set target’s backing observable array exotic object for attr to the result of creating an observable array exotic object in realm, given T, attr’s set an indexed value algorithm, and attr’s delete an indexed value algorithm.
    auto domain_lookup_end_id = "domainLookupEnd"_utf16_fly_string;
    auto native_domain_lookup_end_getter = JS::NativeFunction::create(realm, domain_lookup_end_getter, 0, domain_lookup_end_id, &realm, "get"sv);
    GC::Ptr<JS::NativeFunction> native_domain_lookup_end_setter;

    // 4. Let configurable be false if attr is unforgeable and true otherwise.
    auto domain_lookup_end_attributes = default_attributes;

    // 5. Let desc be the PropertyDescriptor{[[Get]]: getter, [[Set]]: setter, [[Enumerable]]: true, [[Configurable]]: configurable}.

    // 7. Perform ! DefinePropertyOrThrow(target, id, desc).
    object.define_direct_accessor(domain_lookup_end_id, native_domain_lookup_end_getter, native_domain_lookup_end_setter, domain_lookup_end_attributes);

    // 8. FIXME: If attr’s type is an observable array type with type argument T, then set target’s backing observable array exotic object for attr to the result of creating an observable array exotic object in realm, given T, attr’s set an indexed value algorithm, and attr’s delete an indexed value algorithm.
    auto connect_start_id = "connectStart"_utf16_fly_string;
    auto native_connect_start_getter = JS::NativeFunction::create(realm, connect_start_getter, 0, connect_start_id, &realm, "get"sv);
    GC::Ptr<JS::NativeFunction> native_connect_start_setter;

    // 4. Let configurable be false if attr is unforgeable and true otherwise.
    auto connect_start_attributes = default_attributes;

    // 5. Let desc be the PropertyDescriptor{[[Get]]: getter, [[Set]]: setter, [[Enumerable]]: true, [[Configurable]]: configurable}.

    // 7. Perform ! DefinePropertyOrThrow(target, id, desc).
    object.define_direct_accessor(connect_start_id, native_connect_start_getter, native_connect_start_setter, connect_start_attributes);

    // 8. FIXME: If attr’s type is an observable array type with type argument T, then set target’s backing observable array exotic object for attr to the result of creating an observable array exotic object in realm, given T, attr’s set an indexed value algorithm, and attr’s delete an indexed value algorithm.
    auto connect_end_id = "connectEnd"_utf16_fly_string;
    auto native_connect_end_getter = JS::NativeFunction::create(realm, connect_end_getter, 0, connect_end_id, &realm, "get"sv);
    GC::Ptr<JS::NativeFunction> native_connect_end_setter;

    // 4. Let configurable be false if attr is unforgeable and true otherwise.
    auto connect_end_attributes = default_attributes;

    // 5. Let desc be the PropertyDescriptor{[[Get]]: getter, [[Set]]: setter, [[Enumerable]]: true, [[Configurable]]: configurable}.

    // 7. Perform ! DefinePropertyOrThrow(target, id, desc).
    object.define_direct_accessor(connect_end_id, native_connect_end_getter, native_connect_end_setter, connect_end_attributes);

    // 8. FIXME: If attr’s type is an observable array type with type argument T, then set target’s backing observable array exotic object for attr to the result of creating an observable array exotic object in realm, given T, attr’s set an indexed value algorithm, and attr’s delete an indexed value algorithm.
    auto secure_connection_start_id = "secureConnectionStart"_utf16_fly_string;
    auto native_secure_connection_start_getter = JS::NativeFunction::create(realm, secure_connection_start_getter, 0, secure_connection_start_id, &realm, "get"sv);
    GC::Ptr<JS::NativeFunction> native_secure_connection_start_setter;

    // 4. Let configurable be false if attr is unforgeable and true otherwise.
    auto secure_connection_start_attributes = default_attributes;

    // 5. Let desc be the PropertyDescriptor{[[Get]]: getter, [[Set]]: setter, [[Enumerable]]: true, [[Configurable]]: configurable}.

    // 7. Perform ! DefinePropertyOrThrow(target, id, desc).
    object.define_direct_accessor(secure_connection_start_id, native_secure_connection_start_getter, native_secure_connection_start_setter, secure_connection_start_attributes);

    // 8. FIXME: If attr’s type is an observable array type with type argument T, then set target’s backing observable array exotic object for attr to the result of creating an observable array exotic object in realm, given T, attr’s set an indexed value algorithm, and attr’s delete an indexed value algorithm.
    auto request_start_id = "requestStart"_utf16_fly_string;
    auto native_request_start_getter = JS::NativeFunction::create(realm, request_start_getter, 0, request_start_id, &realm, "get"sv);
    GC::Ptr<JS::NativeFunction> native_request_start_setter;

    // 4. Let configurable be false if attr is unforgeable and true otherwise.
    auto request_start_attributes = default_attributes;

    // 5. Let desc be the PropertyDescriptor{[[Get]]: getter, [[Set]]: setter, [[Enumerable]]: true, [[Configurable]]: configurable}.

    // 7. Perform ! DefinePropertyOrThrow(target, id, desc).
    object.define_direct_accessor(request_start_id, native_request_start_getter, native_request_start_setter, request_start_attributes);

    // 8. FIXME: If attr’s type is an observable array type with type argument T, then set target’s backing observable array exotic object for attr to the result of creating an observable array exotic object in realm, given T, attr’s set an indexed value algorithm, and attr’s delete an indexed value algorithm.
    auto response_start_id = "responseStart"_utf16_fly_string;
    auto native_response_start_getter = JS::NativeFunction::create(realm, response_start_getter, 0, response_start_id, &realm, "get"sv);
    GC::Ptr<JS::NativeFunction> native_response_start_setter;

    // 4. Let configurable be false if attr is unforgeable and true otherwise.
    auto response_start_attributes = default_attributes;

    // 5. Let desc be the PropertyDescriptor{[[Get]]: getter, [[Set]]: setter, [[Enumerable]]: true, [[Configurable]]: configurable}.

    // 7. Perform ! DefinePropertyOrThrow(target, id, desc).
    object.define_direct_accessor(response_start_id, native_response_start_getter, native_response_start_setter, response_start_attributes);

    // 8. FIXME: If attr’s type is an observable array type with type argument T, then set target’s backing observable array exotic object for attr to the result of creating an observable array exotic object in realm, given T, attr’s set an indexed value algorithm, and attr’s delete an indexed value algorithm.
    auto response_end_id = "responseEnd"_utf16_fly_string;
    auto native_response_end_getter = JS::NativeFunction::create(realm, response_end_getter, 0, response_end_id, &realm, "get"sv);
    GC::Ptr<JS::NativeFunction> native_response_end_setter;

    // 4. Let configurable be false if attr is unforgeable and true otherwise.
    auto response_end_attributes = default_attributes;

    // 5. Let desc be the PropertyDescriptor{[[Get]]: getter, [[Set]]: setter, [[Enumerable]]: true, [[Configurable]]: configurable}.

    // 7. Perform ! DefinePropertyOrThrow(target, id, desc).
    object.define_direct_accessor(response_end_id, native_response_end_getter, native_response_end_setter, response_end_attributes);

    // 8. FIXME: If attr’s type is an observable array type with type argument T, then set target’s backing observable array exotic object for attr to the result of creating an observable array exotic object in realm, given T, attr’s set an indexed value algorithm, and attr’s delete an indexed value algorithm.
    auto dom_loading_id = "domLoading"_utf16_fly_string;
    auto native_dom_loading_getter = JS::NativeFunction::create(realm, dom_loading_getter, 0, dom_loading_id, &realm, "get"sv);
    GC::Ptr<JS::NativeFunction> native_dom_loading_setter;

    // 4. Let configurable be false if attr is unforgeable and true otherwise.
    auto dom_loading_attributes = default_attributes;

    // 5. Let desc be the PropertyDescriptor{[[Get]]: getter, [[Set]]: setter, [[Enumerable]]: true, [[Configurable]]: configurable}.

    // 7. Perform ! DefinePropertyOrThrow(target, id, desc).
    object.define_direct_accessor(dom_loading_id, native_dom_loading_getter, native_dom_loading_setter, dom_loading_attributes);

    // 8. FIXME: If attr’s type is an observable array type with type argument T, then set target’s backing observable array exotic object for attr to the result of creating an observable array exotic object in realm, given T, attr’s set an indexed value algorithm, and attr’s delete an indexed value algorithm.
    auto dom_interactive_id = "domInteractive"_utf16_fly_string;
    auto native_dom_interactive_getter = JS::NativeFunction::create(realm, dom_interactive_getter, 0, dom_interactive_id, &realm, "get"sv);
    GC::Ptr<JS::NativeFunction> native_dom_interactive_setter;

    // 4. Let configurable be false if attr is unforgeable and true otherwise.
    auto dom_interactive_attributes = default_attributes;

    // 5. Let desc be the PropertyDescriptor{[[Get]]: getter, [[Set]]: setter, [[Enumerable]]: true, [[Configurable]]: configurable}.

    // 7. Perform ! DefinePropertyOrThrow(target, id, desc).
    object.define_direct_accessor(dom_interactive_id, native_dom_interactive_getter, native_dom_interactive_setter, dom_interactive_attributes);

    // 8. FIXME: If attr’s type is an observable array type with type argument T, then set target’s backing observable array exotic object for attr to the result of creating an observable array exotic object in realm, given T, attr’s set an indexed value algorithm, and attr’s delete an indexed value algorithm.
    auto dom_content_loaded_event_start_id = "domContentLoadedEventStart"_utf16_fly_string;
    auto native_dom_content_loaded_event_start_getter = JS::NativeFunction::create(realm, dom_content_loaded_event_start_getter, 0, dom_content_loaded_event_start_id, &realm, "get"sv);
    GC::Ptr<JS::NativeFunction> native_dom_content_loaded_event_start_setter;

    // 4. Let configurable be false if attr is unforgeable and true otherwise.
    auto dom_content_loaded_event_start_attributes = default_attributes;

    // 5. Let desc be the PropertyDescriptor{[[Get]]: getter, [[Set]]: setter, [[Enumerable]]: true, [[Configurable]]: configurable}.

    // 7. Perform ! DefinePropertyOrThrow(target, id, desc).
    object.define_direct_accessor(dom_content_loaded_event_start_id, native_dom_content_loaded_event_start_getter, native_dom_content_loaded_event_start_setter, dom_content_loaded_event_start_attributes);

    // 8. FIXME: If attr’s type is an observable array type with type argument T, then set target’s backing observable array exotic object for attr to the result of creating an observable array exotic object in realm, given T, attr’s set an indexed value algorithm, and attr’s delete an indexed value algorithm.
    auto dom_content_loaded_event_end_id = "domContentLoadedEventEnd"_utf16_fly_string;
    auto native_dom_content_loaded_event_end_getter = JS::NativeFunction::create(realm, dom_content_loaded_event_end_getter, 0, dom_content_loaded_event_end_id, &realm, "get"sv);
    GC::Ptr<JS::NativeFunction> native_dom_content_loaded_event_end_setter;

    // 4. Let configurable be false if attr is unforgeable and true otherwise.
    auto dom_content_loaded_event_end_attributes = default_attributes;

    // 5. Let desc be the PropertyDescriptor{[[Get]]: getter, [[Set]]: setter, [[Enumerable]]: true, [[Configurable]]: configurable}.

    // 7. Perform ! DefinePropertyOrThrow(target, id, desc).
    object.define_direct_accessor(dom_content_loaded_event_end_id, native_dom_content_loaded_event_end_getter, native_dom_content_loaded_event_end_setter, dom_content_loaded_event_end_attributes);

    // 8. FIXME: If attr’s type is an observable array type with type argument T, then set target’s backing observable array exotic object for attr to the result of creating an observable array exotic object in realm, given T, attr’s set an indexed value algorithm, and attr’s delete an indexed value algorithm.
    auto dom_complete_id = "domComplete"_utf16_fly_string;
    auto native_dom_complete_getter = JS::NativeFunction::create(realm, dom_complete_getter, 0, dom_complete_id, &realm, "get"sv);
    GC::Ptr<JS::NativeFunction> native_dom_complete_setter;

    // 4. Let configurable be false if attr is unforgeable and true otherwise.
    auto dom_complete_attributes = default_attributes;

    // 5. Let desc be the PropertyDescriptor{[[Get]]: getter, [[Set]]: setter, [[Enumerable]]: true, [[Configurable]]: configurable}.

    // 7. Perform ! DefinePropertyOrThrow(target, id, desc).
    object.define_direct_accessor(dom_complete_id, native_dom_complete_getter, native_dom_complete_setter, dom_complete_attributes);

    // 8. FIXME: If attr’s type is an observable array type with type argument T, then set target’s backing observable array exotic object for attr to the result of creating an observable array exotic object in realm, given T, attr’s set an indexed value algorithm, and attr’s delete an indexed value algorithm.
    auto load_event_start_id = "loadEventStart"_utf16_fly_string;
    auto native_load_event_start_getter = JS::NativeFunction::create(realm, load_event_start_getter, 0, load_event_start_id, &realm, "get"sv);
    GC::Ptr<JS::NativeFunction> native_load_event_start_setter;

    // 4. Let configurable be false if attr is unforgeable and true otherwise.
    auto load_event_start_attributes = default_attributes;

    // 5. Let desc be the PropertyDescriptor{[[Get]]: getter, [[Set]]: setter, [[Enumerable]]: true, [[Configurable]]: configurable}.

    // 7. Perform ! DefinePropertyOrThrow(target, id, desc).
    object.define_direct_accessor(load_event_start_id, native_load_event_start_getter, native_load_event_start_setter, load_event_start_attributes);

    // 8. FIXME: If attr’s type is an observable array type with type argument T, then set target’s backing observable array exotic object for attr to the result of creating an observable array exotic object in realm, given T, attr’s set an indexed value algorithm, and attr’s delete an indexed value algorithm.
    auto load_event_end_id = "loadEventEnd"_utf16_fly_string;
    auto native_load_event_end_getter = JS::NativeFunction::create(realm, load_event_end_getter, 0, load_event_end_id, &realm, "get"sv);
    GC::Ptr<JS::NativeFunction> native_load_event_end_setter;

    // 4. Let configurable be false if attr is unforgeable and true otherwise.
    auto load_event_end_attributes = default_attributes;

    // 5. Let desc be the PropertyDescriptor{[[Get]]: getter, [[Set]]: setter, [[Enumerable]]: true, [[Configurable]]: configurable}.

    // 7. Perform ! DefinePropertyOrThrow(target, id, desc).
    object.define_direct_accessor(load_event_end_id, native_load_event_end_getter, native_load_event_end_setter, load_event_end_attributes);

    // 8. FIXME: If attr’s type is an observable array type with type argument T, then set target’s backing observable array exotic object for attr to the result of creating an observable array exotic object in realm, given T, attr’s set an indexed value algorithm, and attr’s delete an indexed value algorithm.
    object.define_native_function(realm, "toJSON"_utf16_fly_string, to_json, 0, default_attributes);

    object.define_direct_property(vm.well_known_symbol_to_string_tag(), JS::PrimitiveString::create(vm, "PerformanceTiming"_utf16), JS::Attribute::Configurable);
}

void PerformanceTimingPrototype::define_unforgeable_attributes(JS::Realm& realm, [[maybe_unused]] JS::Object& object)
{
    [[maybe_unused]] auto& vm = realm.vm();
    [[maybe_unused]] u8 default_attributes = JS::Attribute::Enumerable;
}

[[maybe_unused]] static JS::ThrowCompletionOr<NavigationTiming::PerformanceTiming*> impl_from(JS::VM& vm, JS::Value js_value)
{

    if (auto impl = js_value.as_if<NavigationTiming::PerformanceTiming>())
        return impl.ptr();
    return vm.throw_completion<JS::TypeError>(JS::ErrorType::NotAnObjectOfType, "PerformanceTiming");
}

[[maybe_unused]] static JS::ThrowCompletionOr<NavigationTiming::PerformanceTiming*> impl_from(JS::VM& vm)
{
    auto this_value = vm.this_value();
    if (this_value.is_nullish())
        this_value = &vm.current_realm()->global_object();
    return impl_from(vm, this_value);
}

JS_DEFINE_NATIVE_FUNCTION(PerformanceTimingPrototype::navigation_start_getter)
{
    WebIDL::log_trace(vm, "PerformanceTimingPrototype::navigation_start_getter");
    [[maybe_unused]] auto& realm = *vm.current_realm();

    auto* idl_object = TRY(impl_from(vm));


    auto R = TRY(throw_dom_exception_if_needed(vm, [&] { return idl_object->navigation_start(); }));

    return JS::Value(static_cast<double>(R));
}

JS_DEFINE_NATIVE_FUNCTION(PerformanceTimingPrototype::unload_event_start_getter)
{
    WebIDL::log_trace(vm, "PerformanceTimingPrototype::unload_event_start_getter");
    [[maybe_unused]] auto& realm = *vm.current_realm();

    auto* idl_object = TRY(impl_from(vm));


    auto R = TRY(throw_dom_exception_if_needed(vm, [&] { return idl_object->unload_event_start(); }));

    return JS::Value(static_cast<double>(R));
}

JS_DEFINE_NATIVE_FUNCTION(PerformanceTimingPrototype::unload_event_end_getter)
{
    WebIDL::log_trace(vm, "PerformanceTimingPrototype::unload_event_end_getter");
    [[maybe_unused]] auto& realm = *vm.current_realm();

    auto* idl_object = TRY(impl_from(vm));


    auto R = TRY(throw_dom_exception_if_needed(vm, [&] { return idl_object->unload_event_end(); }));

    return JS::Value(static_cast<double>(R));
}

JS_DEFINE_NATIVE_FUNCTION(PerformanceTimingPrototype::redirect_start_getter)
{
    WebIDL::log_trace(vm, "PerformanceTimingPrototype::redirect_start_getter");
    [[maybe_unused]] auto& realm = *vm.current_realm();

    auto* idl_object = TRY(impl_from(vm));


    auto R = TRY(throw_dom_exception_if_needed(vm, [&] { return idl_object->redirect_start(); }));

    return JS::Value(static_cast<double>(R));
}

JS_DEFINE_NATIVE_FUNCTION(PerformanceTimingPrototype::redirect_end_getter)
{
    WebIDL::log_trace(vm, "PerformanceTimingPrototype::redirect_end_getter");
    [[maybe_unused]] auto& realm = *vm.current_realm();

    auto* idl_object = TRY(impl_from(vm));


    auto R = TRY(throw_dom_exception_if_needed(vm, [&] { return idl_object->redirect_end(); }));

    return JS::Value(static_cast<double>(R));
}

JS_DEFINE_NATIVE_FUNCTION(PerformanceTimingPrototype::fetch_start_getter)
{
    WebIDL::log_trace(vm, "PerformanceTimingPrototype::fetch_start_getter");
    [[maybe_unused]] auto& realm = *vm.current_realm();

    auto* idl_object = TRY(impl_from(vm));


    auto R = TRY(throw_dom_exception_if_needed(vm, [&] { return idl_object->fetch_start(); }));

    return JS::Value(static_cast<double>(R));
}

JS_DEFINE_NATIVE_FUNCTION(PerformanceTimingPrototype::domain_lookup_start_getter)
{
    WebIDL::log_trace(vm, "PerformanceTimingPrototype::domain_lookup_start_getter");
    [[maybe_unused]] auto& realm = *vm.current_realm();

    auto* idl_object = TRY(impl_from(vm));


    auto R = TRY(throw_dom_exception_if_needed(vm, [&] { return idl_object->domain_lookup_start(); }));

    return JS::Value(static_cast<double>(R));
}

JS_DEFINE_NATIVE_FUNCTION(PerformanceTimingPrototype::domain_lookup_end_getter)
{
    WebIDL::log_trace(vm, "PerformanceTimingPrototype::domain_lookup_end_getter");
    [[maybe_unused]] auto& realm = *vm.current_realm();

    auto* idl_object = TRY(impl_from(vm));


    auto R = TRY(throw_dom_exception_if_needed(vm, [&] { return idl_object->domain_lookup_end(); }));

    return JS::Value(static_cast<double>(R));
}

JS_DEFINE_NATIVE_FUNCTION(PerformanceTimingPrototype::connect_start_getter)
{
    WebIDL::log_trace(vm, "PerformanceTimingPrototype::connect_start_getter");
    [[maybe_unused]] auto& realm = *vm.current_realm();

    auto* idl_object = TRY(impl_from(vm));


    auto R = TRY(throw_dom_exception_if_needed(vm, [&] { return idl_object->connect_start(); }));

    return JS::Value(static_cast<double>(R));
}

JS_DEFINE_NATIVE_FUNCTION(PerformanceTimingPrototype::connect_end_getter)
{
    WebIDL::log_trace(vm, "PerformanceTimingPrototype::connect_end_getter");
    [[maybe_unused]] auto& realm = *vm.current_realm();

    auto* idl_object = TRY(impl_from(vm));


    auto R = TRY(throw_dom_exception_if_needed(vm, [&] { return idl_object->connect_end(); }));

    return JS::Value(static_cast<double>(R));
}

JS_DEFINE_NATIVE_FUNCTION(PerformanceTimingPrototype::secure_connection_start_getter)
{
    WebIDL::log_trace(vm, "PerformanceTimingPrototype::secure_connection_start_getter");
    [[maybe_unused]] auto& realm = *vm.current_realm();

    auto* idl_object = TRY(impl_from(vm));


    auto R = TRY(throw_dom_exception_if_needed(vm, [&] { return idl_object->secure_connection_start(); }));

    return JS::Value(static_cast<double>(R));
}

JS_DEFINE_NATIVE_FUNCTION(PerformanceTimingPrototype::request_start_getter)
{
    WebIDL::log_trace(vm, "PerformanceTimingPrototype::request_start_getter");
    [[maybe_unused]] auto& realm = *vm.current_realm();

    auto* idl_object = TRY(impl_from(vm));


    auto R = TRY(throw_dom_exception_if_needed(vm, [&] { return idl_object->request_start(); }));

    return JS::Value(static_cast<double>(R));
}

JS_DEFINE_NATIVE_FUNCTION(PerformanceTimingPrototype::response_start_getter)
{
    WebIDL::log_trace(vm, "PerformanceTimingPrototype::response_start_getter");
    [[maybe_unused]] auto& realm = *vm.current_realm();

    auto* idl_object = TRY(impl_from(vm));


    auto R = TRY(throw_dom_exception_if_needed(vm, [&] { return idl_object->response_start(); }));

    return JS::Value(static_cast<double>(R));
}

JS_DEFINE_NATIVE_FUNCTION(PerformanceTimingPrototype::response_end_getter)
{
    WebIDL::log_trace(vm, "PerformanceTimingPrototype::response_end_getter");
    [[maybe_unused]] auto& realm = *vm.current_realm();

    auto* idl_object = TRY(impl_from(vm));


    auto R = TRY(throw_dom_exception_if_needed(vm, [&] { return idl_object->response_end(); }));

    return JS::Value(static_cast<double>(R));
}

JS_DEFINE_NATIVE_FUNCTION(PerformanceTimingPrototype::dom_loading_getter)
{
    WebIDL::log_trace(vm, "PerformanceTimingPrototype::dom_loading_getter");
    [[maybe_unused]] auto& realm = *vm.current_realm();

    auto* idl_object = TRY(impl_from(vm));


    auto R = TRY(throw_dom_exception_if_needed(vm, [&] { return idl_object->dom_loading(); }));

    return JS::Value(static_cast<double>(R));
}

JS_DEFINE_NATIVE_FUNCTION(PerformanceTimingPrototype::dom_interactive_getter)
{
    WebIDL::log_trace(vm, "PerformanceTimingPrototype::dom_interactive_getter");
    [[maybe_unused]] auto& realm = *vm.current_realm();

    auto* idl_object = TRY(impl_from(vm));


    auto R = TRY(throw_dom_exception_if_needed(vm, [&] { return idl_object->dom_interactive(); }));

    return JS::Value(static_cast<double>(R));
}

JS_DEFINE_NATIVE_FUNCTION(PerformanceTimingPrototype::dom_content_loaded_event_start_getter)
{
    WebIDL::log_trace(vm, "PerformanceTimingPrototype::dom_content_loaded_event_start_getter");
    [[maybe_unused]] auto& realm = *vm.current_realm();

    auto* idl_object = TRY(impl_from(vm));


    auto R = TRY(throw_dom_exception_if_needed(vm, [&] { return idl_object->dom_content_loaded_event_start(); }));

    return JS::Value(static_cast<double>(R));
}

JS_DEFINE_NATIVE_FUNCTION(PerformanceTimingPrototype::dom_content_loaded_event_end_getter)
{
    WebIDL::log_trace(vm, "PerformanceTimingPrototype::dom_content_loaded_event_end_getter");
    [[maybe_unused]] auto& realm = *vm.current_realm();

    auto* idl_object = TRY(impl_from(vm));


    auto R = TRY(throw_dom_exception_if_needed(vm, [&] { return idl_object->dom_content_loaded_event_end(); }));

    return JS::Value(static_cast<double>(R));
}

JS_DEFINE_NATIVE_FUNCTION(PerformanceTimingPrototype::dom_complete_getter)
{
    WebIDL::log_trace(vm, "PerformanceTimingPrototype::dom_complete_getter");
    [[maybe_unused]] auto& realm = *vm.current_realm();

    auto* idl_object = TRY(impl_from(vm));


    auto R = TRY(throw_dom_exception_if_needed(vm, [&] { return idl_object->dom_complete(); }));

    return JS::Value(static_cast<double>(R));
}

JS_DEFINE_NATIVE_FUNCTION(PerformanceTimingPrototype::load_event_start_getter)
{
    WebIDL::log_trace(vm, "PerformanceTimingPrototype::load_event_start_getter");
    [[maybe_unused]] auto& realm = *vm.current_realm();

    auto* idl_object = TRY(impl_from(vm));


    auto R = TRY(throw_dom_exception_if_needed(vm, [&] { return idl_object->load_event_start(); }));

    return JS::Value(static_cast<double>(R));
}

JS_DEFINE_NATIVE_FUNCTION(PerformanceTimingPrototype::load_event_end_getter)
{
    WebIDL::log_trace(vm, "PerformanceTimingPrototype::load_event_end_getter");
    [[maybe_unused]] auto& realm = *vm.current_realm();

    auto* idl_object = TRY(impl_from(vm));


    auto R = TRY(throw_dom_exception_if_needed(vm, [&] { return idl_object->load_event_end(); }));

    return JS::Value(static_cast<double>(R));
}

JS_DEFINE_NATIVE_FUNCTION(PerformanceTimingPrototype::to_json)
{
    WebIDL::log_trace(vm, "PerformanceTimingPrototype::to_json");
    auto& realm = *vm.current_realm();

    [[maybe_unused]] auto* idl_object = TRY(impl_from(vm));

    // 4. Let result be OrdinaryObjectCreate(%Object.prototype%).
    auto result = JS::Object::create(realm, realm.intrinsics().object_prototype());

    // 5. For each key → value of map:
    auto navigation_start_0_value = TRY(throw_dom_exception_if_needed(vm, [&] { return idl_object->navigation_start(); }));

    // 1. Let k be key converted to a JavaScript value.
    auto navigation_start_0_key = "navigationStart"_utf16_fly_string;

    // 2. Let v be value converted to a JavaScript value.
    auto navigation_start_0_value_js = JS::Value(static_cast<double>(navigation_start_0_value));

    // 3. Perform ! CreateDataPropertyOrThrow(result, k, v).
    MUST(result->create_data_property(navigation_start_0_key, navigation_start_0_value_js));
    auto unload_event_start_1_value = TRY(throw_dom_exception_if_needed(vm, [&] { return idl_object->unload_event_start(); }));

    // 1. Let k be key converted to a JavaScript value.
    auto unload_event_start_1_key = "unloadEventStart"_utf16_fly_string;

    // 2. Let v be value converted to a JavaScript value.
    auto unload_event_start_1_value_js = JS::Value(static_cast<double>(unload_event_start_1_value));

    // 3. Perform ! CreateDataPropertyOrThrow(result, k, v).
    MUST(result->create_data_property(unload_event_start_1_key, unload_event_start_1_value_js));
    auto unload_event_end_2_value = TRY(throw_dom_exception_if_needed(vm, [&] { return idl_object->unload_event_end(); }));

    // 1. Let k be key converted to a JavaScript value.
    auto unload_event_end_2_key = "unloadEventEnd"_utf16_fly_string;

    // 2. Let v be value converted to a JavaScript value.
    auto unload_event_end_2_value_js = JS::Value(static_cast<double>(unload_event_end_2_value));

    // 3. Perform ! CreateDataPropertyOrThrow(result, k, v).
    MUST(result->create_data_property(unload_event_end_2_key, unload_event_end_2_value_js));
    auto redirect_start_3_value = TRY(throw_dom_exception_if_needed(vm, [&] { return idl_object->redirect_start(); }));

    // 1. Let k be key converted to a JavaScript value.
    auto redirect_start_3_key = "redirectStart"_utf16_fly_string;

    // 2. Let v be value converted to a JavaScript value.
    auto redirect_start_3_value_js = JS::Value(static_cast<double>(redirect_start_3_value));

    // 3. Perform ! CreateDataPropertyOrThrow(result, k, v).
    MUST(result->create_data_property(redirect_start_3_key, redirect_start_3_value_js));
    auto redirect_end_4_value = TRY(throw_dom_exception_if_needed(vm, [&] { return idl_object->redirect_end(); }));

    // 1. Let k be key converted to a JavaScript value.
    auto redirect_end_4_key = "redirectEnd"_utf16_fly_string;

    // 2. Let v be value converted to a JavaScript value.
    auto redirect_end_4_value_js = JS::Value(static_cast<double>(redirect_end_4_value));

    // 3. Perform ! CreateDataPropertyOrThrow(result, k, v).
    MUST(result->create_data_property(redirect_end_4_key, redirect_end_4_value_js));
    auto fetch_start_5_value = TRY(throw_dom_exception_if_needed(vm, [&] { return idl_object->fetch_start(); }));

    // 1. Let k be key converted to a JavaScript value.
    auto fetch_start_5_key = "fetchStart"_utf16_fly_string;

    // 2. Let v be value converted to a JavaScript value.
    auto fetch_start_5_value_js = JS::Value(static_cast<double>(fetch_start_5_value));

    // 3. Perform ! CreateDataPropertyOrThrow(result, k, v).
    MUST(result->create_data_property(fetch_start_5_key, fetch_start_5_value_js));
    auto domain_lookup_start_6_value = TRY(throw_dom_exception_if_needed(vm, [&] { return idl_object->domain_lookup_start(); }));

    // 1. Let k be key converted to a JavaScript value.
    auto domain_lookup_start_6_key = "domainLookupStart"_utf16_fly_string;

    // 2. Let v be value converted to a JavaScript value.
    auto domain_lookup_start_6_value_js = JS::Value(static_cast<double>(domain_lookup_start_6_value));

    // 3. Perform ! CreateDataPropertyOrThrow(result, k, v).
    MUST(result->create_data_property(domain_lookup_start_6_key, domain_lookup_start_6_value_js));
    auto domain_lookup_end_7_value = TRY(throw_dom_exception_if_needed(vm, [&] { return idl_object->domain_lookup_end(); }));

    // 1. Let k be key converted to a JavaScript value.
    auto domain_lookup_end_7_key = "domainLookupEnd"_utf16_fly_string;

    // 2. Let v be value converted to a JavaScript value.
    auto domain_lookup_end_7_value_js = JS::Value(static_cast<double>(domain_lookup_end_7_value));

    // 3. Perform ! CreateDataPropertyOrThrow(result, k, v).
    MUST(result->create_data_property(domain_lookup_end_7_key, domain_lookup_end_7_value_js));
    auto connect_start_8_value = TRY(throw_dom_exception_if_needed(vm, [&] { return idl_object->connect_start(); }));

    // 1. Let k be key converted to a JavaScript value.
    auto connect_start_8_key = "connectStart"_utf16_fly_string;

    // 2. Let v be value converted to a JavaScript value.
    auto connect_start_8_value_js = JS::Value(static_cast<double>(connect_start_8_value));

    // 3. Perform ! CreateDataPropertyOrThrow(result, k, v).
    MUST(result->create_data_property(connect_start_8_key, connect_start_8_value_js));
    auto connect_end_9_value = TRY(throw_dom_exception_if_needed(vm, [&] { return idl_object->connect_end(); }));

    // 1. Let k be key converted to a JavaScript value.
    auto connect_end_9_key = "connectEnd"_utf16_fly_string;

    // 2. Let v be value converted to a JavaScript value.
    auto connect_end_9_value_js = JS::Value(static_cast<double>(connect_end_9_value));

    // 3. Perform ! CreateDataPropertyOrThrow(result, k, v).
    MUST(result->create_data_property(connect_end_9_key, connect_end_9_value_js));
    auto secure_connection_start_10_value = TRY(throw_dom_exception_if_needed(vm, [&] { return idl_object->secure_connection_start(); }));

    // 1. Let k be key converted to a JavaScript value.
    auto secure_connection_start_10_key = "secureConnectionStart"_utf16_fly_string;

    // 2. Let v be value converted to a JavaScript value.
    auto secure_connection_start_10_value_js = JS::Value(static_cast<double>(secure_connection_start_10_value));

    // 3. Perform ! CreateDataPropertyOrThrow(result, k, v).
    MUST(result->create_data_property(secure_connection_start_10_key, secure_connection_start_10_value_js));
    auto request_start_11_value = TRY(throw_dom_exception_if_needed(vm, [&] { return idl_object->request_start(); }));

    // 1. Let k be key converted to a JavaScript value.
    auto request_start_11_key = "requestStart"_utf16_fly_string;

    // 2. Let v be value converted to a JavaScript value.
    auto request_start_11_value_js = JS::Value(static_cast<double>(request_start_11_value));

    // 3. Perform ! CreateDataPropertyOrThrow(result, k, v).
    MUST(result->create_data_property(request_start_11_key, request_start_11_value_js));
    auto response_start_12_value = TRY(throw_dom_exception_if_needed(vm, [&] { return idl_object->response_start(); }));

    // 1. Let k be key converted to a JavaScript value.
    auto response_start_12_key = "responseStart"_utf16_fly_string;

    // 2. Let v be value converted to a JavaScript value.
    auto response_start_12_value_js = JS::Value(static_cast<double>(response_start_12_value));

    // 3. Perform ! CreateDataPropertyOrThrow(result, k, v).
    MUST(result->create_data_property(response_start_12_key, response_start_12_value_js));
    auto response_end_13_value = TRY(throw_dom_exception_if_needed(vm, [&] { return idl_object->response_end(); }));

    // 1. Let k be key converted to a JavaScript value.
    auto response_end_13_key = "responseEnd"_utf16_fly_string;

    // 2. Let v be value converted to a JavaScript value.
    auto response_end_13_value_js = JS::Value(static_cast<double>(response_end_13_value));

    // 3. Perform ! CreateDataPropertyOrThrow(result, k, v).
    MUST(result->create_data_property(response_end_13_key, response_end_13_value_js));
    auto dom_loading_14_value = TRY(throw_dom_exception_if_needed(vm, [&] { return idl_object->dom_loading(); }));

    // 1. Let k be key converted to a JavaScript value.
    auto dom_loading_14_key = "domLoading"_utf16_fly_string;

    // 2. Let v be value converted to a JavaScript value.
    auto dom_loading_14_value_js = JS::Value(static_cast<double>(dom_loading_14_value));

    // 3. Perform ! CreateDataPropertyOrThrow(result, k, v).
    MUST(result->create_data_property(dom_loading_14_key, dom_loading_14_value_js));
    auto dom_interactive_15_value = TRY(throw_dom_exception_if_needed(vm, [&] { return idl_object->dom_interactive(); }));

    // 1. Let k be key converted to a JavaScript value.
    auto dom_interactive_15_key = "domInteractive"_utf16_fly_string;

    // 2. Let v be value converted to a JavaScript value.
    auto dom_interactive_15_value_js = JS::Value(static_cast<double>(dom_interactive_15_value));

    // 3. Perform ! CreateDataPropertyOrThrow(result, k, v).
    MUST(result->create_data_property(dom_interactive_15_key, dom_interactive_15_value_js));
    auto dom_content_loaded_event_start_16_value = TRY(throw_dom_exception_if_needed(vm, [&] { return idl_object->dom_content_loaded_event_start(); }));

    // 1. Let k be key converted to a JavaScript value.
    auto dom_content_loaded_event_start_16_key = "domContentLoadedEventStart"_utf16_fly_string;

    // 2. Let v be value converted to a JavaScript value.
    auto dom_content_loaded_event_start_16_value_js = JS::Value(static_cast<double>(dom_content_loaded_event_start_16_value));

    // 3. Perform ! CreateDataPropertyOrThrow(result, k, v).
    MUST(result->create_data_property(dom_content_loaded_event_start_16_key, dom_content_loaded_event_start_16_value_js));
    auto dom_content_loaded_event_end_17_value = TRY(throw_dom_exception_if_needed(vm, [&] { return idl_object->dom_content_loaded_event_end(); }));

    // 1. Let k be key converted to a JavaScript value.
    auto dom_content_loaded_event_end_17_key = "domContentLoadedEventEnd"_utf16_fly_string;

    // 2. Let v be value converted to a JavaScript value.
    auto dom_content_loaded_event_end_17_value_js = JS::Value(static_cast<double>(dom_content_loaded_event_end_17_value));

    // 3. Perform ! CreateDataPropertyOrThrow(result, k, v).
    MUST(result->create_data_property(dom_content_loaded_event_end_17_key, dom_content_loaded_event_end_17_value_js));
    auto dom_complete_18_value = TRY(throw_dom_exception_if_needed(vm, [&] { return idl_object->dom_complete(); }));

    // 1. Let k be key converted to a JavaScript value.
    auto dom_complete_18_key = "domComplete"_utf16_fly_string;

    // 2. Let v be value converted to a JavaScript value.
    auto dom_complete_18_value_js = JS::Value(static_cast<double>(dom_complete_18_value));

    // 3. Perform ! CreateDataPropertyOrThrow(result, k, v).
    MUST(result->create_data_property(dom_complete_18_key, dom_complete_18_value_js));
    auto load_event_start_19_value = TRY(throw_dom_exception_if_needed(vm, [&] { return idl_object->load_event_start(); }));

    // 1. Let k be key converted to a JavaScript value.
    auto load_event_start_19_key = "loadEventStart"_utf16_fly_string;

    // 2. Let v be value converted to a JavaScript value.
    auto load_event_start_19_value_js = JS::Value(static_cast<double>(load_event_start_19_value));

    // 3. Perform ! CreateDataPropertyOrThrow(result, k, v).
    MUST(result->create_data_property(load_event_start_19_key, load_event_start_19_value_js));
    auto load_event_end_20_value = TRY(throw_dom_exception_if_needed(vm, [&] { return idl_object->load_event_end(); }));

    // 1. Let k be key converted to a JavaScript value.
    auto load_event_end_20_key = "loadEventEnd"_utf16_fly_string;

    // 2. Let v be value converted to a JavaScript value.
    auto load_event_end_20_value_js = JS::Value(static_cast<double>(load_event_end_20_value));

    // 3. Perform ! CreateDataPropertyOrThrow(result, k, v).
    MUST(result->create_data_property(load_event_end_20_key, load_event_end_20_value_js));

    // 6. Return result.
    return result;
}

} // namespace Web::Bindings
