mirror of
https://github.com/lightpanda-io/browser.git
synced 2026-02-08 13:42:58 -05:00
V8's inspector world is made up of 4 components: Inspector, Client, Channel and Session. Currently, we treat all 4 components as a single unit which is tied to the lifetime of CDP BrowserContext - or, loosely speaking, 1 "Inspector Unit" per page / v8::Context. According to https://web.archive.org/web/20210622022956/https://hyperandroid.com/2020/02/12/v8-inspector-from-an-embedder-standpoint/ and conversation with Gemini, it's more typical to have 1 inspector per isolate. The general breakdown is the Inspector is the top-level manager, the Client is our implementation which control how the Inspector works (its function we expose that v8 calls into). These should be tied to the Isolate. Channels and Sessions are more closely tied to Context, where the Channel is v8->zig and the Session us zig->v8. This PR does a few things 1 - It creates 1 Inspector and Client per Isolate (Env.js) 2 - It creates 1 Session/Channel per BrowserContext 3 - It merges v8::Session and v8::Channel into Inspector.Session 4 - It moves the Inspector instance directly into the Env 5 - BrowserContext interacts with the Inspector.Session, not the Inspector 4 is arguably unnecessary with respect to the main goal of this commit, but the end-goal is to tighten the integration. Specifically, rather than CDP having to inform the inspector that a context was created/destroyed, the Env which manages Contexts directly (https://github.com/lightpanda-io/browser/pull/1432) and which now has direct access to the Inspector, is now equipped to keep this in sync.
116 lines
4.0 KiB
Zig
116 lines
4.0 KiB
Zig
// Copyright (C) 2023-2025 Lightpanda (Selecy SAS)
|
|
//
|
|
// Francis Bouvier <francis@lightpanda.io>
|
|
// Pierre Tachoire <pierre@lightpanda.io>
|
|
//
|
|
// This program is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU Affero General Public License as
|
|
// published by the Free Software Foundation, either version 3 of the
|
|
// License, or (at your option) any later version.
|
|
//
|
|
// This program is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU Affero General Public License for more details.
|
|
//
|
|
// 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 std = @import("std");
|
|
pub const App = @import("App.zig");
|
|
pub const Server = @import("Server.zig");
|
|
pub const Page = @import("browser/Page.zig");
|
|
pub const Browser = @import("browser/Browser.zig");
|
|
pub const Session = @import("browser/Session.zig");
|
|
|
|
pub const log = @import("log.zig");
|
|
pub const js = @import("browser/js/js.zig");
|
|
pub const dump = @import("browser/dump.zig");
|
|
pub const build_config = @import("build_config");
|
|
pub const crash_handler = @import("crash_handler.zig");
|
|
|
|
const IS_DEBUG = @import("builtin").mode == .Debug;
|
|
|
|
pub const FetchOpts = struct {
|
|
wait_ms: u32 = 5000,
|
|
dump: dump.RootOpts,
|
|
writer: ?*std.Io.Writer = null,
|
|
};
|
|
pub fn fetch(app: *App, url: [:0]const u8, opts: FetchOpts) !void {
|
|
var browser = try Browser.init(app, .{});
|
|
defer browser.deinit();
|
|
|
|
var session = try browser.newSession();
|
|
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
|
|
// // happens rarely, and I manually fix the file.
|
|
// page.js.startCpuProfiler();
|
|
// defer {
|
|
// if (page.js.stopCpuProfiler()) |profile| {
|
|
// std.fs.cwd().writeFile(.{
|
|
// .sub_path = ".lp-cache/cpu_profile.json",
|
|
// .data = profile,
|
|
// }) catch |err| {
|
|
// log.err(.app, "profile write error", .{ .err = err });
|
|
// };
|
|
// } else |err| {
|
|
// log.err(.app, "profile error", .{ .err = err });
|
|
// }
|
|
// }
|
|
|
|
// // Comment this out to get a heap V8 heap profil
|
|
// page.js.startHeapProfiler();
|
|
// defer {
|
|
// if (page.js.stopHeapProfiler()) |profile| {
|
|
// std.fs.cwd().writeFile(.{
|
|
// .sub_path = ".lp-cache/allocating.heapprofile",
|
|
// .data = profile.@"0",
|
|
// }) catch |err| {
|
|
// log.err(.app, "allocating write error", .{ .err = err });
|
|
// };
|
|
// std.fs.cwd().writeFile(.{
|
|
// .sub_path = ".lp-cache/snapshot.heapsnapshot",
|
|
// .data = profile.@"1",
|
|
// }) catch |err| {
|
|
// log.err(.app, "heapsnapshot write error", .{ .err = err });
|
|
// };
|
|
// } else |err| {
|
|
// log.err(.app, "profile error", .{ .err = err });
|
|
// }
|
|
// }
|
|
|
|
_ = try page.navigate(url, .{
|
|
.reason = .address_bar,
|
|
.kind = .{ .push = null },
|
|
});
|
|
_ = session.wait(opts.wait_ms);
|
|
|
|
const writer = opts.writer orelse return;
|
|
try dump.root(page.window._document, opts.dump, writer, page);
|
|
try writer.flush();
|
|
}
|
|
|
|
pub inline fn assert(ok: bool, comptime ctx: []const u8, args: anytype) void {
|
|
if (!ok) {
|
|
if (comptime IS_DEBUG) {
|
|
unreachable;
|
|
}
|
|
assertionFailure(ctx, args);
|
|
}
|
|
}
|
|
|
|
noinline fn assertionFailure(comptime ctx: []const u8, args: anytype) noreturn {
|
|
@branchHint(.cold);
|
|
if (@inComptime()) {
|
|
@compileError(std.fmt.comptimePrint("assertion failure: " ++ ctx, args));
|
|
}
|
|
@import("crash_handler.zig").crash(ctx, args, @returnAddress());
|
|
}
|
|
|
|
test {
|
|
std.testing.refAllDecls(@This());
|
|
}
|