mirror of
https://github.com/lightpanda-io/browser.git
synced 2026-10-09 04:42:30 -04:00
Merge pull request #3558 from lightpanda-io/cdp-frame-owner
cdp: DOM.getFrameOwner returns the owner <iframe>, nodes carry frameId
This commit is contained in:
2 files changed
+73
-5
No files matched your search
+25
-4
@@ -22,8 +22,10 @@
|
||||
const std = @import("std");
|
||||
const lp = @import("lightpanda");
|
||||
|
||||
const id = @import("id.zig");
|
||||
const Frame = @import("../../browser/Frame.zig");
|
||||
const DOMNode = @import("../../browser/webapi/Node.zig");
|
||||
const IFrame = @import("../../browser/webapi/element/html/IFrame.zig");
|
||||
const NodeRegistry = @import("../../NodeRegistry.zig");
|
||||
|
||||
const log = lp.log;
|
||||
@@ -67,12 +69,12 @@ pub const Search = struct {
|
||||
}
|
||||
|
||||
pub fn create(self: *List, nodes: []const *DOMNode) !Search {
|
||||
const id = self.search_id;
|
||||
defer self.search_id = id +% 1;
|
||||
const search_id = self.search_id;
|
||||
defer self.search_id = search_id +% 1;
|
||||
|
||||
const arena = self.arena.allocator();
|
||||
|
||||
const name = switch (id) {
|
||||
const name = switch (search_id) {
|
||||
0 => "0",
|
||||
1 => "1",
|
||||
2 => "2",
|
||||
@@ -83,7 +85,7 @@ pub const Search = struct {
|
||||
7 => "7",
|
||||
8 => "8",
|
||||
9 => "9",
|
||||
else => try std.fmt.allocPrint(arena, "{d}", .{id}),
|
||||
else => try std.fmt.allocPrint(arena, "{d}", .{search_id}),
|
||||
};
|
||||
|
||||
var registry = self.registry;
|
||||
@@ -217,6 +219,25 @@ pub const Writer = struct {
|
||||
|
||||
try w.objectField("localName");
|
||||
try w.write(element.getLocalName());
|
||||
|
||||
// Chrome names the hosted frame on frame-owner elements and the own
|
||||
// frame on the document element; that's how clients pair an <iframe>
|
||||
// with its Page.frameAttached id.
|
||||
if (element.is(IFrame)) |iframe| {
|
||||
if (iframe.getContentDocument()) |document| {
|
||||
if (document._frame) |child| {
|
||||
try w.objectField("frameId");
|
||||
try w.write(&id.toFrameId(child._frame_id));
|
||||
}
|
||||
}
|
||||
} else if (dom_node._parent) |dom_parent| {
|
||||
if (dom_parent._type == .document) {
|
||||
if (dom_parent.subtype(DOMNode.Document)._frame) |frame| {
|
||||
try w.objectField("frameId");
|
||||
try w.write(&id.toFrameId(frame._frame_id));
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
try w.objectField("localName");
|
||||
try w.write("");
|
||||
|
||||
@@ -610,7 +610,14 @@ fn getFrameOwner(cmd: *CDP.Command) !void {
|
||||
return cmd.sendError(-32000, "Frame with the given id does not belong to the target.", .{});
|
||||
};
|
||||
|
||||
const node = try bc.node_registry.register(frame.window._document.asNode());
|
||||
// The element hosting the frame, as in Chrome; the main frame has none.
|
||||
// Clients (Stagehand's frameLocator, Playwright's contentFrame) match this
|
||||
// backendNodeId against the <iframe> they resolved in the parent.
|
||||
const iframe = frame.iframe orelse {
|
||||
return cmd.sendError(-32000, "Frame with the given id does not belong to the target.", .{});
|
||||
};
|
||||
|
||||
const node = try bc.node_registry.register(iframe.asNode());
|
||||
return cmd.sendResult(.{ .nodeId = node.id, .backendNodeId = node.id }, .{});
|
||||
}
|
||||
|
||||
@@ -1313,6 +1320,46 @@ test "cdp.dom: resolveNode into a child frame's context" {
|
||||
try ctx.expectSentError(-31998, "ContextNotFound", .{ .id = 15 });
|
||||
}
|
||||
|
||||
test "cdp.dom: getFrameOwner returns the owner iframe element" {
|
||||
var ctx = try testing.context();
|
||||
defer ctx.deinit();
|
||||
|
||||
const bc = try ctx.loadBrowserContext(.{ .id = "BID-FO", .url = "cdp/isolated_world.html", .target_id = "FID-000000000X".* });
|
||||
const root = bc.mainFrame() orelse unreachable;
|
||||
const child = root.child_frames.items[0];
|
||||
const iframe_node = try bc.node_registry.register(child.iframe.?.asNode());
|
||||
|
||||
try ctx.processMessage(.{ .id = 10, .method = "DOM.getFrameOwner", .sessionId = "SID-X", .params = .{
|
||||
.frameId = &id.toFrameId(child._frame_id),
|
||||
} });
|
||||
try ctx.expectSentResult(.{ .nodeId = iframe_node.id, .backendNodeId = iframe_node.id }, .{ .id = 10 });
|
||||
|
||||
// The owner element names the frame it hosts.
|
||||
try ctx.processMessage(.{ .id = 11, .method = "DOM.describeNode", .sessionId = "SID-X", .params = .{
|
||||
.backendNodeId = iframe_node.id,
|
||||
} });
|
||||
try ctx.expectSentResult(.{ .node = .{
|
||||
.localName = "iframe",
|
||||
.frameId = &id.toFrameId(child._frame_id),
|
||||
} }, .{ .id = 11 });
|
||||
|
||||
// So does the document element, for its own frame.
|
||||
const child_html = try bc.node_registry.register(child.window._document.getDocumentElement().?.asNode());
|
||||
try ctx.processMessage(.{ .id = 12, .method = "DOM.describeNode", .sessionId = "SID-X", .params = .{
|
||||
.backendNodeId = child_html.id,
|
||||
} });
|
||||
try ctx.expectSentResult(.{ .node = .{
|
||||
.localName = "html",
|
||||
.frameId = &id.toFrameId(child._frame_id),
|
||||
} }, .{ .id = 12 });
|
||||
|
||||
// The main frame has no owner.
|
||||
try ctx.processMessage(.{ .id = 13, .method = "DOM.getFrameOwner", .sessionId = "SID-X", .params = .{
|
||||
.frameId = &id.toFrameId(root._frame_id),
|
||||
} });
|
||||
try ctx.expectSentError(-32000, "Frame with the given id does not belong to the target.", .{ .id = 13 });
|
||||
}
|
||||
|
||||
fn mainWorldContextId(bc: *CDP.BrowserContext, frame: *const Frame) !i32 {
|
||||
var ls: js.Local.Scope = undefined;
|
||||
frame.js.localScope(&ls);
|
||||
|
||||
Reference in new issue
Block a user