LibWeb: Visit custom element's lifecycle callbacks

...instead of using JS::Handle which causes leaks when object holding
the callback can be reached by visiting the callback's dependencies.
This commit is contained in:
Aliaksandr Kalenik 2024-03-11 19:12:52 +01:00 committed by Alexander Kalenik
parent 3d48c55e50
commit 3b4230e0b0
Notes: sideshowbarker 2024-07-18 03:20:18 +09:00
5 changed files with 26 additions and 5 deletions

View file

@ -250,6 +250,7 @@ set(SOURCES
HTML/CORSSettingAttribute.cpp
HTML/CrossOrigin/AbstractOperations.cpp
HTML/CrossOrigin/Reporting.cpp
HTML/CustomElements/CustomElementDefinition.cpp
HTML/CustomElements/CustomElementName.cpp
HTML/CustomElements/CustomElementReactionNames.cpp
HTML/CustomElements/CustomElementRegistry.cpp

View file

@ -1910,7 +1910,7 @@ void Element::enqueue_a_custom_element_callback_reaction(FlyString const& callba
if (callback_iterator == definition->lifecycle_callbacks().end())
return;
if (callback_iterator->value.is_null())
if (!callback_iterator->value)
return;
// 4. If callbackName is "attributeChangedCallback", then:

View file

@ -0,0 +1,18 @@
/*
* Copyright (c) 2024, Aliaksandr Kalenik <kalenik.aliaksandr@gmail.com>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <LibWeb/HTML/CustomElements/CustomElementDefinition.h>
namespace Web::HTML {
void CustomElementDefinition::visit_edges(Visitor& visitor)
{
Base::visit_edges(visitor);
for (auto& callback : m_lifecycle_callbacks)
visitor.visit(callback.value);
}
}

View file

@ -20,7 +20,7 @@ class CustomElementDefinition : public JS::Cell {
JS_CELL(CustomElementDefinition, JS::Cell);
JS_DECLARE_ALLOCATOR(CustomElementDefinition);
using LifecycleCallbacksStorage = OrderedHashMap<FlyString, JS::Handle<WebIDL::CallbackType>>;
using LifecycleCallbacksStorage = OrderedHashMap<FlyString, JS::GCPtr<WebIDL::CallbackType>>;
using ConstructionStackStorage = Vector<Variant<JS::Handle<DOM::Element>, AlreadyConstructedCustomElementMarker>>;
static JS::NonnullGCPtr<CustomElementDefinition> create(JS::Realm& realm, String const& name, String const& local_name, WebIDL::CallbackType& constructor, Vector<String>&& observed_attributes, LifecycleCallbacksStorage&& lifecycle_callbacks, bool form_associated, bool disable_internals, bool disable_shadow)
@ -60,6 +60,8 @@ private:
{
}
virtual void visit_edges(Visitor& visitor) override;
// https://html.spec.whatwg.org/multipage/custom-elements.html#concept-custom-element-definition-name
// A name
// A valid custom element name

View file

@ -201,14 +201,14 @@ JS::ThrowCompletionOr<void> CustomElementRegistry::define(String const& name, We
// 2. If callbackValue is not undefined, then set the value of the entry in lifecycleCallbacks with key callbackName to the result of converting callbackValue to the Web IDL Function callback type. Rethrow any exceptions from the conversion.
if (!callback_value.is_undefined()) {
auto callback = TRY(convert_value_to_callback_function(vm, callback_value));
lifecycle_callbacks.set(callback_name, JS::make_handle(callback));
lifecycle_callbacks.set(callback_name, callback);
}
}
// 5. If the value of the entry in lifecycleCallbacks with key "attributeChangedCallback" is not null, then:
auto attribute_changed_callback_iterator = lifecycle_callbacks.find(CustomElementReactionNames::attributeChangedCallback);
VERIFY(attribute_changed_callback_iterator != lifecycle_callbacks.end());
if (!attribute_changed_callback_iterator->value.is_null()) {
if (attribute_changed_callback_iterator->value) {
// 1. Let observedAttributesIterable be ? Get(constructor, "observedAttributes").
auto observed_attributes_iterable = TRY(constructor->callback->get(JS::PropertyKey { "observedAttributes" }));
@ -253,7 +253,7 @@ JS::ThrowCompletionOr<void> CustomElementRegistry::define(String const& name, We
// 2. If callbackValue is not undefined, then set the value of the entry in lifecycleCallbacks with key callbackName to the result of converting callbackValue to the Web IDL Function callback type. Rethrow any exceptions from the conversion.
if (!callback_value.is_undefined())
lifecycle_callbacks.set(callback_name, JS::make_handle(TRY(convert_value_to_callback_function(vm, callback_value))));
lifecycle_callbacks.set(callback_name, TRY(convert_value_to_callback_function(vm, callback_value)));
}
}