PerformanceObserver.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241
  1. /*
  2. * Copyright (c) 2023, Luke Wilde <lukew@serenityos.org>
  3. *
  4. * SPDX-License-Identifier: BSD-2-Clause
  5. */
  6. #include <LibWeb/Bindings/Intrinsics.h>
  7. #include <LibWeb/Bindings/PerformanceObserverPrototype.h>
  8. #include <LibWeb/HTML/WindowOrWorkerGlobalScope.h>
  9. #include <LibWeb/HighResolutionTime/SupportedPerformanceTypes.h>
  10. #include <LibWeb/PerformanceTimeline/EntryTypes.h>
  11. #include <LibWeb/PerformanceTimeline/PerformanceEntry.h>
  12. #include <LibWeb/PerformanceTimeline/PerformanceObserver.h>
  13. #include <LibWeb/WebIDL/CallbackType.h>
  14. #include <LibWeb/WebIDL/ExceptionOr.h>
  15. namespace Web::PerformanceTimeline {
  16. JS_DEFINE_ALLOCATOR(PerformanceObserver);
  17. WebIDL::ExceptionOr<JS::NonnullGCPtr<PerformanceObserver>> PerformanceObserver::construct_impl(JS::Realm& realm, JS::GCPtr<WebIDL::CallbackType> callback)
  18. {
  19. return realm.heap().allocate<PerformanceObserver>(realm, realm, callback);
  20. }
  21. PerformanceObserver::PerformanceObserver(JS::Realm& realm, JS::GCPtr<WebIDL::CallbackType> callback)
  22. : Bindings::PlatformObject(realm)
  23. , m_callback(move(callback))
  24. {
  25. }
  26. PerformanceObserver::~PerformanceObserver() = default;
  27. void PerformanceObserver::initialize(JS::Realm& realm)
  28. {
  29. Base::initialize(realm);
  30. WEB_SET_PROTOTYPE_FOR_INTERFACE(PerformanceObserver);
  31. }
  32. void PerformanceObserver::visit_edges(Cell::Visitor& visitor)
  33. {
  34. Base::visit_edges(visitor);
  35. visitor.visit(m_callback);
  36. visitor.visit(m_observer_buffer);
  37. }
  38. // https://w3c.github.io/performance-timeline/#dom-performanceobserver-observe
  39. WebIDL::ExceptionOr<void> PerformanceObserver::observe(PerformanceObserverInit& options)
  40. {
  41. auto& realm = this->realm();
  42. // 1. Let relevantGlobal be this's relevant global object.
  43. auto* relevant_global = dynamic_cast<HTML::WindowOrWorkerGlobalScopeMixin*>(&HTML::relevant_global_object(*this));
  44. VERIFY(relevant_global);
  45. // 2. If options's entryTypes and type members are both omitted, then throw a "TypeError".
  46. if (!options.entry_types.has_value() && !options.type.has_value())
  47. return WebIDL::SimpleException { WebIDL::SimpleExceptionType::TypeError, "Must specify one of entryTypes or type"sv };
  48. // 3. If options's entryTypes is present and any other member is also present, then throw a "TypeError".
  49. if (options.entry_types.has_value() && (options.type.has_value() || options.buffered.has_value()))
  50. return WebIDL::SimpleException { WebIDL::SimpleExceptionType::TypeError, "Cannot specify type or buffered if entryTypes is specified"sv };
  51. // 4. Update or check this's observer type by running these steps:
  52. // 1. If this's observer type is "undefined":
  53. if (m_observer_type == ObserverType::Undefined) {
  54. // 1. If options's entryTypes member is present, then set this's observer type to "multiple".
  55. if (options.entry_types.has_value())
  56. m_observer_type = ObserverType::Multiple;
  57. // 2. If options's type member is present, then set this's observer type to "single".
  58. if (options.type.has_value())
  59. m_observer_type = ObserverType::Single;
  60. }
  61. // 2. If this's observer type is "single" and options's entryTypes member is present, then throw an "InvalidModificationError".
  62. else if (m_observer_type == ObserverType::Single) {
  63. if (options.entry_types.has_value())
  64. return WebIDL::InvalidModificationError::create(realm, "Cannot change a PerformanceObserver from observing a single type to observing multiple types"_fly_string);
  65. }
  66. // 3. If this's observer type is "multiple" and options's type member is present, then throw an "InvalidModificationError".
  67. else if (m_observer_type == ObserverType::Multiple) {
  68. if (options.type.has_value())
  69. return WebIDL::InvalidModificationError::create(realm, "Cannot change a PerformanceObserver from observing multiple types to observing a single type"_fly_string);
  70. }
  71. // 5. Set this's requires dropped entries to true.
  72. m_requires_dropped_entries = true;
  73. // 6. If this's observer type is "multiple", run the following steps:
  74. if (m_observer_type == ObserverType::Multiple) {
  75. // 1. Let entry types be options's entryTypes sequence.
  76. VERIFY(options.entry_types.has_value());
  77. auto& entry_types = options.entry_types.value();
  78. // 2. Remove all types from entry types that are not contained in relevantGlobal's frozen array of supported entry types.
  79. // The user agent SHOULD notify developers if entry types is modified. For example, a console warning listing removed
  80. // types might be appropriate.
  81. entry_types.remove_all_matching([](String const& type) {
  82. #define __ENUMERATE_SUPPORTED_PERFORMANCE_ENTRY_TYPES(entry_type, cpp_class) \
  83. if (entry_type == type) \
  84. return false;
  85. ENUMERATE_SUPPORTED_PERFORMANCE_ENTRY_TYPES
  86. #undef __ENUMERATE_SUPPORTED_PERFORMANCE_ENTRY_TYPES
  87. dbgln("Potential FIXME: Removing unsupported PerformanceEntry type '{}' from list of observed types in PerformanceObserver::observe()", type);
  88. return true;
  89. });
  90. // 3. If the resulting entry types sequence is an empty sequence, abort these steps.
  91. // The user agent SHOULD notify developers when the steps are aborted to notify that registration has been aborted.
  92. // For example, a console warning might be appropriate.
  93. if (entry_types.is_empty()) {
  94. dbgln("Potential FIXME: Returning from PerformanceObserver::observe() as we don't support any of the specified types (or none was specified).");
  95. return {};
  96. }
  97. // 4. If the list of registered performance observer objects of relevantGlobal contains a registered performance
  98. // observer whose observer is this, replace its options list with a list containing options as its only item.
  99. // 5. Otherwise, create and append a registered performance observer object to the list of registered performance
  100. // observer objects of relevantGlobal, with observer set to this and options list set to a list containing
  101. // options as its only item.
  102. // NOTE: See the comment on PerformanceObserver::options_list about why this doesn't create a separate registered
  103. // performance observer object.
  104. m_options_list.clear();
  105. m_options_list.append(options);
  106. relevant_global->register_performance_observer({}, *this);
  107. }
  108. // 7. Otherwise, run the following steps:
  109. else {
  110. // 1. Assert that this's observer type is "single".
  111. VERIFY(m_observer_type == ObserverType::Single);
  112. // 2. If options's type is not contained in the relevantGlobal's frozen array of supported entry types, abort these steps.
  113. // The user agent SHOULD notify developers when this happens, for instance via a console warning.
  114. VERIFY(options.type.has_value());
  115. auto& type = options.type.value();
  116. bool recognized_type = false;
  117. #define __ENUMERATE_SUPPORTED_PERFORMANCE_ENTRY_TYPES(entry_type, cpp_class) \
  118. if (!recognized_type && entry_type == type) \
  119. recognized_type = true;
  120. ENUMERATE_SUPPORTED_PERFORMANCE_ENTRY_TYPES
  121. #undef __ENUMERATE_SUPPORTED_PERFORMANCE_ENTRY_TYPES
  122. if (!recognized_type) {
  123. dbgln("Potential FIXME: Returning from PerformanceObserver::observe() as we don't support the PerformanceEntry type '{}'", type);
  124. return {};
  125. }
  126. // 3. If the list of registered performance observer objects of relevantGlobal contains a registered performance
  127. // observer obs whose observer is this:
  128. if (relevant_global->has_registered_performance_observer(*this)) {
  129. // 1. If obs's options list contains a PerformanceObserverInit item currentOptions whose type is equal to options's type,
  130. // replace currentOptions with options in obs's options list.
  131. auto index = m_options_list.find_first_index_if([&options](PerformanceObserverInit const& entry) {
  132. return entry.type == options.type;
  133. });
  134. if (index.has_value()) {
  135. m_options_list[index.value()] = options;
  136. } else {
  137. // Otherwise, append options to obs's options list.
  138. m_options_list.append(options);
  139. }
  140. }
  141. // 4. Otherwise, create and append a registered performance observer object to the list of registered performance
  142. // observer objects of relevantGlobal, with observer set to the this and options list set to a list containing
  143. // options as its only item.
  144. else {
  145. m_options_list.clear();
  146. m_options_list.append(options);
  147. relevant_global->register_performance_observer({}, *this);
  148. }
  149. // 5. If options's buffered flag is set:
  150. if (options.buffered.has_value() && options.buffered.value()) {
  151. // 1. Let tuple be the relevant performance entry tuple of options's type and relevantGlobal.
  152. auto const& tuple = relevant_global->relevant_performance_entry_tuple(type);
  153. // 2. For each entry in tuple's performance entry buffer:
  154. for (auto const& entry : tuple.performance_entry_buffer) {
  155. // 1. If should add entry with entry and options as parameters returns true, append entry to the observer buffer.
  156. if (entry->should_add_entry(options) == ShouldAddEntry::Yes)
  157. m_observer_buffer.append(*entry);
  158. }
  159. // 3. Queue the PerformanceObserver task with relevantGlobal as input.
  160. relevant_global->queue_the_performance_observer_task();
  161. }
  162. }
  163. return {};
  164. }
  165. // https://w3c.github.io/performance-timeline/#dom-performanceobserver-disconnect
  166. void PerformanceObserver::disconnect()
  167. {
  168. // 1. Remove this from the list of registered performance observer objects of relevant global object.
  169. auto* relevant_global = dynamic_cast<HTML::WindowOrWorkerGlobalScopeMixin*>(&HTML::relevant_global_object(*this));
  170. VERIFY(relevant_global);
  171. relevant_global->unregister_performance_observer({}, *this);
  172. // 2. Empty this's observer buffer.
  173. m_observer_buffer.clear();
  174. // 3. Empty this's options list.
  175. m_options_list.clear();
  176. }
  177. // https://w3c.github.io/performance-timeline/#dom-performanceobserver-takerecords
  178. Vector<JS::Handle<PerformanceTimeline::PerformanceEntry>> PerformanceObserver::take_records()
  179. {
  180. // The takeRecords() method must return a copy of this's observer buffer, and also empty this's observer buffer.
  181. Vector<JS::Handle<PerformanceTimeline::PerformanceEntry>> records;
  182. for (auto& record : m_observer_buffer)
  183. records.append(*record);
  184. m_observer_buffer.clear();
  185. return records;
  186. }
  187. // https://w3c.github.io/performance-timeline/#dom-performanceobserver-supportedentrytypes
  188. JS::NonnullGCPtr<JS::Object> PerformanceObserver::supported_entry_types(JS::VM& vm)
  189. {
  190. // 1. Let globalObject be the environment settings object's global object.
  191. auto* window_or_worker = dynamic_cast<HTML::WindowOrWorkerGlobalScopeMixin*>(&vm.get_global_object());
  192. VERIFY(window_or_worker);
  193. // 2. Return globalObject's frozen array of supported entry types.
  194. return window_or_worker->supported_entry_types();
  195. }
  196. void PerformanceObserver::unset_requires_dropped_entries(Badge<HTML::WindowOrWorkerGlobalScopeMixin>)
  197. {
  198. m_requires_dropped_entries = false;
  199. }
  200. void PerformanceObserver::append_to_observer_buffer(Badge<HTML::WindowOrWorkerGlobalScopeMixin>, JS::NonnullGCPtr<PerformanceTimeline::PerformanceEntry> entry)
  201. {
  202. m_observer_buffer.append(entry);
  203. }
  204. }