1
0
Fork 0
mirror of https://github.com/LadybirdBrowser/ladybird.git synced 2025-06-08 05:27:14 +09:00
ladybird/Libraries/LibWeb/ResourceTiming/PerformanceResourceTiming.cpp
Andreas Kling a6dfc74e93 LibWeb: Only set prototype once for object with IDL interface
Before this change, we were going through the chain of base classes for
each IDL interface object and having them set the prototype to their
prototype.

Instead of doing that, reorder things so that we set the right prototype
immediately in Foo::initialize(), and then don't bother in all the base
class overrides.

This knocks off a ~1% profile item on Speedometer 3.
2025-04-20 18:43:11 +02:00

340 lines
18 KiB
C++

/*
* 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)
{
WEB_SET_PROTOTYPE_FOR_INTERFACE(PerformanceResourceTiming);
Base::initialize(realm);
}
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;
}
}