Compare commits

..

1 Commits

Author SHA1 Message Date
Karl Seguin
492fd86bad Expand the lifetime of the XHR reference
We need to take the self-reference to the XHR object as soon as the request is
made. Previously, we were waiting until we got the start callback, but v8 could
(and does) drop the reference before that happens. Unfortunately, that means
we can no longer use _transfer == null to tell if we own a reference or not, so
a new boolean was added.
2026-03-31 16:46:31 +08:00
21 changed files with 643 additions and 650 deletions

View File

@@ -44,6 +44,7 @@ _page: *Page,
_proto: *XMLHttpRequestEventTarget, _proto: *XMLHttpRequestEventTarget,
_arena: Allocator, _arena: Allocator,
_transfer: ?*HttpClient.Transfer = null, _transfer: ?*HttpClient.Transfer = null,
_has_ref: bool = false,
_url: [:0]const u8 = "", _url: [:0]const u8 = "",
_method: net_http.Method = .GET, _method: net_http.Method = .GET,
@@ -136,6 +137,14 @@ pub fn deinit(self: *XMLHttpRequest, session: *Session) void {
session.releaseArena(self._arena); session.releaseArena(self._arena);
} }
fn releaseSelfRef(self: *XMLHttpRequest) void {
if (self._has_ref == false) {
return;
}
self.releaseRef(self._page._session);
self._has_ref = false;
}
pub fn releaseRef(self: *XMLHttpRequest, session: *Session) void { pub fn releaseRef(self: *XMLHttpRequest, session: *Session) void {
self._rc.release(self, session); self._rc.release(self, session);
} }
@@ -252,6 +261,8 @@ pub fn send(self: *XMLHttpRequest, body_: ?[]const u8) !void {
.error_callback = httpErrorCallback, .error_callback = httpErrorCallback,
.shutdown_callback = httpShutdownCallback, .shutdown_callback = httpShutdownCallback,
}); });
self.acquireRef();
self._has_ref = true;
} }
fn handleBlobUrl(self: *XMLHttpRequest, page: *Page) !void { fn handleBlobUrl(self: *XMLHttpRequest, page: *Page) !void {
@@ -393,7 +404,6 @@ fn httpStartCallback(transfer: *HttpClient.Transfer) !void {
log.debug(.http, "request start", .{ .method = self._method, .url = self._url, .source = "xhr" }); log.debug(.http, "request start", .{ .method = self._method, .url = self._url, .source = "xhr" });
} }
self._transfer = transfer; self._transfer = transfer;
self.acquireRef();
} }
fn httpHeaderCallback(transfer: *HttpClient.Transfer, header: net_http.Header) !void { fn httpHeaderCallback(transfer: *HttpClient.Transfer, header: net_http.Header) !void {
@@ -501,8 +511,8 @@ fn httpErrorCallback(ctx: *anyopaque, err: anyerror) void {
self.handleError(err); self.handleError(err);
if (self._transfer != null) { if (self._transfer != null) {
self._transfer = null; self._transfer = null;
self.releaseRef(self._page._session);
} }
self.releaseSelfRef();
} }
fn httpShutdownCallback(ctx: *anyopaque) void { fn httpShutdownCallback(ctx: *anyopaque) void {
@@ -515,8 +525,8 @@ pub fn abort(self: *XMLHttpRequest) void {
if (self._transfer) |transfer| { if (self._transfer) |transfer| {
self._transfer = null; self._transfer = null;
transfer.abort(error.Abort); transfer.abort(error.Abort);
self.releaseRef(self._page._session);
} }
self.releaseSelfRef();
} }
fn handleError(self: *XMLHttpRequest, err: anyerror) void { fn handleError(self: *XMLHttpRequest, err: anyerror) void {

File diff suppressed because it is too large Load Diff

View File

@@ -18,9 +18,8 @@
const std = @import("std"); const std = @import("std");
const id = @import("../id.zig"); const id = @import("../id.zig");
const CDP = @import("../CDP.zig");
pub fn processMessage(cmd: *CDP.Command) !void { pub fn processMessage(cmd: anytype) !void {
const action = std.meta.stringToEnum(enum { const action = std.meta.stringToEnum(enum {
enable, enable,
disable, disable,
@@ -33,15 +32,15 @@ pub fn processMessage(cmd: *CDP.Command) !void {
.getFullAXTree => return getFullAXTree(cmd), .getFullAXTree => return getFullAXTree(cmd),
} }
} }
fn enable(cmd: *CDP.Command) !void { fn enable(cmd: anytype) !void {
return cmd.sendResult(null, .{}); return cmd.sendResult(null, .{});
} }
fn disable(cmd: *CDP.Command) !void { fn disable(cmd: anytype) !void {
return cmd.sendResult(null, .{}); return cmd.sendResult(null, .{});
} }
fn getFullAXTree(cmd: *CDP.Command) !void { fn getFullAXTree(cmd: anytype) !void {
const params = (try cmd.params(struct { const params = (try cmd.params(struct {
depth: ?i32 = null, depth: ?i32 = null,
frameId: ?[]const u8 = null, frameId: ?[]const u8 = null,

View File

@@ -17,7 +17,6 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>. // along with this program. If not, see <https://www.gnu.org/licenses/>.
const std = @import("std"); const std = @import("std");
const CDP = @import("../CDP.zig");
// TODO: hard coded data // TODO: hard coded data
const PROTOCOL_VERSION = "1.3"; const PROTOCOL_VERSION = "1.3";
@@ -36,7 +35,7 @@ const PRODUCT = "Chrome/124.0.6367.29";
const JS_VERSION = "12.4.254.8"; const JS_VERSION = "12.4.254.8";
const DEV_TOOLS_WINDOW_ID = 1923710101; const DEV_TOOLS_WINDOW_ID = 1923710101;
pub fn processMessage(cmd: *CDP.Command) !void { pub fn processMessage(cmd: anytype) !void {
const action = std.meta.stringToEnum(enum { const action = std.meta.stringToEnum(enum {
getVersion, getVersion,
setPermission, setPermission,
@@ -58,7 +57,7 @@ pub fn processMessage(cmd: *CDP.Command) !void {
} }
} }
fn getVersion(cmd: *CDP.Command) !void { fn getVersion(cmd: anytype) !void {
// TODO: pre-serialize? // TODO: pre-serialize?
return cmd.sendResult(.{ return cmd.sendResult(.{
.protocolVersion = PROTOCOL_VERSION, .protocolVersion = PROTOCOL_VERSION,
@@ -70,7 +69,7 @@ fn getVersion(cmd: *CDP.Command) !void {
} }
// TODO: noop method // TODO: noop method
fn setDownloadBehavior(cmd: *CDP.Command) !void { fn setDownloadBehavior(cmd: anytype) !void {
// const params = (try cmd.params(struct { // const params = (try cmd.params(struct {
// behavior: []const u8, // behavior: []const u8,
// browserContextId: ?[]const u8 = null, // browserContextId: ?[]const u8 = null,
@@ -81,7 +80,7 @@ fn setDownloadBehavior(cmd: *CDP.Command) !void {
return cmd.sendResult(null, .{ .include_session_id = false }); return cmd.sendResult(null, .{ .include_session_id = false });
} }
fn getWindowForTarget(cmd: *CDP.Command) !void { fn getWindowForTarget(cmd: anytype) !void {
// const params = (try cmd.params(struct { // const params = (try cmd.params(struct {
// targetId: ?[]const u8 = null, // targetId: ?[]const u8 = null,
// })) orelse return error.InvalidParams; // })) orelse return error.InvalidParams;
@@ -92,22 +91,22 @@ fn getWindowForTarget(cmd: *CDP.Command) !void {
} }
// TODO: noop method // TODO: noop method
fn setWindowBounds(cmd: *CDP.Command) !void { fn setWindowBounds(cmd: anytype) !void {
return cmd.sendResult(null, .{}); return cmd.sendResult(null, .{});
} }
// TODO: noop method // TODO: noop method
fn grantPermissions(cmd: *CDP.Command) !void { fn grantPermissions(cmd: anytype) !void {
return cmd.sendResult(null, .{}); return cmd.sendResult(null, .{});
} }
// TODO: noop method // TODO: noop method
fn setPermission(cmd: *CDP.Command) !void { fn setPermission(cmd: anytype) !void {
return cmd.sendResult(null, .{}); return cmd.sendResult(null, .{});
} }
// TODO: noop method // TODO: noop method
fn resetPermissions(cmd: *CDP.Command) !void { fn resetPermissions(cmd: anytype) !void {
return cmd.sendResult(null, .{}); return cmd.sendResult(null, .{});
} }

View File

@@ -17,9 +17,8 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>. // along with this program. If not, see <https://www.gnu.org/licenses/>.
const std = @import("std"); const std = @import("std");
const CDP = @import("../CDP.zig");
pub fn processMessage(cmd: *CDP.Command) !void { pub fn processMessage(cmd: anytype) !void {
const action = std.meta.stringToEnum(enum { const action = std.meta.stringToEnum(enum {
enable, enable,
}, cmd.input.action) orelse return error.UnknownMethod; }, cmd.input.action) orelse return error.UnknownMethod;

View File

@@ -18,18 +18,17 @@
const std = @import("std"); const std = @import("std");
const id = @import("../id.zig"); const id = @import("../id.zig");
const CDP = @import("../CDP.zig");
const Node = @import("../Node.zig");
const log = @import("../../log.zig"); const log = @import("../../log.zig");
const dump = @import("../../browser/dump.zig"); const Node = @import("../Node.zig");
const js = @import("../../browser/js/js.zig");
const DOMNode = @import("../../browser/webapi/Node.zig"); const DOMNode = @import("../../browser/webapi/Node.zig");
const Selector = @import("../../browser/webapi/selector/Selector.zig"); const Selector = @import("../../browser/webapi/selector/Selector.zig");
const dump = @import("../../browser/dump.zig");
const js = @import("../../browser/js/js.zig");
const Allocator = std.mem.Allocator; const Allocator = std.mem.Allocator;
pub fn processMessage(cmd: *CDP.Command) !void { pub fn processMessage(cmd: anytype) !void {
const action = std.meta.stringToEnum(enum { const action = std.meta.stringToEnum(enum {
enable, enable,
getDocument, getDocument,
@@ -70,7 +69,7 @@ pub fn processMessage(cmd: *CDP.Command) !void {
} }
// https://chromedevtools.github.io/devtools-protocol/tot/DOM/#method-getDocument // https://chromedevtools.github.io/devtools-protocol/tot/DOM/#method-getDocument
fn getDocument(cmd: *CDP.Command) !void { fn getDocument(cmd: anytype) !void {
const Params = struct { const Params = struct {
// CDP documentation implies that 0 isn't valid, but it _does_ work in Chrome // CDP documentation implies that 0 isn't valid, but it _does_ work in Chrome
depth: i32 = 3, depth: i32 = 3,
@@ -90,7 +89,7 @@ fn getDocument(cmd: *CDP.Command) !void {
} }
// https://chromedevtools.github.io/devtools-protocol/tot/DOM/#method-performSearch // https://chromedevtools.github.io/devtools-protocol/tot/DOM/#method-performSearch
fn performSearch(cmd: *CDP.Command) !void { fn performSearch(cmd: anytype) !void {
const params = (try cmd.params(struct { const params = (try cmd.params(struct {
query: []const u8, query: []const u8,
includeUserAgentShadowDOM: ?bool = null, includeUserAgentShadowDOM: ?bool = null,
@@ -117,7 +116,7 @@ fn performSearch(cmd: *CDP.Command) !void {
// hierarchy of each nodes. // hierarchy of each nodes.
// We dispatch event in the reverse order: from the top level to the direct parents. // We dispatch event in the reverse order: from the top level to the direct parents.
// We should dispatch a node only if it has never been sent. // We should dispatch a node only if it has never been sent.
fn dispatchSetChildNodes(cmd: *CDP.Command, dom_nodes: []const *DOMNode) !void { fn dispatchSetChildNodes(cmd: anytype, dom_nodes: []const *DOMNode) !void {
const arena = cmd.arena; const arena = cmd.arena;
const bc = cmd.browser_context orelse return error.BrowserContextNotLoaded; const bc = cmd.browser_context orelse return error.BrowserContextNotLoaded;
const session_id = bc.session_id orelse return error.SessionIdNotLoaded; const session_id = bc.session_id orelse return error.SessionIdNotLoaded;
@@ -173,7 +172,7 @@ fn dispatchSetChildNodes(cmd: *CDP.Command, dom_nodes: []const *DOMNode) !void {
} }
// https://chromedevtools.github.io/devtools-protocol/tot/DOM/#method-discardSearchResults // https://chromedevtools.github.io/devtools-protocol/tot/DOM/#method-discardSearchResults
fn discardSearchResults(cmd: *CDP.Command) !void { fn discardSearchResults(cmd: anytype) !void {
const params = (try cmd.params(struct { const params = (try cmd.params(struct {
searchId: []const u8, searchId: []const u8,
})) orelse return error.InvalidParams; })) orelse return error.InvalidParams;
@@ -185,7 +184,7 @@ fn discardSearchResults(cmd: *CDP.Command) !void {
} }
// https://chromedevtools.github.io/devtools-protocol/tot/DOM/#method-getSearchResults // https://chromedevtools.github.io/devtools-protocol/tot/DOM/#method-getSearchResults
fn getSearchResults(cmd: *CDP.Command) !void { fn getSearchResults(cmd: anytype) !void {
const params = (try cmd.params(struct { const params = (try cmd.params(struct {
searchId: []const u8, searchId: []const u8,
fromIndex: u32, fromIndex: u32,
@@ -210,7 +209,7 @@ fn getSearchResults(cmd: *CDP.Command) !void {
return cmd.sendResult(.{ .nodeIds = node_ids[params.fromIndex..params.toIndex] }, .{}); return cmd.sendResult(.{ .nodeIds = node_ids[params.fromIndex..params.toIndex] }, .{});
} }
fn querySelector(cmd: *CDP.Command) !void { fn querySelector(cmd: anytype) !void {
const params = (try cmd.params(struct { const params = (try cmd.params(struct {
nodeId: Node.Id, nodeId: Node.Id,
selector: []const u8, selector: []const u8,
@@ -236,7 +235,7 @@ fn querySelector(cmd: *CDP.Command) !void {
}, .{}); }, .{});
} }
fn querySelectorAll(cmd: *CDP.Command) !void { fn querySelectorAll(cmd: anytype) !void {
const params = (try cmd.params(struct { const params = (try cmd.params(struct {
nodeId: Node.Id, nodeId: Node.Id,
selector: []const u8, selector: []const u8,
@@ -267,7 +266,7 @@ fn querySelectorAll(cmd: *CDP.Command) !void {
}, .{}); }, .{});
} }
fn resolveNode(cmd: *CDP.Command) !void { fn resolveNode(cmd: anytype) !void {
const params = (try cmd.params(struct { const params = (try cmd.params(struct {
nodeId: ?Node.Id = null, nodeId: ?Node.Id = null,
backendNodeId: ?u32 = null, backendNodeId: ?u32 = null,
@@ -328,7 +327,7 @@ fn resolveNode(cmd: *CDP.Command) !void {
} }, .{}); } }, .{});
} }
fn describeNode(cmd: *CDP.Command) !void { fn describeNode(cmd: anytype) !void {
const params = (try cmd.params(struct { const params = (try cmd.params(struct {
nodeId: ?Node.Id = null, nodeId: ?Node.Id = null,
backendNodeId: ?Node.Id = null, backendNodeId: ?Node.Id = null,
@@ -375,7 +374,7 @@ fn rectToQuad(rect: DOMNode.Element.DOMRect) Quad {
}; };
} }
fn scrollIntoViewIfNeeded(cmd: *CDP.Command) !void { fn scrollIntoViewIfNeeded(cmd: anytype) !void {
const params = (try cmd.params(struct { const params = (try cmd.params(struct {
nodeId: ?Node.Id = null, nodeId: ?Node.Id = null,
backendNodeId: ?u32 = null, backendNodeId: ?u32 = null,
@@ -398,7 +397,7 @@ fn scrollIntoViewIfNeeded(cmd: *CDP.Command) !void {
return cmd.sendResult(null, .{}); return cmd.sendResult(null, .{});
} }
fn getNode(arena: Allocator, bc: *CDP.BrowserContext, node_id: ?Node.Id, backend_node_id: ?Node.Id, object_id: ?[]const u8) !*Node { fn getNode(arena: Allocator, bc: anytype, node_id: ?Node.Id, backend_node_id: ?Node.Id, object_id: ?[]const u8) !*Node {
const input_node_id = node_id orelse backend_node_id; const input_node_id = node_id orelse backend_node_id;
if (input_node_id) |input_node_id_| { if (input_node_id) |input_node_id_| {
return bc.node_registry.lookup_by_id.get(input_node_id_) orelse return error.NodeNotFound; return bc.node_registry.lookup_by_id.get(input_node_id_) orelse return error.NodeNotFound;
@@ -418,7 +417,7 @@ fn getNode(arena: Allocator, bc: *CDP.BrowserContext, node_id: ?Node.Id, backend
// https://chromedevtools.github.io/devtools-protocol/tot/DOM/#method-getContentQuads // https://chromedevtools.github.io/devtools-protocol/tot/DOM/#method-getContentQuads
// Related to: https://drafts.csswg.org/cssom-view/#the-geometryutils-interface // Related to: https://drafts.csswg.org/cssom-view/#the-geometryutils-interface
fn getContentQuads(cmd: *CDP.Command) !void { fn getContentQuads(cmd: anytype) !void {
const params = (try cmd.params(struct { const params = (try cmd.params(struct {
nodeId: ?Node.Id = null, nodeId: ?Node.Id = null,
backendNodeId: ?Node.Id = null, backendNodeId: ?Node.Id = null,
@@ -444,7 +443,7 @@ fn getContentQuads(cmd: *CDP.Command) !void {
return cmd.sendResult(.{ .quads = &.{quad} }, .{}); return cmd.sendResult(.{ .quads = &.{quad} }, .{});
} }
fn getBoxModel(cmd: *CDP.Command) !void { fn getBoxModel(cmd: anytype) !void {
const params = (try cmd.params(struct { const params = (try cmd.params(struct {
nodeId: ?Node.Id = null, nodeId: ?Node.Id = null,
backendNodeId: ?u32 = null, backendNodeId: ?u32 = null,
@@ -473,7 +472,7 @@ fn getBoxModel(cmd: *CDP.Command) !void {
} }, .{}); } }, .{});
} }
fn requestChildNodes(cmd: *CDP.Command) !void { fn requestChildNodes(cmd: anytype) !void {
const params = (try cmd.params(struct { const params = (try cmd.params(struct {
nodeId: Node.Id, nodeId: Node.Id,
depth: i32 = 1, depth: i32 = 1,
@@ -497,7 +496,7 @@ fn requestChildNodes(cmd: *CDP.Command) !void {
return cmd.sendResult(null, .{}); return cmd.sendResult(null, .{});
} }
fn getFrameOwner(cmd: *CDP.Command) !void { fn getFrameOwner(cmd: anytype) !void {
const params = (try cmd.params(struct { const params = (try cmd.params(struct {
frameId: []const u8, frameId: []const u8,
})) orelse return error.InvalidParams; })) orelse return error.InvalidParams;
@@ -513,7 +512,7 @@ fn getFrameOwner(cmd: *CDP.Command) !void {
return cmd.sendResult(.{ .nodeId = node.id, .backendNodeId = node.id }, .{}); return cmd.sendResult(.{ .nodeId = node.id, .backendNodeId = node.id }, .{});
} }
fn getOuterHTML(cmd: *CDP.Command) !void { fn getOuterHTML(cmd: anytype) !void {
const params = (try cmd.params(struct { const params = (try cmd.params(struct {
nodeId: ?Node.Id = null, nodeId: ?Node.Id = null,
backendNodeId: ?Node.Id = null, backendNodeId: ?Node.Id = null,
@@ -535,7 +534,7 @@ fn getOuterHTML(cmd: *CDP.Command) !void {
return cmd.sendResult(.{ .outerHTML = aw.written() }, .{}); return cmd.sendResult(.{ .outerHTML = aw.written() }, .{});
} }
fn requestNode(cmd: *CDP.Command) !void { fn requestNode(cmd: anytype) !void {
const params = (try cmd.params(struct { const params = (try cmd.params(struct {
objectId: []const u8, objectId: []const u8,
})) orelse return error.InvalidParams; })) orelse return error.InvalidParams;

View File

@@ -17,10 +17,9 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>. // along with this program. If not, see <https://www.gnu.org/licenses/>.
const std = @import("std"); const std = @import("std");
const CDP = @import("../CDP.zig");
const log = @import("../../log.zig"); const log = @import("../../log.zig");
pub fn processMessage(cmd: *CDP.Command) !void { pub fn processMessage(cmd: anytype) !void {
const action = std.meta.stringToEnum(enum { const action = std.meta.stringToEnum(enum {
setEmulatedMedia, setEmulatedMedia,
setFocusEmulationEnabled, setFocusEmulationEnabled,
@@ -39,7 +38,7 @@ pub fn processMessage(cmd: *CDP.Command) !void {
} }
// TODO: noop method // TODO: noop method
fn setEmulatedMedia(cmd: *CDP.Command) !void { fn setEmulatedMedia(cmd: anytype) !void {
// const input = (try const incoming.params(struct { // const input = (try const incoming.params(struct {
// media: ?[]const u8 = null, // media: ?[]const u8 = null,
// features: ?[]struct{ // features: ?[]struct{
@@ -52,7 +51,7 @@ fn setEmulatedMedia(cmd: *CDP.Command) !void {
} }
// TODO: noop method // TODO: noop method
fn setFocusEmulationEnabled(cmd: *CDP.Command) !void { fn setFocusEmulationEnabled(cmd: anytype) !void {
// const input = (try const incoming.params(struct { // const input = (try const incoming.params(struct {
// enabled: bool, // enabled: bool,
// })) orelse return error.InvalidParams; // })) orelse return error.InvalidParams;
@@ -60,16 +59,16 @@ fn setFocusEmulationEnabled(cmd: *CDP.Command) !void {
} }
// TODO: noop method // TODO: noop method
fn setDeviceMetricsOverride(cmd: *CDP.Command) !void { fn setDeviceMetricsOverride(cmd: anytype) !void {
return cmd.sendResult(null, .{}); return cmd.sendResult(null, .{});
} }
// TODO: noop method // TODO: noop method
fn setTouchEmulationEnabled(cmd: *CDP.Command) !void { fn setTouchEmulationEnabled(cmd: anytype) !void {
return cmd.sendResult(null, .{}); return cmd.sendResult(null, .{});
} }
fn setUserAgentOverride(cmd: *CDP.Command) !void { fn setUserAgentOverride(cmd: anytype) !void {
log.info(.app, "setUserAgentOverride ignored", .{}); log.info(.app, "setUserAgentOverride ignored", .{});
return cmd.sendResult(null, .{}); return cmd.sendResult(null, .{});
} }

View File

@@ -17,19 +17,17 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>. // along with this program. If not, see <https://www.gnu.org/licenses/>.
const std = @import("std"); const std = @import("std");
const Allocator = std.mem.Allocator;
const id = @import("../id.zig"); const id = @import("../id.zig");
const CDP = @import("../CDP.zig");
const log = @import("../../log.zig"); const log = @import("../../log.zig");
const network = @import("network.zig");
const HttpClient = @import("../../browser/HttpClient.zig"); const HttpClient = @import("../../browser/HttpClient.zig");
const net_http = @import("../../network/http.zig"); const net_http = @import("../../network/http.zig");
const Notification = @import("../../Notification.zig"); const Notification = @import("../../Notification.zig");
const network = @import("network.zig"); pub fn processMessage(cmd: anytype) !void {
const Allocator = std.mem.Allocator;
pub fn processMessage(cmd: *CDP.Command) !void {
const action = std.meta.stringToEnum(enum { const action = std.meta.stringToEnum(enum {
disable, disable,
enable, enable,
@@ -137,13 +135,13 @@ const ErrorReason = enum {
BlockedByResponse, BlockedByResponse,
}; };
fn disable(cmd: *CDP.Command) !void { fn disable(cmd: anytype) !void {
const bc = cmd.browser_context orelse return error.BrowserContextNotLoaded; const bc = cmd.browser_context orelse return error.BrowserContextNotLoaded;
bc.fetchDisable(); bc.fetchDisable();
return cmd.sendResult(null, .{}); return cmd.sendResult(null, .{});
} }
fn enable(cmd: *CDP.Command) !void { fn enable(cmd: anytype) !void {
const params = (try cmd.params(EnableParam)) orelse EnableParam{}; const params = (try cmd.params(EnableParam)) orelse EnableParam{};
if (!arePatternsSupported(params.patterns)) { if (!arePatternsSupported(params.patterns)) {
log.warn(.not_implemented, "Fetch.enable", .{ .params = "pattern" }); log.warn(.not_implemented, "Fetch.enable", .{ .params = "pattern" });
@@ -182,7 +180,7 @@ fn arePatternsSupported(patterns: []RequestPattern) bool {
return true; return true;
} }
pub fn requestIntercept(bc: *CDP.BrowserContext, intercept: *const Notification.RequestIntercept) !void { pub fn requestIntercept(bc: anytype, intercept: *const Notification.RequestIntercept) !void {
// detachTarget could be called, in which case, we still have a page doing // detachTarget could be called, in which case, we still have a page doing
// things, but no session. // things, but no session.
const session_id = bc.session_id orelse return; const session_id = bc.session_id orelse return;
@@ -217,7 +215,7 @@ pub fn requestIntercept(bc: *CDP.BrowserContext, intercept: *const Notification.
intercept.wait_for_interception.* = true; intercept.wait_for_interception.* = true;
} }
fn continueRequest(cmd: *CDP.Command) !void { fn continueRequest(cmd: anytype) !void {
const bc = cmd.browser_context orelse return error.BrowserContextNotLoaded; const bc = cmd.browser_context orelse return error.BrowserContextNotLoaded;
const params = (try cmd.params(struct { const params = (try cmd.params(struct {
requestId: []const u8, // INT-{d}" requestId: []const u8, // INT-{d}"
@@ -277,7 +275,7 @@ const AuthChallengeResponse = enum {
ProvideCredentials, ProvideCredentials,
}; };
fn continueWithAuth(cmd: *CDP.Command) !void { fn continueWithAuth(cmd: anytype) !void {
const bc = cmd.browser_context orelse return error.BrowserContextNotLoaded; const bc = cmd.browser_context orelse return error.BrowserContextNotLoaded;
const params = (try cmd.params(struct { const params = (try cmd.params(struct {
requestId: []const u8, // "INT-{d}" requestId: []const u8, // "INT-{d}"
@@ -320,7 +318,7 @@ fn continueWithAuth(cmd: *CDP.Command) !void {
return cmd.sendResult(null, .{}); return cmd.sendResult(null, .{});
} }
fn fulfillRequest(cmd: *CDP.Command) !void { fn fulfillRequest(cmd: anytype) !void {
const bc = cmd.browser_context orelse return error.BrowserContextNotLoaded; const bc = cmd.browser_context orelse return error.BrowserContextNotLoaded;
const params = (try cmd.params(struct { const params = (try cmd.params(struct {
@@ -362,7 +360,7 @@ fn fulfillRequest(cmd: *CDP.Command) !void {
return cmd.sendResult(null, .{}); return cmd.sendResult(null, .{});
} }
fn failRequest(cmd: *CDP.Command) !void { fn failRequest(cmd: anytype) !void {
const bc = cmd.browser_context orelse return error.BrowserContextNotLoaded; const bc = cmd.browser_context orelse return error.BrowserContextNotLoaded;
const params = (try cmd.params(struct { const params = (try cmd.params(struct {
requestId: []const u8, // "INT-{d}" requestId: []const u8, // "INT-{d}"
@@ -384,7 +382,7 @@ fn failRequest(cmd: *CDP.Command) !void {
return cmd.sendResult(null, .{}); return cmd.sendResult(null, .{});
} }
pub fn requestAuthRequired(bc: *CDP.BrowserContext, intercept: *const Notification.RequestAuthRequired) !void { pub fn requestAuthRequired(bc: anytype, intercept: *const Notification.RequestAuthRequired) !void {
// detachTarget could be called, in which case, we still have a page doing // detachTarget could be called, in which case, we still have a page doing
// things, but no session. // things, but no session.
const session_id = bc.session_id orelse return; const session_id = bc.session_id orelse return;

View File

@@ -17,9 +17,8 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>. // along with this program. If not, see <https://www.gnu.org/licenses/>.
const std = @import("std"); const std = @import("std");
const CDP = @import("../CDP.zig");
pub fn processMessage(cmd: *CDP.Command) !void { pub fn processMessage(cmd: anytype) !void {
const action = std.meta.stringToEnum(enum { const action = std.meta.stringToEnum(enum {
dispatchKeyEvent, dispatchKeyEvent,
dispatchMouseEvent, dispatchMouseEvent,
@@ -34,7 +33,7 @@ pub fn processMessage(cmd: *CDP.Command) !void {
} }
// https://chromedevtools.github.io/devtools-protocol/tot/Input/#method-dispatchKeyEvent // https://chromedevtools.github.io/devtools-protocol/tot/Input/#method-dispatchKeyEvent
fn dispatchKeyEvent(cmd: *CDP.Command) !void { fn dispatchKeyEvent(cmd: anytype) !void {
const params = (try cmd.params(struct { const params = (try cmd.params(struct {
type: Type, type: Type,
key: []const u8 = "", key: []const u8 = "",
@@ -75,7 +74,7 @@ fn dispatchKeyEvent(cmd: *CDP.Command) !void {
} }
// https://chromedevtools.github.io/devtools-protocol/tot/Input/#method-dispatchMouseEvent // https://chromedevtools.github.io/devtools-protocol/tot/Input/#method-dispatchMouseEvent
fn dispatchMouseEvent(cmd: *CDP.Command) !void { fn dispatchMouseEvent(cmd: anytype) !void {
const params = (try cmd.params(struct { const params = (try cmd.params(struct {
x: f64, x: f64,
y: f64, y: f64,
@@ -105,7 +104,7 @@ fn dispatchMouseEvent(cmd: *CDP.Command) !void {
} }
// https://chromedevtools.github.io/devtools-protocol/tot/Input/#method-insertText // https://chromedevtools.github.io/devtools-protocol/tot/Input/#method-insertText
fn insertText(cmd: *CDP.Command) !void { fn insertText(cmd: anytype) !void {
const params = (try cmd.params(struct { const params = (try cmd.params(struct {
text: []const u8, // The text to insert text: []const u8, // The text to insert
})) orelse return error.InvalidParams; })) orelse return error.InvalidParams;

View File

@@ -17,9 +17,8 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>. // along with this program. If not, see <https://www.gnu.org/licenses/>.
const std = @import("std"); const std = @import("std");
const CDP = @import("../CDP.zig");
pub fn processMessage(cmd: *CDP.Command) !void { pub fn processMessage(cmd: anytype) !void {
const action = std.meta.stringToEnum(enum { const action = std.meta.stringToEnum(enum {
enable, enable,
disable, disable,

View File

@@ -17,9 +17,8 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>. // along with this program. If not, see <https://www.gnu.org/licenses/>.
const std = @import("std"); const std = @import("std");
const CDP = @import("../CDP.zig");
pub fn processMessage(cmd: *CDP.Command) !void { pub fn processMessage(cmd: anytype) !void {
const action = std.meta.stringToEnum(enum { const action = std.meta.stringToEnum(enum {
enable, enable,
disable, disable,

View File

@@ -18,18 +18,14 @@
const std = @import("std"); const std = @import("std");
const lp = @import("lightpanda"); const lp = @import("lightpanda");
const CDP = @import("../CDP.zig");
const Node = @import("../Node.zig");
const DOMNode = @import("../../browser/webapi/Node.zig");
const markdown = lp.markdown; const markdown = lp.markdown;
const SemanticTree = lp.SemanticTree; const SemanticTree = lp.SemanticTree;
const interactive = lp.interactive; const interactive = lp.interactive;
const structured_data = lp.structured_data; const structured_data = lp.structured_data;
const Node = @import("../Node.zig");
const DOMNode = @import("../../browser/webapi/Node.zig");
pub fn processMessage(cmd: *CDP.Command) !void { pub fn processMessage(cmd: anytype) !void {
const action = std.meta.stringToEnum(enum { const action = std.meta.stringToEnum(enum {
getMarkdown, getMarkdown,
getSemanticTree, getSemanticTree,

View File

@@ -18,21 +18,18 @@
const std = @import("std"); const std = @import("std");
const lp = @import("lightpanda"); const lp = @import("lightpanda");
const Allocator = std.mem.Allocator;
const log = @import("../../log.zig"); const log = @import("../../log.zig");
const id = @import("../id.zig"); const CdpStorage = @import("storage.zig");
const CDP = @import("../CDP.zig");
const id = @import("../id.zig");
const URL = @import("../../browser/URL.zig"); const URL = @import("../../browser/URL.zig");
const Transfer = @import("../../browser/HttpClient.zig").Transfer; const Transfer = @import("../../browser/HttpClient.zig").Transfer;
const Notification = @import("../../Notification.zig"); const Notification = @import("../../Notification.zig");
const Mime = @import("../../browser/Mime.zig"); const Mime = @import("../../browser/Mime.zig");
const CdpStorage = @import("storage.zig"); pub fn processMessage(cmd: anytype) !void {
const Allocator = std.mem.Allocator;
pub fn processMessage(cmd: *CDP.Command) !void {
const action = std.meta.stringToEnum(enum { const action = std.meta.stringToEnum(enum {
enable, enable,
disable, disable,
@@ -62,19 +59,19 @@ pub fn processMessage(cmd: *CDP.Command) !void {
} }
} }
fn enable(cmd: *CDP.Command) !void { fn enable(cmd: anytype) !void {
const bc = cmd.browser_context orelse return error.BrowserContextNotLoaded; const bc = cmd.browser_context orelse return error.BrowserContextNotLoaded;
try bc.networkEnable(); try bc.networkEnable();
return cmd.sendResult(null, .{}); return cmd.sendResult(null, .{});
} }
fn disable(cmd: *CDP.Command) !void { fn disable(cmd: anytype) !void {
const bc = cmd.browser_context orelse return error.BrowserContextNotLoaded; const bc = cmd.browser_context orelse return error.BrowserContextNotLoaded;
bc.networkDisable(); bc.networkDisable();
return cmd.sendResult(null, .{}); return cmd.sendResult(null, .{});
} }
fn setExtraHTTPHeaders(cmd: *CDP.Command) !void { fn setExtraHTTPHeaders(cmd: anytype) !void {
const params = (try cmd.params(struct { const params = (try cmd.params(struct {
headers: std.json.ArrayHashMap([]const u8), headers: std.json.ArrayHashMap([]const u8),
})) orelse return error.InvalidParams; })) orelse return error.InvalidParams;
@@ -113,7 +110,7 @@ fn cookieMatches(cookie: *const Cookie, name: []const u8, domain: ?[]const u8, p
return true; return true;
} }
fn deleteCookies(cmd: *CDP.Command) !void { fn deleteCookies(cmd: anytype) !void {
const params = (try cmd.params(struct { const params = (try cmd.params(struct {
name: []const u8, name: []const u8,
url: ?[:0]const u8 = null, url: ?[:0]const u8 = null,
@@ -147,14 +144,14 @@ fn deleteCookies(cmd: *CDP.Command) !void {
return cmd.sendResult(null, .{}); return cmd.sendResult(null, .{});
} }
fn clearBrowserCookies(cmd: *CDP.Command) !void { fn clearBrowserCookies(cmd: anytype) !void {
if (try cmd.params(struct {}) != null) return error.InvalidParams; if (try cmd.params(struct {}) != null) return error.InvalidParams;
const bc = cmd.browser_context orelse return error.BrowserContextNotLoaded; const bc = cmd.browser_context orelse return error.BrowserContextNotLoaded;
bc.session.cookie_jar.clearRetainingCapacity(); bc.session.cookie_jar.clearRetainingCapacity();
return cmd.sendResult(null, .{}); return cmd.sendResult(null, .{});
} }
fn setCookie(cmd: *CDP.Command) !void { fn setCookie(cmd: anytype) !void {
const params = (try cmd.params( const params = (try cmd.params(
CdpStorage.CdpCookie, CdpStorage.CdpCookie,
)) orelse return error.InvalidParams; )) orelse return error.InvalidParams;
@@ -165,7 +162,7 @@ fn setCookie(cmd: *CDP.Command) !void {
try cmd.sendResult(.{ .success = true }, .{}); try cmd.sendResult(.{ .success = true }, .{});
} }
fn setCookies(cmd: *CDP.Command) !void { fn setCookies(cmd: anytype) !void {
const params = (try cmd.params(struct { const params = (try cmd.params(struct {
cookies: []const CdpStorage.CdpCookie, cookies: []const CdpStorage.CdpCookie,
})) orelse return error.InvalidParams; })) orelse return error.InvalidParams;
@@ -181,7 +178,7 @@ fn setCookies(cmd: *CDP.Command) !void {
const GetCookiesParam = struct { const GetCookiesParam = struct {
urls: ?[]const [:0]const u8 = null, urls: ?[]const [:0]const u8 = null,
}; };
fn getCookies(cmd: *CDP.Command) !void { fn getCookies(cmd: anytype) !void {
const bc = cmd.browser_context orelse return error.BrowserContextNotLoaded; const bc = cmd.browser_context orelse return error.BrowserContextNotLoaded;
const params = (try cmd.params(GetCookiesParam)) orelse GetCookiesParam{}; const params = (try cmd.params(GetCookiesParam)) orelse GetCookiesParam{};
@@ -204,7 +201,7 @@ fn getCookies(cmd: *CDP.Command) !void {
try cmd.sendResult(.{ .cookies = writer }, .{}); try cmd.sendResult(.{ .cookies = writer }, .{});
} }
fn getResponseBody(cmd: *CDP.Command) !void { fn getResponseBody(cmd: anytype) !void {
const params = (try cmd.params(struct { const params = (try cmd.params(struct {
requestId: []const u8, // "REQ-{d}" requestId: []const u8, // "REQ-{d}"
})) orelse return error.InvalidParams; })) orelse return error.InvalidParams;
@@ -230,7 +227,7 @@ fn getResponseBody(cmd: *CDP.Command) !void {
}, .{}); }, .{});
} }
pub fn httpRequestFail(bc: *CDP.BrowserContext, msg: *const Notification.RequestFail) !void { pub fn httpRequestFail(bc: anytype, msg: *const Notification.RequestFail) !void {
// It's possible that the request failed because we aborted when the client // It's possible that the request failed because we aborted when the client
// sent Target.closeTarget. In that case, bc.session_id will be cleared // sent Target.closeTarget. In that case, bc.session_id will be cleared
// already, and we can skip sending these messages to the client. // already, and we can skip sending these messages to the client.
@@ -250,7 +247,7 @@ pub fn httpRequestFail(bc: *CDP.BrowserContext, msg: *const Notification.Request
}, .{ .session_id = session_id }); }, .{ .session_id = session_id });
} }
pub fn httpRequestStart(bc: *CDP.BrowserContext, msg: *const Notification.RequestStart) !void { pub fn httpRequestStart(bc: anytype, msg: *const Notification.RequestStart) !void {
// detachTarget could be called, in which case, we still have a page doing // detachTarget could be called, in which case, we still have a page doing
// things, but no session. // things, but no session.
const session_id = bc.session_id orelse return; const session_id = bc.session_id orelse return;
@@ -279,7 +276,7 @@ pub fn httpRequestStart(bc: *CDP.BrowserContext, msg: *const Notification.Reques
}, .{ .session_id = session_id }); }, .{ .session_id = session_id });
} }
pub fn httpResponseHeaderDone(arena: Allocator, bc: *CDP.BrowserContext, msg: *const Notification.ResponseHeaderDone) !void { pub fn httpResponseHeaderDone(arena: Allocator, bc: anytype, msg: *const Notification.ResponseHeaderDone) !void {
// detachTarget could be called, in which case, we still have a page doing // detachTarget could be called, in which case, we still have a page doing
// things, but no session. // things, but no session.
const session_id = bc.session_id orelse return; const session_id = bc.session_id orelse return;
@@ -296,7 +293,7 @@ pub fn httpResponseHeaderDone(arena: Allocator, bc: *CDP.BrowserContext, msg: *c
}, .{ .session_id = session_id }); }, .{ .session_id = session_id });
} }
pub fn httpRequestDone(bc: *CDP.BrowserContext, msg: *const Notification.RequestDone) !void { pub fn httpRequestDone(bc: anytype, msg: *const Notification.RequestDone) !void {
// detachTarget could be called, in which case, we still have a page doing // detachTarget could be called, in which case, we still have a page doing
// things, but no session. // things, but no session.
const session_id = bc.session_id orelse return; const session_id = bc.session_id orelse return;

View File

@@ -1,5 +1,4 @@
// Copyright (C) 2023-2025 Lightpanda (Selecy SAS) // Copyright (C) 2023-2025 Lightpanda (Selecy SAS)
// //
// Francis Bouvier <francis@lightpanda.io> // Francis Bouvier <francis@lightpanda.io>
// Pierre Tachoire <pierre@lightpanda.io> // Pierre Tachoire <pierre@lightpanda.io>
@@ -23,8 +22,6 @@ const lp = @import("lightpanda");
const screenshot_png = @embedFile("screenshot.png"); const screenshot_png = @embedFile("screenshot.png");
const id = @import("../id.zig"); const id = @import("../id.zig");
const CDP = @import("../CDP.zig");
const log = @import("../../log.zig"); const log = @import("../../log.zig");
const js = @import("../../browser/js/js.zig"); const js = @import("../../browser/js/js.zig");
const URL = @import("../../browser/URL.zig"); const URL = @import("../../browser/URL.zig");
@@ -34,7 +31,7 @@ const Notification = @import("../../Notification.zig");
const Allocator = std.mem.Allocator; const Allocator = std.mem.Allocator;
pub fn processMessage(cmd: *CDP.Command) !void { pub fn processMessage(cmd: anytype) !void {
const action = std.meta.stringToEnum(enum { const action = std.meta.stringToEnum(enum {
enable, enable,
getFrameTree, getFrameTree,
@@ -81,7 +78,7 @@ const Frame = struct {
gatedAPIFeatures: [][]const u8 = &[0][]const u8{}, gatedAPIFeatures: [][]const u8 = &[0][]const u8{},
}; };
fn getFrameTree(cmd: *CDP.Command) !void { fn getFrameTree(cmd: anytype) !void {
// Stagehand parses the response and error if we don't return a // Stagehand parses the response and error if we don't return a
// correct one for this call when browser context or target id are missing. // correct one for this call when browser context or target id are missing.
const startup = .{ const startup = .{
@@ -111,7 +108,7 @@ fn getFrameTree(cmd: *CDP.Command) !void {
}, .{}); }, .{});
} }
fn setLifecycleEventsEnabled(cmd: *CDP.Command) !void { fn setLifecycleEventsEnabled(cmd: anytype) !void {
const params = (try cmd.params(struct { const params = (try cmd.params(struct {
enabled: bool, enabled: bool,
})) orelse return error.InvalidParams; })) orelse return error.InvalidParams;
@@ -152,7 +149,7 @@ fn setLifecycleEventsEnabled(cmd: *CDP.Command) !void {
return cmd.sendResult(null, .{}); return cmd.sendResult(null, .{});
} }
fn addScriptToEvaluateOnNewDocument(cmd: *CDP.Command) !void { fn addScriptToEvaluateOnNewDocument(cmd: anytype) !void {
const params = (try cmd.params(struct { const params = (try cmd.params(struct {
source: []const u8, source: []const u8,
worldName: ?[]const u8 = null, worldName: ?[]const u8 = null,
@@ -182,7 +179,7 @@ fn addScriptToEvaluateOnNewDocument(cmd: *CDP.Command) !void {
}, .{}); }, .{});
} }
fn removeScriptToEvaluateOnNewDocument(cmd: *CDP.Command) !void { fn removeScriptToEvaluateOnNewDocument(cmd: anytype) !void {
const params = (try cmd.params(struct { const params = (try cmd.params(struct {
identifier: []const u8, identifier: []const u8,
})) orelse return error.InvalidParams; })) orelse return error.InvalidParams;
@@ -201,7 +198,7 @@ fn removeScriptToEvaluateOnNewDocument(cmd: *CDP.Command) !void {
return cmd.sendResult(null, .{}); return cmd.sendResult(null, .{});
} }
fn close(cmd: *CDP.Command) !void { fn close(cmd: anytype) !void {
const bc = cmd.browser_context orelse return error.BrowserContextNotLoaded; const bc = cmd.browser_context orelse return error.BrowserContextNotLoaded;
const target_id = bc.target_id orelse return error.TargetNotLoaded; const target_id = bc.target_id orelse return error.TargetNotLoaded;
@@ -238,7 +235,7 @@ fn close(cmd: *CDP.Command) !void {
bc.target_id = null; bc.target_id = null;
} }
fn createIsolatedWorld(cmd: *CDP.Command) !void { fn createIsolatedWorld(cmd: anytype) !void {
const params = (try cmd.params(struct { const params = (try cmd.params(struct {
frameId: []const u8, frameId: []const u8,
worldName: []const u8, worldName: []const u8,
@@ -258,7 +255,7 @@ fn createIsolatedWorld(cmd: *CDP.Command) !void {
return cmd.sendResult(.{ .executionContextId = js_context.id }, .{}); return cmd.sendResult(.{ .executionContextId = js_context.id }, .{});
} }
fn navigate(cmd: *CDP.Command) !void { fn navigate(cmd: anytype) !void {
const params = (try cmd.params(struct { const params = (try cmd.params(struct {
url: [:0]const u8, url: [:0]const u8,
// referrer: ?[]const u8 = null, // referrer: ?[]const u8 = null,
@@ -292,7 +289,7 @@ fn navigate(cmd: *CDP.Command) !void {
}); });
} }
fn doReload(cmd: *CDP.Command) !void { fn doReload(cmd: anytype) !void {
const params = try cmd.params(struct { const params = try cmd.params(struct {
ignoreCache: ?bool = null, ignoreCache: ?bool = null,
scriptToEvaluateOnLoad: ?[]const u8 = null, scriptToEvaluateOnLoad: ?[]const u8 = null,
@@ -322,7 +319,7 @@ fn doReload(cmd: *CDP.Command) !void {
}); });
} }
pub fn pageNavigate(bc: *CDP.BrowserContext, event: *const Notification.PageNavigate) !void { pub fn pageNavigate(bc: anytype, event: *const Notification.PageNavigate) !void {
// detachTarget could be called, in which case, we still have a page doing // detachTarget could be called, in which case, we still have a page doing
// things, but no session. // things, but no session.
const session_id = bc.session_id orelse return; const session_id = bc.session_id orelse return;
@@ -374,7 +371,7 @@ pub fn pageNavigate(bc: *CDP.BrowserContext, event: *const Notification.PageNavi
}, .{ .session_id = session_id }); }, .{ .session_id = session_id });
} }
pub fn pageRemove(bc: *CDP.BrowserContext) !void { pub fn pageRemove(bc: anytype) !void {
// Clear all remote object mappings to prevent stale objectIds from being used // Clear all remote object mappings to prevent stale objectIds from being used
// after the context is destroy // after the context is destroy
bc.inspector_session.inspector.resetContextGroup(); bc.inspector_session.inspector.resetContextGroup();
@@ -385,7 +382,7 @@ pub fn pageRemove(bc: *CDP.BrowserContext) !void {
} }
} }
pub fn pageCreated(bc: *CDP.BrowserContext, page: *Page) !void { pub fn pageCreated(bc: anytype, page: *Page) !void {
_ = bc.cdp.page_arena.reset(.{ .retain_with_limit = 1024 * 512 }); _ = bc.cdp.page_arena.reset(.{ .retain_with_limit = 1024 * 512 });
for (bc.isolated_worlds.items) |isolated_world| { for (bc.isolated_worlds.items) |isolated_world| {
@@ -397,7 +394,7 @@ pub fn pageCreated(bc: *CDP.BrowserContext, page: *Page) !void {
bc.captured_responses = .empty; bc.captured_responses = .empty;
} }
pub fn pageFrameCreated(bc: *CDP.BrowserContext, event: *const Notification.PageFrameCreated) !void { pub fn pageFrameCreated(bc: anytype, event: *const Notification.PageFrameCreated) !void {
const session_id = bc.session_id orelse return; const session_id = bc.session_id orelse return;
const cdp = bc.cdp; const cdp = bc.cdp;
@@ -418,7 +415,7 @@ pub fn pageFrameCreated(bc: *CDP.BrowserContext, event: *const Notification.Page
} }
} }
pub fn pageNavigated(arena: Allocator, bc: *CDP.BrowserContext, event: *const Notification.PageNavigated) !void { pub fn pageNavigated(arena: Allocator, bc: anytype, event: *const Notification.PageNavigated) !void {
// detachTarget could be called, in which case, we still have a page doing // detachTarget could be called, in which case, we still have a page doing
// things, but no session. // things, but no session.
const session_id = bc.session_id orelse return; const session_id = bc.session_id orelse return;
@@ -621,15 +618,15 @@ pub fn pageLoaded(bc: anytype, event: *const Notification.PageLoaded) !void {
}, .{ .session_id = session_id }); }, .{ .session_id = session_id });
} }
pub fn pageNetworkIdle(bc: *CDP.BrowserContext, event: *const Notification.PageNetworkIdle) !void { pub fn pageNetworkIdle(bc: anytype, event: *const Notification.PageNetworkIdle) !void {
return sendPageLifecycle(bc, "networkIdle", event.timestamp, &id.toFrameId(event.frame_id), &id.toLoaderId(event.req_id)); return sendPageLifecycle(bc, "networkIdle", event.timestamp, &id.toFrameId(event.frame_id), &id.toLoaderId(event.req_id));
} }
pub fn pageNetworkAlmostIdle(bc: *CDP.BrowserContext, event: *const Notification.PageNetworkAlmostIdle) !void { pub fn pageNetworkAlmostIdle(bc: anytype, event: *const Notification.PageNetworkAlmostIdle) !void {
return sendPageLifecycle(bc, "networkAlmostIdle", event.timestamp, &id.toFrameId(event.frame_id), &id.toLoaderId(event.req_id)); return sendPageLifecycle(bc, "networkAlmostIdle", event.timestamp, &id.toFrameId(event.frame_id), &id.toLoaderId(event.req_id));
} }
fn sendPageLifecycle(bc: *CDP.BrowserContext, name: []const u8, timestamp: u64, frame_id: []const u8, loader_id: []const u8) !void { fn sendPageLifecycle(bc: anytype, name: []const u8, timestamp: u64, frame_id: []const u8, loader_id: []const u8) !void {
// detachTarget could be called, in which case, we still have a page doing // detachTarget could be called, in which case, we still have a page doing
// things, but no session. // things, but no session.
const session_id = bc.session_id orelse return; const session_id = bc.session_id orelse return;
@@ -664,7 +661,7 @@ fn base64Encode(comptime input: []const u8) [std.base64.standard.Encoder.calcSiz
return buf; return buf;
} }
fn captureScreenshot(cmd: *CDP.Command) !void { fn captureScreenshot(cmd: anytype) !void {
const Params = struct { const Params = struct {
format: ?[]const u8 = "png", format: ?[]const u8 = "png",
quality: ?u8 = null, quality: ?u8 = null,
@@ -700,7 +697,7 @@ fn captureScreenshot(cmd: *CDP.Command) !void {
}, .{}); }, .{});
} }
fn getLayoutMetrics(cmd: *CDP.Command) !void { fn getLayoutMetrics(cmd: anytype) !void {
const width = 1920; const width = 1920;
const height = 1080; const height = 1080;

View File

@@ -17,9 +17,8 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>. // along with this program. If not, see <https://www.gnu.org/licenses/>.
const std = @import("std"); const std = @import("std");
const CDP = @import("../CDP.zig");
pub fn processMessage(cmd: *CDP.Command) !void { pub fn processMessage(cmd: anytype) !void {
const action = std.meta.stringToEnum(enum { const action = std.meta.stringToEnum(enum {
enable, enable,
disable, disable,

View File

@@ -19,9 +19,7 @@
const std = @import("std"); const std = @import("std");
const builtin = @import("builtin"); const builtin = @import("builtin");
const CDP = @import("../CDP.zig"); pub fn processMessage(cmd: anytype) !void {
pub fn processMessage(cmd: *CDP.Command) !void {
const action = std.meta.stringToEnum(enum { const action = std.meta.stringToEnum(enum {
enable, enable,
runIfWaitingForDebugger, runIfWaitingForDebugger,
@@ -38,7 +36,7 @@ pub fn processMessage(cmd: *CDP.Command) !void {
} }
} }
fn sendInspector(cmd: *CDP.Command, action: anytype) !void { fn sendInspector(cmd: anytype, action: anytype) !void {
// save script in file at debug mode // save script in file at debug mode
if (builtin.mode == .Debug) { if (builtin.mode == .Debug) {
try logInspector(cmd, action); try logInspector(cmd, action);
@@ -50,7 +48,7 @@ fn sendInspector(cmd: *CDP.Command, action: anytype) !void {
bc.callInspector(cmd.input.json); bc.callInspector(cmd.input.json);
} }
fn logInspector(cmd: *CDP.Command, action: anytype) !void { fn logInspector(cmd: anytype, action: anytype) !void {
const script = switch (action) { const script = switch (action) {
.evaluate => blk: { .evaluate => blk: {
const params = (try cmd.params(struct { const params = (try cmd.params(struct {

View File

@@ -17,9 +17,8 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>. // along with this program. If not, see <https://www.gnu.org/licenses/>.
const std = @import("std"); const std = @import("std");
const CDP = @import("../CDP.zig");
pub fn processMessage(cmd: *CDP.Command) !void { pub fn processMessage(cmd: anytype) !void {
const action = std.meta.stringToEnum(enum { const action = std.meta.stringToEnum(enum {
enable, enable,
disable, disable,
@@ -33,7 +32,7 @@ pub fn processMessage(cmd: *CDP.Command) !void {
} }
} }
fn setIgnoreCertificateErrors(cmd: *CDP.Command) !void { fn setIgnoreCertificateErrors(cmd: anytype) !void {
const params = (try cmd.params(struct { const params = (try cmd.params(struct {
ignore: bool, ignore: bool,
})) orelse return error.InvalidParams; })) orelse return error.InvalidParams;

View File

@@ -18,16 +18,13 @@
const std = @import("std"); const std = @import("std");
const CDP = @import("../CDP.zig");
const log = @import("../../log.zig"); const log = @import("../../log.zig");
const URL = @import("../../browser/URL.zig"); const URL = @import("../../browser/URL.zig");
const Cookie = @import("../../browser/webapi/storage/storage.zig").Cookie; const Cookie = @import("../../browser/webapi/storage/storage.zig").Cookie;
const CookieJar = Cookie.Jar; const CookieJar = Cookie.Jar;
pub const PreparedUri = Cookie.PreparedUri; pub const PreparedUri = Cookie.PreparedUri;
pub fn processMessage(cmd: *CDP.Command) !void { pub fn processMessage(cmd: anytype) !void {
const action = std.meta.stringToEnum(enum { const action = std.meta.stringToEnum(enum {
clearCookies, clearCookies,
setCookies, setCookies,
@@ -43,7 +40,7 @@ pub fn processMessage(cmd: *CDP.Command) !void {
const BrowserContextParam = struct { browserContextId: ?[]const u8 = null }; const BrowserContextParam = struct { browserContextId: ?[]const u8 = null };
fn clearCookies(cmd: *CDP.Command) !void { fn clearCookies(cmd: anytype) !void {
const bc = cmd.browser_context orelse return error.BrowserContextNotLoaded; const bc = cmd.browser_context orelse return error.BrowserContextNotLoaded;
const params = (try cmd.params(BrowserContextParam)) orelse BrowserContextParam{}; const params = (try cmd.params(BrowserContextParam)) orelse BrowserContextParam{};
@@ -58,7 +55,7 @@ fn clearCookies(cmd: *CDP.Command) !void {
return cmd.sendResult(null, .{}); return cmd.sendResult(null, .{});
} }
fn getCookies(cmd: *CDP.Command) !void { fn getCookies(cmd: anytype) !void {
const bc = cmd.browser_context orelse return error.BrowserContextNotLoaded; const bc = cmd.browser_context orelse return error.BrowserContextNotLoaded;
const params = (try cmd.params(BrowserContextParam)) orelse BrowserContextParam{}; const params = (try cmd.params(BrowserContextParam)) orelse BrowserContextParam{};
@@ -72,7 +69,7 @@ fn getCookies(cmd: *CDP.Command) !void {
try cmd.sendResult(.{ .cookies = writer }, .{}); try cmd.sendResult(.{ .cookies = writer }, .{});
} }
fn setCookies(cmd: *CDP.Command) !void { fn setCookies(cmd: anytype) !void {
const bc = cmd.browser_context orelse return error.BrowserContextNotLoaded; const bc = cmd.browser_context orelse return error.BrowserContextNotLoaded;
const params = (try cmd.params(struct { const params = (try cmd.params(struct {
cookies: []const CdpCookie, cookies: []const CdpCookie,

View File

@@ -20,13 +20,11 @@ const std = @import("std");
const lp = @import("lightpanda"); const lp = @import("lightpanda");
const id = @import("../id.zig"); const id = @import("../id.zig");
const CDP = @import("../CDP.zig");
const log = @import("../../log.zig"); const log = @import("../../log.zig");
const URL = @import("../../browser/URL.zig"); const URL = @import("../../browser/URL.zig");
const js = @import("../../browser/js/js.zig"); const js = @import("../../browser/js/js.zig");
pub fn processMessage(cmd: *CDP.Command) !void { pub fn processMessage(cmd: anytype) !void {
const action = std.meta.stringToEnum(enum { const action = std.meta.stringToEnum(enum {
getTargets, getTargets,
attachToTarget, attachToTarget,
@@ -62,7 +60,7 @@ pub fn processMessage(cmd: *CDP.Command) !void {
} }
} }
fn getTargets(cmd: *CDP.Command) !void { fn getTargets(cmd: anytype) !void {
// If no context available, return an empty array. // If no context available, return an empty array.
const bc = cmd.browser_context orelse { const bc = cmd.browser_context orelse {
return cmd.sendResult(.{ return cmd.sendResult(.{
@@ -88,7 +86,7 @@ fn getTargets(cmd: *CDP.Command) !void {
}, .{ .include_session_id = false }); }, .{ .include_session_id = false });
} }
fn getBrowserContexts(cmd: *CDP.Command) !void { fn getBrowserContexts(cmd: anytype) !void {
var browser_context_ids: []const []const u8 = undefined; var browser_context_ids: []const []const u8 = undefined;
if (cmd.browser_context) |bc| { if (cmd.browser_context) |bc| {
browser_context_ids = &.{bc.id}; browser_context_ids = &.{bc.id};
@@ -101,7 +99,7 @@ fn getBrowserContexts(cmd: *CDP.Command) !void {
}, .{ .include_session_id = false }); }, .{ .include_session_id = false });
} }
fn createBrowserContext(cmd: *CDP.Command) !void { fn createBrowserContext(cmd: anytype) !void {
const params = try cmd.params(struct { const params = try cmd.params(struct {
disposeOnDetach: bool = false, disposeOnDetach: bool = false,
proxyServer: ?[:0]const u8 = null, proxyServer: ?[:0]const u8 = null,
@@ -132,7 +130,7 @@ fn createBrowserContext(cmd: *CDP.Command) !void {
}, .{}); }, .{});
} }
fn disposeBrowserContext(cmd: *CDP.Command) !void { fn disposeBrowserContext(cmd: anytype) !void {
const params = (try cmd.params(struct { const params = (try cmd.params(struct {
browserContextId: []const u8, browserContextId: []const u8,
})) orelse return error.InvalidParams; })) orelse return error.InvalidParams;
@@ -143,7 +141,7 @@ fn disposeBrowserContext(cmd: *CDP.Command) !void {
try cmd.sendResult(null, .{}); try cmd.sendResult(null, .{});
} }
fn createTarget(cmd: *CDP.Command) !void { fn createTarget(cmd: anytype) !void {
const params = (try cmd.params(struct { const params = (try cmd.params(struct {
url: [:0]const u8 = "about:blank", url: [:0]const u8 = "about:blank",
// width: ?u64 = null, // width: ?u64 = null,
@@ -232,7 +230,7 @@ fn createTarget(cmd: *CDP.Command) !void {
}, .{}); }, .{});
} }
fn attachToTarget(cmd: *CDP.Command) !void { fn attachToTarget(cmd: anytype) !void {
const params = (try cmd.params(struct { const params = (try cmd.params(struct {
targetId: []const u8, targetId: []const u8,
flatten: bool = true, flatten: bool = true,
@@ -249,7 +247,7 @@ fn attachToTarget(cmd: *CDP.Command) !void {
return cmd.sendResult(.{ .sessionId = bc.session_id }, .{}); return cmd.sendResult(.{ .sessionId = bc.session_id }, .{});
} }
fn attachToBrowserTarget(cmd: *CDP.Command) !void { fn attachToBrowserTarget(cmd: anytype) !void {
const bc = cmd.browser_context orelse return error.BrowserContextNotLoaded; const bc = cmd.browser_context orelse return error.BrowserContextNotLoaded;
const session_id = bc.session_id orelse cmd.cdp.session_id_gen.next(); const session_id = bc.session_id orelse cmd.cdp.session_id_gen.next();
@@ -271,7 +269,7 @@ fn attachToBrowserTarget(cmd: *CDP.Command) !void {
return cmd.sendResult(.{ .sessionId = bc.session_id }, .{}); return cmd.sendResult(.{ .sessionId = bc.session_id }, .{});
} }
fn closeTarget(cmd: *CDP.Command) !void { fn closeTarget(cmd: anytype) !void {
const params = (try cmd.params(struct { const params = (try cmd.params(struct {
targetId: []const u8, targetId: []const u8,
})) orelse return error.InvalidParams; })) orelse return error.InvalidParams;
@@ -312,7 +310,7 @@ fn closeTarget(cmd: *CDP.Command) !void {
bc.target_id = null; bc.target_id = null;
} }
fn getTargetInfo(cmd: *CDP.Command) !void { fn getTargetInfo(cmd: anytype) !void {
const Params = struct { const Params = struct {
targetId: ?[]const u8 = null, targetId: ?[]const u8 = null,
}; };
@@ -349,7 +347,7 @@ fn getTargetInfo(cmd: *CDP.Command) !void {
}, .{ .include_session_id = false }); }, .{ .include_session_id = false });
} }
fn sendMessageToTarget(cmd: *CDP.Command) !void { fn sendMessageToTarget(cmd: anytype) !void {
const params = (try cmd.params(struct { const params = (try cmd.params(struct {
message: []const u8, message: []const u8,
sessionId: []const u8, sessionId: []const u8,
@@ -367,19 +365,32 @@ fn sendMessageToTarget(cmd: *CDP.Command) !void {
return error.UnknownSessionId; return error.UnknownSessionId;
} }
var aw = std.Io.Writer.Allocating.init(cmd.arena); const Capture = struct {
cmd.cdp.dispatch(cmd.arena, .{ .capture = &aw.writer }, params.message) catch |err| { aw: std.Io.Writer.Allocating,
pub fn sendJSON(self: *@This(), message: anytype) !void {
return std.json.Stringify.value(message, .{
.emit_null_optional_fields = false,
}, &self.aw.writer);
}
};
var capture = Capture{
.aw = .init(cmd.arena),
};
cmd.cdp.dispatch(cmd.arena, &capture, params.message) catch |err| {
log.err(.cdp, "internal dispatch error", .{ .err = err, .id = cmd.input.id, .message = params.message }); log.err(.cdp, "internal dispatch error", .{ .err = err, .id = cmd.input.id, .message = params.message });
return err; return err;
}; };
try cmd.sendEvent("Target.receivedMessageFromTarget", .{ try cmd.sendEvent("Target.receivedMessageFromTarget", .{
.message = aw.written(), .message = capture.aw.written(),
.sessionId = params.sessionId, .sessionId = params.sessionId,
}, .{}); }, .{});
} }
fn detachFromTarget(cmd: *CDP.Command) !void { fn detachFromTarget(cmd: anytype) !void {
if (cmd.browser_context) |bc| { if (cmd.browser_context) |bc| {
if (bc.session_id) |session_id| { if (bc.session_id) |session_id| {
try cmd.sendEvent("Target.detachedFromTarget", .{ try cmd.sendEvent("Target.detachedFromTarget", .{
@@ -393,11 +404,11 @@ fn detachFromTarget(cmd: *CDP.Command) !void {
} }
// TODO: noop method // TODO: noop method
fn setDiscoverTargets(cmd: *CDP.Command) !void { fn setDiscoverTargets(cmd: anytype) !void {
return cmd.sendResult(null, .{}); return cmd.sendResult(null, .{});
} }
fn setAutoAttach(cmd: *CDP.Command) !void { fn setAutoAttach(cmd: anytype) !void {
const params = (try cmd.params(struct { const params = (try cmd.params(struct {
autoAttach: bool, autoAttach: bool,
waitForDebuggerOnStart: bool, waitForDebuggerOnStart: bool,
@@ -457,7 +468,7 @@ fn setAutoAttach(cmd: *CDP.Command) !void {
try cmd.sendResult(null, .{}); try cmd.sendResult(null, .{});
} }
fn doAttachtoTarget(cmd: *CDP.Command, target_id: []const u8) !void { fn doAttachtoTarget(cmd: anytype, target_id: []const u8) !void {
const bc = cmd.browser_context.?; const bc = cmd.browser_context.?;
const session_id = bc.session_id orelse cmd.cdp.session_id_gen.next(); const session_id = bc.session_id orelse cmd.cdp.session_id_gen.next();

View File

@@ -64,7 +64,7 @@ const TestContext = struct {
session_id: ?[]const u8 = null, session_id: ?[]const u8 = null,
url: ?[:0]const u8 = null, url: ?[:0]const u8 = null,
}; };
pub fn loadBrowserContext(self: *TestContext, opts: BrowserContextOpts) !*CDP.BrowserContext { pub fn loadBrowserContext(self: *TestContext, opts: BrowserContextOpts) !*CDP.BrowserContext(CDP) {
var c = self.cdp(); var c = self.cdp();
if (c.browser_context) |bc| { if (c.browser_context) |bc| {
_ = c.disposeBrowserContext(bc.id); _ = c.disposeBrowserContext(bc.id);

View File

@@ -237,6 +237,10 @@ pub fn RC(comptime T: type) type {
session.releaseArena(kv.value.arena); session.releaseArena(kv.value.arena);
} }
} }
pub fn format(self: @This(), writer: *std.Io.Writer) !void {
return writer.print("{d}", .{self._refs});
}
}; };
} }