diff --git a/src/browser/js/Caller.zig b/src/browser/js/Caller.zig index d9a5fc55..e3962313 100644 --- a/src/browser/js/Caller.zig +++ b/src/browser/js/Caller.zig @@ -21,6 +21,7 @@ const log = @import("../../log.zig"); const string = @import("../../string.zig"); const Page = @import("../Page.zig"); +const WorkerGlobalScope = @import("../webapi/WorkerGlobalScope.zig"); const js = @import("js.zig"); const Local = @import("Local.zig"); @@ -54,9 +55,15 @@ fn initWithContext(self: *Caller, ctx: *Context, v8_context: *const v8.Context) .isolate = ctx.isolate, }, .prev_local = ctx.local, - .prev_context = ctx.page.js, + .prev_context = switch (ctx.global) { + .page => |page| page.js, + .worker => |worker| worker.js, + }, }; - ctx.page.js = ctx; + switch (ctx.global) { + .page => |page| page.js = ctx, + .worker => |worker| worker.js = ctx, + } ctx.local = &self.local; } @@ -87,7 +94,10 @@ pub fn deinit(self: *Caller) void { ctx.call_depth = call_depth; ctx.local = self.prev_local; - ctx.page.js = self.prev_context; + switch (ctx.global) { + .page => |page| page.js = self.prev_context, + .worker => |worker| worker.js = self.prev_context, + } } pub const CallOpts = struct { @@ -169,7 +179,7 @@ fn _getIndex(comptime T: type, local: *const Local, func: anytype, idx: u32, inf @field(args, "0") = try TaggedOpaque.fromJS(*T, info.getThis()); @field(args, "1") = idx; if (@typeInfo(F).@"fn".params.len == 3) { - @field(args, "2") = local.ctx.page; + @field(args, "2") = getGlobalArg(@TypeOf(args.@"2"), local.ctx); } const ret = @call(.auto, func, args); return handleIndexedReturn(T, F, true, local, ret, info, opts); @@ -196,7 +206,7 @@ fn _getNamedIndex(comptime T: type, local: *const Local, func: anytype, name: *c @field(args, "0") = try TaggedOpaque.fromJS(*T, info.getThis()); @field(args, "1") = try nameToString(local, @TypeOf(args.@"1"), name); if (@typeInfo(F).@"fn".params.len == 3) { - @field(args, "2") = local.ctx.page; + @field(args, "2") = getGlobalArg(@TypeOf(args.@"2"), local.ctx); } const ret = @call(.auto, func, args); return handleIndexedReturn(T, F, true, local, ret, info, opts); @@ -224,7 +234,7 @@ fn _setNamedIndex(comptime T: type, local: *const Local, func: anytype, name: *c @field(args, "1") = try nameToString(local, @TypeOf(args.@"1"), name); @field(args, "2") = try local.jsValueToZig(@TypeOf(@field(args, "2")), js_value); if (@typeInfo(F).@"fn".params.len == 4) { - @field(args, "3") = local.ctx.page; + @field(args, "3") = getGlobalArg(@TypeOf(args.@"3"), local.ctx); } const ret = @call(.auto, func, args); return handleIndexedReturn(T, F, false, local, ret, info, opts); @@ -250,7 +260,7 @@ fn _deleteNamedIndex(comptime T: type, local: *const Local, func: anytype, name: @field(args, "0") = try TaggedOpaque.fromJS(*T, info.getThis()); @field(args, "1") = try nameToString(local, @TypeOf(args.@"1"), name); if (@typeInfo(F).@"fn".params.len == 3) { - @field(args, "2") = local.ctx.page; + @field(args, "2") = getGlobalArg(@TypeOf(args.@"2"), local.ctx); } const ret = @call(.auto, func, args); return handleIndexedReturn(T, F, false, local, ret, info, opts); @@ -276,7 +286,7 @@ fn _getEnumerator(comptime T: type, local: *const Local, func: anytype, info: Pr var args: ParameterTypes(F) = undefined; @field(args, "0") = try TaggedOpaque.fromJS(*T, info.getThis()); if (@typeInfo(F).@"fn".params.len == 2) { - @field(args, "1") = local.ctx.page; + @field(args, "1") = getGlobalArg(@TypeOf(args.@"1"), local.ctx); } const ret = @call(.auto, func, args); return handleIndexedReturn(T, F, true, local, ret, info, opts); @@ -434,10 +444,38 @@ fn isPage(comptime T: type) bool { return T == *Page or T == *const Page; } +fn isWorker(comptime T: type) bool { + return T == *WorkerGlobalScope or T == *const WorkerGlobalScope; +} + fn isExecution(comptime T: type) bool { return T == *js.Execution or T == *const js.Execution; } +fn getGlobalArg(comptime T: type, ctx: *Context) T { + if (comptime isPage(T)) { + return switch (ctx.global) { + .page => |page| page, + .worker => { + if (comptime IS_DEBUG) std.debug.assert(false); + unreachable; + }, + }; + } + + if (comptime isWorker(T)) { + return switch (ctx.global) { + .page => { + if (comptime IS_DEBUG) std.debug.assert(false); + unreachable; + }, + .worker => |worker| worker, + }; + } + + @compileError("Unsupported global arg type: " ++ @typeName(T)); +} + // These wrap the raw v8 C API to provide a cleaner interface. pub const FunctionCallbackInfo = struct { handle: *const v8.FunctionCallbackInfo, @@ -706,16 +744,17 @@ fn getArgs(comptime F: type, comptime offset: usize, local: *const Local, info: return args; } - // If the last parameter is the Page, set it, and exclude it + // If the last parameter is the Page or Worker, set it, and exclude it // from our params slice, because we don't want to bind it to // a JS argument - if (comptime isPage(params[params.len - 1].type.?)) { - @field(args, tupleFieldName(params.len - 1 + offset)) = local.ctx.page; + const LastParamType = params[params.len - 1].type.?; + if (comptime isPage(LastParamType) or isWorker(LastParamType)) { + @field(args, tupleFieldName(params.len - 1 + offset)) = getGlobalArg(LastParamType, local.ctx); break :blk params[0 .. params.len - 1]; } // If the last parameter is Execution, set it from the context - if (comptime isExecution(params[params.len - 1].type.?)) { + if (comptime isExecution(LastParamType)) { @field(args, tupleFieldName(params.len - 1 + offset)) = &local.ctx.execution; break :blk params[0 .. params.len - 1]; } diff --git a/src/browser/js/Context.zig b/src/browser/js/Context.zig index b83554db..3f41053e 100644 --- a/src/browser/js/Context.zig +++ b/src/browser/js/Context.zig @@ -30,6 +30,7 @@ const Execution = @import("Execution.zig"); const Page = @import("../Page.zig"); const Session = @import("../Session.zig"); const ScriptManager = @import("../ScriptManager.zig"); +const WorkerGlobalScope = @import("../webapi/WorkerGlobalScope.zig"); const v8 = js.v8; const Caller = js.Caller; @@ -38,12 +39,17 @@ const Allocator = std.mem.Allocator; const IS_DEBUG = @import("builtin").mode == .Debug; -// Loosely maps to a Browser Page. +// Loosely maps to a Browser Page or Worker. const Context = @This(); +pub const GlobalScope = union(enum) { + page: *Page, + worker: *WorkerGlobalScope, +}; + id: usize, env: *Env, -page: *Page, +global: GlobalScope, session: *Session, isolate: js.Isolate, @@ -262,7 +268,16 @@ pub fn toLocal(self: *Context, global: anytype) js.Local.ToLocalReturnType(@Type } pub fn getIncumbent(self: *Context) *Page { - return fromC(v8.v8__Isolate__GetIncumbentContext(self.env.isolate.handle).?).?.page; + const ctx = fromC(v8.v8__Isolate__GetIncumbentContext(self.env.isolate.handle).?).?; + return switch (ctx.global) { + .page => |page| page, + .worker => { + if (comptime IS_DEBUG) { + std.debug.assert(false); + } + unreachable; + }, + }; } pub fn stringToPersistedFunction( @@ -532,7 +547,10 @@ pub fn dynamicModuleCallback( if (resource_value.isNullOrUndefined()) { // will only be null / undefined in extreme cases (e.g. WPT tests) // where you're - break :blk self.page.base(); + break :blk switch (self.global) { + .page => |page| page.base(), + .worker => |worker| worker.base(), + }; } break :blk js.String.toSliceZ(.{ .local = &local, .handle = resource_name.? }) catch |err| { @@ -872,17 +890,26 @@ pub fn enter(self: *Context, hs: *js.HandleScope) Entered { const isolate = self.isolate; js.HandleScope.init(hs, isolate); - const page = self.page; - const original = page.js; - page.js = self; + const original = switch (self.global) { + .page => |page| blk: { + const orig = page.js; + page.js = self; + break :blk orig; + }, + .worker => |worker| blk: { + const orig = worker.js; + worker.js = self; + break :blk orig; + }, + }; const handle: *const v8.Context = @ptrCast(v8.v8__Global__Get(&self.handle, isolate.handle)); v8.v8__Context__Enter(handle); - return .{ .original = original, .handle = handle, .handle_scope = hs }; + return .{ .original = original, .handle = handle, .handle_scope = hs, .global = self.global }; } const Entered = struct { - // the context we should restore on the page + // the context we should restore on the page/worker original: *Context, // the handle of the entered context @@ -890,8 +917,13 @@ const Entered = struct { handle_scope: *js.HandleScope, + global: GlobalScope, + pub fn exit(self: Entered) void { - self.original.page.js = self.original; + switch (self.global) { + .page => |page| page.js = self.original, + .worker => |worker| worker.js = self.original, + } v8.v8__Context__Exit(self.handle); self.handle_scope.deinit(); } @@ -900,7 +932,15 @@ const Entered = struct { pub fn queueMutationDelivery(self: *Context) !void { self.enqueueMicrotask(struct { fn run(ctx: *Context) void { - ctx.page.deliverMutations(); + switch (ctx.global) { + .page => |page| page.deliverMutations(), + .worker => { + if (comptime IS_DEBUG) { + std.debug.assert(false); + } + unreachable; + }, + } } }.run); } @@ -908,7 +948,15 @@ pub fn queueMutationDelivery(self: *Context) !void { pub fn queueIntersectionChecks(self: *Context) !void { self.enqueueMicrotask(struct { fn run(ctx: *Context) void { - ctx.page.performScheduledIntersectionChecks(); + switch (ctx.global) { + .page => |page| page.performScheduledIntersectionChecks(), + .worker => { + if (comptime IS_DEBUG) { + std.debug.assert(false); + } + unreachable; + }, + } } }.run); } @@ -916,7 +964,15 @@ pub fn queueIntersectionChecks(self: *Context) !void { pub fn queueIntersectionDelivery(self: *Context) !void { self.enqueueMicrotask(struct { fn run(ctx: *Context) void { - ctx.page.deliverIntersections(); + switch (ctx.global) { + .page => |page| page.deliverIntersections(), + .worker => { + if (comptime IS_DEBUG) { + std.debug.assert(false); + } + unreachable; + }, + } } }.run); } @@ -924,7 +980,15 @@ pub fn queueIntersectionDelivery(self: *Context) !void { pub fn queueSlotchangeDelivery(self: *Context) !void { self.enqueueMicrotask(struct { fn run(ctx: *Context) void { - ctx.page.deliverSlotchangeEvents(); + switch (ctx.global) { + .page => |page| page.deliverSlotchangeEvents(), + .worker => { + if (comptime IS_DEBUG) { + std.debug.assert(false); + } + unreachable; + }, + } } }.run); } diff --git a/src/browser/js/Env.zig b/src/browser/js/Env.zig index 24cc5363..4b4369ea 100644 --- a/src/browser/js/Env.zig +++ b/src/browser/js/Env.zig @@ -316,7 +316,7 @@ pub fn createContext(self: *Env, page: *Page, params: ContextParams) !*Context { const context = try context_arena.create(Context); context.* = .{ .env = self, - .page = page, + .global = .{ .page = page }, .origin = origin, .id = context_id, .session = session, @@ -332,8 +332,16 @@ pub fn createContext(self: *Env, page: *Page, params: ContextParams) !*Context { .identity_arena = params.identity_arena, .execution = undefined, }; - // Initialize execution after context is created since it contains self-references - context.execution = js.Execution.fromContext(context); + + context.execution = .{ + .buf = &page.buf, + .context = context, + .arena = page.arena, + .call_arena = params.call_arena, + ._factory = page._factory, + ._scheduler = &context.scheduler, + .url = &page.url, + }; { // Multiple contexts can be created for the same Window (via CDP). We only @@ -531,13 +539,19 @@ fn promiseRejectCallback(message_handle: v8.PromiseRejectMessage) callconv(.c) v .call_arena = ctx.call_arena, }; - const page = ctx.page; - page.window.unhandledPromiseRejection(promise_event == v8.kPromiseRejectWithNoHandler, .{ - .local = &local, - .handle = &message_handle, - }, page) catch |err| { - log.warn(.browser, "unhandled rejection handler", .{ .err = err }); - }; + switch (ctx.global) { + .page => |page| { + page.window.unhandledPromiseRejection(promise_event == v8.kPromiseRejectWithNoHandler, .{ + .local = &local, + .handle = &message_handle, + }, page) catch |err| { + log.warn(.browser, "unhandled rejection handler", .{ .err = err }); + }; + }, + .worker => { + // TODO: Worker promise rejection handling + }, + } } fn fatalCallback(c_location: [*c]const u8, c_message: [*c]const u8) callconv(.c) void { diff --git a/src/browser/js/Execution.zig b/src/browser/js/Execution.zig index 6c807aa2..7d6ad6fd 100644 --- a/src/browser/js/Execution.zig +++ b/src/browser/js/Execution.zig @@ -43,14 +43,5 @@ call_arena: Allocator, _scheduler: *Scheduler, buf: []u8, -pub fn fromContext(ctx: *Context) Execution { - const page = ctx.page; - return .{ - .context = ctx, - ._factory = page._factory, - .arena = page.arena, - .call_arena = ctx.call_arena, - ._scheduler = &ctx.scheduler, - .buf = &page.buf, - }; -} +// Pointer to the url field (Page or WorkerGlobalScope) - allows access to current url even after navigation +url: *[:0]const u8, diff --git a/src/browser/js/Local.zig b/src/browser/js/Local.zig index c9a3b880..42b430b6 100644 --- a/src/browser/js/Local.zig +++ b/src/browser/js/Local.zig @@ -332,7 +332,18 @@ pub fn zigValueToJs(self: *const Local, value: anytype, comptime opts: CallOpts) } if (@typeInfo(ptr.child) == .@"struct" and @hasDecl(ptr.child, "runtimeGenericWrap")) { - const wrap = try value.runtimeGenericWrap(self.ctx.page); + const page = switch (self.ctx.global) { + .page => |p| p, + .worker => { + // No Worker-related API currently uses this, so haven't + // added support for it + if (comptime IS_DEBUG) { + std.debug.assert(false); + } + unreachable; + }, + }; + const wrap = try value.runtimeGenericWrap(page); return self.zigValueToJs(wrap, opts); } @@ -409,7 +420,18 @@ pub fn zigValueToJs(self: *const Local, value: anytype, comptime opts: CallOpts) // zig fmt: on if (@hasDecl(T, "runtimeGenericWrap")) { - const wrap = try value.runtimeGenericWrap(self.ctx.page); + const page = switch (self.ctx.global) { + .page => |p| p, + .worker => { + // No Worker-related API currently uses this, so haven't + // added support for it + if (comptime IS_DEBUG) { + std.debug.assert(false); + } + unreachable; + }, + }; + const wrap = try value.runtimeGenericWrap(page); return self.zigValueToJs(wrap, opts); } diff --git a/src/browser/js/bridge.zig b/src/browser/js/bridge.zig index 24845d11..dc5e6910 100644 --- a/src/browser/js/bridge.zig +++ b/src/browser/js/bridge.zig @@ -400,14 +400,18 @@ pub fn unknownWindowPropertyCallback(c_name: ?*const v8.Name, handle: ?*const v8 return 0; }; - const page = local.ctx.page; - const document = page.document; - - if (document.getElementById(property, page)) |el| { - const js_val = local.zigValueToJs(el, .{}) catch return 0; - var pc = Caller.PropertyCallbackInfo{ .handle = handle.? }; - pc.getReturnValue().set(js_val); - return 1; + // Only Page contexts have document.getElementById lookup + switch (local.ctx.global) { + .page => |page| { + const document = page.document; + if (document.getElementById(property, page)) |el| { + const js_val = local.zigValueToJs(el, .{}) catch return 0; + var pc = Caller.PropertyCallbackInfo{ .handle = handle.? }; + pc.getReturnValue().set(js_val); + return 1; + } + }, + .worker => {}, // no global lookup in a worker } if (comptime IS_DEBUG) { @@ -445,7 +449,8 @@ pub fn unknownWindowPropertyCallback(c_name: ?*const v8.Name, handle: ?*const v8 .{ "ApplePaySession", {} }, }); if (!ignored.has(property)) { - const key = std.fmt.bufPrint(&local.ctx.page.buf, "Window:{s}", .{property}) catch return 0; + var buf: [2048]u8 = undefined; + const key = std.fmt.bufPrint(&buf, "Window:{s}", .{property}) catch return 0; logUnknownProperty(local, key) catch return 0; } } @@ -508,7 +513,8 @@ pub fn unknownObjectPropertyCallback(comptime JsApi: type) *const fn (?*const v8 const ignored = std.StaticStringMap(void).initComptime(.{}); if (!ignored.has(property)) { - const key = std.fmt.bufPrint(&local.ctx.page.buf, "{s}:{s}", .{ if (@hasDecl(JsApi.Meta, "name")) JsApi.Meta.name else @typeName(JsApi), property }) catch return 0; + var buf: [2048]u8 = undefined; + const key = std.fmt.bufPrint(&buf, "{s}:{s}", .{ if (@hasDecl(JsApi.Meta, "name")) JsApi.Meta.name else @typeName(JsApi), property }) catch return 0; logUnknownProperty(local, key) catch return 0; } // not intercepted diff --git a/src/browser/webapi/KeyValueList.zig b/src/browser/webapi/KeyValueList.zig index 7bfa5807..85e21464 100644 --- a/src/browser/webapi/KeyValueList.zig +++ b/src/browser/webapi/KeyValueList.zig @@ -34,7 +34,7 @@ pub fn registerTypes() []const type { }; } -const Normalizer = *const fn ([]const u8, *Page) []const u8; +const Normalizer = *const fn ([]const u8, []u8) []const u8; pub const Entry = struct { name: String, @@ -62,14 +62,14 @@ pub fn copy(arena: Allocator, original: KeyValueList) !KeyValueList { return list; } -pub fn fromJsObject(arena: Allocator, js_obj: js.Object, comptime normalizer: ?Normalizer, page: *Page) !KeyValueList { +pub fn fromJsObject(arena: Allocator, js_obj: js.Object, comptime normalizer: ?Normalizer, buf: []u8) !KeyValueList { var it = try js_obj.nameIterator(); var list = KeyValueList.init(); try list.ensureTotalCapacity(arena, it.count); while (try it.next()) |name| { const js_value = try js_obj.get(name); - const normalized = if (comptime normalizer) |n| n(name, page) else name; + const normalized = if (comptime normalizer) |n| n(name, buf) else name; list._entries.appendAssumeCapacity(.{ .name = try String.init(arena, normalized, .{}), @@ -80,12 +80,12 @@ pub fn fromJsObject(arena: Allocator, js_obj: js.Object, comptime normalizer: ?N return list; } -pub fn fromArray(arena: Allocator, kvs: []const [2][]const u8, comptime normalizer: ?Normalizer, page: *Page) !KeyValueList { +pub fn fromArray(arena: Allocator, kvs: []const [2][]const u8, comptime normalizer: ?Normalizer, buf: []u8) !KeyValueList { var list = KeyValueList.init(); try list.ensureTotalCapacity(arena, kvs.len); for (kvs) |pair| { - const normalized = if (comptime normalizer) |n| n(pair[0], page) else pair[0]; + const normalized = if (comptime normalizer) |n| n(pair[0], buf) else pair[0]; list._entries.appendAssumeCapacity(.{ .name = try String.init(arena, normalized, .{}), diff --git a/src/browser/webapi/URL.zig b/src/browser/webapi/URL.zig index 3070b7a1..c2d40765 100644 --- a/src/browser/webapi/URL.zig +++ b/src/browser/webapi/URL.zig @@ -39,7 +39,7 @@ pub const eqlDocument = @import("../URL.zig").eqlDocument; pub fn init(url: [:0]const u8, base_: ?[:0]const u8, exec: *const Execution) !*URL { const arena = exec.arena; - const page = exec.context.page; + const context_url = exec.url.*; if (std.mem.eql(u8, url, "about:blank")) { return exec._factory.create(URL{ @@ -50,9 +50,9 @@ pub fn init(url: [:0]const u8, base_: ?[:0]const u8, exec: *const Execution) !*U const url_is_absolute = @import("../URL.zig").isCompleteHTTPUrl(url); const base = if (base_) |b| blk: { - // If URL is absolute, base is ignored (but we still use page.url internally) + // If URL is absolute, base is ignored (but we still use context url internally) if (url_is_absolute) { - break :blk page.url; + break :blk context_url; } // For relative URLs, base must be a valid absolute URL if (!@import("../URL.zig").isCompleteHTTPUrl(b)) { @@ -61,7 +61,7 @@ pub fn init(url: [:0]const u8, base_: ?[:0]const u8, exec: *const Execution) !*U break :blk b; } else if (!url_is_absolute) { return error.TypeError; - } else page.url; + } else context_url; const raw = try resolve(arena, base, url, .{ .always_dupe = true }); @@ -149,8 +149,7 @@ pub fn getSearchParams(self: *URL, exec: *const Execution) !*URLSearchParams { } pub fn setHref(self: *URL, value: []const u8, exec: *const Execution) !void { - const page = exec.context.page; - const base = if (U.isCompleteHTTPUrl(value)) page.url else self._raw; + const base = if (U.isCompleteHTTPUrl(value)) exec.url.* else self._raw; const raw = try U.resolve(self._arena orelse exec.arena, base, value, .{ .always_dupe = true }); self._raw = raw; diff --git a/src/browser/webapi/WorkerGlobalScope.zig b/src/browser/webapi/WorkerGlobalScope.zig index 9ad3b0c1..6ea9bd90 100644 --- a/src/browser/webapi/WorkerGlobalScope.zig +++ b/src/browser/webapi/WorkerGlobalScope.zig @@ -16,23 +16,41 @@ // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . -const js = @import("../js/js.zig"); +const std = @import("std"); +const JS = @import("../js/js.zig"); const base64 = @import("encoding/base64.zig"); const Console = @import("Console.zig"); const Crypto = @import("Crypto.zig"); const EventTarget = @import("EventTarget.zig"); +const Factory = @import("../Factory.zig"); const Performance = @import("Performance.zig"); +const Session = @import("../Session.zig"); + +const Allocator = std.mem.Allocator; const WorkerGlobalScope = @This(); +// Infrastructure fields (similar to Page) +_session: *Session, +_factory: *Factory, +arena: Allocator, +url: [:0]const u8, +buf: [1024]u8 = undefined, // same size as page.buf +js: *JS.Context = undefined, + +// WebAPI fields _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, +_on_error: ?JS.Function.Global = null, +_on_rejection_handled: ?JS.Function.Global = null, +_on_unhandled_rejection: ?JS.Function.Global = null, + +pub fn base(self: *const WorkerGlobalScope) [:0]const u8 { + return self.url; +} pub fn asEventTarget(self: *WorkerGlobalScope) *EventTarget { return self._proto; @@ -54,7 +72,7 @@ pub fn getPerformance(self: *WorkerGlobalScope) *Performance { return &self._performance; } -pub fn getOnError(self: *const WorkerGlobalScope) ?js.Function.Global { +pub fn getOnError(self: *const WorkerGlobalScope) ?JS.Function.Global { return self._on_error; } @@ -62,7 +80,7 @@ pub fn setOnError(self: *WorkerGlobalScope, setter: ?FunctionSetter) void { self._on_error = getFunctionFromSetter(setter); } -pub fn getOnRejectionHandled(self: *const WorkerGlobalScope) ?js.Function.Global { +pub fn getOnRejectionHandled(self: *const WorkerGlobalScope) ?JS.Function.Global { return self._on_rejection_handled; } @@ -70,7 +88,7 @@ pub fn setOnRejectionHandled(self: *WorkerGlobalScope, setter: ?FunctionSetter) self._on_rejection_handled = getFunctionFromSetter(setter); } -pub fn getOnUnhandledRejection(self: *const WorkerGlobalScope) ?js.Function.Global { +pub fn getOnUnhandledRejection(self: *const WorkerGlobalScope) ?JS.Function.Global { return self._on_unhandled_rejection; } @@ -78,15 +96,15 @@ pub fn setOnUnhandledRejection(self: *WorkerGlobalScope, setter: ?FunctionSetter self._on_unhandled_rejection = getFunctionFromSetter(setter); } -pub fn btoa(_: *const WorkerGlobalScope, input: []const u8, exec: *js.Execution) ![]const u8 { +pub fn btoa(_: *const WorkerGlobalScope, input: []const u8, exec: *JS.Execution) ![]const u8 { return base64.encode(exec.call_arena, input); } -pub fn atob(_: *const WorkerGlobalScope, input: []const u8, exec: *js.Execution) ![]const u8 { +pub fn atob(_: *const WorkerGlobalScope, input: []const u8, exec: *JS.Execution) ![]const u8 { return base64.decode(exec.call_arena, input); } -pub fn structuredClone(_: *const WorkerGlobalScope, value: js.Value) !js.Value { +pub fn structuredClone(_: *const WorkerGlobalScope, value: JS.Value) !JS.Value { return value.structuredClone(); } @@ -96,11 +114,11 @@ pub fn structuredClone(_: *const WorkerGlobalScope, value: js.Value) !js.Value { // TODO: Timer functions - need scheduler integration const FunctionSetter = union(enum) { - func: js.Function.Global, - anything: js.Value, + func: JS.Function.Global, + anything: JS.Value, }; -fn getFunctionFromSetter(setter_: ?FunctionSetter) ?js.Function.Global { +fn getFunctionFromSetter(setter_: ?FunctionSetter) ?JS.Function.Global { const setter = setter_ orelse return null; return switch (setter) { .func => |func| func, @@ -109,7 +127,7 @@ fn getFunctionFromSetter(setter_: ?FunctionSetter) ?js.Function.Global { } pub const JsApi = struct { - pub const bridge = js.Bridge(WorkerGlobalScope); + pub const bridge = JS.Bridge(WorkerGlobalScope); pub const Meta = struct { pub const name = "WorkerGlobalScope"; diff --git a/src/browser/webapi/collections/ChildNodes.zig b/src/browser/webapi/collections/ChildNodes.zig index f23fe189..5d7403fd 100644 --- a/src/browser/webapi/collections/ChildNodes.zig +++ b/src/browser/webapi/collections/ChildNodes.zig @@ -23,7 +23,6 @@ const Node = @import("../Node.zig"); const Page = @import("../../Page.zig"); const Session = @import("../../Session.zig"); const GenericIterator = @import("iterator.zig").Entry; -const Execution = js.Execution; // Optimized for node.childNodes, which has to be a live list. // No need to go through a TreeWalker or add any filtering. @@ -138,9 +137,9 @@ const Iterator = struct { const Entry = struct { u32, *Node }; - pub fn next(self: *Iterator, exec: *const Execution) !?Entry { + pub fn next(self: *Iterator, page: *const Page) !?Entry { const index = self.index; - const node = try self.list.getAtIndex(index, exec.context.page) orelse return null; + const node = try self.list.getAtIndex(index, page) orelse return null; self.index = index + 1; return .{ index, node }; } diff --git a/src/browser/webapi/collections/DOMTokenList.zig b/src/browser/webapi/collections/DOMTokenList.zig index a0409bcf..cab25447 100644 --- a/src/browser/webapi/collections/DOMTokenList.zig +++ b/src/browser/webapi/collections/DOMTokenList.zig @@ -24,7 +24,6 @@ const js = @import("../../js/js.zig"); const Page = @import("../../Page.zig"); const Element = @import("../Element.zig"); const GenericIterator = @import("iterator.zig").Entry; -const Execution = js.Execution; pub const DOMTokenList = @This(); @@ -203,16 +202,16 @@ pub fn setValue(self: *DOMTokenList, value: String, page: *Page) !void { try self._element.setAttribute(self._attribute_name, value, page); } -pub fn keys(self: *DOMTokenList, exec: *const Execution) !*KeyIterator { - return .init(.{ .list = self }, exec); +pub fn keys(self: *DOMTokenList, page: *Page) !*KeyIterator { + return .init(.{ .list = self }, page); } -pub fn values(self: *DOMTokenList, exec: *const Execution) !*ValueIterator { - return .init(.{ .list = self }, exec); +pub fn values(self: *DOMTokenList, page: *Page) !*ValueIterator { + return .init(.{ .list = self }, page); } -pub fn entries(self: *DOMTokenList, exec: *const Execution) !*EntryIterator { - return .init(.{ .list = self }, exec); +pub fn entries(self: *DOMTokenList, page: *Page) !*EntryIterator { + return .init(.{ .list = self }, page); } pub fn forEach(self: *DOMTokenList, cb_: js.Function, js_this_: ?js.Object, page: *Page) !void { @@ -282,9 +281,9 @@ const Iterator = struct { const Entry = struct { u32, []const u8 }; - pub fn next(self: *Iterator, exec: *const Execution) !?Entry { + pub fn next(self: *Iterator, page: *Page) !?Entry { const index = self.index; - const node = try self.list.item(index, exec.context.page) orelse return null; + const node = try self.list.item(index, page) orelse return null; self.index = index + 1; return .{ index, node }; } diff --git a/src/browser/webapi/collections/NodeList.zig b/src/browser/webapi/collections/NodeList.zig index 716c12d8..3d298de2 100644 --- a/src/browser/webapi/collections/NodeList.zig +++ b/src/browser/webapi/collections/NodeList.zig @@ -24,7 +24,6 @@ const js = @import("../../js/js.zig"); const Page = @import("../../Page.zig"); const Session = @import("../../Session.zig"); const Node = @import("../Node.zig"); -const Execution = js.Execution; const ChildNodes = @import("ChildNodes.zig"); const RadioNodeList = @import("RadioNodeList.zig"); @@ -79,28 +78,28 @@ pub fn getAtIndex(self: *NodeList, index: usize, page: *Page) !?*Node { }; } -pub fn keys(self: *NodeList, exec: *const Execution) !*KeyIterator { - return .init(.{ .list = self }, exec); +pub fn keys(self: *NodeList, page: *Page) !*KeyIterator { + return .init(.{ .list = self }, page); } -pub fn values(self: *NodeList, exec: *const Execution) !*ValueIterator { - return .init(.{ .list = self }, exec); +pub fn values(self: *NodeList, page: *Page) !*ValueIterator { + return .init(.{ .list = self }, page); } -pub fn entries(self: *NodeList, exec: *const Execution) !*EntryIterator { - return .init(.{ .list = self }, exec); +pub fn entries(self: *NodeList, page: *Page) !*EntryIterator { + return .init(.{ .list = self }, page); } -pub fn forEach(self: *NodeList, cb: js.Function, exec: *const Execution) !void { +pub fn forEach(self: *NodeList, cb: js.Function, page: *Page) !void { var i: i32 = 0; - var it = try self.values(exec); + var it = try self.values(page); // the iterator takes a reference against our list - defer self.releaseRef(exec.context.page._session); + defer self.releaseRef(page._session); while (true) : (i += 1) { - const next = try it.next(exec); + const next = try it.next(page); if (next.done) { return; } @@ -136,9 +135,9 @@ const Iterator = struct { self.list.acquireRef(); } - pub fn next(self: *Iterator, exec: *const Execution) !?Entry { + pub fn next(self: *Iterator, page: *Page) !?Entry { const index = self.index; - const node = try self.list.getAtIndex(index, exec.context.page) orelse return null; + const node = try self.list.getAtIndex(index, page) orelse return null; self.index = index + 1; return .{ index, node }; } diff --git a/src/browser/webapi/net/Headers.zig b/src/browser/webapi/net/Headers.zig index 615cc255..438e3532 100644 --- a/src/browser/webapi/net/Headers.zig +++ b/src/browser/webapi/net/Headers.zig @@ -20,8 +20,8 @@ pub const InitOpts = union(enum) { pub fn init(opts_: ?InitOpts, page: *Page) !*Headers { const list = if (opts_) |opts| switch (opts) { .obj => |obj| try KeyValueList.copy(page.arena, obj._list), - .js_obj => |js_obj| try KeyValueList.fromJsObject(page.arena, js_obj, normalizeHeaderName, page), - .strings => |kvs| try KeyValueList.fromArray(page.arena, kvs, normalizeHeaderName, page), + .js_obj => |js_obj| try KeyValueList.fromJsObject(page.arena, js_obj, normalizeHeaderName, &page.buf), + .strings => |kvs| try KeyValueList.fromArray(page.arena, kvs, normalizeHeaderName, &page.buf), } else KeyValueList.init(); return page._factory.create(Headers{ @@ -30,17 +30,17 @@ pub fn init(opts_: ?InitOpts, page: *Page) !*Headers { } pub fn append(self: *Headers, name: []const u8, value: []const u8, page: *Page) !void { - const normalized_name = normalizeHeaderName(name, page); + const normalized_name = normalizeHeaderName(name, &page.buf); try self._list.append(page.arena, normalized_name, value); } pub fn delete(self: *Headers, name: []const u8, page: *Page) void { - const normalized_name = normalizeHeaderName(name, page); + const normalized_name = normalizeHeaderName(name, &page.buf); self._list.delete(normalized_name, null); } pub fn get(self: *const Headers, name: []const u8, page: *Page) !?[]const u8 { - const normalized_name = normalizeHeaderName(name, page); + const normalized_name = normalizeHeaderName(name, &page.buf); const all_values = try self._list.getAll(page.call_arena, normalized_name); if (all_values.len == 0) { @@ -53,12 +53,12 @@ pub fn get(self: *const Headers, name: []const u8, page: *Page) !?[]const u8 { } pub fn has(self: *const Headers, name: []const u8, page: *Page) bool { - const normalized_name = normalizeHeaderName(name, page); + const normalized_name = normalizeHeaderName(name, &page.buf); return self._list.has(normalized_name); } pub fn set(self: *Headers, name: []const u8, value: []const u8, page: *Page) !void { - const normalized_name = normalizeHeaderName(name, page); + const normalized_name = normalizeHeaderName(name, &page.buf); try self._list.set(page.arena, normalized_name, value); } @@ -94,11 +94,11 @@ pub fn populateHttpHeader(self: *Headers, allocator: Allocator, http_headers: *h } } -fn normalizeHeaderName(name: []const u8, page: *Page) []const u8 { - if (name.len > page.buf.len) { +fn normalizeHeaderName(name: []const u8, buf: []u8) []const u8 { + if (name.len > buf.len) { return name; } - return std.ascii.lowerString(&page.buf, name); + return std.ascii.lowerString(buf, name); } pub const JsApi = struct { diff --git a/src/browser/webapi/net/URLSearchParams.zig b/src/browser/webapi/net/URLSearchParams.zig index 101c82e3..733dc645 100644 --- a/src/browser/webapi/net/URLSearchParams.zig +++ b/src/browser/webapi/net/URLSearchParams.zig @@ -53,7 +53,7 @@ pub fn init(opts_: ?InitOpts, exec: *const Execution) !*URLSearchParams { } if (js_val.isObject()) { // normalizer is null, so page won't be used - break :blk try KeyValueList.fromJsObject(arena, js_val.toObject(), null, exec.context.page); + break :blk try KeyValueList.fromJsObject(arena, js_val.toObject(), null, exec.buf); } if (js_val.isString()) |js_str| { break :blk try paramsFromString(arena, try js_str.toSliceWithAlloc(arena), exec.buf);