|
| 1 | +// Copyright (C) 2023-2025 Lightpanda (Selecy SAS) |
| 2 | +// |
| 3 | +// Francis Bouvier <[email protected]> |
| 4 | +// Pierre Tachoire <[email protected]> |
| 5 | +// |
| 6 | +// This program is free software: you can redistribute it and/or modify |
| 7 | +// it under the terms of the GNU Affero General Public License as |
| 8 | +// published by the Free Software Foundation, either version 3 of the |
| 9 | +// License, or (at your option) any later version. |
| 10 | +// |
| 11 | +// This program is distributed in the hope that it will be useful, |
| 12 | +// but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 13 | +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 14 | +// GNU Affero General Public License for more details. |
| 15 | +// |
| 16 | +// You should have received a copy of the GNU Affero General Public License |
| 17 | +// along with this program. If not, see <https://www.gnu.org/licenses/>. |
| 18 | + |
| 19 | +const std = @import("std"); |
| 20 | + |
| 21 | +const parser = @import("../netsurf.zig"); |
| 22 | +const EventTarget = @import("../dom/event_target.zig").EventTarget; |
| 23 | + |
| 24 | +// https://developer.mozilla.org/en-US/docs/Web/API/Performance |
| 25 | +pub const Performance = struct { |
| 26 | + pub const prototype = *EventTarget; |
| 27 | + |
| 28 | + // Extend libdom event target for pure zig struct. |
| 29 | + base: parser.EventTargetTBase = parser.EventTargetTBase{}, |
| 30 | + |
| 31 | + time_origin: std.time.Timer, |
| 32 | + // if (Window.crossOriginIsolated) -> Resolution in isolated contexts: 5 microseconds |
| 33 | + // else -> Resolution in non-isolated contexts: 100 microseconds |
| 34 | + const ms_resolution = 100; |
| 35 | + |
| 36 | + fn limited_resolution_ms(nanoseconds: u64) f64 { |
| 37 | + const elapsed_at_resolution = ((nanoseconds / std.time.ns_per_ms) + ms_resolution / 2) / ms_resolution * ms_resolution; |
| 38 | + const elapsed = @as(f64, @floatFromInt(elapsed_at_resolution)); |
| 39 | + return elapsed / @as(f64, std.time.us_per_ms); |
| 40 | + } |
| 41 | + |
| 42 | + pub fn get_timeOrigin(self: *const Performance) f64 { |
| 43 | + const is_posix = switch (@import("builtin").os.tag) { // From std.time.zig L125 |
| 44 | + .windows, .uefi, .wasi => false, |
| 45 | + else => true, |
| 46 | + }; |
| 47 | + const zero = std.time.Instant{ .timestamp = if (!is_posix) 0 else .{ .sec = 0, .nsec = 0 } }; |
| 48 | + const started = self.time_origin.started.since(zero); |
| 49 | + return limited_resolution_ms(started); |
| 50 | + } |
| 51 | + |
| 52 | + pub fn _now(self: *Performance) f64 { |
| 53 | + return limited_resolution_ms(self.time_origin.read()); |
| 54 | + } |
| 55 | +}; |
| 56 | + |
| 57 | +const testing = @import("./../../testing.zig"); |
| 58 | + |
| 59 | +test "Performance: get_timeOrigin" { |
| 60 | + var perf = Performance{ .time_origin = try std.time.Timer.start() }; |
| 61 | + const time_origin = perf.get_timeOrigin(); |
| 62 | + try testing.expect(time_origin >= 0); |
| 63 | + |
| 64 | + // Check resolution |
| 65 | + try testing.expectDelta(@rem(time_origin * std.time.us_per_ms, 100.0), 0.0, 0.1); |
| 66 | +} |
| 67 | + |
| 68 | +test "Performance: now" { |
| 69 | + var perf = Performance{ .time_origin = try std.time.Timer.start() }; |
| 70 | + |
| 71 | + // Monotonically increasing |
| 72 | + var now = perf._now(); |
| 73 | + while (now <= 0) { // Loop for now to not be 0 |
| 74 | + try testing.expect(now == 0); |
| 75 | + now = perf._now(); |
| 76 | + } |
| 77 | + // Check resolution |
| 78 | + try testing.expectDelta(@rem(now * std.time.us_per_ms, 100.0), 0.0, 0.1); |
| 79 | + |
| 80 | + var after = perf._now(); |
| 81 | + while (after <= now) { // Loop untill after > now |
| 82 | + try testing.expect(after == now); |
| 83 | + after = perf._now(); |
| 84 | + } |
| 85 | + // Check resolution |
| 86 | + try testing.expectDelta(@rem(after * std.time.us_per_ms, 100.0), 0.0, 0.1); |
| 87 | +} |
0 commit comments