MutationObserver.cpp 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201
  1. /*
  2. * Copyright (c) 2022, Luke Wilde <lukew@serenityos.org>
  3. *
  4. * SPDX-License-Identifier: BSD-2-Clause
  5. */
  6. #include <LibWeb/Bindings/Intrinsics.h>
  7. #include <LibWeb/Bindings/MainThreadVM.h>
  8. #include <LibWeb/Bindings/MutationObserverPrototype.h>
  9. #include <LibWeb/DOM/MutationObserver.h>
  10. #include <LibWeb/DOM/Node.h>
  11. namespace Web::DOM {
  12. JS_DEFINE_ALLOCATOR(MutationObserver);
  13. JS_DEFINE_ALLOCATOR(TransientRegisteredObserver);
  14. WebIDL::ExceptionOr<JS::NonnullGCPtr<MutationObserver>> MutationObserver::construct_impl(JS::Realm& realm, JS::GCPtr<WebIDL::CallbackType> callback)
  15. {
  16. return realm.create<MutationObserver>(realm, callback);
  17. }
  18. // https://dom.spec.whatwg.org/#dom-mutationobserver-mutationobserver
  19. MutationObserver::MutationObserver(JS::Realm& realm, JS::GCPtr<WebIDL::CallbackType> callback)
  20. : PlatformObject(realm)
  21. , m_callback(move(callback))
  22. {
  23. // 1. Set this’s callback to callback.
  24. // 2. Append this to this’s relevant agent’s mutation observers.
  25. auto* agent_custom_data = verify_cast<Bindings::WebEngineCustomData>(realm.vm().custom_data());
  26. agent_custom_data->mutation_observers.append(*this);
  27. }
  28. MutationObserver::~MutationObserver()
  29. {
  30. auto* agent_custom_data = verify_cast<Bindings::WebEngineCustomData>(vm().custom_data());
  31. agent_custom_data->mutation_observers.remove_all_matching([this](auto& observer) {
  32. return observer.ptr() == this;
  33. });
  34. }
  35. void MutationObserver::initialize(JS::Realm& realm)
  36. {
  37. Base::initialize(realm);
  38. WEB_SET_PROTOTYPE_FOR_INTERFACE(MutationObserver);
  39. }
  40. void MutationObserver::visit_edges(Cell::Visitor& visitor)
  41. {
  42. Base::visit_edges(visitor);
  43. visitor.visit(m_callback);
  44. visitor.visit(m_record_queue);
  45. }
  46. // https://dom.spec.whatwg.org/#dom-mutationobserver-observe
  47. WebIDL::ExceptionOr<void> MutationObserver::observe(Node& target, MutationObserverInit options)
  48. {
  49. // 1. If either options["attributeOldValue"] or options["attributeFilter"] exists, and options["attributes"] does not exist, then set options["attributes"] to true.
  50. if ((options.attribute_old_value.has_value() || options.attribute_filter.has_value()) && !options.attributes.has_value())
  51. options.attributes = true;
  52. // 2. If options["characterDataOldValue"] exists and options["characterData"] does not exist, then set options["characterData"] to true.
  53. if (options.character_data_old_value.has_value() && !options.character_data.has_value())
  54. options.character_data = true;
  55. // 3. If none of options["childList"], options["attributes"], and options["characterData"] is true, then throw a TypeError.
  56. if (!options.child_list && (!options.attributes.has_value() || !options.attributes.value()) && (!options.character_data.has_value() || !options.character_data.value()))
  57. return WebIDL::SimpleException { WebIDL::SimpleExceptionType::TypeError, "Options must have one of childList, attributes or characterData set to true."sv };
  58. // 4. If options["attributeOldValue"] is true and options["attributes"] is false, then throw a TypeError.
  59. // NOTE: If attributeOldValue is present, attributes will be present because of step 1.
  60. if (options.attribute_old_value.has_value() && options.attribute_old_value.value() && !options.attributes.value())
  61. return WebIDL::SimpleException { WebIDL::SimpleExceptionType::TypeError, "attributes must be true if attributeOldValue is true."sv };
  62. // 5. If options["attributeFilter"] is present and options["attributes"] is false, then throw a TypeError.
  63. // NOTE: If attributeFilter is present, attributes will be present because of step 1.
  64. if (options.attribute_filter.has_value() && !options.attributes.value())
  65. return WebIDL::SimpleException { WebIDL::SimpleExceptionType::TypeError, "attributes must be true if attributeFilter is present."sv };
  66. // 6. If options["characterDataOldValue"] is true and options["characterData"] is false, then throw a TypeError.
  67. // NOTE: If characterDataOldValue is present, characterData will be present because of step 2.
  68. if (options.character_data_old_value.has_value() && options.character_data_old_value.value() && !options.character_data.value())
  69. return WebIDL::SimpleException { WebIDL::SimpleExceptionType::TypeError, "characterData must be true if characterDataOldValue is true."sv };
  70. // 7. For each registered of target’s registered observer list, if registered’s observer is this:
  71. bool updated_existing_observer = false;
  72. if (target.registered_observer_list()) {
  73. for (auto& registered_observer : *target.registered_observer_list()) {
  74. if (registered_observer->observer().ptr() != this)
  75. continue;
  76. updated_existing_observer = true;
  77. // 1. For each node of this’s node list, remove all transient registered observers whose source is registered from node’s registered observer list.
  78. for (auto& node : m_node_list) {
  79. // FIXME: Is this correct?
  80. if (node.is_null())
  81. continue;
  82. if (node->registered_observer_list()) {
  83. node->registered_observer_list()->remove_all_matching([&registered_observer](RegisteredObserver& observer) {
  84. return is<TransientRegisteredObserver>(observer) && verify_cast<TransientRegisteredObserver>(observer).source().ptr() == registered_observer;
  85. });
  86. }
  87. }
  88. // 2. Set registered’s options to options.
  89. registered_observer->set_options(options);
  90. break;
  91. }
  92. }
  93. // 8. Otherwise:
  94. if (!updated_existing_observer) {
  95. // 1. Append a new registered observer whose observer is this and options is options to target’s registered observer list.
  96. auto new_registered_observer = RegisteredObserver::create(*this, options);
  97. target.add_registered_observer(new_registered_observer);
  98. // 2. Append target to this’s node list.
  99. m_node_list.append(target.make_weak_ptr<Node>());
  100. }
  101. return {};
  102. }
  103. // https://dom.spec.whatwg.org/#dom-mutationobserver-disconnect
  104. void MutationObserver::disconnect()
  105. {
  106. // 1. For each node of this’s node list, remove any registered observer from node’s registered observer list for which this is the observer.
  107. for (auto& node : m_node_list) {
  108. // FIXME: Is this correct?
  109. if (node.is_null())
  110. continue;
  111. if (node->registered_observer_list()) {
  112. node->registered_observer_list()->remove_all_matching([this](RegisteredObserver& registered_observer) {
  113. return registered_observer.observer().ptr() == this;
  114. });
  115. }
  116. }
  117. // 2. Empty this’s record queue.
  118. m_record_queue.clear();
  119. }
  120. // https://dom.spec.whatwg.org/#dom-mutationobserver-takerecords
  121. Vector<JS::Handle<MutationRecord>> MutationObserver::take_records()
  122. {
  123. // 1. Let records be a clone of this’s record queue.
  124. Vector<JS::Handle<MutationRecord>> records;
  125. for (auto& record : m_record_queue)
  126. records.append(*record);
  127. // 2. Empty this’s record queue.
  128. m_record_queue.clear();
  129. // 3. Return records.
  130. return records;
  131. }
  132. JS::NonnullGCPtr<RegisteredObserver> RegisteredObserver::create(MutationObserver& observer, MutationObserverInit const& options)
  133. {
  134. return observer.heap().allocate<RegisteredObserver>(observer, options);
  135. }
  136. RegisteredObserver::RegisteredObserver(MutationObserver& observer, MutationObserverInit const& options)
  137. : m_observer(observer)
  138. , m_options(options)
  139. {
  140. }
  141. RegisteredObserver::~RegisteredObserver() = default;
  142. void RegisteredObserver::visit_edges(Cell::Visitor& visitor)
  143. {
  144. Base::visit_edges(visitor);
  145. visitor.visit(m_observer);
  146. }
  147. JS::NonnullGCPtr<TransientRegisteredObserver> TransientRegisteredObserver::create(MutationObserver& observer, MutationObserverInit const& options, RegisteredObserver& source)
  148. {
  149. return observer.heap().allocate<TransientRegisteredObserver>(observer, options, source);
  150. }
  151. TransientRegisteredObserver::TransientRegisteredObserver(MutationObserver& observer, MutationObserverInit const& options, RegisteredObserver& source)
  152. : RegisteredObserver(observer, options)
  153. , m_source(source)
  154. {
  155. }
  156. TransientRegisteredObserver::~TransientRegisteredObserver() = default;
  157. void TransientRegisteredObserver::visit_edges(Cell::Visitor& visitor)
  158. {
  159. Base::visit_edges(visitor);
  160. visitor.visit(m_source);
  161. }
  162. }