/src/serenity/Userland/Libraries/LibWeb/UserTiming/PerformanceMark.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Copyright (c) 2023, Luke Wilde <lukew@serenityos.org> |
3 | | * |
4 | | * SPDX-License-Identifier: BSD-2-Clause |
5 | | */ |
6 | | |
7 | | #include <LibWeb/Bindings/Intrinsics.h> |
8 | | #include <LibWeb/Bindings/PerformanceMarkPrototype.h> |
9 | | #include <LibWeb/HTML/StructuredSerialize.h> |
10 | | #include <LibWeb/HTML/Window.h> |
11 | | #include <LibWeb/HighResolutionTime/TimeOrigin.h> |
12 | | #include <LibWeb/NavigationTiming/EntryNames.h> |
13 | | #include <LibWeb/PerformanceTimeline/EntryTypes.h> |
14 | | #include <LibWeb/UserTiming/PerformanceMark.h> |
15 | | #include <LibWeb/WebIDL/ExceptionOr.h> |
16 | | |
17 | | namespace Web::UserTiming { |
18 | | |
19 | | JS_DEFINE_ALLOCATOR(PerformanceMark); |
20 | | |
21 | | PerformanceMark::PerformanceMark(JS::Realm& realm, String const& name, HighResolutionTime::DOMHighResTimeStamp start_time, HighResolutionTime::DOMHighResTimeStamp duration, JS::Value detail) |
22 | 0 | : PerformanceTimeline::PerformanceEntry(realm, name, start_time, duration) |
23 | 0 | , m_detail(detail) |
24 | 0 | { |
25 | 0 | } |
26 | | |
27 | 0 | PerformanceMark::~PerformanceMark() = default; |
28 | | |
29 | | // https://w3c.github.io/user-timing/#dfn-performancemark-constructor |
30 | | WebIDL::ExceptionOr<JS::NonnullGCPtr<PerformanceMark>> PerformanceMark::construct_impl(JS::Realm& realm, String const& mark_name, Web::UserTiming::PerformanceMarkOptions const& mark_options) |
31 | 0 | { |
32 | 0 | auto& current_global_object = realm.global_object(); |
33 | 0 | auto& vm = realm.vm(); |
34 | | |
35 | | // 1. If the current global object is a Window object and markName uses the same name as a read only attribute in the PerformanceTiming interface, throw a SyntaxError. |
36 | 0 | if (is<HTML::Window>(current_global_object)) { |
37 | 0 | bool matched = false; |
38 | |
|
39 | 0 | #define __ENUMERATE_NAVIGATION_TIMING_ENTRY_NAME(name, _) \ |
40 | 0 | if (mark_name == NavigationTiming::EntryNames::name) \ |
41 | 0 | matched = true; |
42 | 0 | ENUMERATE_NAVIGATION_TIMING_ENTRY_NAMES |
43 | 0 | #undef __ENUMERATE_NAVIGATION_TIMING_ENTRY_NAME |
44 | |
|
45 | 0 | if (matched) |
46 | 0 | return WebIDL::SyntaxError::create(realm, MUST(String::formatted("'{}' markName cannot be used in a Window context because it is part of the PerformanceTiming interface", mark_name))); |
47 | 0 | } |
48 | | |
49 | | // NOTE: Step 2 (creating the entry) is done after determining values, as we set the values once during creation and never change them after. |
50 | | |
51 | | // 3. Set entry's name attribute to markName. |
52 | 0 | auto const& name = mark_name; |
53 | | |
54 | | // 4. Set entry's entryType attribute to DOMString "mark". |
55 | | // NOTE: Already done via the `entry_type` virtual function. |
56 | | |
57 | | // 5. Set entry's startTime attribute as follows: |
58 | 0 | HighResolutionTime::DOMHighResTimeStamp start_time { 0.0 }; |
59 | | |
60 | | // 1. If markOptions's startTime member is present, then: |
61 | 0 | if (mark_options.start_time.has_value()) { |
62 | | // 1. If markOptions's startTime is negative, throw a TypeError. |
63 | 0 | if (mark_options.start_time.value() < 0.0) |
64 | 0 | return WebIDL::SimpleException { WebIDL::SimpleExceptionType::TypeError, "startTime cannot be negative"sv }; |
65 | | |
66 | | // 2. Otherwise, set entry's startTime to the value of markOptions's startTime. |
67 | 0 | start_time = mark_options.start_time.value(); |
68 | 0 | } |
69 | | // 2. Otherwise, set it to the value that would be returned by the Performance object's now() method. |
70 | 0 | else { |
71 | | // FIXME: Performance#now doesn't currently use TimeOrigin's functions, update this and Performance#now to match Performance#now's specification. |
72 | 0 | start_time = HighResolutionTime::unsafe_shared_current_time(); |
73 | 0 | } |
74 | | |
75 | | // 6. Set entry's duration attribute to 0. |
76 | 0 | constexpr HighResolutionTime::DOMHighResTimeStamp duration = 0.0; |
77 | | |
78 | | // 7. If markOptions's detail is null, set entry's detail to null. |
79 | 0 | JS::Value detail; |
80 | 0 | if (mark_options.detail.is_null()) { |
81 | 0 | detail = JS::js_null(); |
82 | 0 | } |
83 | | // 8. Otherwise: |
84 | 0 | else { |
85 | | // 1. Let record be the result of calling the StructuredSerialize algorithm on markOptions's detail. |
86 | 0 | auto record = TRY(HTML::structured_serialize(vm, mark_options.detail)); |
87 | | |
88 | | // 2. Set entry's detail to the result of calling the StructuredDeserialize algorithm on record and the current realm. |
89 | 0 | detail = TRY(HTML::structured_deserialize(vm, record, realm, Optional<HTML::DeserializationMemory> {})); |
90 | 0 | } |
91 | | |
92 | | // 2. Create a new PerformanceMark object (entry) with the current global object's realm. |
93 | 0 | return realm.heap().allocate<PerformanceMark>(realm, realm, name, start_time, duration, detail); |
94 | 0 | } |
95 | | |
96 | | FlyString const& PerformanceMark::entry_type() const |
97 | 0 | { |
98 | 0 | return PerformanceTimeline::EntryTypes::mark; |
99 | 0 | } |
100 | | |
101 | | void PerformanceMark::initialize(JS::Realm& realm) |
102 | 0 | { |
103 | 0 | Base::initialize(realm); |
104 | 0 | WEB_SET_PROTOTYPE_FOR_INTERFACE(PerformanceMark); |
105 | 0 | } |
106 | | |
107 | | void PerformanceMark::visit_edges(JS::Cell::Visitor& visitor) |
108 | 0 | { |
109 | 0 | Base::visit_edges(visitor); |
110 | 0 | visitor.visit(m_detail); |
111 | 0 | } |
112 | | |
113 | | } |