mirror of
https://github.com/lightpanda-io/browser.git
synced 2025-10-29 07:03:29 +00:00
Merge branch 'main' into cdp_struct
This commit is contained in:
@@ -98,6 +98,12 @@ pub const Browser = struct {
|
||||
self.session = null;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn currentPage(self: *Browser) ?*Page {
|
||||
if (self.session.page == null) return null;
|
||||
|
||||
return &self.session.page.?;
|
||||
}
|
||||
};
|
||||
|
||||
// Session is like a browser's tab.
|
||||
@@ -239,6 +245,7 @@ pub const Page = struct {
|
||||
}
|
||||
|
||||
pub fn deinit(self: *Page) void {
|
||||
self.end();
|
||||
self.arena.deinit();
|
||||
self.session.page = null;
|
||||
}
|
||||
@@ -276,6 +283,7 @@ pub const Page = struct {
|
||||
self.session.window.replaceLocation(&self.location) catch |e| {
|
||||
log.err("reset window location: {any}", .{e});
|
||||
};
|
||||
self.doc = null;
|
||||
|
||||
// clear netsurf memory arena.
|
||||
parser.deinit();
|
||||
|
||||
@@ -20,6 +20,7 @@ const std = @import("std");
|
||||
const Allocator = std.mem.Allocator;
|
||||
const json = std.json;
|
||||
|
||||
const dom = @import("dom.zig");
|
||||
const Loop = @import("jsruntime").Loop;
|
||||
const Client = @import("../server.zig").Client;
|
||||
const asUint = @import("../str/parser.zig").asUint;
|
||||
@@ -64,6 +65,8 @@ pub const CDP = struct {
|
||||
security_origin: []const u8,
|
||||
page_life_cycle_events: bool,
|
||||
secure_context_type: []const u8,
|
||||
node_list: dom.NodeList,
|
||||
node_search_list: dom.NodeSearchList,
|
||||
|
||||
pub fn init(allocator: Allocator, client: *Client, loop: *Loop) CDP {
|
||||
return .{
|
||||
@@ -81,14 +84,32 @@ pub const CDP = struct {
|
||||
.loader_id = LOADER_ID,
|
||||
.message_arena = std.heap.ArenaAllocator.init(allocator),
|
||||
.page_life_cycle_events = false, // TODO; Target based value
|
||||
.node_list = dom.NodeList.init(allocator),
|
||||
.node_search_list = dom.NodeSearchList.init(allocator),
|
||||
};
|
||||
}
|
||||
|
||||
pub fn deinit(self: *CDP) void {
|
||||
self.node_list.deinit();
|
||||
for (self.node_search_list.items) |*s| {
|
||||
s.deinit();
|
||||
}
|
||||
self.node_search_list.deinit();
|
||||
|
||||
self.browser.deinit();
|
||||
self.message_arena.deinit();
|
||||
}
|
||||
|
||||
pub fn reset(self: *CDP) void {
|
||||
self.node_list.reset();
|
||||
|
||||
// deinit all node searches.
|
||||
for (self.node_search_list.items) |*s| {
|
||||
s.deinit();
|
||||
}
|
||||
self.node_search_list.clearAndFree();
|
||||
}
|
||||
|
||||
pub fn newSession(self: *CDP) !void {
|
||||
self.session = try self.browser.newSession(self);
|
||||
}
|
||||
|
||||
232
src/cdp/dom.zig
232
src/cdp/dom.zig
@@ -18,13 +18,245 @@
|
||||
|
||||
const std = @import("std");
|
||||
const cdp = @import("cdp.zig");
|
||||
const css = @import("../dom/css.zig");
|
||||
|
||||
const parser = @import("netsurf");
|
||||
|
||||
pub fn processMessage(cmd: anytype) !void {
|
||||
const action = std.meta.stringToEnum(enum {
|
||||
enable,
|
||||
getDocument,
|
||||
performSearch,
|
||||
getSearchResults,
|
||||
discardSearchResults,
|
||||
}, cmd.action) orelse return error.UnknownMethod;
|
||||
|
||||
switch (action) {
|
||||
.enable => return cmd.sendResult(null, .{}),
|
||||
.getDocument => return getDocument(cmd),
|
||||
.performSearch => return performSearch(cmd),
|
||||
.getSearchResults => return getSearchResults(cmd),
|
||||
.discardSearchResults => return discardSearchResults(cmd),
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// NodeList references tree nodes with an array id.
|
||||
pub const NodeList = struct {
|
||||
coll: List,
|
||||
|
||||
const List = std.ArrayList(*parser.Node);
|
||||
|
||||
pub fn init(alloc: std.mem.Allocator) NodeList {
|
||||
return .{
|
||||
.coll = List.init(alloc),
|
||||
};
|
||||
}
|
||||
|
||||
pub fn deinit(self: *NodeList) void {
|
||||
self.coll.deinit();
|
||||
}
|
||||
|
||||
pub fn reset(self: *NodeList) void {
|
||||
self.coll.clearAndFree();
|
||||
}
|
||||
|
||||
pub fn set(self: *NodeList, node: *parser.Node) !NodeId {
|
||||
const coll = &self.coll;
|
||||
for (coll.items, 0..) |n, i| {
|
||||
if (n == node) {
|
||||
return @intCast(i);
|
||||
}
|
||||
}
|
||||
|
||||
try coll.append(node);
|
||||
return @intCast(coll.items.len);
|
||||
}
|
||||
};
|
||||
|
||||
const NodeId = u32;
|
||||
|
||||
const Node = struct {
|
||||
nodeId: NodeId,
|
||||
parentId: ?NodeId = null,
|
||||
backendNodeId: NodeId,
|
||||
nodeType: u32,
|
||||
nodeName: []const u8 = "",
|
||||
localName: []const u8 = "",
|
||||
nodeValue: []const u8 = "",
|
||||
childNodeCount: ?u32 = null,
|
||||
children: ?[]const Node = null,
|
||||
documentURL: ?[]const u8 = null,
|
||||
baseURL: ?[]const u8 = null,
|
||||
xmlVersion: []const u8 = "",
|
||||
compatibilityMode: []const u8 = "NoQuirksMode",
|
||||
isScrollable: bool = false,
|
||||
|
||||
fn init(n: *parser.Node, nlist: *NodeList) !Node {
|
||||
const id = try nlist.set(n);
|
||||
return .{
|
||||
.nodeId = id,
|
||||
.backendNodeId = id,
|
||||
.nodeType = @intFromEnum(try parser.nodeType(n)),
|
||||
.nodeName = try parser.nodeName(n),
|
||||
.localName = try parser.nodeLocalName(n),
|
||||
.nodeValue = try parser.nodeValue(n) orelse "",
|
||||
};
|
||||
}
|
||||
|
||||
fn initChildren(
|
||||
self: *Node,
|
||||
alloc: std.mem.Allocator,
|
||||
n: *parser.Node,
|
||||
nlist: *NodeList,
|
||||
) !std.ArrayList(Node) {
|
||||
const children = try parser.nodeGetChildNodes(n);
|
||||
const ln = try parser.nodeListLength(children);
|
||||
self.childNodeCount = ln;
|
||||
|
||||
var list = try std.ArrayList(Node).initCapacity(alloc, ln);
|
||||
|
||||
for (0..ln) |i| {
|
||||
const child = try parser.nodeListItem(children, @intCast(i)) orelse continue;
|
||||
list.appendAssumeCapacity(try Node.init(child, nlist));
|
||||
}
|
||||
|
||||
self.children = list.items;
|
||||
|
||||
return list;
|
||||
}
|
||||
};
|
||||
|
||||
// https://chromedevtools.github.io/devtools-protocol/tot/DOM/#method-getDocument
|
||||
fn getDocument(cmd: anytype) !void {
|
||||
// const params = (try cmd.params(struct {
|
||||
// depth: ?u32 = null,
|
||||
// pierce: ?bool = null,
|
||||
// })) orelse return error.InvalidParams;
|
||||
|
||||
// retrieve the root node
|
||||
const page = cmd.session.page orelse return error.NoPage;
|
||||
const doc = page.doc orelse return error.NoDocument;
|
||||
|
||||
const state = cmd.cdp;
|
||||
const node = parser.documentToNode(doc);
|
||||
var n = try Node.init(node, &state.node_list);
|
||||
_ = try n.initChildren(cmd.arena, node, &state.node_list);
|
||||
|
||||
return cmd.sendResult(.{
|
||||
.root = n,
|
||||
}, .{});
|
||||
}
|
||||
|
||||
pub const NodeSearch = struct {
|
||||
coll: List,
|
||||
name: []u8,
|
||||
alloc: std.mem.Allocator,
|
||||
|
||||
var count: u8 = 0;
|
||||
|
||||
const List = std.ArrayListUnmanaged(NodeId);
|
||||
|
||||
pub fn initCapacity(alloc: std.mem.Allocator, ln: usize) !NodeSearch {
|
||||
count += 1;
|
||||
|
||||
return .{
|
||||
.alloc = alloc,
|
||||
.coll = try List.initCapacity(alloc, ln),
|
||||
.name = try std.fmt.allocPrint(alloc, "{d}", .{count}),
|
||||
};
|
||||
}
|
||||
|
||||
pub fn deinit(self: *NodeSearch) void {
|
||||
self.coll.deinit(self.alloc);
|
||||
self.alloc.free(self.name);
|
||||
}
|
||||
|
||||
pub fn append(self: *NodeSearch, id: NodeId) !void {
|
||||
try self.coll.append(self.alloc, id);
|
||||
}
|
||||
};
|
||||
pub const NodeSearchList = std.ArrayList(NodeSearch);
|
||||
|
||||
// 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;
|
||||
|
||||
// retrieve the root node
|
||||
const page = cmd.session.page orelse return error.NoPage;
|
||||
const doc = page.doc orelse return error.NoDocument;
|
||||
|
||||
const list = try css.querySelectorAll(cmd.cdp.allocator, parser.documentToNode(doc), params.query);
|
||||
const ln = list.nodes.items.len;
|
||||
var ns = try NodeSearch.initCapacity(cmd.cdp.allocator, ln);
|
||||
|
||||
var state = cmd.cdp;
|
||||
for (list.nodes.items) |n| {
|
||||
const id = try state.node_list.set(n);
|
||||
try ns.append(id);
|
||||
}
|
||||
|
||||
try state.node_search_list.append(ns);
|
||||
|
||||
return cmd.sendResult(.{
|
||||
.searchId = ns.name,
|
||||
.resultCount = @as(u32, @intCast(ln)),
|
||||
}, .{});
|
||||
}
|
||||
|
||||
// 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;
|
||||
|
||||
var state = cmd.cdp;
|
||||
// retrieve the search from context
|
||||
for (state.node_search_list.items, 0..) |*s, i| {
|
||||
if (!std.mem.eql(u8, s.name, params.searchId)) continue;
|
||||
|
||||
s.deinit();
|
||||
_ = state.node_search_list.swapRemove(i);
|
||||
break;
|
||||
}
|
||||
|
||||
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 state = cmd.cdp;
|
||||
// retrieve the search from context
|
||||
var ns: ?*const NodeSearch = undefined;
|
||||
for (state.node_search_list.items) |s| {
|
||||
if (!std.mem.eql(u8, s.name, params.searchId)) continue;
|
||||
ns = &s;
|
||||
break;
|
||||
}
|
||||
|
||||
if (ns == null) {
|
||||
return error.searchResultNotFound;
|
||||
}
|
||||
|
||||
const items = ns.?.coll.items;
|
||||
|
||||
if (params.fromIndex >= items.len) return error.BadFromIndex;
|
||||
if (params.toIndex > items.len) return error.BadToIndex;
|
||||
|
||||
return cmd.sendResult(.{
|
||||
.nodeIds = ns.?.coll.items[params.fromIndex..params.toIndex]
|
||||
}, .{});
|
||||
}
|
||||
|
||||
@@ -178,6 +178,7 @@ fn navigate(cmd: anytype) !void {
|
||||
|
||||
// change state
|
||||
var state = cmd.cdp;
|
||||
state.reset();
|
||||
state.url = params.url;
|
||||
|
||||
// TODO: hard coded ID
|
||||
@@ -264,6 +265,9 @@ fn navigate(cmd: anytype) !void {
|
||||
try cmd.sendEvent("Page.lifecycleEvent", life_event, .{ .session_id = session_id });
|
||||
}
|
||||
|
||||
|
||||
try cmd.sendEvent("DOM.documentUpdated", null, .{.session_id = session_id});
|
||||
|
||||
// frameNavigated event
|
||||
try cmd.sendEvent("Page.frameNavigated", .{
|
||||
.type = "Navigation",
|
||||
|
||||
@@ -1008,6 +1008,7 @@ pub fn nodeLocalName(node: *Node) ![]const u8 {
|
||||
var s: ?*String = undefined;
|
||||
const err = nodeVtable(node).dom_node_get_local_name.?(node, &s);
|
||||
try DOMErr(err);
|
||||
if (s == null) return "";
|
||||
var s_lower: ?*String = undefined;
|
||||
const errStr = c.dom_string_tolower(s, true, &s_lower);
|
||||
try DOMErr(errStr);
|
||||
@@ -1098,6 +1099,7 @@ pub fn nodeName(node: *Node) ![]const u8 {
|
||||
var s: ?*String = undefined;
|
||||
const err = nodeVtable(node).dom_node_get_node_name.?(node, &s);
|
||||
try DOMErr(err);
|
||||
if (s == null) return "";
|
||||
return strToData(s.?);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user