#include <AK/String.h>
#include <AK/TypeCasts.h>
#include <AK/Variant.h>
#include <LibJS/Runtime/AbstractOperations.h>
#include <LibJS/Runtime/ArrayBuffer.h>
#include <LibJS/Runtime/DataView.h>
#include <LibJS/Runtime/Error.h>
#include <LibJS/Runtime/PrimitiveString.h>
#include <LibJS/Runtime/Promise.h>
#include <LibJS/Runtime/Realm.h>
#include <LibJS/Runtime/TypedArray.h>
#include <LibJS/Runtime/VM.h>
#include <LibJS/Runtime/Value.h>
#include <LibJS/Runtime/ValueInlines.h>
#include <LibWeb/Bindings/ExceptionOrUtils.h>
#include <LibWeb/Bindings/FontFace.h>
#include <LibWeb/Bindings/Intrinsics.h>
#include <LibWeb/CSS/FontFace.h>
#include <LibWeb/WebIDL/AbstractOperations.h>
#include <LibWeb/WebIDL/Promise.h>
#include <LibWeb/WebIDL/Tracing.h>

namespace Web::Bindings {

void FontFaceConstructor::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(2), JS::Attribute::Configurable);
    object.define_direct_property(vm.names.name, JS::PrimitiveString::create(vm, "FontFace"_utf16), JS::Attribute::Configurable);
    object.define_direct_property(vm.names.prototype, &ensure_web_prototype<FontFacePrototype>(realm, "FontFace"_fly_string), 0);
}

