diff --git a/Userland/Libraries/LibJS/Runtime/Array.h b/Userland/Libraries/LibJS/Runtime/Array.h index d5f40d2a5b..5e3bbf8a0c 100644 --- a/Userland/Libraries/LibJS/Runtime/Array.h +++ b/Userland/Libraries/LibJS/Runtime/Array.h @@ -44,7 +44,7 @@ public: virtual ThrowCompletionOr> internal_get_own_property(PropertyKey const&) const override final; virtual ThrowCompletionOr internal_define_own_property(PropertyKey const&, PropertyDescriptor const&) override final; - virtual ThrowCompletionOr internal_delete(PropertyKey const&) override final; + virtual ThrowCompletionOr internal_delete(PropertyKey const&) override; virtual ThrowCompletionOr> internal_own_property_keys() const override final; [[nodiscard]] bool length_is_writable() const { return m_length_writable; } diff --git a/Userland/Libraries/LibWeb/CMakeLists.txt b/Userland/Libraries/LibWeb/CMakeLists.txt index 426fa29e83..d398e48b9a 100644 --- a/Userland/Libraries/LibWeb/CMakeLists.txt +++ b/Userland/Libraries/LibWeb/CMakeLists.txt @@ -647,6 +647,7 @@ set(SOURCES WebIDL/Buffers.cpp WebIDL/CallbackType.cpp WebIDL/DOMException.cpp + WebIDL/ObservableArray.cpp WebIDL/OverloadResolution.cpp WebIDL/Promise.cpp WebSockets/WebSocket.cpp diff --git a/Userland/Libraries/LibWeb/WebIDL/ObservableArray.cpp b/Userland/Libraries/LibWeb/WebIDL/ObservableArray.cpp new file mode 100644 index 0000000000..a54fc4a460 --- /dev/null +++ b/Userland/Libraries/LibWeb/WebIDL/ObservableArray.cpp @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2024, Aliaksandr Kalenik + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include + +namespace Web::WebIDL { + +JS::NonnullGCPtr ObservableArray::create(JS::Realm& realm) +{ + auto prototype = realm.intrinsics().array_prototype(); + return realm.heap().allocate(realm, prototype); +} + +ObservableArray::ObservableArray(Object& prototype) + : JS::Array(prototype) +{ +} + +void ObservableArray::visit_edges(JS::Cell::Visitor& visitor) +{ + Base::visit_edges(visitor); + visitor.visit(m_on_set_an_indexed_value); + visitor.visit(m_on_delete_an_indexed_value); +} + +void ObservableArray::set_on_set_an_indexed_value_callback(SetAnIndexedValueCallbackFunction&& callback) +{ + m_on_set_an_indexed_value = create_heap_function(heap(), move(callback)); +} + +void ObservableArray::set_on_delete_an_indexed_value_callback(DeleteAnIndexedValueCallbackFunction&& callback) +{ + m_on_delete_an_indexed_value = create_heap_function(heap(), move(callback)); +} + +JS::ThrowCompletionOr ObservableArray::internal_set(JS::PropertyKey const& property_key, JS::Value value, JS::Value receiver, JS::CacheablePropertyMetadata* metadata) +{ + if (property_key.is_number() && m_on_set_an_indexed_value) + TRY(Bindings::throw_dom_exception_if_needed(vm(), [&] { return m_on_set_an_indexed_value->function()(value); })); + return TRY(Base::internal_set(property_key, value, receiver, metadata)); +} + +JS::ThrowCompletionOr ObservableArray::internal_delete(JS::PropertyKey const& property_key) +{ + if (property_key.is_number() && m_on_delete_an_indexed_value) + TRY(Bindings::throw_dom_exception_if_needed(vm(), [&] { return m_on_delete_an_indexed_value->function()(); })); + return JS::Array::internal_delete(property_key); +} + +JS::ThrowCompletionOr ObservableArray::append(JS::Value value) +{ + if (m_on_set_an_indexed_value) + TRY(Bindings::throw_dom_exception_if_needed(vm(), [&] { return m_on_set_an_indexed_value->function()(value); })); + indexed_properties().append(value); + return {}; +} + +void ObservableArray::clear() +{ + while (!indexed_properties().is_empty()) { + indexed_properties().storage()->take_first(); + } +} + +} diff --git a/Userland/Libraries/LibWeb/WebIDL/ObservableArray.h b/Userland/Libraries/LibWeb/WebIDL/ObservableArray.h new file mode 100644 index 0000000000..7a1078f0bf --- /dev/null +++ b/Userland/Libraries/LibWeb/WebIDL/ObservableArray.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2024, Aliaksandr Kalenik + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include + +namespace Web::WebIDL { + +// https://webidl.spec.whatwg.org/#idl-observable-array +class ObservableArray final : public JS::Array { +public: + static JS::NonnullGCPtr create(JS::Realm& realm); + + virtual JS::ThrowCompletionOr internal_set(JS::PropertyKey const& property_key, JS::Value value, JS::Value receiver, JS::CacheablePropertyMetadata* metadata = nullptr) override; + virtual JS::ThrowCompletionOr internal_delete(JS::PropertyKey const& property_key) override; + + using SetAnIndexedValueCallbackFunction = Function(JS::Value&)>; + using DeleteAnIndexedValueCallbackFunction = Function()>; + + void set_on_set_an_indexed_value_callback(SetAnIndexedValueCallbackFunction&& callback); + void set_on_delete_an_indexed_value_callback(DeleteAnIndexedValueCallbackFunction&& callback); + + JS::ThrowCompletionOr append(JS::Value value); + void clear(); + + explicit ObservableArray(Object& prototype); + + virtual void visit_edges(JS::Cell::Visitor&) override; + +private: + using SetAnIndexedValueCallbackHeapFunction = JS::HeapFunction; + using DeleteAnIndexedValueCallbackHeapFunction = JS::HeapFunction; + + JS::GCPtr m_on_set_an_indexed_value; + JS::GCPtr m_on_delete_an_indexed_value; +}; + +}