mirror of
https://github.com/lightpanda-io/browser.git
synced 2026-03-22 04:34:44 +00:00
Merge pull request #1582 from lightpanda-io/cdp_per_page_frame_id
Rework CDP frameIds (and loaderIds and requestIds and interceptorIds)
This commit is contained in:
@@ -17,6 +17,7 @@
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
const std = @import("std");
|
||||
const id = @import("../id.zig");
|
||||
|
||||
pub fn processMessage(cmd: anytype) !void {
|
||||
const action = std.meta.stringToEnum(enum {
|
||||
@@ -46,15 +47,18 @@ fn getFullAXTree(cmd: anytype) !void {
|
||||
})) orelse return error.InvalidParams;
|
||||
|
||||
const bc = cmd.browser_context orelse return error.BrowserContextNotLoaded;
|
||||
const session = bc.session;
|
||||
|
||||
if (params.frameId) |frameId| {
|
||||
const target_id = bc.target_id orelse return error.TargetNotLoaded;
|
||||
if (std.mem.eql(u8, target_id, frameId) == false) {
|
||||
const page = blk: {
|
||||
const frame_id = params.frameId orelse {
|
||||
break :blk session.currentPage() orelse return error.PageNotLoaded;
|
||||
};
|
||||
const page_id = try id.toPageId(.frame_id, frame_id);
|
||||
break :blk session.findPage(page_id) orelse {
|
||||
return cmd.sendError(-32000, "Frame with the given id does not belong to the target.", .{});
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
const page = bc.session.currentPage() orelse return error.PageNotLoaded;
|
||||
const doc = page.window._document.asNode();
|
||||
const node = try bc.node_registry.register(doc);
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
const std = @import("std");
|
||||
const id = @import("../id.zig");
|
||||
const log = @import("../../log.zig");
|
||||
const Node = @import("../Node.zig");
|
||||
const DOMNode = @import("../../browser/webapi/Node.zig");
|
||||
@@ -497,12 +498,11 @@ fn getFrameOwner(cmd: anytype) !void {
|
||||
})) orelse return error.InvalidParams;
|
||||
|
||||
const bc = cmd.browser_context orelse return error.BrowserContextNotLoaded;
|
||||
const target_id = bc.target_id orelse return error.TargetNotLoaded;
|
||||
if (std.mem.eql(u8, target_id, params.frameId) == false) {
|
||||
return cmd.sendError(-32000, "Frame with the given id does not belong to the target.", .{});
|
||||
}
|
||||
const page_id = try id.toPageId(.frame_id, params.frameId);
|
||||
|
||||
const page = bc.session.currentPage() orelse return error.PageNotLoaded;
|
||||
const page = bc.session.findPage(page_id) orelse {
|
||||
return cmd.sendError(-32000, "Frame with the given id does not belong to the target.", .{});
|
||||
};
|
||||
|
||||
const node = try bc.node_registry.register(page.window._document.asNode());
|
||||
return cmd.sendResult(.{ .nodeId = node.id, .backendNodeId = node.id }, .{});
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
const std = @import("std");
|
||||
const Allocator = std.mem.Allocator;
|
||||
|
||||
const id = @import("../id.zig");
|
||||
const log = @import("../../log.zig");
|
||||
const network = @import("network.zig");
|
||||
|
||||
@@ -48,7 +49,7 @@ pub fn processMessage(cmd: anytype) !void {
|
||||
// Stored in CDP
|
||||
pub const InterceptState = struct {
|
||||
allocator: Allocator,
|
||||
waiting: std.AutoArrayHashMapUnmanaged(u64, *Http.Transfer),
|
||||
waiting: std.AutoArrayHashMapUnmanaged(u32, *Http.Transfer),
|
||||
|
||||
pub fn init(allocator: Allocator) !InterceptState {
|
||||
return .{
|
||||
@@ -65,8 +66,8 @@ pub const InterceptState = struct {
|
||||
return self.waiting.put(self.allocator, transfer.id, transfer);
|
||||
}
|
||||
|
||||
pub fn remove(self: *InterceptState, id: u64) ?*Http.Transfer {
|
||||
const entry = self.waiting.fetchSwapRemove(id) orelse return null;
|
||||
pub fn remove(self: *InterceptState, request_id: u32) ?*Http.Transfer {
|
||||
const entry = self.waiting.fetchSwapRemove(request_id) orelse return null;
|
||||
return entry.value;
|
||||
}
|
||||
|
||||
@@ -178,13 +179,11 @@ fn arePatternsSupported(patterns: []RequestPattern) bool {
|
||||
return true;
|
||||
}
|
||||
|
||||
pub fn requestIntercept(arena: Allocator, bc: anytype, 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
|
||||
// things, but no session.
|
||||
const session_id = bc.session_id orelse return;
|
||||
|
||||
const target_id = bc.target_id orelse unreachable;
|
||||
|
||||
// We keep it around to wait for modifications to the request.
|
||||
// NOTE: we assume whomever created the request created it with a lifetime of the Page.
|
||||
// TODO: What to do when receiving replies for a previous page's requests?
|
||||
@@ -193,16 +192,16 @@ pub fn requestIntercept(arena: Allocator, bc: anytype, intercept: *const Notific
|
||||
try bc.intercept_state.put(transfer);
|
||||
|
||||
try bc.cdp.sendEvent("Fetch.requestPaused", .{
|
||||
.requestId = try std.fmt.allocPrint(arena, "INTERCEPT-{d}", .{transfer.id}),
|
||||
.requestId = &id.toInterceptId(transfer.id),
|
||||
.frameId = &id.toFrameId(transfer.req.page_id),
|
||||
.request = network.TransferAsRequestWriter.init(transfer),
|
||||
.frameId = target_id,
|
||||
.resourceType = switch (transfer.req.resource_type) {
|
||||
.script => "Script",
|
||||
.xhr => "XHR",
|
||||
.document => "Document",
|
||||
.fetch => "Fetch",
|
||||
},
|
||||
.networkId = try std.fmt.allocPrint(arena, "REQ-{d}", .{transfer.id}),
|
||||
.networkId = &id.toRequestId(transfer.id), // matches the Network REQ-ID
|
||||
}, .{ .session_id = session_id });
|
||||
|
||||
log.debug(.cdp, "request intercept", .{
|
||||
@@ -218,7 +217,7 @@ pub fn requestIntercept(arena: Allocator, bc: anytype, intercept: *const Notific
|
||||
fn continueRequest(cmd: anytype) !void {
|
||||
const bc = cmd.browser_context orelse return error.BrowserContextNotLoaded;
|
||||
const params = (try cmd.params(struct {
|
||||
requestId: []const u8, // "INTERCEPT-{d}"
|
||||
requestId: []const u8, // INT-{d}"
|
||||
url: ?[]const u8 = null,
|
||||
method: ?[]const u8 = null,
|
||||
postData: ?[]const u8 = null,
|
||||
@@ -278,7 +277,7 @@ const AuthChallengeResponse = enum {
|
||||
fn continueWithAuth(cmd: anytype) !void {
|
||||
const bc = cmd.browser_context orelse return error.BrowserContextNotLoaded;
|
||||
const params = (try cmd.params(struct {
|
||||
requestId: []const u8, // "INTERCEPT-{d}"
|
||||
requestId: []const u8, // "INT-{d}"
|
||||
authChallengeResponse: struct {
|
||||
response: AuthChallengeResponse,
|
||||
username: []const u8 = "",
|
||||
@@ -322,7 +321,7 @@ fn fulfillRequest(cmd: anytype) !void {
|
||||
const bc = cmd.browser_context orelse return error.BrowserContextNotLoaded;
|
||||
|
||||
const params = (try cmd.params(struct {
|
||||
requestId: []const u8, // "INTERCEPT-{d}"
|
||||
requestId: []const u8, // "INT-{d}"
|
||||
responseCode: u16,
|
||||
responseHeaders: ?[]const Http.Header = null,
|
||||
binaryResponseHeaders: ?[]const u8 = null,
|
||||
@@ -363,7 +362,7 @@ fn fulfillRequest(cmd: anytype) !void {
|
||||
fn failRequest(cmd: anytype) !void {
|
||||
const bc = cmd.browser_context orelse return error.BrowserContextNotLoaded;
|
||||
const params = (try cmd.params(struct {
|
||||
requestId: []const u8, // "INTERCEPT-{d}"
|
||||
requestId: []const u8, // "INT-{d}"
|
||||
errorReason: ErrorReason,
|
||||
})) orelse return error.InvalidParams;
|
||||
|
||||
@@ -382,13 +381,11 @@ fn failRequest(cmd: anytype) !void {
|
||||
return cmd.sendResult(null, .{});
|
||||
}
|
||||
|
||||
pub fn requestAuthRequired(arena: Allocator, bc: anytype, 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
|
||||
// things, but no session.
|
||||
const session_id = bc.session_id orelse return;
|
||||
|
||||
const target_id = bc.target_id orelse unreachable;
|
||||
|
||||
// We keep it around to wait for modifications to the request.
|
||||
// NOTE: we assume whomever created the request created it with a lifetime of the Page.
|
||||
// TODO: What to do when receiving replies for a previous page's requests?
|
||||
@@ -399,9 +396,9 @@ pub fn requestAuthRequired(arena: Allocator, bc: anytype, intercept: *const Noti
|
||||
const challenge = transfer._auth_challenge orelse return error.NullAuthChallenge;
|
||||
|
||||
try bc.cdp.sendEvent("Fetch.authRequired", .{
|
||||
.requestId = try std.fmt.allocPrint(arena, "INTERCEPT-{d}", .{transfer.id}),
|
||||
.requestId = &id.toInterceptId(transfer.id),
|
||||
.frameId = &id.toFrameId(transfer.req.page_id),
|
||||
.request = network.TransferAsRequestWriter.init(transfer),
|
||||
.frameId = target_id,
|
||||
.resourceType = switch (transfer.req.resource_type) {
|
||||
.script => "Script",
|
||||
.xhr => "XHR",
|
||||
@@ -414,7 +411,7 @@ pub fn requestAuthRequired(arena: Allocator, bc: anytype, intercept: *const Noti
|
||||
.scheme = if (challenge.scheme == .digest) "digest" else "basic",
|
||||
.realm = challenge.realm,
|
||||
},
|
||||
.networkId = try std.fmt.allocPrint(arena, "REQ-{d}", .{transfer.id}),
|
||||
.networkId = &id.toRequestId(transfer.id),
|
||||
}, .{ .session_id = session_id });
|
||||
|
||||
log.debug(.cdp, "request auth required", .{
|
||||
@@ -427,10 +424,10 @@ pub fn requestAuthRequired(arena: Allocator, bc: anytype, intercept: *const Noti
|
||||
intercept.wait_for_interception.* = true;
|
||||
}
|
||||
|
||||
// Get u64 from requestId which is formatted as: "INTERCEPT-{d}"
|
||||
fn idFromRequestId(request_id: []const u8) !u64 {
|
||||
if (!std.mem.startsWith(u8, request_id, "INTERCEPT-")) {
|
||||
// Get u32 from requestId which is formatted as: "INT-{d}"
|
||||
fn idFromRequestId(request_id: []const u8) !u32 {
|
||||
if (!std.mem.startsWith(u8, request_id, "INT-")) {
|
||||
return error.InvalidParams;
|
||||
}
|
||||
return std.fmt.parseInt(u64, request_id[10..], 10) catch return error.InvalidParams;
|
||||
return std.fmt.parseInt(u32, request_id[4..], 10) catch return error.InvalidParams;
|
||||
}
|
||||
|
||||
@@ -116,30 +116,3 @@ fn insertText(cmd: anytype) !void {
|
||||
|
||||
try cmd.sendResult(null, .{});
|
||||
}
|
||||
|
||||
fn clickNavigate(cmd: anytype, uri: std.Uri) !void {
|
||||
const bc = cmd.browser_context.?;
|
||||
|
||||
var url_buf: std.ArrayList(u8) = .{};
|
||||
try uri.writeToStream(.{
|
||||
.scheme = true,
|
||||
.authentication = true,
|
||||
.authority = true,
|
||||
.port = true,
|
||||
.path = true,
|
||||
.query = true,
|
||||
}, url_buf.writer(cmd.arena));
|
||||
const url = url_buf.items;
|
||||
|
||||
try cmd.sendEvent("Page.frameRequestedNavigation", .{
|
||||
.url = url,
|
||||
.frameId = bc.target_id.?,
|
||||
.reason = "anchorClick",
|
||||
.disposition = "currentTab",
|
||||
}, .{ .session_id = bc.session_id.? });
|
||||
|
||||
try bc.session.removePage();
|
||||
_ = try bc.session.createPage(null);
|
||||
|
||||
try @import("page.zig").navigateToUrl(cmd, url, false);
|
||||
}
|
||||
|
||||
@@ -21,6 +21,8 @@ const lp = @import("lightpanda");
|
||||
const Allocator = std.mem.Allocator;
|
||||
|
||||
const CdpStorage = @import("storage.zig");
|
||||
|
||||
const id = @import("../id.zig");
|
||||
const URL = @import("../../browser/URL.zig");
|
||||
const Transfer = @import("../../http/Client.zig").Transfer;
|
||||
const Notification = @import("../../Notification.zig");
|
||||
@@ -208,7 +210,7 @@ fn getResponseBody(cmd: anytype) !void {
|
||||
}, .{});
|
||||
}
|
||||
|
||||
pub fn httpRequestFail(arena: Allocator, bc: anytype, 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
|
||||
// sent Target.closeTarget. In that case, bc.session_id will be cleared
|
||||
// already, and we can skip sending these messages to the client.
|
||||
@@ -220,7 +222,7 @@ pub fn httpRequestFail(arena: Allocator, bc: anytype, msg: *const Notification.R
|
||||
|
||||
// We're missing a bunch of fields, but, for now, this seems like enough
|
||||
try bc.cdp.sendEvent("Network.loadingFailed", .{
|
||||
.requestId = try std.fmt.allocPrint(arena, "REQ-{d}", .{msg.transfer.id}),
|
||||
.requestId = &id.toRequestId(msg.transfer.id),
|
||||
// Seems to be what chrome answers with. I assume it depends on the type of error?
|
||||
.type = "Ping",
|
||||
.errorText = msg.err,
|
||||
@@ -228,28 +230,27 @@ pub fn httpRequestFail(arena: Allocator, bc: anytype, msg: *const Notification.R
|
||||
}, .{ .session_id = session_id });
|
||||
}
|
||||
|
||||
pub fn httpRequestStart(arena: Allocator, bc: anytype, 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
|
||||
// things, but no session.
|
||||
const session_id = bc.session_id orelse return;
|
||||
|
||||
const target_id = bc.target_id orelse unreachable;
|
||||
const page = bc.session.currentPage() orelse unreachable;
|
||||
const transfer = msg.transfer;
|
||||
const req = &transfer.req;
|
||||
const page_id = req.page_id;
|
||||
const page = bc.session.findPage(page_id) orelse return;
|
||||
|
||||
// Modify request with extra CDP headers
|
||||
for (bc.extra_headers.items) |extra| {
|
||||
try msg.transfer.req.headers.add(extra);
|
||||
try req.headers.add(extra);
|
||||
}
|
||||
|
||||
const transfer = msg.transfer;
|
||||
const loader_id = try std.fmt.allocPrint(arena, "REQ-{d}", .{transfer.id});
|
||||
|
||||
// We're missing a bunch of fields, but, for now, this seems like enough
|
||||
try bc.cdp.sendEvent("Network.requestWillBeSent", .{
|
||||
.requestId = loader_id,
|
||||
.frameId = target_id,
|
||||
.loaderId = loader_id,
|
||||
.type = msg.transfer.req.resource_type.string(),
|
||||
.loaderId = &id.toLoaderId(transfer.id),
|
||||
.requestId = &id.toRequestId(transfer.id),
|
||||
.frameId = &id.toFrameId(page_id),
|
||||
.type = req.resource_type.string(),
|
||||
.documentURL = page.url,
|
||||
.request = TransferAsRequestWriter.init(transfer),
|
||||
.initiator = .{ .type = "other" },
|
||||
@@ -262,29 +263,27 @@ pub fn httpResponseHeaderDone(arena: Allocator, bc: anytype, msg: *const Notific
|
||||
// detachTarget could be called, in which case, we still have a page doing
|
||||
// things, but no session.
|
||||
const session_id = bc.session_id orelse return;
|
||||
const target_id = bc.target_id orelse unreachable;
|
||||
|
||||
const transfer = msg.transfer;
|
||||
const loader_id = try std.fmt.allocPrint(arena, "REQ-{d}", .{transfer.id});
|
||||
|
||||
// We're missing a bunch of fields, but, for now, this seems like enough
|
||||
try bc.cdp.sendEvent("Network.responseReceived", .{
|
||||
.requestId = loader_id,
|
||||
.frameId = target_id,
|
||||
.loaderId = loader_id,
|
||||
.loaderId = &id.toLoaderId(transfer.id),
|
||||
.requestId = &id.toRequestId(transfer.id),
|
||||
.frameId = &id.toFrameId(transfer.req.page_id),
|
||||
.response = TransferAsResponseWriter.init(arena, msg.transfer),
|
||||
.hasExtraInfo = false, // TODO change after adding Network.responseReceivedExtraInfo
|
||||
}, .{ .session_id = session_id });
|
||||
}
|
||||
|
||||
pub fn httpRequestDone(arena: Allocator, bc: anytype, 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
|
||||
// things, but no session.
|
||||
const session_id = bc.session_id orelse return;
|
||||
|
||||
const transfer = msg.transfer;
|
||||
try bc.cdp.sendEvent("Network.loadingFinished", .{
|
||||
.requestId = try std.fmt.allocPrint(arena, "REQ-{d}", .{msg.transfer.id}),
|
||||
.encodedDataLength = msg.transfer.bytes_received,
|
||||
.requestId = &id.toRequestId(transfer.id),
|
||||
.encodedDataLength = transfer.bytes_received,
|
||||
}, .{ .session_id = session_id });
|
||||
}
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
const std = @import("std");
|
||||
const lp = @import("lightpanda");
|
||||
|
||||
const id = @import("../id.zig");
|
||||
const log = @import("../../log.zig");
|
||||
const js = @import("../../browser/js/js.zig");
|
||||
const Page = @import("../../browser/Page.zig");
|
||||
@@ -73,9 +74,9 @@ fn getFrameTree(cmd: anytype) !void {
|
||||
return cmd.sendResult(.{
|
||||
.frameTree = .{
|
||||
.frame = Frame{
|
||||
.id = target_id,
|
||||
.loaderId = bc.loader_id,
|
||||
.id = &target_id,
|
||||
.securityOrigin = bc.security_origin,
|
||||
.loaderId = "LID-0000000001",
|
||||
.url = bc.getURL() orelse "about:blank",
|
||||
.secureContextType = bc.secure_context_type,
|
||||
},
|
||||
@@ -103,18 +104,21 @@ fn setLifecycleEventsEnabled(cmd: anytype) !void {
|
||||
const page = bc.session.currentPage() orelse return error.PageNotLoaded;
|
||||
|
||||
if (page._load_state == .complete) {
|
||||
const frame_id = &id.toFrameId(page.id);
|
||||
const loader_id = &id.toLoaderId(page._req_id);
|
||||
|
||||
const now = timestampF(.monotonic);
|
||||
try sendPageLifecycle(bc, "DOMContentLoaded", now);
|
||||
try sendPageLifecycle(bc, "load", now);
|
||||
try sendPageLifecycle(bc, "DOMContentLoaded", now, frame_id, loader_id);
|
||||
try sendPageLifecycle(bc, "load", now, frame_id, loader_id);
|
||||
|
||||
const http_client = page._session.browser.http_client;
|
||||
const http_active = http_client.active;
|
||||
const total_network_activity = http_active + http_client.intercepted;
|
||||
if (page._notified_network_almost_idle.check(total_network_activity <= 2)) {
|
||||
try sendPageLifecycle(bc, "networkAlmostIdle", now);
|
||||
try sendPageLifecycle(bc, "networkAlmostIdle", now, frame_id, loader_id);
|
||||
}
|
||||
if (page._notified_network_idle.check(total_network_activity == 0)) {
|
||||
try sendPageLifecycle(bc, "networkIdle", now);
|
||||
try sendPageLifecycle(bc, "networkIdle", now, frame_id, loader_id);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -227,16 +231,15 @@ fn navigate(cmd: anytype) !void {
|
||||
});
|
||||
}
|
||||
|
||||
pub fn pageNavigate(arena: Allocator, bc: anytype, 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
|
||||
// things, but no session.
|
||||
const session_id = bc.session_id orelse return;
|
||||
|
||||
const loader_id = try std.fmt.allocPrint(arena, "REQ-{d}", .{event.req_id});
|
||||
const target_id = bc.target_id orelse unreachable;
|
||||
|
||||
bc.reset();
|
||||
|
||||
const frame_id = &id.toFrameId(event.page_id);
|
||||
const loader_id = &id.toLoaderId(event.req_id);
|
||||
|
||||
var cdp = bc.cdp;
|
||||
const reason_: ?[]const u8 = switch (event.opts.reason) {
|
||||
.anchor => "anchorClick",
|
||||
@@ -250,14 +253,14 @@ pub fn pageNavigate(arena: Allocator, bc: anytype, event: *const Notification.Pa
|
||||
};
|
||||
if (reason_) |reason| {
|
||||
try cdp.sendEvent("Page.frameScheduledNavigation", .{
|
||||
.frameId = target_id,
|
||||
.frameId = frame_id,
|
||||
.delay = 0,
|
||||
.reason = reason,
|
||||
.url = event.url,
|
||||
}, .{ .session_id = session_id });
|
||||
|
||||
try cdp.sendEvent("Page.frameRequestedNavigation", .{
|
||||
.frameId = target_id,
|
||||
.frameId = frame_id,
|
||||
.reason = reason,
|
||||
.url = event.url,
|
||||
.disposition = "currentTab",
|
||||
@@ -266,7 +269,7 @@ pub fn pageNavigate(arena: Allocator, bc: anytype, event: *const Notification.Pa
|
||||
|
||||
// frameStartedNavigating event
|
||||
try cdp.sendEvent("Page.frameStartedNavigating", .{
|
||||
.frameId = target_id,
|
||||
.frameId = frame_id,
|
||||
.url = event.url,
|
||||
.loaderId = loader_id,
|
||||
.navigationType = "differentDocument",
|
||||
@@ -274,7 +277,7 @@ pub fn pageNavigate(arena: Allocator, bc: anytype, event: *const Notification.Pa
|
||||
|
||||
// frameStartedLoading event
|
||||
try cdp.sendEvent("Page.frameStartedLoading", .{
|
||||
.frameId = target_id,
|
||||
.frameId = frame_id,
|
||||
}, .{ .session_id = session_id });
|
||||
}
|
||||
|
||||
@@ -301,9 +304,10 @@ pub fn pageNavigated(arena: Allocator, bc: anytype, event: *const Notification.P
|
||||
// detachTarget could be called, in which case, we still have a page doing
|
||||
// things, but no session.
|
||||
const session_id = bc.session_id orelse return;
|
||||
const loader_id = try std.fmt.allocPrint(arena, "REQ-{d}", .{event.req_id});
|
||||
const target_id = bc.target_id orelse unreachable;
|
||||
|
||||
const timestamp = event.timestamp;
|
||||
const frame_id = &id.toFrameId(event.page_id);
|
||||
const loader_id = &id.toLoaderId(event.req_id);
|
||||
|
||||
var cdp = bc.cdp;
|
||||
|
||||
@@ -316,7 +320,7 @@ pub fn pageNavigated(arena: Allocator, bc: anytype, event: *const Notification.P
|
||||
try cdp.sendJSON(.{
|
||||
.id = input_id,
|
||||
.result = .{
|
||||
.frameId = target_id,
|
||||
.frameId = frame_id,
|
||||
.loaderId = loader_id,
|
||||
},
|
||||
.sessionId = session_id,
|
||||
@@ -326,7 +330,7 @@ pub fn pageNavigated(arena: Allocator, bc: anytype, event: *const Notification.P
|
||||
if (bc.page_life_cycle_events) {
|
||||
try cdp.sendEvent("Page.lifecycleEvent", LifecycleEvent{
|
||||
.name = "init",
|
||||
.frameId = target_id,
|
||||
.frameId = frame_id,
|
||||
.loaderId = loader_id,
|
||||
.timestamp = event.timestamp,
|
||||
}, .{ .session_id = session_id });
|
||||
@@ -345,7 +349,7 @@ pub fn pageNavigated(arena: Allocator, bc: anytype, event: *const Notification.P
|
||||
|
||||
if (reason_ != null) {
|
||||
try cdp.sendEvent("Page.frameClearedScheduledNavigation", .{
|
||||
.frameId = target_id,
|
||||
.frameId = frame_id,
|
||||
}, .{ .session_id = session_id });
|
||||
}
|
||||
|
||||
@@ -356,7 +360,7 @@ pub fn pageNavigated(arena: Allocator, bc: anytype, event: *const Notification.P
|
||||
|
||||
{
|
||||
const page = bc.session.currentPage() orelse return error.PageNotLoaded;
|
||||
const aux_data = try std.fmt.allocPrint(arena, "{{\"isDefault\":true,\"type\":\"default\",\"frameId\":\"{s}\"}}", .{target_id});
|
||||
const aux_data = try std.fmt.allocPrint(arena, "{{\"isDefault\":true,\"type\":\"default\",\"frameId\":\"{s}\",\"loaderId\":\"{s}\"}}", .{ frame_id, loader_id });
|
||||
|
||||
var ls: js.Local.Scope = undefined;
|
||||
page.js.localScope(&ls);
|
||||
@@ -371,7 +375,7 @@ pub fn pageNavigated(arena: Allocator, bc: anytype, event: *const Notification.P
|
||||
);
|
||||
}
|
||||
for (bc.isolated_worlds.items) |isolated_world| {
|
||||
const aux_json = try std.fmt.allocPrint(arena, "{{\"isDefault\":false,\"type\":\"isolated\",\"frameId\":\"{s}\"}}", .{target_id});
|
||||
const aux_json = try std.fmt.allocPrint(arena, "{{\"isDefault\":false,\"type\":\"isolated\",\"frameId\":\"{s}\",\"loaderId\":\"{s}\"}}", .{ frame_id, loader_id });
|
||||
|
||||
// Calling contextCreated will assign a new Id to the context and send the contextCreated event
|
||||
|
||||
@@ -392,7 +396,7 @@ pub fn pageNavigated(arena: Allocator, bc: anytype, event: *const Notification.P
|
||||
try cdp.sendEvent("Page.frameNavigated", .{
|
||||
.type = "Navigation",
|
||||
.frame = Frame{
|
||||
.id = target_id,
|
||||
.id = frame_id,
|
||||
.url = event.url,
|
||||
.loaderId = loader_id,
|
||||
.securityOrigin = bc.security_origin,
|
||||
@@ -419,7 +423,7 @@ pub fn pageNavigated(arena: Allocator, bc: anytype, event: *const Notification.P
|
||||
try cdp.sendEvent("Page.lifecycleEvent", LifecycleEvent{
|
||||
.timestamp = timestamp,
|
||||
.name = "DOMContentLoaded",
|
||||
.frameId = target_id,
|
||||
.frameId = frame_id,
|
||||
.loaderId = loader_id,
|
||||
}, .{ .session_id = session_id });
|
||||
}
|
||||
@@ -436,35 +440,33 @@ pub fn pageNavigated(arena: Allocator, bc: anytype, event: *const Notification.P
|
||||
try cdp.sendEvent("Page.lifecycleEvent", LifecycleEvent{
|
||||
.timestamp = timestamp,
|
||||
.name = "load",
|
||||
.frameId = target_id,
|
||||
.frameId = frame_id,
|
||||
.loaderId = loader_id,
|
||||
}, .{ .session_id = session_id });
|
||||
}
|
||||
|
||||
// frameStoppedLoading
|
||||
return cdp.sendEvent("Page.frameStoppedLoading", .{
|
||||
.frameId = target_id,
|
||||
.frameId = frame_id,
|
||||
}, .{ .session_id = session_id });
|
||||
}
|
||||
|
||||
pub fn pageNetworkIdle(bc: anytype, event: *const Notification.PageNetworkIdle) !void {
|
||||
return sendPageLifecycle(bc, "networkIdle", event.timestamp);
|
||||
return sendPageLifecycle(bc, "networkIdle", event.timestamp, &id.toFrameId(event.page_id), &id.toLoaderId(event.req_id));
|
||||
}
|
||||
|
||||
pub fn pageNetworkAlmostIdle(bc: anytype, event: *const Notification.PageNetworkAlmostIdle) !void {
|
||||
return sendPageLifecycle(bc, "networkAlmostIdle", event.timestamp);
|
||||
return sendPageLifecycle(bc, "networkAlmostIdle", event.timestamp, &id.toFrameId(event.page_id), &id.toLoaderId(event.req_id));
|
||||
}
|
||||
|
||||
fn sendPageLifecycle(bc: anytype, name: []const u8, timestamp: u64) !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
|
||||
// things, but no session.
|
||||
const session_id = bc.session_id orelse return;
|
||||
|
||||
const loader_id = bc.loader_id;
|
||||
const target_id = bc.target_id orelse unreachable;
|
||||
return bc.cdp.sendEvent("Page.lifecycleEvent", LifecycleEvent{
|
||||
.name = name,
|
||||
.frameId = target_id,
|
||||
.frameId = frame_id,
|
||||
.loaderId = loader_id,
|
||||
.timestamp = timestamp,
|
||||
}, .{ .session_id = session_id });
|
||||
@@ -487,15 +489,15 @@ test "cdp.page: getFrameTree" {
|
||||
try ctx.expectSentError(-31998, "BrowserContextNotLoaded", .{ .id = 10 });
|
||||
}
|
||||
|
||||
const bc = try ctx.loadBrowserContext(.{ .id = "BID-9", .target_id = "TID-3" });
|
||||
const bc = try ctx.loadBrowserContext(.{ .id = "BID-9", .url = "hi.html", .target_id = "FID-000000000X".* });
|
||||
{
|
||||
try ctx.processMessage(.{ .id = 11, .method = "Page.getFrameTree" });
|
||||
try ctx.expectSentResult(.{
|
||||
.frameTree = .{
|
||||
.frame = .{
|
||||
.id = "TID-3",
|
||||
.loaderId = bc.loader_id,
|
||||
.url = "about:blank",
|
||||
.id = "FID-000000000X",
|
||||
.loaderId = "LID-0000000001",
|
||||
.url = "http://127.0.0.1:9582/src/browser/tests/hi.html",
|
||||
.domainAndRegistry = "",
|
||||
.securityOrigin = bc.security_origin,
|
||||
.mimeType = "text/html",
|
||||
|
||||
@@ -18,6 +18,8 @@
|
||||
|
||||
const std = @import("std");
|
||||
const lp = @import("lightpanda");
|
||||
|
||||
const id = @import("../id.zig");
|
||||
const log = @import("../../log.zig");
|
||||
const js = @import("../../browser/js/js.zig");
|
||||
|
||||
@@ -66,11 +68,11 @@ fn getTargets(cmd: anytype) !void {
|
||||
}, .{ .include_session_id = false });
|
||||
};
|
||||
|
||||
const target_id = bc.target_id orelse {
|
||||
const target_id = &(bc.target_id orelse {
|
||||
return cmd.sendResult(.{
|
||||
.targetInfos = [_]TargetInfo{},
|
||||
}, .{ .include_session_id = false });
|
||||
};
|
||||
});
|
||||
|
||||
return cmd.sendResult(.{
|
||||
.targetInfos = [_]TargetInfo{.{
|
||||
@@ -171,11 +173,12 @@ fn createTarget(cmd: anytype) !void {
|
||||
// if target_id is null, we should never have a session_id
|
||||
lp.assert(bc.session_id == null, "CDP.target.createTarget not null session_id", .{});
|
||||
|
||||
const target_id = cmd.cdp.target_id_gen.next();
|
||||
|
||||
bc.target_id = target_id;
|
||||
|
||||
const page = try bc.session.createPage();
|
||||
|
||||
// the target_id == the frame_id of the "root" page
|
||||
const frame_id = id.toFrameId(page.id);
|
||||
bc.target_id = frame_id;
|
||||
const target_id = &bc.target_id.?;
|
||||
{
|
||||
var ls: js.Local.Scope = undefined;
|
||||
page.js.localScope(&ls);
|
||||
@@ -195,7 +198,6 @@ fn createTarget(cmd: anytype) !void {
|
||||
// change CDP state
|
||||
bc.security_origin = "://";
|
||||
bc.secure_context_type = "InsecureScheme";
|
||||
bc.loader_id = LOADER_ID;
|
||||
|
||||
// send targetCreated event
|
||||
// TODO: should this only be sent when Target.setDiscoverTargets
|
||||
@@ -234,7 +236,7 @@ fn attachToTarget(cmd: anytype) !void {
|
||||
})) orelse return error.InvalidParams;
|
||||
|
||||
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);
|
||||
if (std.mem.eql(u8, target_id, params.targetId) == false) {
|
||||
return error.UnknownTargetId;
|
||||
}
|
||||
@@ -255,7 +257,7 @@ fn closeTarget(cmd: anytype) !void {
|
||||
})) orelse return error.InvalidParams;
|
||||
|
||||
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);
|
||||
if (std.mem.eql(u8, target_id, params.targetId) == false) {
|
||||
return error.UnknownTargetId;
|
||||
}
|
||||
@@ -298,7 +300,7 @@ fn getTargetInfo(cmd: anytype) !void {
|
||||
|
||||
if (params.targetId) |param_target_id| {
|
||||
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);
|
||||
if (std.mem.eql(u8, target_id, param_target_id) == false) {
|
||||
return error.UnknownTargetId;
|
||||
}
|
||||
@@ -415,10 +417,11 @@ fn setAutoAttach(cmd: anytype) !void {
|
||||
// autoAttach is set to true, we must attach to all existing targets.
|
||||
if (cmd.browser_context) |bc| {
|
||||
if (bc.target_id == null) {
|
||||
// hasn't attached yet
|
||||
const target_id = cmd.cdp.target_id_gen.next();
|
||||
try doAttachtoTarget(cmd, target_id);
|
||||
bc.target_id = target_id;
|
||||
if (bc.session.currentPage()) |page| {
|
||||
// the target_id == the frame_id of the "root" page
|
||||
bc.target_id = id.toFrameId(page.id);
|
||||
try doAttachtoTarget(cmd, &bc.target_id.?);
|
||||
}
|
||||
}
|
||||
// should we send something here?
|
||||
return;
|
||||
@@ -612,14 +615,14 @@ test "cdp.target: closeTarget" {
|
||||
|
||||
// pretend we createdTarget first
|
||||
_ = try bc.session.createPage();
|
||||
bc.target_id = "TID-A";
|
||||
bc.target_id = "TID-000000000A".*;
|
||||
{
|
||||
try testing.expectError(error.UnknownTargetId, ctx.processMessage(.{ .id = 10, .method = "Target.closeTarget", .params = .{ .targetId = "TID-8" } }));
|
||||
try ctx.expectSentError(-31998, "UnknownTargetId", .{ .id = 10 });
|
||||
}
|
||||
|
||||
{
|
||||
try ctx.processMessage(.{ .id = 11, .method = "Target.closeTarget", .params = .{ .targetId = "TID-A" } });
|
||||
try ctx.processMessage(.{ .id = 11, .method = "Target.closeTarget", .params = .{ .targetId = "TID-000000000A" } });
|
||||
try ctx.expectSentResult(.{ .success = true }, .{ .id = 11 });
|
||||
try testing.expectEqual(null, bc.session.page);
|
||||
try testing.expectEqual(null, bc.target_id);
|
||||
@@ -643,14 +646,14 @@ test "cdp.target: attachToTarget" {
|
||||
|
||||
// pretend we createdTarget first
|
||||
_ = try bc.session.createPage();
|
||||
bc.target_id = "TID-B";
|
||||
bc.target_id = "TID-000000000B".*;
|
||||
{
|
||||
try testing.expectError(error.UnknownTargetId, ctx.processMessage(.{ .id = 10, .method = "Target.attachToTarget", .params = .{ .targetId = "TID-8" } }));
|
||||
try ctx.expectSentError(-31998, "UnknownTargetId", .{ .id = 10 });
|
||||
}
|
||||
|
||||
{
|
||||
try ctx.processMessage(.{ .id = 11, .method = "Target.attachToTarget", .params = .{ .targetId = "TID-B" } });
|
||||
try ctx.processMessage(.{ .id = 11, .method = "Target.attachToTarget", .params = .{ .targetId = "TID-000000000B" } });
|
||||
const session_id = bc.session_id.?;
|
||||
try ctx.expectSentResult(.{ .sessionId = session_id }, .{ .id = 11 });
|
||||
try ctx.expectSentEvent("Target.attachedToTarget", .{ .sessionId = session_id, .targetInfo = .{ .url = "chrome://newtab/", .title = "about:blank", .attached = true, .type = "page", .canAccessOpener = false, .browserContextId = "BID-9", .targetId = bc.target_id.? } }, .{});
|
||||
@@ -687,17 +690,17 @@ test "cdp.target: getTargetInfo" {
|
||||
|
||||
// pretend we createdTarget first
|
||||
_ = try bc.session.createPage();
|
||||
bc.target_id = "TID-A";
|
||||
bc.target_id = "TID-000000000C".*;
|
||||
{
|
||||
try testing.expectError(error.UnknownTargetId, ctx.processMessage(.{ .id = 10, .method = "Target.getTargetInfo", .params = .{ .targetId = "TID-8" } }));
|
||||
try ctx.expectSentError(-31998, "UnknownTargetId", .{ .id = 10 });
|
||||
}
|
||||
|
||||
{
|
||||
try ctx.processMessage(.{ .id = 11, .method = "Target.getTargetInfo", .params = .{ .targetId = "TID-A" } });
|
||||
try ctx.processMessage(.{ .id = 11, .method = "Target.getTargetInfo", .params = .{ .targetId = "TID-000000000C" } });
|
||||
try ctx.expectSentResult(.{
|
||||
.targetInfo = .{
|
||||
.targetId = "TID-A",
|
||||
.targetId = "TID-000000000C",
|
||||
.type = "page",
|
||||
.title = "",
|
||||
.url = "about:blank",
|
||||
|
||||
Reference in New Issue
Block a user