From 197de3dc0c48b2505dee1dd90aa66800cde2e984 Mon Sep 17 00:00:00 2001 From: Karl Seguin Date: Wed, 8 Apr 2026 09:23:11 +0800 Subject: [PATCH] Limit Worker API to supported types In order for an API to be supported by workers, their dependency on *Page has to be removed. To keep this PR smaller, we're only converting a minimum number of APIs from Page to Execution. All other APIs should not be exposed to the worker (better to get a FormData undefined than to try to segfault trying to execute FormData without a page). --- src/browser/js/bridge.zig | 18 ++++---- src/browser/webapi/Console.zig | 50 ++++++++++----------- src/browser/webapi/DOMException.zig | 5 +-- src/browser/webapi/EventTarget.zig | 6 +-- src/browser/webapi/File.zig | 9 ++-- src/browser/webapi/WorkerGlobalScope.zig | 10 +---- src/browser/webapi/collections/NodeList.zig | 3 +- src/browser/webapi/encoding/TextDecoder.zig | 7 ++- src/browser/webapi/net/URLSearchParams.zig | 1 - 9 files changed, 47 insertions(+), 62 deletions(-) diff --git a/src/browser/js/bridge.zig b/src/browser/js/bridge.zig index 0485d037..22cbed30 100644 --- a/src/browser/js/bridge.zig +++ b/src/browser/js/bridge.zig @@ -907,21 +907,21 @@ pub const WorkerJsApis = flattenTypes(&.{ @import("../webapi/WorkerGlobalScope.zig"), @import("../webapi/EventTarget.zig"), @import("../webapi/DOMException.zig"), - @import("../webapi/AbortController.zig"), - @import("../webapi/AbortSignal.zig"), - @import("../webapi/URL.zig"), @import("../webapi/net/URLSearchParams.zig"), - @import("../webapi/net/Headers.zig"), - @import("../webapi/net/Request.zig"), - @import("../webapi/net/Response.zig"), @import("../webapi/encoding/TextEncoder.zig"), @import("../webapi/encoding/TextDecoder.zig"), - @import("../webapi/Blob.zig"), @import("../webapi/File.zig"), - @import("../webapi/net/FormData.zig"), @import("../webapi/Console.zig"), @import("../webapi/Crypto.zig"), - @import("../webapi/Performance.zig"), + // @import("../webapi/URL.zig"), + // @import("../webapi/Blob.zig"), + // @import("../webapi/net/FormData.zig"), + // @import("../webapi/Performance.zig"), + // @import("../webapi/net/Response.zig"), + // @import("../webapi/net/Request.zig"), + // @import("../webapi/net/Headers.zig"), + // @import("../webapi/AbortSignal.zig"), + // @import("../webapi/AbortController.zig"), }); // Master list of ALL JS APIs across all contexts. diff --git a/src/browser/webapi/Console.zig b/src/browser/webapi/Console.zig index 036eea04..8d4ebc88 100644 --- a/src/browser/webapi/Console.zig +++ b/src/browser/webapi/Console.zig @@ -19,7 +19,6 @@ const std = @import("std"); const js = @import("../js/js.zig"); -const Page = @import("../Page.zig"); const logger = @import("../../log.zig"); const Console = @This(); @@ -29,55 +28,55 @@ _counts: std.StringHashMapUnmanaged(u64) = .{}, pub const init: Console = .{}; -pub fn trace(_: *const Console, values: []js.Value, page: *Page) !void { +pub fn trace(_: *const Console, values: []js.Value, exec: *js.Execution) !void { logger.debug(.js, "console.trace", .{ - .stack = page.js.local.?.stackTrace() catch "???", - .args = ValueWriter{ .page = page, .values = values }, + .stack = exec.context.local.?.stackTrace() catch "???", + .args = ValueWriter{ .values = values }, }); } -pub fn debug(_: *const Console, values: []js.Value, page: *Page) void { - logger.debug(.js, "console.debug", .{ValueWriter{ .page = page, .values = values }}); +pub fn debug(_: *const Console, values: []js.Value) void { + logger.debug(.js, "console.debug", .{ValueWriter{ .values = values }}); } -pub fn info(_: *const Console, values: []js.Value, page: *Page) void { - logger.info(.js, "console.info", .{ValueWriter{ .page = page, .values = values }}); +pub fn info(_: *const Console, values: []js.Value) void { + logger.info(.js, "console.info", .{ValueWriter{ .values = values }}); } -pub fn log(_: *const Console, values: []js.Value, page: *Page) void { - logger.info(.js, "console.log", .{ValueWriter{ .page = page, .values = values }}); +pub fn log(_: *const Console, values: []js.Value) void { + logger.info(.js, "console.log", .{ValueWriter{ .values = values }}); } -pub fn warn(_: *const Console, values: []js.Value, page: *Page) void { - logger.warn(.js, "console.warn", .{ValueWriter{ .page = page, .values = values }}); +pub fn warn(_: *const Console, values: []js.Value) void { + logger.warn(.js, "console.warn", .{ValueWriter{ .values = values }}); } pub fn clear(_: *const Console) void {} -pub fn assert(_: *const Console, assertion: js.Value, values: []js.Value, page: *Page) void { +pub fn assert(_: *const Console, assertion: js.Value, values: []js.Value) void { if (assertion.toBool()) { return; } - logger.warn(.js, "console.assert", .{ValueWriter{ .page = page, .values = values }}); + logger.warn(.js, "console.assert", .{ValueWriter{ .values = values }}); } -pub fn @"error"(_: *const Console, values: []js.Value, page: *Page) void { - logger.warn(.js, "console.error", .{ValueWriter{ .page = page, .values = values, .include_stack = true }}); +pub fn @"error"(_: *const Console, values: []js.Value, exec: *js.Execution) void { + logger.warn(.js, "console.error", .{ValueWriter{ .values = values, .stack = exec.context.local.?.stackTrace() catch |err| @errorName(err) orelse "???" }}); } pub fn table(_: *const Console, data: js.Value, columns: ?js.Value) void { logger.info(.js, "console.table", .{ .data = data, .columns = columns }); } -pub fn count(self: *Console, label_: ?[]const u8, page: *Page) !void { +pub fn count(self: *Console, label_: ?[]const u8, exec: *js.Execution) !void { const label = label_ orelse "default"; - const gop = try self._counts.getOrPut(page.arena, label); + const gop = try self._counts.getOrPut(exec.arena, label); var current: u64 = 0; if (gop.found_existing) { current = gop.value_ptr.*; } else { - gop.key_ptr.* = try page.dupeString(label); + gop.key_ptr.* = try exec.arena.dupe(u8, label); } const c = current + 1; @@ -95,15 +94,15 @@ pub fn countReset(self: *Console, label_: ?[]const u8) !void { logger.info(.js, "console.countReset", .{ .label = label, .count = kv.value }); } -pub fn time(self: *Console, label_: ?[]const u8, page: *Page) !void { +pub fn time(self: *Console, label_: ?[]const u8, exec: *js.Execution) !void { const label = label_ orelse "default"; - const gop = try self._timers.getOrPut(page.arena, label); + const gop = try self._timers.getOrPut(exec.arena, label); if (gop.found_existing) { logger.info(.js, "console.time", .{ .label = label, .err = "duplicate timer" }); return; } - gop.key_ptr.* = try page.dupeString(label); + gop.key_ptr.* = try exec.arena.dupe(u8, label); gop.value_ptr.* = timestamp(); } @@ -143,16 +142,15 @@ fn timestamp() u64 { } const ValueWriter = struct { - page: *Page, values: []js.Value, - include_stack: bool = false, + stack: ?[]const u8 = null, pub fn format(self: ValueWriter, writer: *std.io.Writer) !void { for (self.values, 1..) |value, i| { try writer.print("\n arg({d}): {f}", .{ i, value }); } - if (self.include_stack) { - try writer.print("\n stack: {s}", .{self.page.js.local.?.stackTrace() catch |err| @errorName(err) orelse "???"}); + if (self.stack) |s| { + try writer.print("\n stack: {s}", .{s}); } } diff --git a/src/browser/webapi/DOMException.zig b/src/browser/webapi/DOMException.zig index 55c47e74..c0011cd4 100644 --- a/src/browser/webapi/DOMException.zig +++ b/src/browser/webapi/DOMException.zig @@ -18,7 +18,6 @@ const std = @import("std"); const js = @import("../js/js.zig"); -const Page = @import("../Page.zig"); const DOMException = @This(); @@ -129,7 +128,7 @@ pub fn getMessage(self: *const DOMException) []const u8 { }; } -pub fn toString(self: *const DOMException, page: *Page) ![]const u8 { +pub fn toString(self: *const DOMException, exec: *js.Execution) ![]const u8 { const msg = blk: { if (self._custom_message) |msg| { break :blk msg; @@ -139,7 +138,7 @@ pub fn toString(self: *const DOMException, page: *Page) ![]const u8 { else => break :blk self.getMessage(), } }; - return std.fmt.bufPrint(&page.buf, "{s}: {s}", .{ self.getName(), msg }) catch return msg; + return std.fmt.bufPrint(exec.buf, "{s}: {s}", .{ self.getName(), msg }) catch return msg; } const Code = enum(u8) { diff --git a/src/browser/webapi/EventTarget.zig b/src/browser/webapi/EventTarget.zig index d72908ba..6147d6a9 100644 --- a/src/browser/webapi/EventTarget.zig +++ b/src/browser/webapi/EventTarget.zig @@ -19,7 +19,7 @@ const std = @import("std"); const js = @import("../js/js.zig"); -const Page = @import("../Page.zig"); +const Session = @import("../Session.zig"); const EventManager = @import("../EventManager.zig"); const Event = @import("Event.zig"); @@ -52,8 +52,8 @@ pub const Type = union(enum) { websocket: *@import("net/WebSocket.zig"), }; -pub fn init(page: *Page) !*EventTarget { - return page._factory.create(EventTarget{ +pub fn init(session: *Session) !*EventTarget { + return session.factory.create(EventTarget{ ._type = .generic, }); } diff --git a/src/browser/webapi/File.zig b/src/browser/webapi/File.zig index e4c70662..9f4cfb46 100644 --- a/src/browser/webapi/File.zig +++ b/src/browser/webapi/File.zig @@ -20,7 +20,6 @@ const std = @import("std"); const lp = @import("lightpanda"); const js = @import("../js/js.zig"); -const Page = @import("../Page.zig"); const Session = @import("../Session.zig"); const Blob = @import("Blob.zig"); @@ -30,10 +29,10 @@ const File = @This(); _proto: *Blob, // TODO: Implement File API. -pub fn init(page: *Page) !*File { - const arena = try page.getArena(.tiny, "File"); - errdefer page.releaseArena(arena); - return page._factory.blob(arena, File{ ._proto = undefined }); +pub fn init(session: *Session) !*File { + const arena = try session.getArena(.tiny, "File"); + errdefer session.releaseArena(arena); + return session.factory.blob(arena, File{ ._proto = undefined }); } pub const JsApi = struct { diff --git a/src/browser/webapi/WorkerGlobalScope.zig b/src/browser/webapi/WorkerGlobalScope.zig index f8f7ec7f..a1c5a3c0 100644 --- a/src/browser/webapi/WorkerGlobalScope.zig +++ b/src/browser/webapi/WorkerGlobalScope.zig @@ -33,7 +33,6 @@ const Worker = @import("Worker.zig"); const Crypto = @import("Crypto.zig"); const Console = @import("Console.zig"); const EventTarget = @import("EventTarget.zig"); -const Performance = @import("Performance.zig"); const MessageEvent = @import("event/MessageEvent.zig"); const ErrorEvent = @import("event/ErrorEvent.zig"); @@ -67,7 +66,6 @@ _closed: bool = false, _proto: *EventTarget, _console: Console = .init, _crypto: Crypto = .init, -_performance: Performance, _on_error: ?JS.Function.Global = null, _on_rejection_handled: ?JS.Function.Global = null, _on_unhandled_rejection: ?JS.Function.Global = null, @@ -92,8 +90,7 @@ pub fn init(worker: *Worker, url: [:0]const u8) !*WorkerGlobalScope { ._proto = undefined, ._factory = factory, ._worker = worker, - ._event_manager = EventManagerBase.init(arena), - ._performance = .init(), + ._event_manager = .init(arena), }); errdefer factory.destroy(self); @@ -148,10 +145,6 @@ pub fn getCrypto(self: *WorkerGlobalScope) *Crypto { return &self._crypto; } -pub fn getPerformance(self: *WorkerGlobalScope) *Performance { - return &self._performance; -} - pub fn getOnError(self: *const WorkerGlobalScope) ?JS.Function.Global { return self._on_error; } @@ -414,7 +407,6 @@ pub const JsApi = struct { pub const self = bridge.accessor(WorkerGlobalScope.getSelf, null, .{}); pub const console = bridge.accessor(WorkerGlobalScope.getConsole, null, .{}); pub const crypto = bridge.accessor(WorkerGlobalScope.getCrypto, null, .{}); - pub const performance = bridge.accessor(WorkerGlobalScope.getPerformance, null, .{}); pub const onerror = bridge.accessor(WorkerGlobalScope.getOnError, WorkerGlobalScope.setOnError, .{}); pub const onrejectionhandled = bridge.accessor(WorkerGlobalScope.getOnRejectionHandled, WorkerGlobalScope.setOnRejectionHandled, .{}); diff --git a/src/browser/webapi/collections/NodeList.zig b/src/browser/webapi/collections/NodeList.zig index 82e23f9e..cd905df7 100644 --- a/src/browser/webapi/collections/NodeList.zig +++ b/src/browser/webapi/collections/NodeList.zig @@ -83,7 +83,7 @@ pub fn keys(self: *NodeList, page: *Page) !*KeyIterator { } pub fn values(self: *NodeList, page: *Page) !*ValueIterator { - return .init(.{ .list = self }, page.js.execution); + return .init(.{ .list = self }, page); } pub fn entries(self: *NodeList, page: *Page) !*EntryIterator { @@ -102,7 +102,6 @@ pub fn forEach(self: *NodeList, cb: js.Function, page: *Page) !void { }; } } -} const GenericIterator = @import("iterator.zig").Entry; pub const KeyIterator = GenericIterator(Iterator, "0"); diff --git a/src/browser/webapi/encoding/TextDecoder.zig b/src/browser/webapi/encoding/TextDecoder.zig index 89ef3023..16176d66 100644 --- a/src/browser/webapi/encoding/TextDecoder.zig +++ b/src/browser/webapi/encoding/TextDecoder.zig @@ -21,7 +21,6 @@ const lp = @import("lightpanda"); const js = @import("../../js/js.zig"); const html5ever = @import("../../parser/html5ever.zig"); -const Page = @import("../../Page.zig"); const Session = @import("../../Session.zig"); const Allocator = std.mem.Allocator; @@ -42,7 +41,7 @@ const InitOpts = struct { ignoreBOM: bool = false, }; -pub fn init(label_: ?[]const u8, opts_: ?InitOpts, page: *Page) !*TextDecoder { +pub fn init(label_: ?[]const u8, opts_: ?InitOpts, session: *Session) !*TextDecoder { const label = label_ orelse "utf-8"; const info = html5ever.encoding_for_label(label.ptr, label.len); @@ -56,8 +55,8 @@ pub fn init(label_: ?[]const u8, opts_: ?InitOpts, page: *Page) !*TextDecoder { return error.RangeError; } - const arena = try page.getArena(.large, "TextDecoder"); - errdefer page.releaseArena(arena); + const arena = try session.getArena(.large, "TextDecoder"); + errdefer session.releaseArena(arena); const opts = opts_ orelse InitOpts{}; const self = try arena.create(TextDecoder); diff --git a/src/browser/webapi/net/URLSearchParams.zig b/src/browser/webapi/net/URLSearchParams.zig index 95c0594e..b0a31760 100644 --- a/src/browser/webapi/net/URLSearchParams.zig +++ b/src/browser/webapi/net/URLSearchParams.zig @@ -23,7 +23,6 @@ const log = @import("../../../log.zig"); const String = @import("../../../string.zig").String; const Allocator = std.mem.Allocator; -const Page = @import("../../Page.zig"); const FormData = @import("FormData.zig"); const KeyValueList = @import("../KeyValueList.zig"); const Execution = js.Execution;