mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-11-22 07:30:19 +00:00
LibWeb: Implement PerformanceObserver
This commit is contained in:
parent
5055883b9f
commit
af2886449a
Notes:
sideshowbarker
2024-07-16 23:05:02 +09:00
Author: https://github.com/Lubrsi Commit: https://github.com/SerenityOS/serenity/commit/af2886449a Pull-request: https://github.com/SerenityOS/serenity/pull/20760 Reviewed-by: https://github.com/kalenikaliaksandr
22 changed files with 793 additions and 57 deletions
|
@ -4,5 +4,7 @@ source_set("PerformanceTimeline") {
|
|||
sources = [
|
||||
"EntryTypes.cpp",
|
||||
"PerformanceEntry.cpp",
|
||||
"PerformanceObserver.cpp",
|
||||
"PerformanceObserverEntryList.cpp",
|
||||
]
|
||||
}
|
||||
|
|
|
@ -212,6 +212,8 @@ standard_idl_files = [
|
|||
"//Userland/Libraries/LibWeb/MathML/MathMLElement.idl",
|
||||
"//Userland/Libraries/LibWeb/NavigationTiming/PerformanceTiming.idl",
|
||||
"//Userland/Libraries/LibWeb/PerformanceTimeline/PerformanceEntry.idl",
|
||||
"//Userland/Libraries/LibWeb/PerformanceTimeline/PerformanceObserver.idl",
|
||||
"//Userland/Libraries/LibWeb/PerformanceTimeline/PerformanceObserverEntryList.idl",
|
||||
"//Userland/Libraries/LibWeb/RequestIdleCallback/IdleDeadline.idl",
|
||||
"//Userland/Libraries/LibWeb/ResizeObserver/ResizeObserver.idl",
|
||||
"//Userland/Libraries/LibWeb/Streams/ByteLengthQueuingStrategy.idl",
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
TypeError: Must specify one of entryTypes or type
|
||||
TypeError: Cannot specify type or buffered if entryTypes is specified
|
||||
TypeError: Cannot specify type or buffered if entryTypes is specified
|
||||
InvalidModificationError: Cannot change a PerformanceObserver from observing multiple types to observing a single type
|
||||
observer === globalObserver: true
|
||||
list instanceof PerformanceObserverEntryList: true
|
||||
allEntries instanceof Array: true
|
||||
allEntries.length === 3: true
|
||||
allEntries[0] === startMark: true
|
||||
allEntries[1] === endMark: true
|
||||
allEntries[2] === measureMark: true
|
||||
markEntries instanceof Array: true
|
||||
markEntries.length === 2: true
|
||||
markEntries[0] === startMark: true
|
||||
markEntries[1] === endMark: true
|
||||
measureEntries instanceof Array: true
|
||||
measureEntries.length === 1: true
|
||||
measureEntries[0] === measureMark: true
|
||||
startEntries instanceof Array: true
|
||||
startEntries.length === 1: true
|
||||
startEntries[0] === startMark: true
|
||||
endEntries instanceof Array: true
|
||||
endEntries.length === 1: true
|
||||
endEntries[0] === endMark: true
|
||||
measureEntriesByName instanceof Array: true
|
||||
measureEntriesByName.length === 1: true
|
||||
measureEntriesByName[0] === measureMark: true
|
||||
records.length === 0: true
|
|
@ -0,0 +1,98 @@
|
|||
<script src="../include.js"></script>
|
||||
<script>
|
||||
const bufferedMessages = [];
|
||||
function printlnBuffered(message) {
|
||||
bufferedMessages.push(message);
|
||||
}
|
||||
|
||||
const globalObserver = new PerformanceObserver((list, observer) => {
|
||||
printlnBuffered(`observer === globalObserver: ${observer === globalObserver}`);
|
||||
printlnBuffered(
|
||||
`list instanceof PerformanceObserverEntryList: ${
|
||||
list instanceof PerformanceObserverEntryList
|
||||
}`
|
||||
);
|
||||
|
||||
const allEntries = list.getEntries();
|
||||
printlnBuffered(`allEntries instanceof Array: ${allEntries instanceof Array}`);
|
||||
printlnBuffered(`allEntries.length === 3: ${allEntries.length === 3}`);
|
||||
printlnBuffered(`allEntries[0] === startMark: ${allEntries[0] === startMark}`);
|
||||
printlnBuffered(`allEntries[1] === endMark: ${allEntries[1] === endMark}`);
|
||||
printlnBuffered(`allEntries[2] === measureMark: ${allEntries[2] === measureMark}`);
|
||||
|
||||
const markEntries = list.getEntriesByType("mark");
|
||||
printlnBuffered(`markEntries instanceof Array: ${markEntries instanceof Array}`);
|
||||
printlnBuffered(`markEntries.length === 2: ${markEntries.length === 2}`);
|
||||
printlnBuffered(`markEntries[0] === startMark: ${markEntries[0] === startMark}`);
|
||||
printlnBuffered(`markEntries[1] === endMark: ${markEntries[1] === endMark}`);
|
||||
|
||||
const measureEntries = list.getEntriesByType("measure");
|
||||
printlnBuffered(`measureEntries instanceof Array: ${measureEntries instanceof Array}`);
|
||||
printlnBuffered(`measureEntries.length === 1: ${measureEntries.length === 1}`);
|
||||
printlnBuffered(`measureEntries[0] === measureMark: ${measureEntries[0] === measureMark}`);
|
||||
|
||||
const startEntries = list.getEntriesByName("start");
|
||||
printlnBuffered(`startEntries instanceof Array: ${startEntries instanceof Array}`);
|
||||
printlnBuffered(`startEntries.length === 1: ${startEntries.length === 1}`);
|
||||
printlnBuffered(`startEntries[0] === startMark: ${startEntries[0] === startMark}`);
|
||||
|
||||
const endEntries = list.getEntriesByName("end");
|
||||
printlnBuffered(`endEntries instanceof Array: ${endEntries instanceof Array}`);
|
||||
printlnBuffered(`endEntries.length === 1: ${endEntries.length === 1}`);
|
||||
printlnBuffered(`endEntries[0] === endMark: ${endEntries[0] === endMark}`);
|
||||
|
||||
const measureEntriesByName = list.getEntriesByName("measure");
|
||||
printlnBuffered(
|
||||
`measureEntriesByName instanceof Array: ${measureEntriesByName instanceof Array}`
|
||||
);
|
||||
printlnBuffered(`measureEntriesByName.length === 1: ${measureEntriesByName.length === 1}`);
|
||||
printlnBuffered(
|
||||
`measureEntriesByName[0] === measureMark: ${measureEntriesByName[0] === measureMark}`
|
||||
);
|
||||
});
|
||||
globalObserver.observe({ entryTypes: ["measure", "mark"] });
|
||||
|
||||
const startMark = performance.mark("start");
|
||||
const endMark = performance.mark("end");
|
||||
const measureMark = performance.measure("measure", "start", "end");
|
||||
|
||||
function printCatchedException(func) {
|
||||
try {
|
||||
func();
|
||||
} catch (e) {
|
||||
if (e instanceof DOMException) {
|
||||
printlnBuffered(`${e.name}: ${e.message}`);
|
||||
} else {
|
||||
printlnBuffered(e.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
printCatchedException(() => {
|
||||
globalObserver.observe();
|
||||
});
|
||||
|
||||
printCatchedException(() => {
|
||||
globalObserver.observe({ entryTypes: [], buffered: true });
|
||||
});
|
||||
|
||||
printCatchedException(() => {
|
||||
globalObserver.observe({ entryTypes: [], type: "" });
|
||||
});
|
||||
|
||||
printCatchedException(() => {
|
||||
globalObserver.observe({ type: "" });
|
||||
});
|
||||
|
||||
test(() => {
|
||||
for (const message of bufferedMessages) {
|
||||
println(message);
|
||||
}
|
||||
|
||||
globalObserver.disconnect();
|
||||
performance.mark("bad");
|
||||
performance.measure("badmeasure", "end", "bad");
|
||||
const records = globalObserver.takeRecords();
|
||||
println(`records.length === 0: ${records.length === 0}`);
|
||||
});
|
||||
</script>
|
|
@ -509,6 +509,8 @@ set(SOURCES
|
|||
Painting/ViewportPaintable.cpp
|
||||
PerformanceTimeline/EntryTypes.cpp
|
||||
PerformanceTimeline/PerformanceEntry.cpp
|
||||
PerformanceTimeline/PerformanceObserver.cpp
|
||||
PerformanceTimeline/PerformanceObserverEntryList.cpp
|
||||
PermissionsPolicy/AutoplayAllowlist.cpp
|
||||
PixelUnits.cpp
|
||||
Platform/AudioCodecPlugin.cpp
|
||||
|
|
|
@ -543,6 +543,9 @@ struct LinearGradientData;
|
|||
|
||||
namespace Web::PerformanceTimeline {
|
||||
class PerformanceEntry;
|
||||
class PerformanceObserver;
|
||||
class PerformanceObserverEntryList;
|
||||
struct PerformanceObserverInit;
|
||||
}
|
||||
|
||||
namespace Web::PermissionsPolicy {
|
||||
|
|
|
@ -41,6 +41,9 @@ public:
|
|||
// https://www.w3.org/TR/intersection-observer/#intersectionobserver-task-source
|
||||
IntersectionObserver,
|
||||
|
||||
// https://w3c.github.io/performance-timeline/#dfn-performance-timeline-task-source
|
||||
PerformanceTimeline,
|
||||
|
||||
// Some elements, such as the HTMLMediaElement, must have a unique task source per instance.
|
||||
// Keep this field last, to serve as the base value of all unique task sources.
|
||||
UniqueTaskSourceStart,
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -48,12 +48,18 @@ public:
|
|||
void clear_interval(i32);
|
||||
|
||||
PerformanceTimeline::PerformanceEntryTuple& relevant_performance_entry_tuple(FlyString const& entry_type);
|
||||
WebIDL::ExceptionOr<void> queue_performance_entry(JS::NonnullGCPtr<PerformanceTimeline::PerformanceEntry> new_entry);
|
||||
void queue_performance_entry(JS::NonnullGCPtr<PerformanceTimeline::PerformanceEntry> new_entry);
|
||||
void clear_performance_entry_buffer(Badge<HighResolutionTime::Performance>, FlyString const& entry_type);
|
||||
void remove_entries_from_performance_entry_buffer(Badge<HighResolutionTime::Performance>, FlyString const& entry_type, String entry_name);
|
||||
|
||||
ErrorOr<Vector<JS::Handle<PerformanceTimeline::PerformanceEntry>>> filter_buffer_map_by_name_and_type(Optional<String> name, Optional<String> type) const;
|
||||
|
||||
void register_performance_observer(Badge<PerformanceTimeline::PerformanceObserver>, JS::NonnullGCPtr<PerformanceTimeline::PerformanceObserver>);
|
||||
void unregister_performance_observer(Badge<PerformanceTimeline::PerformanceObserver>, JS::NonnullGCPtr<PerformanceTimeline::PerformanceObserver>);
|
||||
bool has_registered_performance_observer(JS::NonnullGCPtr<PerformanceTimeline::PerformanceObserver>);
|
||||
|
||||
void queue_the_performance_observer_task();
|
||||
|
||||
protected:
|
||||
void initialize(JS::Realm&);
|
||||
void visit_edges(JS::Cell::Visitor&);
|
||||
|
@ -70,8 +76,11 @@ private:
|
|||
|
||||
// https://www.w3.org/TR/performance-timeline/#performance-timeline
|
||||
// Each global object has:
|
||||
// FIXME: - a performance observer task queued flag
|
||||
// FIXME: - a list of registered performance observer objects that is initially empty
|
||||
// - a performance observer task queued flag
|
||||
bool m_performance_observer_task_queued { false };
|
||||
|
||||
// - a list of registered performance observer objects that is initially empty
|
||||
OrderedHashTable<JS::NonnullGCPtr<PerformanceTimeline::PerformanceObserver>> m_registered_performance_observer_objects;
|
||||
|
||||
// https://www.w3.org/TR/performance-timeline/#dfn-performance-entry-buffer-map
|
||||
// a performance entry buffer map map, keyed on a DOMString, representing the entry type to which the buffer belongs. The map's value is the following tuple:
|
||||
|
|
|
@ -63,7 +63,7 @@ WebIDL::ExceptionOr<JS::NonnullGCPtr<UserTiming::PerformanceMark>> Performance::
|
|||
// 2. Queue entry.
|
||||
auto* window_or_worker = dynamic_cast<HTML::WindowOrWorkerGlobalScopeMixin*>(&realm.global_object());
|
||||
VERIFY(window_or_worker);
|
||||
TRY(window_or_worker->queue_performance_entry(entry));
|
||||
window_or_worker->queue_performance_entry(entry);
|
||||
|
||||
// 3. Add entry to the performance entry buffer.
|
||||
// FIXME: This seems to be a holdover from moving to the `queue` structure for PerformanceObserver, as this would cause a double append.
|
||||
|
@ -293,7 +293,7 @@ WebIDL::ExceptionOr<JS::NonnullGCPtr<UserTiming::PerformanceMeasure>> Performanc
|
|||
auto entry = realm.heap().allocate<UserTiming::PerformanceMeasure>(realm, realm, measure_name, start_time, duration, detail);
|
||||
|
||||
// 10. Queue entry.
|
||||
TRY(window_or_worker->queue_performance_entry(entry));
|
||||
window_or_worker->queue_performance_entry(entry);
|
||||
|
||||
// 11. Add entry to the performance entry buffer.
|
||||
// FIXME: This seems to be a holdover from moving to the `queue` structure for PerformanceObserver, as this would cause a double append.
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
/*
|
||||
* Copyright (c) 2023, Luke Wilde <lukew@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace Web::HighResolutionTime {
|
||||
|
||||
// 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)
|
||||
|
||||
}
|
|
@ -36,7 +36,7 @@ public:
|
|||
HighResolutionTime::DOMHighResTimeStamp duration() const { return m_duration; }
|
||||
|
||||
// https://w3c.github.io/timing-entrytypes-registry/#dfn-should-add-entry
|
||||
virtual PerformanceTimeline::ShouldAddEntry should_add_entry() const = 0;
|
||||
virtual PerformanceTimeline::ShouldAddEntry should_add_entry(Optional<PerformanceObserverInit const&> = {}) const = 0;
|
||||
|
||||
protected:
|
||||
PerformanceEntry(JS::Realm&, String const& name, HighResolutionTime::DOMHighResTimeStamp start_time, HighResolutionTime::DOMHighResTimeStamp duration);
|
||||
|
|
|
@ -14,7 +14,7 @@ namespace Web::PerformanceTimeline {
|
|||
struct PerformanceEntryTuple {
|
||||
// https://www.w3.org/TR/performance-timeline/#dfn-performance-entry-buffer
|
||||
// A performance entry buffer to store PerformanceEntry objects, that is initially empty.
|
||||
Vector<JS::Handle<PerformanceEntry>> performance_entry_buffer;
|
||||
Vector<JS::NonnullGCPtr<PerformanceEntry>> performance_entry_buffer;
|
||||
|
||||
// https://www.w3.org/TR/performance-timeline/#dfn-maxbuffersize
|
||||
// An integer maxBufferSize, initialized to the registry value for this entry type.
|
||||
|
@ -45,6 +45,12 @@ struct PerformanceEntryTuple {
|
|||
// 4. Return true.
|
||||
return true;
|
||||
}
|
||||
|
||||
void visit_edges(JS::Cell::Visitor& visitor)
|
||||
{
|
||||
for (auto& entry : performance_entry_buffer)
|
||||
visitor.visit(entry);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,229 @@
|
|||
/*
|
||||
* Copyright (c) 2023, Luke Wilde <lukew@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <LibWeb/Bindings/Intrinsics.h>
|
||||
#include <LibWeb/Bindings/PerformanceObserverPrototype.h>
|
||||
#include <LibWeb/HTML/WindowOrWorkerGlobalScope.h>
|
||||
#include <LibWeb/HighResolutionTime/SupportedPerformanceTypes.h>
|
||||
#include <LibWeb/PerformanceTimeline/EntryTypes.h>
|
||||
#include <LibWeb/PerformanceTimeline/PerformanceEntry.h>
|
||||
#include <LibWeb/PerformanceTimeline/PerformanceObserver.h>
|
||||
#include <LibWeb/WebIDL/CallbackType.h>
|
||||
#include <LibWeb/WebIDL/ExceptionOr.h>
|
||||
|
||||
namespace Web::PerformanceTimeline {
|
||||
|
||||
WebIDL::ExceptionOr<JS::NonnullGCPtr<PerformanceObserver>> PerformanceObserver::construct_impl(JS::Realm& realm, JS::GCPtr<WebIDL::CallbackType> callback)
|
||||
{
|
||||
return realm.heap().allocate<PerformanceObserver>(realm, realm, callback);
|
||||
}
|
||||
|
||||
PerformanceObserver::PerformanceObserver(JS::Realm& realm, JS::GCPtr<WebIDL::CallbackType> callback)
|
||||
: Bindings::PlatformObject(realm)
|
||||
, m_callback(move(callback))
|
||||
{
|
||||
}
|
||||
|
||||
PerformanceObserver::~PerformanceObserver() = default;
|
||||
|
||||
void PerformanceObserver::initialize(JS::Realm& realm)
|
||||
{
|
||||
Base::initialize(realm);
|
||||
set_prototype(&Bindings::ensure_web_prototype<Bindings::PerformanceObserverPrototype>(realm, "PerformanceObserver"));
|
||||
}
|
||||
|
||||
void PerformanceObserver::visit_edges(Cell::Visitor& visitor)
|
||||
{
|
||||
Base::visit_edges(visitor);
|
||||
visitor.visit(m_callback.ptr());
|
||||
for (auto& entry : m_observer_buffer)
|
||||
visitor.visit(entry);
|
||||
}
|
||||
|
||||
// https://w3c.github.io/performance-timeline/#dom-performanceobserver-observe
|
||||
WebIDL::ExceptionOr<void> PerformanceObserver::observe(PerformanceObserverInit& options)
|
||||
{
|
||||
auto& realm = this->realm();
|
||||
|
||||
// 1. Let relevantGlobal be this's relevant global object.
|
||||
auto* relevant_global = dynamic_cast<HTML::WindowOrWorkerGlobalScopeMixin*>(&HTML::relevant_global_object(*this));
|
||||
VERIFY(relevant_global);
|
||||
|
||||
// 2. If options's entryTypes and type members are both omitted, then throw a "TypeError".
|
||||
if (!options.entry_types.has_value() && !options.type.has_value())
|
||||
return WebIDL::SimpleException { WebIDL::SimpleExceptionType::TypeError, "Must specify one of entryTypes or type"sv };
|
||||
|
||||
// 3. If options's entryTypes is present and any other member is also present, then throw a "TypeError".
|
||||
if (options.entry_types.has_value() && (options.type.has_value() || options.buffered.has_value()))
|
||||
return WebIDL::SimpleException { WebIDL::SimpleExceptionType::TypeError, "Cannot specify type or buffered if entryTypes is specified"sv };
|
||||
|
||||
// 4. Update or check this's observer type by running these steps:
|
||||
// 1. If this's observer type is "undefined":
|
||||
if (m_observer_type == ObserverType::Undefined) {
|
||||
// 1. If options's entryTypes member is present, then set this's observer type to "multiple".
|
||||
if (options.entry_types.has_value())
|
||||
m_observer_type = ObserverType::Multiple;
|
||||
|
||||
// 2. If options's type member is present, then set this's observer type to "single".
|
||||
if (options.type.has_value())
|
||||
m_observer_type = ObserverType::Single;
|
||||
}
|
||||
// 2. If this's observer type is "single" and options's entryTypes member is present, then throw an "InvalidModificationError".
|
||||
else if (m_observer_type == ObserverType::Single) {
|
||||
if (options.entry_types.has_value())
|
||||
return WebIDL::InvalidModificationError::create(realm, "Cannot change a PerformanceObserver from observing a single type to observing multiple types"sv);
|
||||
}
|
||||
// 3. If this's observer type is "multiple" and options's type member is present, then throw an "InvalidModificationError".
|
||||
else if (m_observer_type == ObserverType::Multiple) {
|
||||
if (options.type.has_value())
|
||||
return WebIDL::InvalidModificationError::create(realm, "Cannot change a PerformanceObserver from observing multiple types to observing a single type"sv);
|
||||
}
|
||||
|
||||
// 5. Set this's requires dropped entries to true.
|
||||
m_requires_dropped_entries = true;
|
||||
|
||||
// 6. If this's observer type is "multiple", run the following steps:
|
||||
if (m_observer_type == ObserverType::Multiple) {
|
||||
// 1. Let entry types be options's entryTypes sequence.
|
||||
VERIFY(options.entry_types.has_value());
|
||||
auto& entry_types = options.entry_types.value();
|
||||
|
||||
// 2. Remove all types from entry types that are not contained in relevantGlobal's frozen array of supported entry types.
|
||||
// The user agent SHOULD notify developers if entry types is modified. For example, a console warning listing removed
|
||||
// types might be appropriate.
|
||||
entry_types.remove_all_matching([](String const& type) {
|
||||
#define __ENUMERATE_SUPPORTED_PERFORMANCE_ENTRY_TYPES(entry_type, cpp_class) \
|
||||
if (entry_type == type) \
|
||||
return false;
|
||||
ENUMERATE_SUPPORTED_PERFORMANCE_ENTRY_TYPES
|
||||
#undef __ENUMERATE_SUPPORTED_PERFORMANCE_ENTRY_TYPES
|
||||
|
||||
dbgln("Potential FIXME: Removing unsupported PerformanceEntry type '{}' from list of observed types in PerformanceObserver::observe()", type);
|
||||
return true;
|
||||
});
|
||||
|
||||
// 3. If the resulting entry types sequence is an empty sequence, abort these steps.
|
||||
// The user agent SHOULD notify developers when the steps are aborted to notify that registration has been aborted.
|
||||
// For example, a console warning might be appropriate.
|
||||
if (entry_types.is_empty()) {
|
||||
dbgln("Potential FIXME: Returning from PerformanceObserver::observe() as we don't support any of the specified types (or none was specified).");
|
||||
return {};
|
||||
}
|
||||
|
||||
// 4. If the list of registered performance observer objects of relevantGlobal contains a registered performance
|
||||
// observer whose observer is this, replace its options list with a list containing options as its only item.
|
||||
// 5. Otherwise, create and append a registered performance observer object to the list of registered performance
|
||||
// observer objects of relevantGlobal, with observer set to this and options list set to a list containing
|
||||
// options as its only item.
|
||||
// NOTE: See the comment on PerformanceObserver::options_list about why this doesn't create a separate registered
|
||||
// performance observer object.
|
||||
m_options_list.clear();
|
||||
m_options_list.append(options);
|
||||
relevant_global->register_performance_observer({}, *this);
|
||||
}
|
||||
// 7. Otherwise, run the following steps:
|
||||
else {
|
||||
// 1. Assert that this's observer type is "single".
|
||||
VERIFY(m_observer_type == ObserverType::Single);
|
||||
|
||||
// 2. If options's type is not contained in the relevantGlobal's frozen array of supported entry types, abort these steps.
|
||||
// The user agent SHOULD notify developers when this happens, for instance via a console warning.
|
||||
VERIFY(options.type.has_value());
|
||||
auto& type = options.type.value();
|
||||
bool recognized_type = false;
|
||||
|
||||
#define __ENUMERATE_SUPPORTED_PERFORMANCE_ENTRY_TYPES(entry_type, cpp_class) \
|
||||
if (!recognized_type && entry_type == type) \
|
||||
recognized_type = true;
|
||||
ENUMERATE_SUPPORTED_PERFORMANCE_ENTRY_TYPES
|
||||
#undef __ENUMERATE_SUPPORTED_PERFORMANCE_ENTRY_TYPES
|
||||
|
||||
if (!recognized_type) {
|
||||
dbgln("Potential FIXME: Returning from PerformanceObserver::observe() as we don't support the PerformanceEntry type '{}'", type);
|
||||
return {};
|
||||
}
|
||||
|
||||
// 3. If the list of registered performance observer objects of relevantGlobal contains a registered performance
|
||||
// observer obs whose observer is this:
|
||||
if (relevant_global->has_registered_performance_observer(*this)) {
|
||||
// 1. If obs's options list contains a PerformanceObserverInit item currentOptions whose type is equal to options's type,
|
||||
// replace currentOptions with options in obs's options list.
|
||||
auto index = m_options_list.find_first_index_if([&options](PerformanceObserverInit const& entry) {
|
||||
return entry.type == options.type;
|
||||
});
|
||||
if (index.has_value()) {
|
||||
m_options_list[index.value()] = options;
|
||||
} else {
|
||||
// Otherwise, append options to obs's options list.
|
||||
m_options_list.append(options);
|
||||
}
|
||||
}
|
||||
// 4. Otherwise, create and append a registered performance observer object to the list of registered performance
|
||||
// observer objects of relevantGlobal, with observer set to the this and options list set to a list containing
|
||||
// options as its only item.
|
||||
else {
|
||||
m_options_list.clear();
|
||||
m_options_list.append(options);
|
||||
relevant_global->register_performance_observer({}, *this);
|
||||
}
|
||||
|
||||
// 5. If options's buffered flag is set:
|
||||
if (options.buffered.has_value() && options.buffered.value()) {
|
||||
// 1. Let tuple be the relevant performance entry tuple of options's type and relevantGlobal.
|
||||
auto const& tuple = relevant_global->relevant_performance_entry_tuple(type);
|
||||
|
||||
// 2. For each entry in tuple's performance entry buffer:
|
||||
for (auto const& entry : tuple.performance_entry_buffer) {
|
||||
// 1. If should add entry with entry and options as parameters returns true, append entry to the observer buffer.
|
||||
if (entry->should_add_entry(options) == ShouldAddEntry::Yes)
|
||||
m_observer_buffer.append(*entry);
|
||||
}
|
||||
|
||||
// 3. Queue the PerformanceObserver task with relevantGlobal as input.
|
||||
relevant_global->queue_the_performance_observer_task();
|
||||
}
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
// https://w3c.github.io/performance-timeline/#dom-performanceobserver-disconnect
|
||||
void PerformanceObserver::disconnect()
|
||||
{
|
||||
// 1. Remove this from the list of registered performance observer objects of relevant global object.
|
||||
auto* relevant_global = dynamic_cast<HTML::WindowOrWorkerGlobalScopeMixin*>(&HTML::relevant_global_object(*this));
|
||||
VERIFY(relevant_global);
|
||||
relevant_global->unregister_performance_observer({}, *this);
|
||||
|
||||
// 2. Empty this's observer buffer.
|
||||
m_observer_buffer.clear();
|
||||
|
||||
// 3. Empty this's options list.
|
||||
m_options_list.clear();
|
||||
}
|
||||
|
||||
// https://w3c.github.io/performance-timeline/#dom-performanceobserver-takerecords
|
||||
Vector<JS::Handle<PerformanceTimeline::PerformanceEntry>> PerformanceObserver::take_records()
|
||||
{
|
||||
// The takeRecords() method must return a copy of this's observer buffer, and also empty this's observer buffer.
|
||||
Vector<JS::Handle<PerformanceTimeline::PerformanceEntry>> records;
|
||||
for (auto& record : m_observer_buffer)
|
||||
records.append(*record);
|
||||
m_observer_buffer.clear();
|
||||
return records;
|
||||
}
|
||||
|
||||
void PerformanceObserver::unset_requires_dropped_entries(Badge<HTML::WindowOrWorkerGlobalScopeMixin>)
|
||||
{
|
||||
m_requires_dropped_entries = false;
|
||||
}
|
||||
|
||||
void PerformanceObserver::append_to_observer_buffer(Badge<HTML::WindowOrWorkerGlobalScopeMixin>, JS::NonnullGCPtr<PerformanceTimeline::PerformanceEntry> entry)
|
||||
{
|
||||
m_observer_buffer.append(entry);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,77 @@
|
|||
/*
|
||||
* Copyright (c) 2023, Luke Wilde <lukew@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <LibWeb/Bindings/PlatformObject.h>
|
||||
#include <LibWeb/HTML/WindowOrWorkerGlobalScope.h>
|
||||
|
||||
namespace Web::PerformanceTimeline {
|
||||
|
||||
// https://w3c.github.io/performance-timeline/#dom-performanceobserverinit
|
||||
struct PerformanceObserverInit {
|
||||
Optional<Vector<String>> entry_types;
|
||||
Optional<String> type;
|
||||
Optional<bool> buffered;
|
||||
};
|
||||
|
||||
// https://w3c.github.io/performance-timeline/#dom-performanceobserver
|
||||
class PerformanceObserver final : public Bindings::PlatformObject {
|
||||
WEB_PLATFORM_OBJECT(PerformanceObserver, Bindings::PlatformObject);
|
||||
|
||||
public:
|
||||
enum class ObserverType {
|
||||
Undefined,
|
||||
Single,
|
||||
Multiple,
|
||||
};
|
||||
|
||||
static WebIDL::ExceptionOr<JS::NonnullGCPtr<PerformanceObserver>> construct_impl(JS::Realm&, JS::GCPtr<WebIDL::CallbackType>);
|
||||
virtual ~PerformanceObserver() override;
|
||||
|
||||
WebIDL::ExceptionOr<void> observe(PerformanceObserverInit& options);
|
||||
void disconnect();
|
||||
Vector<JS::Handle<PerformanceTimeline::PerformanceEntry>> take_records();
|
||||
|
||||
bool requires_dropped_entries() const { return m_requires_dropped_entries; }
|
||||
void unset_requires_dropped_entries(Badge<HTML::WindowOrWorkerGlobalScopeMixin>);
|
||||
|
||||
Vector<PerformanceObserverInit> const& options_list() const { return m_options_list; }
|
||||
|
||||
WebIDL::CallbackType& callback() { return *m_callback; }
|
||||
|
||||
void append_to_observer_buffer(Badge<HTML::WindowOrWorkerGlobalScopeMixin>, JS::NonnullGCPtr<PerformanceTimeline::PerformanceEntry>);
|
||||
|
||||
private:
|
||||
PerformanceObserver(JS::Realm&, JS::GCPtr<WebIDL::CallbackType>);
|
||||
|
||||
virtual void initialize(JS::Realm&) override;
|
||||
virtual void visit_edges(Cell::Visitor&) override;
|
||||
|
||||
// https://w3c.github.io/performance-timeline/#dfn-observer-callback
|
||||
// A PerformanceObserverCallback observer callback set on creation.
|
||||
JS::GCPtr<WebIDL::CallbackType> m_callback;
|
||||
|
||||
// https://w3c.github.io/performance-timeline/#dfn-observer-buffer
|
||||
// A PerformanceEntryList object called the observer buffer that is initially empty.
|
||||
Vector<JS::NonnullGCPtr<PerformanceTimeline::PerformanceEntry>> m_observer_buffer;
|
||||
|
||||
// https://w3c.github.io/performance-timeline/#dfn-observer-type
|
||||
// A DOMString observer type which is initially "undefined".
|
||||
ObserverType m_observer_type { ObserverType::Undefined };
|
||||
|
||||
// https://w3c.github.io/performance-timeline/#dfn-requires-dropped-entries
|
||||
// A boolean requires dropped entries which is initially set to false.
|
||||
bool m_requires_dropped_entries { false };
|
||||
|
||||
// https://w3c.github.io/performance-timeline/#dfn-options-list
|
||||
// A registered performance observer is a struct consisting of an observer member (a PerformanceObserver object)
|
||||
// and an options list member (a list of PerformanceObserverInit dictionaries).
|
||||
// NOTE: This doesn't use a separate struct as methods such as disconnect() assume it can access an options list from `this`: a PerformanceObserver.
|
||||
Vector<PerformanceObserverInit> m_options_list;
|
||||
};
|
||||
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
#import <PerformanceTimeline/PerformanceObserverEntryList.idl>
|
||||
|
||||
// https://w3c.github.io/performance-timeline/#dom-performanceobservercallbackoptions
|
||||
dictionary PerformanceObserverCallbackOptions {
|
||||
unsigned long long droppedEntriesCount;
|
||||
};
|
||||
|
||||
callback PerformanceObserverCallback = undefined (PerformanceObserverEntryList entries, PerformanceObserver observer, optional PerformanceObserverCallbackOptions options = {});
|
||||
|
||||
// https://w3c.github.io/performance-timeline/#dom-performanceobserverinit
|
||||
dictionary PerformanceObserverInit {
|
||||
sequence<DOMString> entryTypes;
|
||||
DOMString type;
|
||||
boolean buffered;
|
||||
};
|
||||
|
||||
// https://w3c.github.io/performance-timeline/#dom-performanceobserver
|
||||
[Exposed=(Window,Worker), UseNewAKString]
|
||||
interface PerformanceObserver {
|
||||
constructor(PerformanceObserverCallback callback);
|
||||
undefined observe(optional PerformanceObserverInit options = {});
|
||||
undefined disconnect();
|
||||
PerformanceEntryList takeRecords();
|
||||
//[SameObject] static readonly attribute sequence<DOMString> supportedEntryTypes;
|
||||
};
|
|
@ -0,0 +1,91 @@
|
|||
/*
|
||||
* Copyright (c) 2023, Luke Wilde <lukew@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <AK/QuickSort.h>
|
||||
#include <LibWeb/Bindings/Intrinsics.h>
|
||||
#include <LibWeb/Bindings/PerformanceObserverEntryListPrototype.h>
|
||||
#include <LibWeb/PerformanceTimeline/PerformanceEntry.h>
|
||||
#include <LibWeb/PerformanceTimeline/PerformanceObserverEntryList.h>
|
||||
#include <LibWeb/WebIDL/ExceptionOr.h>
|
||||
|
||||
namespace Web::PerformanceTimeline {
|
||||
|
||||
PerformanceObserverEntryList::PerformanceObserverEntryList(JS::Realm& realm, Vector<JS::NonnullGCPtr<PerformanceTimeline::PerformanceEntry>>&& entry_list)
|
||||
: Bindings::PlatformObject(realm)
|
||||
, m_entry_list(move(entry_list))
|
||||
{
|
||||
}
|
||||
|
||||
PerformanceObserverEntryList::~PerformanceObserverEntryList() = default;
|
||||
|
||||
void PerformanceObserverEntryList::initialize(JS::Realm& realm)
|
||||
{
|
||||
Base::initialize(realm);
|
||||
set_prototype(&Bindings::ensure_web_prototype<Bindings::PerformanceObserverEntryListPrototype>(realm, "PerformanceObserverEntryList"));
|
||||
}
|
||||
|
||||
void PerformanceObserverEntryList::visit_edges(Cell::Visitor& visitor)
|
||||
{
|
||||
Base::visit_edges(visitor);
|
||||
for (auto& entry : m_entry_list)
|
||||
visitor.visit(entry);
|
||||
}
|
||||
|
||||
// https://www.w3.org/TR/performance-timeline/#dfn-filter-buffer-by-name-and-type
|
||||
ErrorOr<Vector<JS::Handle<PerformanceTimeline::PerformanceEntry>>> filter_buffer_by_name_and_type(Vector<JS::NonnullGCPtr<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://w3c.github.io/performance-timeline/#dom-performanceobserverentrylist-getentries
|
||||
WebIDL::ExceptionOr<Vector<JS::Handle<PerformanceTimeline::PerformanceEntry>>> PerformanceObserverEntryList::get_entries() const
|
||||
{
|
||||
// Returns a PerformanceEntryList object returned by filter buffer by name and type algorithm with this's entry list,
|
||||
// name and type set to null.
|
||||
return TRY_OR_THROW_OOM(vm(), filter_buffer_by_name_and_type(m_entry_list, /* name= */ Optional<String> {}, /* type= */ Optional<String> {}));
|
||||
}
|
||||
|
||||
// https://w3c.github.io/performance-timeline/#dom-performanceobserverentrylist-getentriesbytype
|
||||
WebIDL::ExceptionOr<Vector<JS::Handle<PerformanceTimeline::PerformanceEntry>>> PerformanceObserverEntryList::get_entries_by_type(String const& type) const
|
||||
{
|
||||
// Returns a PerformanceEntryList object returned by filter buffer by name and type algorithm with this's entry list,
|
||||
// name set to null, and type set to the method's input type parameter.
|
||||
return TRY_OR_THROW_OOM(vm(), filter_buffer_by_name_and_type(m_entry_list, /* name= */ Optional<String> {}, type));
|
||||
}
|
||||
|
||||
// https://w3c.github.io/performance-timeline/#dom-performanceobserverentrylist-getentriesbyname
|
||||
WebIDL::ExceptionOr<Vector<JS::Handle<PerformanceTimeline::PerformanceEntry>>> PerformanceObserverEntryList::get_entries_by_name(String const& name, Optional<String> type) const
|
||||
{
|
||||
// Returns a PerformanceEntryList object returned by filter buffer by name and type algorithm with this's entry list,
|
||||
// name set to the method input name parameter, and type set to null if optional entryType is omitted, or set to the
|
||||
// method's input type parameter otherwise.
|
||||
return TRY_OR_THROW_OOM(vm(), filter_buffer_by_name_and_type(m_entry_list, name, type));
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
/*
|
||||
* Copyright (c) 2023, Luke Wilde <lukew@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <LibWeb/Bindings/PlatformObject.h>
|
||||
|
||||
namespace Web::PerformanceTimeline {
|
||||
|
||||
// https://w3c.github.io/performance-timeline/#performanceobserverentrylist-interface
|
||||
class PerformanceObserverEntryList final : public Bindings::PlatformObject {
|
||||
WEB_PLATFORM_OBJECT(PerformanceObserverEntryList, Bindings::PlatformObject);
|
||||
|
||||
public:
|
||||
virtual ~PerformanceObserverEntryList() override;
|
||||
|
||||
WebIDL::ExceptionOr<Vector<JS::Handle<PerformanceTimeline::PerformanceEntry>>> get_entries() const;
|
||||
WebIDL::ExceptionOr<Vector<JS::Handle<PerformanceTimeline::PerformanceEntry>>> get_entries_by_type(String const& type) const;
|
||||
WebIDL::ExceptionOr<Vector<JS::Handle<PerformanceTimeline::PerformanceEntry>>> get_entries_by_name(String const& name, Optional<String> type) const;
|
||||
|
||||
private:
|
||||
PerformanceObserverEntryList(JS::Realm&, Vector<JS::NonnullGCPtr<PerformanceTimeline::PerformanceEntry>>&&);
|
||||
|
||||
virtual void initialize(JS::Realm&) override;
|
||||
virtual void visit_edges(Cell::Visitor&) override;
|
||||
|
||||
// https://w3c.github.io/performance-timeline/#dfn-entry-list
|
||||
// Returns a PerformanceEntryList object returned by filter buffer by name and type algorithm with this's entry list,
|
||||
// name and type set to null.
|
||||
Vector<JS::NonnullGCPtr<PerformanceTimeline::PerformanceEntry>> m_entry_list;
|
||||
};
|
||||
|
||||
ErrorOr<Vector<JS::Handle<PerformanceTimeline::PerformanceEntry>>> filter_buffer_by_name_and_type(Vector<JS::NonnullGCPtr<PerformanceTimeline::PerformanceEntry>> const& buffer, Optional<String> name, Optional<String> type);
|
||||
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
#import <HighResolutionTime/Performance.idl>
|
||||
|
||||
// https://w3c.github.io/performance-timeline/#performanceobserverentrylist-interface
|
||||
[Exposed=(Window,Worker), UseNewAKString]
|
||||
interface PerformanceObserverEntryList {
|
||||
PerformanceEntryList getEntries();
|
||||
PerformanceEntryList getEntriesByType(DOMString type);
|
||||
PerformanceEntryList getEntriesByName(DOMString name, optional DOMString type);
|
||||
};
|
|
@ -36,7 +36,7 @@ public:
|
|||
static Optional<u64> max_buffer_size() { return OptionalNone {}; }
|
||||
|
||||
// https://w3c.github.io/timing-entrytypes-registry/#dfn-should-add-entry
|
||||
virtual PerformanceTimeline::ShouldAddEntry should_add_entry() const override { return PerformanceTimeline::ShouldAddEntry::Yes; }
|
||||
virtual PerformanceTimeline::ShouldAddEntry should_add_entry(Optional<PerformanceTimeline::PerformanceObserverInit const&> = {}) const override { return PerformanceTimeline::ShouldAddEntry::Yes; }
|
||||
|
||||
virtual FlyString const& entry_type() const override;
|
||||
|
||||
|
|
|
@ -38,7 +38,7 @@ public:
|
|||
static Optional<u64> max_buffer_size() { return OptionalNone {}; }
|
||||
|
||||
// https://w3c.github.io/timing-entrytypes-registry/#dfn-should-add-entry
|
||||
virtual PerformanceTimeline::ShouldAddEntry should_add_entry() const override { return PerformanceTimeline::ShouldAddEntry::Yes; }
|
||||
virtual PerformanceTimeline::ShouldAddEntry should_add_entry(Optional<PerformanceTimeline::PerformanceObserverInit const&> = {}) const override { return PerformanceTimeline::ShouldAddEntry::Yes; }
|
||||
|
||||
virtual FlyString const& entry_type() const override;
|
||||
|
||||
|
|
|
@ -199,6 +199,8 @@ libweb_js_bindings(IntersectionObserver/IntersectionObserverEntry)
|
|||
libweb_js_bindings(MathML/MathMLElement)
|
||||
libweb_js_bindings(NavigationTiming/PerformanceTiming)
|
||||
libweb_js_bindings(PerformanceTimeline/PerformanceEntry)
|
||||
libweb_js_bindings(PerformanceTimeline/PerformanceObserver)
|
||||
libweb_js_bindings(PerformanceTimeline/PerformanceObserverEntryList)
|
||||
libweb_js_bindings(RequestIdleCallback/IdleDeadline)
|
||||
libweb_js_bindings(ResizeObserver/ResizeObserver)
|
||||
libweb_js_bindings(Streams/ByteLengthQueuingStrategy)
|
||||
|
|
Loading…
Reference in a new issue