diff --git a/.github/workflows/e2e-test.yml b/.github/workflows/e2e-test.yml index a47829bc..f069ebb5 100644 --- a/.github/workflows/e2e-test.yml +++ b/.github/workflows/e2e-test.yml @@ -51,8 +51,11 @@ jobs: - uses: ./.github/actions/install + - name: v8 snapshot + run: zig build -Dprebuilt_v8_path=v8/libc_v8.a -Doptimize=ReleaseFast snapshot_creator -- src/snapshot.bin + - name: zig build release - run: zig build -Dprebuilt_v8_path=v8/libc_v8.a -Doptimize=ReleaseFast -Dcpu=x86_64 + run: zig build -Dsnapshot_path=../../snapshot.bin -Dprebuilt_v8_path=v8/libc_v8.a -Doptimize=ReleaseFast -Dcpu=x86_64 - name: upload artifact uses: actions/upload-artifact@v7 diff --git a/flake.nix b/flake.nix index 330bbdf0..d306ae09 100644 --- a/flake.nix +++ b/flake.nix @@ -70,7 +70,7 @@ gcc.cc.lib crtFiles - # Libaries + # Libraries expat.dev glib.dev glibc.dev diff --git a/src/App.zig b/src/App.zig index 41ca9822..8e3fe0c9 100644 --- a/src/App.zig +++ b/src/App.zig @@ -55,15 +55,15 @@ pub fn init(allocator: Allocator, config: *const Config) !*App { .arena_pool = undefined, }; - app.network = try Network.init(allocator, app, config); - errdefer app.network.deinit(); - app.platform = try Platform.init(); errdefer app.platform.deinit(); app.snapshot = try Snapshot.load(); errdefer app.snapshot.deinit(); + app.network = try Network.init(allocator, app, config); + errdefer app.network.deinit(); + app.app_dir_path = getAndMakeAppDir(allocator); app.telemetry = try Telemetry.init(app, config.mode); diff --git a/src/Config.zig b/src/Config.zig index 500e52b2..9e5f222c 100644 --- a/src/Config.zig +++ b/src/Config.zig @@ -180,6 +180,20 @@ pub fn webBotAuth(self: *const Config) ?WebBotAuthConfig { }; } +pub fn blockPrivateNetworks(self: *const Config) bool { + return switch (self.mode) { + inline .serve, .fetch, .mcp => |opts| opts.common.block_private_networks, + else => unreachable, + }; +} + +pub fn blockCidrs(self: *const Config) ?[]const u8 { + return switch (self.mode) { + inline .serve, .fetch, .mcp => |opts| opts.common.block_cidrs, + else => unreachable, + }; +} + pub fn maxConnections(self: *const Config) u16 { return switch (self.mode) { .serve => |opts| opts.cdp_max_connections, @@ -288,6 +302,9 @@ pub const Common = struct { web_bot_auth_key_file: ?[]const u8 = null, web_bot_auth_keyid: ?[]const u8 = null, web_bot_auth_domain: ?[]const u8 = null, + + block_private_networks: bool = false, + block_cidrs: ?[]const u8 = null, }; /// Pre-formatted HTTP headers for reuse across Http and Client. @@ -350,6 +367,21 @@ pub fn printUsageAndExit(self: *const Config, success: bool) void { \\ we make requests towards. \\ Defaults to false. \\ + \\--block-private-networks + \\ Blocks HTTP requests to private/internal IP addresses + \\ after DNS resolution. Useful for sandboxing, multi-tenant + \\ deployments, and preventing access to internal infrastructure + \\ regardless of what triggers the request (JavaScript, HTML + \\ resources, redirects, etc.). + \\ Defaults to false. + \\ + \\--block-cidrs + \\ Additional CIDR ranges to block, comma-separated. + \\ Prefix with '-' to allow (exempt from blocking). + \\ e.g. --block-cidrs 169.254.169.254/32,fd00:ec2::254/128 + \\ e.g. --block-cidrs 10.0.0.0/8,-10.0.0.42/32 + \\ Can be used standalone or combined with --block-private-networks. + \\ \\--http-proxy The HTTP proxy to use for all HTTP requests. \\ A username:password can be included for basic authentication. \\ Defaults to none. @@ -706,7 +738,7 @@ fn parseServeArgs( } log.fatal(.app, "unknown argument", .{ .mode = "serve", .arg = opt }); - return error.UnkownOption; + return error.UnknownOption; } return serve; @@ -737,7 +769,7 @@ fn parseMcpArgs( } log.fatal(.mcp, "unknown argument", .{ .mode = "mcp", .arg = opt }); - return error.UnkownOption; + return error.UnknownOption; } return result; @@ -879,7 +911,7 @@ fn parseFetchArgs( if (std.mem.startsWith(u8, opt, "--")) { log.fatal(.app, "unknown argument", .{ .mode = "fetch", .arg = opt }); - return error.UnkownOption; + return error.UnknownOption; } if (url != null) { @@ -1236,5 +1268,19 @@ fn parseCommonArg( return true; } + if (std.mem.eql(u8, "--block-private-networks", opt)) { + common.block_private_networks = true; + return true; + } + + if (std.mem.eql(u8, "--block-cidrs", opt)) { + const str = args.next() orelse { + log.fatal(.app, "missing argument value", .{ .arg = "--block-cidrs" }); + return error.InvalidArgument; + }; + common.block_cidrs = try allocator.dupe(u8, str); + return true; + } + return false; } diff --git a/src/browser/Factory.zig b/src/browser/Factory.zig index 14770cea..79297a32 100644 --- a/src/browser/Factory.zig +++ b/src/browser/Factory.zig @@ -380,7 +380,7 @@ pub fn destroy(self: *Factory, value: anytype) void { // We should always destroy from the leaf down. if (@hasDecl(S, "_prototype_root")) { // A Event{._type == .generic} (or any other similar types) - // _should_ be destoyed directly. The _type = .generic is a pseudo + // _should_ be destroyed directly. The _type = .generic is a pseudo // child if (S != Event or value._type != .generic) { log.fatal(.bug, "factory.destroy.event", .{ .type = @typeName(S) }); diff --git a/src/browser/HttpClient.zig b/src/browser/HttpClient.zig index f5b38871..b90029ac 100644 --- a/src/browser/HttpClient.zig +++ b/src/browser/HttpClient.zig @@ -98,7 +98,7 @@ pending_robots_queue: std.StringHashMapUnmanaged(std.ArrayList(Request)) = .empt // Once we have a handle/easy to process a request with, we create a Transfer // which contains the Request as well as any state we need to process the -// request. These wil come and go with each request. +// request. These will come and go with each request. transfer_pool: std.heap.MemoryPool(Transfer), // The current proxy. CDP can change it, changeProxy(null) restores @@ -635,7 +635,7 @@ fn waitForInterceptedResponse(self: *Client, transfer: *Transfer) !bool { } // Above, request will not process if there's an interception request. In such -// cases, the interecptor is expected to call resume to continue the transfer +// cases, the interceptor is expected to call resume to continue the transfer // or transfer.abort() to abort it. fn process(self: *Client, transfer: *Transfer) !void { // libcurl doesn't allow recursive calls, if we're in a `perform()` operation @@ -772,7 +772,7 @@ fn makeRequest(self: *Client, conn: *http.Connection, transfer: *Transfer) anyer // cleaning things up. That's why the above code is in a block. If anything // fails BEFORE `curl_multi_add_handle` succeeds, the we still need to do // cleanup. But if things fail after `curl_multi_add_handle`, we expect - // perfom to pickup the failure and cleanup. + // perform to pickup the failure and cleanup. self.trackConn(conn) catch |err| { transfer._conn = null; transfer.deinit(); @@ -859,7 +859,7 @@ fn processOneMessage(self: *Client, msg: http.Handles.MultiMessage, transfer: *T } transfer._intercept_state = .pending; - // Wether or not this is a blocking request, we're not going + // Whether or not this is a blocking request, we're not going // to process it now. We can end the transfer, which will // release the easy handle back into the pool. The transfer // is still valid/alive (just has no handle). diff --git a/src/browser/Page.zig b/src/browser/Page.zig index a825052e..458e13d8 100644 --- a/src/browser/Page.zig +++ b/src/browser/Page.zig @@ -97,7 +97,7 @@ _parse_mode: enum { document, fragment, document_write } = .document, // identity (a given attribute should return the same *Attribute), so we do // a look here. We don't store this in the Element or Attribute.List.Entry // because that would require additional space per element / Attribute.List.Entry -// even thoug we'll create very few (if any) actual *Attributes. +// even though we'll create very few (if any) actual *Attributes. _attribute_lookup: std.AutoHashMapUnmanaged(usize, *Element.Attribute) = .empty, // Same as _atlribute_lookup, but instead of individual attributes, this is for @@ -480,7 +480,7 @@ pub fn navigate(self: *Page, request_url: [:0]const u8, opts: NavigateOpts) !voi // even though this might be the same _data_ as `default_location`, we // have to do this to make sure window.location is at a unique _address_. - // If we don't do this, mulitple window._location will have the same + // If we don't do this, multiple window._location will have the same // address and thus be mapped to the same v8::Object in the identity map. self.window._location = try Location.init(self.url, self); @@ -690,7 +690,7 @@ fn scheduleNavigationWithArena(originator: *Page, arena: Allocator, request_url: }); // This is a micro-optimization. Terminate any inflight request as early - // as we can. This will be more propery shutdown when we process the + // as we can. This will be more properly shutdown when we process the // scheduled navigation. if (target.parent == null) { session.browser.http_client.abort(); @@ -1174,7 +1174,7 @@ pub fn iframeAddedCallback(self: *Page, iframe: *IFrame) !void { iframe._window = page_frame.window; errdefer iframe._window = null; - // on first load, dispatch frame_created evnet + // on first load, dispatch frame_created event self._session.notification.dispatch(.page_frame_created, &.{ .frame_id = frame_id, .parent_id = self._frame_id, @@ -3213,7 +3213,7 @@ const IdleNotification = union(enum) { init, // timestamp where the state was first triggered. If the state stays - // true (e.g. 0 nework activity for NetworkIdle, or <= 2 for NetworkAlmostIdle) + // true (e.g. 0 network activity for NetworkIdle, or <= 2 for NetworkAlmostIdle) // for 500ms, it'll send the notification and transition to .done. If // the state doesn't stay true, it'll revert to .init. triggered: u64, @@ -3475,7 +3475,10 @@ pub fn handleClick(self: *Page, target: *Node) !void { pub fn triggerKeyboard(self: *Page, keyboard_event: *KeyboardEvent) !void { const event = keyboard_event.asEvent(); - const element = self.window._document._active_element orelse return; + const element = self.window._document._active_element orelse { + event.deinit(self._session); + return; + }; if (comptime IS_DEBUG) { log.debug(.page, "page keydown", .{ diff --git a/src/browser/ScriptManager.zig b/src/browser/ScriptManager.zig index 546a05c5..984ecccc 100644 --- a/src/browser/ScriptManager.zig +++ b/src/browser/ScriptManager.zig @@ -71,7 +71,7 @@ allocator: Allocator, // source is placed here (keyed by the full url) for some point in the future // when v8 asks for it. // The type is confusing (too confusing? move to a union). Starts of as `null` -// then transitions to either an error (from errorCalback) or the completed +// then transitions to either an error (from errorCallback) or the completed // buffer from doneCallback imported_modules: std.StringHashMapUnmanaged(ImportedModule), diff --git a/src/browser/Session.zig b/src/browser/Session.zig index ab043653..2ae90034 100644 --- a/src/browser/Session.zig +++ b/src/browser/Session.zig @@ -236,6 +236,8 @@ pub fn releaseOrigin(self: *Session, origin: *js.Origin) void { /// Reset page_arena and factory for a clean slate. /// Called when root page is removed. fn resetPageResources(self: *Session) void { + defer self.browser.env.memoryPressureNotification(.moderate); + self.identity.deinit(); self.identity = .{}; @@ -294,7 +296,6 @@ pub fn replacePage(self: *Session) !*Page { current.deinit(true); self.resetPageResources(); - self.browser.env.memoryPressureNotification(.moderate); self.page = @as(Page, undefined); const page = &self.page.?; diff --git a/src/browser/StyleManager.zig b/src/browser/StyleManager.zig index f7a69672..161ebca0 100644 --- a/src/browser/StyleManager.zig +++ b/src/browser/StyleManager.zig @@ -667,7 +667,7 @@ const VisibilityProperties = struct { opacity_zero: ?bool = null, pointer_events_none: ?bool = null, - // returne true if any field in VisibilityProperties is not null + // return true if any field in VisibilityProperties is not null fn isRelevant(self: VisibilityProperties) bool { return self.display_none != null or self.visibility_hidden != null or diff --git a/src/browser/dump.zig b/src/browser/dump.zig index bb666e7f..abe322a1 100644 --- a/src/browser/dump.zig +++ b/src/browser/dump.zig @@ -40,7 +40,7 @@ pub const Opts = struct { // Skip shadow DOM entirely (innerHTML/outerHTML) skip, - // Dump everyhting (like "view source") + // Dump everything (like "view source") complete, // Resolve slot elements (like what actually gets rendered) diff --git a/src/browser/js/Caller.zig b/src/browser/js/Caller.zig index cd4d25d3..2ca3ab21 100644 --- a/src/browser/js/Caller.zig +++ b/src/browser/js/Caller.zig @@ -39,9 +39,22 @@ prev_local: ?*const js.Local, prev_context: *Context, // Takes the raw v8 isolate and extracts the context from it. -pub fn init(self: *Caller, v8_isolate: *v8.Isolate) void { - const ctx, const v8_context = Context.fromIsolate(.{ .handle = v8_isolate }); +// Returns false if the context has been destroyed (e.g., navigated-away iframe), +// in which case a JS exception has been thrown and the caller should return immediately. +pub fn init(self: *Caller, v8_isolate: *v8.Isolate) bool { + const ctx, const v8_context = Context.fromIsolate(.{ .handle = v8_isolate }) orelse { + throwDetachedError(v8_isolate); + return false; + }; initWithContext(self, ctx, v8_context); + return true; +} + +fn throwDetachedError(isolate: *v8.Isolate) void { + const message = "Cannot execute in detached context (e.g., navigated-away iframe)"; + const v8_message = v8.v8__String__NewFromUtf8(isolate, message.ptr, v8.kNormal, @intCast(message.len)); + const js_exception = v8.v8__Exception__Error(v8_message); + _ = v8.v8__Isolate__ThrowException(isolate, js_exception); } fn initWithContext(self: *Caller, ctx: *Context, v8_context: *const v8.Context) void { @@ -60,9 +73,9 @@ fn initWithContext(self: *Caller, ctx: *Context, v8_context: *const v8.Context) ctx.local = &self.local; } -pub fn initFromHandle(self: *Caller, handle: ?*const v8.FunctionCallbackInfo) void { +pub fn initFromHandle(self: *Caller, handle: ?*const v8.FunctionCallbackInfo) bool { const isolate = v8.v8__FunctionCallbackInfo__GetIsolate(handle).?; - self.init(isolate); + return self.init(isolate); } pub fn deinit(self: *Caller) void { @@ -348,7 +361,7 @@ fn handleError(comptime T: type, comptime F: type, local: *const Local, err: any error.InvalidArgument => isolate.createTypeError("invalid argument"), error.TypeError => isolate.createTypeError(""), error.OutOfMemory => isolate.createError("out of memory"), - error.IllegalConstructor => isolate.createError("Illegal Contructor"), + error.IllegalConstructor => isolate.createError("Illegal Constructor"), else => blk: { if (comptime opts.dom_exception) { const DOMException = @import("../webapi/DOMException.zig"); @@ -538,7 +551,10 @@ pub const Function = struct { pub fn call(comptime T: type, info_handle: *const v8.FunctionCallbackInfo, func: anytype, comptime opts: Opts) void { const v8_isolate = v8.v8__FunctionCallbackInfo__GetIsolate(info_handle).?; - const ctx, const v8_context = Context.fromIsolate(.{ .handle = v8_isolate }); + const ctx, const v8_context = Context.fromIsolate(.{ .handle = v8_isolate }) orelse { + throwDetachedError(v8_isolate); + return; + }; const info = FunctionCallbackInfo{ .handle = info_handle }; var hs: js.HandleScope = undefined; @@ -617,7 +633,7 @@ pub const Function = struct { if (v8.v8__Object__GetInternalField(js_this, idx)) |cached| { // means we can't cache undefined, since we can't tell the // difference between "it isn't in the cache" and "it's - // in the cache with a valud of undefined" + // in the cache with a value of undefined" if (!v8.v8__Value__IsUndefined(cached)) { return_value.set(cached); return true; diff --git a/src/browser/js/Context.zig b/src/browser/js/Context.zig index beec0625..ffc71db2 100644 --- a/src/browser/js/Context.zig +++ b/src/browser/js/Context.zig @@ -114,7 +114,7 @@ scheduler: Scheduler, unknown_properties: (if (IS_DEBUG) std.StringHashMapUnmanaged(UnknownPropertyStat) else void) = if (IS_DEBUG) .{} else {}, const ModuleEntry = struct { - // Can be null if we're asynchrously loading the module, in + // Can be null if we're asynchronously loading the module, in // which case resolver_promise cannot be null. module: ?js.Module.Global = null, @@ -138,7 +138,8 @@ pub fn fromC(c_context: *const v8.Context) ?*Context { /// Returns the Context and v8::Context for the given isolate. /// If the current context is from a destroyed Context (e.g., navigated-away iframe), /// falls back to the incumbent context (the calling context). -pub fn fromIsolate(isolate: js.Isolate) struct { *Context, *const v8.Context } { +/// Returns null if neither context has a valid Context struct (both were destroyed). +pub fn fromIsolate(isolate: js.Isolate) ?struct { *Context, *const v8.Context } { const v8_context = v8.v8__Isolate__GetCurrentContext(isolate.handle).?; if (fromC(v8_context)) |ctx| { return .{ ctx, v8_context }; @@ -146,7 +147,8 @@ pub fn fromIsolate(isolate: js.Isolate) struct { *Context, *const v8.Context } { // The current context's Context struct has been freed (e.g., iframe navigated away). // Fall back to the incumbent context (the calling context). const v8_incumbent = v8.v8__Isolate__GetIncumbentContext(isolate.handle).?; - return .{ fromC(v8_incumbent).?, v8_incumbent }; + const ctx = fromC(v8_incumbent) orelse return null; + return .{ ctx, v8_incumbent }; } pub fn deinit(self: *Context) void { @@ -742,7 +744,7 @@ fn _dynamicModuleCallback(self: *Context, specifier: [:0]const u8, referrer: []c // since we're going to be doing all the work. entry.resolver_promise = try promise.persist(); - // But we can skip direclty to `resolveDynamicModule` which is + // But we can skip directly to `resolveDynamicModule` which is // what the above callback will eventually do. self.resolveDynamicModule(state, entry.*, local); return promise; @@ -806,7 +808,9 @@ fn resolveDynamicModule(self: *Context, state: *DynamicModuleResolveState, modul const then_callback = newFunctionWithData(local, struct { pub fn callback(callback_handle: ?*const v8.FunctionCallbackInfo) callconv(.c) void { var c: Caller = undefined; - c.initFromHandle(callback_handle); + if (!c.initFromHandle(callback_handle)) { + return; + } defer c.deinit(); const info = Caller.FunctionCallbackInfo{ .handle = callback_handle.? }; @@ -830,7 +834,7 @@ fn resolveDynamicModule(self: *Context, state: *DynamicModuleResolveState, modul const catch_callback = newFunctionWithData(local, struct { pub fn callback(callback_handle: ?*const v8.FunctionCallbackInfo) callconv(.c) void { var c: Caller = undefined; - c.initFromHandle(callback_handle); + if (!c.initFromHandle(callback_handle)) return; defer c.deinit(); const info = Caller.FunctionCallbackInfo{ .handle = callback_handle.? }; diff --git a/src/browser/js/Env.zig b/src/browser/js/Env.zig index bae6a8f0..2c1ebf38 100644 --- a/src/browser/js/Env.zig +++ b/src/browser/js/Env.zig @@ -519,7 +519,7 @@ fn promiseRejectCallback(message_handle: v8.PromiseRejectMessage) callconv(.c) v const promise_handle = v8.v8__PromiseRejectMessage__GetPromise(&message_handle).?; const v8_isolate = v8.v8__Object__GetIsolate(@ptrCast(promise_handle)).?; const isolate = js.Isolate{ .handle = v8_isolate }; - const ctx, const v8_context = Context.fromIsolate(isolate); + const ctx, const v8_context = Context.fromIsolate(isolate) orelse return; const local = js.Local{ .ctx = ctx, diff --git a/src/browser/js/Function.zig b/src/browser/js/Function.zig index 16664cbe..8599bc73 100644 --- a/src/browser/js/Function.zig +++ b/src/browser/js/Function.zig @@ -146,7 +146,7 @@ fn _tryCallWithThis(self: *const Function, comptime T: type, this: anytype, args } break :blk values; }, - else => @compileError("JS Function called with invalid paremter type"), + else => @compileError("JS Function called with invalid parameter type"), }; const c_args = @as(?[*]const ?*v8.Value, @ptrCast(js_args.ptr)); diff --git a/src/browser/js/Inspector.zig b/src/browser/js/Inspector.zig index aa35be7b..d956cc52 100644 --- a/src/browser/js/Inspector.zig +++ b/src/browser/js/Inspector.zig @@ -195,7 +195,7 @@ pub const RemoteObject = struct { // Combines a v8::InspectorSession and a v8::InspectorChannelImpl. The // InspectorSession is for zig -> v8 (sending messages to the inspector). The // Channel is for v8 -> zig, getting events from the Inspector (that we'll pass -// back ot some opaque context, i.e the CDP BrowserContext). +// back to some opaque context, i.e the CDP BrowserContext). // The channel callbacks are defined below, as: // pub export fn v8_inspector__Channel__IMPL__XYZ pub const Session = struct { diff --git a/src/browser/js/Local.zig b/src/browser/js/Local.zig index c9a3b880..4d91ed2e 100644 --- a/src/browser/js/Local.zig +++ b/src/browser/js/Local.zig @@ -39,7 +39,7 @@ const CallOpts = Caller.CallOpts; // v8::Local. In V8, you need a Local or get anything // done, but the local only exists for the lifetime of the HandleScope it was // created on. When V8 calls into Zig, things are pretty straightforward, since -// that callback gives us the currenty-entered V8::Local. But when Zig +// that callback gives us the currently-entered V8::Local. But when Zig // has to call into V8, it's a bit more messy. // As a general rule, think of it this way: // 1 - Caller.zig is for V8 -> Zig @@ -503,7 +503,7 @@ pub fn jsValueToZig(self: *const Local, comptime T: type, js_val: js.Value) !T { .optional => |o| { // If type type is a ?js.Value or a ?js.Object, then we want to pass // a js.Object, not null. Consider a function, - // _doSomething(arg: ?Env.JsObjet) void { ... } + // _doSomething(arg: ?Env.JsObject) void { ... } // // And then these two calls: // doSomething(); diff --git a/src/browser/js/Scheduler.zig b/src/browser/js/Scheduler.zig index 322351f3..d9fb417e 100644 --- a/src/browser/js/Scheduler.zig +++ b/src/browser/js/Scheduler.zig @@ -82,7 +82,7 @@ pub fn run(self: *Scheduler) !void { pub fn hasReadyTasks(self: *Scheduler) bool { const now = milliTimestamp(.monotonic); - return queueuHasReadyTask(&self.low_priority, now) or queueuHasReadyTask(&self.high_priority, now); + return queueHasReadyTask(&self.low_priority, now) or queueHasReadyTask(&self.high_priority, now); } pub fn msToNextHigh(self: *Scheduler) ?u64 { @@ -125,7 +125,7 @@ fn runQueue(self: *Scheduler, queue: *Queue, now: u64) !void { return; } -fn queueuHasReadyTask(queue: *Queue, now: u64) bool { +fn queueHasReadyTask(queue: *Queue, now: u64) bool { const task = queue.peek() orelse return false; return task.run_at <= now; } diff --git a/src/browser/js/Snapshot.zig b/src/browser/js/Snapshot.zig index abea4ee5..5a04861a 100644 --- a/src/browser/js/Snapshot.zig +++ b/src/browser/js/Snapshot.zig @@ -32,7 +32,7 @@ const embedded_snapshot_blob = if (@import("build_config").snapshot_path) |path| // When creating our Snapshot, we use local function templates for every Zig type. // You cannot, from what I can tell, create persisted FunctionTemplates at -// snapshot creation time. But you can embedd those templates (or any other v8 +// snapshot creation time. But you can embed those templates (or any other v8 // Data) so that it's available to contexts created from the snapshot. This is // the starting index of those function templates, which we can extract. At // creation time, in debug, we assert that this is actually a consecutive integer diff --git a/src/browser/js/bridge.zig b/src/browser/js/bridge.zig index 8fbdc315..93c51f78 100644 --- a/src/browser/js/bridge.zig +++ b/src/browser/js/bridge.zig @@ -116,7 +116,9 @@ pub const Constructor = struct { fn wrap(handle: ?*const v8.FunctionCallbackInfo) callconv(.c) void { const v8_isolate = v8.v8__FunctionCallbackInfo__GetIsolate(handle).?; var caller: Caller = undefined; - caller.init(v8_isolate); + if (!caller.init(v8_isolate)) { + return; + } defer caller.deinit(); caller.constructor(T, func, handle.?, .{ @@ -216,7 +218,9 @@ pub const Indexed = struct { fn wrap(idx: u32, handle: ?*const v8.PropertyCallbackInfo) callconv(.c) u8 { const v8_isolate = v8.v8__PropertyCallbackInfo__GetIsolate(handle).?; var caller: Caller = undefined; - caller.init(v8_isolate); + if (!caller.init(v8_isolate)) { + return 0; + } defer caller.deinit(); return caller.getIndex(T, getter, idx, handle.?, .{ @@ -232,7 +236,9 @@ pub const Indexed = struct { fn wrap(handle: ?*const v8.PropertyCallbackInfo) callconv(.c) u8 { const v8_isolate = v8.v8__PropertyCallbackInfo__GetIsolate(handle).?; var caller: Caller = undefined; - caller.init(v8_isolate); + if (!caller.init(v8_isolate)) { + return 0; + } defer caller.deinit(); return caller.getEnumerator(T, enumerator, handle.?, .{}); } @@ -258,7 +264,9 @@ pub const NamedIndexed = struct { fn wrap(c_name: ?*const v8.Name, handle: ?*const v8.PropertyCallbackInfo) callconv(.c) u8 { const v8_isolate = v8.v8__PropertyCallbackInfo__GetIsolate(handle).?; var caller: Caller = undefined; - caller.init(v8_isolate); + if (!caller.init(v8_isolate)) { + return 0; + } defer caller.deinit(); return caller.getNamedIndex(T, getter, c_name.?, handle.?, .{ @@ -272,7 +280,9 @@ pub const NamedIndexed = struct { fn wrap(c_name: ?*const v8.Name, c_value: ?*const v8.Value, handle: ?*const v8.PropertyCallbackInfo) callconv(.c) u8 { const v8_isolate = v8.v8__PropertyCallbackInfo__GetIsolate(handle).?; var caller: Caller = undefined; - caller.init(v8_isolate); + if (!caller.init(v8_isolate)) { + return 0; + } defer caller.deinit(); return caller.setNamedIndex(T, setter, c_name.?, c_value.?, handle.?, .{ @@ -286,7 +296,9 @@ pub const NamedIndexed = struct { fn wrap(c_name: ?*const v8.Name, handle: ?*const v8.PropertyCallbackInfo) callconv(.c) u8 { const v8_isolate = v8.v8__PropertyCallbackInfo__GetIsolate(handle).?; var caller: Caller = undefined; - caller.init(v8_isolate); + if (!caller.init(v8_isolate)) { + return 0; + } defer caller.deinit(); return caller.deleteNamedIndex(T, deleter, c_name.?, handle.?, .{ @@ -387,7 +399,9 @@ pub const Property = struct { pub fn unknownWindowPropertyCallback(c_name: ?*const v8.Name, handle: ?*const v8.PropertyCallbackInfo) callconv(.c) u8 { const v8_isolate = v8.v8__PropertyCallbackInfo__GetIsolate(handle).?; var caller: Caller = undefined; - caller.init(v8_isolate); + if (!caller.init(v8_isolate)) { + return 0; + } defer caller.deinit(); const local = &caller.local; @@ -465,7 +479,9 @@ pub fn unknownObjectPropertyCallback(comptime JsApi: type) *const fn (?*const v8 const v8_isolate = v8.v8__PropertyCallbackInfo__GetIsolate(handle).?; var caller: Caller = undefined; - caller.init(v8_isolate); + if (!caller.init(v8_isolate)) { + return 0; + } defer caller.deinit(); const local = &caller.local; diff --git a/src/browser/parser/Parser.zig b/src/browser/parser/Parser.zig index 0c06cbcc..df85c425 100644 --- a/src/browser/parser/Parser.zig +++ b/src/browser/parser/Parser.zig @@ -35,7 +35,7 @@ pub const ParsedNode = struct { // Data associated with this element to be passed back to html5ever as needed // We only have this for Elements. For other types, like comments, it's null. // html5ever should never ask us for this data on a non-element, and we'll - // assert that, with this opitonal, to make sure our assumption is correct. + // assert that, with this optional, to make sure our assumption is correct. data: ?*anyopaque, }; diff --git a/src/browser/tests/domimplementation.html b/src/browser/tests/domimplementation.html index 302db88a..76b1a50f 100644 --- a/src/browser/tests/domimplementation.html +++ b/src/browser/tests/domimplementation.html @@ -108,7 +108,7 @@ } -
- - - - diff --git a/src/browser/tests/node/normalize.html b/src/browser/tests/node/normalize.html index b85f8324..92c20a35 100644 --- a/src/browser/tests/node/normalize.html +++ b/src/browser/tests/node/normalize.html @@ -35,7 +35,7 @@ Atreides -