ladybird/Libraries/LibWeb/NavigationTiming/PerformanceTiming.h
Martin Chrástek 9f3cf862e8 LibWeb: Fix PerformanceTiming getters producing wrong wall clock values
PerformanceTiming::monotonic_timestamp_to_wall_time_milliseconds
assumes all DocumentLoadTimingInfo values are monotonic timestamps
and converts them via wall_time = timestamp - epoch. This is correct
for navigation_start_time (a true monotonic timestamp), but wrong
for all other timing fields like load_event_end_time, dom_complete_time
etc., which are stored as relative timestamps via
current_high_resolution_time() (relative to time origin).

The computed wall clock value ends up off by navigation_start_time,
causing convert_name_to_timestamp() to return wrapped-around u64
values. This broke performance.measure() when using navigation timing
attribute names as start/end marks.

Add relative_timestamp_to_wall_time_milliseconds which adds back
navigation_start_time before the epoch subtraction, producing correct
UTC epoch ms for these fields.
2026-05-19 19:22:11 +02:00

77 lines
2.8 KiB
C++

/*
* Copyright (c) 2021, Andreas Kling <andreas@ladybird.org>
* Copyright (c) 2025, Tim Ledbetter <tim.ledbetter@ladybird.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <LibWeb/DOM/Document.h>
#include <LibWeb/HTML/Window.h>
namespace Web::NavigationTiming {
class PerformanceTiming final : public Bindings::PlatformObject {
WEB_PLATFORM_OBJECT(PerformanceTiming, Bindings::PlatformObject);
GC_DECLARE_ALLOCATOR(PerformanceTiming);
public:
using AllowOwnPtr = TrueType;
~PerformanceTiming();
u64 navigation_start()
{
return monotonic_timestamp_to_wall_time_milliseconds([](auto& load_info) { return load_info.navigation_start_time; });
}
u64 unload_event_start() { return 0; }
u64 unload_event_end() { return 0; }
u64 redirect_start() { return 0; }
u64 redirect_end() { return 0; }
u64 fetch_start() { return 0; }
u64 domain_lookup_start() { return 0; }
u64 domain_lookup_end() { return 0; }
u64 connect_start() { return 0; }
u64 connect_end() { return 0; }
u64 secure_connection_start() { return 0; }
u64 request_start() { return 0; }
u64 response_start() { return 0; }
u64 response_end() { return 0; }
u64 dom_loading() { return 0; }
u64 dom_interactive()
{
return relative_timestamp_to_wall_time_milliseconds([](auto& load_info) { return load_info.dom_interactive_time; });
}
u64 dom_content_loaded_event_start()
{
return relative_timestamp_to_wall_time_milliseconds([](auto& load_info) { return load_info.dom_content_loaded_event_start_time; });
}
u64 dom_content_loaded_event_end()
{
return relative_timestamp_to_wall_time_milliseconds([](auto& load_info) { return load_info.dom_content_loaded_event_end_time; });
}
u64 dom_complete()
{
return relative_timestamp_to_wall_time_milliseconds([](auto& load_info) { return load_info.dom_complete_time; });
}
u64 load_event_start()
{
return relative_timestamp_to_wall_time_milliseconds([](auto& load_info) { return load_info.load_event_start_time; });
}
u64 load_event_end()
{
return relative_timestamp_to_wall_time_milliseconds([](auto& load_info) { return load_info.load_event_end_time; });
}
private:
explicit PerformanceTiming(JS::Realm&);
DOM::DocumentLoadTimingInfo const& document_load_timing_info(JS::Object const& global_object) const;
u64 monotonic_timestamp_to_wall_time_milliseconds(Function<HighResolutionTime::DOMHighResTimeStamp(DOM::DocumentLoadTimingInfo const&)> selector) const;
u64 relative_timestamp_to_wall_time_milliseconds(Function<HighResolutionTime::DOMHighResTimeStamp(DOM::DocumentLoadTimingInfo const&)> selector) const;
virtual void initialize(JS::Realm&) override;
};
}