JS::ThrowCompletionOr<GC::Ref<JS::Object>> FontFaceConstructor::construct([[maybe_unused]] InterfaceConstructor& constructor, [[maybe_unused]] JS::FunctionObject& new_target)
{
    WebIDL::log_trace(constructor.vm(), "FontFaceConstructor::construct");
    auto& vm = constructor.vm();
    [[maybe_unused]] auto& realm = *vm.current_realm();

    // To internally create a new object implementing the interface FontFace:

    // 3.2. Let prototype be ? Get(newTarget, "prototype").
    auto prototype = TRY(new_target.get(vm.names.prototype));

    // 3.3. If Type(prototype) is not Object, then:
    if (!prototype.is_object()) {
        // 1. Let targetRealm be ? GetFunctionRealm(newTarget).
        auto* target_realm = TRY(JS::get_function_realm(vm, new_target));

        // 2. Set prototype to the interface prototype object for interface in targetRealm.
        VERIFY(target_realm);
        prototype = &Bindings::ensure_web_prototype<FontFacePrototype>(*target_realm, "FontFace"_fly_string);
    }

    if (vm.argument_count() < 2)
        return vm.throw_completion<JS::TypeError>(JS::ErrorType::BadArgCountMany, "FontFace", "2");

    auto arg0 = vm.argument(0);
    auto family = TRY(throw_dom_exception_if_needed(vm, [&] { return [&]() -> JS::ThrowCompletionOr<String> {
        return TRY(WebIDL::to_string(vm, arg0));
    }(); }));

    auto arg1 = vm.argument(1);
    auto source = TRY(throw_dom_exception_if_needed(vm, [&] { return [&]() -> JS::ThrowCompletionOr<Variant<String, GC::Ref<JS::Int8Array>, GC::Ref<JS::Int16Array>, GC::Ref<JS::Int32Array>, GC::Ref<JS::Uint8Array>, GC::Ref<JS::Uint16Array>, GC::Ref<JS::Uint32Array>, GC::Ref<JS::Uint8ClampedArray>, GC::Ref<JS::BigInt64Array>, GC::Ref<JS::BigUint64Array>, GC::Ref<JS::Float16Array>, GC::Ref<JS::Float32Array>, GC::Ref<JS::Float64Array>, GC::Ref<JS::DataView>, GC::Ref<JS::ArrayBuffer>>> {

        if (arg1.is_object()) {
            [[maybe_unused]] auto& object = arg1.as_object();

            if (auto* array_buffer = as_if<JS::ArrayBuffer>(object)) {
                if (!array_buffer->is_shared_array_buffer()) {
                    // 1. If types includes ArrayBuffer, then return the result of converting V to ArrayBuffer.
                    auto array_buffer_union_type = TRY([&]() -> JS::ThrowCompletionOr<GC::Ref<JS::ArrayBuffer>> {
        // A JavaScript value V is converted to an IDL ArrayBuffer value by running the following algorithm:
        // 1. If V is not an Object, or V does not have an [[ArrayBufferData]] internal slot, then throw a TypeError.
        auto builtin_buffer = arg1.as_if<JS::ArrayBuffer>();
        if (!builtin_buffer)
            return vm.throw_completion<JS::TypeError>(JS::ErrorType::NotAnObjectOfType, "ArrayBuffer");

        // 2. If IsSharedArrayBuffer(V) is true, then throw a TypeError.
        if (builtin_buffer->is_shared_array_buffer())
            return vm.throw_completion<JS::TypeError>(JS::ErrorType::SharedArrayBuffer);

        // 3. If the conversion is not to an IDL type associated with the [AllowResizable] extended attribute, and
        //    IsFixedLengthArrayBuffer(V) is false, then throw a TypeError.
        if (!builtin_buffer->is_fixed_length())
            return vm.throw_completion<JS::TypeError>(JS::ErrorType::NotAnObjectOfType, "fixed-length ArrayBuffer");

        // 4. Return the IDL ArrayBuffer value that is a reference to the same object as V.
        return GC::Ref { *builtin_buffer };
    }());
                    return Variant<String, GC::Ref<JS::Int8Array>, GC::Ref<JS::Int16Array>, GC::Ref<JS::Int32Array>, GC::Ref<JS::Uint8Array>, GC::Ref<JS::Uint16Array>, GC::Ref<JS::Uint32Array>, GC::Ref<JS::Uint8ClampedArray>, GC::Ref<JS::BigInt64Array>, GC::Ref<JS::BigUint64Array>, GC::Ref<JS::Float16Array>, GC::Ref<JS::Float32Array>, GC::Ref<JS::Float64Array>, GC::Ref<JS::DataView>, GC::Ref<JS::ArrayBuffer>> { array_buffer_union_type };
                }
            }

            if (as_if<JS::DataView>(object)) {
                // 1. If types includes DataView, then return the result of converting V to DataView.
                auto data_view_union_type = TRY([&]() -> JS::ThrowCompletionOr<GC::Ref<JS::DataView>> {
        // A JavaScript value V is converted to an IDL DataView value by running the following algorithm:
        // 1. If V is not an Object, or V does not have a [[DataView]] internal slot, then throw a TypeError.
        auto builtin_buffer = arg1.as_if<JS::DataView>();
        if (!builtin_buffer)
            return vm.throw_completion<JS::TypeError>(JS::ErrorType::NotAnObjectOfType, "DataView");

        auto& viewed_array_buffer = *builtin_buffer->viewed_array_buffer();

        // 2. If the conversion is not to an IDL type associated with the [AllowShared] extended attribute, and
        //    IsSharedArrayBuffer(V.[[ViewedArrayBuffer]]) is true, then throw a TypeError.
        if (viewed_array_buffer.is_shared_array_buffer())
            return vm.throw_completion<JS::TypeError>(JS::ErrorType::SharedArrayBuffer);

        // 3. If the conversion is not to an IDL type associated with the [AllowResizable] extended attribute, and
        //    IsFixedLengthArrayBuffer(V.[[ViewedArrayBuffer]]) is false, then throw a TypeError.
        if (!viewed_array_buffer.is_fixed_length())
            return vm.throw_completion<JS::TypeError>(JS::ErrorType::NotAnObjectOfType, "fixed-length DataView");

        // 4. Return the IDL DataView value that is a reference to the same object as V.
        return GC::Ref { *builtin_buffer };
    }());
                return Variant<String, GC::Ref<JS::Int8Array>, GC::Ref<JS::Int16Array>, GC::Ref<JS::Int32Array>, GC::Ref<JS::Uint8Array>, GC::Ref<JS::Uint16Array>, GC::Ref<JS::Uint32Array>, GC::Ref<JS::Uint8ClampedArray>, GC::Ref<JS::BigInt64Array>, GC::Ref<JS::BigUint64Array>, GC::Ref<JS::Float16Array>, GC::Ref<JS::Float32Array>, GC::Ref<JS::Float64Array>, GC::Ref<JS::DataView>, GC::Ref<JS::ArrayBuffer>> { data_view_union_type };
            }

            if (as_if<JS::Int8Array>(object)) {
                auto int8_array_union_type = TRY([&]() -> JS::ThrowCompletionOr<GC::Ref<JS::Int8Array>> {
        // A JavaScript value V is converted to an IDL typed array value by running the following algorithm:
        // 1. Let T be the IDL type V is being converted to.
        // 2. If V is not an Object, or V does not have a [[TypedArrayName]] internal slot with a value equal to T's name,
        //    then throw a TypeError.
        auto builtin_buffer = arg1.as_if<JS::Int8Array>();
        if (!builtin_buffer)
            return vm.throw_completion<JS::TypeError>(JS::ErrorType::NotAnObjectOfType, "Int8Array");

        auto& viewed_array_buffer = *builtin_buffer->viewed_array_buffer();

        // 2. If the conversion is not to an IDL type associated with the [AllowShared] extended attribute, and
        //    IsSharedArrayBuffer(V.[[ViewedArrayBuffer]]) is true, then throw a TypeError.
        if (viewed_array_buffer.is_shared_array_buffer())
            return vm.throw_completion<JS::TypeError>(JS::ErrorType::SharedArrayBuffer);

        // 3. If the conversion is not to an IDL type associated with the [AllowResizable] extended attribute, and
        //    IsFixedLengthArrayBuffer(V.[[ViewedArrayBuffer]]) is false, then throw a TypeError.
        if (!viewed_array_buffer.is_fixed_length())
            return vm.throw_completion<JS::TypeError>(JS::ErrorType::NotAnObjectOfType, "fixed-length Int8Array");

        // 5. Return the IDL value of type T that is a reference to the same object as V.
        return GC::Ref { *builtin_buffer };
    }());
                return Variant<String, GC::Ref<JS::Int8Array>, GC::Ref<JS::Int16Array>, GC::Ref<JS::Int32Array>, GC::Ref<JS::Uint8Array>, GC::Ref<JS::Uint16Array>, GC::Ref<JS::Uint32Array>, GC::Ref<JS::Uint8ClampedArray>, GC::Ref<JS::BigInt64Array>, GC::Ref<JS::BigUint64Array>, GC::Ref<JS::Float16Array>, GC::Ref<JS::Float32Array>, GC::Ref<JS::Float64Array>, GC::Ref<JS::DataView>, GC::Ref<JS::ArrayBuffer>> { int8_array_union_type };
            }

            if (as_if<JS::Int16Array>(object)) {
                auto int16_array_union_type = TRY([&]() -> JS::ThrowCompletionOr<GC::Ref<JS::Int16Array>> {
        // A JavaScript value V is converted to an IDL typed array value by running the following algorithm:
        // 1. Let T be the IDL type V is being converted to.
        // 2. If V is not an Object, or V does not have a [[TypedArrayName]] internal slot with a value equal to T's name,
        //    then throw a TypeError.
        auto builtin_buffer = arg1.as_if<JS::Int16Array>();
        if (!builtin_buffer)
            return vm.throw_completion<JS::TypeError>(JS::ErrorType::NotAnObjectOfType, "Int16Array");

        auto& viewed_array_buffer = *builtin_buffer->viewed_array_buffer();

        // 2. If the conversion is not to an IDL type associated with the [AllowShared] extended attribute, and
        //    IsSharedArrayBuffer(V.[[ViewedArrayBuffer]]) is true, then throw a TypeError.
        if (viewed_array_buffer.is_shared_array_buffer())
            return vm.throw_completion<JS::TypeError>(JS::ErrorType::SharedArrayBuffer);

        // 3. If the conversion is not to an IDL type associated with the [AllowResizable] extended attribute, and
        //    IsFixedLengthArrayBuffer(V.[[ViewedArrayBuffer]]) is false, then throw a TypeError.
        if (!viewed_array_buffer.is_fixed_length())
            return vm.throw_completion<JS::TypeError>(JS::ErrorType::NotAnObjectOfType, "fixed-length Int16Array");

        // 5. Return the IDL value of type T that is a reference to the same object as V.
        return GC::Ref { *builtin_buffer };
    }());
                return Variant<String, GC::Ref<JS::Int8Array>, GC::Ref<JS::Int16Array>, GC::Ref<JS::Int32Array>, GC::Ref<JS::Uint8Array>, GC::Ref<JS::Uint16Array>, GC::Ref<JS::Uint32Array>, GC::Ref<JS::Uint8ClampedArray>, GC::Ref<JS::BigInt64Array>, GC::Ref<JS::BigUint64Array>, GC::Ref<JS::Float16Array>, GC::Ref<JS::Float32Array>, GC::Ref<JS::Float64Array>, GC::Ref<JS::DataView>, GC::Ref<JS::ArrayBuffer>> { int16_array_union_type };
            }

            if (as_if<JS::Int32Array>(object)) {
                auto int32_array_union_type = TRY([&]() -> JS::ThrowCompletionOr<GC::Ref<JS::Int32Array>> {
        // A JavaScript value V is converted to an IDL typed array value by running the following algorithm:
        // 1. Let T be the IDL type V is being converted to.
        // 2. If V is not an Object, or V does not have a [[TypedArrayName]] internal slot with a value equal to T's name,
        //    then throw a TypeError.
        auto builtin_buffer = arg1.as_if<JS::Int32Array>();
        if (!builtin_buffer)
            return vm.throw_completion<JS::TypeError>(JS::ErrorType::NotAnObjectOfType, "Int32Array");

        auto& viewed_array_buffer = *builtin_buffer->viewed_array_buffer();

        // 2. If the conversion is not to an IDL type associated with the [AllowShared] extended attribute, and
        //    IsSharedArrayBuffer(V.[[ViewedArrayBuffer]]) is true, then throw a TypeError.
        if (viewed_array_buffer.is_shared_array_buffer())
            return vm.throw_completion<JS::TypeError>(JS::ErrorType::SharedArrayBuffer);

        // 3. If the conversion is not to an IDL type associated with the [AllowResizable] extended attribute, and
        //    IsFixedLengthArrayBuffer(V.[[ViewedArrayBuffer]]) is false, then throw a TypeError.
        if (!viewed_array_buffer.is_fixed_length())
            return vm.throw_completion<JS::TypeError>(JS::ErrorType::NotAnObjectOfType, "fixed-length Int32Array");

        // 5. Return the IDL value of type T that is a reference to the same object as V.
        return GC::Ref { *builtin_buffer };
    }());
                return Variant<String, GC::Ref<JS::Int8Array>, GC::Ref<JS::Int16Array>, GC::Ref<JS::Int32Array>, GC::Ref<JS::Uint8Array>, GC::Ref<JS::Uint16Array>, GC::Ref<JS::Uint32Array>, GC::Ref<JS::Uint8ClampedArray>, GC::Ref<JS::BigInt64Array>, GC::Ref<JS::BigUint64Array>, GC::Ref<JS::Float16Array>, GC::Ref<JS::Float32Array>, GC::Ref<JS::Float64Array>, GC::Ref<JS::DataView>, GC::Ref<JS::ArrayBuffer>> { int32_array_union_type };
            }

            if (as_if<JS::Uint8Array>(object)) {
                auto uint8_array_union_type = TRY([&]() -> JS::ThrowCompletionOr<GC::Ref<JS::Uint8Array>> {
        // A JavaScript value V is converted to an IDL typed array value by running the following algorithm:
        // 1. Let T be the IDL type V is being converted to.
        // 2. If V is not an Object, or V does not have a [[TypedArrayName]] internal slot with a value equal to T's name,
        //    then throw a TypeError.
        auto builtin_buffer = arg1.as_if<JS::Uint8Array>();
        if (!builtin_buffer)
            return vm.throw_completion<JS::TypeError>(JS::ErrorType::NotAnObjectOfType, "Uint8Array");

        auto& viewed_array_buffer = *builtin_buffer->viewed_array_buffer();

        // 2. If the conversion is not to an IDL type associated with the [AllowShared] extended attribute, and
        //    IsSharedArrayBuffer(V.[[ViewedArrayBuffer]]) is true, then throw a TypeError.
        if (viewed_array_buffer.is_shared_array_buffer())
            return vm.throw_completion<JS::TypeError>(JS::ErrorType::SharedArrayBuffer);

        // 3. If the conversion is not to an IDL type associated with the [AllowResizable] extended attribute, and
        //    IsFixedLengthArrayBuffer(V.[[ViewedArrayBuffer]]) is false, then throw a TypeError.
        if (!viewed_array_buffer.is_fixed_length())
            return vm.throw_completion<JS::TypeError>(JS::ErrorType::NotAnObjectOfType, "fixed-length Uint8Array");

        // 5. Return the IDL value of type T that is a reference to the same object as V.
        return GC::Ref { *builtin_buffer };
    }());
                return Variant<String, GC::Ref<JS::Int8Array>, GC::Ref<JS::Int16Array>, GC::Ref<JS::Int32Array>, GC::Ref<JS::Uint8Array>, GC::Ref<JS::Uint16Array>, GC::Ref<JS::Uint32Array>, GC::Ref<JS::Uint8ClampedArray>, GC::Ref<JS::BigInt64Array>, GC::Ref<JS::BigUint64Array>, GC::Ref<JS::Float16Array>, GC::Ref<JS::Float32Array>, GC::Ref<JS::Float64Array>, GC::Ref<JS::DataView>, GC::Ref<JS::ArrayBuffer>> { uint8_array_union_type };
            }

            if (as_if<JS::Uint16Array>(object)) {
                auto uint16_array_union_type = TRY([&]() -> JS::ThrowCompletionOr<GC::Ref<JS::Uint16Array>> {
        // A JavaScript value V is converted to an IDL typed array value by running the following algorithm:
        // 1. Let T be the IDL type V is being converted to.
        // 2. If V is not an Object, or V does not have a [[TypedArrayName]] internal slot with a value equal to T's name,
        //    then throw a TypeError.
        auto builtin_buffer = arg1.as_if<JS::Uint16Array>();
        if (!builtin_buffer)
            return vm.throw_completion<JS::TypeError>(JS::ErrorType::NotAnObjectOfType, "Uint16Array");

        auto& viewed_array_buffer = *builtin_buffer->viewed_array_buffer();

        // 2. If the conversion is not to an IDL type associated with the [AllowShared] extended attribute, and
        //    IsSharedArrayBuffer(V.[[ViewedArrayBuffer]]) is true, then throw a TypeError.
        if (viewed_array_buffer.is_shared_array_buffer())
            return vm.throw_completion<JS::TypeError>(JS::ErrorType::SharedArrayBuffer);

        // 3. If the conversion is not to an IDL type associated with the [AllowResizable] extended attribute, and
        //    IsFixedLengthArrayBuffer(V.[[ViewedArrayBuffer]]) is false, then throw a TypeError.
        if (!viewed_array_buffer.is_fixed_length())
            return vm.throw_completion<JS::TypeError>(JS::ErrorType::NotAnObjectOfType, "fixed-length Uint16Array");

        // 5. Return the IDL value of type T that is a reference to the same object as V.
        return GC::Ref { *builtin_buffer };
    }());
                return Variant<String, GC::Ref<JS::Int8Array>, GC::Ref<JS::Int16Array>, GC::Ref<JS::Int32Array>, GC::Ref<JS::Uint8Array>, GC::Ref<JS::Uint16Array>, GC::Ref<JS::Uint32Array>, GC::Ref<JS::Uint8ClampedArray>, GC::Ref<JS::BigInt64Array>, GC::Ref<JS::BigUint64Array>, GC::Ref<JS::Float16Array>, GC::Ref<JS::Float32Array>, GC::Ref<JS::Float64Array>, GC::Ref<JS::DataView>, GC::Ref<JS::ArrayBuffer>> { uint16_array_union_type };
            }

            if (as_if<JS::Uint32Array>(object)) {
                auto uint32_array_union_type = TRY([&]() -> JS::ThrowCompletionOr<GC::Ref<JS::Uint32Array>> {
        // A JavaScript value V is converted to an IDL typed array value by running the following algorithm:
        // 1. Let T be the IDL type V is being converted to.
        // 2. If V is not an Object, or V does not have a [[TypedArrayName]] internal slot with a value equal to T's name,
        //    then throw a TypeError.
        auto builtin_buffer = arg1.as_if<JS::Uint32Array>();
        if (!builtin_buffer)
            return vm.throw_completion<JS::TypeError>(JS::ErrorType::NotAnObjectOfType, "Uint32Array");

        auto& viewed_array_buffer = *builtin_buffer->viewed_array_buffer();

        // 2. If the conversion is not to an IDL type associated with the [AllowShared] extended attribute, and
        //    IsSharedArrayBuffer(V.[[ViewedArrayBuffer]]) is true, then throw a TypeError.
        if (viewed_array_buffer.is_shared_array_buffer())
            return vm.throw_completion<JS::TypeError>(JS::ErrorType::SharedArrayBuffer);

        // 3. If the conversion is not to an IDL type associated with the [AllowResizable] extended attribute, and
        //    IsFixedLengthArrayBuffer(V.[[ViewedArrayBuffer]]) is false, then throw a TypeError.
        if (!viewed_array_buffer.is_fixed_length())
            return vm.throw_completion<JS::TypeError>(JS::ErrorType::NotAnObjectOfType, "fixed-length Uint32Array");

        // 5. Return the IDL value of type T that is a reference to the same object as V.
        return GC::Ref { *builtin_buffer };
    }());
                return Variant<String, GC::Ref<JS::Int8Array>, GC::Ref<JS::Int16Array>, GC::Ref<JS::Int32Array>, GC::Ref<JS::Uint8Array>, GC::Ref<JS::Uint16Array>, GC::Ref<JS::Uint32Array>, GC::Ref<JS::Uint8ClampedArray>, GC::Ref<JS::BigInt64Array>, GC::Ref<JS::BigUint64Array>, GC::Ref<JS::Float16Array>, GC::Ref<JS::Float32Array>, GC::Ref<JS::Float64Array>, GC::Ref<JS::DataView>, GC::Ref<JS::ArrayBuffer>> { uint32_array_union_type };
            }

            if (as_if<JS::Uint8ClampedArray>(object)) {
                auto uint8_clamped_array_union_type = TRY([&]() -> JS::ThrowCompletionOr<GC::Ref<JS::Uint8ClampedArray>> {
        // A JavaScript value V is converted to an IDL typed array value by running the following algorithm:
        // 1. Let T be the IDL type V is being converted to.
        // 2. If V is not an Object, or V does not have a [[TypedArrayName]] internal slot with a value equal to T's name,
        //    then throw a TypeError.
        auto builtin_buffer = arg1.as_if<JS::Uint8ClampedArray>();
        if (!builtin_buffer)
            return vm.throw_completion<JS::TypeError>(JS::ErrorType::NotAnObjectOfType, "Uint8ClampedArray");

        auto& viewed_array_buffer = *builtin_buffer->viewed_array_buffer();

        // 2. If the conversion is not to an IDL type associated with the [AllowShared] extended attribute, and
        //    IsSharedArrayBuffer(V.[[ViewedArrayBuffer]]) is true, then throw a TypeError.
        if (viewed_array_buffer.is_shared_array_buffer())
            return vm.throw_completion<JS::TypeError>(JS::ErrorType::SharedArrayBuffer);

        // 3. If the conversion is not to an IDL type associated with the [AllowResizable] extended attribute, and
        //    IsFixedLengthArrayBuffer(V.[[ViewedArrayBuffer]]) is false, then throw a TypeError.
        if (!viewed_array_buffer.is_fixed_length())
            return vm.throw_completion<JS::TypeError>(JS::ErrorType::NotAnObjectOfType, "fixed-length Uint8ClampedArray");

        // 5. Return the IDL value of type T that is a reference to the same object as V.
        return GC::Ref { *builtin_buffer };
    }());
                return Variant<String, GC::Ref<JS::Int8Array>, GC::Ref<JS::Int16Array>, GC::Ref<JS::Int32Array>, GC::Ref<JS::Uint8Array>, GC::Ref<JS::Uint16Array>, GC::Ref<JS::Uint32Array>, GC::Ref<JS::Uint8ClampedArray>, GC::Ref<JS::BigInt64Array>, GC::Ref<JS::BigUint64Array>, GC::Ref<JS::Float16Array>, GC::Ref<JS::Float32Array>, GC::Ref<JS::Float64Array>, GC::Ref<JS::DataView>, GC::Ref<JS::ArrayBuffer>> { uint8_clamped_array_union_type };
            }

            if (as_if<JS::BigInt64Array>(object)) {
                auto big_int64_array_union_type = TRY([&]() -> JS::ThrowCompletionOr<GC::Ref<JS::BigInt64Array>> {
        // A JavaScript value V is converted to an IDL typed array value by running the following algorithm:
        // 1. Let T be the IDL type V is being converted to.
        // 2. If V is not an Object, or V does not have a [[TypedArrayName]] internal slot with a value equal to T's name,
        //    then throw a TypeError.
        auto builtin_buffer = arg1.as_if<JS::BigInt64Array>();
        if (!builtin_buffer)
            return vm.throw_completion<JS::TypeError>(JS::ErrorType::NotAnObjectOfType, "BigInt64Array");

        auto& viewed_array_buffer = *builtin_buffer->viewed_array_buffer();

        // 2. If the conversion is not to an IDL type associated with the [AllowShared] extended attribute, and
        //    IsSharedArrayBuffer(V.[[ViewedArrayBuffer]]) is true, then throw a TypeError.
        if (viewed_array_buffer.is_shared_array_buffer())
            return vm.throw_completion<JS::TypeError>(JS::ErrorType::SharedArrayBuffer);

        // 3. If the conversion is not to an IDL type associated with the [AllowResizable] extended attribute, and
        //    IsFixedLengthArrayBuffer(V.[[ViewedArrayBuffer]]) is false, then throw a TypeError.
        if (!viewed_array_buffer.is_fixed_length())
            return vm.throw_completion<JS::TypeError>(JS::ErrorType::NotAnObjectOfType, "fixed-length BigInt64Array");

        // 5. Return the IDL value of type T that is a reference to the same object as V.
        return GC::Ref { *builtin_buffer };
    }());
                return Variant<String, GC::Ref<JS::Int8Array>, GC::Ref<JS::Int16Array>, GC::Ref<JS::Int32Array>, GC::Ref<JS::Uint8Array>, GC::Ref<JS::Uint16Array>, GC::Ref<JS::Uint32Array>, GC::Ref<JS::Uint8ClampedArray>, GC::Ref<JS::BigInt64Array>, GC::Ref<JS::BigUint64Array>, GC::Ref<JS::Float16Array>, GC::Ref<JS::Float32Array>, GC::Ref<JS::Float64Array>, GC::Ref<JS::DataView>, GC::Ref<JS::ArrayBuffer>> { big_int64_array_union_type };
            }

            if (as_if<JS::BigUint64Array>(object)) {
                auto big_uint64_array_union_type = TRY([&]() -> JS::ThrowCompletionOr<GC::Ref<JS::BigUint64Array>> {
        // A JavaScript value V is converted to an IDL typed array value by running the following algorithm:
        // 1. Let T be the IDL type V is being converted to.
        // 2. If V is not an Object, or V does not have a [[TypedArrayName]] internal slot with a value equal to T's name,
        //    then throw a TypeError.
        auto builtin_buffer = arg1.as_if<JS::BigUint64Array>();
        if (!builtin_buffer)
            return vm.throw_completion<JS::TypeError>(JS::ErrorType::NotAnObjectOfType, "BigUint64Array");

        auto& viewed_array_buffer = *builtin_buffer->viewed_array_buffer();

        // 2. If the conversion is not to an IDL type associated with the [AllowShared] extended attribute, and
        //    IsSharedArrayBuffer(V.[[ViewedArrayBuffer]]) is true, then throw a TypeError.
        if (viewed_array_buffer.is_shared_array_buffer())
            return vm.throw_completion<JS::TypeError>(JS::ErrorType::SharedArrayBuffer);

        // 3. If the conversion is not to an IDL type associated with the [AllowResizable] extended attribute, and
        //    IsFixedLengthArrayBuffer(V.[[ViewedArrayBuffer]]) is false, then throw a TypeError.
        if (!viewed_array_buffer.is_fixed_length())
            return vm.throw_completion<JS::TypeError>(JS::ErrorType::NotAnObjectOfType, "fixed-length BigUint64Array");

        // 5. Return the IDL value of type T that is a reference to the same object as V.
        return GC::Ref { *builtin_buffer };
    }());
                return Variant<String, GC::Ref<JS::Int8Array>, GC::Ref<JS::Int16Array>, GC::Ref<JS::Int32Array>, GC::Ref<JS::Uint8Array>, GC::Ref<JS::Uint16Array>, GC::Ref<JS::Uint32Array>, GC::Ref<JS::Uint8ClampedArray>, GC::Ref<JS::BigInt64Array>, GC::Ref<JS::BigUint64Array>, GC::Ref<JS::Float16Array>, GC::Ref<JS::Float32Array>, GC::Ref<JS::Float64Array>, GC::Ref<JS::DataView>, GC::Ref<JS::ArrayBuffer>> { big_uint64_array_union_type };
            }

            if (as_if<JS::Float16Array>(object)) {
                auto float16_array_union_type = TRY([&]() -> JS::ThrowCompletionOr<GC::Ref<JS::Float16Array>> {
        // A JavaScript value V is converted to an IDL typed array value by running the following algorithm:
        // 1. Let T be the IDL type V is being converted to.
        // 2. If V is not an Object, or V does not have a [[TypedArrayName]] internal slot with a value equal to T's name,
        //    then throw a TypeError.
        auto builtin_buffer = arg1.as_if<JS::Float16Array>();
        if (!builtin_buffer)
            return vm.throw_completion<JS::TypeError>(JS::ErrorType::NotAnObjectOfType, "Float16Array");

        auto& viewed_array_buffer = *builtin_buffer->viewed_array_buffer();

        // 2. If the conversion is not to an IDL type associated with the [AllowShared] extended attribute, and
        //    IsSharedArrayBuffer(V.[[ViewedArrayBuffer]]) is true, then throw a TypeError.
        if (viewed_array_buffer.is_shared_array_buffer())
            return vm.throw_completion<JS::TypeError>(JS::ErrorType::SharedArrayBuffer);

        // 3. If the conversion is not to an IDL type associated with the [AllowResizable] extended attribute, and
        //    IsFixedLengthArrayBuffer(V.[[ViewedArrayBuffer]]) is false, then throw a TypeError.
        if (!viewed_array_buffer.is_fixed_length())
            return vm.throw_completion<JS::TypeError>(JS::ErrorType::NotAnObjectOfType, "fixed-length Float16Array");

        // 5. Return the IDL value of type T that is a reference to the same object as V.
        return GC::Ref { *builtin_buffer };
    }());
                return Variant<String, GC::Ref<JS::Int8Array>, GC::Ref<JS::Int16Array>, GC::Ref<JS::Int32Array>, GC::Ref<JS::Uint8Array>, GC::Ref<JS::Uint16Array>, GC::Ref<JS::Uint32Array>, GC::Ref<JS::Uint8ClampedArray>, GC::Ref<JS::BigInt64Array>, GC::Ref<JS::BigUint64Array>, GC::Ref<JS::Float16Array>, GC::Ref<JS::Float32Array>, GC::Ref<JS::Float64Array>, GC::Ref<JS::DataView>, GC::Ref<JS::ArrayBuffer>> { float16_array_union_type };
            }

            if (as_if<JS::Float32Array>(object)) {
                auto float32_array_union_type = TRY([&]() -> JS::ThrowCompletionOr<GC::Ref<JS::Float32Array>> {
        // A JavaScript value V is converted to an IDL typed array value by running the following algorithm:
        // 1. Let T be the IDL type V is being converted to.
        // 2. If V is not an Object, or V does not have a [[TypedArrayName]] internal slot with a value equal to T's name,
        //    then throw a TypeError.
        auto builtin_buffer = arg1.as_if<JS::Float32Array>();
        if (!builtin_buffer)
            return vm.throw_completion<JS::TypeError>(JS::ErrorType::NotAnObjectOfType, "Float32Array");

        auto& viewed_array_buffer = *builtin_buffer->viewed_array_buffer();

        // 2. If the conversion is not to an IDL type associated with the [AllowShared] extended attribute, and
        //    IsSharedArrayBuffer(V.[[ViewedArrayBuffer]]) is true, then throw a TypeError.
        if (viewed_array_buffer.is_shared_array_buffer())
            return vm.throw_completion<JS::TypeError>(JS::ErrorType::SharedArrayBuffer);

        // 3. If the conversion is not to an IDL type associated with the [AllowResizable] extended attribute, and
        //    IsFixedLengthArrayBuffer(V.[[ViewedArrayBuffer]]) is false, then throw a TypeError.
        if (!viewed_array_buffer.is_fixed_length())
            return vm.throw_completion<JS::TypeError>(JS::ErrorType::NotAnObjectOfType, "fixed-length Float32Array");

        // 5. Return the IDL value of type T that is a reference to the same object as V.
        return GC::Ref { *builtin_buffer };
    }());
                return Variant<String, GC::Ref<JS::Int8Array>, GC::Ref<JS::Int16Array>, GC::Ref<JS::Int32Array>, GC::Ref<JS::Uint8Array>, GC::Ref<JS::Uint16Array>, GC::Ref<JS::Uint32Array>, GC::Ref<JS::Uint8ClampedArray>, GC::Ref<JS::BigInt64Array>, GC::Ref<JS::BigUint64Array>, GC::Ref<JS::Float16Array>, GC::Ref<JS::Float32Array>, GC::Ref<JS::Float64Array>, GC::Ref<JS::DataView>, GC::Ref<JS::ArrayBuffer>> { float32_array_union_type };
            }

            if (as_if<JS::Float64Array>(object)) {
                auto float64_array_union_type = TRY([&]() -> JS::ThrowCompletionOr<GC::Ref<JS::Float64Array>> {
        // A JavaScript value V is converted to an IDL typed array value by running the following algorithm:
        // 1. Let T be the IDL type V is being converted to.
        // 2. If V is not an Object, or V does not have a [[TypedArrayName]] internal slot with a value equal to T's name,
        //    then throw a TypeError.
        auto builtin_buffer = arg1.as_if<JS::Float64Array>();
        if (!builtin_buffer)
            return vm.throw_completion<JS::TypeError>(JS::ErrorType::NotAnObjectOfType, "Float64Array");

        auto& viewed_array_buffer = *builtin_buffer->viewed_array_buffer();

        // 2. If the conversion is not to an IDL type associated with the [AllowShared] extended attribute, and
        //    IsSharedArrayBuffer(V.[[ViewedArrayBuffer]]) is true, then throw a TypeError.
        if (viewed_array_buffer.is_shared_array_buffer())
            return vm.throw_completion<JS::TypeError>(JS::ErrorType::SharedArrayBuffer);

        // 3. If the conversion is not to an IDL type associated with the [AllowResizable] extended attribute, and
        //    IsFixedLengthArrayBuffer(V.[[ViewedArrayBuffer]]) is false, then throw a TypeError.
        if (!viewed_array_buffer.is_fixed_length())
            return vm.throw_completion<JS::TypeError>(JS::ErrorType::NotAnObjectOfType, "fixed-length Float64Array");

        // 5. Return the IDL value of type T that is a reference to the same object as V.
        return GC::Ref { *builtin_buffer };
    }());
                return Variant<String, GC::Ref<JS::Int8Array>, GC::Ref<JS::Int16Array>, GC::Ref<JS::Int32Array>, GC::Ref<JS::Uint8Array>, GC::Ref<JS::Uint16Array>, GC::Ref<JS::Uint32Array>, GC::Ref<JS::Uint8ClampedArray>, GC::Ref<JS::BigInt64Array>, GC::Ref<JS::BigUint64Array>, GC::Ref<JS::Float16Array>, GC::Ref<JS::Float32Array>, GC::Ref<JS::Float64Array>, GC::Ref<JS::DataView>, GC::Ref<JS::ArrayBuffer>> { float64_array_union_type };
            }
        }

        auto source_string = TRY([&]() -> JS::ThrowCompletionOr<String> {
        return TRY(WebIDL::to_string(vm, arg1));
    }());
        return Variant<String, GC::Ref<JS::Int8Array>, GC::Ref<JS::Int16Array>, GC::Ref<JS::Int32Array>, GC::Ref<JS::Uint8Array>, GC::Ref<JS::Uint16Array>, GC::Ref<JS::Uint32Array>, GC::Ref<JS::Uint8ClampedArray>, GC::Ref<JS::BigInt64Array>, GC::Ref<JS::BigUint64Array>, GC::Ref<JS::Float16Array>, GC::Ref<JS::Float32Array>, GC::Ref<JS::Float64Array>, GC::Ref<JS::DataView>, GC::Ref<JS::ArrayBuffer>> { source_string };

        return vm.throw_completion<JS::TypeError>("No union types matched"_utf16);
    }(); }));

    auto arg2 = vm.argument(2);
    FontFaceDescriptors descriptors = FontFaceDescriptors {};
    if (!arg2.is_undefined())
        descriptors = TRY(throw_dom_exception_if_needed(vm, [&] { return convert_to_idl_value_for_font_face_descriptors(vm, arg2); }));

    auto impl = TRY(throw_dom_exception_if_needed(vm, [&] { return CSS::FontFace::construct_impl(realm, family, source, descriptors); }));

    // 7. Set instance.[[Prototype]] to prototype.
    VERIFY(prototype.is_object());
    impl->set_prototype(&prototype.as_object());

    // FIXME: Steps 8...11. of the "internally create a new object implementing the interface FontFace" algorithm
    // (https://webidl.spec.whatwg.org/#js-platform-objects) are currently not handled, or are handled within CSS::FontFace::construct_impl().

    return *impl;
}

