pass IdleDeadline to idle callback

This commit is contained in:
Karl Seguin
2025-12-21 18:26:54 +08:00
parent 25ad3559f7
commit da32440a14
3 changed files with 80 additions and 13 deletions

View File

@@ -607,6 +607,7 @@ pub const JsApis = flattenTypes(&.{
@import("../webapi/IntersectionObserver.zig"), @import("../webapi/IntersectionObserver.zig"),
@import("../webapi/CustomElementRegistry.zig"), @import("../webapi/CustomElementRegistry.zig"),
@import("../webapi/ResizeObserver.zig"), @import("../webapi/ResizeObserver.zig"),
@import("../webapi/IdleDeadline.zig"),
@import("../webapi/Blob.zig"), @import("../webapi/Blob.zig"),
@import("../webapi/File.zig"), @import("../webapi/File.zig"),
@import("../webapi/Screen.zig"), @import("../webapi/Screen.zig"),

View File

@@ -0,0 +1,51 @@
// Copyright (C) 2023-2025 Lightpanda (Selecy SAS)
//
// Francis Bouvier <francis@lightpanda.io>
// Pierre Tachoire <pierre@lightpanda.io>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
const std = @import("std");
const IdleDeadline = @This();
pub fn init() IdleDeadline {
return .{};
}
pub fn getDidTimeout(_: *const IdleDeadline) bool {
return false;
}
pub fn timeRemaining(_: *const IdleDeadline) f64 {
// Return a fixed 50ms.
// This allows idle callbacks to perform work without complex
// timing infrastructure.
return 50.0;
}
pub const JsApi = struct {
const js = @import("../js/js.zig");
pub const bridge = js.Bridge(IdleDeadline);
pub const Meta = struct {
pub const name = "IdleDeadline";
pub const prototype_chain = bridge.prototypeChain();
pub var class_id: bridge.ClassId = undefined;
pub const empty_with_no_proto = true;
};
pub const timeRemaining = bridge.function(IdleDeadline.timeRemaining, .{});
pub const didTimeout = bridge.accessor(IdleDeadline.getDidTimeout, null, .{});
};

View File

@@ -223,7 +223,7 @@ pub fn requestAnimationFrame(self: *Window, cb: js.Function, page: *Page) !u32 {
.repeat = false, .repeat = false,
.params = &.{}, .params = &.{},
.low_priority = false, .low_priority = false,
.animation_frame = true, .mode = .animation_frame,
.name = "window.requestAnimationFrame", .name = "window.requestAnimationFrame",
}, page); }, page);
} }
@@ -258,6 +258,7 @@ const RequestIdleCallbackOpts = struct {
pub fn requestIdleCallback(self: *Window, cb: js.Function, opts_: ?RequestIdleCallbackOpts, page: *Page) !u32 { pub fn requestIdleCallback(self: *Window, cb: js.Function, opts_: ?RequestIdleCallbackOpts, page: *Page) !u32 {
const opts = opts_ orelse RequestIdleCallbackOpts{}; const opts = opts_ orelse RequestIdleCallbackOpts{};
return self.scheduleCallback(cb, opts.timeout orelse 50, .{ return self.scheduleCallback(cb, opts.timeout orelse 50, .{
.mode = .idle,
.repeat = false, .repeat = false,
.params = &.{}, .params = &.{},
.low_priority = true, .low_priority = true,
@@ -364,6 +365,7 @@ const ScheduleOpts = struct {
name: []const u8, name: []const u8,
low_priority: bool = false, low_priority: bool = false,
animation_frame: bool = false, animation_frame: bool = false,
mode: ScheduleCallback.Mode = .normal,
}; };
fn scheduleCallback(self: *Window, cb: js.Function, delay_ms: u32, opts: ScheduleOpts, page: *Page) !u32 { fn scheduleCallback(self: *Window, cb: js.Function, delay_ms: u32, opts: ScheduleOpts, page: *Page) !u32 {
if (self._timers.count() > 512) { if (self._timers.count() > 512) {
@@ -393,10 +395,10 @@ fn scheduleCallback(self: *Window, cb: js.Function, delay_ms: u32, opts: Schedul
const callback = try page._factory.create(ScheduleCallback{ const callback = try page._factory.create(ScheduleCallback{
.cb = cb, .cb = cb,
.page = page, .page = page,
.mode = opts.mode,
.name = opts.name, .name = opts.name,
.timer_id = timer_id, .timer_id = timer_id,
.params = persisted_params, .params = persisted_params,
.animation_frame = opts.animation_frame,
.repeat_ms = if (opts.repeat) if (delay_ms == 0) 1 else delay_ms else null, .repeat_ms = if (opts.repeat) if (delay_ms == 0) 1 else delay_ms else null,
}); });
gop.value_ptr.* = callback; gop.value_ptr.* = callback;
@@ -428,7 +430,13 @@ const ScheduleCallback = struct {
removed: bool = false, removed: bool = false,
animation_frame: bool = false, mode: Mode,
const Mode = enum {
idle,
normal,
animation_frame,
};
fn deinit(self: *ScheduleCallback) void { fn deinit(self: *ScheduleCallback) void {
self.page._factory.destroy(self); self.page._factory.destroy(self);
@@ -443,16 +451,23 @@ const ScheduleCallback = struct {
return null; return null;
} }
if (self.animation_frame) { switch (self.mode) {
self.cb.call(void, .{page.window._performance.now()}) catch |err| { .idle => {
// a non-JS error const IdleDeadline = @import("IdleDeadline.zig");
log.warn(.js, "window.RAF", .{ .name = self.name, .err = err }); self.cb.call(void, .{IdleDeadline{}}) catch |err| {
}; log.warn(.js, "window.idleCallback", .{ .name = self.name, .err = err });
} else { };
self.cb.call(void, .{self.params}) catch |err| { },
// a non-JS error .animation_frame => {
log.warn(.js, "window.timer", .{ .name = self.name, .err = err }); self.cb.call(void, .{page.window._performance.now()}) catch |err| {
}; log.warn(.js, "window.RAF", .{ .name = self.name, .err = err });
};
},
.normal => {
self.cb.call(void, .{self.params}) catch |err| {
log.warn(.js, "window.timer", .{ .name = self.name, .err = err });
};
},
} }
if (self.repeat_ms) |ms| { if (self.repeat_ms) |ms| {