mirror of
https://github.com/RGBCube/serenity
synced 2025-07-25 16:37:35 +00:00
LibWeb: Make MutationObserver GC-allocated
This commit is contained in:
parent
43ec0f734f
commit
905eb8cb4d
8 changed files with 134 additions and 68 deletions
|
@ -14,7 +14,6 @@
|
||||||
#include <LibWeb/Bindings/IDLAbstractOperations.h>
|
#include <LibWeb/Bindings/IDLAbstractOperations.h>
|
||||||
#include <LibWeb/Bindings/LocationObject.h>
|
#include <LibWeb/Bindings/LocationObject.h>
|
||||||
#include <LibWeb/Bindings/MainThreadVM.h>
|
#include <LibWeb/Bindings/MainThreadVM.h>
|
||||||
#include <LibWeb/Bindings/MutationObserverWrapper.h>
|
|
||||||
#include <LibWeb/Bindings/WindowProxy.h>
|
#include <LibWeb/Bindings/WindowProxy.h>
|
||||||
#include <LibWeb/DOM/Document.h>
|
#include <LibWeb/DOM/Document.h>
|
||||||
#include <LibWeb/HTML/PromiseRejectionEvent.h>
|
#include <LibWeb/HTML/PromiseRejectionEvent.h>
|
||||||
|
@ -356,22 +355,22 @@ void queue_mutation_observer_microtask(DOM::Document& document)
|
||||||
for (auto& mutation_observer : notify_set) {
|
for (auto& mutation_observer : notify_set) {
|
||||||
// 1. Let records be a clone of mo’s record queue.
|
// 1. Let records be a clone of mo’s record queue.
|
||||||
// 2. Empty mo’s record queue.
|
// 2. Empty mo’s record queue.
|
||||||
auto records = mutation_observer.take_records();
|
auto records = mutation_observer->take_records();
|
||||||
|
|
||||||
// 3. For each node of mo’s node list, remove all transient registered observers whose observer is mo from node’s registered observer list.
|
// 3. For each node of mo’s node list, remove all transient registered observers whose observer is mo from node’s registered observer list.
|
||||||
for (auto& node : mutation_observer.node_list()) {
|
for (auto& node : mutation_observer->node_list()) {
|
||||||
// FIXME: Is this correct?
|
// FIXME: Is this correct?
|
||||||
if (node.is_null())
|
if (node.is_null())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
node->registered_observers_list().remove_all_matching([&mutation_observer](DOM::RegisteredObserver& registered_observer) {
|
node->registered_observers_list().remove_all_matching([&mutation_observer](DOM::RegisteredObserver& registered_observer) {
|
||||||
return is<DOM::TransientRegisteredObserver>(registered_observer) && static_cast<DOM::TransientRegisteredObserver&>(registered_observer).observer.ptr() == &mutation_observer;
|
return is<DOM::TransientRegisteredObserver>(registered_observer) && static_cast<DOM::TransientRegisteredObserver&>(registered_observer).observer().ptr() == mutation_observer.ptr();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// 4. If records is not empty, then invoke mo’s callback with « records, mo », and mo. If this throws an exception, catch it, and report the exception.
|
// 4. If records is not empty, then invoke mo’s callback with « records, mo », and mo. If this throws an exception, catch it, and report the exception.
|
||||||
if (!records.is_empty()) {
|
if (!records.is_empty()) {
|
||||||
auto& callback = mutation_observer.callback();
|
auto& callback = mutation_observer->callback();
|
||||||
auto& realm = callback.callback_context.realm();
|
auto& realm = callback.callback_context.realm();
|
||||||
|
|
||||||
auto* wrapped_records = MUST(JS::Array::create(realm, 0));
|
auto* wrapped_records = MUST(JS::Array::create(realm, 0));
|
||||||
|
@ -381,9 +380,7 @@ void queue_mutation_observer_microtask(DOM::Document& document)
|
||||||
MUST(wrapped_records->create_data_property(property_index, record.ptr()));
|
MUST(wrapped_records->create_data_property(property_index, record.ptr()));
|
||||||
}
|
}
|
||||||
|
|
||||||
auto* wrapped_mutation_observer = Bindings::wrap(realm, mutation_observer);
|
auto result = IDL::invoke_callback(callback, mutation_observer.ptr(), wrapped_records, mutation_observer.ptr());
|
||||||
|
|
||||||
auto result = IDL::invoke_callback(callback, wrapped_mutation_observer, wrapped_records, wrapped_mutation_observer);
|
|
||||||
if (result.is_abrupt())
|
if (result.is_abrupt())
|
||||||
HTML::report_exception(result);
|
HTML::report_exception(result);
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,7 +29,7 @@ struct WebEngineCustomData final : public JS::VM::CustomData {
|
||||||
|
|
||||||
// https://dom.spec.whatwg.org/#mutation-observer-list
|
// https://dom.spec.whatwg.org/#mutation-observer-list
|
||||||
// FIXME: This should be a set.
|
// FIXME: This should be a set.
|
||||||
NonnullRefPtrVector<DOM::MutationObserver> mutation_observers;
|
Vector<JS::Handle<DOM::MutationObserver>> mutation_observers;
|
||||||
|
|
||||||
OwnPtr<JS::ExecutionContext> root_execution_context;
|
OwnPtr<JS::ExecutionContext> root_execution_context;
|
||||||
|
|
||||||
|
|
|
@ -5,23 +5,42 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <LibWeb/Bindings/MainThreadVM.h>
|
#include <LibWeb/Bindings/MainThreadVM.h>
|
||||||
|
#include <LibWeb/Bindings/MutationObserverPrototype.h>
|
||||||
#include <LibWeb/DOM/MutationObserver.h>
|
#include <LibWeb/DOM/MutationObserver.h>
|
||||||
#include <LibWeb/DOM/Node.h>
|
#include <LibWeb/DOM/Node.h>
|
||||||
#include <LibWeb/HTML/Window.h>
|
#include <LibWeb/HTML/Window.h>
|
||||||
|
|
||||||
namespace Web::DOM {
|
namespace Web::DOM {
|
||||||
|
|
||||||
// https://dom.spec.whatwg.org/#dom-mutationobserver-mutationobserver
|
JS::NonnullGCPtr<MutationObserver> MutationObserver::create_with_global_object(HTML::Window& window, JS::GCPtr<Bindings::CallbackType> callback)
|
||||||
MutationObserver::MutationObserver(HTML::Window& window_object, JS::Handle<Bindings::CallbackType> callback)
|
|
||||||
: m_callback(move(callback))
|
|
||||||
{
|
{
|
||||||
|
return *window.heap().allocate<MutationObserver>(window.realm(), window, callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://dom.spec.whatwg.org/#dom-mutationobserver-mutationobserver
|
||||||
|
MutationObserver::MutationObserver(HTML::Window& window, JS::GCPtr<Bindings::CallbackType> callback)
|
||||||
|
: PlatformObject(window.realm())
|
||||||
|
, m_callback(move(callback))
|
||||||
|
{
|
||||||
|
set_prototype(&window.ensure_web_prototype<Bindings::MutationObserverPrototype>("MutationObserver"));
|
||||||
|
|
||||||
// 1. Set this’s callback to callback.
|
// 1. Set this’s callback to callback.
|
||||||
|
|
||||||
// 2. Append this to this’s relevant agent’s mutation observers.
|
// 2. Append this to this’s relevant agent’s mutation observers.
|
||||||
auto* agent_custom_data = verify_cast<Bindings::WebEngineCustomData>(window_object.vm().custom_data());
|
auto* agent_custom_data = verify_cast<Bindings::WebEngineCustomData>(window.vm().custom_data());
|
||||||
agent_custom_data->mutation_observers.append(*this);
|
agent_custom_data->mutation_observers.append(*this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MutationObserver::~MutationObserver() = default;
|
||||||
|
|
||||||
|
void MutationObserver::visit_edges(Cell::Visitor& visitor)
|
||||||
|
{
|
||||||
|
Base::visit_edges(visitor);
|
||||||
|
visitor.visit(m_callback.ptr());
|
||||||
|
for (auto& record : m_record_queue)
|
||||||
|
visitor.visit(record.ptr());
|
||||||
|
}
|
||||||
|
|
||||||
// https://dom.spec.whatwg.org/#dom-mutationobserver-observe
|
// https://dom.spec.whatwg.org/#dom-mutationobserver-observe
|
||||||
ExceptionOr<void> MutationObserver::observe(Node& target, MutationObserverInit options)
|
ExceptionOr<void> MutationObserver::observe(Node& target, MutationObserverInit options)
|
||||||
{
|
{
|
||||||
|
@ -55,7 +74,7 @@ ExceptionOr<void> MutationObserver::observe(Node& target, MutationObserverInit o
|
||||||
// 7. For each registered of target’s registered observer list, if registered’s observer is this:
|
// 7. For each registered of target’s registered observer list, if registered’s observer is this:
|
||||||
bool updated_existing_observer = false;
|
bool updated_existing_observer = false;
|
||||||
for (auto& registered_observer : target.registered_observers_list()) {
|
for (auto& registered_observer : target.registered_observers_list()) {
|
||||||
if (registered_observer.observer.ptr() != this)
|
if (registered_observer.observer().ptr() != this)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
updated_existing_observer = true;
|
updated_existing_observer = true;
|
||||||
|
@ -67,12 +86,12 @@ ExceptionOr<void> MutationObserver::observe(Node& target, MutationObserverInit o
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
node->registered_observers_list().remove_all_matching([®istered_observer](RegisteredObserver& observer) {
|
node->registered_observers_list().remove_all_matching([®istered_observer](RegisteredObserver& observer) {
|
||||||
return is<TransientRegisteredObserver>(observer) && verify_cast<TransientRegisteredObserver>(observer).source.ptr() == ®istered_observer;
|
return is<TransientRegisteredObserver>(observer) && verify_cast<TransientRegisteredObserver>(observer).source().ptr() == ®istered_observer;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. Set registered’s options to options.
|
// 2. Set registered’s options to options.
|
||||||
registered_observer.options = options;
|
registered_observer.set_options(options);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -99,7 +118,7 @@ void MutationObserver::disconnect()
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
node->registered_observers_list().remove_all_matching([this](RegisteredObserver& registered_observer) {
|
node->registered_observers_list().remove_all_matching([this](RegisteredObserver& registered_observer) {
|
||||||
return registered_observer.observer.ptr() == this;
|
return registered_observer.observer().ptr() == this;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -111,7 +130,9 @@ void MutationObserver::disconnect()
|
||||||
Vector<JS::Handle<MutationRecord>> MutationObserver::take_records()
|
Vector<JS::Handle<MutationRecord>> MutationObserver::take_records()
|
||||||
{
|
{
|
||||||
// 1. Let records be a clone of this’s record queue.
|
// 1. Let records be a clone of this’s record queue.
|
||||||
auto records = m_record_queue;
|
Vector<JS::Handle<MutationRecord>> records;
|
||||||
|
for (auto& record : m_record_queue)
|
||||||
|
records.append(*record);
|
||||||
|
|
||||||
// 2. Empty this’s record queue.
|
// 2. Empty this’s record queue.
|
||||||
m_record_queue.clear();
|
m_record_queue.clear();
|
||||||
|
@ -120,4 +141,42 @@ Vector<JS::Handle<MutationRecord>> MutationObserver::take_records()
|
||||||
return records;
|
return records;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
JS::NonnullGCPtr<RegisteredObserver> RegisteredObserver::create(MutationObserver& observer, MutationObserverInit const& options)
|
||||||
|
{
|
||||||
|
return *observer.heap().allocate_without_realm<RegisteredObserver>(observer, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
RegisteredObserver::RegisteredObserver(MutationObserver& observer, MutationObserverInit const& options)
|
||||||
|
: m_observer(observer)
|
||||||
|
, m_options(options)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
RegisteredObserver::~RegisteredObserver() = default;
|
||||||
|
|
||||||
|
void RegisteredObserver::visit_edges(Cell::Visitor& visitor)
|
||||||
|
{
|
||||||
|
Base::visit_edges(visitor);
|
||||||
|
visitor.visit(m_observer.ptr());
|
||||||
|
}
|
||||||
|
|
||||||
|
JS::NonnullGCPtr<TransientRegisteredObserver> TransientRegisteredObserver::create(MutationObserver& observer, MutationObserverInit const& options, RegisteredObserver& source)
|
||||||
|
{
|
||||||
|
return *observer.heap().allocate_without_realm<TransientRegisteredObserver>(observer, options, source);
|
||||||
|
}
|
||||||
|
|
||||||
|
TransientRegisteredObserver::TransientRegisteredObserver(MutationObserver& observer, MutationObserverInit const& options, RegisteredObserver& source)
|
||||||
|
: RegisteredObserver(observer, options)
|
||||||
|
, m_source(source)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
TransientRegisteredObserver::~TransientRegisteredObserver() = default;
|
||||||
|
|
||||||
|
void TransientRegisteredObserver::visit_edges(Cell::Visitor& visitor)
|
||||||
|
{
|
||||||
|
Base::visit_edges(visitor);
|
||||||
|
visitor.visit(m_source.ptr());
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2022, Luke Wilde <lukew@serenityos.org>
|
* Copyright (c) 2022, Luke Wilde <lukew@serenityos.org>
|
||||||
|
* Copyright (c) 2022, Andreas Kling <kling@serenityos.org>
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: BSD-2-Clause
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
*/
|
*/
|
||||||
|
@ -28,18 +29,12 @@ struct MutationObserverInit {
|
||||||
};
|
};
|
||||||
|
|
||||||
// https://dom.spec.whatwg.org/#mutationobserver
|
// https://dom.spec.whatwg.org/#mutationobserver
|
||||||
class MutationObserver final
|
class MutationObserver final : public Bindings::PlatformObject {
|
||||||
: public RefCounted<MutationObserver>
|
WEB_PLATFORM_OBJECT(MutationObserver, Bindings::PlatformObject);
|
||||||
, public Bindings::Wrappable {
|
|
||||||
public:
|
public:
|
||||||
using WrapperType = Bindings::MutationObserverWrapper;
|
static JS::NonnullGCPtr<MutationObserver> create_with_global_object(HTML::Window&, JS::GCPtr<Bindings::CallbackType>);
|
||||||
|
virtual ~MutationObserver() override;
|
||||||
static NonnullRefPtr<MutationObserver> create_with_global_object(HTML::Window& window_object, Bindings::CallbackType* callback)
|
|
||||||
{
|
|
||||||
return adopt_ref(*new MutationObserver(window_object, JS::make_handle(callback)));
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual ~MutationObserver() override = default;
|
|
||||||
|
|
||||||
ExceptionOr<void> observe(Node& target, MutationObserverInit options = {});
|
ExceptionOr<void> observe(Node& target, MutationObserverInit options = {});
|
||||||
void disconnect();
|
void disconnect();
|
||||||
|
@ -56,53 +51,61 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
MutationObserver(HTML::Window& window_object, JS::Handle<Bindings::CallbackType> callback);
|
MutationObserver(HTML::Window&, JS::GCPtr<Bindings::CallbackType>);
|
||||||
|
|
||||||
|
virtual void visit_edges(Cell::Visitor&) override;
|
||||||
|
|
||||||
// https://dom.spec.whatwg.org/#concept-mo-callback
|
// https://dom.spec.whatwg.org/#concept-mo-callback
|
||||||
JS::Handle<Bindings::CallbackType> m_callback;
|
JS::GCPtr<Bindings::CallbackType> m_callback;
|
||||||
|
|
||||||
// https://dom.spec.whatwg.org/#mutationobserver-node-list
|
// https://dom.spec.whatwg.org/#mutationobserver-node-list
|
||||||
|
// NOTE: These are weak, per https://dom.spec.whatwg.org/#garbage-collection
|
||||||
|
// Registered observers in a node’s registered observer list have a weak reference to the node.
|
||||||
Vector<WeakPtr<Node>> m_node_list;
|
Vector<WeakPtr<Node>> m_node_list;
|
||||||
|
|
||||||
// https://dom.spec.whatwg.org/#concept-mo-queue
|
// https://dom.spec.whatwg.org/#concept-mo-queue
|
||||||
Vector<JS::Handle<MutationRecord>> m_record_queue;
|
Vector<JS::NonnullGCPtr<MutationRecord>> m_record_queue;
|
||||||
};
|
};
|
||||||
|
|
||||||
// https://dom.spec.whatwg.org/#registered-observer
|
// https://dom.spec.whatwg.org/#registered-observer
|
||||||
struct RegisteredObserver : public RefCounted<RegisteredObserver> {
|
class RegisteredObserver : public JS::Cell {
|
||||||
static NonnullRefPtr<RegisteredObserver> create(MutationObserver& observer, MutationObserverInit& options)
|
JS_CELL(RegisteredObserver, JS::Cell);
|
||||||
{
|
|
||||||
return adopt_ref(*new RegisteredObserver(observer, options));
|
|
||||||
}
|
|
||||||
|
|
||||||
RegisteredObserver(MutationObserver& observer, MutationObserverInit& options)
|
public:
|
||||||
: observer(observer)
|
static JS::NonnullGCPtr<RegisteredObserver> create(MutationObserver&, MutationObserverInit const&);
|
||||||
, options(options)
|
virtual ~RegisteredObserver() override;
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual ~RegisteredObserver() = default;
|
JS::NonnullGCPtr<MutationObserver> observer() const { return m_observer; }
|
||||||
|
|
||||||
NonnullRefPtr<MutationObserver> observer;
|
MutationObserverInit const& options() const { return m_options; }
|
||||||
MutationObserverInit options;
|
void set_options(MutationObserverInit options) { m_options = move(options); }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
RegisteredObserver(MutationObserver& observer, MutationObserverInit const& options);
|
||||||
|
virtual void visit_edges(Cell::Visitor&) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
JS::NonnullGCPtr<MutationObserver> m_observer;
|
||||||
|
MutationObserverInit m_options;
|
||||||
};
|
};
|
||||||
|
|
||||||
// https://dom.spec.whatwg.org/#transient-registered-observer
|
// https://dom.spec.whatwg.org/#transient-registered-observer
|
||||||
struct TransientRegisteredObserver final : public RegisteredObserver {
|
class TransientRegisteredObserver final : public RegisteredObserver {
|
||||||
static NonnullRefPtr<TransientRegisteredObserver> create(MutationObserver& observer, MutationObserverInit& options, RegisteredObserver& source)
|
JS_CELL(TransientRegisteredObserver, RegisteredObserver);
|
||||||
{
|
|
||||||
return adopt_ref(*new TransientRegisteredObserver(observer, options, source));
|
|
||||||
}
|
|
||||||
|
|
||||||
TransientRegisteredObserver(MutationObserver& observer, MutationObserverInit& options, RegisteredObserver& source)
|
public:
|
||||||
: RegisteredObserver(observer, options)
|
static JS::NonnullGCPtr<TransientRegisteredObserver> create(MutationObserver&, MutationObserverInit const&, RegisteredObserver& source);
|
||||||
, source(source)
|
virtual ~TransientRegisteredObserver() override;
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual ~TransientRegisteredObserver() override = default;
|
JS::NonnullGCPtr<RegisteredObserver> source() const { return m_source; }
|
||||||
|
|
||||||
NonnullRefPtr<RegisteredObserver> source;
|
private:
|
||||||
|
TransientRegisteredObserver(MutationObserver& observer, MutationObserverInit const& options, RegisteredObserver& source);
|
||||||
|
virtual void visit_edges(Cell::Visitor&) override;
|
||||||
|
|
||||||
|
JS::NonnullGCPtr<RegisteredObserver> m_source;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
WRAPPER_HACK(MutationObserver, Web::DOM)
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
#include <AK/IDAllocator.h>
|
#include <AK/IDAllocator.h>
|
||||||
#include <AK/StringBuilder.h>
|
#include <AK/StringBuilder.h>
|
||||||
#include <LibJS/AST.h>
|
#include <LibJS/AST.h>
|
||||||
|
#include <LibJS/Heap/DeferGC.h>
|
||||||
#include <LibJS/Runtime/FunctionObject.h>
|
#include <LibJS/Runtime/FunctionObject.h>
|
||||||
#include <LibWeb/Bindings/MainThreadVM.h>
|
#include <LibWeb/Bindings/MainThreadVM.h>
|
||||||
#include <LibWeb/Bindings/NodePrototype.h>
|
#include <LibWeb/Bindings/NodePrototype.h>
|
||||||
|
@ -89,6 +90,9 @@ void Node::visit_edges(Cell::Visitor& visitor)
|
||||||
visitor.visit(m_last_child.ptr());
|
visitor.visit(m_last_child.ptr());
|
||||||
visitor.visit(m_next_sibling.ptr());
|
visitor.visit(m_next_sibling.ptr());
|
||||||
visitor.visit(m_previous_sibling.ptr());
|
visitor.visit(m_previous_sibling.ptr());
|
||||||
|
|
||||||
|
for (auto& registered_observer : m_registered_observer_list)
|
||||||
|
visitor.visit(registered_observer);
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://dom.spec.whatwg.org/#dom-node-baseuri
|
// https://dom.spec.whatwg.org/#dom-node-baseuri
|
||||||
|
@ -578,8 +582,8 @@ void Node::remove(bool suppress_observers)
|
||||||
// whose observer is registered’s observer, options is registered’s options, and source is registered to node’s registered observer list.
|
// whose observer is registered’s observer, options is registered’s options, and source is registered to node’s registered observer list.
|
||||||
for (auto* inclusive_ancestor = parent; inclusive_ancestor; inclusive_ancestor = inclusive_ancestor->parent()) {
|
for (auto* inclusive_ancestor = parent; inclusive_ancestor; inclusive_ancestor = inclusive_ancestor->parent()) {
|
||||||
for (auto& registered : inclusive_ancestor->m_registered_observer_list) {
|
for (auto& registered : inclusive_ancestor->m_registered_observer_list) {
|
||||||
if (registered.options.subtree) {
|
if (registered.options().subtree) {
|
||||||
auto transient_observer = TransientRegisteredObserver::create(registered.observer, registered.options, registered);
|
auto transient_observer = TransientRegisteredObserver::create(registered.observer(), registered.options(), registered);
|
||||||
m_registered_observer_list.append(move(transient_observer));
|
m_registered_observer_list.append(move(transient_observer));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1321,9 +1325,13 @@ Painting::PaintableBox const* Node::paint_box() const
|
||||||
// https://dom.spec.whatwg.org/#queue-a-mutation-record
|
// https://dom.spec.whatwg.org/#queue-a-mutation-record
|
||||||
void Node::queue_mutation_record(FlyString const& type, String attribute_name, String attribute_namespace, String old_value, JS::NonnullGCPtr<NodeList> added_nodes, JS::NonnullGCPtr<NodeList> removed_nodes, Node* previous_sibling, Node* next_sibling)
|
void Node::queue_mutation_record(FlyString const& type, String attribute_name, String attribute_namespace, String old_value, JS::NonnullGCPtr<NodeList> added_nodes, JS::NonnullGCPtr<NodeList> removed_nodes, Node* previous_sibling, Node* next_sibling)
|
||||||
{
|
{
|
||||||
|
// NOTE: We defer garbage collection until the end of the scope, since we can't safely use MutationObserver* as a hashmap key otherwise.
|
||||||
|
// FIXME: This is a total hack.
|
||||||
|
JS::DeferGC defer_gc(heap());
|
||||||
|
|
||||||
// 1. Let interestedObservers be an empty map.
|
// 1. Let interestedObservers be an empty map.
|
||||||
// mutationObserver -> mappedOldValue
|
// mutationObserver -> mappedOldValue
|
||||||
OrderedHashMap<NonnullRefPtr<MutationObserver>, String> interested_observers;
|
OrderedHashMap<MutationObserver*, String> interested_observers;
|
||||||
|
|
||||||
// 2. Let nodes be the inclusive ancestors of target.
|
// 2. Let nodes be the inclusive ancestors of target.
|
||||||
Vector<JS::Handle<Node>> nodes;
|
Vector<JS::Handle<Node>> nodes;
|
||||||
|
@ -1336,7 +1344,7 @@ void Node::queue_mutation_record(FlyString const& type, String attribute_name, S
|
||||||
for (auto& node : nodes) {
|
for (auto& node : nodes) {
|
||||||
for (auto& registered_observer : node->m_registered_observer_list) {
|
for (auto& registered_observer : node->m_registered_observer_list) {
|
||||||
// 1. Let options be registered’s options.
|
// 1. Let options be registered’s options.
|
||||||
auto& options = registered_observer.options;
|
auto& options = registered_observer.options();
|
||||||
|
|
||||||
// 2. If none of the following are true
|
// 2. If none of the following are true
|
||||||
// - node is not target and options["subtree"] is false
|
// - node is not target and options["subtree"] is false
|
||||||
|
@ -1351,7 +1359,7 @@ void Node::queue_mutation_record(FlyString const& type, String attribute_name, S
|
||||||
&& !(type == MutationType::characterData && (!options.character_data.has_value() || !options.character_data.value()))
|
&& !(type == MutationType::characterData && (!options.character_data.has_value() || !options.character_data.value()))
|
||||||
&& !(type == MutationType::childList && !options.child_list)) {
|
&& !(type == MutationType::childList && !options.child_list)) {
|
||||||
// 1. Let mo be registered’s observer.
|
// 1. Let mo be registered’s observer.
|
||||||
auto mutation_observer = registered_observer.observer;
|
auto mutation_observer = registered_observer.observer();
|
||||||
|
|
||||||
// 2. If interestedObservers[mo] does not exist, then set interestedObservers[mo] to null.
|
// 2. If interestedObservers[mo] does not exist, then set interestedObservers[mo] to null.
|
||||||
if (!interested_observers.contains(mutation_observer))
|
if (!interested_observers.contains(mutation_observer))
|
||||||
|
|
|
@ -15,7 +15,6 @@
|
||||||
#include <LibWeb/Bindings/Wrappable.h>
|
#include <LibWeb/Bindings/Wrappable.h>
|
||||||
#include <LibWeb/DOM/EventTarget.h>
|
#include <LibWeb/DOM/EventTarget.h>
|
||||||
#include <LibWeb/DOM/ExceptionOr.h>
|
#include <LibWeb/DOM/ExceptionOr.h>
|
||||||
#include <LibWeb/DOM/MutationObserver.h>
|
|
||||||
|
|
||||||
namespace Web::DOM {
|
namespace Web::DOM {
|
||||||
|
|
||||||
|
@ -217,8 +216,8 @@ public:
|
||||||
|
|
||||||
size_t length() const;
|
size_t length() const;
|
||||||
|
|
||||||
NonnullRefPtrVector<RegisteredObserver>& registered_observers_list() { return m_registered_observer_list; }
|
auto& registered_observers_list() { return m_registered_observer_list; }
|
||||||
NonnullRefPtrVector<RegisteredObserver> const& registered_observers_list() const { return m_registered_observer_list; }
|
auto const& registered_observers_list() const { return m_registered_observer_list; }
|
||||||
|
|
||||||
void add_registered_observer(RegisteredObserver& registered_observer) { m_registered_observer_list.append(registered_observer); }
|
void add_registered_observer(RegisteredObserver& registered_observer) { m_registered_observer_list.append(registered_observer); }
|
||||||
|
|
||||||
|
@ -638,7 +637,7 @@ protected:
|
||||||
|
|
||||||
// https://dom.spec.whatwg.org/#registered-observer-list
|
// https://dom.spec.whatwg.org/#registered-observer-list
|
||||||
// "Nodes have a strong reference to registered observers in their registered observer list." https://dom.spec.whatwg.org/#garbage-collection
|
// "Nodes have a strong reference to registered observers in their registered observer list." https://dom.spec.whatwg.org/#garbage-collection
|
||||||
NonnullRefPtrVector<RegisteredObserver> m_registered_observer_list;
|
Vector<RegisteredObserver&> m_registered_observer_list;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void queue_tree_mutation_record(JS::NonnullGCPtr<NodeList> added_nodes, JS::NonnullGCPtr<NodeList> removed_nodes, Node* previous_sibling, Node* next_sibling);
|
void queue_tree_mutation_record(JS::NonnullGCPtr<NodeList> added_nodes, JS::NonnullGCPtr<NodeList> removed_nodes, Node* previous_sibling, Node* next_sibling);
|
||||||
|
|
|
@ -158,6 +158,7 @@ class ParentNode;
|
||||||
class Position;
|
class Position;
|
||||||
class ProcessingInstruction;
|
class ProcessingInstruction;
|
||||||
class Range;
|
class Range;
|
||||||
|
class RegisteredObserver;
|
||||||
class ShadowRoot;
|
class ShadowRoot;
|
||||||
class StaticNodeList;
|
class StaticNodeList;
|
||||||
class StaticRange;
|
class StaticRange;
|
||||||
|
@ -469,7 +470,6 @@ class ImageDataWrapper;
|
||||||
class IntersectionObserverWrapper;
|
class IntersectionObserverWrapper;
|
||||||
class LocationObject;
|
class LocationObject;
|
||||||
class MessageChannelWrapper;
|
class MessageChannelWrapper;
|
||||||
class MutationObserverWrapper;
|
|
||||||
class OptionConstructor;
|
class OptionConstructor;
|
||||||
class Path2DWrapper;
|
class Path2DWrapper;
|
||||||
class RangePrototype;
|
class RangePrototype;
|
||||||
|
|
|
@ -39,7 +39,7 @@ libweb_js_wrapper(DOM/Event NO_INSTANCE)
|
||||||
libweb_js_wrapper(DOM/EventTarget NO_INSTANCE)
|
libweb_js_wrapper(DOM/EventTarget NO_INSTANCE)
|
||||||
libweb_js_wrapper(DOM/HTMLCollection)
|
libweb_js_wrapper(DOM/HTMLCollection)
|
||||||
libweb_js_wrapper(DOM/MutationRecord NO_INSTANCE)
|
libweb_js_wrapper(DOM/MutationRecord NO_INSTANCE)
|
||||||
libweb_js_wrapper(DOM/MutationObserver)
|
libweb_js_wrapper(DOM/MutationObserver NO_INSTANCE)
|
||||||
libweb_js_wrapper(DOM/NamedNodeMap NO_INSTANCE)
|
libweb_js_wrapper(DOM/NamedNodeMap NO_INSTANCE)
|
||||||
libweb_js_wrapper(DOM/Node NO_INSTANCE)
|
libweb_js_wrapper(DOM/Node NO_INSTANCE)
|
||||||
libweb_js_wrapper(DOM/NodeIterator NO_INSTANCE)
|
libweb_js_wrapper(DOM/NodeIterator NO_INSTANCE)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue