re-enable minimum viable CDP server

This commit is contained in:
Karl Seguin
2025-10-28 18:56:03 +08:00
parent cdd31353c5
commit d3973172e8
25 changed files with 1512 additions and 1399 deletions

View File

@@ -122,6 +122,135 @@ pub fn isCompleteHTTPUrl(url: []const u8) bool {
std.ascii.startsWithIgnoreCase(url, "ftp://"); std.ascii.startsWithIgnoreCase(url, "ftp://");
} }
pub fn getUsername(raw: [:0]const u8) []const u8 {
const user_info = getUserInfo(raw) orelse return "";
const pos = std.mem.indexOfScalarPos(u8, user_info, 0, ':') orelse return user_info;
return user_info[0..pos];
}
pub fn getPassword(raw: [:0]const u8) []const u8 {
const user_info = getUserInfo(raw) orelse return "";
const pos = std.mem.indexOfScalarPos(u8, user_info, 0, ':') orelse return "";
return user_info[pos + 1 ..];
}
pub fn getPathname(raw: [:0]const u8) []const u8 {
const protocol_end = std.mem.indexOf(u8, raw, "://") orelse 0;
const path_start = std.mem.indexOfScalarPos(u8, raw, if (protocol_end > 0) protocol_end + 3 else 0, '/') orelse raw.len;
const query_or_hash_start = std.mem.indexOfAnyPos(u8, raw, path_start, "?#") orelse raw.len;
if (path_start >= query_or_hash_start) {
if (std.mem.indexOf(u8, raw, "://") != null) return "/";
return "";
}
return raw[path_start..query_or_hash_start];
}
pub fn getProtocol(raw: [:0]const u8) []const u8 {
const pos = std.mem.indexOfScalarPos(u8, raw, 0, ':') orelse return "";
return raw[0 .. pos + 1];
}
pub fn getHostname(raw: [:0]const u8) []const u8 {
const host = getHost(raw);
const pos = std.mem.lastIndexOfScalar(u8, host, ':') orelse return host;
return host[0..pos];
}
pub fn getPort(raw: [:0]const u8) []const u8 {
const host = getHost(raw);
const pos = std.mem.lastIndexOfScalar(u8, host, ':') orelse return "";
if (pos + 1 >= host.len) {
return "";
}
for (host[pos + 1 ..]) |c| {
if (c < '0' or c > '9') {
return "";
}
}
return host[pos + 1 ..];
}
pub fn getSearch(raw: [:0]const u8) []const u8 {
const pos = std.mem.indexOfScalarPos(u8, raw, 0, '?') orelse return "";
const query_part = raw[pos..];
if (std.mem.indexOfScalarPos(u8, query_part, 0, '#')) |fragment_start| {
return query_part[0..fragment_start];
}
return query_part;
}
pub fn getHash(raw: [:0]const u8) []const u8 {
const start = std.mem.indexOfScalarPos(u8, raw, 0, '#') orelse return "";
return raw[start..];
}
pub fn getOrigin(allocator: Allocator, raw: [:0]const u8) !?[]const u8 {
const port = getPort(raw);
const protocol = getProtocol(raw);
const hostname = getHostname(raw);
const p = std.meta.stringToEnum(KnownProtocol, getProtocol(raw)) orelse return null;
const include_port = blk: {
if (port.len == 0) {
break :blk false;
}
if (p == .@"https:" and std.mem.eql(u8, port, "443")) {
break :blk false;
}
if (p == .@"http:" and std.mem.eql(u8, port, "80")) {
break :blk false;
}
break :blk true;
};
if (include_port) {
return try std.fmt.allocPrint(allocator, "{s}//{s}:{s}", .{ protocol, hostname, port });
}
return try std.fmt.allocPrint(allocator, "{s}//{s}", .{ protocol, hostname });
}
fn getUserInfo(raw: [:0]const u8) ?[]const u8 {
const scheme_end = std.mem.indexOf(u8, raw, "://") orelse return null;
const authority_start = scheme_end + 3;
const pos = std.mem.indexOfScalar(u8, raw[authority_start..], '@') orelse return null;
const path_start = std.mem.indexOfScalarPos(u8, raw, authority_start, '/') orelse raw.len;
const full_pos = authority_start + pos;
if (full_pos < path_start) {
return raw[authority_start..full_pos];
}
return null;
}
fn getHost(raw: [:0]const u8) []const u8 {
const scheme_end = std.mem.indexOf(u8, raw, "://") orelse return "";
var authority_start = scheme_end + 3;
if (std.mem.indexOf(u8, raw[authority_start..], "@")) |pos| {
authority_start += pos + 1;
}
const authority = raw[authority_start..];
const path_start = std.mem.indexOfAny(u8, authority, "/?#") orelse return authority;
return authority[0..path_start];
}
const KnownProtocol = enum {
@"http:",
@"https:",
};
const testing = @import("../testing.zig"); const testing = @import("../testing.zig");
test "URL: isCompleteHTTPUrl" { test "URL: isCompleteHTTPUrl" {
try testing.expectEqual(true, isCompleteHTTPUrl("http://example.com/about")); try testing.expectEqual(true, isCompleteHTTPUrl("http://example.com/about"));

View File

@@ -467,4 +467,5 @@ pub const JsApis = flattenTypes(&.{
@import("../webapi/storage/storage.zig"), @import("../webapi/storage/storage.zig"),
@import("../webapi/URL.zig"), @import("../webapi/URL.zig"),
@import("../webapi/Window.zig"), @import("../webapi/Window.zig"),
@import("../webapi/MutationObserver.zig"),
}); });

