#include <AK/String.h>
#include <AK/TypeCasts.h>
#include <AK/Variant.h>
#include <LibGC/Heap.h>
#include <LibJS/Runtime/ArrayBuffer.h>
#include <LibJS/Runtime/DataView.h>
#include <LibJS/Runtime/Error.h>
#include <LibJS/Runtime/FunctionObject.h>
#include <LibJS/Runtime/PrimitiveString.h>
#include <LibJS/Runtime/TypedArray.h>
#include <LibJS/Runtime/VM.h>
#include <LibJS/Runtime/Value.h>
#include <LibJS/Runtime/ValueInlines.h>
#include <LibWeb/Bindings/EventTarget.h>
#include <LibWeb/Bindings/ExceptionOrUtils.h>
#include <LibWeb/Bindings/Intrinsics.h>
#include <LibWeb/Bindings/SourceBuffer.h>
#include <LibWeb/HTML/Scripting/Environments.h>
#include <LibWeb/HTML/TimeRanges.h>
#include <LibWeb/MediaSourceExtensions/SourceBuffer.h>
#include <LibWeb/WebIDL/AbstractOperations.h>
#include <LibWeb/WebIDL/CallbackType.h>
#include <LibWeb/WebIDL/Tracing.h>

namespace Web::Bindings {

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

    object.set_prototype(&ensure_web_constructor<EventTargetPrototype>(realm, "EventTarget"_fly_string));
    object.define_direct_property(vm.names.length, JS::Value(0), JS::Attribute::Configurable);
    object.define_direct_property(vm.names.name, JS::PrimitiveString::create(vm, "SourceBuffer"_utf16), JS::Attribute::Configurable);
    object.define_direct_property(vm.names.prototype, &ensure_web_prototype<SourceBufferPrototype>(realm, "SourceBuffer"_fly_string), 0);
}

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

