Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/browser/cssom/css_style_declaration.zig
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ pub const CSSStyleDeclaration = struct {
return self.order.items.len;
}

pub fn get_parentRule() ?CSSRule {
pub fn get_parentRule(_: *const CSSStyleDeclaration) ?CSSRule {
return null;
}

Expand Down
11 changes: 3 additions & 8 deletions src/browser/html/window.zig
Original file line number Diff line number Diff line change
Expand Up @@ -337,20 +337,15 @@ test "Browser.HTML.Window" {
// Note however that we in this test do not wait as the request is just send to the browser
try runner.testCases(&.{
.{
\\ let start;
\\ let start = 0;
\\ function step(timestamp) {
\\ if (start === undefined) {
\\ start = timestamp;
\\ }
\\ const elapsed = timestamp - start;
\\ if (elapsed < 2000) {
\\ requestAnimationFrame(step);
\\ }
\\ start = timestamp;
\\ }
,
null,
},
.{ "requestAnimationFrame(step);", null }, // returned id is checked in the next test
.{ " start > 0", "true" },
}, .{});

// cancelAnimationFrame should be able to cancel a request with the given id
Expand Down
33 changes: 18 additions & 15 deletions src/cdp/cdp.zig
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ const json = std.json;
const log = @import("../log.zig");
const App = @import("../app.zig").App;
const Env = @import("../browser/env.zig").Env;
const asUint = @import("../str/parser.zig").asUint;
const Browser = @import("../browser/browser.zig").Browser;
const Session = @import("../browser/session.zig").Session;
const Page = @import("../browser/page.zig").Page;
Expand Down Expand Up @@ -182,41 +181,41 @@ pub fn CDPT(comptime TypeProvider: type) type {

switch (domain.len) {
3 => switch (@as(u24, @bitCast(domain[0..3].*))) {
asUint("DOM") => return @import("domains/dom.zig").processMessage(command),
asUint("Log") => return @import("domains/log.zig").processMessage(command),
asUint("CSS") => return @import("domains/css.zig").processMessage(command),
asUint(u24, "DOM") => return @import("domains/dom.zig").processMessage(command),
asUint(u24, "Log") => return @import("domains/log.zig").processMessage(command),
asUint(u24, "CSS") => return @import("domains/css.zig").processMessage(command),
else => {},
},
4 => switch (@as(u32, @bitCast(domain[0..4].*))) {
asUint("Page") => return @import("domains/page.zig").processMessage(command),
asUint(u32, "Page") => return @import("domains/page.zig").processMessage(command),
else => {},
},
5 => switch (@as(u40, @bitCast(domain[0..5].*))) {
asUint("Fetch") => return @import("domains/fetch.zig").processMessage(command),
asUint("Input") => return @import("domains/input.zig").processMessage(command),
asUint(u40, "Fetch") => return @import("domains/fetch.zig").processMessage(command),
asUint(u40, "Input") => return @import("domains/input.zig").processMessage(command),
else => {},
},
6 => switch (@as(u48, @bitCast(domain[0..6].*))) {
asUint("Target") => return @import("domains/target.zig").processMessage(command),
asUint(u48, "Target") => return @import("domains/target.zig").processMessage(command),
else => {},
},
7 => switch (@as(u56, @bitCast(domain[0..7].*))) {
asUint("Browser") => return @import("domains/browser.zig").processMessage(command),
asUint("Runtime") => return @import("domains/runtime.zig").processMessage(command),
asUint("Network") => return @import("domains/network.zig").processMessage(command),
asUint(u56, "Browser") => return @import("domains/browser.zig").processMessage(command),
asUint(u56, "Runtime") => return @import("domains/runtime.zig").processMessage(command),
asUint(u56, "Network") => return @import("domains/network.zig").processMessage(command),
else => {},
},
8 => switch (@as(u64, @bitCast(domain[0..8].*))) {
asUint("Security") => return @import("domains/security.zig").processMessage(command),
asUint(u64, "Security") => return @import("domains/security.zig").processMessage(command),
else => {},
},
9 => switch (@as(u72, @bitCast(domain[0..9].*))) {
asUint("Emulation") => return @import("domains/emulation.zig").processMessage(command),
asUint("Inspector") => return @import("domains/inspector.zig").processMessage(command),
asUint(u72, "Emulation") => return @import("domains/emulation.zig").processMessage(command),
asUint(u72, "Inspector") => return @import("domains/inspector.zig").processMessage(command),
else => {},
},
11 => switch (@as(u88, @bitCast(domain[0..11].*))) {
asUint("Performance") => return @import("domains/performance.zig").processMessage(command),
asUint(u88, "Performance") => return @import("domains/performance.zig").processMessage(command),
else => {},
},
else => {},
Expand Down Expand Up @@ -696,6 +695,10 @@ const InputParams = struct {
}
};

fn asUint(comptime T: type, comptime string: []const u8) T {
return @bitCast(string[0..string.len].*);
}

const testing = @import("testing.zig");
test "cdp: invalid json" {
var ctx = testing.context();
Expand Down
2 changes: 1 addition & 1 deletion src/http/client.zig
Original file line number Diff line number Diff line change
Expand Up @@ -3170,7 +3170,7 @@ test "HttpClient: async tls no body" {
}
}

test "HttpClient: async tls with body x" {
test "HttpClient: async tls with body" {
defer testing.reset();
for (0..5) |_| {
var client = try testClient();
Expand Down
29 changes: 18 additions & 11 deletions src/log.zig
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,16 @@ fn logTo(comptime scope: Scope, level: Level, comptime msg: []const u8, data: an
}

fn logLogfmt(comptime scope: Scope, level: Level, comptime msg: []const u8, data: anytype, writer: anytype) !void {
try logLogFmtPrefix(scope, level, msg, writer);
inline for (@typeInfo(@TypeOf(data)).@"struct".fields) |f| {
const key = " " ++ f.name ++ "=";
try writer.writeAll(key);
try writeValue(.logfmt, @field(data, f.name), writer);
}
try writer.writeByte('\n');
}

fn logLogFmtPrefix(comptime scope: Scope, level: Level, comptime msg: []const u8, writer: anytype) !void {
try writer.writeAll("$time=");
try writer.print("{d}", .{timestamp()});

Expand All @@ -164,15 +174,20 @@ fn logLogfmt(comptime scope: Scope, level: Level, comptime msg: []const u8, data
break :blk prefix ++ "\"" ++ msg ++ "\"";
};
try writer.writeAll(full_msg);
}

fn logPretty(comptime scope: Scope, level: Level, comptime msg: []const u8, data: anytype, writer: anytype) !void {
try logPrettyPrefix(scope, level, msg, writer);
inline for (@typeInfo(@TypeOf(data)).@"struct".fields) |f| {
const key = " " ++ f.name ++ "=";
const key = " " ++ f.name ++ " = ";
try writer.writeAll(key);
try writeValue(.logfmt, @field(data, f.name), writer);
try writeValue(.pretty, @field(data, f.name), writer);
try writer.writeByte('\n');
}
try writer.writeByte('\n');
}

fn logPretty(comptime scope: Scope, level: Level, comptime msg: []const u8, data: anytype, writer: anytype) !void {
fn logPrettyPrefix(comptime scope: Scope, level: Level, comptime msg: []const u8, writer: anytype) !void {
if (scope == .console and level == .fatal and comptime std.mem.eql(u8, msg, "lightpanda")) {
try writer.writeAll("\x1b[0;104mWARN ");
} else {
Expand Down Expand Up @@ -201,14 +216,6 @@ fn logPretty(comptime scope: Scope, level: Level, comptime msg: []const u8, data
try writer.print(" \x1b[0m[+{d}ms]", .{elapsed()});
try writer.writeByte('\n');
}

inline for (@typeInfo(@TypeOf(data)).@"struct".fields) |f| {
const key = " " ++ f.name ++ " = ";
try writer.writeAll(key);
try writeValue(.pretty, @field(data, f.name), writer);
try writer.writeByte('\n');
}
try writer.writeByte('\n');
}

pub fn writeValue(comptime format: Format, value: anytype, writer: anytype) !void {
Expand Down
130 changes: 37 additions & 93 deletions src/runtime/js.zig
Original file line number Diff line number Diff line change
Expand Up @@ -1876,7 +1876,7 @@ pub fn Env(comptime State: type, comptime WebApis: type) type {
defer caller.deinit();

const named_function = comptime NamedFunction.init(Struct, "get_" ++ name);
caller.getter(Struct, named_function, info) catch |err| {
caller.method(Struct, named_function, info) catch |err| {
caller.handleError(Struct, named_function, err, info);
};
}
Expand All @@ -1891,13 +1891,13 @@ pub fn Env(comptime State: type, comptime WebApis: type) type {
const setter_callback = v8.FunctionTemplate.initCallback(isolate, struct {
fn callback(raw_info: ?*const v8.C_FunctionCallbackInfo) callconv(.c) void {
const info = v8.FunctionCallbackInfo.initFromV8(raw_info);
std.debug.assert(info.length() == 1);

var caller = Caller(Self, State).init(info);
defer caller.deinit();

std.debug.assert(info.length() == 1);
const js_value = info.getArg(0);
const named_function = comptime NamedFunction.init(Struct, "set_" ++ name);
caller.setter(Struct, named_function, js_value, info) catch |err| {
caller.method(Struct, named_function, info) catch |err| {
caller.handleError(Struct, named_function, err, info);
};
}
Expand Down Expand Up @@ -2424,66 +2424,6 @@ fn Caller(comptime E: type, comptime State: type) type {
info.getReturnValue().set(try js_context.zigValueToJs(res));
}

fn getter(self: *Self, comptime Struct: type, comptime named_function: NamedFunction, info: v8.FunctionCallbackInfo) !void {
const js_context = self.js_context;
const func = @field(Struct, named_function.name);
const Getter = @TypeOf(func);
if (@typeInfo(Getter).@"fn".return_type == null) {
@compileError(@typeName(Struct) ++ " has a getter without a return type: " ++ @typeName(Getter));
}

var args: ParamterTypes(Getter) = undefined;
const arg_fields = @typeInfo(@TypeOf(args)).@"struct".fields;
switch (arg_fields.len) {
0 => {}, // getters _can_ be parameterless
1, 2 => {
const zig_instance = try E.typeTaggedAnyOpaque(named_function, *Receiver(Struct), info.getThis());
comptime assertSelfReceiver(Struct, named_function);
@field(args, "0") = zig_instance;
if (comptime arg_fields.len == 2) {
comptime assertIsStateArg(Struct, named_function, 1);
@field(args, "1") = js_context.state;
}
},
else => @compileError(named_function.full_name + " has too many parmaters: " ++ @typeName(named_function.func)),
}
const res = @call(.auto, func, args);
info.getReturnValue().set(try js_context.zigValueToJs(res));
}

fn setter(self: *Self, comptime Struct: type, comptime named_function: NamedFunction, js_value: v8.Value, info: v8.FunctionCallbackInfo) !void {
const js_context = self.js_context;
const func = @field(Struct, named_function.name);
comptime assertSelfReceiver(Struct, named_function);

const zig_instance = try E.typeTaggedAnyOpaque(named_function, *Receiver(Struct), info.getThis());

const Setter = @TypeOf(func);
var args: ParamterTypes(Setter) = undefined;
const arg_fields = @typeInfo(@TypeOf(args)).@"struct".fields;
switch (arg_fields.len) {
0 => unreachable, // assertSelfReceiver make sure of this
1 => @compileError(named_function.full_name ++ " only has 1 parameter"),
2, 3 => {
@field(args, "0") = zig_instance;
@field(args, "1") = try js_context.jsValueToZig(named_function, arg_fields[1].type, js_value);
if (comptime arg_fields.len == 3) {
comptime assertIsStateArg(Struct, named_function, 2);
@field(args, "2") = js_context.state;
}
},
else => @compileError(named_function.full_name ++ " setter with more than 3 parameters, why?"),
}

if (@typeInfo(Setter).@"fn".return_type) |return_type| {
if (@typeInfo(return_type) == .error_union) {
_ = try @call(.auto, func, args);
return;
}
}
_ = @call(.auto, func, args);
}

fn getIndex(self: *Self, comptime Struct: type, comptime named_function: NamedFunction, idx: u32, info: v8.PropertyCallbackInfo) !u8 {
const js_context = self.js_context;
const func = @field(Struct, named_function.name);
Expand Down Expand Up @@ -2596,13 +2536,7 @@ fn Caller(comptime E: type, comptime State: type) type {

if (comptime builtin.mode == .Debug and @hasDecl(@TypeOf(info), "length")) {
if (log.enabled(.js, .warn)) {
const args_dump = self.serializeFunctionArgs(info) catch "failed to serialize args";
log.warn(.js, "function call error", .{
.name = named_function.full_name,
.err = err,
.args = args_dump,
.stack = stackForLogs(self.call_arena, isolate) catch |err1| @errorName(err1),
});
logFunctionCallError(self.call_arena, self.isolate, self.v8_context, err, named_function.full_name, info);
}
}

Expand Down Expand Up @@ -2670,6 +2604,7 @@ fn Caller(comptime E: type, comptime State: type) type {
// Does the error we want to return belong to the custom exeception's ErrorSet
fn isErrorSetException(comptime Exception: type, err: anytype) bool {
const Entry = std.meta.Tuple(&.{ []const u8, void });

const error_set = @typeInfo(Exception.ErrorSet).error_set.?;
const entries = comptime blk: {
var kv: [error_set.len]Entry = undefined;
Expand Down Expand Up @@ -2808,28 +2743,6 @@ fn Caller(comptime E: type, comptime State: type) type {
const Const_State = if (ti == .pointer) *const ti.pointer.child else State;
return T == State or T == Const_State;
}

fn serializeFunctionArgs(self: *const Self, info: anytype) ![]const u8 {
const isolate = self.isolate;
const v8_context = self.v8_context;
const arena = self.call_arena;
const separator = log.separator();
const js_parameter_count = info.length();

var arr: std.ArrayListUnmanaged(u8) = .{};
for (0..js_parameter_count) |i| {
const js_value = info.getArg(@intCast(i));
const value_string = try valueToDetailString(arena, js_value, isolate, v8_context);
const value_type = try jsStringToZig(arena, try js_value.typeOf(isolate), isolate);
try std.fmt.format(arr.writer(arena), "{s}{d}: {s} ({s})", .{
separator,
i + 1,
value_string,
value_type,
});
}
return arr.items;
}
};
}

Expand Down Expand Up @@ -3270,6 +3183,37 @@ const NamedFunction = struct {
}
};

// This is extracted to speed up compilation. When left inlined in handleError,
// this can add as much as 10 seconds of compilation time.
fn logFunctionCallError(arena: Allocator, isolate: v8.Isolate, context: v8.Context, err: anyerror, function_name: []const u8, info: v8.FunctionCallbackInfo) void {
const args_dump = serializeFunctionArgs(arena, isolate, context, info) catch "failed to serialize args";
log.warn(.js, "function call error", .{
.name = function_name,
.err = err,
.args = args_dump,
.stack = stackForLogs(arena, isolate) catch |err1| @errorName(err1),
});
}

fn serializeFunctionArgs(arena: Allocator, isolate: v8.Isolate, context: v8.Context, info: v8.FunctionCallbackInfo) ![]const u8 {
const separator = log.separator();
const js_parameter_count = info.length();

var arr: std.ArrayListUnmanaged(u8) = .{};
for (0..js_parameter_count) |i| {
const js_value = info.getArg(@intCast(i));
const value_string = try valueToDetailString(arena, js_value, isolate, context);
const value_type = try jsStringToZig(arena, try js_value.typeOf(isolate), isolate);
try std.fmt.format(arr.writer(arena), "{s}{d}: {s} ({s})", .{
separator,
i + 1,
value_string,
value_type,
});
}
return arr.items;
}

// This is called from V8. Whenever the v8 inspector has to describe a value
// it'll call this function to gets its [optional] subtype - which, from V8's
// point of view, is an arbitrary string.
Expand Down
Loading