MutationObserver.cpp 7.7 KB

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