mirror of
https://github.com/lightpanda-io/browser.git
synced 2025-12-17 08:48:58 +00:00
675 lines
25 KiB
Zig
675 lines
25 KiB
Zig
// Copyright (C) 2023-2024 Lightpanda (Selecy SAS)
|
|
//
|
|
// Francis Bouvier <francis@lightpanda.io>
|
|
// Pierre Tachoire <pierre@lightpanda.io>
|
|
//
|
|
// This program is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU Affero General Public License as
|
|
// published by the Free Software Foundation, either version 3 of the
|
|
// License, or (at your option) any later version.
|
|
//
|
|
// This program is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU Affero General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU Affero General Public License
|
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
|
|
const std = @import("std");
|
|
const log = @import("../../log.zig");
|
|
const Allocator = std.mem.Allocator;
|
|
// const css = @import("../../browser/dom/css.zig");
|
|
// const parser = @import("../../browser/netsurf.zig");
|
|
// const dom_node = @import("../../browser/dom/node.zig");
|
|
|
|
pub fn processMessage(cmd: anytype) !void {
|
|
const action = std.meta.stringToEnum(enum {
|
|
enable,
|
|
// ZIGDOM
|
|
// getDocument,
|
|
// performSearch,
|
|
// getSearchResults,
|
|
// discardSearchResults,
|
|
// querySelector,
|
|
// querySelectorAll,
|
|
// resolveNode,
|
|
// describeNode,
|
|
// scrollIntoViewIfNeeded,
|
|
// getContentQuads,
|
|
// getBoxModel,
|
|
// requestChildNodes,
|
|
// getFrameOwner,
|
|
}, cmd.input.action) orelse return error.UnknownMethod;
|
|
|
|
switch (action) {
|
|
.enable => return cmd.sendResult(null, .{}),
|
|
// @ZIGDOM
|
|
// .getDocument => return getDocument(cmd),
|
|
// .performSearch => return performSearch(cmd),
|
|
// .getSearchResults => return getSearchResults(cmd),
|
|
// .discardSearchResults => return discardSearchResults(cmd),
|
|
// .querySelector => return querySelector(cmd),
|
|
// .querySelectorAll => return querySelectorAll(cmd),
|
|
// .resolveNode => return resolveNode(cmd),
|
|
// .describeNode => return describeNode(cmd),
|
|
// .scrollIntoViewIfNeeded => return scrollIntoViewIfNeeded(cmd),
|
|
// .getContentQuads => return getContentQuads(cmd),
|
|
// .getBoxModel => return getBoxModel(cmd),
|
|
// .requestChildNodes => return requestChildNodes(cmd),
|
|
// .getFrameOwner => return getFrameOwner(cmd),
|
|
}
|
|
}
|
|
|
|
// ZIGDOM
|
|
// // https://chromedevtools.github.io/devtools-protocol/tot/DOM/#method-getDocument
|
|
// fn getDocument(cmd: anytype) !void {
|
|
// const Params = struct {
|
|
// // CDP documentation implies that 0 isn't valid, but it _does_ work in Chrome
|
|
// depth: i32 = 3,
|
|
// pierce: bool = false,
|
|
// };
|
|
// const params = try cmd.params(Params) orelse Params{};
|
|
|
|
// if (params.pierce) {
|
|
// log.warn(.cdp, "not implemented", .{ .feature = "DOM.getDocument: Not implemented pierce parameter" });
|
|
// }
|
|
|
|
// const bc = cmd.browser_context orelse return error.BrowserContextNotLoaded;
|
|
// const page = bc.session.currentPage() orelse return error.PageNotLoaded;
|
|
// const doc = parser.documentHTMLToDocument(page.window.document);
|
|
|
|
// const node = try bc.node_registry.register(parser.documentToNode(doc));
|
|
// return cmd.sendResult(.{ .root = bc.nodeWriter(node, .{ .depth = params.depth }) }, .{});
|
|
// }
|
|
|
|
// // https://chromedevtools.github.io/devtools-protocol/tot/DOM/#method-performSearch
|
|
// fn performSearch(cmd: anytype) !void {
|
|
// const params = (try cmd.params(struct {
|
|
// query: []const u8,
|
|
// includeUserAgentShadowDOM: ?bool = null,
|
|
// })) orelse return error.InvalidParams;
|
|
|
|
// const bc = cmd.browser_context orelse return error.BrowserContextNotLoaded;
|
|
// const page = bc.session.currentPage() orelse return error.PageNotLoaded;
|
|
// const doc = parser.documentHTMLToDocument(page.window.document);
|
|
|
|
// const allocator = cmd.cdp.allocator;
|
|
// var list = try css.querySelectorAll(allocator, parser.documentToNode(doc), params.query);
|
|
// defer list.deinit(allocator);
|
|
|
|
// const search = try bc.node_search_list.create(list.nodes.items);
|
|
|
|
// // dispatch setChildNodesEvents to inform the client of the subpart of node
|
|
// // tree covering the results.
|
|
// try dispatchSetChildNodes(cmd, list.nodes.items);
|
|
|
|
// return cmd.sendResult(.{
|
|
// .searchId = search.name,
|
|
// .resultCount = @as(u32, @intCast(search.node_ids.len)),
|
|
// }, .{});
|
|
// }
|
|
|
|
// // dispatchSetChildNodes send the setChildNodes event for the whole DOM tree
|
|
// // hierarchy of each nodes.
|
|
// // 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.
|
|
// fn dispatchSetChildNodes(cmd: anytype, nodes: []*parser.Node) !void {
|
|
// const arena = cmd.arena;
|
|
// const bc = cmd.browser_context orelse return error.BrowserContextNotLoaded;
|
|
// const session_id = bc.session_id orelse return error.SessionIdNotLoaded;
|
|
|
|
// var parents: std.ArrayListUnmanaged(*Node) = .{};
|
|
// for (nodes) |_n| {
|
|
// var n = _n;
|
|
// while (true) {
|
|
// const p = parser.nodeParentNode(n) orelse break;
|
|
|
|
// // Register the node.
|
|
// const node = try bc.node_registry.register(p);
|
|
// if (node.set_child_nodes_event) break;
|
|
// try parents.append(arena, node);
|
|
// n = p;
|
|
// }
|
|
// }
|
|
|
|
// const plen = parents.items.len;
|
|
// if (plen == 0) return;
|
|
|
|
// var i: usize = plen;
|
|
// // We're going to iterate in reverse order from how we added them.
|
|
// // This ensures that we're emitting the tree of nodes top-down.
|
|
// while (i > 0) {
|
|
// i -= 1;
|
|
// const node = parents.items[i];
|
|
// // Although our above loop won't add an already-sent node to `parents`
|
|
// // this can still be true because two nodes can share the same parent node
|
|
// // so we might have just sent the node a previous iteration of this loop
|
|
// if (node.set_child_nodes_event) continue;
|
|
|
|
// node.set_child_nodes_event = true;
|
|
|
|
// // If the node has no parent, it's the root node.
|
|
// // We don't dispatch event for it because we assume the root node is
|
|
// // dispatched via the DOM.getDocument command.
|
|
// const p = parser.nodeParentNode(node._node) orelse {
|
|
// continue;
|
|
// };
|
|
|
|
// // Retrieve the parent from the registry.
|
|
// const parent_node = try bc.node_registry.register(p);
|
|
|
|
// try cmd.sendEvent("DOM.setChildNodes", .{
|
|
// .parentId = parent_node.id,
|
|
// .nodes = .{bc.nodeWriter(node, .{})},
|
|
// }, .{
|
|
// .session_id = session_id,
|
|
// });
|
|
// }
|
|
// }
|
|
|
|
// // https://chromedevtools.github.io/devtools-protocol/tot/DOM/#method-discardSearchResults
|
|
// fn discardSearchResults(cmd: anytype) !void {
|
|
// const params = (try cmd.params(struct {
|
|
// searchId: []const u8,
|
|
// })) orelse return error.InvalidParams;
|
|
|
|
// const bc = cmd.browser_context orelse return error.BrowserContextNotLoaded;
|
|
|
|
// bc.node_search_list.remove(params.searchId);
|
|
// return cmd.sendResult(null, .{});
|
|
// }
|
|
|
|
// // https://chromedevtools.github.io/devtools-protocol/tot/DOM/#method-getSearchResults
|
|
// fn getSearchResults(cmd: anytype) !void {
|
|
// const params = (try cmd.params(struct {
|
|
// searchId: []const u8,
|
|
// fromIndex: u32,
|
|
// toIndex: u32,
|
|
// })) orelse return error.InvalidParams;
|
|
|
|
// if (params.fromIndex >= params.toIndex) {
|
|
// return error.BadIndices;
|
|
// }
|
|
|
|
// const bc = cmd.browser_context orelse return error.BrowserContextNotLoaded;
|
|
|
|
// const search = bc.node_search_list.get(params.searchId) orelse {
|
|
// return error.SearchResultNotFound;
|
|
// };
|
|
|
|
// const node_ids = search.node_ids;
|
|
|
|
// if (params.fromIndex >= node_ids.len) return error.BadFromIndex;
|
|
// if (params.toIndex > node_ids.len) return error.BadToIndex;
|
|
|
|
// return cmd.sendResult(.{ .nodeIds = node_ids[params.fromIndex..params.toIndex] }, .{});
|
|
// }
|
|
|
|
// fn querySelector(cmd: anytype) !void {
|
|
// const params = (try cmd.params(struct {
|
|
// nodeId: Node.Id,
|
|
// selector: []const u8,
|
|
// })) orelse return error.InvalidParams;
|
|
|
|
// const bc = cmd.browser_context orelse return error.BrowserContextNotLoaded;
|
|
|
|
// const node = bc.node_registry.lookup_by_id.get(params.nodeId) orelse {
|
|
// return cmd.sendError(-32000, "Could not find node with given id", .{});
|
|
// };
|
|
|
|
// const selected_node = try css.querySelector(
|
|
// cmd.arena,
|
|
// node._node,
|
|
// params.selector,
|
|
// ) orelse return error.NodeNotFoundForGivenId;
|
|
|
|
// const registered_node = try bc.node_registry.register(selected_node);
|
|
|
|
// // Dispatch setChildNodesEvents to inform the client of the subpart of node tree covering the results.
|
|
// var array = [1]*parser.Node{selected_node};
|
|
// try dispatchSetChildNodes(cmd, array[0..]);
|
|
|
|
// return cmd.sendResult(.{
|
|
// .nodeId = registered_node.id,
|
|
// }, .{});
|
|
// }
|
|
|
|
// fn querySelectorAll(cmd: anytype) !void {
|
|
// const params = (try cmd.params(struct {
|
|
// nodeId: Node.Id,
|
|
// selector: []const u8,
|
|
// })) orelse return error.InvalidParams;
|
|
|
|
// const bc = cmd.browser_context orelse return error.BrowserContextNotLoaded;
|
|
|
|
// const node = bc.node_registry.lookup_by_id.get(params.nodeId) orelse {
|
|
// return cmd.sendError(-32000, "Could not find node with given id", .{});
|
|
// };
|
|
|
|
// const arena = cmd.arena;
|
|
// const selected_nodes = try css.querySelectorAll(arena, node._node, params.selector);
|
|
// const nodes = selected_nodes.nodes.items;
|
|
|
|
// const node_ids = try arena.alloc(Node.Id, nodes.len);
|
|
// for (nodes, node_ids) |selected_node, *node_id| {
|
|
// node_id.* = (try bc.node_registry.register(selected_node)).id;
|
|
// }
|
|
|
|
// // Dispatch setChildNodesEvents to inform the client of the subpart of node tree covering the results.
|
|
// try dispatchSetChildNodes(cmd, nodes);
|
|
|
|
// return cmd.sendResult(.{
|
|
// .nodeIds = node_ids,
|
|
// }, .{});
|
|
// }
|
|
|
|
// fn resolveNode(cmd: anytype) !void {
|
|
// const params = (try cmd.params(struct {
|
|
// nodeId: ?Node.Id = null,
|
|
// backendNodeId: ?u32 = null,
|
|
// objectGroup: ?[]const u8 = null,
|
|
// executionContextId: ?u32 = null,
|
|
// })) orelse return error.InvalidParams;
|
|
|
|
// const bc = cmd.browser_context orelse return error.BrowserContextNotLoaded;
|
|
// const page = bc.session.currentPage() orelse return error.PageNotLoaded;
|
|
|
|
// var js_context = page.js;
|
|
// if (params.executionContextId) |context_id| {
|
|
// if (js_context.v8_context.debugContextId() != context_id) {
|
|
// for (bc.isolated_worlds.items) |*isolated_world| {
|
|
// js_context = &(isolated_world.executor.context orelse return error.ContextNotFound);
|
|
// if (js_context.v8_context.debugContextId() == context_id) {
|
|
// break;
|
|
// }
|
|
// } else return error.ContextNotFound;
|
|
// }
|
|
// }
|
|
|
|
// const input_node_id = params.nodeId orelse params.backendNodeId orelse return error.InvalidParam;
|
|
// const node = bc.node_registry.lookup_by_id.get(input_node_id) orelse return error.UnknownNode;
|
|
|
|
// // node._node is a *parser.Node we need this to be able to find its most derived type e.g. Node -> Element -> HTMLElement
|
|
// // So we use the Node.Union when retrieve the value from the environment
|
|
// const remote_object = try bc.inspector.getRemoteObject(
|
|
// js_context,
|
|
// params.objectGroup orelse "",
|
|
// try dom_node.Node.toInterface(node._node),
|
|
// );
|
|
// defer remote_object.deinit();
|
|
|
|
// const arena = cmd.arena;
|
|
// return cmd.sendResult(.{ .object = .{
|
|
// .type = try remote_object.getType(arena),
|
|
// .subtype = try remote_object.getSubtype(arena),
|
|
// .className = try remote_object.getClassName(arena),
|
|
// .description = try remote_object.getDescription(arena),
|
|
// .objectId = try remote_object.getObjectId(arena),
|
|
// } }, .{});
|
|
// }
|
|
|
|
// fn describeNode(cmd: anytype) !void {
|
|
// const params = (try cmd.params(struct {
|
|
// nodeId: ?Node.Id = null,
|
|
// backendNodeId: ?Node.Id = null,
|
|
// objectId: ?[]const u8 = null,
|
|
// depth: i32 = 1,
|
|
// pierce: bool = false,
|
|
// })) orelse return error.InvalidParams;
|
|
|
|
// if (params.pierce) {
|
|
// log.warn(.cdp, "not implemented", .{ .feature = "DOM.describeNode: Not implemented pierce parameter" });
|
|
// }
|
|
// const bc = cmd.browser_context orelse return error.BrowserContextNotLoaded;
|
|
|
|
// const node = try getNode(cmd.arena, bc, params.nodeId, params.backendNodeId, params.objectId);
|
|
|
|
// return cmd.sendResult(.{ .node = bc.nodeWriter(node, .{ .depth = params.depth }) }, .{});
|
|
// }
|
|
|
|
// // An array of quad vertices, x immediately followed by y for each point, points clock-wise.
|
|
// // Note Y points downward
|
|
// // We are assuming the start/endpoint is not repeated.
|
|
// const Quad = [8]f64;
|
|
|
|
// const BoxModel = struct {
|
|
// content: Quad,
|
|
// padding: Quad,
|
|
// border: Quad,
|
|
// margin: Quad,
|
|
// width: i32,
|
|
// height: i32,
|
|
// // shapeOutside: ?ShapeOutsideInfo,
|
|
// };
|
|
|
|
// fn rectToQuad(rect: Element.DOMRect) Quad {
|
|
// return Quad{
|
|
// rect.x,
|
|
// rect.y,
|
|
// rect.x + rect.width,
|
|
// rect.y,
|
|
// rect.x + rect.width,
|
|
// rect.y + rect.height,
|
|
// rect.x,
|
|
// rect.y + rect.height,
|
|
// };
|
|
// }
|
|
|
|
// fn scrollIntoViewIfNeeded(cmd: anytype) !void {
|
|
// const params = (try cmd.params(struct {
|
|
// nodeId: ?Node.Id = null,
|
|
// backendNodeId: ?u32 = null,
|
|
// objectId: ?[]const u8 = null,
|
|
// rect: ?Element.DOMRect = null,
|
|
// })) orelse return error.InvalidParams;
|
|
// // Only 1 of nodeId, backendNodeId, objectId may be set, but chrome just takes the first non-null
|
|
|
|
// // We retrieve the node to at least check if it exists and is valid.
|
|
// const bc = cmd.browser_context orelse return error.BrowserContextNotLoaded;
|
|
// const node = try getNode(cmd.arena, bc, params.nodeId, params.backendNodeId, params.objectId);
|
|
|
|
// const node_type = parser.nodeType(node._node);
|
|
// switch (node_type) {
|
|
// .element => {},
|
|
// .document => {},
|
|
// .text => {},
|
|
// else => return error.NodeDoesNotHaveGeometry,
|
|
// }
|
|
|
|
// return cmd.sendResult(null, .{});
|
|
// }
|
|
|
|
// fn getNode(arena: Allocator, browser_context: 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;
|
|
// if (input_node_id) |input_node_id_| {
|
|
// return browser_context.node_registry.lookup_by_id.get(input_node_id_) orelse return error.NodeNotFound;
|
|
// }
|
|
// if (object_id) |object_id_| {
|
|
// // Retrieve the object from which ever context it is in.
|
|
// const parser_node = try browser_context.inspector.getNodePtr(arena, object_id_);
|
|
// return try browser_context.node_registry.register(@ptrCast(@alignCast(parser_node)));
|
|
// }
|
|
// return error.MissingParams;
|
|
// }
|
|
|
|
// // https://chromedevtools.github.io/devtools-protocol/tot/DOM/#method-getContentQuads
|
|
// // Related to: https://drafts.csswg.org/cssom-view/#the-geometryutils-interface
|
|
// fn getContentQuads(cmd: anytype) !void {
|
|
// const params = (try cmd.params(struct {
|
|
// nodeId: ?Node.Id = null,
|
|
// backendNodeId: ?Node.Id = null,
|
|
// objectId: ?[]const u8 = null,
|
|
// })) orelse return error.InvalidParams;
|
|
|
|
// const bc = cmd.browser_context orelse return error.BrowserContextNotLoaded;
|
|
// const page = bc.session.currentPage() orelse return error.PageNotLoaded;
|
|
|
|
// const node = try getNode(cmd.arena, bc, params.nodeId, params.backendNodeId, params.objectId);
|
|
|
|
// // TODO likely if the following CSS properties are set the quads should be empty
|
|
// // visibility: hidden
|
|
// // display: none
|
|
|
|
// if (parser.nodeType(node._node) != .element) return error.NodeIsNotAnElement;
|
|
// // TODO implement for document or text
|
|
// // Most likely document would require some hierachgy in the renderer. It is left unimplemented till we have a good example.
|
|
// // Text may be tricky, multiple quads in case of multiple lines? empty quads of text = ""?
|
|
// // Elements like SVGElement may have multiple quads.
|
|
|
|
// const element = parser.nodeToElement(node._node);
|
|
// const rect = try Element._getBoundingClientRect(element, page);
|
|
// const quad = rectToQuad(rect);
|
|
|
|
// return cmd.sendResult(.{ .quads = &.{quad} }, .{});
|
|
// }
|
|
|
|
// fn getBoxModel(cmd: anytype) !void {
|
|
// const params = (try cmd.params(struct {
|
|
// nodeId: ?Node.Id = null,
|
|
// backendNodeId: ?u32 = null,
|
|
// objectId: ?[]const u8 = null,
|
|
// })) orelse return error.InvalidParams;
|
|
|
|
// const bc = cmd.browser_context orelse return error.BrowserContextNotLoaded;
|
|
// const page = bc.session.currentPage() orelse return error.PageNotLoaded;
|
|
|
|
// const node = try getNode(cmd.arena, bc, params.nodeId, params.backendNodeId, params.objectId);
|
|
|
|
// // TODO implement for document or text
|
|
// if (parser.nodeType(node._node) != .element) return error.NodeIsNotAnElement;
|
|
// const element = parser.nodeToElement(node._node);
|
|
|
|
// const rect = try Element._getBoundingClientRect(element, page);
|
|
// const quad = rectToQuad(rect);
|
|
|
|
// return cmd.sendResult(.{ .model = BoxModel{
|
|
// .content = quad,
|
|
// .padding = quad,
|
|
// .border = quad,
|
|
// .margin = quad,
|
|
// .width = @intFromFloat(rect.width),
|
|
// .height = @intFromFloat(rect.height),
|
|
// } }, .{});
|
|
// }
|
|
|
|
// fn requestChildNodes(cmd: anytype) !void {
|
|
// const params = (try cmd.params(struct {
|
|
// nodeId: Node.Id,
|
|
// depth: i32 = 1,
|
|
// pierce: bool = false,
|
|
// })) orelse return error.InvalidParams;
|
|
|
|
// if (params.depth == 0) return error.InvalidParams;
|
|
// const bc = cmd.browser_context orelse return error.BrowserContextNotLoaded;
|
|
// const session_id = bc.session_id orelse return error.SessionIdNotLoaded;
|
|
// const node = bc.node_registry.lookup_by_id.get(params.nodeId) orelse {
|
|
// return error.InvalidNode;
|
|
// };
|
|
|
|
// try cmd.sendEvent("DOM.setChildNodes", .{
|
|
// .parentId = node.id,
|
|
// .nodes = bc.nodeWriter(node, .{ .depth = params.depth, .exclude_root = true }),
|
|
// }, .{
|
|
// .session_id = session_id,
|
|
// });
|
|
|
|
// return cmd.sendResult(null, .{});
|
|
// }
|
|
|
|
// fn getFrameOwner(cmd: anytype) !void {
|
|
// const params = (try cmd.params(struct {
|
|
// frameId: []const u8,
|
|
// })) 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 = bc.session.currentPage() orelse return error.PageNotLoaded;
|
|
// const doc = parser.documentHTMLToDocument(page.window.document);
|
|
|
|
// const node = try bc.node_registry.register(parser.documentToNode(doc));
|
|
// return cmd.sendResult(.{ .nodeId = node.id, .backendNodeId = node.id }, .{});
|
|
// }
|
|
|
|
// const testing = @import("../testing.zig");
|
|
|
|
// test "cdp.dom: getSearchResults unknown search id" {
|
|
// var ctx = testing.context();
|
|
// defer ctx.deinit();
|
|
|
|
// try testing.expectError(error.BrowserContextNotLoaded, ctx.processMessage(.{
|
|
// .id = 8,
|
|
// .method = "DOM.getSearchResults",
|
|
// .params = .{ .searchId = "Nope", .fromIndex = 0, .toIndex = 10 },
|
|
// }));
|
|
// }
|
|
|
|
// test "cdp.dom: search flow" {
|
|
// var ctx = testing.context();
|
|
// defer ctx.deinit();
|
|
|
|
// _ = try ctx.loadBrowserContext(.{ .id = "BID-A", .html = "<p>1</p> <p>2</p>" });
|
|
|
|
// try ctx.processMessage(.{
|
|
// .id = 12,
|
|
// .method = "DOM.performSearch",
|
|
// .params = .{ .query = "p" },
|
|
// });
|
|
// try ctx.expectSentResult(.{ .searchId = "0", .resultCount = 2 }, .{ .id = 12 });
|
|
|
|
// {
|
|
// // getSearchResults
|
|
// try ctx.processMessage(.{
|
|
// .id = 13,
|
|
// .method = "DOM.getSearchResults",
|
|
// .params = .{ .searchId = "0", .fromIndex = 0, .toIndex = 2 },
|
|
// });
|
|
// try ctx.expectSentResult(.{ .nodeIds = &.{ 1, 2 } }, .{ .id = 13 });
|
|
|
|
// // different fromIndex
|
|
// try ctx.processMessage(.{
|
|
// .id = 14,
|
|
// .method = "DOM.getSearchResults",
|
|
// .params = .{ .searchId = "0", .fromIndex = 1, .toIndex = 2 },
|
|
// });
|
|
// try ctx.expectSentResult(.{ .nodeIds = &.{2} }, .{ .id = 14 });
|
|
|
|
// // different toIndex
|
|
// try ctx.processMessage(.{
|
|
// .id = 15,
|
|
// .method = "DOM.getSearchResults",
|
|
// .params = .{ .searchId = "0", .fromIndex = 0, .toIndex = 1 },
|
|
// });
|
|
// try ctx.expectSentResult(.{ .nodeIds = &.{1} }, .{ .id = 15 });
|
|
// }
|
|
|
|
// try ctx.processMessage(.{
|
|
// .id = 16,
|
|
// .method = "DOM.discardSearchResults",
|
|
// .params = .{ .searchId = "0" },
|
|
// });
|
|
// try ctx.expectSentResult(null, .{ .id = 16 });
|
|
|
|
// // make sure the delete actually did something
|
|
// try testing.expectError(error.SearchResultNotFound, ctx.processMessage(.{
|
|
// .id = 17,
|
|
// .method = "DOM.getSearchResults",
|
|
// .params = .{ .searchId = "0", .fromIndex = 0, .toIndex = 1 },
|
|
// }));
|
|
// }
|
|
|
|
// test "cdp.dom: querySelector unknown search id" {
|
|
// var ctx = testing.context();
|
|
// defer ctx.deinit();
|
|
|
|
// _ = try ctx.loadBrowserContext(.{ .id = "BID-A", .html = "<p>1</p> <p>2</p>" });
|
|
|
|
// try ctx.processMessage(.{
|
|
// .id = 9,
|
|
// .method = "DOM.querySelector",
|
|
// .params = .{ .nodeId = 99, .selector = "" },
|
|
// });
|
|
// try ctx.expectSentError(-32000, "Could not find node with given id", .{});
|
|
|
|
// try ctx.processMessage(.{
|
|
// .id = 9,
|
|
// .method = "DOM.querySelectorAll",
|
|
// .params = .{ .nodeId = 99, .selector = "" },
|
|
// });
|
|
// try ctx.expectSentError(-32000, "Could not find node with given id", .{});
|
|
// }
|
|
|
|
// test "cdp.dom: querySelector Node not found" {
|
|
// var ctx = testing.context();
|
|
// defer ctx.deinit();
|
|
|
|
// _ = try ctx.loadBrowserContext(.{ .id = "BID-A", .html = "<p>1</p> <p>2</p>" });
|
|
|
|
// try ctx.processMessage(.{ // Hacky way to make sure nodeId 1 exists in the registry
|
|
// .id = 3,
|
|
// .method = "DOM.performSearch",
|
|
// .params = .{ .query = "p" },
|
|
// });
|
|
// try ctx.expectSentResult(.{ .searchId = "0", .resultCount = 2 }, .{ .id = 3 });
|
|
|
|
// try testing.expectError(error.NodeNotFoundForGivenId, ctx.processMessage(.{
|
|
// .id = 4,
|
|
// .method = "DOM.querySelector",
|
|
// .params = .{ .nodeId = 1, .selector = "a" },
|
|
// }));
|
|
|
|
// try ctx.processMessage(.{
|
|
// .id = 5,
|
|
// .method = "DOM.querySelectorAll",
|
|
// .params = .{ .nodeId = 1, .selector = "a" },
|
|
// });
|
|
// try ctx.expectSentResult(.{ .nodeIds = &[_]u32{} }, .{ .id = 5 });
|
|
// }
|
|
|
|
// test "cdp.dom: querySelector Nodes found" {
|
|
// var ctx = testing.context();
|
|
// defer ctx.deinit();
|
|
|
|
// _ = try ctx.loadBrowserContext(.{ .id = "BID-A", .html = "<div><p>2</p></div>" });
|
|
|
|
// try ctx.processMessage(.{ // Hacky way to make sure nodeId 1 exists in the registry
|
|
// .id = 3,
|
|
// .method = "DOM.performSearch",
|
|
// .params = .{ .query = "div" },
|
|
// });
|
|
// try ctx.expectSentResult(.{ .searchId = "0", .resultCount = 1 }, .{ .id = 3 });
|
|
|
|
// try ctx.processMessage(.{
|
|
// .id = 4,
|
|
// .method = "DOM.querySelector",
|
|
// .params = .{ .nodeId = 1, .selector = "p" },
|
|
// });
|
|
// try ctx.expectSentEvent("DOM.setChildNodes", null, .{});
|
|
// try ctx.expectSentResult(.{ .nodeId = 6 }, .{ .id = 4 });
|
|
|
|
// try ctx.processMessage(.{
|
|
// .id = 5,
|
|
// .method = "DOM.querySelectorAll",
|
|
// .params = .{ .nodeId = 1, .selector = "p" },
|
|
// });
|
|
// try ctx.expectSentEvent("DOM.setChildNodes", null, .{});
|
|
// try ctx.expectSentResult(.{ .nodeIds = &.{6} }, .{ .id = 5 });
|
|
// }
|
|
|
|
// test "cdp.dom: getBoxModel" {
|
|
// var ctx = testing.context();
|
|
// defer ctx.deinit();
|
|
|
|
// _ = try ctx.loadBrowserContext(.{ .id = "BID-A", .html = "<div><p>2</p></div>" });
|
|
|
|
// try ctx.processMessage(.{ // Hacky way to make sure nodeId 1 exists in the registry
|
|
// .id = 3,
|
|
// .method = "DOM.getDocument",
|
|
// });
|
|
|
|
// try ctx.processMessage(.{
|
|
// .id = 4,
|
|
// .method = "DOM.querySelector",
|
|
// .params = .{ .nodeId = 1, .selector = "p" },
|
|
// });
|
|
// try ctx.expectSentResult(.{ .nodeId = 3 }, .{ .id = 4 });
|
|
|
|
// try ctx.processMessage(.{
|
|
// .id = 5,
|
|
// .method = "DOM.getBoxModel",
|
|
// .params = .{ .nodeId = 6 },
|
|
// });
|
|
// try ctx.expectSentResult(.{ .model = BoxModel{
|
|
// .content = Quad{ 0.0, 0.0, 1.0, 0.0, 1.0, 1.0, 0.0, 1.0 },
|
|
// .padding = Quad{ 0.0, 0.0, 1.0, 0.0, 1.0, 1.0, 0.0, 1.0 },
|
|
// .border = Quad{ 0.0, 0.0, 1.0, 0.0, 1.0, 1.0, 0.0, 1.0 },
|
|
// .margin = Quad{ 0.0, 0.0, 1.0, 0.0, 1.0, 1.0, 0.0, 1.0 },
|
|
// .width = 1,
|
|
// .height = 1,
|
|
// } }, .{ .id = 5 });
|
|
// }
|