diff --git a/src/cdp/domains/dom.zig b/src/cdp/domains/dom.zig index e85ee7fe..396cf530 100644 --- a/src/cdp/domains/dom.zig +++ b/src/cdp/domains/dom.zig @@ -30,6 +30,7 @@ pub fn processMessage(cmd: anytype) !void { getSearchResults, discardSearchResults, resolveNode, + describeNode, }, cmd.input.action) orelse return error.UnknownMethod; switch (action) { @@ -39,6 +40,7 @@ pub fn processMessage(cmd: anytype) !void { .getSearchResults => return getSearchResults(cmd), .discardSearchResults => return discardSearchResults(cmd), .resolveNode => return resolveNode(cmd), + .describeNode => return describeNode(cmd), } } @@ -151,6 +153,37 @@ fn resolveNode(cmd: anytype) !void { } }, .{}); } +fn describeNode(cmd: anytype) !void { + const params = (try cmd.params(struct { + nodeId: ?Node.Id = null, + backendNodeId: ?Node.Id = null, + objectId: ?[]const u8 = null, + depth: u32 = 1, + pierce: bool = false, + })) orelse return error.InvalidParams; + if (params.backendNodeId != null or params.depth != 1 or params.pierce) { + return error.NotYetImplementedParams; + } + + const bc = cmd.browser_context orelse return error.BrowserContextNotLoaded; + + if (params.nodeId != null) { + const node = bc.node_registry.lookup_by_id.get(params.nodeId.?) orelse return error.NodeNotFound; + return cmd.sendResult(.{ .node = bc.nodeWriter(node, .{}) }, .{}); + } else if (params.objectId != null) { + + // Retrieve the object from which ever context it is in. + const js_value = try bc.session.inspector.getValueByObjectId(cmd.arena, bc.session.executor, params.objectId.?); + const entry = js_value.taggedAnyOpaque() orelse return error.ObjectIdIsNotANode; + const subtype = entry.subtype orelse return error.ObjectIdIsNotANode; + if (subtype != .node) return error.ObjectIdIsNotANode; + + const node = try bc.node_registry.register(@ptrCast(entry.ptr)); + return cmd.sendResult(.{ .node = bc.nodeWriter(node, .{}) }, .{}); + } + return error.MissingParams; +} + const testing = @import("../testing.zig"); test "cdp.dom: getSearchResults unknown search id" { diff --git a/src/cdp/testing.zig b/src/cdp/testing.zig index 1ca67c03..22fc9e8e 100644 --- a/src/cdp/testing.zig +++ b/src/cdp/testing.zig @@ -55,13 +55,13 @@ const Browser = struct { if (self.session != null) { return error.MockBrowserSessionAlreadyExists; } - const arena = self.arena.allocator(); + const executor = arena.create(Executor) catch unreachable; self.session = try arena.create(Session); self.session.?.* = .{ .page = null, .arena = arena, - .executor = .{}, + .executor = executor, .inspector = .{}, }; return self.session.?; @@ -78,7 +78,7 @@ const Browser = struct { const Session = struct { page: ?Page = null, arena: Allocator, - executor: Executor, + executor: *Executor, inspector: Inspector, pub fn currentPage(self: *Session) ?*Page { @@ -112,7 +112,7 @@ const Executor = struct {}; const Inspector = struct { pub fn getRemoteObject( self: *const Inspector, - executor: Executor, + executor: *Executor, group: []const u8, value: anytype, ) !RemoteObject { @@ -122,6 +122,27 @@ const Inspector = struct { _ = value; return RemoteObject{}; } + pub fn getValueByObjectId(self: Inspector, alloc: std.mem.Allocator, executor: *const Executor, object_id: []const u8) !Value { + _ = self; + _ = alloc; + _ = executor; + _ = object_id; + return .{}; + } +}; +const Value = struct { + pub fn taggedAnyOpaque(self: Value) ?*TaggedAnyOpaque { + _ = self; + return null; + } +}; +const TaggedAnyOpaque = struct { + ptr: *anyopaque, + subtype: ?SubType = .node, +}; + +const SubType = enum { + node, }; const RemoteObject = struct { diff --git a/src/runtime/js.zig b/src/runtime/js.zig index 5d52536c..a42df1c7 100644 --- a/src/runtime/js.zig +++ b/src/runtime/js.zig @@ -1369,6 +1369,15 @@ pub fn Env(comptime S: type, comptime types: anytype) type { generate_preview, ); } + + // Gets a value by object ID regardless of which context it is in. + // unwrapping the object also tells us the context, for now we assume it is always the default one. + // The executor argument is likely to change to somthing to allow us to find the right Executer with the given context + pub fn getValueByObjectId(self: Inspector, allocator: std.mem.Allocator, executor: *const Executor, object_id: []const u8) !Value { + const unwrapped = try self.session.unwrapObject(allocator, object_id); + // std.debug.assert(executor.context.handle == unwrapped.context.handle); + return .{ .value = unwrapped.value, .executor = executor }; // The values context and groupId are not used here + } }; pub const RemoteObject = v8.RemoteObject; @@ -1382,6 +1391,10 @@ pub fn Env(comptime S: type, comptime types: anytype) type { const executor = self.executor; return valueToString(allocator, self.value, executor.isolate, executor.context); } + + pub fn taggedAnyOpaque(self: Value) ?*TaggedAnyOpaque { + return getTaggedAnyOpaque(self.value); + } }; // Reverses the mapZigInstanceToJs, making sure that our TaggedAnyOpaque