From 235aad32a61cc0a0103dbe6de730a2a1bea88118 Mon Sep 17 00:00:00 2001 From: Pierre Tachoire Date: Wed, 25 Feb 2026 08:35:53 +0100 Subject: [PATCH 1/4] add dummy PerformanceTiming --- src/browser/tests/performance.html | 44 ++++++++++++++++++ src/browser/webapi/Performance.zig | 72 +++++++++++++++++++++++++++++- 2 files changed, 115 insertions(+), 1 deletion(-) diff --git a/src/browser/tests/performance.html b/src/browser/tests/performance.html index 01d23ab4..1c14d42f 100644 --- a/src/browser/tests/performance.html +++ b/src/browser/tests/performance.html @@ -274,6 +274,50 @@ } + + + + + + + + + + + + + + + + diff --git a/src/browser/webapi/Performance.zig b/src/browser/webapi/Performance.zig index 7004f63c..33404e4e 100644 --- a/src/browser/webapi/Performance.zig +++ b/src/browser/webapi/Performance.zig @@ -3,7 +3,7 @@ const Page = @import("../Page.zig"); const datetime = @import("../../datetime.zig"); pub fn registerTypes() []const type { - return &.{ Performance, Entry, Mark, Measure, PerformanceTiming }; + return &.{ Performance, Entry, Mark, Measure, PerformanceTiming, PerformanceNavigation }; } const std = @import("std"); @@ -13,6 +13,7 @@ const Performance = @This(); _time_origin: u64, _entries: std.ArrayList(*Entry) = .{}, _timing: PerformanceTiming = .{}, +_navigation: PerformanceNavigation = .{}, /// Get high-resolution timestamp in microseconds, rounded to 5μs increments /// to match browser behavior (prevents fingerprinting) @@ -29,6 +30,7 @@ pub fn init() Performance { ._time_origin = highResTimestamp(), ._entries = .{}, ._timing = .{}, + ._navigation = .{}, }; } @@ -48,6 +50,10 @@ pub fn getTimeOrigin(self: *const Performance) f64 { return @as(f64, @floatFromInt(self._time_origin)) / 1000.0; } +pub fn getNavigation(self: *Performance) *PerformanceNavigation { + return &self._navigation; +} + pub fn mark( self: *Performance, name: []const u8, @@ -270,6 +276,7 @@ pub const JsApi = struct { pub const getEntriesByName = bridge.function(Performance.getEntriesByName, .{}); pub const timeOrigin = bridge.accessor(Performance.getTimeOrigin, null, .{}); pub const timing = bridge.accessor(Performance.getTiming, null, .{}); + pub const navigation = bridge.accessor(Performance.getNavigation, null, .{}); }; pub const Entry = struct { @@ -519,6 +526,30 @@ pub const PerformanceTiming = struct { }; }; +// PerformanceNavigation implements the Navigation Timing Level 1 API. +// https://www.w3.org/TR/navigation-timing/#sec-navigation-navigation-timing-interface +// Stub implementation — returns 0 for type (TYPE_NAVIGATE) and 0 for redirectCount. +pub const PerformanceNavigation = struct { + // Padding to avoid zero-size struct, which causes identity_map pointer collisions. + _pad: bool = false, + + pub fn getType(_: *const PerformanceNavigation) f64 { return 0; } + pub fn getRedirectCount(_: *const PerformanceNavigation) f64 { return 0; } + + pub const JsApi = struct { + pub const bridge = js.Bridge(PerformanceNavigation); + + pub const Meta = struct { + pub const name = "PerformanceNavigation"; + pub const prototype_chain = bridge.prototypeChain(); + pub var class_id: bridge.ClassId = undefined; + }; + + pub const @"type" = bridge.accessor(PerformanceNavigation.getType, null, .{}); + pub const redirectCount = bridge.accessor(PerformanceNavigation.getRedirectCount, null, .{}); + }; +}; + const testing = @import("../../testing.zig"); test "WebApi: Performance" { try testing.htmlRunner("performance.html", .{}); From df7888d6fb7d4790bdeab4448a992152deeef0a7 Mon Sep 17 00:00:00 2001 From: Pierre Tachoire Date: Wed, 25 Feb 2026 21:10:27 +0100 Subject: [PATCH 3/4] use bridge.property for performance timing and navigation --- src/browser/webapi/Performance.zig | 71 ++++++++++-------------------- 1 file changed, 23 insertions(+), 48 deletions(-) diff --git a/src/browser/webapi/Performance.zig b/src/browser/webapi/Performance.zig index 33404e4e..66d7e520 100644 --- a/src/browser/webapi/Performance.zig +++ b/src/browser/webapi/Performance.zig @@ -471,28 +471,6 @@ pub const PerformanceTiming = struct { // Padding to avoid zero-size struct, which causes identity_map pointer collisions. _pad: bool = false, - pub fn getNavigationStart(_: *const PerformanceTiming) f64 { return 0; } - pub fn getUnloadEventStart(_: *const PerformanceTiming) f64 { return 0; } - pub fn getUnloadEventEnd(_: *const PerformanceTiming) f64 { return 0; } - pub fn getRedirectStart(_: *const PerformanceTiming) f64 { return 0; } - pub fn getRedirectEnd(_: *const PerformanceTiming) f64 { return 0; } - pub fn getFetchStart(_: *const PerformanceTiming) f64 { return 0; } - pub fn getDomainLookupStart(_: *const PerformanceTiming) f64 { return 0; } - pub fn getDomainLookupEnd(_: *const PerformanceTiming) f64 { return 0; } - pub fn getConnectStart(_: *const PerformanceTiming) f64 { return 0; } - pub fn getConnectEnd(_: *const PerformanceTiming) f64 { return 0; } - pub fn getSecureConnectionStart(_: *const PerformanceTiming) f64 { return 0; } - pub fn getRequestStart(_: *const PerformanceTiming) f64 { return 0; } - pub fn getResponseStart(_: *const PerformanceTiming) f64 { return 0; } - pub fn getResponseEnd(_: *const PerformanceTiming) f64 { return 0; } - pub fn getDomLoading(_: *const PerformanceTiming) f64 { return 0; } - pub fn getDomInteractive(_: *const PerformanceTiming) f64 { return 0; } - pub fn getDomContentLoadedEventStart(_: *const PerformanceTiming) f64 { return 0; } - pub fn getDomContentLoadedEventEnd(_: *const PerformanceTiming) f64 { return 0; } - pub fn getDomComplete(_: *const PerformanceTiming) f64 { return 0; } - pub fn getLoadEventStart(_: *const PerformanceTiming) f64 { return 0; } - pub fn getLoadEventEnd(_: *const PerformanceTiming) f64 { return 0; } - pub const JsApi = struct { pub const bridge = js.Bridge(PerformanceTiming); @@ -502,27 +480,27 @@ pub const PerformanceTiming = struct { pub var class_id: bridge.ClassId = undefined; }; - pub const navigationStart = bridge.accessor(PerformanceTiming.getNavigationStart, null, .{}); - pub const unloadEventStart = bridge.accessor(PerformanceTiming.getUnloadEventStart, null, .{}); - pub const unloadEventEnd = bridge.accessor(PerformanceTiming.getUnloadEventEnd, null, .{}); - pub const redirectStart = bridge.accessor(PerformanceTiming.getRedirectStart, null, .{}); - pub const redirectEnd = bridge.accessor(PerformanceTiming.getRedirectEnd, null, .{}); - pub const fetchStart = bridge.accessor(PerformanceTiming.getFetchStart, null, .{}); - pub const domainLookupStart = bridge.accessor(PerformanceTiming.getDomainLookupStart, null, .{}); - pub const domainLookupEnd = bridge.accessor(PerformanceTiming.getDomainLookupEnd, null, .{}); - pub const connectStart = bridge.accessor(PerformanceTiming.getConnectStart, null, .{}); - pub const connectEnd = bridge.accessor(PerformanceTiming.getConnectEnd, null, .{}); - pub const secureConnectionStart = bridge.accessor(PerformanceTiming.getSecureConnectionStart, null, .{}); - pub const requestStart = bridge.accessor(PerformanceTiming.getRequestStart, null, .{}); - pub const responseStart = bridge.accessor(PerformanceTiming.getResponseStart, null, .{}); - pub const responseEnd = bridge.accessor(PerformanceTiming.getResponseEnd, null, .{}); - pub const domLoading = bridge.accessor(PerformanceTiming.getDomLoading, null, .{}); - pub const domInteractive = bridge.accessor(PerformanceTiming.getDomInteractive, null, .{}); - pub const domContentLoadedEventStart = bridge.accessor(PerformanceTiming.getDomContentLoadedEventStart, null, .{}); - pub const domContentLoadedEventEnd = bridge.accessor(PerformanceTiming.getDomContentLoadedEventEnd, null, .{}); - pub const domComplete = bridge.accessor(PerformanceTiming.getDomComplete, null, .{}); - pub const loadEventStart = bridge.accessor(PerformanceTiming.getLoadEventStart, null, .{}); - pub const loadEventEnd = bridge.accessor(PerformanceTiming.getLoadEventEnd, null, .{}); + pub const navigationStart = bridge.property(0.0, .{ .template = false, .readonly = true }); + pub const unloadEventStart = bridge.property(0.0, .{ .template = false, .readonly = true }); + pub const unloadEventEnd = bridge.property(0.0, .{ .template = false, .readonly = true }); + pub const redirectStart = bridge.property(0.0, .{ .template = false, .readonly = true }); + pub const redirectEnd = bridge.property(0.0, .{ .template = false, .readonly = true }); + pub const fetchStart = bridge.property(0.0, .{ .template = false, .readonly = true }); + pub const domainLookupStart = bridge.property(0.0, .{ .template = false, .readonly = true }); + pub const domainLookupEnd = bridge.property(0.0, .{ .template = false, .readonly = true }); + pub const connectStart = bridge.property(0.0, .{ .template = false, .readonly = true }); + pub const connectEnd = bridge.property(0.0, .{ .template = false, .readonly = true }); + pub const secureConnectionStart = bridge.property(0.0, .{ .template = false, .readonly = true }); + pub const requestStart = bridge.property(0.0, .{ .template = false, .readonly = true }); + pub const responseStart = bridge.property(0.0, .{ .template = false, .readonly = true }); + pub const responseEnd = bridge.property(0.0, .{ .template = false, .readonly = true }); + pub const domLoading = bridge.property(0.0, .{ .template = false, .readonly = true }); + pub const domInteractive = bridge.property(0.0, .{ .template = false, .readonly = true }); + pub const domContentLoadedEventStart = bridge.property(0.0, .{ .template = false, .readonly = true }); + pub const domContentLoadedEventEnd = bridge.property(0.0, .{ .template = false, .readonly = true }); + pub const domComplete = bridge.property(0.0, .{ .template = false, .readonly = true }); + pub const loadEventStart = bridge.property(0.0, .{ .template = false, .readonly = true }); + pub const loadEventEnd = bridge.property(0.0, .{ .template = false, .readonly = true }); }; }; @@ -533,9 +511,6 @@ pub const PerformanceNavigation = struct { // Padding to avoid zero-size struct, which causes identity_map pointer collisions. _pad: bool = false, - pub fn getType(_: *const PerformanceNavigation) f64 { return 0; } - pub fn getRedirectCount(_: *const PerformanceNavigation) f64 { return 0; } - pub const JsApi = struct { pub const bridge = js.Bridge(PerformanceNavigation); @@ -545,8 +520,8 @@ pub const PerformanceNavigation = struct { pub var class_id: bridge.ClassId = undefined; }; - pub const @"type" = bridge.accessor(PerformanceNavigation.getType, null, .{}); - pub const redirectCount = bridge.accessor(PerformanceNavigation.getRedirectCount, null, .{}); + pub const @"type" = bridge.property(0.0, .{ .template = false, .readonly = true }); + pub const redirectCount = bridge.property(0.0, .{ .template = false, .readonly = true }); }; }; From 181178296fd1d3d8fabd23d09818bcee7c2e083a Mon Sep 17 00:00:00 2001 From: Pierre Tachoire Date: Wed, 25 Feb 2026 21:14:09 +0100 Subject: [PATCH 4/4] set empty_with_no_proto for performance timing and navigation --- src/browser/webapi/Performance.zig | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/browser/webapi/Performance.zig b/src/browser/webapi/Performance.zig index 66d7e520..e9ca0ebf 100644 --- a/src/browser/webapi/Performance.zig +++ b/src/browser/webapi/Performance.zig @@ -478,6 +478,7 @@ pub const PerformanceTiming = struct { pub const name = "PerformanceTiming"; pub const prototype_chain = bridge.prototypeChain(); pub var class_id: bridge.ClassId = undefined; + pub const empty_with_no_proto = true; }; pub const navigationStart = bridge.property(0.0, .{ .template = false, .readonly = true }); @@ -518,6 +519,7 @@ pub const PerformanceNavigation = struct { pub const name = "PerformanceNavigation"; pub const prototype_chain = bridge.prototypeChain(); pub var class_id: bridge.ClassId = undefined; + pub const empty_with_no_proto = true; }; pub const @"type" = bridge.property(0.0, .{ .template = false, .readonly = true });