diff --git a/build.zig.zon b/build.zig.zon index 27cc26ec..9d57095f 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -5,8 +5,8 @@ .fingerprint = 0xda130f3af836cea0, .dependencies = .{ .v8 = .{ - .url = "https://github.com/lightpanda-io/zig-v8-fork/archive/5ce9ade6c6d7f7ab9ab4582a6b1b36fdc8e246e2.tar.gz", - .hash = "v8-0.0.0-xddH6yTGAwA__kfiDozsVXL2_jnycrtDUfR8kIADQFUz", + .url = "https://github.com/lightpanda-io/zig-v8-fork/archive/305bb3706716d32d59b2ffa674731556caa1002b.tar.gz", + .hash = "v8-0.0.0-xddH63bVAwBSEobaUok9J0er1FqsvEujCDDVy6ItqKQ5", }, //.v8 = .{ .path = "../zig-v8-fork" } }, diff --git a/src/browser/html/window.zig b/src/browser/html/window.zig index 332e9252..5e03c022 100644 --- a/src/browser/html/window.zig +++ b/src/browser/html/window.zig @@ -136,14 +136,6 @@ pub const Window = struct { self.onload_callback = null; } - pub fn get_window(self: *Window) *Window { - return self; - } - - pub fn get_navigator(self: *Window) *Navigator { - return &self.navigator; - } - pub fn get_location(self: *Window) *Location { return &self.location; } @@ -152,22 +144,6 @@ pub const Window = struct { return page.navigateFromWebAPI(url, .{ .reason = .script }); } - pub fn get_console(self: *Window) *Console { - return &self.console; - } - - pub fn get_crypto(self: *Window) *Crypto { - return &self.crypto; - } - - pub fn get_self(self: *Window) *Window { - return self; - } - - pub fn get_parent(self: *Window) *Window { - return self; - } - // frames return the window itself, but accessing it via a pseudo // array returns the Window object corresponding to the given frame or // iframe. @@ -205,10 +181,6 @@ pub const Window = struct { return frames.get_length(); } - pub fn get_top(self: *Window) *Window { - return self; - } - pub fn get_document(self: *Window) ?*parser.DocumentHTML { return self.document; } @@ -243,14 +215,6 @@ pub const Window = struct { return &self.storage_shelf.?.bucket.session; } - pub fn get_performance(self: *Window) *Performance { - return &self.performance; - } - - pub fn get_screen(self: *Window) *Screen { - return &self.screen; - } - pub fn get_CSS(self: *Window) *Css { return &self.css; } @@ -463,6 +427,18 @@ pub const Window = struct { // and thus the target has already been set to the document. return self.base.redispatchEvent(evt); } + + pub fn postAttach(self: *Window, js_this: js.This) !void { + try js_this.set("top", self, .{}); + try js_this.set("self", self, .{}); + try js_this.set("parent", self, .{}); + try js_this.set("window", self, .{}); + try js_this.set("crypto", &self.crypto, .{}); + try js_this.set("screen", &self.screen, .{}); + try js_this.set("console", &self.console, .{}); + try js_this.set("navigator", &self.navigator, .{}); + try js_this.set("performance", &self.performance, .{}); + } }; const TimerCallback = struct { diff --git a/src/browser/js/Context.zig b/src/browser/js/Context.zig index 00288b47..142378b9 100644 --- a/src/browser/js/Context.zig +++ b/src/browser/js/Context.zig @@ -29,6 +29,8 @@ isolate: v8.Isolate, v8_context: v8.Context, handle_scope: ?v8.HandleScope, +cpu_profiler: ?v8.CpuProfiler = null, + // references Env.templates templates: []v8.FunctionTemplate, @@ -1815,6 +1817,11 @@ fn zigJsonToJs(isolate: v8.Isolate, v8_context: v8.Context, value: std.json.Valu } } +pub fn getGlobalThis(self: *Context) js.This { + const js_global = self.v8_context.getGlobal(); + return .{ .obj = .{ .js_obj = js_global, .context = self } }; +} + // == Misc == // An interface for types that want to have their jsDeinit function to be // called when the call context ends @@ -1843,3 +1850,26 @@ const DestructorCallback = struct { self.destructorFn(self.ptr); } }; + +// == Profiler == +pub fn startCpuProfiler(self: *Context) void { + if (builtin.mode != .Debug) { + // Still testing this out, don't have it properly exposed, so add this + // guard for the time being to prevent any accidental/weird prod issues. + @compileError("CPU Profiling is only available in debug builds"); + } + + std.debug.assert(self.cpu_profiler == null); + v8.CpuProfiler.useDetailedSourcePositionsForProfiling(self.isolate); + const cpu_profiler = v8.CpuProfiler.init(self.isolate); + const title = self.isolate.initStringUtf8("v8_cpu_profile"); + cpu_profiler.startProfiling(title); + self.cpu_profiler = cpu_profiler; +} + +pub fn stopCpuProfiler(self: *Context) ![]const u8 { + const title = self.isolate.initStringUtf8("v8_cpu_profile"); + const profile = self.cpu_profiler.?.stopProfiling(title) orelse unreachable; + const serialized = profile.serialize(self.isolate).?; + return self.jsStringToZig(serialized, .{}); +} diff --git a/src/browser/js/Object.zig b/src/browser/js/Object.zig index 36946c15..0fc44ba3 100644 --- a/src/browser/js/Object.zig +++ b/src/browser/js/Object.zig @@ -27,7 +27,7 @@ pub fn setIndex(self: Object, index: u32, value: anytype, opts: SetOpts) !void { return self.set(key, value, opts); } -pub fn set(self: Object, key: []const u8, value: anytype, opts: SetOpts) !void { +pub fn set(self: Object, key: []const u8, value: anytype, opts: SetOpts) error{ FailedToSet, OutOfMemory }!void { const context = self.context; const js_key = v8.String.initUtf8(context.isolate, key); diff --git a/src/browser/page.zig b/src/browser/page.zig index 721e4c26..d668b67c 100644 --- a/src/browser/page.zig +++ b/src/browser/page.zig @@ -849,7 +849,7 @@ pub const Page = struct { _ = self.session.browser.transfer_arena.reset(.{ .retain_with_limit = 4 * 1024 }); } - // extracted because this sis called from tests to set things up. + // extracted because this is called from tests to set things up. pub fn setDocument(self: *Page, html_doc: *parser.DocumentHTML) !void { const doc = parser.documentHTMLToDocument(html_doc); try parser.documentSetDocumentURI(doc, self.url.raw); diff --git a/src/main.zig b/src/main.zig index 46b7bc04..05f55c58 100644 --- a/src/main.zig +++ b/src/main.zig @@ -165,6 +165,24 @@ fn run(alloc: Allocator) !void { // page const page = try session.createPage(); + // // Comment this out to get a profile of the JS code in v8/profile.json. + // // You can open this in Chrome's profiler. + // // I've seen it generate invalid JSON, but I'm not sure why. It only + // // happens rarely, and I manually fix the file. + // page.js.startCpuProfiler(); + // defer { + // if (page.js.stopCpuProfiler()) |profile| { + // std.fs.cwd().writeFile(.{ + // .sub_path = "v8/profile.json", + // .data = profile, + // }) catch |err| { + // log.err(.app, "profile write error", .{ .err = err }); + // }; + // } else |err| { + // log.err(.app, "profile error", .{ .err = err }); + // } + // } + _ = page.navigate(url, .{}) catch |err| switch (err) { error.UnsupportedUriScheme, error.UriMissingHost => { log.fatal(.app, "invalid fetch URL", .{ .err = err, .url = url });