void SourceBufferPrototype::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(GC::Ref { ensure_web_prototype<EventTargetPrototype>(realm, "EventTarget"_fly_string) });

    auto mode_id = "mode"_utf16_fly_string;
    auto native_mode_getter = JS::NativeFunction::create(realm, mode_getter, 0, mode_id, &realm, "get"sv);
    auto native_mode_setter = JS::NativeFunction::create(realm, mode_setter, 1, mode_id, &realm, "set"sv);

    // 4. Let configurable be false if attr is unforgeable and true otherwise.
    auto mode_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(mode_id, native_mode_getter, native_mode_setter, mode_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 updating_id = "updating"_utf16_fly_string;
    auto native_updating_getter = JS::NativeFunction::create(realm, updating_getter, 0, updating_id, &realm, "get"sv);
    GC::Ptr<JS::NativeFunction> native_updating_setter;

    // 4. Let configurable be false if attr is unforgeable and true otherwise.
    auto updating_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(updating_id, native_updating_getter, native_updating_setter, updating_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 buffered_id = "buffered"_utf16_fly_string;
    auto native_buffered_getter = JS::NativeFunction::create(realm, buffered_getter, 0, buffered_id, &realm, "get"sv);
    GC::Ptr<JS::NativeFunction> native_buffered_setter;

    // 4. Let configurable be false if attr is unforgeable and true otherwise.
    auto buffered_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(buffered_id, native_buffered_getter, native_buffered_setter, buffered_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 onupdatestart_id = "onupdatestart"_utf16_fly_string;
    auto native_onupdatestart_getter = JS::NativeFunction::create(realm, onupdatestart_getter, 0, onupdatestart_id, &realm, "get"sv);
    auto native_onupdatestart_setter = JS::NativeFunction::create(realm, onupdatestart_setter, 1, onupdatestart_id, &realm, "set"sv);

    // 4. Let configurable be false if attr is unforgeable and true otherwise.
    auto onupdatestart_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(onupdatestart_id, native_onupdatestart_getter, native_onupdatestart_setter, onupdatestart_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 onupdate_id = "onupdate"_utf16_fly_string;
    auto native_onupdate_getter = JS::NativeFunction::create(realm, onupdate_getter, 0, onupdate_id, &realm, "get"sv);
    auto native_onupdate_setter = JS::NativeFunction::create(realm, onupdate_setter, 1, onupdate_id, &realm, "set"sv);

    // 4. Let configurable be false if attr is unforgeable and true otherwise.
    auto onupdate_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(onupdate_id, native_onupdate_getter, native_onupdate_setter, onupdate_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 onupdateend_id = "onupdateend"_utf16_fly_string;
    auto native_onupdateend_getter = JS::NativeFunction::create(realm, onupdateend_getter, 0, onupdateend_id, &realm, "get"sv);
    auto native_onupdateend_setter = JS::NativeFunction::create(realm, onupdateend_setter, 1, onupdateend_id, &realm, "set"sv);

    // 4. Let configurable be false if attr is unforgeable and true otherwise.
    auto onupdateend_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(onupdateend_id, native_onupdateend_getter, native_onupdateend_setter, onupdateend_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 onerror_id = "onerror"_utf16_fly_string;
    auto native_onerror_getter = JS::NativeFunction::create(realm, onerror_getter, 0, onerror_id, &realm, "get"sv);
    auto native_onerror_setter = JS::NativeFunction::create(realm, onerror_setter, 1, onerror_id, &realm, "set"sv);

    // 4. Let configurable be false if attr is unforgeable and true otherwise.
    auto onerror_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(onerror_id, native_onerror_getter, native_onerror_setter, onerror_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 onabort_id = "onabort"_utf16_fly_string;
    auto native_onabort_getter = JS::NativeFunction::create(realm, onabort_getter, 0, onabort_id, &realm, "get"sv);
    auto native_onabort_setter = JS::NativeFunction::create(realm, onabort_setter, 1, onabort_id, &realm, "set"sv);

    // 4. Let configurable be false if attr is unforgeable and true otherwise.
    auto onabort_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(onabort_id, native_onabort_getter, native_onabort_setter, onabort_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, "appendBuffer"_utf16_fly_string, append_buffer, 1, default_attributes);

    object.define_native_function(realm, "abort"_utf16_fly_string, abort, 0, default_attributes);

    object.define_native_function(realm, "changeType"_utf16_fly_string, change_type, 1, default_attributes);

    object.define_direct_property("remove"_utf16_fly_string, JS::js_undefined(), default_attributes | JS::Attribute::Unimplemented);

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

void SourceBufferPrototype::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<MediaSourceExtensions::SourceBuffer*> impl_from(JS::VM& vm, JS::Value js_value)
{

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

[[maybe_unused]] static JS::ThrowCompletionOr<MediaSourceExtensions::SourceBuffer*> 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(SourceBufferPrototype::mode_getter)
{
    WebIDL::log_trace(vm, "SourceBufferPrototype::mode_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->mode(); }));

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

JS_DEFINE_NATIVE_FUNCTION(SourceBufferPrototype::updating_getter)
{
    WebIDL::log_trace(vm, "SourceBufferPrototype::updating_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->updating(); }));

    return JS::Value(R);
}

JS_DEFINE_NATIVE_FUNCTION(SourceBufferPrototype::buffered_getter)
{
    WebIDL::log_trace(vm, "SourceBufferPrototype::buffered_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->buffered(); }));

    return JS::Value(R);
}

JS_DEFINE_NATIVE_FUNCTION(SourceBufferPrototype::onupdatestart_getter)
{
    WebIDL::log_trace(vm, "SourceBufferPrototype::onupdatestart_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->onupdatestart(); }));

    return [&]() -> JS::Value {
        // 1. If the IDL nullable type T? value is null, then the JavaScript value is null.
        if (!R)
            return JS::js_null();

        // 2. Otherwise, the JavaScript value is the result of converting the IDL nullable type value to the inner IDL type T.
        return JS::Value(R->callback);
    }();
}

JS_DEFINE_NATIVE_FUNCTION(SourceBufferPrototype::onupdate_getter)
{
    WebIDL::log_trace(vm, "SourceBufferPrototype::onupdate_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->onupdate(); }));

    return [&]() -> JS::Value {
        // 1. If the IDL nullable type T? value is null, then the JavaScript value is null.
        if (!R)
            return JS::js_null();

        // 2. Otherwise, the JavaScript value is the result of converting the IDL nullable type value to the inner IDL type T.
        return JS::Value(R->callback);
    }();
}

JS_DEFINE_NATIVE_FUNCTION(SourceBufferPrototype::onupdateend_getter)
{
    WebIDL::log_trace(vm, "SourceBufferPrototype::onupdateend_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->onupdateend(); }));

    return [&]() -> JS::Value {
        // 1. If the IDL nullable type T? value is null, then the JavaScript value is null.
        if (!R)
            return JS::js_null();

        // 2. Otherwise, the JavaScript value is the result of converting the IDL nullable type value to the inner IDL type T.
        return JS::Value(R->callback);
    }();
}

JS_DEFINE_NATIVE_FUNCTION(SourceBufferPrototype::onerror_getter)
{
    WebIDL::log_trace(vm, "SourceBufferPrototype::onerror_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->onerror(); }));

    return [&]() -> JS::Value {
        // 1. If the IDL nullable type T? value is null, then the JavaScript value is null.
        if (!R)
            return JS::js_null();

        // 2. Otherwise, the JavaScript value is the result of converting the IDL nullable type value to the inner IDL type T.
        return JS::Value(R->callback);
    }();
}

JS_DEFINE_NATIVE_FUNCTION(SourceBufferPrototype::onabort_getter)
{
    WebIDL::log_trace(vm, "SourceBufferPrototype::onabort_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->onabort(); }));

    return [&]() -> JS::Value {
        // 1. If the IDL nullable type T? value is null, then the JavaScript value is null.
        if (!R)
            return JS::js_null();

        // 2. Otherwise, the JavaScript value is the result of converting the IDL nullable type value to the inner IDL type T.
        return JS::Value(R->callback);
    }();
}

JS_DEFINE_NATIVE_FUNCTION(SourceBufferPrototype::mode_setter)
{
    WebIDL::log_trace(vm, "SourceBufferPrototype::mode_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]] MediaSourceExtensions::SourceBuffer* 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:
        // -> attribute's type is an enumeration
        // 1. Let S be ? ToString(V).
        auto maybe_idl_value = throw_dom_exception_if_needed(vm, [&] { return convert_to_idl_value_for_append_mode(vm, V); });

        // 2. If S is not one of the enumeration's values, then return undefined.
        if (maybe_idl_value.is_error())
            return JS::js_undefined();

        // 3. Otherwise, idlValue is the enumeration value equal to S.
        auto idl_value = maybe_idl_value.release_value();

        // 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_mode(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(SourceBufferPrototype::onupdatestart_setter)
{
    WebIDL::log_trace(vm, "SourceBufferPrototype::onupdatestart_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]] MediaSourceExtensions::SourceBuffer* 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<GC::Ptr<WebIDL::CallbackType>> {
        GC::Ptr<WebIDL::CallbackType> value;
        if (V.is_object()) {

        if (!V.is_nullish()) {

            value = TRY([&]() -> JS::ThrowCompletionOr<GC::Ptr<WebIDL::CallbackType>> {

        return vm.heap().allocate<WebIDL::CallbackType>(V.as_object(),  HTML::incumbent_realm(), WebIDL::OperationReturnsPromise::No);
    }());
        }
        }
        return value;
    }(); }));

        // 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_onupdatestart(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(SourceBufferPrototype::onupdate_setter)
{
    WebIDL::log_trace(vm, "SourceBufferPrototype::onupdate_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]] MediaSourceExtensions::SourceBuffer* 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<GC::Ptr<WebIDL::CallbackType>> {
        GC::Ptr<WebIDL::CallbackType> value;
        if (V.is_object()) {

        if (!V.is_nullish()) {

            value = TRY([&]() -> JS::ThrowCompletionOr<GC::Ptr<WebIDL::CallbackType>> {

        return vm.heap().allocate<WebIDL::CallbackType>(V.as_object(),  HTML::incumbent_realm(), WebIDL::OperationReturnsPromise::No);
    }());
        }
        }
        return value;
    }(); }));

        // 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_onupdate(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(SourceBufferPrototype::onupdateend_setter)
{
    WebIDL::log_trace(vm, "SourceBufferPrototype::onupdateend_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]] MediaSourceExtensions::SourceBuffer* 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<GC::Ptr<WebIDL::CallbackType>> {
        GC::Ptr<WebIDL::CallbackType> value;
        if (V.is_object()) {

        if (!V.is_nullish()) {

            value = TRY([&]() -> JS::ThrowCompletionOr<GC::Ptr<WebIDL::CallbackType>> {

        return vm.heap().allocate<WebIDL::CallbackType>(V.as_object(),  HTML::incumbent_realm(), WebIDL::OperationReturnsPromise::No);
    }());
        }
        }
        return value;
    }(); }));

        // 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_onupdateend(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(SourceBufferPrototype::onerror_setter)
{
    WebIDL::log_trace(vm, "SourceBufferPrototype::onerror_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]] MediaSourceExtensions::SourceBuffer* 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<GC::Ptr<WebIDL::CallbackType>> {
        GC::Ptr<WebIDL::CallbackType> value;
        if (V.is_object()) {

        if (!V.is_nullish()) {

            value = TRY([&]() -> JS::ThrowCompletionOr<GC::Ptr<WebIDL::CallbackType>> {

        return vm.heap().allocate<WebIDL::CallbackType>(V.as_object(),  HTML::incumbent_realm(), WebIDL::OperationReturnsPromise::No);
    }());
        }
        }
        return value;
    }(); }));

        // 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_onerror(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(SourceBufferPrototype::onabort_setter)
{
    WebIDL::log_trace(vm, "SourceBufferPrototype::onabort_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]] MediaSourceExtensions::SourceBuffer* 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<GC::Ptr<WebIDL::CallbackType>> {
        GC::Ptr<WebIDL::CallbackType> value;
        if (V.is_object()) {

        if (!V.is_nullish()) {

            value = TRY([&]() -> JS::ThrowCompletionOr<GC::Ptr<WebIDL::CallbackType>> {

        return vm.heap().allocate<WebIDL::CallbackType>(V.as_object(),  HTML::incumbent_realm(), WebIDL::OperationReturnsPromise::No);
    }());
        }
        }
        return value;
    }(); }));

        // 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_onabort(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(SourceBufferPrototype::append_buffer)
{
    WebIDL::log_trace(vm, "SourceBufferPrototype::append_buffer");
    [[maybe_unused]] auto& realm = *vm.current_realm();
    [[maybe_unused]] MediaSourceExtensions::SourceBuffer* idl_object = TRY(impl_from(vm));

    if (vm.argument_count() < 1)
        return vm.throw_completion<JS::TypeError>(JS::ErrorType::BadArgCountOne, "appendBuffer");

    auto arg0 = vm.argument(0);
    auto data = TRY(throw_dom_exception_if_needed(vm, [&] { return [&]() -> JS::ThrowCompletionOr<Variant<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 (arg0.is_object()) {
            [[maybe_unused]] auto& object = arg0.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 = arg0.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<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 = arg0.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<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 = arg0.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<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 = arg0.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<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 = arg0.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<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 = arg0.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<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 = arg0.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<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 = arg0.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<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 = arg0.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<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 = arg0.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<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 = arg0.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<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 = arg0.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<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 = arg0.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<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 = arg0.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<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 };
            }
        }

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

    [[maybe_unused]] auto R = TRY(throw_dom_exception_if_needed(vm, [&] { return idl_object->append_buffer(data); }));
    return JS::js_undefined();
}

JS_DEFINE_NATIVE_FUNCTION(SourceBufferPrototype::abort)
{
    WebIDL::log_trace(vm, "SourceBufferPrototype::abort");
    [[maybe_unused]] auto& realm = *vm.current_realm();
    [[maybe_unused]] MediaSourceExtensions::SourceBuffer* idl_object = TRY(impl_from(vm));

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

JS_DEFINE_NATIVE_FUNCTION(SourceBufferPrototype::change_type)
{
    WebIDL::log_trace(vm, "SourceBufferPrototype::change_type");
    [[maybe_unused]] auto& realm = *vm.current_realm();
    [[maybe_unused]] MediaSourceExtensions::SourceBuffer* idl_object = TRY(impl_from(vm));

    if (vm.argument_count() < 1)
        return vm.throw_completion<JS::TypeError>(JS::ErrorType::BadArgCountOne, "changeType");

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

    [[maybe_unused]] auto R = TRY(throw_dom_exception_if_needed(vm, [&] { return idl_object->change_type(type); }));
    return JS::js_undefined();
}

// https://webidl.spec.whatwg.org/#idl-enumeration
JS::ThrowCompletionOr<AppendMode> convert_to_idl_value_for_append_mode(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 == "segments"sv)
        return AppendMode::Segments;
    if (value_as_string == "sequence"sv)
        return AppendMode::Sequence;
    return vm.throw_completion<JS::TypeError>(JS::ErrorType::InvalidEnumerationValue, value_as_string, "AppendMode");
}

// https://webidl.spec.whatwg.org/#idl-enumeration
Utf16String idl_enum_to_string(AppendMode 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 AppendMode::Segments:
        return "segments"_utf16;
    case AppendMode::Sequence:
        return "sequence"_utf16;
    }
    VERIFY_NOT_REACHED();
}

} // namespace Web::Bindings
