mirror of
https://github.com/lightpanda-io/browser.git
synced 2025-10-28 22:53:28 +00:00
Initial commit
Signed-off-by: Francis Bouvier <francis.bouvier@gmail.com>
This commit is contained in:
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
zig-cache
|
||||||
|
zig-out
|
||||||
|
vendor
|
||||||
77
build.zig
Normal file
77
build.zig
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
const std = @import("std");
|
||||||
|
|
||||||
|
const jsruntime_path: []const u8 = "vendor/jsruntime-lib/";
|
||||||
|
const jsruntime_pkgs = @import("vendor/jsruntime-lib/build.zig").packages(jsruntime_path);
|
||||||
|
|
||||||
|
pub fn build(b: *std.build.Builder) !void {
|
||||||
|
const target = b.standardTargetOptions(.{});
|
||||||
|
const mode = b.standardReleaseOptions();
|
||||||
|
|
||||||
|
// browser
|
||||||
|
// -------
|
||||||
|
|
||||||
|
// compile and install
|
||||||
|
const exe = b.addExecutable("browsercore", "src/main.zig");
|
||||||
|
try common(exe, mode, target);
|
||||||
|
exe.install();
|
||||||
|
|
||||||
|
// run
|
||||||
|
const run_cmd = exe.run();
|
||||||
|
run_cmd.step.dependOn(b.getInstallStep());
|
||||||
|
if (b.args) |args| {
|
||||||
|
run_cmd.addArgs(args);
|
||||||
|
}
|
||||||
|
|
||||||
|
// step
|
||||||
|
const run_step = b.step("run", "Run the app");
|
||||||
|
run_step.dependOn(&run_cmd.step);
|
||||||
|
|
||||||
|
// shell
|
||||||
|
// -----
|
||||||
|
|
||||||
|
// compile and install
|
||||||
|
const shell = b.addExecutable("browsercore-shell", "src/main_shell.zig");
|
||||||
|
try common(shell, mode, target);
|
||||||
|
try jsruntime_pkgs.add_shell(shell, mode);
|
||||||
|
// do not install shell binary
|
||||||
|
shell.install();
|
||||||
|
|
||||||
|
// run
|
||||||
|
const shell_cmd = shell.run();
|
||||||
|
shell_cmd.step.dependOn(b.getInstallStep());
|
||||||
|
if (b.args) |args| {
|
||||||
|
shell_cmd.addArgs(args);
|
||||||
|
}
|
||||||
|
|
||||||
|
// step
|
||||||
|
const shell_step = b.step("shell", "Run JS shell");
|
||||||
|
shell_step.dependOn(&shell_cmd.step);
|
||||||
|
|
||||||
|
// test
|
||||||
|
// ----
|
||||||
|
|
||||||
|
// compile
|
||||||
|
const exe_tests = b.addTest("src/run_tests.zig");
|
||||||
|
try common(exe_tests, mode, target);
|
||||||
|
|
||||||
|
// step
|
||||||
|
const test_step = b.step("test", "Run unit tests");
|
||||||
|
test_step.dependOn(&exe_tests.step);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn common(
|
||||||
|
step: *std.build.LibExeObjStep,
|
||||||
|
mode: std.builtin.Mode,
|
||||||
|
target: std.zig.CrossTarget,
|
||||||
|
) !void {
|
||||||
|
step.setTarget(target);
|
||||||
|
step.setBuildMode(mode);
|
||||||
|
try jsruntime_pkgs.add(step, mode);
|
||||||
|
linkLexbor(step);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn linkLexbor(step: *std.build.LibExeObjStep) void {
|
||||||
|
const lib_path = "../lexbor/liblexbor_static.a";
|
||||||
|
step.addObjectFile(lib_path);
|
||||||
|
step.addIncludePath("../lexbor/source");
|
||||||
|
}
|
||||||
24
src/dom.zig
Normal file
24
src/dom.zig
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
const Console = @import("jsruntime").Console;
|
||||||
|
|
||||||
|
pub const EventTarget = @import("dom/event_target.zig").EventTarget;
|
||||||
|
pub const Node = @import("dom/node.zig").Node;
|
||||||
|
|
||||||
|
pub const Element = @import("dom/element.zig").Element;
|
||||||
|
pub const HTMLElement = @import("dom/element.zig").HTMLElement;
|
||||||
|
pub const HTMLBodyElement = @import("dom/element.zig").HTMLBodyElement;
|
||||||
|
|
||||||
|
pub const Document = @import("dom/document.zig").Document;
|
||||||
|
pub const HTMLDocument = @import("dom/document.zig").HTMLDocument;
|
||||||
|
|
||||||
|
pub const Interfaces = .{
|
||||||
|
Console,
|
||||||
|
EventTarget,
|
||||||
|
Node,
|
||||||
|
|
||||||
|
Element,
|
||||||
|
HTMLElement,
|
||||||
|
HTMLBodyElement,
|
||||||
|
|
||||||
|
Document,
|
||||||
|
HTMLDocument,
|
||||||
|
};
|
||||||
122
src/dom/document.zig
Normal file
122
src/dom/document.zig
Normal file
@@ -0,0 +1,122 @@
|
|||||||
|
const std = @import("std");
|
||||||
|
|
||||||
|
const jsruntime = @import("jsruntime");
|
||||||
|
const Case = jsruntime.test_utils.Case;
|
||||||
|
const checkCases = jsruntime.test_utils.checkCases;
|
||||||
|
|
||||||
|
const parser = @import("../parser.zig");
|
||||||
|
|
||||||
|
const DOM = @import("../dom.zig");
|
||||||
|
const Node = DOM.Node;
|
||||||
|
const Element = DOM.Element;
|
||||||
|
const HTMLElement = DOM.HTMLElement;
|
||||||
|
const HTMLBodyElement = DOM.HTMLBodyElement;
|
||||||
|
|
||||||
|
pub const Document = struct {
|
||||||
|
proto: Node,
|
||||||
|
base: ?*parser.Document,
|
||||||
|
|
||||||
|
pub const prototype = *Node;
|
||||||
|
|
||||||
|
pub fn init(base: ?*parser.Document) Document {
|
||||||
|
return .{
|
||||||
|
.proto = Node.init(null),
|
||||||
|
.base = base,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn constructor() Document {
|
||||||
|
return Document.init(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn getElementById(self: Document, elem_dom: *parser.Element, id: []const u8) ?Element {
|
||||||
|
if (self.base == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const collection = parser.collectionInit(self.base.?, 1);
|
||||||
|
defer parser.collectionDeinit(collection);
|
||||||
|
const case_sensitve = true;
|
||||||
|
parser.elementsByAttr(elem_dom, collection, "id", id, case_sensitve) catch |err| {
|
||||||
|
std.debug.print("getElementById error: {s}\n", .{@errorName(err)});
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
if (collection.array.length == 0) {
|
||||||
|
// no results
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const element_base = parser.collectionElement(collection, 0);
|
||||||
|
return Element.init(element_base);
|
||||||
|
}
|
||||||
|
|
||||||
|
// JS funcs
|
||||||
|
// --------
|
||||||
|
|
||||||
|
pub fn get_body(_: Document) ?void {
|
||||||
|
// TODO
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn _getElementById(_: Document, _: []u8) ?Element {
|
||||||
|
// TODO
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const HTMLDocument = struct {
|
||||||
|
proto: Document,
|
||||||
|
base: *parser.DocumentHTML,
|
||||||
|
|
||||||
|
pub const prototype = *Document;
|
||||||
|
|
||||||
|
pub fn init() HTMLDocument {
|
||||||
|
return .{
|
||||||
|
.proto = Document.init(null),
|
||||||
|
.base = parser.documentHTMLInit(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn deinit(self: HTMLDocument) void {
|
||||||
|
parser.documentHTMLDeinit(self.base);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn parse(self: *HTMLDocument, html: []const u8) !void {
|
||||||
|
try parser.documentHTMLParse(self.base, html);
|
||||||
|
self.proto.base = parser.documentHTMLToDocument(self.base);
|
||||||
|
}
|
||||||
|
|
||||||
|
// JS funcs
|
||||||
|
// --------
|
||||||
|
|
||||||
|
pub fn get_body(self: HTMLDocument) ?HTMLBodyElement {
|
||||||
|
const body_dom = parser.documentHTMLBody(self.base);
|
||||||
|
return HTMLBodyElement.init(body_dom);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn _getElementById(self: HTMLDocument, id: []u8) ?HTMLElement {
|
||||||
|
const body_dom = parser.documentHTMLBody(self.base);
|
||||||
|
if (self.proto.getElementById(body_dom, id)) |elem| {
|
||||||
|
return HTMLElement.init(elem.base);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn testExecFn(
|
||||||
|
js_env: *jsruntime.Env,
|
||||||
|
comptime _: []jsruntime.API,
|
||||||
|
) !void {
|
||||||
|
var constructor = [_]Case{
|
||||||
|
.{ .src = "document.__proto__.constructor.name", .ex = "HTMLDocument" },
|
||||||
|
.{ .src = "document.__proto__.__proto__.constructor.name", .ex = "Document" },
|
||||||
|
.{ .src = "document.__proto__.__proto__.__proto__.constructor.name", .ex = "Node" },
|
||||||
|
.{ .src = "document.__proto__.__proto__.__proto__.__proto__.constructor.name", .ex = "EventTarget" },
|
||||||
|
};
|
||||||
|
try checkCases(js_env, &constructor);
|
||||||
|
|
||||||
|
var getElementById = [_]Case{
|
||||||
|
.{ .src = "let getElementById = document.getElementById('content')", .ex = "undefined" },
|
||||||
|
.{ .src = "getElementById.constructor.name", .ex = "HTMLElement" },
|
||||||
|
.{ .src = "getElementById.localName", .ex = "main" },
|
||||||
|
};
|
||||||
|
try checkCases(js_env, &getElementById);
|
||||||
|
}
|
||||||
54
src/dom/element.zig
Normal file
54
src/dom/element.zig
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
const std = @import("std");
|
||||||
|
|
||||||
|
const jsruntime = @import("jsruntime");
|
||||||
|
const Case = jsruntime.test_utils.Case;
|
||||||
|
const checkCases = jsruntime.test_utils.checkCases;
|
||||||
|
|
||||||
|
const parser = @import("../parser.zig");
|
||||||
|
|
||||||
|
const DOM = @import("../dom.zig");
|
||||||
|
const Node = DOM.Node;
|
||||||
|
|
||||||
|
pub const Element = struct {
|
||||||
|
proto: Node,
|
||||||
|
base: *parser.Element,
|
||||||
|
|
||||||
|
pub const prototype = *Node;
|
||||||
|
|
||||||
|
pub fn init(base: *parser.Element) Element {
|
||||||
|
return .{
|
||||||
|
.proto = Node.init(null),
|
||||||
|
.base = base,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// JS funcs
|
||||||
|
// --------
|
||||||
|
|
||||||
|
pub fn get_localName(self: Element) []const u8 {
|
||||||
|
return parser.elementLocalName(self.base);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// HTML elements
|
||||||
|
// -------------
|
||||||
|
|
||||||
|
pub const HTMLElement = struct {
|
||||||
|
proto: Element,
|
||||||
|
|
||||||
|
pub const prototype = *Element;
|
||||||
|
|
||||||
|
pub fn init(elem_base: *parser.Element) HTMLElement {
|
||||||
|
return .{ .proto = Element.init(elem_base) };
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const HTMLBodyElement = struct {
|
||||||
|
proto: HTMLElement,
|
||||||
|
|
||||||
|
pub const prototype = *HTMLElement;
|
||||||
|
|
||||||
|
pub fn init(elem_base: *parser.Element) HTMLBodyElement {
|
||||||
|
return .{ .proto = HTMLElement.init(elem_base) };
|
||||||
|
}
|
||||||
|
};
|
||||||
13
src/dom/event_target.zig
Normal file
13
src/dom/event_target.zig
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
const parser = @import("../parser.zig");
|
||||||
|
|
||||||
|
pub const EventTarget = struct {
|
||||||
|
base: ?*parser.EventTarget = null,
|
||||||
|
|
||||||
|
pub fn init(base: ?*parser.EventTarget) EventTarget {
|
||||||
|
return .{ .base = base };
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn constructor() EventTarget {
|
||||||
|
return .{};
|
||||||
|
}
|
||||||
|
};
|
||||||
36
src/dom/node.zig
Normal file
36
src/dom/node.zig
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
const std = @import("std");
|
||||||
|
|
||||||
|
const parser = @import("../parser.zig");
|
||||||
|
|
||||||
|
const EventTarget = @import("event_target.zig").EventTarget;
|
||||||
|
|
||||||
|
pub fn create_tree(node: ?*parser.Node, _: ?*anyopaque) callconv(.C) parser.Action {
|
||||||
|
if (node == null) {
|
||||||
|
return parser.ActionStop;
|
||||||
|
}
|
||||||
|
const node_type = parser.nodeType(node.?);
|
||||||
|
const node_name = parser.nodeName(node.?);
|
||||||
|
std.debug.print("type: {any}, name: {s}\n", .{ node_type, node_name });
|
||||||
|
if (node_type == parser.NodeType.element) {
|
||||||
|
std.debug.print("yes\n", .{});
|
||||||
|
}
|
||||||
|
return parser.ActionOk;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const Node = struct {
|
||||||
|
proto: EventTarget,
|
||||||
|
base: ?*parser.Node = null,
|
||||||
|
|
||||||
|
pub const prototype = *EventTarget;
|
||||||
|
|
||||||
|
pub fn init(base: ?*parser.Node) Node {
|
||||||
|
return .{ .proto = EventTarget.init(null), .base = base };
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn make_tree(self: Node) !void {
|
||||||
|
if (self.base) |node| {
|
||||||
|
try parser.nodeWalk(node, create_tree);
|
||||||
|
}
|
||||||
|
return error.NodeParserNull;
|
||||||
|
}
|
||||||
|
};
|
||||||
6
src/html.zig
Normal file
6
src/html.zig
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
pub const html: []const u8 =
|
||||||
|
\\<main id='content'>
|
||||||
|
\\<a href='foo'>OK</a>
|
||||||
|
\\<p>blah-blah-blah</p>
|
||||||
|
\\</main>
|
||||||
|
;
|
||||||
27
src/main.zig
Normal file
27
src/main.zig
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
const std = @import("std");
|
||||||
|
|
||||||
|
const runtime = @import("jsruntime");
|
||||||
|
|
||||||
|
const EventTarget = @import("dom/event_target.zig").EventTarget;
|
||||||
|
const Node = @import("dom/node.zig").Node;
|
||||||
|
const Document = @import("dom/document.zig").Document;
|
||||||
|
|
||||||
|
pub fn main() !void {
|
||||||
|
|
||||||
|
// // generate APIs
|
||||||
|
// _ = comptime runtime.compile(.{ EventTarget, Node, Document });
|
||||||
|
|
||||||
|
// // create v8 vm
|
||||||
|
// const vm = runtime.VM.init();
|
||||||
|
// defer vm.deinit();
|
||||||
|
|
||||||
|
// // document
|
||||||
|
// var doc = Document.init();
|
||||||
|
// defer doc.deinit();
|
||||||
|
// var html: []const u8 = "<div><a href='foo'>OK</a><p>blah-blah-blah</p></div>";
|
||||||
|
// try doc.parse(html);
|
||||||
|
|
||||||
|
// try doc.proto.make_tree();
|
||||||
|
|
||||||
|
std.debug.print("ok\n", .{});
|
||||||
|
}
|
||||||
51
src/main_shell.zig
Normal file
51
src/main_shell.zig
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
const std = @import("std");
|
||||||
|
|
||||||
|
const jsruntime = @import("jsruntime");
|
||||||
|
const Console = @import("jsruntime").Console;
|
||||||
|
|
||||||
|
const DOM = @import("dom.zig");
|
||||||
|
|
||||||
|
const html = @import("html.zig").html;
|
||||||
|
|
||||||
|
var doc: DOM.HTMLDocument = undefined;
|
||||||
|
|
||||||
|
fn execJS(
|
||||||
|
alloc: std.mem.Allocator,
|
||||||
|
js_env: *jsruntime.Env,
|
||||||
|
comptime apis: []jsruntime.API,
|
||||||
|
) !void {
|
||||||
|
|
||||||
|
// start JS env
|
||||||
|
js_env.start();
|
||||||
|
defer js_env.stop();
|
||||||
|
|
||||||
|
// add document object
|
||||||
|
try js_env.addObject(apis, doc, "document");
|
||||||
|
|
||||||
|
// launch shellExec
|
||||||
|
try jsruntime.shellExec(alloc, js_env, apis);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn main() !void {
|
||||||
|
|
||||||
|
// generate APIs
|
||||||
|
const apis = jsruntime.compile(DOM.Interfaces);
|
||||||
|
|
||||||
|
// document
|
||||||
|
var base_doc = DOM.Document.init();
|
||||||
|
defer base_doc.deinit();
|
||||||
|
try base_doc.parse(html);
|
||||||
|
doc = DOM.HTMLDocument{ .proto = base_doc };
|
||||||
|
|
||||||
|
// create JS vm
|
||||||
|
const vm = jsruntime.VM.init();
|
||||||
|
defer vm.deinit();
|
||||||
|
|
||||||
|
// alloc
|
||||||
|
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
|
||||||
|
defer _ = gpa.deinit();
|
||||||
|
const alloc = gpa.allocator();
|
||||||
|
|
||||||
|
// launch shell
|
||||||
|
try jsruntime.shell(alloc, false, apis, execJS, .{ .app_name = "browsercore" });
|
||||||
|
}
|
||||||
232
src/parser.zig
Normal file
232
src/parser.zig
Normal file
@@ -0,0 +1,232 @@
|
|||||||
|
const std = @import("std");
|
||||||
|
|
||||||
|
const c = @cImport({
|
||||||
|
@cInclude("../../lexbor/source/lexbor/html/html.h");
|
||||||
|
});
|
||||||
|
|
||||||
|
// Public API
|
||||||
|
// ----------
|
||||||
|
|
||||||
|
// EventTarget
|
||||||
|
|
||||||
|
pub const EventTarget = c.lxb_dom_event_target_t;
|
||||||
|
|
||||||
|
// Node
|
||||||
|
|
||||||
|
pub const Node = c.lxb_dom_node_t;
|
||||||
|
|
||||||
|
pub const NodeType = enum(u4) {
|
||||||
|
undef,
|
||||||
|
element,
|
||||||
|
attribute,
|
||||||
|
text,
|
||||||
|
cdata_section,
|
||||||
|
entity_reference,
|
||||||
|
entity,
|
||||||
|
processing_instruction,
|
||||||
|
comment,
|
||||||
|
document,
|
||||||
|
document_type,
|
||||||
|
document_fragment,
|
||||||
|
notation,
|
||||||
|
last_entry,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn nodeEventTarget(node: *Node) *EventTarget {
|
||||||
|
return c.lxb_dom_interface_event_target(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const nodeWalker = (fn (node: ?*Node, _: ?*anyopaque) callconv(.C) Action);
|
||||||
|
|
||||||
|
pub fn nodeName(node: *Node) [*c]const u8 {
|
||||||
|
var s: usize = undefined;
|
||||||
|
return c.lxb_dom_node_name(node, &s);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn nodeType(node: *Node) NodeType {
|
||||||
|
return @intToEnum(NodeType, node.*.type);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn nodeWalk(node: *Node, comptime walker: nodeWalker) !void {
|
||||||
|
c.lxb_dom_node_simple_walk(node, walker, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Element
|
||||||
|
|
||||||
|
pub const Element = c.lxb_dom_element_t;
|
||||||
|
|
||||||
|
pub fn elementNode(element: *Element) *Node {
|
||||||
|
return c.lxb_dom_interface_node(element);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn elementLocalName(element: *Element) []const u8 {
|
||||||
|
const local_name = c.lxb_dom_element_local_name(element, null);
|
||||||
|
return std.mem.sliceTo(local_name, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn elementsByAttr(
|
||||||
|
element: *Element,
|
||||||
|
collection: *Collection,
|
||||||
|
attr: []const u8,
|
||||||
|
value: []const u8,
|
||||||
|
case_sensitve: bool,
|
||||||
|
) !void {
|
||||||
|
const status = c.lxb_dom_elements_by_attr(
|
||||||
|
element,
|
||||||
|
collection,
|
||||||
|
attr.ptr,
|
||||||
|
attr.len,
|
||||||
|
value.ptr,
|
||||||
|
value.len,
|
||||||
|
case_sensitve,
|
||||||
|
);
|
||||||
|
if (status != 0) {
|
||||||
|
return error.ElementsByAttr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// DocumentHTML
|
||||||
|
|
||||||
|
pub const DocumentHTML = c.lxb_html_document_t;
|
||||||
|
|
||||||
|
pub fn documentHTMLInit() *DocumentHTML {
|
||||||
|
return c.lxb_html_document_create();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn documentHTMLDeinit(document_html: *DocumentHTML) void {
|
||||||
|
_ = c.lxb_html_document_destroy(document_html);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn documentHTMLParse(document_html: *DocumentHTML, html: []const u8) !void {
|
||||||
|
const status = c.lxb_html_document_parse(document_html, html.ptr, html.len - 1);
|
||||||
|
if (status != 0) {
|
||||||
|
return error.DocumentHTMLParse;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn documentHTMLToNode(document_html: *DocumentHTML) *Node {
|
||||||
|
return c.lxb_dom_interface_node(document_html);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn documentHTMLToDocument(document_html: *DocumentHTML) *Document {
|
||||||
|
return &document_html.dom_document;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn documentHTMLBody(document_html: *DocumentHTML) *Element {
|
||||||
|
return c.lxb_dom_interface_element(document_html.body);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Document
|
||||||
|
|
||||||
|
pub const Document = c.lxb_dom_document_t;
|
||||||
|
|
||||||
|
// Collection
|
||||||
|
|
||||||
|
pub const Collection = c.lxb_dom_collection_t;
|
||||||
|
|
||||||
|
pub fn collectionInit(document: *Document, size: usize) *Collection {
|
||||||
|
return c.lxb_dom_collection_make(document, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn collectionDeinit(collection: *Collection) void {
|
||||||
|
_ = c.lxb_dom_collection_destroy(collection, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn collectionElement(collection: *Collection, index: usize) *Element {
|
||||||
|
return c.lxb_dom_collection_element(collection, index);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Base
|
||||||
|
|
||||||
|
pub const Action = c.lexbor_action_t;
|
||||||
|
|
||||||
|
// TODO: use enum?
|
||||||
|
pub const ActionStop = c.LEXBOR_ACTION_STOP;
|
||||||
|
pub const ActionNext = c.LEXBOR_ACTION_NEXT;
|
||||||
|
pub const ActionOk = c.LEXBOR_ACTION_OK;
|
||||||
|
|
||||||
|
// Playground
|
||||||
|
// ----------
|
||||||
|
|
||||||
|
fn serialize_callback(_: [*c]const u8, _: usize, _: ?*anyopaque) callconv(.C) c_uint {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn walker_play(nn: ?*c.lxb_dom_node_t, _: ?*anyopaque) callconv(.C) c.lexbor_action_t {
|
||||||
|
if (nn == null) {
|
||||||
|
return c.LEXBOR_ACTION_STOP;
|
||||||
|
}
|
||||||
|
const n = nn.?;
|
||||||
|
|
||||||
|
var s: usize = undefined;
|
||||||
|
const name = c.lxb_dom_node_name(n, &s);
|
||||||
|
|
||||||
|
std.debug.print("type: {d}, name: {s}\n", .{ n.*.type, name });
|
||||||
|
if (n.*.local_name == c.LXB_TAG_A) {
|
||||||
|
const element = c.lxb_dom_interface_element(n);
|
||||||
|
const attr = element.*.first_attr;
|
||||||
|
std.debug.print("link, attr: {any}\n", .{attr.*.upper_name});
|
||||||
|
}
|
||||||
|
return c.LEXBOR_ACTION_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn parse_document() void {
|
||||||
|
const html = "<div><a href='foo'>OK</a><p>blah-blah-blah</p></div>";
|
||||||
|
const html_len = html.len - 1;
|
||||||
|
|
||||||
|
// parse
|
||||||
|
const doc = c.lxb_html_document_create();
|
||||||
|
const status_parse = c.lxb_html_document_parse(doc, html, html_len);
|
||||||
|
std.debug.print("status parse: {any}\n", .{status_parse});
|
||||||
|
|
||||||
|
// tree
|
||||||
|
const document_node = c.lxb_dom_interface_node(doc);
|
||||||
|
std.debug.print("document node is empty: {any}\n", .{c.lxb_dom_node_is_empty(document_node)});
|
||||||
|
std.debug.print("document node type: {any}\n", .{document_node.*.type});
|
||||||
|
std.debug.print("document node name: {any}\n", .{document_node.*.local_name});
|
||||||
|
|
||||||
|
c.lxb_dom_node_simple_walk(document_node, walker_play, null);
|
||||||
|
|
||||||
|
const first_child = c.lxb_dom_node_last_child(document_node);
|
||||||
|
if (first_child == null) {
|
||||||
|
std.debug.print("hummm is null\n", .{});
|
||||||
|
}
|
||||||
|
std.debug.print("first child type: {any}\n", .{first_child.*.type});
|
||||||
|
std.debug.print("first child name: {any}\n", .{first_child.*.local_name});
|
||||||
|
|
||||||
|
const tt = c.lxb_dom_node_first_child(first_child);
|
||||||
|
std.debug.print("tt type: {any}\n", .{tt.*.type});
|
||||||
|
std.debug.print("tt name: {any}\n", .{tt.*.local_name});
|
||||||
|
std.debug.print("{any}\n", .{c.LXB_DOM_NODE_TYPE_TEXT});
|
||||||
|
|
||||||
|
var s: usize = undefined;
|
||||||
|
const tt_name = c.lxb_dom_node_name(tt, &s);
|
||||||
|
std.debug.print("tt name: {s}\n", .{tt_name});
|
||||||
|
|
||||||
|
const nn = tt.*.first_child;
|
||||||
|
if (nn == null) {
|
||||||
|
std.debug.print("is null\n", .{});
|
||||||
|
}
|
||||||
|
|
||||||
|
// text
|
||||||
|
var text_len: usize = undefined;
|
||||||
|
var text = c.lxb_dom_node_text_content(tt, &text_len);
|
||||||
|
std.debug.print("size: {d}\n", .{text_len});
|
||||||
|
std.debug.print("text: {s}\n", .{text});
|
||||||
|
|
||||||
|
// serialize
|
||||||
|
const status_serialize = c.lxb_html_serialize_pretty_tree_cb(
|
||||||
|
document_node,
|
||||||
|
c.LXB_HTML_SERIALIZE_OPT_UNDEF,
|
||||||
|
0,
|
||||||
|
serialize_callback,
|
||||||
|
null,
|
||||||
|
);
|
||||||
|
std.debug.print("status serialize: {any}\n", .{status_serialize});
|
||||||
|
|
||||||
|
// destroy
|
||||||
|
_ = c.lxb_html_document_destroy(doc);
|
||||||
|
// _ = c.lxb_dom_document_destroy_text(first_child.*.owner_document, &text);
|
||||||
|
// _ = c.lxb_dom_document_destroy_text(c.lxb_dom_interface_document(document), text);
|
||||||
|
std.debug.print("text2: {s}\n", .{text}); // should not work
|
||||||
|
}
|
||||||
46
src/run_tests.zig
Normal file
46
src/run_tests.zig
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
const std = @import("std");
|
||||||
|
|
||||||
|
const jsruntime = @import("jsruntime");
|
||||||
|
|
||||||
|
const DOM = @import("dom.zig");
|
||||||
|
const document = @import("dom/document.zig");
|
||||||
|
const element = @import("dom/element.zig");
|
||||||
|
|
||||||
|
const html = @import("html.zig").html;
|
||||||
|
|
||||||
|
var doc: DOM.HTMLDocument = undefined;
|
||||||
|
|
||||||
|
fn testsExecFn(
|
||||||
|
_: std.mem.Allocator,
|
||||||
|
js_env: *jsruntime.Env,
|
||||||
|
comptime apis: []jsruntime.API,
|
||||||
|
) !void {
|
||||||
|
|
||||||
|
// start JS env
|
||||||
|
js_env.start();
|
||||||
|
defer js_env.stop();
|
||||||
|
|
||||||
|
// add document object
|
||||||
|
try js_env.addObject(apis, doc, "document");
|
||||||
|
|
||||||
|
// run tests
|
||||||
|
try document.testExecFn(js_env, apis);
|
||||||
|
}
|
||||||
|
|
||||||
|
test {
|
||||||
|
// generate APIs
|
||||||
|
const apis = jsruntime.compile(DOM.Interfaces);
|
||||||
|
|
||||||
|
// document
|
||||||
|
doc = DOM.HTMLDocument.init();
|
||||||
|
defer doc.deinit();
|
||||||
|
try doc.parse(html);
|
||||||
|
|
||||||
|
// create JS vm
|
||||||
|
const vm = jsruntime.VM.init();
|
||||||
|
defer vm.deinit();
|
||||||
|
|
||||||
|
var alloc = jsruntime.bench_allocator(std.testing.allocator);
|
||||||
|
|
||||||
|
try jsruntime.loadEnv(alloc.allocator(), false, testsExecFn, apis);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user