View File

@@ -141,7 +141,7 @@ pub fn wait(self: *Session, wait_ms: u32) WaitResult {
return .done; return .done;
}; };
if (self.page) |*page| { if (self.page) |page| {
return page.wait(wait_ms); return page.wait(wait_ms);
} }
return .no_page; return .no_page;

View File

@@ -0,0 +1,23 @@
const js = @import("../js/js.zig");
// @ZIGDOM (haha, bet you wish you hadn't opened this file)
// puppeteer's startup script creates a MutationObserver, even if it doesn't use
// it in simple scripts. This not-even-a-skeleton is required for puppeteer/cdp.js
// to run
const MutationObserver = @This();
pub fn init() MutationObserver {
return .{};
}
pub const JsApi = struct {
pub const bridge = js.Bridge(MutationObserver);
pub const Meta = struct {
pub const name = "MutationObserver";
pub const prototype_chain = bridge.prototypeChain();
pub var class_index: u16 = 0;
};
pub const constructor = bridge.constructor(MutationObserver.init, .{});
};

View File

@@ -340,6 +340,9 @@ pub fn setNodeValue(self: *const Node, value: ?[]const u8, page: *Page) !void {
} }
pub fn format(self: *Node, writer: *std.Io.Writer) !void { pub fn format(self: *Node, writer: *std.Io.Writer) !void {
// // If you need extra debugging:
// return @import("../dump.zig").deep(self, .{}, writer);
return switch (self._type) { return switch (self._type) {
.cdata => |cd| cd.format(writer), .cdata => |cd| cd.format(writer),
.element => |el| writer.print("{f}", .{el}), .element => |el| writer.print("{f}", .{el}),

View File

@@ -39,15 +39,23 @@ pub fn TreeWalker(comptime mode: Mode) type {
self._next = children.first(); self._next = children.first();
} else if (node._child_link.next) |n| { } else if (node._child_link.next) |n| {
self._next = Node.linkToNode(n); self._next = Node.linkToNode(n);
} else if (node._parent) |n| {
if (n == self._root) {
self._next = null;
} else { } else {
self._next = Node.linkToNodeOrNull(n._child_link.next); // No children, no next sibling - walk up until we find a next sibling or hit root
var current = node._parent;
while (current) |parent| {
if (parent == self._root) {
self._next = null;
break;
} }
if (parent._child_link.next) |next_sibling| {
self._next = Node.linkToNode(next_sibling);
break;
}
current = parent._parent;
} else { } else {
self._next = null; self._next = null;
} }
}
return node; return node;
} }

View File

@@ -1,6 +1,7 @@
const std = @import("std"); const std = @import("std");
const js = @import("../js/js.zig"); const js = @import("../js/js.zig");
const U = @import("../URL.zig");
const Page = @import("../Page.zig"); const Page = @import("../Page.zig");
const URLSearchParams = @import("net/URLSearchParams.zig"); const URLSearchParams = @import("net/URLSearchParams.zig");
@@ -42,106 +43,42 @@ pub fn init(url: [:0]const u8, base_: ?[:0]const u8, page: *Page) !*URL {
} }
pub fn getUsername(self: *const URL) []const u8 { pub fn getUsername(self: *const URL) []const u8 {
const user_info = self.getUserInfo() orelse return ""; return U.getUsername(self._raw);
const pos = std.mem.indexOfScalarPos(u8, user_info, 0, ':') orelse return user_info;
return user_info[0..pos];
} }
pub fn getPassword(self: *const URL) []const u8 { pub fn getPassword(self: *const URL) []const u8 {
const user_info = self.getUserInfo() orelse return ""; return U.getPassword(self._raw);
const pos = std.mem.indexOfScalarPos(u8, user_info, 0, ':') orelse return "";
return user_info[pos + 1 ..];
} }
pub fn getPathname(self: *const URL) []const u8 { pub fn getPathname(self: *const URL) []const u8 {
const raw = self._raw; return U.getPathname(self._raw);
const protocol_end = std.mem.indexOf(u8, raw, "://") orelse 0;
const path_start = std.mem.indexOfScalarPos(u8, raw, if (protocol_end > 0) protocol_end + 3 else 0, '/') orelse raw.len;
const query_or_hash_start = std.mem.indexOfAnyPos(u8, raw, path_start, "?#") orelse raw.len;
if (path_start >= query_or_hash_start) {
if (std.mem.indexOf(u8, raw, "://") != null) return "/";
return "";
}
return raw[path_start..query_or_hash_start];
} }
pub fn getProtocol(self: *const URL) []const u8 { pub fn getProtocol(self: *const URL) []const u8 {
const raw = self._raw; return U.getProtocol(self._raw);
const pos = std.mem.indexOfScalarPos(u8, raw, 0, ':') orelse return "";
return raw[0 .. pos + 1];
} }
pub fn getHostname(self: *const URL) []const u8 { pub fn getHostname(self: *const URL) []const u8 {
const host = self.getHost(); return U.getHostname(self._raw);
const pos = std.mem.lastIndexOfScalar(u8, host, ':') orelse return host;
return host[0..pos];
} }
pub fn getPort(self: *const URL) []const u8 { pub fn getPort(self: *const URL) []const u8 {
const host = self.getHost(); return U.getPort(self._raw);
const pos = std.mem.lastIndexOfScalar(u8, host, ':') orelse return "";
if (pos + 1 >= host.len) {
return "";
}
for (host[pos + 1 ..]) |c| {
if (c < '0' or c > '9') {
return "";
}
}
return host[pos + 1 ..];
} }
pub fn getOrigin(self: *const URL, page: *const Page) ![]const u8 { pub fn getOrigin(self: *const URL, page: *const Page) ![]const u8 {
const port = self.getPort(); return (try U.getOrigin(page.call_arena, self._raw)) orelse {
const protocol = self.getProtocol();
const hostname = self.getHostname();
const p = std.meta.stringToEnum(KnownProtocol, self.getProtocol()) orelse {
// yes, a null string, that's what the spec wants // yes, a null string, that's what the spec wants
return "null"; return "null";
}; };
const include_port = blk: {
if (port.len == 0) {
break :blk false;
}
if (p == .@"https:" and std.mem.eql(u8, port, "443")) {
break :blk false;
}
if (p == .@"http:" and std.mem.eql(u8, port, "80")) {
break :blk false;
}
break :blk true;
};
if (include_port) {
return std.fmt.allocPrint(page.call_arena, "{s}//{s}:{s}", .{ protocol, hostname, port });
}
return std.fmt.allocPrint(page.call_arena, "{s}//{s}", .{ protocol, hostname });
} }
pub fn getSearch(self: *const URL) []const u8 { pub fn getSearch(self: *const URL) []const u8 {
const raw = self._raw; return U.getSearch(self._raw);
const pos = std.mem.indexOfScalarPos(u8, raw, 0, '?') orelse return "";
const query_part = raw[pos..];
if (std.mem.indexOfScalarPos(u8, query_part, 0, '#')) |fragment_start| {
return query_part[0..fragment_start];
}
return query_part;
} }
pub fn getHash(self: *const URL) []const u8 { pub fn getHash(self: *const URL) []const u8 {
const raw = self._raw; return U.getHash(self._raw);
const start = std.mem.indexOfScalarPos(u8, raw, 0, '#') orelse return "";
return raw[start..];
} }
pub fn getSearchParams(self: *URL, page: *Page) !*URLSearchParams { pub fn getSearchParams(self: *URL, page: *Page) !*URLSearchParams {

View File

@@ -9,6 +9,7 @@ pub fn registerTypes() []const type {
} }
pub const Jar = @import("cookie.zig").Jar; pub const Jar = @import("cookie.zig").Jar;
pub const Cookie =@import("cookie.zig").Cookie;
pub const Shed = struct { pub const Shed = struct {
_origins: std.StringHashMapUnmanaged(*Bucket) = .empty, _origins: std.StringHashMapUnmanaged(*Bucket) = .empty,

File diff suppressed because it is too large Load Diff

View File

@@ -24,12 +24,12 @@ const log = @import("../log.zig");
const js = @import("../browser/js/js.zig"); const js = @import("../browser/js/js.zig");
const polyfill = @import("../browser/polyfill/polyfill.zig"); const polyfill = @import("../browser/polyfill/polyfill.zig");
const App = @import("../app.zig").App; const App = @import("../App.zig");
const Browser = @import("../browser/browser.zig").Browser; const Browser = @import("../browser/Browser.zig");
const Session = @import("../browser/session.zig").Session; const Session = @import("../browser/Session.zig");
const Page = @import("../browser/page.zig").Page; const Page = @import("../browser/Page.zig");
const Incrementing = @import("../id.zig").Incrementing; const Incrementing = @import("../id.zig").Incrementing;
const Notification = @import("../notification.zig").Notification; const Notification = @import("../Notification.zig");
const LogInterceptor = @import("domains/log.zig").LogInterceptor; const LogInterceptor = @import("domains/log.zig").LogInterceptor;
const InterceptState = @import("domains/fetch.zig").InterceptState; const InterceptState = @import("domains/fetch.zig").InterceptState;
@@ -37,7 +37,7 @@ pub const URL_BASE = "chrome://newtab/";
pub const LOADER_ID = "LOADERID24DD2FD56CF1EF33C965C79C"; pub const LOADER_ID = "LOADERID24DD2FD56CF1EF33C965C79C";
pub const CDP = CDPT(struct { pub const CDP = CDPT(struct {
const Client = *@import("../server.zig").Client; const Client = *@import("../Server.zig").Client;
}); });
const SessionIdGen = Incrementing(u32, "SID"); const SessionIdGen = Incrementing(u32, "SID");
@@ -117,7 +117,7 @@ pub fn CDPT(comptime TypeProvider: type) type {
// timeouts (or http events) which are ready to be processed. // timeouts (or http events) which are ready to be processed.
pub fn hasPage() bool {} pub fn hasPage() bool {}
pub fn pageWait(self: *Self, ms: i32) Session.WaitResult { pub fn pageWait(self: *Self, ms: u32) Session.WaitResult {
const session = &(self.browser.session orelse return .no_page); const session = &(self.browser.session orelse return .no_page);
return session.wait(ms); return session.wait(ms);
} }
@@ -203,7 +203,8 @@ pub fn CDPT(comptime TypeProvider: type) type {
}, },
5 => switch (@as(u40, @bitCast(domain[0..5].*))) { 5 => switch (@as(u40, @bitCast(domain[0..5].*))) {
asUint(u40, "Fetch") => return @import("domains/fetch.zig").processMessage(command), asUint(u40, "Fetch") => return @import("domains/fetch.zig").processMessage(command),
asUint(u40, "Input") => return @import("domains/input.zig").processMessage(command), // @ZIGDOM
// asUint(u40, "Input") => return @import("domains/input.zig").processMessage(command),
else => {}, else => {},
}, },
6 => switch (@as(u48, @bitCast(domain[0..6].*))) { 6 => switch (@as(u48, @bitCast(domain[0..6].*))) {
@@ -286,7 +287,8 @@ pub fn CDPT(comptime TypeProvider: type) type {
} }
pub fn BrowserContext(comptime CDP_T: type) type { pub fn BrowserContext(comptime CDP_T: type) type {
const Node = @import("Node.zig"); // @ZIGMOD
// const Node = @import("Node.zig");
return struct { return struct {
id: []const u8, id: []const u8,
@@ -326,8 +328,9 @@ pub fn BrowserContext(comptime CDP_T: type) type {
security_origin: []const u8, security_origin: []const u8,
page_life_cycle_events: bool, page_life_cycle_events: bool,
secure_context_type: []const u8, secure_context_type: []const u8,
node_registry: Node.Registry, // @ZIGDOM
node_search_list: Node.Search.List, // node_registry: Node.Registry,
// node_search_list: Node.Search.List,
inspector: js.Inspector, inspector: js.Inspector,
isolated_worlds: std.ArrayListUnmanaged(IsolatedWorld), isolated_worlds: std.ArrayListUnmanaged(IsolatedWorld),
@@ -360,8 +363,9 @@ pub fn BrowserContext(comptime CDP_T: type) type {
const inspector = try cdp.browser.env.newInspector(arena, self); const inspector = try cdp.browser.env.newInspector(arena, self);
var registry = Node.Registry.init(allocator); // @ZIGDOM
errdefer registry.deinit(); // var registry = Node.Registry.init(allocator);
// errdefer registry.deinit();
self.* = .{ self.* = .{
.id = id, .id = id,
@@ -374,8 +378,9 @@ pub fn BrowserContext(comptime CDP_T: type) type {
.secure_context_type = "Secure", // TODO = enum .secure_context_type = "Secure", // TODO = enum
.loader_id = LOADER_ID, .loader_id = LOADER_ID,
.page_life_cycle_events = false, // TODO; Target based value .page_life_cycle_events = false, // TODO; Target based value
.node_registry = registry, // @ZIGDOM
.node_search_list = undefined, // .node_registry = registry,
// .node_search_list = undefined,
.isolated_worlds = .empty, .isolated_worlds = .empty,
.inspector = inspector, .inspector = inspector,
.notification_arena = cdp.notification_arena.allocator(), .notification_arena = cdp.notification_arena.allocator(),
@@ -383,7 +388,8 @@ pub fn BrowserContext(comptime CDP_T: type) type {
.captured_responses = .empty, .captured_responses = .empty,
.log_interceptor = LogInterceptor(Self).init(allocator, self), .log_interceptor = LogInterceptor(Self).init(allocator, self),
}; };
self.node_search_list = Node.Search.List.init(allocator, &self.node_registry); // ZIGDOM
// self.node_search_list = Node.Search.List.init(allocator, &self.node_registry);
errdefer self.deinit(); errdefer self.deinit();
try cdp.browser.notification.register(.page_remove, self, onPageRemove); try cdp.browser.notification.register(.page_remove, self, onPageRemove);
@@ -418,8 +424,9 @@ pub fn BrowserContext(comptime CDP_T: type) type {
world.deinit(); world.deinit();
} }
self.isolated_worlds.clearRetainingCapacity(); self.isolated_worlds.clearRetainingCapacity();
self.node_registry.deinit(); // @ZIGDOM
self.node_search_list.deinit(); // self.node_registry.deinit();
// self.node_search_list.deinit();
self.cdp.browser.notification.unregisterAll(self); self.cdp.browser.notification.unregisterAll(self);
if (self.http_proxy_changed) { if (self.http_proxy_changed) {
@@ -433,8 +440,10 @@ pub fn BrowserContext(comptime CDP_T: type) type {
} }
pub fn reset(self: *Self) void { pub fn reset(self: *Self) void {
self.node_registry.reset(); // @ZIGDOM
self.node_search_list.reset(); _ = self;
// self.node_registry.reset();
// self.node_search_list.reset();
} }
pub fn createIsolatedWorld(self: *Self, world_name: []const u8, grant_universal_access: bool) !*IsolatedWorld { pub fn createIsolatedWorld(self: *Self, world_name: []const u8, grant_universal_access: bool) !*IsolatedWorld {
@@ -453,19 +462,20 @@ pub fn BrowserContext(comptime CDP_T: type) type {
return world; return world;
} }
pub fn nodeWriter(self: *Self, root: *const Node, opts: Node.Writer.Opts) Node.Writer { // @ZIGDOM
return .{ // pub fn nodeWriter(self: *Self, root: *const Node, opts: Node.Writer.Opts) Node.Writer {
.root = root, // return .{
.depth = opts.depth, // .root = root,
.exclude_root = opts.exclude_root, // .depth = opts.depth,
.registry = &self.node_registry, // .exclude_root = opts.exclude_root,
}; // .registry = &self.node_registry,
} // };
// }
pub fn getURL(self: *const Self) ?[]const u8 { pub fn getURL(self: *const Self) ?[:0]const u8 {
const page = self.session.currentPage() orelse return null; const page = self.session.currentPage() orelse return null;
const raw_url = page.url.raw; const url = page.url;
return if (raw_url.len == 0) null else raw_url; return if (url.len == 0) null else url;
} }
pub fn networkEnable(self: *Self) !void { pub fn networkEnable(self: *Self) !void {

File diff suppressed because it is too large Load Diff

View File

@@ -23,7 +23,7 @@ const log = @import("../../log.zig");
const network = @import("network.zig"); const network = @import("network.zig");
const Http = @import("../../http/Http.zig"); const Http = @import("../../http/Http.zig");
const Notification = @import("../../notification.zig").Notification; const Notification = @import("../../Notification.zig");
pub fn processMessage(cmd: anytype) !void { pub fn processMessage(cmd: anytype) !void {
const action = std.meta.stringToEnum(enum { const action = std.meta.stringToEnum(enum {

View File

@@ -17,7 +17,7 @@
// 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 Page = @import("../../browser/page.zig").Page; const Page = @import("../../browser/Page.zig");
pub fn processMessage(cmd: anytype) !void { pub fn processMessage(cmd: anytype) !void {
const action = std.meta.stringToEnum(enum { const action = std.meta.stringToEnum(enum {

View File

@@ -101,7 +101,7 @@ pub fn LogInterceptor(comptime BC: type) type {
.fatal => "error", .fatal => "error",
}, },
.text = self.allocating.written(), .text = self.allocating.written(),
.timestamp = @import("../../datetime.zig").milliTimestamp(), .timestamp = @import("../../datetime.zig").milliTimestamp(.monotonic),
}, },
}, .{ }, .{
.session_id = self.bc.session_id, .session_id = self.bc.session_id,

View File

@@ -21,7 +21,7 @@ const Allocator = std.mem.Allocator;
const CdpStorage = @import("storage.zig"); const CdpStorage = @import("storage.zig");
const Transfer = @import("../../http/Client.zig").Transfer; const Transfer = @import("../../http/Client.zig").Transfer;
const Notification = @import("../../notification.zig").Notification; const Notification = @import("../../Notification.zig");
pub fn processMessage(cmd: anytype) !void { pub fn processMessage(cmd: anytype) !void {
const action = std.meta.stringToEnum(enum { const action = std.meta.stringToEnum(enum {
@@ -87,7 +87,7 @@ fn setExtraHTTPHeaders(cmd: anytype) !void {
return cmd.sendResult(null, .{}); return cmd.sendResult(null, .{});
} }
const Cookie = @import("../../browser/storage/storage.zig").Cookie; const Cookie = @import("../../browser/webapi/storage/storage.zig").Cookie;
// Only matches the cookie on provided parameters // Only matches the cookie on provided parameters
fn cookieMatches(cookie: *const Cookie, name: []const u8, domain: ?[]const u8, path: ?[]const u8) bool { fn cookieMatches(cookie: *const Cookie, name: []const u8, domain: ?[]const u8, path: ?[]const u8) bool {
@@ -173,7 +173,7 @@ fn getCookies(cmd: anytype) !void {
const params = (try cmd.params(GetCookiesParam)) orelse GetCookiesParam{}; const params = (try cmd.params(GetCookiesParam)) orelse GetCookiesParam{};
// If not specified, use the URLs of the page and all of its subframes. TODO subframes // If not specified, use the URLs of the page and all of its subframes. TODO subframes
const page_url = if (bc.session.page) |*page| page.url.raw else null; // @speed: avoid repasing the URL const page_url = if (bc.session.page) |page| page.url else null;
const param_urls = params.urls orelse &[_][]const u8{page_url orelse return error.InvalidParams}; const param_urls = params.urls orelse &[_][]const u8{page_url orelse return error.InvalidParams};
var urls = try std.ArrayListUnmanaged(CdpStorage.PreparedUri).initCapacity(cmd.arena, param_urls.len); var urls = try std.ArrayListUnmanaged(CdpStorage.PreparedUri).initCapacity(cmd.arena, param_urls.len);
@@ -247,7 +247,7 @@ pub fn httpRequestStart(arena: Allocator, bc: anytype, msg: *const Notification.
.requestId = try std.fmt.allocPrint(arena, "REQ-{d}", .{transfer.id}), .requestId = try std.fmt.allocPrint(arena, "REQ-{d}", .{transfer.id}),
.frameId = target_id, .frameId = target_id,
.loaderId = bc.loader_id, .loaderId = bc.loader_id,
.documentUrl = DocumentUrlWriter.init(&page.url.uri), .documentUrl = page.url,
.request = TransferAsRequestWriter.init(transfer), .request = TransferAsRequestWriter.init(transfer),
.initiator = .{ .type = "other" }, .initiator = .{ .type = "other" },
}, .{ .session_id = session_id }); }, .{ .session_id = session_id });
@@ -416,34 +416,35 @@ const TransferAsResponseWriter = struct {
} }
}; };
const DocumentUrlWriter = struct { // @ZIGDOM - do we still need this? just send the full URL?
uri: *std.Uri, // const DocumentUrlWriter = struct {
// uri: *std.Uri,
fn init(uri: *std.Uri) DocumentUrlWriter { // fn init(uri: *std.Uri) DocumentUrlWriter {
return .{ // return .{
.uri = uri, // .uri = uri,
}; // };
} // }
pub fn jsonStringify(self: *const DocumentUrlWriter, jws: anytype) !void { // pub fn jsonStringify(self: *const DocumentUrlWriter, jws: anytype) !void {
self._jsonStringify(jws) catch return error.WriteFailed; // self._jsonStringify(jws) catch return error.WriteFailed;
} // }
fn _jsonStringify(self: *const DocumentUrlWriter, jws: anytype) !void { // fn _jsonStringify(self: *const DocumentUrlWriter, jws: anytype) !void {
const writer = jws.writer; // const writer = jws.writer;
try jws.beginWriteRaw(); // try jws.beginWriteRaw();
try writer.writeByte('\"'); // try writer.writeByte('\"');
try self.uri.writeToStream(writer, .{ // try self.uri.writeToStream(writer, .{
.scheme = true, // .scheme = true,
.authentication = true, // .authentication = true,
.authority = true, // .authority = true,
.path = true, // .path = true,
.query = true, // .query = true,
}); // });
try writer.writeByte('\"'); // try writer.writeByte('\"');
jws.endWriteRaw(); // jws.endWriteRaw();
} // }
}; // };
fn idFromRequestId(request_id: []const u8) !u64 { fn idFromRequestId(request_id: []const u8) !u64 {
if (!std.mem.startsWith(u8, request_id, "REQ-")) { if (!std.mem.startsWith(u8, request_id, "REQ-")) {

View File

@@ -17,8 +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 Page = @import("../../browser/page.zig").Page; const Page = @import("../../browser/Page.zig");
const Notification = @import("../../notification.zig").Notification; const Notification = @import("../../Notification.zig");
const Allocator = std.mem.Allocator; const Allocator = std.mem.Allocator;
@@ -134,7 +134,7 @@ fn createIsolatedWorld(cmd: anytype) !void {
fn navigate(cmd: anytype) !void { fn navigate(cmd: anytype) !void {
const params = (try cmd.params(struct { const params = (try cmd.params(struct {
url: []const u8, url: [:0]const u8,
// referrer: ?[]const u8 = null, // referrer: ?[]const u8 = null,
// transitionType: ?[]const u8 = null, // TODO: enum // transitionType: ?[]const u8 = null, // TODO: enum
// frameId: ?[]const u8 = null, // frameId: ?[]const u8 = null,
@@ -253,7 +253,8 @@ pub fn pageNavigate(arena: Allocator, bc: anytype, event: *const Notification.Pa
bc.inspector.contextCreated( bc.inspector.contextCreated(
page.js, page.js,
"", "",
try page.origin(arena), "", // @ZIGDOM
// try page.origin(arena),
aux_data, aux_data,
true, true,
); );
@@ -360,7 +361,7 @@ pub fn pageNetworkAlmostIdle(bc: anytype, event: *const Notification.PageNetwork
return sendPageLifecycle(bc, "networkAlmostIdle", event.timestamp); return sendPageLifecycle(bc, "networkAlmostIdle", event.timestamp);
} }
fn sendPageLifecycle(bc: anytype, name: []const u8, timestamp: u32) !void { fn sendPageLifecycle(bc: anytype, name: []const u8, timestamp: u64) !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;
@@ -379,7 +380,7 @@ const LifecycleEvent = struct {
frameId: []const u8, frameId: []const u8,
loaderId: ?[]const u8, loaderId: ?[]const u8,
name: []const u8, name: []const u8,
timestamp: u32, timestamp: u64,
}; };
const testing = @import("../testing.zig"); const testing = @import("../testing.zig");

View File

@@ -19,9 +19,9 @@
const std = @import("std"); const std = @import("std");
const log = @import("../../log.zig"); const log = @import("../../log.zig");
const Cookie = @import("../../browser/storage/storage.zig").Cookie; const Cookie = @import("../../browser/webapi/storage/storage.zig").Cookie;
const CookieJar = @import("../../browser/storage/storage.zig").CookieJar; const CookieJar = @import("../../browser/webapi/storage/storage.zig").Jar;
pub const PreparedUri = @import("../../browser/storage/cookie.zig").PreparedUri; pub const PreparedUri = @import("../../browser/webapi/storage/cookie.zig").PreparedUri;
pub fn processMessage(cmd: anytype) !void { pub fn processMessage(cmd: anytype) !void {
const action = std.meta.stringToEnum(enum { const action = std.meta.stringToEnum(enum {

View File

@@ -143,13 +143,14 @@ fn createTarget(cmd: anytype) !void {
bc.target_id = target_id; bc.target_id = target_id;
var page = try bc.session.createPage(); const page = try bc.session.createPage();
{ {
const aux_data = try std.fmt.allocPrint(cmd.arena, "{{\"isDefault\":true,\"type\":\"default\",\"frameId\":\"{s}\"}}", .{target_id}); const aux_data = try std.fmt.allocPrint(cmd.arena, "{{\"isDefault\":true,\"type\":\"default\",\"frameId\":\"{s}\"}}", .{target_id});
bc.inspector.contextCreated( bc.inspector.contextCreated(
page.js, page.js,
"", "",
try page.origin(cmd.arena), "", // @ZIGDOM
// try page.origin(arena),
aux_data, aux_data,
true, true,
); );

View File

@@ -24,7 +24,6 @@ const ArenaAllocator = std.heap.ArenaAllocator;
const Testing = @This(); const Testing = @This();
const main = @import("cdp.zig"); const main = @import("cdp.zig");
const parser = @import("../browser/netsurf.zig");
const base = @import("../testing.zig"); const base = @import("../testing.zig");
pub const allocator = base.allocator; pub const allocator = base.allocator;

View File

@@ -176,7 +176,7 @@ pub fn abort(self: *Client) void {
} }
} }
pub fn tick(self: *Client, timeout_ms: i32) !PerformStatus { pub fn tick(self: *Client, timeout_ms: u32) !PerformStatus {
while (true) { while (true) {
if (self.handles.hasAvailable() == false) { if (self.handles.hasAvailable() == false) {
break; break;
@@ -188,7 +188,7 @@ pub fn tick(self: *Client, timeout_ms: i32) !PerformStatus {
const handle = self.handles.getFreeHandle().?; const handle = self.handles.getFreeHandle().?;
try self.makeRequest(handle, transfer); try self.makeRequest(handle, transfer);
} }
return self.perform(timeout_ms); return self.perform(@intCast(timeout_ms));
} }
pub fn request(self: *Client, req: Request) !void { pub fn request(self: *Client, req: Request) !void {

View File

@@ -83,7 +83,7 @@ pub fn deinit(self: *Http) void {
self.arena.deinit(); self.arena.deinit();
} }
pub fn poll(self: *Http, timeout_ms: i32) Client.PerformStatus { pub fn poll(self: *Http, timeout_ms: u32) Client.PerformStatus {
return self.client.tick(timeout_ms) catch |err| { return self.client.tick(timeout_ms) catch |err| {
log.err(.app, "http poll", .{ .err = err }); log.err(.app, "http poll", .{ .err = err });
return .normal; return .normal;

View File

@@ -1,5 +1,7 @@
const std = @import("std"); const std = @import("std");
pub const App = @import("App.zig"); pub const App = @import("App.zig");
pub const Server = @import("Server.zig");
pub const log = @import("log.zig"); pub const log = @import("log.zig");
pub const dump = @import("browser/dump.zig"); pub const dump = @import("browser/dump.zig");
pub const build_config = @import("build_config"); pub const build_config = @import("build_config");

View File

@@ -99,27 +99,24 @@ fn run(allocator: Allocator, main_arena: Allocator) !void {
app.telemetry.record(.{ .run = {} }); app.telemetry.record(.{ .run = {} });
switch (args.mode) { switch (args.mode) {
.serve => { .serve => |opts| {
return; log.debug(.app, "startup", .{ .mode = "serve" });
// @ZIGDOM-CDP const address = std.net.Address.parseIp4(opts.host, opts.port) catch |err| {
// .serve => |opts| { log.fatal(.app, "invalid server address", .{ .err = err, .host = opts.host, .port = opts.port });
// log.debug(.app, "startup", .{ .mode = "serve" }); return args.printUsageAndExit(false);
// const address = std.net.Address.parseIp4(opts.host, opts.port) catch |err| { };
// log.fatal(.app, "invalid server address", .{ .err = err, .host = opts.host, .port = opts.port });
// return args.printUsageAndExit(false);
// };
// // _server is global to handle graceful shutdown. // _server is global to handle graceful shutdown.
// _server = try lp.Server.init(app, address); _server = try lp.Server.init(app, address);
// const server = &_server.?; const server = &_server.?;
// defer server.deinit(); defer server.deinit();
// // max timeout of 1 week. // max timeout of 1 week.
// const timeout = if (opts.timeout > 604_800) 604_800_000 else @as(i32, opts.timeout) * 1000; const timeout = if (opts.timeout > 604_800) 604_800_000 else @as(u32, opts.timeout) * 1000;
// server.run(address, timeout) catch |err| { server.run(address, timeout) catch |err| {
// log.fatal(.app, "server run error", .{ .err = err }); log.fatal(.app, "server run error", .{ .err = err });
// return err; return err;
// }; };
}, },
.fetch => |opts| { .fetch => |opts| {
const url = opts.url; const url = opts.url;

View File

@@ -26,7 +26,7 @@ const Allocator = std.mem.Allocator;
const ArenaAllocator = std.heap.ArenaAllocator; const ArenaAllocator = std.heap.ArenaAllocator;
const log = @import("log.zig"); const log = @import("log.zig");
const App = @import("app.zig").App; const App = @import("App.zig");
const CDP = @import("cdp/cdp.zig").CDP; const CDP = @import("cdp/cdp.zig").CDP;
const MAX_HTTP_REQUEST_SIZE = 4096; const MAX_HTTP_REQUEST_SIZE = 4096;
@@ -69,7 +69,7 @@ pub fn deinit(self: *Server) void {
self.allocator.free(self.json_version_response); self.allocator.free(self.json_version_response);
} }
pub fn run(self: *Server, address: net.Address, timeout_ms: i32) !void { pub fn run(self: *Server, address: net.Address, timeout_ms: u32) !void {
const flags = posix.SOCK.STREAM | posix.SOCK.CLOEXEC; const flags = posix.SOCK.STREAM | posix.SOCK.CLOEXEC;
const listener = try posix.socket(address.any.family, flags, posix.IPPROTO.TCP); const listener = try posix.socket(address.any.family, flags, posix.IPPROTO.TCP);
self.listener = listener; self.listener = listener;
@@ -112,7 +112,7 @@ pub fn run(self: *Server, address: net.Address, timeout_ms: i32) !void {
} }
} }
fn readLoop(self: *Server, socket: posix.socket_t, timeout_ms: i32) !void { fn readLoop(self: *Server, socket: posix.socket_t, timeout_ms: u32) !void {
// This shouldn't be necessary, but the Client is HUGE (> 512KB) because // This shouldn't be necessary, but the Client is HUGE (> 512KB) because
// it has a large read buffer. I don't know why, but v8 crashes if this // it has a large read buffer. I don't know why, but v8 crashes if this
// is on the stack (and I assume it's related to its size). // is on the stack (and I assume it's related to its size).
@@ -143,7 +143,7 @@ fn readLoop(self: *Server, socket: posix.socket_t, timeout_ms: i32) !void {
} }
var cdp = &client.mode.cdp; var cdp = &client.mode.cdp;
var last_message = timestamp(); var last_message = timestamp(.monotonic);
var ms_remaining = timeout_ms; var ms_remaining = timeout_ms;
while (true) { while (true) {
switch (cdp.pageWait(ms_remaining)) { switch (cdp.pageWait(ms_remaining)) {
@@ -151,7 +151,7 @@ fn readLoop(self: *Server, socket: posix.socket_t, timeout_ms: i32) !void {
if (try client.readSocket() == false) { if (try client.readSocket() == false) {
return; return;
} }
last_message = timestamp(); last_message = timestamp(.monotonic);
ms_remaining = timeout_ms; ms_remaining = timeout_ms;
}, },
.no_page => { .no_page => {
@@ -162,16 +162,16 @@ fn readLoop(self: *Server, socket: posix.socket_t, timeout_ms: i32) !void {
if (try client.readSocket() == false) { if (try client.readSocket() == false) {
return; return;
} }
last_message = timestamp(); last_message = timestamp(.monotonic);
ms_remaining = timeout_ms; ms_remaining = timeout_ms;
}, },
.done => { .done => {
const elapsed = timestamp() - last_message; const elapsed = timestamp(.monotonic) - last_message;
if (elapsed > ms_remaining) { if (elapsed > ms_remaining) {
log.info(.app, "CDP timeout", .{}); log.info(.app, "CDP timeout", .{});
return; return;
} }
ms_remaining -= @as(i32, @intCast(elapsed)); ms_remaining -= @intCast(elapsed);
}, },
} }
} }
@@ -928,9 +928,7 @@ fn buildJSONVersionResponse(
return try std.fmt.allocPrint(allocator, response_format, .{ body_len, address }); return try std.fmt.allocPrint(allocator, response_format, .{ body_len, address });
} }
fn timestamp() u32 { pub const timestamp = @import("datetime.zig").timestamp;
return @import("datetime.zig").timestamp();
}
// In-place string lowercase // In-place string lowercase
fn toLower(str: []u8) []u8 { fn toLower(str: []u8) []u8 {

View File

@@ -6,7 +6,7 @@ const Thread = std.Thread;
const Allocator = std.mem.Allocator; const Allocator = std.mem.Allocator;
const log = @import("../log.zig"); const log = @import("../log.zig");
const App = @import("../app.zig").App; const App = @import("../App.zig");
const Http = @import("../http/Http.zig"); const Http = @import("../http/Http.zig");
const telemetry = @import("telemetry.zig"); const telemetry = @import("telemetry.zig");