mirror of
https://github.com/lightpanda-io/browser.git
synced 2025-12-16 08:18:59 +00:00
This is the last of the big changes to the js code
This Pr largely tightens up a lot of the code. 'v8' is no longer imported
outside of js. A number of helper functions have been moved to the js.Context.
For example, js.Function.getName used to call:
```zig
return js.valueToString(allocator, name, self.context.isolate, self.context.v8_context);
```
It now calls:
```zig
return self.context.valueToString(name, .{ .allocator = allocator });
```
Page.main_context has been renamed to `Page.js`. This, in combination with new
promise helpers, turns:
```zig
const resolver = page.main_context.createPromiseResolver();
try resolver.resolve({});
return resolver.promise();
```
into:
```zig
return page.js.resolvePromise({});
```
This commit is contained in:
@@ -663,7 +663,7 @@ const Script = struct {
|
||||
.cacheable = cacheable,
|
||||
});
|
||||
|
||||
const js_context = page.main_context;
|
||||
const js_context = page.js;
|
||||
var try_catch: js.TryCatch = undefined;
|
||||
try_catch.init(js_context);
|
||||
defer try_catch.deinit();
|
||||
@@ -707,10 +707,10 @@ const Script = struct {
|
||||
switch (callback) {
|
||||
.string => |str| {
|
||||
var try_catch: js.TryCatch = undefined;
|
||||
try_catch.init(page.main_context);
|
||||
try_catch.init(page.js);
|
||||
defer try_catch.deinit();
|
||||
|
||||
_ = page.main_context.exec(str, typ) catch |err| {
|
||||
_ = page.js.exec(str, typ) catch |err| {
|
||||
const msg = try_catch.err(page.arena) catch @errorName(err) orelse "unknown";
|
||||
log.warn(.user_script, "script callback", .{
|
||||
.url = self.url,
|
||||
|
||||
@@ -79,9 +79,7 @@ pub fn _replace(self: *CSSStyleSheet, text: []const u8, page: *Page) !js.Promise
|
||||
// TODO: clear self.css_rules
|
||||
// parse text and re-populate self.css_rules
|
||||
|
||||
const resolver = page.main_context.createPromiseResolver();
|
||||
try resolver.resolve({});
|
||||
return resolver.promise();
|
||||
return page.js.resolvePromise({});
|
||||
}
|
||||
|
||||
pub fn _replaceSync(self: *CSSStyleSheet, text: []const u8) !void {
|
||||
|
||||
@@ -49,7 +49,7 @@ pub fn get_pending(self: *const Animation) bool {
|
||||
|
||||
pub fn get_finished(self: *Animation, page: *Page) !js.Promise {
|
||||
if (self.finished_resolver == null) {
|
||||
const resolver = page.main_context.createPromiseResolver();
|
||||
const resolver = page.js.createPromiseResolver(.none);
|
||||
try resolver.resolve(self);
|
||||
self.finished_resolver = resolver;
|
||||
}
|
||||
@@ -59,7 +59,7 @@ pub fn get_finished(self: *Animation, page: *Page) !js.Promise {
|
||||
pub fn get_ready(self: *Animation, page: *Page) !js.Promise {
|
||||
// never resolved, because we're always "finished"
|
||||
if (self.ready_resolver == null) {
|
||||
const resolver = page.main_context.createPromiseResolver();
|
||||
const resolver = page.js.createPromiseResolver(.none);
|
||||
self.ready_resolver = resolver;
|
||||
}
|
||||
return self.ready_resolver.?.promise();
|
||||
|
||||
@@ -304,7 +304,7 @@ pub const Document = struct {
|
||||
return obj;
|
||||
}
|
||||
|
||||
const obj = try page.main_context.newArray(0).persist();
|
||||
const obj = try page.js.createArray(0).persist();
|
||||
state.adopted_style_sheets = obj;
|
||||
return obj;
|
||||
}
|
||||
|
||||
@@ -50,7 +50,7 @@ pub const ShadowRoot = struct {
|
||||
return obj;
|
||||
}
|
||||
|
||||
const obj = try page.main_context.newArray(0).persist();
|
||||
const obj = try page.js.createArray(0).persist();
|
||||
self.adopted_style_sheets = obj;
|
||||
return obj;
|
||||
}
|
||||
|
||||
@@ -24,7 +24,6 @@ const Page = @import("../page.zig").Page;
|
||||
|
||||
const iterator = @import("../iterator/iterator.zig");
|
||||
|
||||
const v8 = @import("v8");
|
||||
// https://developer.mozilla.org/en-US/docs/Web/API/Headers
|
||||
const Headers = @This();
|
||||
|
||||
|
||||
@@ -27,8 +27,6 @@ const Response = @import("./Response.zig");
|
||||
const Http = @import("../../http/Http.zig");
|
||||
const ReadableStream = @import("../streams/ReadableStream.zig");
|
||||
|
||||
const v8 = @import("v8");
|
||||
|
||||
const Headers = @import("Headers.zig");
|
||||
const HeadersInit = @import("Headers.zig").HeadersInit;
|
||||
|
||||
@@ -245,20 +243,15 @@ pub fn _bytes(self: *Response, page: *Page) !js.Promise {
|
||||
if (self.body_used) {
|
||||
return error.TypeError;
|
||||
}
|
||||
|
||||
const resolver = page.main_context.createPromiseResolver();
|
||||
|
||||
try resolver.resolve(self.body);
|
||||
self.body_used = true;
|
||||
return resolver.promise();
|
||||
return page.js.resolvePromise(self.body);
|
||||
}
|
||||
|
||||
pub fn _json(self: *Response, page: *Page) !js.Promise {
|
||||
if (self.body_used) {
|
||||
return error.TypeError;
|
||||
}
|
||||
|
||||
const resolver = page.main_context.createPromiseResolver();
|
||||
self.body_used = true;
|
||||
|
||||
if (self.body) |body| {
|
||||
const p = std.json.parseFromSliceLeaky(
|
||||
@@ -271,25 +264,17 @@ pub fn _json(self: *Response, page: *Page) !js.Promise {
|
||||
return error.SyntaxError;
|
||||
};
|
||||
|
||||
try resolver.resolve(p);
|
||||
} else {
|
||||
try resolver.resolve(null);
|
||||
return page.js.resolvePromise(p);
|
||||
}
|
||||
|
||||
self.body_used = true;
|
||||
return resolver.promise();
|
||||
return page.js.resolvePromise(null);
|
||||
}
|
||||
|
||||
pub fn _text(self: *Response, page: *Page) !js.Promise {
|
||||
if (self.body_used) {
|
||||
return error.TypeError;
|
||||
}
|
||||
|
||||
const resolver = page.main_context.createPromiseResolver();
|
||||
|
||||
try resolver.resolve(self.body);
|
||||
self.body_used = true;
|
||||
return resolver.promise();
|
||||
return page.js.resolvePromise(self.body);
|
||||
}
|
||||
|
||||
const testing = @import("../../testing.zig");
|
||||
|
||||
@@ -20,8 +20,6 @@ const std = @import("std");
|
||||
const js = @import("../js/js.zig");
|
||||
const log = @import("../../log.zig");
|
||||
|
||||
const v8 = @import("v8");
|
||||
|
||||
const HttpClient = @import("../../http/Client.zig");
|
||||
const Http = @import("../../http/Http.zig");
|
||||
const URL = @import("../../url.zig").URL;
|
||||
@@ -170,14 +168,8 @@ pub fn _bytes(self: *Response, page: *Page) !js.Promise {
|
||||
return error.TypeError;
|
||||
}
|
||||
|
||||
const resolver = js.PromiseResolver{
|
||||
.context = page.main_context,
|
||||
.resolver = v8.PromiseResolver.init(page.main_context.v8_context),
|
||||
};
|
||||
|
||||
try resolver.resolve(self.body);
|
||||
self.body_used = true;
|
||||
return resolver.promise();
|
||||
return page.js.resolvePromise(self.body);
|
||||
}
|
||||
|
||||
pub fn _json(self: *Response, page: *Page) !js.Promise {
|
||||
@@ -185,9 +177,8 @@ pub fn _json(self: *Response, page: *Page) !js.Promise {
|
||||
return error.TypeError;
|
||||
}
|
||||
|
||||
const resolver = page.main_context.createPromiseResolver();
|
||||
|
||||
if (self.body) |body| {
|
||||
self.body_used = true;
|
||||
const p = std.json.parseFromSliceLeaky(
|
||||
std.json.Value,
|
||||
page.call_arena,
|
||||
@@ -198,25 +189,18 @@ pub fn _json(self: *Response, page: *Page) !js.Promise {
|
||||
return error.SyntaxError;
|
||||
};
|
||||
|
||||
try resolver.resolve(p);
|
||||
} else {
|
||||
try resolver.resolve(null);
|
||||
return page.js.resolvePromise(p);
|
||||
}
|
||||
|
||||
self.body_used = true;
|
||||
return resolver.promise();
|
||||
return page.js.resolvePromise(null);
|
||||
}
|
||||
|
||||
pub fn _text(self: *Response, page: *Page) !js.Promise {
|
||||
if (self.body_used) {
|
||||
return error.TypeError;
|
||||
}
|
||||
|
||||
const resolver = page.main_context.createPromiseResolver();
|
||||
|
||||
try resolver.resolve(self.body);
|
||||
self.body_used = true;
|
||||
return resolver.promise();
|
||||
|
||||
return page.js.resolvePromise(self.body);
|
||||
}
|
||||
|
||||
const testing = @import("../../testing.zig");
|
||||
|
||||
@@ -131,7 +131,7 @@ pub fn fetch(input: RequestInput, options: ?RequestInit, page: *Page) !js.Promis
|
||||
|
||||
try page.requestCookie(.{}).headersForRequest(arena, req.url, &headers);
|
||||
|
||||
const resolver = try page.main_context.createPersistentPromiseResolver(.page);
|
||||
const resolver = try page.js.createPromiseResolver(.page);
|
||||
|
||||
const fetch_ctx = try arena.create(FetchContext);
|
||||
fetch_ctx.* = .{
|
||||
|
||||
@@ -118,7 +118,7 @@ pub const AbortSignal = struct {
|
||||
};
|
||||
pub fn _throwIfAborted(self: *const AbortSignal, page: *Page) ThrowIfAborted {
|
||||
if (self.aborted) {
|
||||
const ex = page.main_context.throw(self.reason orelse DEFAULT_REASON);
|
||||
const ex = page.js.throw(self.reason orelse DEFAULT_REASON);
|
||||
return .{ .exception = ex };
|
||||
}
|
||||
return .{ .undefined = {} };
|
||||
|
||||
@@ -71,7 +71,7 @@ pub fn get_state(self: *History, page: *Page) !?js.Value {
|
||||
if (self.current) |curr| {
|
||||
const entry = self.stack.items[curr];
|
||||
if (entry.state) |state| {
|
||||
const value = try js.Value.fromJson(page.main_context, state);
|
||||
const value = try js.Value.fromJson(page.js, state);
|
||||
return value;
|
||||
} else {
|
||||
return null;
|
||||
@@ -201,7 +201,7 @@ pub const PopStateEvent = struct {
|
||||
|
||||
pub fn get_state(self: *const PopStateEvent, page: *Page) !?js.Value {
|
||||
if (self.state) |state| {
|
||||
const value = try js.Value.fromJson(page.main_context, state);
|
||||
const value = try js.Value.fromJson(page.js, state);
|
||||
return value;
|
||||
} else {
|
||||
return null;
|
||||
|
||||
@@ -37,7 +37,6 @@ const domcss = @import("../dom/css.zig");
|
||||
const Css = @import("../css/css.zig").Css;
|
||||
const EventHandler = @import("../events/event.zig").EventHandler;
|
||||
|
||||
const v8 = @import("v8");
|
||||
const Request = @import("../fetch/Request.zig");
|
||||
const fetchFn = @import("../fetch/fetch.zig").fetch;
|
||||
|
||||
|
||||
@@ -77,9 +77,9 @@ pub fn constructor(self: *Caller, comptime Struct: type, comptime named_function
|
||||
const this = info.getThis();
|
||||
if (@typeInfo(ReturnType) == .error_union) {
|
||||
const non_error_res = res catch |err| return err;
|
||||
_ = try Context.mapZigInstanceToJs(self.v8_context, this, non_error_res);
|
||||
_ = try self.context.mapZigInstanceToJs(this, non_error_res);
|
||||
} else {
|
||||
_ = try Context.mapZigInstanceToJs(self.v8_context, this, res);
|
||||
_ = try self.context.mapZigInstanceToJs(this, res);
|
||||
}
|
||||
info.getReturnValue().set(this);
|
||||
}
|
||||
@@ -209,7 +209,7 @@ fn namedSetOrDeleteCall(res: anytype, has_value: bool) !u8 {
|
||||
}
|
||||
|
||||
fn nameToString(self: *Caller, name: v8.Name) ![]const u8 {
|
||||
return js.valueToString(self.call_arena, .{ .handle = name.handle }, self.isolate, self.v8_context);
|
||||
return self.context.valueToString(.{ .handle = name.handle }, .{});
|
||||
}
|
||||
|
||||
fn isSelfReceiver(comptime Struct: type, comptime named_function: NamedFunction) bool {
|
||||
@@ -258,7 +258,7 @@ pub fn handleError(self: *Caller, comptime Struct: type, comptime named_function
|
||||
|
||||
if (comptime @import("builtin").mode == .Debug and @hasDecl(@TypeOf(info), "length")) {
|
||||
if (log.enabled(.js, .warn)) {
|
||||
logFunctionCallError(self.call_arena, self.isolate, self.v8_context, err, named_function.full_name, info);
|
||||
self.logFunctionCallError(err, named_function.full_name, info);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -461,6 +461,38 @@ fn getArgs(self: *const Caller, comptime Struct: type, comptime named_function:
|
||||
return args;
|
||||
}
|
||||
|
||||
// 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(self: *Caller, err: anyerror, function_name: []const u8, info: v8.FunctionCallbackInfo) void {
|
||||
const args_dump = self.serializeFunctionArgs(info) catch "failed to serialize args";
|
||||
log.info(.js, "function call error", .{
|
||||
.name = function_name,
|
||||
.err = err,
|
||||
.args = args_dump,
|
||||
.stack = self.context.stackTrace() catch |err1| @errorName(err1),
|
||||
});
|
||||
}
|
||||
|
||||
fn serializeFunctionArgs(self: *Caller, info: v8.FunctionCallbackInfo) ![]const u8 {
|
||||
const separator = log.separator();
|
||||
const js_parameter_count = info.length();
|
||||
|
||||
const context = self.context;
|
||||
var arr: std.ArrayListUnmanaged(u8) = .{};
|
||||
for (0..js_parameter_count) |i| {
|
||||
const js_value = info.getArg(@intCast(i));
|
||||
const value_string = try context.valueToDetailString(js_value);
|
||||
const value_type = try context.jsStringToZig(try js_value.typeOf(self.isolate), .{});
|
||||
try std.fmt.format(arr.writer(context.call_arena), "{s}{d}: {s} ({s})", .{
|
||||
separator,
|
||||
i + 1,
|
||||
value_string,
|
||||
value_type,
|
||||
});
|
||||
}
|
||||
return arr.items;
|
||||
}
|
||||
|
||||
// We want the function name, or more precisely, the "Struct.function" for
|
||||
// displaying helpful @compileError.
|
||||
// However, there's no way to get the name from a std.Builtin.Fn, so we create
|
||||
@@ -524,37 +556,6 @@ fn isPage(comptime T: type) bool {
|
||||
return T == *Page or T == *const Page;
|
||||
}
|
||||
|
||||
// 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.info(.js, "function call error", .{
|
||||
.name = function_name,
|
||||
.err = err,
|
||||
.args = args_dump,
|
||||
.stack = Context.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 js.valueToDetailString(arena, js_value, isolate, context);
|
||||
const value_type = try js.stringToZig(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;
|
||||
}
|
||||
|
||||
fn createTypeException(isolate: v8.Isolate, msg: []const u8) v8.Value {
|
||||
return v8.Exception.initTypeError(v8.String.initUtf8(isolate, msg));
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -74,15 +74,7 @@ pub fn init(allocator: Allocator, platform: *const Platform, _: Opts) !*Env {
|
||||
isolate.enter();
|
||||
errdefer isolate.exit();
|
||||
|
||||
isolate.setHostInitializeImportMetaObjectCallback(struct {
|
||||
fn callback(c_context: ?*v8.C_Context, c_module: ?*v8.C_Module, c_meta: ?*v8.C_Value) callconv(.c) void {
|
||||
const v8_context = v8.Context{ .handle = c_context.? };
|
||||
const context: *Context = @ptrFromInt(v8_context.getEmbedderData(1).castTo(v8.BigInt).getUint64());
|
||||
context.initializeImportMeta(v8.Module{ .handle = c_module.? }, v8.Object{ .handle = c_meta.? }) catch |err| {
|
||||
log.err(.js, "import meta", .{ .err = err });
|
||||
};
|
||||
}
|
||||
}.callback);
|
||||
isolate.setHostInitializeImportMetaObjectCallback(Context.metaObjectCallback);
|
||||
|
||||
var temp_scope: v8.HandleScope = undefined;
|
||||
v8.HandleScope.init(&temp_scope, isolate);
|
||||
@@ -226,11 +218,10 @@ pub fn dumpMemoryStats(self: *Env) void {
|
||||
fn promiseRejectCallback(v8_msg: v8.C_PromiseRejectMessage) callconv(.c) void {
|
||||
const msg = v8.PromiseRejectMessage.initFromC(v8_msg);
|
||||
const isolate = msg.getPromise().toObject().getIsolate();
|
||||
const v8_context = isolate.getCurrentContext();
|
||||
const context: *Context = @ptrFromInt(v8_context.getEmbedderData(1).castTo(v8.BigInt).getUint64());
|
||||
const context = Context.fromIsolate(isolate);
|
||||
|
||||
const value =
|
||||
if (msg.getValue()) |v8_value| js.valueToString(context.call_arena, v8_value, isolate, v8_context) catch |err| @errorName(err) else "no value";
|
||||
if (msg.getValue()) |v8_value| context.valueToString(v8_value, .{}) catch |err| @errorName(err) else "no value";
|
||||
|
||||
log.debug(.js, "unhandled rejection", .{ .value = value });
|
||||
}
|
||||
|
||||
@@ -58,12 +58,12 @@ pub fn deinit(self: *ExecutionWorld) void {
|
||||
// when the handle_scope is freed.
|
||||
// We also maintain our own "context_arena" which allows us to have
|
||||
// all page related memory easily managed.
|
||||
pub fn createContext(self: *ExecutionWorld, global: anytype, page: *Page, script_manager: ?*ScriptManager, enter: bool, global_callback: ?js.GlobalMissingCallback) !*Context {
|
||||
pub fn createContext(self: *ExecutionWorld, page: *Page, enter: bool, global_callback: ?js.GlobalMissingCallback) !*Context {
|
||||
std.debug.assert(self.context == null);
|
||||
|
||||
const env = self.env;
|
||||
const isolate = env.isolate;
|
||||
const Global = @TypeOf(global.*);
|
||||
const Global = @TypeOf(page.window);
|
||||
const templates = &self.env.templates;
|
||||
|
||||
var v8_context: v8.Context = blk: {
|
||||
@@ -84,12 +84,9 @@ pub fn createContext(self: *ExecutionWorld, global: anytype, page: *Page, script
|
||||
.getter = struct {
|
||||
fn callback(c_name: ?*const v8.C_Name, raw_info: ?*const v8.C_PropertyCallbackInfo) callconv(.c) u8 {
|
||||
const info = v8.PropertyCallbackInfo.initFromV8(raw_info);
|
||||
const _isolate = info.getIsolate();
|
||||
const v8_context = _isolate.getCurrentContext();
|
||||
const context = Context.fromIsolate(info.getIsolate());
|
||||
|
||||
const context: *Context = @ptrFromInt(v8_context.getEmbedderData(1).castTo(v8.BigInt).getUint64());
|
||||
|
||||
const property = js.valueToString(context.call_arena, .{ .handle = c_name.? }, _isolate, v8_context) catch "???";
|
||||
const property = context.valueToString(.{ .handle = c_name.? }, .{}) catch "???";
|
||||
if (context.global_callback.?.missing(property, context)) {
|
||||
return v8.Intercepted.Yes;
|
||||
}
|
||||
@@ -180,7 +177,7 @@ pub fn createContext(self: *ExecutionWorld, global: anytype, page: *Page, script
|
||||
.templates = &env.templates,
|
||||
.meta_lookup = &env.meta_lookup,
|
||||
.handle_scope = handle_scope,
|
||||
.script_manager = script_manager,
|
||||
.script_manager = &page.script_manager,
|
||||
.call_arena = self.call_arena.allocator(),
|
||||
.context_arena = self.context_arena.allocator(),
|
||||
.global_callback = global_callback,
|
||||
@@ -188,9 +185,8 @@ pub fn createContext(self: *ExecutionWorld, global: anytype, page: *Page, script
|
||||
|
||||
var context = &self.context.?;
|
||||
{
|
||||
// Given a context, we can get our executor.
|
||||
// (we store a pointer to our executor in the context's
|
||||
// embeddeder data)
|
||||
// Store a pointer to our context inside the v8 context so that, given
|
||||
// a v8 context, we can get our context out
|
||||
const data = isolate.initBigIntU64(@intCast(@intFromPtr(context)));
|
||||
v8_context.setEmbedderData(1, data);
|
||||
}
|
||||
@@ -240,7 +236,7 @@ pub fn createContext(self: *ExecutionWorld, global: anytype, page: *Page, script
|
||||
}
|
||||
}
|
||||
|
||||
_ = try context._mapZigInstanceToJs(v8_context.getGlobal(), global);
|
||||
try context.setupGlobal();
|
||||
return context;
|
||||
}
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ pub const Result = struct {
|
||||
|
||||
pub fn getName(self: *const Function, allocator: Allocator) ![]const u8 {
|
||||
const name = self.func.castToFunction().getName();
|
||||
return js.valueToString(allocator, name, self.context.isolate, self.context.v8_context);
|
||||
return self.context.valueToString(name, .{ .allocator = allocator });
|
||||
}
|
||||
|
||||
pub fn setName(self: *const Function, name: []const u8) void {
|
||||
|
||||
@@ -80,7 +80,7 @@ pub fn contextCreated(
|
||||
// we'll create it and track it for cleanup when the context ends.
|
||||
pub fn getRemoteObject(
|
||||
self: *const Inspector,
|
||||
context: *const Context,
|
||||
context: *Context,
|
||||
group: []const u8,
|
||||
value: anytype,
|
||||
) !RemoteObject {
|
||||
|
||||
@@ -52,24 +52,22 @@ pub fn isTruthy(self: Object) bool {
|
||||
}
|
||||
|
||||
pub fn toString(self: Object) ![]const u8 {
|
||||
const context = self.context;
|
||||
const js_value = self.js_obj.toValue();
|
||||
return js.valueToString(context.call_arena, js_value, context.isolate, context.v8_context);
|
||||
return self.context.valueToString(js_value, .{});
|
||||
}
|
||||
|
||||
pub fn toDetailString(self: Object) ![]const u8 {
|
||||
const context = self.context;
|
||||
const js_value = self.js_obj.toValue();
|
||||
return js.valueToDetailString(context.call_arena, js_value, context.isolate, context.v8_context);
|
||||
return self.context.valueToDetailString(js_value);
|
||||
}
|
||||
|
||||
pub fn format(self: Object, comptime _: []const u8, _: std.fmt.FormatOptions, writer: anytype) !void {
|
||||
return writer.writeAll(try self.toString());
|
||||
}
|
||||
|
||||
pub fn toJson(self: Object, allocator: std.mem.Allocator) ![]u8 {
|
||||
pub fn toJson(self: Object, allocator: Allocator) ![]u8 {
|
||||
const json_string = try v8.Json.stringify(self.context.v8_context, self.js_obj.toValue(), null);
|
||||
const str = try js.stringToZig(allocator, json_string, self.context.isolate);
|
||||
const str = try self.context.jsStringToZig(json_string, .{ .allocator = allocator });
|
||||
return str;
|
||||
}
|
||||
|
||||
@@ -137,11 +135,6 @@ pub fn nameIterator(self: Object) js.ValueIterator {
|
||||
};
|
||||
}
|
||||
|
||||
pub fn constructorName(self: Object, allocator: Allocator) ![]const u8 {
|
||||
const str = try self.js_obj.getConstructorName();
|
||||
return js.StringToZig(allocator, str, self.context.isolate);
|
||||
}
|
||||
|
||||
pub fn toZig(self: Object, comptime Struct: type, comptime name: []const u8, comptime T: type) !T {
|
||||
const named_function = comptime Caller.NamedFunction.init(Struct, name);
|
||||
return self.context.jsValueToZig(named_function, T, self.js_obj.toValue());
|
||||
|
||||
@@ -23,7 +23,3 @@ pub fn setIndex(self: This, index: u32, value: anytype, opts: js.Object.SetOpts)
|
||||
pub fn set(self: This, key: []const u8, value: anytype, opts: js.Object.SetOpts) !void {
|
||||
return self.obj.set(key, value, opts);
|
||||
}
|
||||
|
||||
pub fn constructorName(self: This, allocator: Allocator) ![]const u8 {
|
||||
return try self.obj.constructorName(allocator);
|
||||
}
|
||||
|
||||
@@ -21,15 +21,14 @@ pub fn hasCaught(self: TryCatch) bool {
|
||||
// the caller needs to deinit the string returned
|
||||
pub fn exception(self: TryCatch, allocator: Allocator) !?[]const u8 {
|
||||
const msg = self.inner.getException() orelse return null;
|
||||
const context = self.context;
|
||||
return try js.valueToString(allocator, msg, context.isolate, context.v8_context);
|
||||
return try self.context.valueToString(msg, .{ .allocator = allocator });
|
||||
}
|
||||
|
||||
// the caller needs to deinit the string returned
|
||||
pub fn stack(self: TryCatch, allocator: Allocator) !?[]const u8 {
|
||||
const context = self.context;
|
||||
const s = self.inner.getStackTrace(context.v8_context) orelse return null;
|
||||
return try js.valueToString(allocator, s, context.isolate, context.v8_context);
|
||||
return try context.valueToString(s, .{ .allocator = allocator });
|
||||
}
|
||||
|
||||
// the caller needs to deinit the string returned
|
||||
@@ -37,7 +36,7 @@ pub fn sourceLine(self: TryCatch, allocator: Allocator) !?[]const u8 {
|
||||
const context = self.context;
|
||||
const msg = self.inner.getMessage() orelse return null;
|
||||
const sl = msg.getSourceLine(context.v8_context) orelse return null;
|
||||
return try js.stringToZig(allocator, sl, context.isolate);
|
||||
return try context.jsStringToZig(sl, .{ .allocator = allocator });
|
||||
}
|
||||
|
||||
pub fn sourceLineNumber(self: TryCatch) ?u32 {
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
const std = @import("std");
|
||||
const builtin = @import("builtin");
|
||||
pub const v8 = @import("v8");
|
||||
|
||||
const types = @import("types.zig");
|
||||
@@ -64,9 +63,7 @@ pub const PromiseResolver = struct {
|
||||
resolver: v8.PromiseResolver,
|
||||
|
||||
pub fn promise(self: PromiseResolver) Promise {
|
||||
return .{
|
||||
.promise = self.resolver.getPromise(),
|
||||
};
|
||||
return self.resolver.getPromise();
|
||||
}
|
||||
|
||||
pub fn resolve(self: PromiseResolver, value: anytype) !void {
|
||||
@@ -101,9 +98,7 @@ pub const PersistentPromiseResolver = struct {
|
||||
}
|
||||
|
||||
pub fn promise(self: PersistentPromiseResolver) Promise {
|
||||
return .{
|
||||
.promise = self.resolver.castToPromiseResolver().getPromise(),
|
||||
};
|
||||
return self.resolver.castToPromiseResolver().getPromise();
|
||||
}
|
||||
|
||||
pub fn resolve(self: PersistentPromiseResolver, value: anytype) !void {
|
||||
@@ -129,9 +124,7 @@ pub const PersistentPromiseResolver = struct {
|
||||
}
|
||||
};
|
||||
|
||||
pub const Promise = struct {
|
||||
promise: v8.Promise,
|
||||
};
|
||||
pub const Promise = v8.Promise;
|
||||
|
||||
// When doing jsValueToZig, string ([]const u8) are managed by the
|
||||
// call_arena. That means that if the API wants to persist the string
|
||||
@@ -148,8 +141,7 @@ pub const Exception = struct {
|
||||
|
||||
// the caller needs to deinit the string returned
|
||||
pub fn exception(self: Exception, allocator: Allocator) ![]const u8 {
|
||||
const context = self.context;
|
||||
return try valueToString(allocator, self.inner, context.isolate, context.v8_context);
|
||||
return self.context.valueToString(self.inner, .{ .allocator = allocator });
|
||||
}
|
||||
};
|
||||
|
||||
@@ -159,8 +151,7 @@ pub const Value = struct {
|
||||
|
||||
// the caller needs to deinit the string returned
|
||||
pub fn toString(self: Value, allocator: Allocator) ![]const u8 {
|
||||
const context = self.context;
|
||||
return valueToString(allocator, self.value, context.isolate, context.v8_context);
|
||||
return self.context.valueToString(self.value, .{ .allocator = allocator });
|
||||
}
|
||||
|
||||
pub fn fromJson(ctx: *Context, json: []const u8) !Value {
|
||||
@@ -476,93 +467,6 @@ pub const TaggedAnyOpaque = struct {
|
||||
subtype: ?types.Sub,
|
||||
};
|
||||
|
||||
pub fn valueToDetailString(arena: Allocator, value: v8.Value, isolate: v8.Isolate, v8_context: v8.Context) ![]u8 {
|
||||
var str: ?v8.String = null;
|
||||
if (value.isObject() and !value.isFunction()) blk: {
|
||||
str = v8.Json.stringify(v8_context, value, null) catch break :blk;
|
||||
|
||||
if (str.?.lenUtf8(isolate) == 2) {
|
||||
// {} isn't useful, null this so that we can get the toDetailString
|
||||
// (which might also be useless, but maybe not)
|
||||
str = null;
|
||||
}
|
||||
}
|
||||
|
||||
if (str == null) {
|
||||
str = try value.toDetailString(v8_context);
|
||||
}
|
||||
|
||||
const s = try stringToZig(arena, str.?, isolate);
|
||||
if (comptime builtin.mode == .Debug) {
|
||||
if (std.mem.eql(u8, s, "[object Object]")) {
|
||||
if (debugValueToString(arena, value.castTo(v8.Object), isolate, v8_context)) |ds| {
|
||||
return ds;
|
||||
} else |err| {
|
||||
log.err(.js, "debug serialize value", .{ .err = err });
|
||||
}
|
||||
}
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
pub fn valueToString(allocator: Allocator, value: v8.Value, isolate: v8.Isolate, v8_context: v8.Context) ![]u8 {
|
||||
if (value.isSymbol()) {
|
||||
// symbol's can't be converted to a string
|
||||
return allocator.dupe(u8, "$Symbol");
|
||||
}
|
||||
const str = try value.toString(v8_context);
|
||||
return stringToZig(allocator, str, isolate);
|
||||
}
|
||||
|
||||
pub fn valueToStringZ(allocator: Allocator, value: v8.Value, isolate: v8.Isolate, v8_context: v8.Context) ![:0]u8 {
|
||||
const str = try value.toString(v8_context);
|
||||
const len = str.lenUtf8(isolate);
|
||||
const buf = try allocator.allocSentinel(u8, len, 0);
|
||||
const n = str.writeUtf8(isolate, buf);
|
||||
std.debug.assert(n == len);
|
||||
return buf;
|
||||
}
|
||||
|
||||
pub fn stringToZig(allocator: Allocator, str: v8.String, isolate: v8.Isolate) ![]u8 {
|
||||
const len = str.lenUtf8(isolate);
|
||||
const buf = try allocator.alloc(u8, len);
|
||||
const n = str.writeUtf8(isolate, buf);
|
||||
std.debug.assert(n == len);
|
||||
return buf;
|
||||
}
|
||||
|
||||
fn debugValueToString(arena: Allocator, js_obj: v8.Object, isolate: v8.Isolate, v8_context: v8.Context) ![]u8 {
|
||||
if (comptime builtin.mode != .Debug) {
|
||||
@compileError("debugValue can only be called in debug mode");
|
||||
}
|
||||
|
||||
var arr: std.ArrayListUnmanaged(u8) = .empty;
|
||||
var writer = arr.writer(arena);
|
||||
|
||||
const names_arr = js_obj.getOwnPropertyNames(v8_context);
|
||||
const names_obj = names_arr.castTo(v8.Object);
|
||||
const len = names_arr.length();
|
||||
|
||||
try writer.writeAll("(JSON.stringify failed, dumping top-level fields)\n");
|
||||
for (0..len) |i| {
|
||||
const field_name = try names_obj.getAtIndex(v8_context, @intCast(i));
|
||||
const field_value = try js_obj.getValue(v8_context, field_name);
|
||||
const name = try valueToString(arena, field_name, isolate, v8_context);
|
||||
const value = try valueToString(arena, field_value, isolate, v8_context);
|
||||
try writer.writeAll(name);
|
||||
try writer.writeAll(": ");
|
||||
if (std.mem.indexOfAny(u8, value, &std.ascii.whitespace) == null) {
|
||||
try writer.writeAll(value);
|
||||
} else {
|
||||
try writer.writeByte('"');
|
||||
try writer.writeAll(value);
|
||||
try writer.writeByte('"');
|
||||
}
|
||||
try writer.writeByte(' ');
|
||||
}
|
||||
return arr.items;
|
||||
}
|
||||
|
||||
// These are here, and not in Inspector.zig, because Inspector.zig isn't always
|
||||
// included (e.g. in the wpt build).
|
||||
|
||||
|
||||
@@ -74,7 +74,7 @@ pub const Page = struct {
|
||||
|
||||
// Our JavaScript context for this specific page. This is what we use to
|
||||
// execute any JavaScript
|
||||
main_context: *js.Context,
|
||||
js: *js.Context,
|
||||
|
||||
// indicates intention to navigate to another page on the next loop execution.
|
||||
delayed_navigation: bool = false,
|
||||
@@ -140,11 +140,11 @@ pub const Page = struct {
|
||||
.scheduler = Scheduler.init(arena),
|
||||
.keydown_event_node = .{ .func = keydownCallback },
|
||||
.window_clicked_event_node = .{ .func = windowClicked },
|
||||
.main_context = undefined,
|
||||
.js = undefined,
|
||||
};
|
||||
|
||||
self.main_context = try session.executor.createContext(&self.window, self, &self.script_manager, true, js.GlobalMissingCallback.init(&self.polyfill_loader));
|
||||
try polyfill.preload(self.arena, self.main_context);
|
||||
self.js = try session.executor.createContext(self, true, js.GlobalMissingCallback.init(&self.polyfill_loader));
|
||||
try polyfill.preload(self.arena, self.js);
|
||||
|
||||
try self.scheduler.add(self, runMicrotasks, 5, .{ .name = "page.microtasks" });
|
||||
// message loop must run only non-test env
|
||||
@@ -277,7 +277,7 @@ pub const Page = struct {
|
||||
var ms_remaining = wait_ms;
|
||||
|
||||
var try_catch: js.TryCatch = undefined;
|
||||
try_catch.init(self.main_context);
|
||||
try_catch.init(self.js);
|
||||
defer try_catch.deinit();
|
||||
|
||||
var scheduler = &self.scheduler;
|
||||
@@ -1116,7 +1116,7 @@ pub const Page = struct {
|
||||
|
||||
pub fn stackTrace(self: *Page) !?[]const u8 {
|
||||
if (comptime builtin.mode == .Debug) {
|
||||
return self.main_context.stackTrace();
|
||||
return self.js.stackTrace();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -105,8 +105,8 @@ const QueueingStrategy = struct {
|
||||
pub fn constructor(underlying: ?UnderlyingSource, _strategy: ?QueueingStrategy, page: *Page) !*ReadableStream {
|
||||
const strategy: QueueingStrategy = _strategy orelse .{};
|
||||
|
||||
const cancel_resolver = try page.main_context.createPersistentPromiseResolver(.self);
|
||||
const closed_resolver = try page.main_context.createPersistentPromiseResolver(.self);
|
||||
const cancel_resolver = try page.js.createPromiseResolver(.self);
|
||||
const closed_resolver = try page.js.createPromiseResolver(.self);
|
||||
|
||||
const stream = try page.arena.create(ReadableStream);
|
||||
stream.* = ReadableStream{
|
||||
|
||||
@@ -47,43 +47,26 @@ pub fn _read(self: *const ReadableStreamDefaultReader, page: *Page) !js.Promise
|
||||
.readable => {
|
||||
if (stream.queue.items.len > 0) {
|
||||
const data = self.stream.queue.orderedRemove(0);
|
||||
const resolver = page.main_context.createPromiseResolver();
|
||||
|
||||
try resolver.resolve(ReadableStreamReadResult.init(data, false));
|
||||
const promise = page.js.resolvePromise(ReadableStreamReadResult.init(data, false));
|
||||
try self.stream.pullIf();
|
||||
return resolver.promise();
|
||||
} else {
|
||||
return promise;
|
||||
}
|
||||
if (self.stream.reader_resolver) |rr| {
|
||||
return rr.promise();
|
||||
} else {
|
||||
const persistent_resolver = try page.main_context.createPersistentPromiseResolver(.page);
|
||||
}
|
||||
const persistent_resolver = try page.js.createPromiseResolver(.page);
|
||||
self.stream.reader_resolver = persistent_resolver;
|
||||
return persistent_resolver.promise();
|
||||
}
|
||||
}
|
||||
},
|
||||
.closed => |_| {
|
||||
const resolver = page.main_context.createPromiseResolver();
|
||||
|
||||
if (stream.queue.items.len > 0) {
|
||||
const data = self.stream.queue.orderedRemove(0);
|
||||
try resolver.resolve(ReadableStreamReadResult.init(data, false));
|
||||
} else {
|
||||
try resolver.resolve(ReadableStreamReadResult{ .done = true });
|
||||
return page.js.resolvePromise(ReadableStreamReadResult.init(data, false));
|
||||
}
|
||||
|
||||
return resolver.promise();
|
||||
},
|
||||
.cancelled => |_| {
|
||||
const resolver = page.main_context.createPromiseResolver();
|
||||
try resolver.resolve(ReadableStreamReadResult{ .value = .empty, .done = true });
|
||||
return resolver.promise();
|
||||
},
|
||||
.errored => |err| {
|
||||
const resolver = page.main_context.createPromiseResolver();
|
||||
try resolver.reject(err);
|
||||
return resolver.promise();
|
||||
return page.js.resolvePromise(ReadableStreamReadResult{ .done = true });
|
||||
},
|
||||
.cancelled => |_| return page.js.resolvePromise(ReadableStreamReadResult{ .value = .empty, .done = true }),
|
||||
.errored => |err| return page.js.rejectPromise(err),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -690,9 +690,7 @@ const IsolatedWorld = struct {
|
||||
return;
|
||||
}
|
||||
_ = try self.executor.createContext(
|
||||
&page.window,
|
||||
page,
|
||||
null,
|
||||
false,
|
||||
js.GlobalMissingCallback.init(&self.polyfill_loader),
|
||||
);
|
||||
|
||||
@@ -274,7 +274,7 @@ fn resolveNode(cmd: anytype) !void {
|
||||
const bc = cmd.browser_context orelse return error.BrowserContextNotLoaded;
|
||||
const page = bc.session.currentPage() orelse return error.PageNotLoaded;
|
||||
|
||||
var js_context = page.main_context;
|
||||
var js_context = page.js;
|
||||
if (params.executionContextId) |context_id| {
|
||||
if (js_context.v8_context.debugContextId() != context_id) {
|
||||
for (bc.isolated_worlds.items) |*isolated_world| {
|
||||
|
||||
@@ -251,7 +251,7 @@ pub fn pageNavigate(arena: Allocator, bc: anytype, event: *const Notification.Pa
|
||||
const page = bc.session.currentPage() orelse return error.PageNotLoaded;
|
||||
const aux_data = try std.fmt.allocPrint(arena, "{{\"isDefault\":true,\"type\":\"default\",\"frameId\":\"{s}\"}}", .{target_id});
|
||||
bc.inspector.contextCreated(
|
||||
page.main_context,
|
||||
page.js,
|
||||
"",
|
||||
try page.origin(arena),
|
||||
aux_data,
|
||||
|
||||
@@ -147,7 +147,7 @@ fn createTarget(cmd: anytype) !void {
|
||||
{
|
||||
const aux_data = try std.fmt.allocPrint(cmd.arena, "{{\"isDefault\":true,\"type\":\"default\",\"frameId\":\"{s}\"}}", .{target_id});
|
||||
bc.inspector.contextCreated(
|
||||
page.main_context,
|
||||
page.js,
|
||||
"",
|
||||
try page.origin(cmd.arena),
|
||||
aux_data,
|
||||
|
||||
@@ -123,7 +123,7 @@ fn run(
|
||||
|
||||
_ = page.wait(2000);
|
||||
|
||||
const js_context = page.main_context;
|
||||
const js_context = page.js;
|
||||
var try_catch: js.TryCatch = undefined;
|
||||
try_catch.init(js_context);
|
||||
defer try_catch.deinit();
|
||||
|
||||
@@ -395,7 +395,7 @@ pub fn htmlRunner(file: []const u8) !void {
|
||||
|
||||
page.arena = @import("root").tracking_allocator;
|
||||
|
||||
const js_context = page.main_context;
|
||||
const js_context = page.js;
|
||||
var try_catch: js.TryCatch = undefined;
|
||||
try_catch.init(js_context);
|
||||
defer try_catch.deinit();
|
||||
@@ -409,7 +409,7 @@ pub fn htmlRunner(file: []const u8) !void {
|
||||
page.session.browser.runMessageLoop();
|
||||
|
||||
const needs_second_wait = try js_context.exec("testing._onPageWait.length > 0", "check_onPageWait");
|
||||
if (needs_second_wait.value.toBool(page.main_context.isolate)) {
|
||||
if (needs_second_wait.value.toBool(page.js.isolate)) {
|
||||
// sets the isSecondWait flag in testing.
|
||||
_ = js_context.exec("testing._isSecondWait = true", "set_second_wait_flag") catch {};
|
||||
_ = page.wait(2000);
|
||||
|
||||
Reference in New Issue
Block a user