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).
This commit is contained in:
Karl Seguin
2026-04-08 09:23:11 +08:00
parent d1d561f5c1
commit 197de3dc0c
9 changed files with 47 additions and 62 deletions

View File

@@ -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.

View File

@@ -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});
}
}

View File

@@ -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) {

View File

@@ -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,
});
}

View File

@@ -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 {

View File

@@ -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, .{});

View File

@@ -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");

View File

@@ -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);

View File

@@ -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;