mirror of
https://github.com/RGBCube/serenity
synced 2025-05-31 15:38:10 +00:00
LibWeb: Implement PerformanceObserver
This commit is contained in:
parent
5055883b9f
commit
af2886449a
22 changed files with 793 additions and 57 deletions
|
@ -23,8 +23,11 @@
|
|||
#include <LibWeb/HTML/Timer.h>
|
||||
#include <LibWeb/HTML/Window.h>
|
||||
#include <LibWeb/HTML/WindowOrWorkerGlobalScope.h>
|
||||
#include <LibWeb/HighResolutionTime/SupportedPerformanceTypes.h>
|
||||
#include <LibWeb/Infra/Base64.h>
|
||||
#include <LibWeb/PerformanceTimeline/EntryTypes.h>
|
||||
#include <LibWeb/PerformanceTimeline/PerformanceObserver.h>
|
||||
#include <LibWeb/PerformanceTimeline/PerformanceObserverEntryList.h>
|
||||
#include <LibWeb/UserTiming/PerformanceMark.h>
|
||||
#include <LibWeb/UserTiming/PerformanceMeasure.h>
|
||||
#include <LibWeb/WebIDL/AbstractOperations.h>
|
||||
|
@ -35,11 +38,6 @@ namespace Web::HTML {
|
|||
|
||||
WindowOrWorkerGlobalScopeMixin::~WindowOrWorkerGlobalScopeMixin() = default;
|
||||
|
||||
// Please keep these in alphabetical order based on the entry type :^)
|
||||
#define ENUMERATE_SUPPORTED_PERFORMANCE_ENTRY_TYPES \
|
||||
__ENUMERATE_SUPPORTED_PERFORMANCE_ENTRY_TYPES(PerformanceTimeline::EntryTypes::mark, UserTiming::PerformanceMark) \
|
||||
__ENUMERATE_SUPPORTED_PERFORMANCE_ENTRY_TYPES(PerformanceTimeline::EntryTypes::measure, UserTiming::PerformanceMeasure)
|
||||
|
||||
void WindowOrWorkerGlobalScopeMixin::initialize(JS::Realm&)
|
||||
{
|
||||
#define __ENUMERATE_SUPPORTED_PERFORMANCE_ENTRY_TYPES(entry_type, cpp_class) \
|
||||
|
@ -58,6 +56,10 @@ void WindowOrWorkerGlobalScopeMixin::visit_edges(JS::Cell::Visitor& visitor)
|
|||
{
|
||||
for (auto& it : m_timers)
|
||||
visitor.visit(it.value);
|
||||
for (auto& observer : m_registered_performance_observer_objects)
|
||||
visitor.visit(observer);
|
||||
for (auto& entry : m_performance_entry_buffer_map)
|
||||
entry.value.visit_edges(visitor);
|
||||
}
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/webappapis.html#dom-origin
|
||||
|
@ -301,11 +303,10 @@ PerformanceTimeline::PerformanceEntryTuple& WindowOrWorkerGlobalScopeMixin::rele
|
|||
}
|
||||
|
||||
// https://www.w3.org/TR/performance-timeline/#dfn-queue-a-performanceentry
|
||||
WebIDL::ExceptionOr<void> WindowOrWorkerGlobalScopeMixin::queue_performance_entry(JS::NonnullGCPtr<PerformanceTimeline::PerformanceEntry> new_entry)
|
||||
void WindowOrWorkerGlobalScopeMixin::queue_performance_entry(JS::NonnullGCPtr<PerformanceTimeline::PerformanceEntry> new_entry)
|
||||
{
|
||||
auto& vm = new_entry->vm();
|
||||
|
||||
// FIXME: 1. Let interested observers be an initially empty set of PerformanceObserver objects.
|
||||
// 1. Let interested observers be an initially empty set of PerformanceObserver objects.
|
||||
Vector<JS::Handle<PerformanceTimeline::PerformanceObserver>> interested_observers;
|
||||
|
||||
// 2. Let entryType be newEntry’s entryType value.
|
||||
auto const& entry_type = new_entry->entry_type();
|
||||
|
@ -313,14 +314,31 @@ WebIDL::ExceptionOr<void> WindowOrWorkerGlobalScopeMixin::queue_performance_entr
|
|||
// 3. Let relevantGlobal be newEntry's relevant global object.
|
||||
// NOTE: Already is `this`.
|
||||
|
||||
// FIXME: 4. For each registered performance observer regObs in relevantGlobal's list of registered performance observer
|
||||
// objects:
|
||||
// 1. If regObs's options list contains a PerformanceObserverInit options whose entryTypes member includes entryType
|
||||
// or whose type member equals to entryType:
|
||||
// 1. If should add entry with newEntry and options returns true, append regObs's observer to interested observers.
|
||||
// 4. For each registered performance observer regObs in relevantGlobal's list of registered performance observer
|
||||
// objects:
|
||||
for (auto const& registered_observer : m_registered_performance_observer_objects) {
|
||||
// 1. If regObs's options list contains a PerformanceObserverInit options whose entryTypes member includes entryType
|
||||
// or whose type member equals to entryType:
|
||||
auto iterator = registered_observer->options_list().find_if([&entry_type](PerformanceTimeline::PerformanceObserverInit const& entry) {
|
||||
if (entry.entry_types.has_value())
|
||||
return entry.entry_types->contains_slow(String::from_utf8(entry_type).release_value_but_fixme_should_propagate_errors());
|
||||
|
||||
// FIXME: 5. For each observer in interested observers:
|
||||
// 1. Append newEntry to observer's observer buffer.
|
||||
VERIFY(entry.type.has_value());
|
||||
return entry.type.value() == entry_type;
|
||||
});
|
||||
|
||||
if (!iterator.is_end()) {
|
||||
// 1. If should add entry with newEntry and options returns true, append regObs's observer to interested observers.
|
||||
if (new_entry->should_add_entry(*iterator) == PerformanceTimeline::ShouldAddEntry::Yes)
|
||||
interested_observers.append(registered_observer);
|
||||
}
|
||||
}
|
||||
|
||||
// 5. For each observer in interested observers:
|
||||
for (auto const& observer : interested_observers) {
|
||||
// 1. Append newEntry to observer's observer buffer.
|
||||
observer->append_to_observer_buffer({}, new_entry);
|
||||
}
|
||||
|
||||
// 6. Let tuple be the relevant performance entry tuple of entryType and relevantGlobal.
|
||||
auto& tuple = relevant_performance_entry_tuple(entry_type);
|
||||
|
@ -334,10 +352,10 @@ WebIDL::ExceptionOr<void> WindowOrWorkerGlobalScopeMixin::queue_performance_entr
|
|||
|
||||
// 9. If isBufferFull is false and shouldAdd is true, append newEntry to tuple's performance entry buffer.
|
||||
if (!is_buffer_full && should_add == PerformanceTimeline::ShouldAddEntry::Yes)
|
||||
TRY_OR_THROW_OOM(vm, tuple.performance_entry_buffer.try_append(JS::make_handle(new_entry)));
|
||||
tuple.performance_entry_buffer.append(new_entry);
|
||||
|
||||
// FIXME: 10. Queue the PerformanceObserver task with relevantGlobal as input.
|
||||
return {};
|
||||
// 10. Queue the PerformanceObserver task with relevantGlobal as input.
|
||||
queue_the_performance_observer_task();
|
||||
}
|
||||
|
||||
void WindowOrWorkerGlobalScopeMixin::clear_performance_entry_buffer(Badge<HighResolutionTime::Performance>, FlyString const& entry_type)
|
||||
|
@ -354,35 +372,6 @@ void WindowOrWorkerGlobalScopeMixin::remove_entries_from_performance_entry_buffe
|
|||
});
|
||||
}
|
||||
|
||||
// https://www.w3.org/TR/performance-timeline/#dfn-filter-buffer-by-name-and-type
|
||||
static ErrorOr<Vector<JS::Handle<PerformanceTimeline::PerformanceEntry>>> filter_buffer_by_name_and_type(Vector<JS::Handle<PerformanceTimeline::PerformanceEntry>> const& buffer, Optional<String> name, Optional<String> type)
|
||||
{
|
||||
// 1. Let result be an initially empty list.
|
||||
Vector<JS::Handle<PerformanceTimeline::PerformanceEntry>> result;
|
||||
|
||||
// 2. For each PerformanceEntry entry in buffer, run the following steps:
|
||||
for (auto const& entry : buffer) {
|
||||
// 1. If type is not null and if type is not identical to entry's entryType attribute, continue to next entry.
|
||||
if (type.has_value() && type.value() != entry->entry_type())
|
||||
continue;
|
||||
|
||||
// 2. If name is not null and if name is not identical to entry's name attribute, continue to next entry.
|
||||
if (name.has_value() && name.value() != entry->name())
|
||||
continue;
|
||||
|
||||
// 3. append entry to result.
|
||||
TRY(result.try_append(entry));
|
||||
}
|
||||
|
||||
// 3. Sort results's entries in chronological order with respect to startTime
|
||||
quick_sort(result, [](auto const& left_entry, auto const& right_entry) {
|
||||
return left_entry->start_time() < right_entry->start_time();
|
||||
});
|
||||
|
||||
// 4. Return result.
|
||||
return result;
|
||||
}
|
||||
|
||||
// https://www.w3.org/TR/performance-timeline/#dfn-filter-buffer-map-by-name-and-type
|
||||
ErrorOr<Vector<JS::Handle<PerformanceTimeline::PerformanceEntry>>> WindowOrWorkerGlobalScopeMixin::filter_buffer_map_by_name_and_type(Optional<String> name, Optional<String> type) const
|
||||
{
|
||||
|
@ -431,4 +420,111 @@ ErrorOr<Vector<JS::Handle<PerformanceTimeline::PerformanceEntry>>> WindowOrWorke
|
|||
return result;
|
||||
}
|
||||
|
||||
void WindowOrWorkerGlobalScopeMixin::register_performance_observer(Badge<PerformanceTimeline::PerformanceObserver>, JS::NonnullGCPtr<PerformanceTimeline::PerformanceObserver> observer)
|
||||
{
|
||||
m_registered_performance_observer_objects.set(observer, AK::HashSetExistingEntryBehavior::Keep);
|
||||
}
|
||||
|
||||
void WindowOrWorkerGlobalScopeMixin::unregister_performance_observer(Badge<PerformanceTimeline::PerformanceObserver>, JS::NonnullGCPtr<PerformanceTimeline::PerformanceObserver> observer)
|
||||
{
|
||||
m_registered_performance_observer_objects.remove(observer);
|
||||
}
|
||||
|
||||
bool WindowOrWorkerGlobalScopeMixin::has_registered_performance_observer(JS::NonnullGCPtr<PerformanceTimeline::PerformanceObserver> observer)
|
||||
{
|
||||
return m_registered_performance_observer_objects.contains(observer);
|
||||
}
|
||||
|
||||
// https://w3c.github.io/performance-timeline/#dfn-queue-the-performanceobserver-task
|
||||
void WindowOrWorkerGlobalScopeMixin::queue_the_performance_observer_task()
|
||||
{
|
||||
// 1. If relevantGlobal's performance observer task queued flag is set, terminate these steps.
|
||||
if (m_performance_observer_task_queued)
|
||||
return;
|
||||
|
||||
// 2. Set relevantGlobal's performance observer task queued flag.
|
||||
m_performance_observer_task_queued = true;
|
||||
|
||||
// 3. Queue a task that consists of running the following substeps. The task source for the queued task is the performance
|
||||
// timeline task source.
|
||||
queue_global_task(Task::Source::PerformanceTimeline, this_impl(), [this]() {
|
||||
auto& realm = this_impl().realm();
|
||||
|
||||
// 1. Unset performance observer task queued flag of relevantGlobal.
|
||||
m_performance_observer_task_queued = false;
|
||||
|
||||
// 2. Let notifyList be a copy of relevantGlobal's list of registered performance observer objects.
|
||||
auto notify_list = m_registered_performance_observer_objects;
|
||||
|
||||
// 3. For each registered performance observer object registeredObserver in notifyList, run these steps:
|
||||
for (auto& registered_observer : notify_list) {
|
||||
// 1. Let po be registeredObserver's observer.
|
||||
// 2. Let entries be a copy of po’s observer buffer.
|
||||
// 4. Empty po’s observer buffer.
|
||||
auto entries = registered_observer->take_records();
|
||||
|
||||
// 3. If entries is empty, return.
|
||||
// FIXME: Do they mean `continue`?
|
||||
if (entries.is_empty())
|
||||
continue;
|
||||
|
||||
Vector<JS::NonnullGCPtr<PerformanceTimeline::PerformanceEntry>> entries_as_gc_ptrs;
|
||||
for (auto& entry : entries)
|
||||
entries_as_gc_ptrs.append(*entry);
|
||||
|
||||
// 5. Let observerEntryList be a new PerformanceObserverEntryList, with its entry list set to entries.
|
||||
auto observer_entry_list = realm.heap().allocate<PerformanceTimeline::PerformanceObserverEntryList>(realm, realm, move(entries_as_gc_ptrs));
|
||||
|
||||
// 6. Let droppedEntriesCount be null.
|
||||
Optional<u64> dropped_entries_count;
|
||||
|
||||
// 7. If po's requires dropped entries is set, perform the following steps:
|
||||
if (registered_observer->requires_dropped_entries()) {
|
||||
// 1. Set droppedEntriesCount to 0.
|
||||
dropped_entries_count = 0;
|
||||
|
||||
// 2. For each PerformanceObserverInit item in registeredObserver's options list:
|
||||
for (auto const& item : registered_observer->options_list()) {
|
||||
// 1. For each DOMString entryType that appears either as item's type or in item's entryTypes:
|
||||
auto increment_dropped_entries_count = [this, &dropped_entries_count](FlyString const& type) {
|
||||
// 1. Let map be relevantGlobal's performance entry buffer map.
|
||||
auto const& map = m_performance_entry_buffer_map;
|
||||
|
||||
// 2. Let tuple be the result of getting the value of entry on map given entryType as key.
|
||||
auto const& tuple = map.get(type);
|
||||
VERIFY(tuple.has_value());
|
||||
|
||||
// 3. Increase droppedEntriesCount by tuple's dropped entries count.
|
||||
dropped_entries_count.value() += tuple->dropped_entries_count;
|
||||
};
|
||||
|
||||
if (item.type.has_value()) {
|
||||
increment_dropped_entries_count(item.type.value());
|
||||
} else {
|
||||
VERIFY(item.entry_types.has_value());
|
||||
for (auto const& type : item.entry_types.value())
|
||||
increment_dropped_entries_count(type);
|
||||
}
|
||||
}
|
||||
|
||||
// 3. Set po's requires dropped entries to false.
|
||||
registered_observer->unset_requires_dropped_entries({});
|
||||
}
|
||||
|
||||
// 8. Let callbackOptions be a PerformanceObserverCallbackOptions with its droppedEntriesCount set to
|
||||
// droppedEntriesCount if droppedEntriesCount is not null, otherwise unset.
|
||||
auto callback_options = JS::Object::create(realm, realm.intrinsics().object_prototype());
|
||||
if (dropped_entries_count.has_value())
|
||||
MUST(callback_options->create_data_property("droppedEntriesCount", JS::Value(dropped_entries_count.value())));
|
||||
|
||||
// 9. Call po’s observer callback with observerEntryList as the first argument, with po as the second
|
||||
// argument and as callback this value, and with callbackOptions as the third argument.
|
||||
// If this throws an exception, report the exception.
|
||||
auto completion = WebIDL::invoke_callback(registered_observer->callback(), registered_observer, observer_entry_list, registered_observer, callback_options);
|
||||
if (completion.is_abrupt())
|
||||
HTML::report_exception(completion, realm);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue