mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-07-29 12:19:54 +00:00
LibWeb: Implement Resource Timing
This commit is contained in:
parent
23c84e62a5
commit
6d1f78198d
Notes:
github-actions[bot]
2025-03-06 16:01:58 +00:00
Author: https://github.com/Lubrsi
Commit: 6d1f78198d
Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/3704
Reviewed-by: https://github.com/ADKaster ✅
21 changed files with 741 additions and 14 deletions
|
@ -681,6 +681,7 @@ set(SOURCES
|
||||||
Painting/VideoPaintable.cpp
|
Painting/VideoPaintable.cpp
|
||||||
Painting/ViewportPaintable.cpp
|
Painting/ViewportPaintable.cpp
|
||||||
PerformanceTimeline/EntryTypes.cpp
|
PerformanceTimeline/EntryTypes.cpp
|
||||||
|
PerformanceTimeline/EventNames.cpp
|
||||||
PerformanceTimeline/PerformanceEntry.cpp
|
PerformanceTimeline/PerformanceEntry.cpp
|
||||||
PerformanceTimeline/PerformanceObserver.cpp
|
PerformanceTimeline/PerformanceObserver.cpp
|
||||||
PerformanceTimeline/PerformanceObserverEntryList.cpp
|
PerformanceTimeline/PerformanceObserverEntryList.cpp
|
||||||
|
@ -701,6 +702,7 @@ set(SOURCES
|
||||||
ResizeObserver/ResizeObserver.cpp
|
ResizeObserver/ResizeObserver.cpp
|
||||||
ResizeObserver/ResizeObserverEntry.cpp
|
ResizeObserver/ResizeObserverEntry.cpp
|
||||||
ResizeObserver/ResizeObserverSize.cpp
|
ResizeObserver/ResizeObserverSize.cpp
|
||||||
|
ResourceTiming/PerformanceResourceTiming.cpp
|
||||||
SecureContexts/AbstractOperations.cpp
|
SecureContexts/AbstractOperations.cpp
|
||||||
ServiceWorker/Job.cpp
|
ServiceWorker/Job.cpp
|
||||||
ServiceWorker/Registration.cpp
|
ServiceWorker/Registration.cpp
|
||||||
|
|
|
@ -53,6 +53,7 @@
|
||||||
#include <LibWeb/MixedContent/AbstractOperations.h>
|
#include <LibWeb/MixedContent/AbstractOperations.h>
|
||||||
#include <LibWeb/Platform/EventLoopPlugin.h>
|
#include <LibWeb/Platform/EventLoopPlugin.h>
|
||||||
#include <LibWeb/ReferrerPolicy/AbstractOperations.h>
|
#include <LibWeb/ReferrerPolicy/AbstractOperations.h>
|
||||||
|
#include <LibWeb/ResourceTiming/PerformanceResourceTiming.h>
|
||||||
#include <LibWeb/SRI/SRI.h>
|
#include <LibWeb/SRI/SRI.h>
|
||||||
#include <LibWeb/SecureContexts/AbstractOperations.h>
|
#include <LibWeb/SecureContexts/AbstractOperations.h>
|
||||||
#include <LibWeb/Streams/TransformStream.h>
|
#include <LibWeb/Streams/TransformStream.h>
|
||||||
|
@ -687,13 +688,11 @@ void fetch_response_handover(JS::Realm& realm, Infrastructure::FetchParams const
|
||||||
body_info.content_type = MimeSniff::minimise_a_supported_mime_type(mime_type.value());
|
body_info.content_type = MimeSniff::minimise_a_supported_mime_type(mime_type.value());
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME: 8. If fetchParams’s request’s initiator type is not null, then mark resource timing given timingInfo,
|
// 8. If fetchParams’s request’s initiator type is not null, then mark resource timing given timingInfo,
|
||||||
// request’s URL, request’s initiator type, global, cacheState, bodyInfo, and responseStatus.
|
// request’s URL, request’s initiator type, global, cacheState, bodyInfo, and responseStatus.
|
||||||
(void)timing_info;
|
if (fetch_params.request()->initiator_type().has_value()) {
|
||||||
(void)global;
|
ResourceTiming::PerformanceResourceTiming::mark_resource_timing(timing_info, fetch_params.request()->url().to_string(), Infrastructure::initiator_type_to_string(fetch_params.request()->initiator_type().value()), global, cache_state, body_info, response_status);
|
||||||
(void)cache_state;
|
}
|
||||||
(void)body_info;
|
|
||||||
(void)response_status;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// 4. Let processResponseEndOfBodyTask be the following steps:
|
// 4. Let processResponseEndOfBodyTask be the following steps:
|
||||||
|
|
|
@ -442,6 +442,55 @@ StringView request_mode_to_string(Request::Mode mode)
|
||||||
VERIFY_NOT_REACHED();
|
VERIFY_NOT_REACHED();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FlyString initiator_type_to_string(Request::InitiatorType initiator_type)
|
||||||
|
{
|
||||||
|
switch (initiator_type) {
|
||||||
|
case Request::InitiatorType::Audio:
|
||||||
|
return "audio"_fly_string;
|
||||||
|
case Request::InitiatorType::Beacon:
|
||||||
|
return "beacon"_fly_string;
|
||||||
|
case Request::InitiatorType::Body:
|
||||||
|
return "body"_fly_string;
|
||||||
|
case Request::InitiatorType::CSS:
|
||||||
|
return "css"_fly_string;
|
||||||
|
case Request::InitiatorType::EarlyHint:
|
||||||
|
return "early-hints"_fly_string;
|
||||||
|
case Request::InitiatorType::Embed:
|
||||||
|
return "embed"_fly_string;
|
||||||
|
case Request::InitiatorType::Fetch:
|
||||||
|
return "fetch"_fly_string;
|
||||||
|
case Request::InitiatorType::Font:
|
||||||
|
return "font"_fly_string;
|
||||||
|
case Request::InitiatorType::Frame:
|
||||||
|
return "frame"_fly_string;
|
||||||
|
case Request::InitiatorType::IFrame:
|
||||||
|
return "iframe"_fly_string;
|
||||||
|
case Request::InitiatorType::Image:
|
||||||
|
return "image"_fly_string;
|
||||||
|
case Request::InitiatorType::IMG:
|
||||||
|
return "img"_fly_string;
|
||||||
|
case Request::InitiatorType::Input:
|
||||||
|
return "input"_fly_string;
|
||||||
|
case Request::InitiatorType::Link:
|
||||||
|
return "link"_fly_string;
|
||||||
|
case Request::InitiatorType::Object:
|
||||||
|
return "object"_fly_string;
|
||||||
|
case Request::InitiatorType::Ping:
|
||||||
|
return "ping"_fly_string;
|
||||||
|
case Request::InitiatorType::Script:
|
||||||
|
return "script"_fly_string;
|
||||||
|
case Request::InitiatorType::Track:
|
||||||
|
return "track"_fly_string;
|
||||||
|
case Request::InitiatorType::Video:
|
||||||
|
return "video"_fly_string;
|
||||||
|
case Request::InitiatorType::XMLHttpRequest:
|
||||||
|
return "xmlhttprequest"_fly_string;
|
||||||
|
case Request::InitiatorType::Other:
|
||||||
|
return "other"_fly_string;
|
||||||
|
}
|
||||||
|
VERIFY_NOT_REACHED();
|
||||||
|
}
|
||||||
|
|
||||||
Optional<Request::Priority> request_priority_from_string(StringView string)
|
Optional<Request::Priority> request_priority_from_string(StringView string)
|
||||||
{
|
{
|
||||||
if (string.equals_ignoring_ascii_case("high"sv))
|
if (string.equals_ignoring_ascii_case("high"sv))
|
||||||
|
|
|
@ -531,6 +531,7 @@ private:
|
||||||
|
|
||||||
StringView request_destination_to_string(Request::Destination);
|
StringView request_destination_to_string(Request::Destination);
|
||||||
StringView request_mode_to_string(Request::Mode);
|
StringView request_mode_to_string(Request::Mode);
|
||||||
|
FlyString initiator_type_to_string(Request::InitiatorType);
|
||||||
|
|
||||||
Optional<Request::Priority> request_priority_from_string(StringView);
|
Optional<Request::Priority> request_priority_from_string(StringView);
|
||||||
|
|
||||||
|
|
|
@ -74,6 +74,7 @@ enum class MediaEncodingType : u8;
|
||||||
enum class MediaKeysRequirement : u8;
|
enum class MediaKeysRequirement : u8;
|
||||||
enum class ReadableStreamReaderMode : u8;
|
enum class ReadableStreamReaderMode : u8;
|
||||||
enum class ReferrerPolicy : u8;
|
enum class ReferrerPolicy : u8;
|
||||||
|
enum class RenderBlockingStatusType : u8;
|
||||||
enum class RequestCache : u8;
|
enum class RequestCache : u8;
|
||||||
enum class RequestCredentials : u8;
|
enum class RequestCredentials : u8;
|
||||||
enum class RequestDestination : u8;
|
enum class RequestDestination : u8;
|
||||||
|
@ -756,6 +757,10 @@ namespace Web::ResizeObserver {
|
||||||
class ResizeObserver;
|
class ResizeObserver;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace Web::ResourceTiming {
|
||||||
|
class PerformanceResourceTiming;
|
||||||
|
}
|
||||||
|
|
||||||
namespace Web::Selection {
|
namespace Web::Selection {
|
||||||
class Selection;
|
class Selection;
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,10 +34,12 @@
|
||||||
#include <LibWeb/IndexedDB/IDBFactory.h>
|
#include <LibWeb/IndexedDB/IDBFactory.h>
|
||||||
#include <LibWeb/Infra/Strings.h>
|
#include <LibWeb/Infra/Strings.h>
|
||||||
#include <LibWeb/PerformanceTimeline/EntryTypes.h>
|
#include <LibWeb/PerformanceTimeline/EntryTypes.h>
|
||||||
|
#include <LibWeb/PerformanceTimeline/EventNames.h>
|
||||||
#include <LibWeb/PerformanceTimeline/PerformanceObserver.h>
|
#include <LibWeb/PerformanceTimeline/PerformanceObserver.h>
|
||||||
#include <LibWeb/PerformanceTimeline/PerformanceObserverEntryList.h>
|
#include <LibWeb/PerformanceTimeline/PerformanceObserverEntryList.h>
|
||||||
#include <LibWeb/Platform/EventLoopPlugin.h>
|
#include <LibWeb/Platform/EventLoopPlugin.h>
|
||||||
#include <LibWeb/Platform/ImageCodecPlugin.h>
|
#include <LibWeb/Platform/ImageCodecPlugin.h>
|
||||||
|
#include <LibWeb/ResourceTiming/PerformanceResourceTiming.h>
|
||||||
#include <LibWeb/UserTiming/PerformanceMark.h>
|
#include <LibWeb/UserTiming/PerformanceMark.h>
|
||||||
#include <LibWeb/UserTiming/PerformanceMeasure.h>
|
#include <LibWeb/UserTiming/PerformanceMeasure.h>
|
||||||
#include <LibWeb/WebIDL/AbstractOperations.h>
|
#include <LibWeb/WebIDL/AbstractOperations.h>
|
||||||
|
@ -75,6 +77,7 @@ void WindowOrWorkerGlobalScopeMixin::visit_edges(JS::Cell::Visitor& visitor)
|
||||||
entry.value.visit_edges(visitor);
|
entry.value.visit_edges(visitor);
|
||||||
visitor.visit(m_registered_event_sources);
|
visitor.visit(m_registered_event_sources);
|
||||||
visitor.visit(m_crypto);
|
visitor.visit(m_crypto);
|
||||||
|
visitor.visit(m_resource_timing_secondary_buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
void WindowOrWorkerGlobalScopeMixin::finalize()
|
void WindowOrWorkerGlobalScopeMixin::finalize()
|
||||||
|
@ -472,7 +475,6 @@ void WindowOrWorkerGlobalScopeMixin::add_performance_entry(GC::Ref<PerformanceTi
|
||||||
tuple.performance_entry_buffer.append(new_entry);
|
tuple.performance_entry_buffer.append(new_entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void WindowOrWorkerGlobalScopeMixin::clear_performance_entry_buffer(Badge<HighResolutionTime::Performance>, FlyString const& entry_type)
|
void WindowOrWorkerGlobalScopeMixin::clear_performance_entry_buffer(Badge<HighResolutionTime::Performance>, FlyString const& entry_type)
|
||||||
{
|
{
|
||||||
auto& tuple = relevant_performance_entry_tuple(entry_type);
|
auto& tuple = relevant_performance_entry_tuple(entry_type);
|
||||||
|
@ -643,6 +645,102 @@ void WindowOrWorkerGlobalScopeMixin::queue_the_performance_observer_task()
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// https://w3c.github.io/resource-timing/#dfn-add-a-performanceresourcetiming-entry
|
||||||
|
void WindowOrWorkerGlobalScopeMixin::add_resource_timing_entry(Badge<ResourceTiming::PerformanceResourceTiming>, GC::Ref<ResourceTiming::PerformanceResourceTiming> entry)
|
||||||
|
{
|
||||||
|
// 1. If can add resource timing entry returns true and resource timing buffer full event pending flag is false,
|
||||||
|
// run the following substeps:
|
||||||
|
if (can_add_resource_timing_entry() && !m_resource_timing_buffer_full_event_pending) {
|
||||||
|
// a. Add new entry to the performance entry buffer.
|
||||||
|
// b. Increase resource timing buffer current size by 1.
|
||||||
|
add_performance_entry(entry);
|
||||||
|
|
||||||
|
// c. Return.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. If resource timing buffer full event pending flag is false, run the following substeps:
|
||||||
|
if (!m_resource_timing_buffer_full_event_pending) {
|
||||||
|
// a. Set resource timing buffer full event pending flag to true.
|
||||||
|
m_resource_timing_buffer_full_event_pending = true;
|
||||||
|
|
||||||
|
// b. Queue a task on the performance timeline task source to run fire a buffer full event.
|
||||||
|
HTML::queue_a_task(HTML::Task::Source::PerformanceTimeline, nullptr, nullptr, GC::create_function(this_impl().heap(), [this] {
|
||||||
|
fire_resource_timing_buffer_full_event();
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. Add new entry to the resource timing secondary buffer.
|
||||||
|
// 4. Increase resource timing secondary buffer current size by 1.
|
||||||
|
m_resource_timing_secondary_buffer.append(entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://w3c.github.io/resource-timing/#dfn-can-add-resource-timing-entry
|
||||||
|
bool WindowOrWorkerGlobalScopeMixin::can_add_resource_timing_entry()
|
||||||
|
{
|
||||||
|
// 1. If resource timing buffer current size is smaller than resource timing buffer size limit, return true.
|
||||||
|
// 2. Return false.
|
||||||
|
return resource_timing_buffer_current_size() < m_resource_timing_buffer_size_limit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://w3c.github.io/resource-timing/#dfn-resource-timing-buffer-current-size
|
||||||
|
size_t WindowOrWorkerGlobalScopeMixin::resource_timing_buffer_current_size()
|
||||||
|
{
|
||||||
|
// A resource timing buffer current size which is initially 0.
|
||||||
|
auto resource_timing_tuple = relevant_performance_entry_tuple(PerformanceTimeline::EntryTypes::resource);
|
||||||
|
return resource_timing_tuple.performance_entry_buffer.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://w3c.github.io/resource-timing/#dfn-fire-a-buffer-full-event
|
||||||
|
void WindowOrWorkerGlobalScopeMixin::fire_resource_timing_buffer_full_event()
|
||||||
|
{
|
||||||
|
// 1. While resource timing secondary buffer is not empty, run the following substeps:
|
||||||
|
while (!m_resource_timing_secondary_buffer.is_empty()) {
|
||||||
|
// 1. Let number of excess entries before be resource timing secondary buffer current size.
|
||||||
|
auto number_of_excess_entries_before = m_resource_timing_secondary_buffer.size();
|
||||||
|
|
||||||
|
// 2. If can add resource timing entry returns false, then fire an event named resourcetimingbufferfull at the Performance object.
|
||||||
|
if (!can_add_resource_timing_entry()) {
|
||||||
|
auto full_event = DOM::Event::create(this_impl().realm(), PerformanceTimeline::EventNames::resourcetimingbufferfull);
|
||||||
|
performance()->dispatch_event(full_event);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. Run copy secondary buffer.
|
||||||
|
copy_resource_timing_secondary_buffer();
|
||||||
|
|
||||||
|
// 4. Let number of excess entries after be resource timing secondary buffer current size.
|
||||||
|
auto number_of_excess_entries_after = m_resource_timing_secondary_buffer.size();
|
||||||
|
|
||||||
|
// 5. If number of excess entries before is lower than or equals number of excess entries after, then remove
|
||||||
|
// all entries from resource timing secondary buffer, set resource timing secondary buffer current size to
|
||||||
|
// 0, and abort these steps.
|
||||||
|
if (number_of_excess_entries_before <= number_of_excess_entries_after) {
|
||||||
|
m_resource_timing_secondary_buffer.clear();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. Set resource timing buffer full event pending flag to false.
|
||||||
|
m_resource_timing_buffer_full_event_pending = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://w3c.github.io/resource-timing/#dfn-copy-secondary-buffer
|
||||||
|
void WindowOrWorkerGlobalScopeMixin::copy_resource_timing_secondary_buffer()
|
||||||
|
{
|
||||||
|
// 1. While resource timing secondary buffer is not empty and can add resource timing entry returns true,
|
||||||
|
// run the following substeps:
|
||||||
|
while (!m_resource_timing_secondary_buffer.is_empty() && can_add_resource_timing_entry()) {
|
||||||
|
// 1. Let entry be the oldest PerformanceResourceTiming in resource timing secondary buffer.
|
||||||
|
// 2. Add entry to the end of performance entry buffer.
|
||||||
|
// 3. Increment resource timing buffer current size by 1.
|
||||||
|
// 4. Remove entry from resource timing secondary buffer.
|
||||||
|
// 5. Decrement resource timing secondary buffer current size by 1.
|
||||||
|
auto entry = m_resource_timing_secondary_buffer.take_first();
|
||||||
|
auto& resource_tuple = relevant_performance_entry_tuple(PerformanceTimeline::EntryTypes::resource);
|
||||||
|
resource_tuple.performance_entry_buffer.append(entry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void WindowOrWorkerGlobalScopeMixin::register_event_source(Badge<EventSource>, GC::Ref<EventSource> event_source)
|
void WindowOrWorkerGlobalScopeMixin::register_event_source(Badge<EventSource>, GC::Ref<EventSource> event_source)
|
||||||
{
|
{
|
||||||
m_registered_event_sources.set(event_source);
|
m_registered_event_sources.set(event_source);
|
||||||
|
|
|
@ -66,6 +66,9 @@ public:
|
||||||
|
|
||||||
void queue_the_performance_observer_task();
|
void queue_the_performance_observer_task();
|
||||||
|
|
||||||
|
void set_resource_timing_buffer_size_limit(Badge<HighResolutionTime::Performance>, u32 value) { m_resource_timing_buffer_size_limit = value; }
|
||||||
|
void add_resource_timing_entry(Badge<ResourceTiming::PerformanceResourceTiming>, GC::Ref<ResourceTiming::PerformanceResourceTiming> entry);
|
||||||
|
|
||||||
void register_event_source(Badge<EventSource>, GC::Ref<EventSource>);
|
void register_event_source(Badge<EventSource>, GC::Ref<EventSource>);
|
||||||
void unregister_event_source(Badge<EventSource>, GC::Ref<EventSource>);
|
void unregister_event_source(Badge<EventSource>, GC::Ref<EventSource>);
|
||||||
void forcibly_close_all_event_sources();
|
void forcibly_close_all_event_sources();
|
||||||
|
@ -112,6 +115,11 @@ private:
|
||||||
|
|
||||||
GC::Ref<WebIDL::Promise> create_image_bitmap_impl(ImageBitmapSource& image, Optional<WebIDL::Long> sx, Optional<WebIDL::Long> sy, Optional<WebIDL::Long> sw, Optional<WebIDL::Long> sh, Optional<ImageBitmapOptions>& options) const;
|
GC::Ref<WebIDL::Promise> create_image_bitmap_impl(ImageBitmapSource& image, Optional<WebIDL::Long> sx, Optional<WebIDL::Long> sy, Optional<WebIDL::Long> sw, Optional<WebIDL::Long> sh, Optional<ImageBitmapOptions>& options) const;
|
||||||
|
|
||||||
|
size_t resource_timing_buffer_current_size();
|
||||||
|
bool can_add_resource_timing_entry();
|
||||||
|
void fire_resource_timing_buffer_full_event();
|
||||||
|
void copy_resource_timing_secondary_buffer();
|
||||||
|
|
||||||
IDAllocator m_timer_id_allocator;
|
IDAllocator m_timer_id_allocator;
|
||||||
HashMap<int, GC::Ref<Timer>> m_timers;
|
HashMap<int, GC::Ref<Timer>> m_timers;
|
||||||
|
|
||||||
|
@ -141,6 +149,24 @@ private:
|
||||||
bool m_error_reporting_mode { false };
|
bool m_error_reporting_mode { false };
|
||||||
|
|
||||||
WebSockets::WebSocket::List m_registered_web_sockets;
|
WebSockets::WebSocket::List m_registered_web_sockets;
|
||||||
|
|
||||||
|
// https://w3c.github.io/resource-timing/#sec-extensions-performance-interface
|
||||||
|
// Each ECMAScript global environment has:
|
||||||
|
// https://w3c.github.io/resource-timing/#dfn-resource-timing-buffer-size-limit
|
||||||
|
// A resource timing buffer size limit which should initially be 250 or greater.
|
||||||
|
// The recommended minimum number of PerformanceResourceTiming objects is 250, though this may be changed by the
|
||||||
|
// user agent. setResourceTimingBufferSize can be called to request a change to this limit.
|
||||||
|
u32 m_resource_timing_buffer_size_limit { 250 };
|
||||||
|
|
||||||
|
// https://w3c.github.io/resource-timing/#dfn-resource-timing-buffer-full-event-pending-flag
|
||||||
|
// A resource timing buffer full event pending flag which is initially false.
|
||||||
|
bool m_resource_timing_buffer_full_event_pending { false };
|
||||||
|
|
||||||
|
// https://w3c.github.io/resource-timing/#dfn-resource-timing-secondary-buffer-current-size
|
||||||
|
// A resource timing secondary buffer current size which is initially 0.
|
||||||
|
// https://w3c.github.io/resource-timing/#dfn-resource-timing-secondary-buffer
|
||||||
|
// A resource timing secondary buffer to store PerformanceResourceTiming objects that is initially empty.
|
||||||
|
Vector<GC::Ref<ResourceTiming::PerformanceResourceTiming>> m_resource_timing_secondary_buffer;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
#include <LibWeb/NavigationTiming/PerformanceNavigation.h>
|
#include <LibWeb/NavigationTiming/PerformanceNavigation.h>
|
||||||
#include <LibWeb/NavigationTiming/PerformanceTiming.h>
|
#include <LibWeb/NavigationTiming/PerformanceTiming.h>
|
||||||
#include <LibWeb/PerformanceTimeline/EntryTypes.h>
|
#include <LibWeb/PerformanceTimeline/EntryTypes.h>
|
||||||
|
#include <LibWeb/PerformanceTimeline/EventNames.h>
|
||||||
|
|
||||||
namespace Web::HighResolutionTime {
|
namespace Web::HighResolutionTime {
|
||||||
|
|
||||||
|
@ -330,6 +331,35 @@ void Performance::clear_measures(Optional<String> measure_name)
|
||||||
// 3. Return undefined.
|
// 3. Return undefined.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// https://w3c.github.io/resource-timing/#dom-performance-clearresourcetimings
|
||||||
|
void Performance::clear_resource_timings()
|
||||||
|
{
|
||||||
|
// 1. Remove all PerformanceResourceTiming objects in the performance entry buffer.
|
||||||
|
// 2. Set resource timing buffer current size to 0.
|
||||||
|
window_or_worker().clear_performance_entry_buffer({}, PerformanceTimeline::EntryTypes::resource);
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://w3c.github.io/resource-timing/#dom-performance-setresourcetimingbuffersize
|
||||||
|
void Performance::set_resource_timing_buffer_size(u32 max_size)
|
||||||
|
{
|
||||||
|
// 1. Set resource timing buffer size limit to the maxSize parameter. If the maxSize parameter is less than
|
||||||
|
// resource timing buffer current size, no PerformanceResourceTiming objects are to be removed from the
|
||||||
|
// performance entry buffer.
|
||||||
|
window_or_worker().set_resource_timing_buffer_size_limit({}, max_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://w3c.github.io/resource-timing/#dom-performance-onresourcetimingbufferfull
|
||||||
|
void Performance::set_onresourcetimingbufferfull(WebIDL::CallbackType* event_handler)
|
||||||
|
{
|
||||||
|
set_event_handler_attribute(PerformanceTimeline::EventNames::resourcetimingbufferfull, event_handler);
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://w3c.github.io/resource-timing/#dom-performance-onresourcetimingbufferfull
|
||||||
|
WebIDL::CallbackType* Performance::onresourcetimingbufferfull()
|
||||||
|
{
|
||||||
|
return event_handler_attribute(PerformanceTimeline::EventNames::resourcetimingbufferfull);
|
||||||
|
}
|
||||||
|
|
||||||
// https://www.w3.org/TR/performance-timeline/#getentries-method
|
// https://www.w3.org/TR/performance-timeline/#getentries-method
|
||||||
WebIDL::ExceptionOr<Vector<GC::Root<PerformanceTimeline::PerformanceEntry>>> Performance::get_entries() const
|
WebIDL::ExceptionOr<Vector<GC::Root<PerformanceTimeline::PerformanceEntry>>> Performance::get_entries() const
|
||||||
{
|
{
|
||||||
|
|
|
@ -30,6 +30,11 @@ public:
|
||||||
WebIDL::ExceptionOr<GC::Ref<UserTiming::PerformanceMeasure>> measure(String const& measure_name, Variant<String, UserTiming::PerformanceMeasureOptions> const& start_or_measure_options, Optional<String> end_mark);
|
WebIDL::ExceptionOr<GC::Ref<UserTiming::PerformanceMeasure>> measure(String const& measure_name, Variant<String, UserTiming::PerformanceMeasureOptions> const& start_or_measure_options, Optional<String> end_mark);
|
||||||
void clear_measures(Optional<String> measure_name);
|
void clear_measures(Optional<String> measure_name);
|
||||||
|
|
||||||
|
void clear_resource_timings();
|
||||||
|
void set_resource_timing_buffer_size(u32 max_size);
|
||||||
|
void set_onresourcetimingbufferfull(WebIDL::CallbackType*);
|
||||||
|
WebIDL::CallbackType* onresourcetimingbufferfull();
|
||||||
|
|
||||||
WebIDL::ExceptionOr<Vector<GC::Root<PerformanceTimeline::PerformanceEntry>>> get_entries() const;
|
WebIDL::ExceptionOr<Vector<GC::Root<PerformanceTimeline::PerformanceEntry>>> get_entries() const;
|
||||||
WebIDL::ExceptionOr<Vector<GC::Root<PerformanceTimeline::PerformanceEntry>>> get_entries_by_type(String const& type) const;
|
WebIDL::ExceptionOr<Vector<GC::Root<PerformanceTimeline::PerformanceEntry>>> get_entries_by_type(String const& type) const;
|
||||||
WebIDL::ExceptionOr<Vector<GC::Root<PerformanceTimeline::PerformanceEntry>>> get_entries_by_name(String const& name, Optional<String> type) const;
|
WebIDL::ExceptionOr<Vector<GC::Root<PerformanceTimeline::PerformanceEntry>>> get_entries_by_name(String const& name, Optional<String> type) const;
|
||||||
|
|
|
@ -23,6 +23,12 @@ interface Performance : EventTarget {
|
||||||
PerformanceMeasure measure(DOMString measureName, optional (DOMString or PerformanceMeasureOptions) startOrMeasureOptions = {}, optional DOMString endMark);
|
PerformanceMeasure measure(DOMString measureName, optional (DOMString or PerformanceMeasureOptions) startOrMeasureOptions = {}, optional DOMString endMark);
|
||||||
undefined clearMeasures(optional DOMString measureName);
|
undefined clearMeasures(optional DOMString measureName);
|
||||||
|
|
||||||
|
// https://w3c.github.io/resource-timing/#sec-extensions-performance-interface
|
||||||
|
// "Resource Timing" extensions to the Performance interface
|
||||||
|
undefined clearResourceTimings();
|
||||||
|
undefined setResourceTimingBufferSize(unsigned long maxSize);
|
||||||
|
attribute EventHandler onresourcetimingbufferfull;
|
||||||
|
|
||||||
// https://www.w3.org/TR/performance-timeline/#extensions-to-the-performance-interface
|
// https://www.w3.org/TR/performance-timeline/#extensions-to-the-performance-interface
|
||||||
// "Performance Timeline" extensions to the Performance interface
|
// "Performance Timeline" extensions to the Performance interface
|
||||||
PerformanceEntryList getEntries();
|
PerformanceEntryList getEntries();
|
||||||
|
|
|
@ -9,8 +9,9 @@
|
||||||
namespace Web::HighResolutionTime {
|
namespace Web::HighResolutionTime {
|
||||||
|
|
||||||
// Please keep these in alphabetical order based on the entry type :^)
|
// Please keep these in alphabetical order based on the entry type :^)
|
||||||
#define ENUMERATE_SUPPORTED_PERFORMANCE_ENTRY_TYPES \
|
#define ENUMERATE_SUPPORTED_PERFORMANCE_ENTRY_TYPES \
|
||||||
__ENUMERATE_SUPPORTED_PERFORMANCE_ENTRY_TYPES(PerformanceTimeline::EntryTypes::mark, UserTiming::PerformanceMark) \
|
__ENUMERATE_SUPPORTED_PERFORMANCE_ENTRY_TYPES(PerformanceTimeline::EntryTypes::mark, UserTiming::PerformanceMark) \
|
||||||
__ENUMERATE_SUPPORTED_PERFORMANCE_ENTRY_TYPES(PerformanceTimeline::EntryTypes::measure, UserTiming::PerformanceMeasure)
|
__ENUMERATE_SUPPORTED_PERFORMANCE_ENTRY_TYPES(PerformanceTimeline::EntryTypes::measure, UserTiming::PerformanceMeasure) \
|
||||||
|
__ENUMERATE_SUPPORTED_PERFORMANCE_ENTRY_TYPES(PerformanceTimeline::EntryTypes::resource, ResourceTiming::PerformanceResourceTiming)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
16
Libraries/LibWeb/PerformanceTimeline/EventNames.cpp
Normal file
16
Libraries/LibWeb/PerformanceTimeline/EventNames.cpp
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2025, Luke Wilde <luke@ladybird.org>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <LibWeb/PerformanceTimeline/EventNames.h>
|
||||||
|
|
||||||
|
namespace Web::PerformanceTimeline::EventNames {
|
||||||
|
|
||||||
|
#define __ENUMERATE_PERFORMANCE_TIMELINE_EVENT(name) \
|
||||||
|
FlyString name = #name##_fly_string;
|
||||||
|
ENUMERATE_PERFORMANCE_TIMELINE_EVENTS
|
||||||
|
#undef __ENUMERATE_PERFORMANCE_TIMELINE_EVENT
|
||||||
|
|
||||||
|
}
|
20
Libraries/LibWeb/PerformanceTimeline/EventNames.h
Normal file
20
Libraries/LibWeb/PerformanceTimeline/EventNames.h
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2025, Luke Wilde <luke@ladybird.org>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <AK/FlyString.h>
|
||||||
|
|
||||||
|
namespace Web::PerformanceTimeline::EventNames {
|
||||||
|
|
||||||
|
#define ENUMERATE_PERFORMANCE_TIMELINE_EVENTS \
|
||||||
|
__ENUMERATE_PERFORMANCE_TIMELINE_EVENT(resourcetimingbufferfull)
|
||||||
|
|
||||||
|
#define __ENUMERATE_PERFORMANCE_TIMELINE_EVENT(name) extern FlyString name;
|
||||||
|
ENUMERATE_PERFORMANCE_TIMELINE_EVENTS
|
||||||
|
#undef __ENUMERATE_PERFORMANCE_TIMELINE_EVENT
|
||||||
|
|
||||||
|
}
|
340
Libraries/LibWeb/ResourceTiming/PerformanceResourceTiming.cpp
Normal file
340
Libraries/LibWeb/ResourceTiming/PerformanceResourceTiming.cpp
Normal file
|
@ -0,0 +1,340 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2025, Luke Wilde <lukew@serenityos.org>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <LibWeb/Bindings/Intrinsics.h>
|
||||||
|
#include <LibWeb/Fetch/Infrastructure/FetchTimingInfo.h>
|
||||||
|
#include <LibWeb/HTML/WindowOrWorkerGlobalScope.h>
|
||||||
|
#include <LibWeb/HighResolutionTime/TimeOrigin.h>
|
||||||
|
#include <LibWeb/PerformanceTimeline/EntryTypes.h>
|
||||||
|
#include <LibWeb/ResourceTiming/PerformanceResourceTiming.h>
|
||||||
|
|
||||||
|
namespace Web::ResourceTiming {
|
||||||
|
|
||||||
|
GC_DEFINE_ALLOCATOR(PerformanceResourceTiming);
|
||||||
|
|
||||||
|
PerformanceResourceTiming::PerformanceResourceTiming(JS::Realm& realm, String const& name, HighResolutionTime::DOMHighResTimeStamp start_time, HighResolutionTime::DOMHighResTimeStamp duration, GC::Ref<Fetch::Infrastructure::FetchTimingInfo> timing_info)
|
||||||
|
: PerformanceTimeline::PerformanceEntry(realm, name, start_time, duration)
|
||||||
|
, m_timing_info(timing_info)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
PerformanceResourceTiming::~PerformanceResourceTiming() = default;
|
||||||
|
|
||||||
|
// https://w3c.github.io/resource-timing/#dfn-entrytype
|
||||||
|
FlyString const& PerformanceResourceTiming::entry_type() const
|
||||||
|
{
|
||||||
|
// entryType
|
||||||
|
// The entryType getter steps are to return the DOMString "resource".
|
||||||
|
return PerformanceTimeline::EntryTypes::resource;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PerformanceResourceTiming::initialize(JS::Realm& realm)
|
||||||
|
{
|
||||||
|
Base::initialize(realm);
|
||||||
|
WEB_SET_PROTOTYPE_FOR_INTERFACE(PerformanceResourceTiming);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PerformanceResourceTiming::visit_edges(JS::Cell::Visitor& visitor)
|
||||||
|
{
|
||||||
|
Base::visit_edges(visitor);
|
||||||
|
visitor.visit(m_timing_info);
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://w3c.github.io/resource-timing/#dfn-convert-fetch-timestamp
|
||||||
|
HighResolutionTime::DOMHighResTimeStamp convert_fetch_timestamp(HighResolutionTime::DOMHighResTimeStamp time_stamp, JS::Object const& global)
|
||||||
|
{
|
||||||
|
// 1. If ts is zero, return zero.
|
||||||
|
if (time_stamp == 0.0)
|
||||||
|
return 0.0;
|
||||||
|
|
||||||
|
// 2. Otherwise, return the relative high resolution coarse time given ts and global.
|
||||||
|
return HighResolutionTime::relative_high_resolution_coarsen_time(time_stamp, global);
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://w3c.github.io/resource-timing/#dfn-mark-resource-timing
|
||||||
|
void PerformanceResourceTiming::mark_resource_timing(GC::Ref<Fetch::Infrastructure::FetchTimingInfo> timing_info, String const& requested_url, FlyString const& initiator_type, JS::Object& global, Optional<Fetch::Infrastructure::Response::CacheState> const& cache_mode, Fetch::Infrastructure::Response::BodyInfo body_info, Fetch::Infrastructure::Status response_status, FlyString delivery_type)
|
||||||
|
{
|
||||||
|
// 1. Create a PerformanceResourceTiming object entry in global's realm.
|
||||||
|
auto& window_or_worker = as<HTML::WindowOrWorkerGlobalScopeMixin>(global);
|
||||||
|
auto& realm = window_or_worker.this_impl().realm();
|
||||||
|
|
||||||
|
// https://w3c.github.io/resource-timing/#dfn-name
|
||||||
|
// name
|
||||||
|
// The name getter steps are to return this's requested URL.
|
||||||
|
|
||||||
|
// https://w3c.github.io/resource-timing/#dfn-starttime
|
||||||
|
// startTime
|
||||||
|
// The startTime getter steps are to convert fetch timestamp for this's timing info's start time and this's relevant global object.
|
||||||
|
|
||||||
|
// https://w3c.github.io/resource-timing/#dfn-duration
|
||||||
|
// duration
|
||||||
|
// The duration getter steps are to return this's timing info's end time minus this's timing info's start time.
|
||||||
|
auto converted_start_time = convert_fetch_timestamp(timing_info->start_time(), global);
|
||||||
|
auto converted_end_time = convert_fetch_timestamp(timing_info->end_time(), global);
|
||||||
|
auto entry = realm.create<PerformanceResourceTiming>(realm, requested_url, converted_start_time, converted_end_time - converted_start_time, timing_info);
|
||||||
|
|
||||||
|
// Setup the resource timing entry for entry, given initiatorType, requestedURL, timingInfo, cacheMode, bodyInfo, responseStatus, and deliveryType.
|
||||||
|
entry->setup_the_resource_timing_entry(initiator_type, requested_url, timing_info, cache_mode, move(body_info), response_status, delivery_type);
|
||||||
|
|
||||||
|
// 3. Queue entry.
|
||||||
|
window_or_worker.queue_performance_entry(entry);
|
||||||
|
|
||||||
|
// 4. Add entry to global's performance entry buffer.
|
||||||
|
window_or_worker.add_resource_timing_entry({}, entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://www.w3.org/TR/resource-timing/#dfn-setup-the-resource-timing-entry
|
||||||
|
void PerformanceResourceTiming::setup_the_resource_timing_entry(FlyString const& initiator_type, String const& requested_url, GC::Ref<Fetch::Infrastructure::FetchTimingInfo> timing_info, Optional<Fetch::Infrastructure::Response::CacheState> const& cache_mode, Fetch::Infrastructure::Response::BodyInfo body_info, Fetch::Infrastructure::Status response_status, FlyString delivery_type)
|
||||||
|
{
|
||||||
|
// 2. Setup the resource timing entry for entry, given initiatorType, requestedURL, timingInfo, cacheMode, bodyInfo, responseStatus, and deliveryType.
|
||||||
|
// https://w3c.github.io/resource-timing/#dfn-setup-the-resource-timing-entry
|
||||||
|
|
||||||
|
// 1. Assert that cacheMode is the empty string, "local", or "validated".
|
||||||
|
|
||||||
|
// 2. Set entry's initiator type to initiatorType.
|
||||||
|
m_initiator_type = initiator_type;
|
||||||
|
|
||||||
|
// 3. Set entry's requested URL to requestedURL.
|
||||||
|
m_requested_url = requested_url;
|
||||||
|
|
||||||
|
// 4. Set entry's timing info to timingInfo.
|
||||||
|
m_timing_info = timing_info;
|
||||||
|
|
||||||
|
// 5. Set entry's response body info to bodyInfo.
|
||||||
|
m_response_body_info = move(body_info);
|
||||||
|
|
||||||
|
// 6. Set entry's cache mode to cacheMode.
|
||||||
|
m_cache_mode = cache_mode;
|
||||||
|
|
||||||
|
// 7. Set entry's response status to responseStatus.
|
||||||
|
m_response_status = response_status;
|
||||||
|
|
||||||
|
// 8. If deliveryType is the empty string and cacheMode is not, then set deliveryType to "cache".
|
||||||
|
if (delivery_type.is_empty() && cache_mode.has_value())
|
||||||
|
delivery_type = "cache"_fly_string;
|
||||||
|
|
||||||
|
// 9. Set entry's delivery type to deliveryType.
|
||||||
|
m_delivery_type = delivery_type;
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://w3c.github.io/resource-timing/#dom-performanceresourcetiming-nexthopprotocol
|
||||||
|
FlyString PerformanceResourceTiming::next_hop_protocol() const
|
||||||
|
{
|
||||||
|
// The nextHopProtocol getter steps are to isomorphic decode this's timing info's final connection timing info's
|
||||||
|
// ALPN negotiated protocol. See Recording connection timing info for more info.
|
||||||
|
// NOTE: "final connection timing info" can be null, e.g. if this is the timing of a cross-origin resource and
|
||||||
|
// the Timing-Allow-Origin check fails. We return empty string in this case.
|
||||||
|
if (!m_timing_info->final_connection_timing_info().has_value())
|
||||||
|
return ""_fly_string;
|
||||||
|
|
||||||
|
return m_timing_info->final_connection_timing_info()->alpn_negotiated_protocol;
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://w3c.github.io/resource-timing/#dom-performanceresourcetiming-workerstart
|
||||||
|
HighResolutionTime::DOMHighResTimeStamp PerformanceResourceTiming::worker_start() const
|
||||||
|
{
|
||||||
|
// The workerStart getter steps are to convert fetch timestamp for this's timing info's final service worker start
|
||||||
|
// time and the relevant global object for this. See HTTP fetch for more info.
|
||||||
|
return convert_fetch_timestamp(m_timing_info->final_service_worker_start_time(), HTML::relevant_global_object(*this));
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://w3c.github.io/resource-timing/#dom-performanceresourcetiming-redirectstart
|
||||||
|
HighResolutionTime::DOMHighResTimeStamp PerformanceResourceTiming::redirect_start() const
|
||||||
|
{
|
||||||
|
// The redirectStart getter steps are to convert fetch timestamp for this's timing info's redirect start time and
|
||||||
|
// the relevant global object for this. See HTTP-redirect fetch for more info.
|
||||||
|
return convert_fetch_timestamp(m_timing_info->redirect_start_time(), HTML::relevant_global_object(*this));
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://w3c.github.io/resource-timing/#dom-performanceresourcetiming-redirectend
|
||||||
|
HighResolutionTime::DOMHighResTimeStamp PerformanceResourceTiming::redirect_end() const
|
||||||
|
{
|
||||||
|
// The redirectEnd getter steps are to convert fetch timestamp for this's timing info's redirect end time and the
|
||||||
|
// relevant global object for this. See HTTP-redirect fetch for more info.
|
||||||
|
return convert_fetch_timestamp(m_timing_info->redirect_end_time(), HTML::relevant_global_object(*this));
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://w3c.github.io/resource-timing/#dom-performanceresourcetiming-fetchstart
|
||||||
|
HighResolutionTime::DOMHighResTimeStamp PerformanceResourceTiming::fetch_start() const
|
||||||
|
{
|
||||||
|
// The fetchStart getter steps are to convert fetch timestamp for this's timing info's post-redirect start time and
|
||||||
|
// the relevant global object for this. See HTTP fetch for more info.
|
||||||
|
return convert_fetch_timestamp(m_timing_info->post_redirect_start_time(), HTML::relevant_global_object(*this));
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://w3c.github.io/resource-timing/#dom-performanceresourcetiming-domainlookupstart
|
||||||
|
HighResolutionTime::DOMHighResTimeStamp PerformanceResourceTiming::domain_lookup_start() const
|
||||||
|
{
|
||||||
|
// The domainLookupStart getter steps are to convert fetch timestamp for this's timing info's final connection
|
||||||
|
// timing info's domain lookup start time and the relevant global object for this. See Recording connection timing
|
||||||
|
// info for more info.
|
||||||
|
// NOTE: "final connection timing info" can be null, e.g. if this is the timing of a cross-origin resource and
|
||||||
|
// the Timing-Allow-Origin check fails. We return 0.0 in this case.
|
||||||
|
if (!m_timing_info->final_connection_timing_info().has_value())
|
||||||
|
return 0.0;
|
||||||
|
|
||||||
|
return convert_fetch_timestamp(m_timing_info->final_connection_timing_info()->domain_lookup_start_time, HTML::relevant_global_object(*this));
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://w3c.github.io/resource-timing/#dom-performanceresourcetiming-domainlookupend
|
||||||
|
HighResolutionTime::DOMHighResTimeStamp PerformanceResourceTiming::domain_lookup_end() const
|
||||||
|
{
|
||||||
|
// The domainLookupEnd getter steps are to convert fetch timestamp for this's timing info's final connection timing
|
||||||
|
// info's domain lookup end time and the relevant global object for this. See Recording connection timing info for
|
||||||
|
// more info.
|
||||||
|
// NOTE: "final connection timing info" can be null, e.g. if this is the timing of a cross-origin resource and
|
||||||
|
// the Timing-Allow-Origin check fails. We return 0.0 in this case.
|
||||||
|
if (!m_timing_info->final_connection_timing_info().has_value())
|
||||||
|
return 0.0;
|
||||||
|
|
||||||
|
return convert_fetch_timestamp(m_timing_info->final_connection_timing_info()->domain_lookup_end_time, HTML::relevant_global_object(*this));
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://w3c.github.io/resource-timing/#dom-performanceresourcetiming-connectstart
|
||||||
|
HighResolutionTime::DOMHighResTimeStamp PerformanceResourceTiming::connect_start() const
|
||||||
|
{
|
||||||
|
// The connectStart getter steps are to convert fetch timestamp for this's timing info's final connection timing
|
||||||
|
// info's connection start time and the relevant global object for this. See Recording connection timing info for
|
||||||
|
// more info.
|
||||||
|
// NOTE: "final connection timing info" can be null, e.g. if this is the timing of a cross-origin resource and
|
||||||
|
// the Timing-Allow-Origin check fails. We return 0.0 in this case.
|
||||||
|
if (!m_timing_info->final_connection_timing_info().has_value())
|
||||||
|
return 0.0;
|
||||||
|
|
||||||
|
return convert_fetch_timestamp(m_timing_info->final_connection_timing_info()->connection_start_time, HTML::relevant_global_object(*this));
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://w3c.github.io/resource-timing/#dom-performanceresourcetiming-connectend
|
||||||
|
HighResolutionTime::DOMHighResTimeStamp PerformanceResourceTiming::connect_end() const
|
||||||
|
{
|
||||||
|
// The connectEnd getter steps are to convert fetch timestamp for this's timing info's final connection timing
|
||||||
|
// info's connection end time and the relevant global object for this. See Recording connection timing info for
|
||||||
|
// more info.
|
||||||
|
// NOTE: "final connection timing info" can be null, e.g. if this is the timing of a cross-origin resource and
|
||||||
|
// the Timing-Allow-Origin check fails. We return 0.0 in this case.
|
||||||
|
if (!m_timing_info->final_connection_timing_info().has_value())
|
||||||
|
return 0.0;
|
||||||
|
|
||||||
|
return convert_fetch_timestamp(m_timing_info->final_connection_timing_info()->connection_end_time, HTML::relevant_global_object(*this));
|
||||||
|
}
|
||||||
|
|
||||||
|
HighResolutionTime::DOMHighResTimeStamp PerformanceResourceTiming::secure_connection_start() const
|
||||||
|
{
|
||||||
|
// The secureConnectionStart getter steps are to convert fetch timestamp for this's timing info's final connection
|
||||||
|
// timing info's secure connection start time and the relevant global object for this. See Recording connection
|
||||||
|
// timing info for more info.
|
||||||
|
// NOTE: "final connection timing info" can be null, e.g. if this is the timing of a cross-origin resource and
|
||||||
|
// the Timing-Allow-Origin check fails. We return 0.0 in this case.
|
||||||
|
if (!m_timing_info->final_connection_timing_info().has_value())
|
||||||
|
return 0.0;
|
||||||
|
|
||||||
|
return convert_fetch_timestamp(m_timing_info->final_connection_timing_info()->secure_connection_start_time, HTML::relevant_global_object(*this));
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://w3c.github.io/resource-timing/#dom-performanceresourcetiming-requeststart
|
||||||
|
HighResolutionTime::DOMHighResTimeStamp PerformanceResourceTiming::request_start() const
|
||||||
|
{
|
||||||
|
// The requestStart getter steps are to convert fetch timestamp for this's timing info's final network-request
|
||||||
|
// start time and the relevant global object for this. See HTTP fetch for more info.
|
||||||
|
return convert_fetch_timestamp(m_timing_info->final_network_request_start_time(), HTML::relevant_global_object(*this));
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://w3c.github.io/resource-timing/#dom-performanceresourcetiming-finalresponseheadersstart
|
||||||
|
HighResolutionTime::DOMHighResTimeStamp PerformanceResourceTiming::final_response_headers_start() const
|
||||||
|
{
|
||||||
|
// The finalResponseHeadersStart getter steps are to convert fetch timestamp for this's timing info's final
|
||||||
|
// network-response start time and the relevant global object for this. See HTTP fetch for more info.
|
||||||
|
return convert_fetch_timestamp(m_timing_info->final_network_response_start_time(), HTML::relevant_global_object(*this));
|
||||||
|
}
|
||||||
|
|
||||||
|
HighResolutionTime::DOMHighResTimeStamp PerformanceResourceTiming::first_interim_response_start() const
|
||||||
|
{
|
||||||
|
// The firstInterimResponseStart getter steps are to convert fetch timestamp for this's timing info's first interim
|
||||||
|
// network-response start time and the relevant global object for this. See HTTP fetch for more info.
|
||||||
|
return convert_fetch_timestamp(m_timing_info->first_interim_network_response_start_time(), HTML::relevant_global_object(*this));
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://w3c.github.io/resource-timing/#dom-performanceresourcetiming-responsestart
|
||||||
|
HighResolutionTime::DOMHighResTimeStamp PerformanceResourceTiming::response_start() const
|
||||||
|
{
|
||||||
|
// The responseStart getter steps are to return this's firstInterimResponseStart if it is not 0;
|
||||||
|
// Otherwise this's finalResponseHeadersStart.
|
||||||
|
auto first_interim_response_start_time = first_interim_response_start();
|
||||||
|
if (first_interim_response_start_time != 0.0)
|
||||||
|
return first_interim_response_start_time;
|
||||||
|
|
||||||
|
return final_response_headers_start();
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://w3c.github.io/resource-timing/#dom-performanceresourcetiming-responseend
|
||||||
|
HighResolutionTime::DOMHighResTimeStamp PerformanceResourceTiming::response_end() const
|
||||||
|
{
|
||||||
|
// The responseEnd getter steps are to convert fetch timestamp for this's timing info's end time and the relevant
|
||||||
|
// global object for this. See fetch for more info.
|
||||||
|
return convert_fetch_timestamp(m_timing_info->end_time(), HTML::relevant_global_object(*this));
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://w3c.github.io/resource-timing/#dom-performanceresourcetiming-encodedbodysize
|
||||||
|
u64 PerformanceResourceTiming::encoded_body_size() const
|
||||||
|
{
|
||||||
|
// The encodedBodySize getter steps are to return this's resource info's encoded size.
|
||||||
|
return m_response_body_info.encoded_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://w3c.github.io/resource-timing/#dom-performanceresourcetiming-decodedbodysize
|
||||||
|
u64 PerformanceResourceTiming::decoded_body_size() const
|
||||||
|
{
|
||||||
|
// The decodedBodySize getter steps are to return this's resource info's decoded size.
|
||||||
|
return m_response_body_info.decoded_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://w3c.github.io/resource-timing/#dom-performanceresourcetiming-transfersize
|
||||||
|
u64 PerformanceResourceTiming::transfer_size() const
|
||||||
|
{
|
||||||
|
if (m_cache_mode.has_value()) {
|
||||||
|
// 1. If this's cache mode is "local", then return 0.
|
||||||
|
if (m_cache_mode.value() == Fetch::Infrastructure::Response::CacheState::Local)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
// 2. If this's cache mode is "validated", then return 300.
|
||||||
|
if (m_cache_mode.value() == Fetch::Infrastructure::Response::CacheState::Validated)
|
||||||
|
return 300;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. Return this's response body info's encoded size plus 300.
|
||||||
|
// Spec Note: The constant number added to transferSize replaces exposing the total byte size of the HTTP headers,
|
||||||
|
// as that may expose the presence of certain cookies. See this issue: https://github.com/w3c/resource-timing/issues/238
|
||||||
|
return m_response_body_info.encoded_size + 300;
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://w3c.github.io/resource-timing/#dom-performanceresourcetiming-responsestatus
|
||||||
|
Fetch::Infrastructure::Status PerformanceResourceTiming::response_status() const
|
||||||
|
{
|
||||||
|
// The responseStatus getter steps are to return this's response status.
|
||||||
|
// Spec Note: responseStatus is determined in Fetch. For a cross-origin no-cors request it would be 0 because the
|
||||||
|
// response would be an opaque filtered response.
|
||||||
|
return m_response_status;
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://w3c.github.io/resource-timing/#dom-performanceresourcetiming-renderblockingstatus
|
||||||
|
Bindings::RenderBlockingStatusType PerformanceResourceTiming::render_blocking_status() const
|
||||||
|
{
|
||||||
|
// The renderBlockingStatus getter steps are to return blocking if this's timing info's render-blocking is true;
|
||||||
|
// otherwise non-blocking.
|
||||||
|
if (m_timing_info->render_blocking())
|
||||||
|
return Bindings::RenderBlockingStatusType::Blocking;
|
||||||
|
|
||||||
|
return Bindings::RenderBlockingStatusType::NonBlocking;
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://w3c.github.io/resource-timing/#dom-performanceresourcetiming-contenttype
|
||||||
|
String const& PerformanceResourceTiming::content_type() const
|
||||||
|
{
|
||||||
|
// The contentType getter steps are to return this's resource info's content type.
|
||||||
|
return m_response_body_info.content_type;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
90
Libraries/LibWeb/ResourceTiming/PerformanceResourceTiming.h
Normal file
90
Libraries/LibWeb/ResourceTiming/PerformanceResourceTiming.h
Normal file
|
@ -0,0 +1,90 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2025, Luke Wilde <luke@ladybird.org>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <AK/FlyString.h>
|
||||||
|
#include <LibWeb/Bindings/PerformanceResourceTimingPrototype.h>
|
||||||
|
#include <LibWeb/Fetch/Infrastructure/HTTP/Requests.h>
|
||||||
|
#include <LibWeb/Fetch/Infrastructure/HTTP/Responses.h>
|
||||||
|
#include <LibWeb/PerformanceTimeline/PerformanceEntry.h>
|
||||||
|
|
||||||
|
namespace Web::ResourceTiming {
|
||||||
|
|
||||||
|
// https://w3c.github.io/resource-timing/#dom-performanceresourcetiming
|
||||||
|
class PerformanceResourceTiming : public PerformanceTimeline::PerformanceEntry {
|
||||||
|
WEB_PLATFORM_OBJECT(PerformanceResourceTiming, PerformanceTimeline::PerformanceEntry);
|
||||||
|
GC_DECLARE_ALLOCATOR(PerformanceResourceTiming);
|
||||||
|
|
||||||
|
public:
|
||||||
|
virtual ~PerformanceResourceTiming() override;
|
||||||
|
|
||||||
|
static void mark_resource_timing(GC::Ref<Fetch::Infrastructure::FetchTimingInfo> timing_info, String const& requested_url, FlyString const& initiator_type, JS::Object& global, Optional<Fetch::Infrastructure::Response::CacheState> const& cache_mode, Fetch::Infrastructure::Response::BodyInfo body_info, Fetch::Infrastructure::Status response_status, FlyString delivery_type = ""_fly_string);
|
||||||
|
|
||||||
|
// NOTE: These three functions are answered by the registry for the given entry type.
|
||||||
|
// https://w3c.github.io/timing-entrytypes-registry/#registry
|
||||||
|
|
||||||
|
// https://w3c.github.io/timing-entrytypes-registry/#dfn-availablefromtimeline
|
||||||
|
static PerformanceTimeline::AvailableFromTimeline available_from_timeline() { return PerformanceTimeline::AvailableFromTimeline::Yes; }
|
||||||
|
|
||||||
|
// https://w3c.github.io/timing-entrytypes-registry/#dfn-maxbuffersize
|
||||||
|
static Optional<u64> max_buffer_size() { return 250; }
|
||||||
|
|
||||||
|
// https://w3c.github.io/timing-entrytypes-registry/#dfn-should-add-entry
|
||||||
|
virtual PerformanceTimeline::ShouldAddEntry should_add_entry(Optional<PerformanceTimeline::PerformanceObserverInit const&> = {}) const override { return PerformanceTimeline::ShouldAddEntry::Yes; }
|
||||||
|
|
||||||
|
virtual FlyString const& entry_type() const override;
|
||||||
|
|
||||||
|
// https://w3c.github.io/resource-timing/#dom-performanceresourcetiming-initiatortype
|
||||||
|
FlyString const& initiator_type() const { return m_initiator_type; }
|
||||||
|
|
||||||
|
// https://w3c.github.io/resource-timing/#dom-performanceresourcetiming-deliverytype
|
||||||
|
FlyString const& delivery_type() const { return m_delivery_type; }
|
||||||
|
|
||||||
|
FlyString next_hop_protocol() const;
|
||||||
|
|
||||||
|
virtual HighResolutionTime::DOMHighResTimeStamp worker_start() const;
|
||||||
|
virtual HighResolutionTime::DOMHighResTimeStamp redirect_start() const;
|
||||||
|
virtual HighResolutionTime::DOMHighResTimeStamp redirect_end() const;
|
||||||
|
virtual HighResolutionTime::DOMHighResTimeStamp fetch_start() const;
|
||||||
|
HighResolutionTime::DOMHighResTimeStamp domain_lookup_start() const;
|
||||||
|
HighResolutionTime::DOMHighResTimeStamp domain_lookup_end() const;
|
||||||
|
HighResolutionTime::DOMHighResTimeStamp connect_start() const;
|
||||||
|
HighResolutionTime::DOMHighResTimeStamp connect_end() const;
|
||||||
|
HighResolutionTime::DOMHighResTimeStamp secure_connection_start() const;
|
||||||
|
HighResolutionTime::DOMHighResTimeStamp request_start() const;
|
||||||
|
HighResolutionTime::DOMHighResTimeStamp final_response_headers_start() const;
|
||||||
|
HighResolutionTime::DOMHighResTimeStamp first_interim_response_start() const;
|
||||||
|
HighResolutionTime::DOMHighResTimeStamp response_start() const;
|
||||||
|
HighResolutionTime::DOMHighResTimeStamp response_end() const;
|
||||||
|
u64 encoded_body_size() const;
|
||||||
|
u64 decoded_body_size() const;
|
||||||
|
u64 transfer_size() const;
|
||||||
|
Fetch::Infrastructure::Status response_status() const;
|
||||||
|
Bindings::RenderBlockingStatusType render_blocking_status() const;
|
||||||
|
String const& content_type() const;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
PerformanceResourceTiming(JS::Realm&, String const& name, HighResolutionTime::DOMHighResTimeStamp start_time, HighResolutionTime::DOMHighResTimeStamp duration, GC::Ref<Fetch::Infrastructure::FetchTimingInfo> timing_info);
|
||||||
|
|
||||||
|
void setup_the_resource_timing_entry(FlyString const& initiator_type, String const& requested_url, GC::Ref<Fetch::Infrastructure::FetchTimingInfo> timing_info, Optional<Fetch::Infrastructure::Response::CacheState> const& cache_mode, Fetch::Infrastructure::Response::BodyInfo body_info, Fetch::Infrastructure::Status response_status, FlyString delivery_type = ""_fly_string);
|
||||||
|
|
||||||
|
virtual void initialize(JS::Realm&) override;
|
||||||
|
virtual void visit_edges(JS::Cell::Visitor&) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
FlyString m_initiator_type;
|
||||||
|
String m_requested_url;
|
||||||
|
GC::Ref<Fetch::Infrastructure::FetchTimingInfo> m_timing_info;
|
||||||
|
Fetch::Infrastructure::Response::BodyInfo m_response_body_info;
|
||||||
|
Optional<Fetch::Infrastructure::Response::CacheState> m_cache_mode;
|
||||||
|
Fetch::Infrastructure::Status m_response_status;
|
||||||
|
FlyString m_delivery_type;
|
||||||
|
};
|
||||||
|
|
||||||
|
HighResolutionTime::DOMHighResTimeStamp convert_fetch_timestamp(HighResolutionTime::DOMHighResTimeStamp time_stamp, JS::Object const& global);
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,36 @@
|
||||||
|
#import <PerformanceTimeline/PerformanceEntry.idl>
|
||||||
|
|
||||||
|
// https://w3c.github.io/resource-timing/#dom-renderblockingstatustype
|
||||||
|
enum RenderBlockingStatusType {
|
||||||
|
"blocking",
|
||||||
|
"non-blocking"
|
||||||
|
};
|
||||||
|
|
||||||
|
// https://w3c.github.io/resource-timing/#dom-performanceresourcetiming
|
||||||
|
[Exposed=(Window,Worker)]
|
||||||
|
interface PerformanceResourceTiming : PerformanceEntry {
|
||||||
|
readonly attribute DOMString initiatorType;
|
||||||
|
readonly attribute DOMString deliveryType;
|
||||||
|
readonly attribute ByteString nextHopProtocol;
|
||||||
|
readonly attribute DOMHighResTimeStamp workerStart;
|
||||||
|
readonly attribute DOMHighResTimeStamp redirectStart;
|
||||||
|
readonly attribute DOMHighResTimeStamp redirectEnd;
|
||||||
|
readonly attribute DOMHighResTimeStamp fetchStart;
|
||||||
|
readonly attribute DOMHighResTimeStamp domainLookupStart;
|
||||||
|
readonly attribute DOMHighResTimeStamp domainLookupEnd;
|
||||||
|
readonly attribute DOMHighResTimeStamp connectStart;
|
||||||
|
readonly attribute DOMHighResTimeStamp connectEnd;
|
||||||
|
readonly attribute DOMHighResTimeStamp secureConnectionStart;
|
||||||
|
readonly attribute DOMHighResTimeStamp requestStart;
|
||||||
|
readonly attribute DOMHighResTimeStamp finalResponseHeadersStart;
|
||||||
|
readonly attribute DOMHighResTimeStamp firstInterimResponseStart;
|
||||||
|
readonly attribute DOMHighResTimeStamp responseStart;
|
||||||
|
readonly attribute DOMHighResTimeStamp responseEnd;
|
||||||
|
readonly attribute unsigned long long transferSize;
|
||||||
|
readonly attribute unsigned long long encodedBodySize;
|
||||||
|
readonly attribute unsigned long long decodedBodySize;
|
||||||
|
readonly attribute unsigned short responseStatus;
|
||||||
|
readonly attribute RenderBlockingStatusType renderBlockingStatus;
|
||||||
|
readonly attribute DOMString contentType;
|
||||||
|
[Default] object toJSON();
|
||||||
|
};
|
|
@ -952,8 +952,8 @@ WebIDL::ExceptionOr<void> XMLHttpRequest::send(Optional<DocumentOrXMLHttpRequest
|
||||||
m_fetch_controller->terminate();
|
m_fetch_controller->terminate();
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME: 7. Report timing for this’s fetch controller given the current global object.
|
// 7. Report timing for this’s fetch controller given the current global object.
|
||||||
// We cannot do this for responses that have a body yet, as we do not setup the stream that then calls processResponseEndOfBody in `fetch_response_handover`.
|
m_fetch_controller->report_timing(HTML::current_principal_global_object());
|
||||||
|
|
||||||
// 8. Run handle response end-of-body for this.
|
// 8. Run handle response end-of-body for this.
|
||||||
TRY(handle_response_end_of_body());
|
TRY(handle_response_end_of_body());
|
||||||
|
|
|
@ -285,6 +285,7 @@ libweb_js_bindings(RequestIdleCallback/IdleDeadline)
|
||||||
libweb_js_bindings(ResizeObserver/ResizeObserver)
|
libweb_js_bindings(ResizeObserver/ResizeObserver)
|
||||||
libweb_js_bindings(ResizeObserver/ResizeObserverEntry)
|
libweb_js_bindings(ResizeObserver/ResizeObserverEntry)
|
||||||
libweb_js_bindings(ResizeObserver/ResizeObserverSize)
|
libweb_js_bindings(ResizeObserver/ResizeObserverSize)
|
||||||
|
libweb_js_bindings(ResourceTiming/PerformanceResourceTiming)
|
||||||
libweb_js_bindings(ServiceWorker/ServiceWorker)
|
libweb_js_bindings(ServiceWorker/ServiceWorker)
|
||||||
libweb_js_bindings(ServiceWorker/ServiceWorkerContainer)
|
libweb_js_bindings(ServiceWorker/ServiceWorkerContainer)
|
||||||
libweb_js_bindings(ServiceWorker/ServiceWorkerGlobalScope GLOBAL)
|
libweb_js_bindings(ServiceWorker/ServiceWorkerGlobalScope GLOBAL)
|
||||||
|
|
|
@ -4523,6 +4523,7 @@ using namespace Web::NavigationTiming;
|
||||||
using namespace Web::PerformanceTimeline;
|
using namespace Web::PerformanceTimeline;
|
||||||
using namespace Web::RequestIdleCallback;
|
using namespace Web::RequestIdleCallback;
|
||||||
using namespace Web::ResizeObserver;
|
using namespace Web::ResizeObserver;
|
||||||
|
using namespace Web::ResourceTiming;
|
||||||
using namespace Web::Selection;
|
using namespace Web::Selection;
|
||||||
using namespace Web::ServiceWorker;
|
using namespace Web::ServiceWorker;
|
||||||
using namespace Web::StorageAPI;
|
using namespace Web::StorageAPI;
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
PerformanceObserver.supportedEntryTypes: mark,measure
|
PerformanceObserver.supportedEntryTypes: mark,measure,resource
|
||||||
PerformanceObserver.supportedEntryTypes instanceof Array: true
|
PerformanceObserver.supportedEntryTypes instanceof Array: true
|
||||||
Object.isFrozen(PerformanceObserver.supportedEntryTypes): true
|
Object.isFrozen(PerformanceObserver.supportedEntryTypes): true
|
||||||
PerformanceObserver.supportedEntryTypes === PerformanceObserver.supportedEntryTypes: true
|
PerformanceObserver.supportedEntryTypes === PerformanceObserver.supportedEntryTypes: true
|
||||||
|
|
|
@ -282,6 +282,7 @@ PerformanceMeasure
|
||||||
PerformanceNavigation
|
PerformanceNavigation
|
||||||
PerformanceObserver
|
PerformanceObserver
|
||||||
PerformanceObserverEntryList
|
PerformanceObserverEntryList
|
||||||
|
PerformanceResourceTiming
|
||||||
PerformanceTiming
|
PerformanceTiming
|
||||||
PeriodicWave
|
PeriodicWave
|
||||||
Plugin
|
Plugin
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue