mirror of
https://github.com/lightpanda-io/browser.git
synced 2026-04-03 16:10:29 +00:00
Make context work with Page of WGS
A context can be created for either a Page or a Worker. This removes the Context.page field and replaces it with a Context.global union.
This commit is contained in:
@@ -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];
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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, .{}),
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -16,23 +16,41 @@
|
||||
// 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 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";
|
||||
|
||||
@@ -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 };
|
||||
}
|
||||
|
||||
@@ -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 };
|
||||
}
|
||||
|
||||
@@ -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 };
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user