Merge pull request #829 from lightpanda-io/pumpmessageloop
Some checks failed
e2e-test / zig build release (push) Has been cancelled
e2e-test / demo-scripts (push) Has been cancelled
e2e-test / cdp-and-hyperfine-bench (push) Has been cancelled
e2e-test / perf-fmt (push) Has been cancelled
zig-test / zig build dev (push) Has been cancelled
zig-test / browser fetch (push) Has been cancelled
zig-test / zig test (push) Has been cancelled
zig-test / perf-fmt (push) Has been cancelled
nightly build / build-linux-x86_64 (push) Has been cancelled
nightly build / build-linux-aarch64 (push) Has been cancelled
nightly build / build-macos-aarch64 (push) Has been cancelled
nightly build / build-macos-x86_64 (push) Has been cancelled
wpt / web platform tests json output (push) Has been cancelled
wpt / perf-fmt (push) Has been cancelled

add pump message loop calls
This commit is contained in:
Pierre Tachoire
2025-07-03 10:11:26 -07:00
committed by GitHub
8 changed files with 68 additions and 9 deletions

View File

@@ -4,6 +4,7 @@ const Allocator = std.mem.Allocator;
const log = @import("log.zig");
const Loop = @import("runtime/loop.zig").Loop;
const http = @import("http/client.zig");
const Platform = @import("runtime/js.zig").Platform;
const Telemetry = @import("telemetry/telemetry.zig").Telemetry;
const Notification = @import("notification.zig").Notification;
@@ -13,6 +14,7 @@ const Notification = @import("notification.zig").Notification;
pub const App = struct {
loop: *Loop,
config: Config,
platform: ?*const Platform,
allocator: Allocator,
telemetry: Telemetry,
http_client: http.Client,
@@ -28,6 +30,7 @@ pub const App = struct {
pub const Config = struct {
run_mode: RunMode,
platform: ?*const Platform = null,
tls_verify_host: bool = true,
http_proxy: ?std.Uri = null,
proxy_type: ?http.ProxyType = null,
@@ -53,6 +56,7 @@ pub const App = struct {
.loop = loop,
.allocator = allocator,
.telemetry = undefined,
.platform = config.platform,
.app_dir_path = app_dir_path,
.notification = notification,
.http_client = try http.Client.init(allocator, loop, .{

View File

@@ -27,6 +27,8 @@ const App = @import("../app.zig").App;
const Session = @import("session.zig").Session;
const Notification = @import("../notification.zig").Notification;
const log = @import("../log.zig");
const http = @import("../http/client.zig");
// Browser is an instance of the browser.
@@ -47,7 +49,7 @@ pub const Browser = struct {
pub fn init(app: *App) !Browser {
const allocator = app.allocator;
const env = try Env.init(allocator, .{});
const env = try Env.init(allocator, app.platform, .{});
errdefer env.deinit();
const notification = try Notification.init(allocator, app.notification);
@@ -95,7 +97,14 @@ pub const Browser = struct {
}
pub fn runMicrotasks(self: *const Browser) void {
return self.env.runMicrotasks();
self.env.runMicrotasks();
}
pub fn runMessageLoop(self: *const Browser) void {
while (self.env.pumpMessageLoop()) {
log.debug(.browser, "pumpMessageLoop", .{});
}
self.env.runIdleTasks();
}
};

View File

@@ -78,7 +78,10 @@ pub const Page = struct {
renderer: Renderer,
// run v8 micro tasks
microtask_node: Loop.CallbackNode,
// run v8 pump message loop and idle tasks
messageloop_node: Loop.CallbackNode,
keydown_event_node: parser.EventNode,
window_clicked_event_node: parser.EventNode,
@@ -106,6 +109,7 @@ pub const Page = struct {
.state_pool = &browser.state_pool,
.cookie_jar = &session.cookie_jar,
.microtask_node = .{ .func = microtaskCallback },
.messageloop_node = .{ .func = messageLoopCallback },
.keydown_event_node = .{ .func = keydownCallback },
.window_clicked_event_node = .{ .func = windowClicked },
.request_factory = browser.http_client.requestFactory(.{
@@ -119,6 +123,10 @@ pub const Page = struct {
try polyfill.load(self.arena, self.main_context);
_ = try session.browser.app.loop.timeout(1 * std.time.ns_per_ms, &self.microtask_node);
// message loop must run only non-test env
if (comptime !builtin.is_test) {
_ = try session.browser.app.loop.timeout(1 * std.time.ns_per_ms, &self.messageloop_node);
}
}
fn microtaskCallback(node: *Loop.CallbackNode, repeat_delay: *?u63) void {
@@ -127,6 +135,12 @@ pub const Page = struct {
repeat_delay.* = 1 * std.time.ns_per_ms;
}
fn messageLoopCallback(node: *Loop.CallbackNode, repeat_delay: *?u63) void {
const self: *Page = @fieldParentPtr("messageloop_node", node);
self.session.browser.runMessageLoop();
repeat_delay.* = 100 * std.time.ns_per_ms;
}
// dump writes the page content into the given file.
pub fn dump(self: *const Page, out: std.fs.File) !void {
if (self.raw_data) |raw_data| {

View File

@@ -83,6 +83,7 @@ fn run(alloc: Allocator) !void {
var app = try App.init(alloc, .{
.run_mode = args.mode,
.platform = &platform,
.http_proxy = args.httpProxy(),
.proxy_type = args.proxyType(),
.proxy_auth = args.proxyAuth(),
@@ -602,7 +603,7 @@ test "tests:beforeAll" {
log.opts.format = .logfmt;
test_wg.startMany(3);
_ = try Platform.init();
const platform = try Platform.init();
{
const address = try std.net.Address.parseIp("127.0.0.1", 9582);
@@ -618,7 +619,7 @@ test "tests:beforeAll" {
{
const address = try std.net.Address.parseIp("127.0.0.1", 9583);
const thread = try std.Thread.spawn(.{}, serveCDP, .{address});
const thread = try std.Thread.spawn(.{}, serveCDP, .{ address, &platform });
thread.detach();
}
@@ -800,11 +801,12 @@ fn serveHTTPS(address: std.net.Address) !void {
}
}
fn serveCDP(address: std.net.Address) !void {
fn serveCDP(address: std.net.Address, platform: *const Platform) !void {
var gpa: std.heap.GeneralPurposeAllocator(.{}) = .init;
var app = try App.init(gpa.allocator(), .{
.run_mode = .serve,
.tls_verify_host = false,
.platform = platform,
});
defer app.deinit();

View File

@@ -70,7 +70,13 @@ pub fn main() !void {
defer _ = test_arena.reset(.{ .retain_capacity = {} });
var err_out: ?[]const u8 = null;
const result = run(test_arena.allocator(), test_file, &loader, &err_out) catch |err| blk: {
const result = run(
test_arena.allocator(),
&platform,
test_file,
&loader,
&err_out,
) catch |err| blk: {
if (err_out == null) {
err_out = @errorName(err);
}
@@ -89,7 +95,13 @@ pub fn main() !void {
try writer.finalize();
}
fn run(arena: Allocator, test_file: []const u8, loader: *FileLoader, err_out: *?[]const u8) !?[]const u8 {
fn run(
arena: Allocator,
platform: *const Platform,
test_file: []const u8,
loader: *FileLoader,
err_out: *?[]const u8,
) !?[]const u8 {
// document
const html = blk: {
const full_path = try std.fs.path.join(arena, &.{ WPT_DIR, test_file });
@@ -110,6 +122,7 @@ fn run(arena: Allocator, test_file: []const u8, loader: *FileLoader, err_out: *?
var runner = try @import("testing.zig").jsRunner(arena, .{
.url = "http://127.0.0.1",
.html = html,
.platform = platform,
});
defer runner.deinit();

View File

@@ -156,6 +156,8 @@ pub fn Env(comptime State: type, comptime WebApis: type) type {
return struct {
allocator: Allocator,
platform: ?*const Platform,
// the global isolate
isolate: v8.Isolate,
@@ -181,7 +183,7 @@ pub fn Env(comptime State: type, comptime WebApis: type) type {
const Opts = struct {};
pub fn init(allocator: Allocator, _: Opts) !*Self {
pub fn init(allocator: Allocator, platform: ?*const Platform, _: Opts) !*Self {
// var params = v8.initCreateParams();
var params = try allocator.create(v8.CreateParams);
errdefer allocator.destroy(params);
@@ -215,6 +217,7 @@ pub fn Env(comptime State: type, comptime WebApis: type) type {
errdefer allocator.destroy(env);
env.* = .{
.platform = platform,
.isolate = isolate,
.templates = undefined,
.allocator = allocator,
@@ -270,6 +273,16 @@ pub fn Env(comptime State: type, comptime WebApis: type) type {
self.isolate.performMicrotasksCheckpoint();
}
pub fn pumpMessageLoop(self: *const Self) bool {
// assume it's not-null.
return self.platform.?.inner.pumpMessageLoop(self.isolate, false);
}
pub fn runIdleTasks(self: *const Self) void {
// assume it's not-null.
return self.platform.?.inner.runIdleTasks(self.isolate, 1);
}
pub fn newExecutionWorld(self: *Self) !ExecutionWorld {
return .{
.env = self,

View File

@@ -42,7 +42,7 @@ pub fn Runner(comptime State: type, comptime Global: type, comptime types: anyty
const self = try allocator.create(Self);
errdefer allocator.destroy(self);
self.env = try Env.init(allocator, .{});
self.env = try Env.init(allocator, null, .{});
errdefer self.env.deinit();
self.executor = try self.env.newExecutionWorld();

View File

@@ -19,6 +19,8 @@
const std = @import("std");
const Allocator = std.mem.Allocator;
const Platform = @import("runtime/js.zig").Platform;
pub const allocator = std.testing.allocator;
pub const expectError = std.testing.expectError;
pub const expect = std.testing.expect;
@@ -383,6 +385,7 @@ pub const JsRunner = struct {
var app = try App.init(alloc, .{
.run_mode = .serve,
.tls_verify_host = false,
.platform = opts.platform,
});
errdefer app.deinit();
@@ -474,6 +477,7 @@ pub const JsRunner = struct {
};
const RunnerOpts = struct {
platform: ?*const Platform = null,
url: []const u8 = "https://lightpanda.io/opensource-browser/",
html: []const u8 =
\\ <div id="content">