void FontFacePrototype::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 family_id = "family"_utf16_fly_string;
    auto native_family_getter = JS::NativeFunction::create(realm, family_getter, 0, family_id, &realm, "get"sv);
    auto native_family_setter = JS::NativeFunction::create(realm, family_setter, 1, family_id, &realm, "set"sv);

    // 4. Let configurable be false if attr is unforgeable and true otherwise.
    auto family_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(family_id, native_family_getter, native_family_setter, family_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 style_id = "style"_utf16_fly_string;
    auto native_style_getter = JS::NativeFunction::create(realm, style_getter, 0, style_id, &realm, "get"sv);
    auto native_style_setter = JS::NativeFunction::create(realm, style_setter, 1, style_id, &realm, "set"sv);

    // 4. Let configurable be false if attr is unforgeable and true otherwise.
    auto style_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(style_id, native_style_getter, native_style_setter, style_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 weight_id = "weight"_utf16_fly_string;
    auto native_weight_getter = JS::NativeFunction::create(realm, weight_getter, 0, weight_id, &realm, "get"sv);
    auto native_weight_setter = JS::NativeFunction::create(realm, weight_setter, 1, weight_id, &realm, "set"sv);

    // 4. Let configurable be false if attr is unforgeable and true otherwise.
    auto weight_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(weight_id, native_weight_getter, native_weight_setter, weight_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 stretch_id = "stretch"_utf16_fly_string;
    auto native_stretch_getter = JS::NativeFunction::create(realm, stretch_getter, 0, stretch_id, &realm, "get"sv);
    auto native_stretch_setter = JS::NativeFunction::create(realm, stretch_setter, 1, stretch_id, &realm, "set"sv);

    // 4. Let configurable be false if attr is unforgeable and true otherwise.
    auto stretch_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(stretch_id, native_stretch_getter, native_stretch_setter, stretch_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 unicode_range_id = "unicodeRange"_utf16_fly_string;
    auto native_unicode_range_getter = JS::NativeFunction::create(realm, unicode_range_getter, 0, unicode_range_id, &realm, "get"sv);
    auto native_unicode_range_setter = JS::NativeFunction::create(realm, unicode_range_setter, 1, unicode_range_id, &realm, "set"sv);

    // 4. Let configurable be false if attr is unforgeable and true otherwise.
    auto unicode_range_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(unicode_range_id, native_unicode_range_getter, native_unicode_range_setter, unicode_range_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 feature_settings_id = "featureSettings"_utf16_fly_string;
    auto native_feature_settings_getter = JS::NativeFunction::create(realm, feature_settings_getter, 0, feature_settings_id, &realm, "get"sv);
    auto native_feature_settings_setter = JS::NativeFunction::create(realm, feature_settings_setter, 1, feature_settings_id, &realm, "set"sv);

    // 4. Let configurable be false if attr is unforgeable and true otherwise.
    auto feature_settings_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(feature_settings_id, native_feature_settings_getter, native_feature_settings_setter, feature_settings_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 variation_settings_id = "variationSettings"_utf16_fly_string;
    auto native_variation_settings_getter = JS::NativeFunction::create(realm, variation_settings_getter, 0, variation_settings_id, &realm, "get"sv);
    auto native_variation_settings_setter = JS::NativeFunction::create(realm, variation_settings_setter, 1, variation_settings_id, &realm, "set"sv);

    // 4. Let configurable be false if attr is unforgeable and true otherwise.
    auto variation_settings_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(variation_settings_id, native_variation_settings_getter, native_variation_settings_setter, variation_settings_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 display_id = "display"_utf16_fly_string;
    auto native_display_getter = JS::NativeFunction::create(realm, display_getter, 0, display_id, &realm, "get"sv);
    auto native_display_setter = JS::NativeFunction::create(realm, display_setter, 1, display_id, &realm, "set"sv);

    // 4. Let configurable be false if attr is unforgeable and true otherwise.
    auto display_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(display_id, native_display_getter, native_display_setter, display_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 ascent_override_id = "ascentOverride"_utf16_fly_string;
    auto native_ascent_override_getter = JS::NativeFunction::create(realm, ascent_override_getter, 0, ascent_override_id, &realm, "get"sv);
    auto native_ascent_override_setter = JS::NativeFunction::create(realm, ascent_override_setter, 1, ascent_override_id, &realm, "set"sv);

    // 4. Let configurable be false if attr is unforgeable and true otherwise.
    auto ascent_override_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(ascent_override_id, native_ascent_override_getter, native_ascent_override_setter, ascent_override_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 descent_override_id = "descentOverride"_utf16_fly_string;
    auto native_descent_override_getter = JS::NativeFunction::create(realm, descent_override_getter, 0, descent_override_id, &realm, "get"sv);
    auto native_descent_override_setter = JS::NativeFunction::create(realm, descent_override_setter, 1, descent_override_id, &realm, "set"sv);

    // 4. Let configurable be false if attr is unforgeable and true otherwise.
    auto descent_override_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(descent_override_id, native_descent_override_getter, native_descent_override_setter, descent_override_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 line_gap_override_id = "lineGapOverride"_utf16_fly_string;
    auto native_line_gap_override_getter = JS::NativeFunction::create(realm, line_gap_override_getter, 0, line_gap_override_id, &realm, "get"sv);
    auto native_line_gap_override_setter = JS::NativeFunction::create(realm, line_gap_override_setter, 1, line_gap_override_id, &realm, "set"sv);

    // 4. Let configurable be false if attr is unforgeable and true otherwise.
    auto line_gap_override_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(line_gap_override_id, native_line_gap_override_getter, native_line_gap_override_setter, line_gap_override_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 status_id = "status"_utf16_fly_string;
    auto native_status_getter = JS::NativeFunction::create(realm, status_getter, 0, status_id, &realm, "get"sv);
    GC::Ptr<JS::NativeFunction> native_status_setter;

    // 4. Let configurable be false if attr is unforgeable and true otherwise.
    auto status_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(status_id, native_status_getter, native_status_setter, status_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 loaded_id = "loaded"_utf16_fly_string;
    auto native_loaded_getter = JS::NativeFunction::create(realm, loaded_getter, 0, loaded_id, &realm, "get"sv);
    GC::Ptr<JS::NativeFunction> native_loaded_setter;

    // 4. Let configurable be false if attr is unforgeable and true otherwise.
    auto loaded_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(loaded_id, native_loaded_getter, native_loaded_setter, loaded_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, "load"_utf16_fly_string, load, 0, default_attributes);

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

void FontFacePrototype::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<CSS::FontFace*> impl_from(JS::VM& vm, JS::Value js_value)
{

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

[[maybe_unused]] static JS::ThrowCompletionOr<CSS::FontFace*> 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(FontFacePrototype::family_getter)
{
    WebIDL::log_trace(vm, "FontFacePrototype::family_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->family(); }));

    return WebIDL::primitive_string_from_string(vm, R);
}

JS_DEFINE_NATIVE_FUNCTION(FontFacePrototype::style_getter)
{
    WebIDL::log_trace(vm, "FontFacePrototype::style_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->style(); }));

    return WebIDL::primitive_string_from_string(vm, R);
}

JS_DEFINE_NATIVE_FUNCTION(FontFacePrototype::weight_getter)
{
    WebIDL::log_trace(vm, "FontFacePrototype::weight_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->weight(); }));

    return WebIDL::primitive_string_from_string(vm, R);
}

JS_DEFINE_NATIVE_FUNCTION(FontFacePrototype::stretch_getter)
{
    WebIDL::log_trace(vm, "FontFacePrototype::stretch_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->stretch(); }));

    return WebIDL::primitive_string_from_string(vm, R);
}

JS_DEFINE_NATIVE_FUNCTION(FontFacePrototype::unicode_range_getter)
{
    WebIDL::log_trace(vm, "FontFacePrototype::unicode_range_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->unicode_range(); }));

    return WebIDL::primitive_string_from_string(vm, R);
}

JS_DEFINE_NATIVE_FUNCTION(FontFacePrototype::feature_settings_getter)
{
    WebIDL::log_trace(vm, "FontFacePrototype::feature_settings_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->feature_settings(); }));

    return WebIDL::primitive_string_from_string(vm, R);
}

JS_DEFINE_NATIVE_FUNCTION(FontFacePrototype::variation_settings_getter)
{
    WebIDL::log_trace(vm, "FontFacePrototype::variation_settings_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->variation_settings(); }));

    return WebIDL::primitive_string_from_string(vm, R);
}

JS_DEFINE_NATIVE_FUNCTION(FontFacePrototype::display_getter)
{
    WebIDL::log_trace(vm, "FontFacePrototype::display_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->display(); }));

    return WebIDL::primitive_string_from_string(vm, R);
}

JS_DEFINE_NATIVE_FUNCTION(FontFacePrototype::ascent_override_getter)
{
    WebIDL::log_trace(vm, "FontFacePrototype::ascent_override_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->ascent_override(); }));

    return WebIDL::primitive_string_from_string(vm, R);
}

JS_DEFINE_NATIVE_FUNCTION(FontFacePrototype::descent_override_getter)
{
    WebIDL::log_trace(vm, "FontFacePrototype::descent_override_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->descent_override(); }));

    return WebIDL::primitive_string_from_string(vm, R);
}

JS_DEFINE_NATIVE_FUNCTION(FontFacePrototype::line_gap_override_getter)
{
    WebIDL::log_trace(vm, "FontFacePrototype::line_gap_override_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->line_gap_override(); }));

    return WebIDL::primitive_string_from_string(vm, R);
}

JS_DEFINE_NATIVE_FUNCTION(FontFacePrototype::status_getter)
{
    WebIDL::log_trace(vm, "FontFacePrototype::status_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->status(); }));

    return JS::PrimitiveString::create(vm, idl_enum_to_string(R));
}

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

    auto steps = [&]() -> JS::ThrowCompletionOr<GC::Ptr<WebIDL::Promise>> {
        // 1. Let idlObject be null.
        [[maybe_unused]] auto* idl_object = TRY(impl_from(vm));
        auto R = TRY(throw_dom_exception_if_needed(vm, [&] { return idl_object->loaded(); }));
        return R;
    };

    auto maybe_R = steps();

    // 2. And then, if an exception E was thrown:

    // 1. If attribute’s type is a promise type, then return ! Call(%Promise.reject%, %Promise%, «E»).
    if (maybe_R.is_throw_completion())
        return WebIDL::create_rejected_promise(realm, maybe_R.error_value())->promise();

    // 2. Otherwise, end these steps and allow the exception to propagate.
    auto R = maybe_R.release_value();

    // 4. Return the result of converting R to a JavaScript value of the type attribute is declared as.
    return GC::Ref { as<JS::Promise>(*R->promise()) };
}

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

    // 1. Let V be undefined.
    auto V = JS::js_undefined();

    // 2. If any arguments were passed, then set V to the value of the first argument passed.
    if (vm.argument_count() > 0)
        V = vm.argument(0);

    // 3. Let id be attribute’s identifier.

    // 4. Let idlObject be null.
    [[maybe_unused]] CSS::FontFace* idl_object = nullptr;

    // 5. If attribute is a regular attribute:

    // 1. Let jsValue be the this value, if it is not null or undefined, or realm’s global object otherwise. (This will subsequently cause a TypeError in a few steps, if the global object does not implement target and [LegacyLenientThis] is not specified.)
    auto js_value = vm.this_value();
    if (js_value.is_nullish())
        js_value = &realm.global_object();

    // 2. FIXME: If jsValue is a platform object, then perform a security check, passing jsValue, attribute’s identifier, and "setter".

    // 3. Let validThis be true if jsValue implements target, or false otherwise.
    auto maybe_idl_object = impl_from(vm, js_value);

    // 4. If validThis is false and attribute was not specified with the [LegacyLenientThis] extended attribute, then throw a TypeError.
    idl_object = TRY(maybe_idl_object);

    auto original_steps = [&]() -> JS::ThrowCompletionOr<JS::Value> {
        // 6. Let idlValue be determined as follows:
        // -> Otherwise, idlValue is the result of converting V to an IDL value of attribute’s type.
        auto idl_value = TRY(throw_dom_exception_if_needed(vm, [&] { return [&]() -> JS::ThrowCompletionOr<String> {
        return TRY(WebIDL::to_string(vm, V));
    }(); }));

        // 7. Run the setter steps of attribute with idlObject as this and idlValue as the value.
        auto setter_result = [&]() -> JS::ThrowCompletionOr<void> {
            TRY(throw_dom_exception_if_needed(vm, [&] { return idl_object->set_family(idl_value); }));
    return {};
        }();

        if (setter_result.is_error())
            return setter_result.release_error();

        return JS::js_undefined();
    };

    // 8. Return undefined.
    return TRY(original_steps());
}

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

    // 1. Let V be undefined.
    auto V = JS::js_undefined();

    // 2. If any arguments were passed, then set V to the value of the first argument passed.
    if (vm.argument_count() > 0)
        V = vm.argument(0);

    // 3. Let id be attribute’s identifier.

    // 4. Let idlObject be null.
    [[maybe_unused]] CSS::FontFace* idl_object = nullptr;

    // 5. If attribute is a regular attribute:

    // 1. Let jsValue be the this value, if it is not null or undefined, or realm’s global object otherwise. (This will subsequently cause a TypeError in a few steps, if the global object does not implement target and [LegacyLenientThis] is not specified.)
    auto js_value = vm.this_value();
    if (js_value.is_nullish())
        js_value = &realm.global_object();

    // 2. FIXME: If jsValue is a platform object, then perform a security check, passing jsValue, attribute’s identifier, and "setter".

    // 3. Let validThis be true if jsValue implements target, or false otherwise.
    auto maybe_idl_object = impl_from(vm, js_value);

    // 4. If validThis is false and attribute was not specified with the [LegacyLenientThis] extended attribute, then throw a TypeError.
    idl_object = TRY(maybe_idl_object);

    auto original_steps = [&]() -> JS::ThrowCompletionOr<JS::Value> {
        // 6. Let idlValue be determined as follows:
        // -> Otherwise, idlValue is the result of converting V to an IDL value of attribute’s type.
        auto idl_value = TRY(throw_dom_exception_if_needed(vm, [&] { return [&]() -> JS::ThrowCompletionOr<String> {
        return TRY(WebIDL::to_string(vm, V));
    }(); }));

        // 7. Run the setter steps of attribute with idlObject as this and idlValue as the value.
        auto setter_result = [&]() -> JS::ThrowCompletionOr<void> {
            TRY(throw_dom_exception_if_needed(vm, [&] { return idl_object->set_style(idl_value); }));
    return {};
        }();

        if (setter_result.is_error())
            return setter_result.release_error();

        return JS::js_undefined();
    };

    // 8. Return undefined.
    return TRY(original_steps());
}

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

    // 1. Let V be undefined.
    auto V = JS::js_undefined();

    // 2. If any arguments were passed, then set V to the value of the first argument passed.
    if (vm.argument_count() > 0)
        V = vm.argument(0);

    // 3. Let id be attribute’s identifier.

    // 4. Let idlObject be null.
    [[maybe_unused]] CSS::FontFace* idl_object = nullptr;

    // 5. If attribute is a regular attribute:

    // 1. Let jsValue be the this value, if it is not null or undefined, or realm’s global object otherwise. (This will subsequently cause a TypeError in a few steps, if the global object does not implement target and [LegacyLenientThis] is not specified.)
    auto js_value = vm.this_value();
    if (js_value.is_nullish())
        js_value = &realm.global_object();

    // 2. FIXME: If jsValue is a platform object, then perform a security check, passing jsValue, attribute’s identifier, and "setter".

    // 3. Let validThis be true if jsValue implements target, or false otherwise.
    auto maybe_idl_object = impl_from(vm, js_value);

    // 4. If validThis is false and attribute was not specified with the [LegacyLenientThis] extended attribute, then throw a TypeError.
    idl_object = TRY(maybe_idl_object);

    auto original_steps = [&]() -> JS::ThrowCompletionOr<JS::Value> {
        // 6. Let idlValue be determined as follows:
        // -> Otherwise, idlValue is the result of converting V to an IDL value of attribute’s type.
        auto idl_value = TRY(throw_dom_exception_if_needed(vm, [&] { return [&]() -> JS::ThrowCompletionOr<String> {
        return TRY(WebIDL::to_string(vm, V));
    }(); }));

        // 7. Run the setter steps of attribute with idlObject as this and idlValue as the value.
        auto setter_result = [&]() -> JS::ThrowCompletionOr<void> {
            TRY(throw_dom_exception_if_needed(vm, [&] { return idl_object->set_weight(idl_value); }));
    return {};
        }();

        if (setter_result.is_error())
            return setter_result.release_error();

        return JS::js_undefined();
    };

    // 8. Return undefined.
    return TRY(original_steps());
}

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

    // 1. Let V be undefined.
    auto V = JS::js_undefined();

    // 2. If any arguments were passed, then set V to the value of the first argument passed.
    if (vm.argument_count() > 0)
        V = vm.argument(0);

    // 3. Let id be attribute’s identifier.

    // 4. Let idlObject be null.
    [[maybe_unused]] CSS::FontFace* idl_object = nullptr;

    // 5. If attribute is a regular attribute:

    // 1. Let jsValue be the this value, if it is not null or undefined, or realm’s global object otherwise. (This will subsequently cause a TypeError in a few steps, if the global object does not implement target and [LegacyLenientThis] is not specified.)
    auto js_value = vm.this_value();
    if (js_value.is_nullish())
        js_value = &realm.global_object();

    // 2. FIXME: If jsValue is a platform object, then perform a security check, passing jsValue, attribute’s identifier, and "setter".

    // 3. Let validThis be true if jsValue implements target, or false otherwise.
    auto maybe_idl_object = impl_from(vm, js_value);

    // 4. If validThis is false and attribute was not specified with the [LegacyLenientThis] extended attribute, then throw a TypeError.
    idl_object = TRY(maybe_idl_object);

    auto original_steps = [&]() -> JS::ThrowCompletionOr<JS::Value> {
        // 6. Let idlValue be determined as follows:
        // -> Otherwise, idlValue is the result of converting V to an IDL value of attribute’s type.
        auto idl_value = TRY(throw_dom_exception_if_needed(vm, [&] { return [&]() -> JS::ThrowCompletionOr<String> {
        return TRY(WebIDL::to_string(vm, V));
    }(); }));

        // 7. Run the setter steps of attribute with idlObject as this and idlValue as the value.
        auto setter_result = [&]() -> JS::ThrowCompletionOr<void> {
            TRY(throw_dom_exception_if_needed(vm, [&] { return idl_object->set_stretch(idl_value); }));
    return {};
        }();

        if (setter_result.is_error())
            return setter_result.release_error();

        return JS::js_undefined();
    };

    // 8. Return undefined.
    return TRY(original_steps());
}

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

    // 1. Let V be undefined.
    auto V = JS::js_undefined();

    // 2. If any arguments were passed, then set V to the value of the first argument passed.
    if (vm.argument_count() > 0)
        V = vm.argument(0);

    // 3. Let id be attribute’s identifier.

    // 4. Let idlObject be null.
    [[maybe_unused]] CSS::FontFace* idl_object = nullptr;

    // 5. If attribute is a regular attribute:

    // 1. Let jsValue be the this value, if it is not null or undefined, or realm’s global object otherwise. (This will subsequently cause a TypeError in a few steps, if the global object does not implement target and [LegacyLenientThis] is not specified.)
    auto js_value = vm.this_value();
    if (js_value.is_nullish())
        js_value = &realm.global_object();

    // 2. FIXME: If jsValue is a platform object, then perform a security check, passing jsValue, attribute’s identifier, and "setter".

    // 3. Let validThis be true if jsValue implements target, or false otherwise.
    auto maybe_idl_object = impl_from(vm, js_value);

    // 4. If validThis is false and attribute was not specified with the [LegacyLenientThis] extended attribute, then throw a TypeError.
    idl_object = TRY(maybe_idl_object);

    auto original_steps = [&]() -> JS::ThrowCompletionOr<JS::Value> {
        // 6. Let idlValue be determined as follows:
        // -> Otherwise, idlValue is the result of converting V to an IDL value of attribute’s type.
        auto idl_value = TRY(throw_dom_exception_if_needed(vm, [&] { return [&]() -> JS::ThrowCompletionOr<String> {
        return TRY(WebIDL::to_string(vm, V));
    }(); }));

        // 7. Run the setter steps of attribute with idlObject as this and idlValue as the value.
        auto setter_result = [&]() -> JS::ThrowCompletionOr<void> {
            TRY(throw_dom_exception_if_needed(vm, [&] { return idl_object->set_unicode_range(idl_value); }));
    return {};
        }();

        if (setter_result.is_error())
            return setter_result.release_error();

        return JS::js_undefined();
    };

    // 8. Return undefined.
    return TRY(original_steps());
}

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

    // 1. Let V be undefined.
    auto V = JS::js_undefined();

    // 2. If any arguments were passed, then set V to the value of the first argument passed.
    if (vm.argument_count() > 0)
        V = vm.argument(0);

    // 3. Let id be attribute’s identifier.

    // 4. Let idlObject be null.
    [[maybe_unused]] CSS::FontFace* idl_object = nullptr;

    // 5. If attribute is a regular attribute:

    // 1. Let jsValue be the this value, if it is not null or undefined, or realm’s global object otherwise. (This will subsequently cause a TypeError in a few steps, if the global object does not implement target and [LegacyLenientThis] is not specified.)
    auto js_value = vm.this_value();
    if (js_value.is_nullish())
        js_value = &realm.global_object();

    // 2. FIXME: If jsValue is a platform object, then perform a security check, passing jsValue, attribute’s identifier, and "setter".

    // 3. Let validThis be true if jsValue implements target, or false otherwise.
    auto maybe_idl_object = impl_from(vm, js_value);

    // 4. If validThis is false and attribute was not specified with the [LegacyLenientThis] extended attribute, then throw a TypeError.
    idl_object = TRY(maybe_idl_object);

    auto original_steps = [&]() -> JS::ThrowCompletionOr<JS::Value> {
        // 6. Let idlValue be determined as follows:
        // -> Otherwise, idlValue is the result of converting V to an IDL value of attribute’s type.
        auto idl_value = TRY(throw_dom_exception_if_needed(vm, [&] { return [&]() -> JS::ThrowCompletionOr<String> {
        return TRY(WebIDL::to_string(vm, V));
    }(); }));

        // 7. Run the setter steps of attribute with idlObject as this and idlValue as the value.
        auto setter_result = [&]() -> JS::ThrowCompletionOr<void> {
            TRY(throw_dom_exception_if_needed(vm, [&] { return idl_object->set_feature_settings(idl_value); }));
    return {};
        }();

        if (setter_result.is_error())
            return setter_result.release_error();

        return JS::js_undefined();
    };

    // 8. Return undefined.
    return TRY(original_steps());
}

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

    // 1. Let V be undefined.
    auto V = JS::js_undefined();

    // 2. If any arguments were passed, then set V to the value of the first argument passed.
    if (vm.argument_count() > 0)
        V = vm.argument(0);

    // 3. Let id be attribute’s identifier.

    // 4. Let idlObject be null.
    [[maybe_unused]] CSS::FontFace* idl_object = nullptr;

    // 5. If attribute is a regular attribute:

    // 1. Let jsValue be the this value, if it is not null or undefined, or realm’s global object otherwise. (This will subsequently cause a TypeError in a few steps, if the global object does not implement target and [LegacyLenientThis] is not specified.)
    auto js_value = vm.this_value();
    if (js_value.is_nullish())
        js_value = &realm.global_object();

    // 2. FIXME: If jsValue is a platform object, then perform a security check, passing jsValue, attribute’s identifier, and "setter".

    // 3. Let validThis be true if jsValue implements target, or false otherwise.
    auto maybe_idl_object = impl_from(vm, js_value);

    // 4. If validThis is false and attribute was not specified with the [LegacyLenientThis] extended attribute, then throw a TypeError.
    idl_object = TRY(maybe_idl_object);

    auto original_steps = [&]() -> JS::ThrowCompletionOr<JS::Value> {
        // 6. Let idlValue be determined as follows:
        // -> Otherwise, idlValue is the result of converting V to an IDL value of attribute’s type.
        auto idl_value = TRY(throw_dom_exception_if_needed(vm, [&] { return [&]() -> JS::ThrowCompletionOr<String> {
        return TRY(WebIDL::to_string(vm, V));
    }(); }));

        // 7. Run the setter steps of attribute with idlObject as this and idlValue as the value.
        auto setter_result = [&]() -> JS::ThrowCompletionOr<void> {
            TRY(throw_dom_exception_if_needed(vm, [&] { return idl_object->set_variation_settings(idl_value); }));
    return {};
        }();

        if (setter_result.is_error())
            return setter_result.release_error();

        return JS::js_undefined();
    };

    // 8. Return undefined.
    return TRY(original_steps());
}

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

    // 1. Let V be undefined.
    auto V = JS::js_undefined();

    // 2. If any arguments were passed, then set V to the value of the first argument passed.
    if (vm.argument_count() > 0)
        V = vm.argument(0);

    // 3. Let id be attribute’s identifier.

    // 4. Let idlObject be null.
    [[maybe_unused]] CSS::FontFace* idl_object = nullptr;

    // 5. If attribute is a regular attribute:

    // 1. Let jsValue be the this value, if it is not null or undefined, or realm’s global object otherwise. (This will subsequently cause a TypeError in a few steps, if the global object does not implement target and [LegacyLenientThis] is not specified.)
    auto js_value = vm.this_value();
    if (js_value.is_nullish())
        js_value = &realm.global_object();

    // 2. FIXME: If jsValue is a platform object, then perform a security check, passing jsValue, attribute’s identifier, and "setter".

    // 3. Let validThis be true if jsValue implements target, or false otherwise.
    auto maybe_idl_object = impl_from(vm, js_value);

    // 4. If validThis is false and attribute was not specified with the [LegacyLenientThis] extended attribute, then throw a TypeError.
    idl_object = TRY(maybe_idl_object);

    auto original_steps = [&]() -> JS::ThrowCompletionOr<JS::Value> {
        // 6. Let idlValue be determined as follows:
        // -> Otherwise, idlValue is the result of converting V to an IDL value of attribute’s type.
        auto idl_value = TRY(throw_dom_exception_if_needed(vm, [&] { return [&]() -> JS::ThrowCompletionOr<String> {
        return TRY(WebIDL::to_string(vm, V));
    }(); }));

        // 7. Run the setter steps of attribute with idlObject as this and idlValue as the value.
        auto setter_result = [&]() -> JS::ThrowCompletionOr<void> {
            TRY(throw_dom_exception_if_needed(vm, [&] { return idl_object->set_display(idl_value); }));
    return {};
        }();

        if (setter_result.is_error())
            return setter_result.release_error();

        return JS::js_undefined();
    };

    // 8. Return undefined.
    return TRY(original_steps());
}

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

    // 1. Let V be undefined.
    auto V = JS::js_undefined();

    // 2. If any arguments were passed, then set V to the value of the first argument passed.
    if (vm.argument_count() > 0)
        V = vm.argument(0);

    // 3. Let id be attribute’s identifier.

    // 4. Let idlObject be null.
    [[maybe_unused]] CSS::FontFace* idl_object = nullptr;

    // 5. If attribute is a regular attribute:

    // 1. Let jsValue be the this value, if it is not null or undefined, or realm’s global object otherwise. (This will subsequently cause a TypeError in a few steps, if the global object does not implement target and [LegacyLenientThis] is not specified.)
    auto js_value = vm.this_value();
    if (js_value.is_nullish())
        js_value = &realm.global_object();

    // 2. FIXME: If jsValue is a platform object, then perform a security check, passing jsValue, attribute’s identifier, and "setter".

    // 3. Let validThis be true if jsValue implements target, or false otherwise.
    auto maybe_idl_object = impl_from(vm, js_value);

    // 4. If validThis is false and attribute was not specified with the [LegacyLenientThis] extended attribute, then throw a TypeError.
    idl_object = TRY(maybe_idl_object);

    auto original_steps = [&]() -> JS::ThrowCompletionOr<JS::Value> {
        // 6. Let idlValue be determined as follows:
        // -> Otherwise, idlValue is the result of converting V to an IDL value of attribute’s type.
        auto idl_value = TRY(throw_dom_exception_if_needed(vm, [&] { return [&]() -> JS::ThrowCompletionOr<String> {
        return TRY(WebIDL::to_string(vm, V));
    }(); }));

        // 7. Run the setter steps of attribute with idlObject as this and idlValue as the value.
        auto setter_result = [&]() -> JS::ThrowCompletionOr<void> {
            TRY(throw_dom_exception_if_needed(vm, [&] { return idl_object->set_ascent_override(idl_value); }));
    return {};
        }();

        if (setter_result.is_error())
            return setter_result.release_error();

        return JS::js_undefined();
    };

    // 8. Return undefined.
    return TRY(original_steps());
}

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

    // 1. Let V be undefined.
    auto V = JS::js_undefined();

    // 2. If any arguments were passed, then set V to the value of the first argument passed.
    if (vm.argument_count() > 0)
        V = vm.argument(0);

    // 3. Let id be attribute’s identifier.

    // 4. Let idlObject be null.
    [[maybe_unused]] CSS::FontFace* idl_object = nullptr;

    // 5. If attribute is a regular attribute:

    // 1. Let jsValue be the this value, if it is not null or undefined, or realm’s global object otherwise. (This will subsequently cause a TypeError in a few steps, if the global object does not implement target and [LegacyLenientThis] is not specified.)
    auto js_value = vm.this_value();
    if (js_value.is_nullish())
        js_value = &realm.global_object();

    // 2. FIXME: If jsValue is a platform object, then perform a security check, passing jsValue, attribute’s identifier, and "setter".

    // 3. Let validThis be true if jsValue implements target, or false otherwise.
    auto maybe_idl_object = impl_from(vm, js_value);

    // 4. If validThis is false and attribute was not specified with the [LegacyLenientThis] extended attribute, then throw a TypeError.
    idl_object = TRY(maybe_idl_object);

    auto original_steps = [&]() -> JS::ThrowCompletionOr<JS::Value> {
        // 6. Let idlValue be determined as follows:
        // -> Otherwise, idlValue is the result of converting V to an IDL value of attribute’s type.
        auto idl_value = TRY(throw_dom_exception_if_needed(vm, [&] { return [&]() -> JS::ThrowCompletionOr<String> {
        return TRY(WebIDL::to_string(vm, V));
    }(); }));

        // 7. Run the setter steps of attribute with idlObject as this and idlValue as the value.
        auto setter_result = [&]() -> JS::ThrowCompletionOr<void> {
            TRY(throw_dom_exception_if_needed(vm, [&] { return idl_object->set_descent_override(idl_value); }));
    return {};
        }();

        if (setter_result.is_error())
            return setter_result.release_error();

        return JS::js_undefined();
    };

    // 8. Return undefined.
    return TRY(original_steps());
}

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

    // 1. Let V be undefined.
    auto V = JS::js_undefined();

    // 2. If any arguments were passed, then set V to the value of the first argument passed.
    if (vm.argument_count() > 0)
        V = vm.argument(0);

    // 3. Let id be attribute’s identifier.

    // 4. Let idlObject be null.
    [[maybe_unused]] CSS::FontFace* idl_object = nullptr;

    // 5. If attribute is a regular attribute:

    // 1. Let jsValue be the this value, if it is not null or undefined, or realm’s global object otherwise. (This will subsequently cause a TypeError in a few steps, if the global object does not implement target and [LegacyLenientThis] is not specified.)
    auto js_value = vm.this_value();
    if (js_value.is_nullish())
        js_value = &realm.global_object();

    // 2. FIXME: If jsValue is a platform object, then perform a security check, passing jsValue, attribute’s identifier, and "setter".

    // 3. Let validThis be true if jsValue implements target, or false otherwise.
    auto maybe_idl_object = impl_from(vm, js_value);

    // 4. If validThis is false and attribute was not specified with the [LegacyLenientThis] extended attribute, then throw a TypeError.
    idl_object = TRY(maybe_idl_object);

    auto original_steps = [&]() -> JS::ThrowCompletionOr<JS::Value> {
        // 6. Let idlValue be determined as follows:
        // -> Otherwise, idlValue is the result of converting V to an IDL value of attribute’s type.
        auto idl_value = TRY(throw_dom_exception_if_needed(vm, [&] { return [&]() -> JS::ThrowCompletionOr<String> {
        return TRY(WebIDL::to_string(vm, V));
    }(); }));

        // 7. Run the setter steps of attribute with idlObject as this and idlValue as the value.
        auto setter_result = [&]() -> JS::ThrowCompletionOr<void> {
            TRY(throw_dom_exception_if_needed(vm, [&] { return idl_object->set_line_gap_override(idl_value); }));
    return {};
        }();

        if (setter_result.is_error())
            return setter_result.release_error();

        return JS::js_undefined();
    };

    // 8. Return undefined.
    return TRY(original_steps());
}

JS_DEFINE_NATIVE_FUNCTION(FontFacePrototype::load)
{
    WebIDL::log_trace(vm, "FontFacePrototype::load");
    [[maybe_unused]] auto& realm = *vm.current_realm();
    auto steps = [&realm, &vm]() -> JS::ThrowCompletionOr<GC::Ref<WebIDL::Promise>> {
        (void)realm;
    [[maybe_unused]] CSS::FontFace* idl_object = TRY(impl_from(vm));

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

    auto maybe_R = steps();

    // And then, if an exception E was thrown:
    // 1. If op has a return type that is a promise type, then return ! Call(%Promise.reject%, %Promise%, «E»).
    // 2. Otherwise, end these steps and allow the exception to propagate.
    if (maybe_R.is_throw_completion())
        return WebIDL::create_rejected_promise(realm, maybe_R.error_value())->promise();

    return GC::Ref { as<JS::Promise>(*maybe_R.release_value()->promise()) };
}

// https://webidl.spec.whatwg.org/#idl-enumeration
JS::ThrowCompletionOr<FontFaceLoadStatus> convert_to_idl_value_for_font_face_load_status(JS::VM& vm, JS::Value value)
{
    // 1. Let S be the result of calling ? ToString(V).
    auto value_as_string = TRY(value.to_utf16_string(vm));

    // 2. If S is not one of E’s enumeration values, then throw a TypeError.
    // 3. Return the enumeration value of type E that is equal to S.
    if (value_as_string == "unloaded"sv)
        return FontFaceLoadStatus::Unloaded;
    if (value_as_string == "loading"sv)
        return FontFaceLoadStatus::Loading;
    if (value_as_string == "loaded"sv)
        return FontFaceLoadStatus::Loaded;
    if (value_as_string == "error"sv)
        return FontFaceLoadStatus::Error;
    return vm.throw_completion<JS::TypeError>(JS::ErrorType::InvalidEnumerationValue, value_as_string, "FontFaceLoadStatus");
}

// https://webidl.spec.whatwg.org/#idl-enumeration
Utf16String idl_enum_to_string(FontFaceLoadStatus value)
{
    // The result of converting an IDL enumeration type value to a JavaScript value is the String value that represents the same sequence of code units as the enumeration value.
    switch (value) {
    case FontFaceLoadStatus::Unloaded:
        return "unloaded"_utf16;
    case FontFaceLoadStatus::Loading:
        return "loading"_utf16;
    case FontFaceLoadStatus::Loaded:
        return "loaded"_utf16;
    case FontFaceLoadStatus::Error:
        return "error"_utf16;
    }
    VERIFY_NOT_REACHED();
}

// https://webidl.spec.whatwg.org/#es-dictionary
JS::ThrowCompletionOr<FontFaceDescriptors> convert_to_idl_value_for_font_face_descriptors(JS::VM& vm, JS::Value js_dict)
{
    // 1. If jsDict is not an Object and jsDict is neither undefined nor null, then throw a TypeError.
    if (!js_dict.is_object() && !js_dict.is_nullish())
        return vm.throw_completion<JS::TypeError>(JS::ErrorType::NotAnObjectOfType, "FontFaceDescriptors");

    // 2. Let idlDict be an empty ordered map, representing a dictionary of type D.
    // 3. Let dictionaries be a list consisting of D and all of D’s inherited dictionaries, in order from least to most derived.
    // 4. For each dictionary dictionary in dictionaries, in order:
    // NB: We defer construction until the return initializer because some members may not be default-constructible. Inherited dictionaries are represented by the generated C++ struct inheritance.

    // 5. Return idlDict.
    return FontFaceDescriptors {
        .ascent_override = TRY([&]() -> JS::ThrowCompletionOr<String> {
            // 1. Let key be the identifier of member.
            // 2. If jsDict is either undefined or null, then:
            //     1. Let jsMemberValue be undefined.
            // 3. Otherwise,
            //     1. Let jsMemberValue be ? Get(jsDict, key).
            auto js_member_value = JS::js_undefined();
            if (js_dict.is_object())
                js_member_value = TRY(js_dict.as_object().get("ascentOverride"_utf16_fly_string));

            // 4. If jsMemberValue is not undefined, then:
            if (!js_member_value.is_undefined()) {
                // 1. Let idlMemberValue be the result of converting jsMemberValue to an IDL value whose type is the type member is declared to be of.
                auto idl_member_value = TRY(throw_dom_exception_if_needed(vm, [&] { return [&]() -> JS::ThrowCompletionOr<String> {
        return TRY(WebIDL::to_string(vm, js_member_value));
    }(); }));

                // 2. Set idlDict[key] to idlMemberValue.
                return idl_member_value;
            }
            // 5. Otherwise, if jsMemberValue is undefined but member has a default value, then:
            // 1. Let idlMemberValue be the result of converting member's default value to an IDL value whose type is the type member is declared to be of.
            auto idl_member_value = "normal"_string;

            // 2. Set idlDict[key] to idlMemberValue.
            return idl_member_value;
        }()),
        .descent_override = TRY([&]() -> JS::ThrowCompletionOr<String> {
            // 1. Let key be the identifier of member.
            // 2. If jsDict is either undefined or null, then:
            //     1. Let jsMemberValue be undefined.
            // 3. Otherwise,
            //     1. Let jsMemberValue be ? Get(jsDict, key).
            auto js_member_value = JS::js_undefined();
            if (js_dict.is_object())
                js_member_value = TRY(js_dict.as_object().get("descentOverride"_utf16_fly_string));

            // 4. If jsMemberValue is not undefined, then:
            if (!js_member_value.is_undefined()) {
                // 1. Let idlMemberValue be the result of converting jsMemberValue to an IDL value whose type is the type member is declared to be of.
                auto idl_member_value = TRY(throw_dom_exception_if_needed(vm, [&] { return [&]() -> JS::ThrowCompletionOr<String> {
        return TRY(WebIDL::to_string(vm, js_member_value));
    }(); }));

                // 2. Set idlDict[key] to idlMemberValue.
                return idl_member_value;
            }
            // 5. Otherwise, if jsMemberValue is undefined but member has a default value, then:
            // 1. Let idlMemberValue be the result of converting member's default value to an IDL value whose type is the type member is declared to be of.
            auto idl_member_value = "normal"_string;

            // 2. Set idlDict[key] to idlMemberValue.
            return idl_member_value;
        }()),
        .display = TRY([&]() -> JS::ThrowCompletionOr<String> {
            // 1. Let key be the identifier of member.
            // 2. If jsDict is either undefined or null, then:
            //     1. Let jsMemberValue be undefined.
            // 3. Otherwise,
            //     1. Let jsMemberValue be ? Get(jsDict, key).
            auto js_member_value = JS::js_undefined();
            if (js_dict.is_object())
                js_member_value = TRY(js_dict.as_object().get("display"_utf16_fly_string));

            // 4. If jsMemberValue is not undefined, then:
            if (!js_member_value.is_undefined()) {
                // 1. Let idlMemberValue be the result of converting jsMemberValue to an IDL value whose type is the type member is declared to be of.
                auto idl_member_value = TRY(throw_dom_exception_if_needed(vm, [&] { return [&]() -> JS::ThrowCompletionOr<String> {
        return TRY(WebIDL::to_string(vm, js_member_value));
    }(); }));

                // 2. Set idlDict[key] to idlMemberValue.
                return idl_member_value;
            }
            // 5. Otherwise, if jsMemberValue is undefined but member has a default value, then:
            // 1. Let idlMemberValue be the result of converting member's default value to an IDL value whose type is the type member is declared to be of.
            auto idl_member_value = "auto"_string;

            // 2. Set idlDict[key] to idlMemberValue.
            return idl_member_value;
        }()),
        .feature_settings = TRY([&]() -> JS::ThrowCompletionOr<String> {
            // 1. Let key be the identifier of member.
            // 2. If jsDict is either undefined or null, then:
            //     1. Let jsMemberValue be undefined.
            // 3. Otherwise,
            //     1. Let jsMemberValue be ? Get(jsDict, key).
            auto js_member_value = JS::js_undefined();
            if (js_dict.is_object())
                js_member_value = TRY(js_dict.as_object().get("featureSettings"_utf16_fly_string));

            // 4. If jsMemberValue is not undefined, then:
            if (!js_member_value.is_undefined()) {
                // 1. Let idlMemberValue be the result of converting jsMemberValue to an IDL value whose type is the type member is declared to be of.
                auto idl_member_value = TRY(throw_dom_exception_if_needed(vm, [&] { return [&]() -> JS::ThrowCompletionOr<String> {
        return TRY(WebIDL::to_string(vm, js_member_value));
    }(); }));

                // 2. Set idlDict[key] to idlMemberValue.
                return idl_member_value;
            }
            // 5. Otherwise, if jsMemberValue is undefined but member has a default value, then:
            // 1. Let idlMemberValue be the result of converting member's default value to an IDL value whose type is the type member is declared to be of.
            auto idl_member_value = "normal"_string;

            // 2. Set idlDict[key] to idlMemberValue.
            return idl_member_value;
        }()),
        .line_gap_override = TRY([&]() -> JS::ThrowCompletionOr<String> {
            // 1. Let key be the identifier of member.
            // 2. If jsDict is either undefined or null, then:
            //     1. Let jsMemberValue be undefined.
            // 3. Otherwise,
            //     1. Let jsMemberValue be ? Get(jsDict, key).
            auto js_member_value = JS::js_undefined();
            if (js_dict.is_object())
                js_member_value = TRY(js_dict.as_object().get("lineGapOverride"_utf16_fly_string));

            // 4. If jsMemberValue is not undefined, then:
            if (!js_member_value.is_undefined()) {
                // 1. Let idlMemberValue be the result of converting jsMemberValue to an IDL value whose type is the type member is declared to be of.
                auto idl_member_value = TRY(throw_dom_exception_if_needed(vm, [&] { return [&]() -> JS::ThrowCompletionOr<String> {
        return TRY(WebIDL::to_string(vm, js_member_value));
    }(); }));

                // 2. Set idlDict[key] to idlMemberValue.
                return idl_member_value;
            }
            // 5. Otherwise, if jsMemberValue is undefined but member has a default value, then:
            // 1. Let idlMemberValue be the result of converting member's default value to an IDL value whose type is the type member is declared to be of.
            auto idl_member_value = "normal"_string;

            // 2. Set idlDict[key] to idlMemberValue.
            return idl_member_value;
        }()),
        .stretch = TRY([&]() -> JS::ThrowCompletionOr<String> {
            // 1. Let key be the identifier of member.
            // 2. If jsDict is either undefined or null, then:
            //     1. Let jsMemberValue be undefined.
            // 3. Otherwise,
            //     1. Let jsMemberValue be ? Get(jsDict, key).
            auto js_member_value = JS::js_undefined();
            if (js_dict.is_object())
                js_member_value = TRY(js_dict.as_object().get("stretch"_utf16_fly_string));

            // 4. If jsMemberValue is not undefined, then:
            if (!js_member_value.is_undefined()) {
                // 1. Let idlMemberValue be the result of converting jsMemberValue to an IDL value whose type is the type member is declared to be of.
                auto idl_member_value = TRY(throw_dom_exception_if_needed(vm, [&] { return [&]() -> JS::ThrowCompletionOr<String> {
        return TRY(WebIDL::to_string(vm, js_member_value));
    }(); }));

                // 2. Set idlDict[key] to idlMemberValue.
                return idl_member_value;
            }
            // 5. Otherwise, if jsMemberValue is undefined but member has a default value, then:
            // 1. Let idlMemberValue be the result of converting member's default value to an IDL value whose type is the type member is declared to be of.
            auto idl_member_value = "normal"_string;

            // 2. Set idlDict[key] to idlMemberValue.
            return idl_member_value;
        }()),
        .style = TRY([&]() -> JS::ThrowCompletionOr<String> {
            // 1. Let key be the identifier of member.
            // 2. If jsDict is either undefined or null, then:
            //     1. Let jsMemberValue be undefined.
            // 3. Otherwise,
            //     1. Let jsMemberValue be ? Get(jsDict, key).
            auto js_member_value = JS::js_undefined();
            if (js_dict.is_object())
                js_member_value = TRY(js_dict.as_object().get("style"_utf16_fly_string));

            // 4. If jsMemberValue is not undefined, then:
            if (!js_member_value.is_undefined()) {
                // 1. Let idlMemberValue be the result of converting jsMemberValue to an IDL value whose type is the type member is declared to be of.
                auto idl_member_value = TRY(throw_dom_exception_if_needed(vm, [&] { return [&]() -> JS::ThrowCompletionOr<String> {
        return TRY(WebIDL::to_string(vm, js_member_value));
    }(); }));

                // 2. Set idlDict[key] to idlMemberValue.
                return idl_member_value;
            }
            // 5. Otherwise, if jsMemberValue is undefined but member has a default value, then:
            // 1. Let idlMemberValue be the result of converting member's default value to an IDL value whose type is the type member is declared to be of.
            auto idl_member_value = "normal"_string;

            // 2. Set idlDict[key] to idlMemberValue.
            return idl_member_value;
        }()),
        .unicode_range = TRY([&]() -> JS::ThrowCompletionOr<String> {
            // 1. Let key be the identifier of member.
            // 2. If jsDict is either undefined or null, then:
            //     1. Let jsMemberValue be undefined.
            // 3. Otherwise,
            //     1. Let jsMemberValue be ? Get(jsDict, key).
            auto js_member_value = JS::js_undefined();
            if (js_dict.is_object())
                js_member_value = TRY(js_dict.as_object().get("unicodeRange"_utf16_fly_string));

            // 4. If jsMemberValue is not undefined, then:
            if (!js_member_value.is_undefined()) {
                // 1. Let idlMemberValue be the result of converting jsMemberValue to an IDL value whose type is the type member is declared to be of.
                auto idl_member_value = TRY(throw_dom_exception_if_needed(vm, [&] { return [&]() -> JS::ThrowCompletionOr<String> {
        return TRY(WebIDL::to_string(vm, js_member_value));
    }(); }));

                // 2. Set idlDict[key] to idlMemberValue.
                return idl_member_value;
            }
            // 5. Otherwise, if jsMemberValue is undefined but member has a default value, then:
            // 1. Let idlMemberValue be the result of converting member's default value to an IDL value whose type is the type member is declared to be of.
            auto idl_member_value = "U+0-10FFFF"_string;

            // 2. Set idlDict[key] to idlMemberValue.
            return idl_member_value;
        }()),
        .variation_settings = TRY([&]() -> JS::ThrowCompletionOr<String> {
            // 1. Let key be the identifier of member.
            // 2. If jsDict is either undefined or null, then:
            //     1. Let jsMemberValue be undefined.
            // 3. Otherwise,
            //     1. Let jsMemberValue be ? Get(jsDict, key).
            auto js_member_value = JS::js_undefined();
            if (js_dict.is_object())
                js_member_value = TRY(js_dict.as_object().get("variationSettings"_utf16_fly_string));

            // 4. If jsMemberValue is not undefined, then:
            if (!js_member_value.is_undefined()) {
                // 1. Let idlMemberValue be the result of converting jsMemberValue to an IDL value whose type is the type member is declared to be of.
                auto idl_member_value = TRY(throw_dom_exception_if_needed(vm, [&] { return [&]() -> JS::ThrowCompletionOr<String> {
        return TRY(WebIDL::to_string(vm, js_member_value));
    }(); }));

                // 2. Set idlDict[key] to idlMemberValue.
                return idl_member_value;
            }
            // 5. Otherwise, if jsMemberValue is undefined but member has a default value, then:
            // 1. Let idlMemberValue be the result of converting member's default value to an IDL value whose type is the type member is declared to be of.
            auto idl_member_value = "normal"_string;

            // 2. Set idlDict[key] to idlMemberValue.
            return idl_member_value;
        }()),
        .weight = TRY([&]() -> JS::ThrowCompletionOr<String> {
            // 1. Let key be the identifier of member.
            // 2. If jsDict is either undefined or null, then:
            //     1. Let jsMemberValue be undefined.
            // 3. Otherwise,
            //     1. Let jsMemberValue be ? Get(jsDict, key).
            auto js_member_value = JS::js_undefined();
            if (js_dict.is_object())
                js_member_value = TRY(js_dict.as_object().get("weight"_utf16_fly_string));

            // 4. If jsMemberValue is not undefined, then:
            if (!js_member_value.is_undefined()) {
                // 1. Let idlMemberValue be the result of converting jsMemberValue to an IDL value whose type is the type member is declared to be of.
                auto idl_member_value = TRY(throw_dom_exception_if_needed(vm, [&] { return [&]() -> JS::ThrowCompletionOr<String> {
        return TRY(WebIDL::to_string(vm, js_member_value));
    }(); }));

                // 2. Set idlDict[key] to idlMemberValue.
                return idl_member_value;
            }
            // 5. Otherwise, if jsMemberValue is undefined but member has a default value, then:
            // 1. Let idlMemberValue be the result of converting member's default value to an IDL value whose type is the type member is declared to be of.
            auto idl_member_value = "normal"_string;

            // 2. Set idlDict[key] to idlMemberValue.
            return idl_member_value;
        }()),
    };
}

} // namespace Web::Bindings
