mirror of
https://github.com/lightpanda-io/browser.git
synced 2025-10-29 07:03:29 +00:00
netsurf: move to public only API
And add some Node APIs: - getters: firstChild, lastChild, nextSibling, previoussibling, parentNode, parentElement, nodeName, nodeType, ownerDocument, isConnected - getters/setters: nodeValue, textContent - methods: appendChild And some test comptime optimizations on Document Signed-off-by: Francis Bouvier <francis.bouvier@gmail.com>
This commit is contained in:
@@ -5,6 +5,9 @@ const Console = @import("jsruntime").Console;
|
|||||||
// DOM
|
// DOM
|
||||||
const EventTarget = @import("dom/event_target.zig").EventTarget;
|
const EventTarget = @import("dom/event_target.zig").EventTarget;
|
||||||
const N = @import("dom/node.zig");
|
const N = @import("dom/node.zig");
|
||||||
|
const CharacterData = @import("dom/character_data.zig").CharacterData;
|
||||||
|
const Comment = @import("dom/comment.zig").Comment;
|
||||||
|
const Text = @import("dom/text.zig").Text;
|
||||||
const Element = @import("dom/element.zig").Element;
|
const Element = @import("dom/element.zig").Element;
|
||||||
const Document = @import("dom/document.zig").Document;
|
const Document = @import("dom/document.zig").Document;
|
||||||
|
|
||||||
@@ -22,6 +25,8 @@ const interfaces = .{
|
|||||||
EventTarget,
|
EventTarget,
|
||||||
N.Node,
|
N.Node,
|
||||||
N.Types,
|
N.Types,
|
||||||
|
CharacterData,
|
||||||
|
Comment,
|
||||||
Element,
|
Element,
|
||||||
Document,
|
Document,
|
||||||
|
|
||||||
|
|||||||
20
src/dom/character_data.zig
Normal file
20
src/dom/character_data.zig
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
const generate = @import("../generate.zig");
|
||||||
|
const parser = @import("../netsurf.zig");
|
||||||
|
|
||||||
|
const Node = @import("node.zig").Node;
|
||||||
|
const Comment = @import("comment.zig").Comment;
|
||||||
|
const Text = @import("text.zig").Text;
|
||||||
|
|
||||||
|
pub const CharacterData = struct {
|
||||||
|
pub const Self = parser.CharacterData;
|
||||||
|
pub const prototype = *Node;
|
||||||
|
pub const mem_guarantied = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const Types = generate.Tuple(.{
|
||||||
|
Comment,
|
||||||
|
Text,
|
||||||
|
});
|
||||||
|
const Generated = generate.Union.compile(Types);
|
||||||
|
pub const Union = Generated._union;
|
||||||
|
pub const Tags = Generated._enum;
|
||||||
9
src/dom/comment.zig
Normal file
9
src/dom/comment.zig
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
const parser = @import("../netsurf.zig");
|
||||||
|
|
||||||
|
const CharacterData = @import("character_data.zig").CharacterData;
|
||||||
|
|
||||||
|
pub const Comment = struct {
|
||||||
|
pub const Self = parser.Comment;
|
||||||
|
pub const prototype = *CharacterData;
|
||||||
|
pub const mem_guarantied = true;
|
||||||
|
};
|
||||||
233
src/dom/node.zig
233
src/dom/node.zig
@@ -1,10 +1,14 @@
|
|||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
|
|
||||||
|
const jsruntime = @import("jsruntime");
|
||||||
|
const Case = jsruntime.test_utils.Case;
|
||||||
|
const checkCases = jsruntime.test_utils.checkCases;
|
||||||
const generate = @import("../generate.zig");
|
const generate = @import("../generate.zig");
|
||||||
|
|
||||||
const parser = @import("../netsurf.zig");
|
const parser = @import("../netsurf.zig");
|
||||||
|
|
||||||
const EventTarget = @import("event_target.zig").EventTarget;
|
const EventTarget = @import("event_target.zig").EventTarget;
|
||||||
|
const CData = @import("character_data.zig");
|
||||||
const HTMLDocument = @import("../html/document.zig").HTMLDocument;
|
const HTMLDocument = @import("../html/document.zig").HTMLDocument;
|
||||||
const HTMLElem = @import("../html/elements.zig");
|
const HTMLElem = @import("../html/elements.zig");
|
||||||
|
|
||||||
@@ -12,12 +16,241 @@ pub const Node = struct {
|
|||||||
pub const Self = parser.Node;
|
pub const Self = parser.Node;
|
||||||
pub const prototype = *EventTarget;
|
pub const prototype = *EventTarget;
|
||||||
pub const mem_guarantied = true;
|
pub const mem_guarantied = true;
|
||||||
|
|
||||||
|
pub fn toInterface(node: *parser.Node) Union {
|
||||||
|
return switch (parser.nodeType(node)) {
|
||||||
|
.element => HTMLElem.toInterface(Union, @as(*parser.Element, @ptrCast(node))),
|
||||||
|
.comment => .{ .Comment = @as(*parser.Comment, @ptrCast(node)) },
|
||||||
|
.text => .{ .Text = @as(*parser.Text, @ptrCast(node)) },
|
||||||
|
.document => .{ .HTMLDocument = @as(*parser.DocumentHTML, @ptrCast(node)) },
|
||||||
|
else => @panic("node type not handled"), // TODO
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// JS funcs
|
||||||
|
// --------
|
||||||
|
|
||||||
|
// Read-only attributes
|
||||||
|
|
||||||
|
pub fn get_firstChild(self: *parser.Node) ?Union {
|
||||||
|
const res = parser.nodeFirstChild(self);
|
||||||
|
if (res == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return Node.toInterface(res.?);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_lastChild(self: *parser.Node) ?Union {
|
||||||
|
const res = parser.nodeLastChild(self);
|
||||||
|
if (res == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return Node.toInterface(res.?);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_nextSibling(self: *parser.Node) ?Union {
|
||||||
|
const res = parser.nodeNextSibling(self);
|
||||||
|
if (res == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return Node.toInterface(res.?);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_previousSibling(self: *parser.Node) ?Union {
|
||||||
|
const res = parser.nodePreviousSibling(self);
|
||||||
|
if (res == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return Node.toInterface(res.?);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_parentNode(self: *parser.Node) ?Union {
|
||||||
|
const res = parser.nodeParentNode(self);
|
||||||
|
if (res == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return Node.toInterface(res.?);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_parentElement(self: *parser.Node) ?HTMLElem.Union {
|
||||||
|
const res = parser.nodeParentElement(self);
|
||||||
|
if (res == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return HTMLElem.toInterface(HTMLElem.Union, @as(*parser.Element, @ptrCast(res.?)));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_nodeName(self: *parser.Node) []const u8 {
|
||||||
|
return parser.nodeName(self);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_nodeType(self: *parser.Node) u8 {
|
||||||
|
return @intFromEnum(parser.nodeType(self));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_ownerDocument(self: *parser.Node) ?*parser.DocumentHTML {
|
||||||
|
const res = parser.nodeOwnerDocument(self);
|
||||||
|
if (res == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return @as(*parser.DocumentHTML, @ptrCast(res.?));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_isConnected(self: *parser.Node) bool {
|
||||||
|
// TODO: handle Shadow DOM
|
||||||
|
if (parser.nodeType(self) == .document) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return Node.get_parentNode(self) != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read/Write attributes
|
||||||
|
|
||||||
|
pub fn get_nodeValue(self: *parser.Node) ?[]const u8 {
|
||||||
|
return parser.nodeValue(self);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_nodeValue(self: *parser.Node, data: []u8) void {
|
||||||
|
parser.nodeSetValue(self, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_textContent(self: *parser.Node) ?[]const u8 {
|
||||||
|
return parser.nodeTextContent(self);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_textContent(self: *parser.Node, data: []u8) void {
|
||||||
|
return parser.nodeSetTextContent(self, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Methods
|
||||||
|
|
||||||
|
pub fn _appendChild(self: *parser.Node, child: *parser.Node) Union {
|
||||||
|
// TODO: DocumentFragment special case
|
||||||
|
const res = parser.nodeAppendChild(self, child);
|
||||||
|
return Node.toInterface(res);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const Types = generate.Tuple(.{
|
pub const Types = generate.Tuple(.{
|
||||||
|
CData.Types,
|
||||||
HTMLElem.Types,
|
HTMLElem.Types,
|
||||||
HTMLDocument,
|
HTMLDocument,
|
||||||
});
|
});
|
||||||
const Generated = generate.Union.compile(Types);
|
const Generated = generate.Union.compile(Types);
|
||||||
pub const Union = Generated._union;
|
pub const Union = Generated._union;
|
||||||
pub const Tags = Generated._enum;
|
pub const Tags = Generated._enum;
|
||||||
|
|
||||||
|
// Tests
|
||||||
|
// -----
|
||||||
|
|
||||||
|
pub fn testExecFn(
|
||||||
|
_: std.mem.Allocator,
|
||||||
|
js_env: *jsruntime.Env,
|
||||||
|
comptime _: []jsruntime.API,
|
||||||
|
) !void {
|
||||||
|
var first_child = [_]Case{
|
||||||
|
// for next test cases
|
||||||
|
.{ .src = "let content = document.getElementById('content')", .ex = "undefined" },
|
||||||
|
.{ .src = "let link = document.getElementById('link')", .ex = "undefined" },
|
||||||
|
|
||||||
|
.{ .src = "let first_child = document.body.firstChild", .ex = "undefined" },
|
||||||
|
.{ .src = "first_child.localName", .ex = "div" },
|
||||||
|
.{ .src = "first_child.__proto__.constructor.name", .ex = "HTMLDivElement" },
|
||||||
|
.{ .src = "document.getElementById('para-empty').firstChild.firstChild", .ex = "null" },
|
||||||
|
};
|
||||||
|
try checkCases(js_env, &first_child);
|
||||||
|
|
||||||
|
var last_child = [_]Case{
|
||||||
|
.{ .src = "let last_child = content.lastChild", .ex = "undefined" },
|
||||||
|
.{ .src = "last_child.__proto__.constructor.name", .ex = "Comment" },
|
||||||
|
};
|
||||||
|
try checkCases(js_env, &last_child);
|
||||||
|
|
||||||
|
var next_sibling = [_]Case{
|
||||||
|
.{ .src = "let next_sibling = link.nextSibling", .ex = "undefined" },
|
||||||
|
.{ .src = "next_sibling.localName", .ex = "p" },
|
||||||
|
.{ .src = "next_sibling.__proto__.constructor.name", .ex = "HTMLParagraphElement" },
|
||||||
|
.{ .src = "content.nextSibling", .ex = "null" },
|
||||||
|
};
|
||||||
|
try checkCases(js_env, &next_sibling);
|
||||||
|
|
||||||
|
var prev_sibling = [_]Case{
|
||||||
|
.{ .src = "let prev_sibling = document.getElementById('para-empty').previousSibling", .ex = "undefined" },
|
||||||
|
.{ .src = "prev_sibling.localName", .ex = "a" },
|
||||||
|
.{ .src = "prev_sibling.__proto__.constructor.name", .ex = "HTMLAnchorElement" },
|
||||||
|
.{ .src = "content.previousSibling", .ex = "null" },
|
||||||
|
};
|
||||||
|
try checkCases(js_env, &prev_sibling);
|
||||||
|
|
||||||
|
var parent = [_]Case{
|
||||||
|
.{ .src = "let parent = document.getElementById('para').parentElement", .ex = "undefined" },
|
||||||
|
.{ .src = "parent.localName", .ex = "div" },
|
||||||
|
.{ .src = "parent.__proto__.constructor.name", .ex = "HTMLDivElement" },
|
||||||
|
.{ .src = "let h = content.parentElement.parentElement", .ex = "undefined" },
|
||||||
|
.{ .src = "h.parentElement", .ex = "null" },
|
||||||
|
.{ .src = "h.parentNode.__proto__.constructor.name", .ex = "HTMLDocument" },
|
||||||
|
};
|
||||||
|
try checkCases(js_env, &parent);
|
||||||
|
|
||||||
|
var node_name = [_]Case{
|
||||||
|
.{ .src = "content.firstChild.nodeName === 'A'", .ex = "true" },
|
||||||
|
.{ .src = "link.firstChild.nodeName === '#text'", .ex = "true" },
|
||||||
|
.{ .src = "content.lastChild.nodeName === '#comment'", .ex = "true" },
|
||||||
|
.{ .src = "document.nodeName === '#document'", .ex = "true" },
|
||||||
|
};
|
||||||
|
try checkCases(js_env, &node_name);
|
||||||
|
|
||||||
|
var node_type = [_]Case{
|
||||||
|
.{ .src = "content.firstChild.nodeType === 1", .ex = "true" },
|
||||||
|
.{ .src = "link.firstChild.nodeType === 3", .ex = "true" },
|
||||||
|
.{ .src = "content.lastChild.nodeType === 8", .ex = "true" },
|
||||||
|
.{ .src = "document.nodeType === 9", .ex = "true" },
|
||||||
|
};
|
||||||
|
try checkCases(js_env, &node_type);
|
||||||
|
|
||||||
|
var owner = [_]Case{
|
||||||
|
.{ .src = "let owner = content.ownerDocument", .ex = "undefined" },
|
||||||
|
.{ .src = "owner.__proto__.constructor.name", .ex = "HTMLDocument" },
|
||||||
|
.{ .src = "document.ownerDocument", .ex = "null" },
|
||||||
|
.{ .src = "let owner2 = document.createElement('div').ownerDocument", .ex = "undefined" },
|
||||||
|
.{ .src = "owner2.__proto__.constructor.name", .ex = "HTMLDocument" },
|
||||||
|
};
|
||||||
|
try checkCases(js_env, &owner);
|
||||||
|
|
||||||
|
var connected = [_]Case{
|
||||||
|
.{ .src = "content.isConnected", .ex = "true" },
|
||||||
|
.{ .src = "document.isConnected", .ex = "true" },
|
||||||
|
.{ .src = "document.createElement('div').isConnected", .ex = "false" },
|
||||||
|
};
|
||||||
|
try checkCases(js_env, &connected);
|
||||||
|
|
||||||
|
var node_value = [_]Case{
|
||||||
|
.{ .src = "content.lastChild.nodeValue === 'comment'", .ex = "true" },
|
||||||
|
.{ .src = "link.nodeValue === null", .ex = "true" },
|
||||||
|
.{ .src = "let text = link.firstChild", .ex = "undefined" },
|
||||||
|
.{ .src = "text.nodeValue === 'OK'", .ex = "true" },
|
||||||
|
.{ .src = "text.nodeValue = 'OK modified'", .ex = "OK modified" },
|
||||||
|
.{ .src = "text.nodeValue === 'OK modified'", .ex = "true" },
|
||||||
|
.{ .src = "link.nodeValue = 'nothing'", .ex = "nothing" },
|
||||||
|
};
|
||||||
|
try checkCases(js_env, &node_value);
|
||||||
|
|
||||||
|
var node_text_content = [_]Case{
|
||||||
|
.{ .src = "text.textContent === 'OK modified'", .ex = "true" },
|
||||||
|
.{ .src = "content.textContent === 'OK modified And'", .ex = "true" },
|
||||||
|
.{ .src = "text.textContent = 'OK'", .ex = "OK" },
|
||||||
|
.{ .src = "text.textContent", .ex = "OK" },
|
||||||
|
.{ .src = "document.getElementById('para-empty').textContent", .ex = "" },
|
||||||
|
.{ .src = "document.getElementById('para-empty').textContent = 'OK'", .ex = "OK" },
|
||||||
|
.{ .src = "document.getElementById('para-empty').firstChild.nodeName === '#text'", .ex = "true" },
|
||||||
|
};
|
||||||
|
try checkCases(js_env, &node_text_content);
|
||||||
|
|
||||||
|
var node_append_child = [_]Case{
|
||||||
|
.{ .src = "let append = document.createElement('h1')", .ex = "undefined" },
|
||||||
|
.{ .src = "content.appendChild(append).toString()", .ex = "[object HTMLHeadingElement]" },
|
||||||
|
.{ .src = "content.lastChild.__proto__.constructor.name", .ex = "HTMLHeadingElement" },
|
||||||
|
.{ .src = "content.appendChild(link).toString()", .ex = "[object HTMLAnchorElement]" },
|
||||||
|
};
|
||||||
|
try checkCases(js_env, &node_append_child);
|
||||||
|
}
|
||||||
|
|||||||
9
src/dom/text.zig
Normal file
9
src/dom/text.zig
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
const parser = @import("../netsurf.zig");
|
||||||
|
|
||||||
|
const CharacterData = @import("character_data.zig").CharacterData;
|
||||||
|
|
||||||
|
pub const Text = struct {
|
||||||
|
pub const Self = parser.Text;
|
||||||
|
pub const prototype = *CharacterData;
|
||||||
|
pub const mem_guarantied = true;
|
||||||
|
};
|
||||||
@@ -41,7 +41,7 @@ pub const HTMLDocument = struct {
|
|||||||
// -----
|
// -----
|
||||||
|
|
||||||
pub fn testExecFn(
|
pub fn testExecFn(
|
||||||
alloc: std.mem.Allocator,
|
_: std.mem.Allocator,
|
||||||
js_env: *jsruntime.Env,
|
js_env: *jsruntime.Env,
|
||||||
comptime _: []jsruntime.API,
|
comptime _: []jsruntime.API,
|
||||||
) !void {
|
) !void {
|
||||||
@@ -50,7 +50,7 @@ pub fn testExecFn(
|
|||||||
.{ .src = "document.__proto__.__proto__.constructor.name", .ex = "Document" },
|
.{ .src = "document.__proto__.__proto__.constructor.name", .ex = "Document" },
|
||||||
.{ .src = "document.__proto__.__proto__.__proto__.constructor.name", .ex = "Node" },
|
.{ .src = "document.__proto__.__proto__.__proto__.constructor.name", .ex = "Node" },
|
||||||
.{ .src = "document.__proto__.__proto__.__proto__.__proto__.constructor.name", .ex = "EventTarget" },
|
.{ .src = "document.__proto__.__proto__.__proto__.__proto__.constructor.name", .ex = "EventTarget" },
|
||||||
.{ .src = "document.body.localName === 'body'", .ex = "true" },
|
.{ .src = "document.body.localName == 'body'", .ex = "true" },
|
||||||
};
|
};
|
||||||
try checkCases(js_env, &constructor);
|
try checkCases(js_env, &constructor);
|
||||||
|
|
||||||
@@ -63,31 +63,24 @@ pub fn testExecFn(
|
|||||||
|
|
||||||
const tags = comptime parser.Tag.all();
|
const tags = comptime parser.Tag.all();
|
||||||
const elements = comptime parser.Tag.allElements();
|
const elements = comptime parser.Tag.allElements();
|
||||||
var createElements: [(tags.len - 1) * 3]Case = undefined;
|
comptime var createElements: [(tags.len) * 3]Case = undefined;
|
||||||
inline for (tags, 0..) |tag, i| {
|
inline for (tags, elements, 0..) |tag, element_name, i| {
|
||||||
if (tag == .undef) {
|
// if (tag == .undef) {
|
||||||
continue;
|
// continue;
|
||||||
}
|
// }
|
||||||
const tag_name = @tagName(tag);
|
const tag_name = @tagName(tag);
|
||||||
const element_name = elements[i];
|
|
||||||
createElements[i * 3] = Case{
|
createElements[i * 3] = Case{
|
||||||
.src = try std.fmt.allocPrint(alloc, "var {s}Elem = document.createElement('{s}')", .{ tag_name, tag_name }),
|
.src = "var " ++ tag_name ++ "Elem = document.createElement('" ++ tag_name ++ "')",
|
||||||
.ex = "undefined",
|
.ex = "undefined",
|
||||||
};
|
};
|
||||||
createElements[(i * 3) + 1] = Case{
|
createElements[(i * 3) + 1] = Case{
|
||||||
.src = try std.fmt.allocPrint(alloc, "{s}Elem.constructor.name", .{tag_name}),
|
.src = tag_name ++ "Elem.constructor.name",
|
||||||
.ex = try std.fmt.allocPrint(alloc, "HTML{s}Element", .{element_name}),
|
.ex = "HTML" ++ element_name ++ "Element",
|
||||||
};
|
};
|
||||||
createElements[(i * 3) + 2] = Case{
|
createElements[(i * 3) + 2] = Case{
|
||||||
.src = try std.fmt.allocPrint(alloc, "{s}Elem.localName", .{tag_name}),
|
.src = tag_name ++ "Elem.localName",
|
||||||
.ex = tag_name,
|
.ex = tag_name,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
try checkCases(js_env, &createElements);
|
try checkCases(js_env, &createElements);
|
||||||
|
|
||||||
var unknown = [_]Case{
|
|
||||||
.{ .src = "let unknown = document.createElement('unknown')", .ex = "undefined" },
|
|
||||||
.{ .src = "unknown.constructor.name", .ex = "HTMLUnknownElement" },
|
|
||||||
};
|
|
||||||
try checkCases(js_env, &unknown);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -454,7 +454,8 @@ pub const HTMLVideoElement = struct {
|
|||||||
pub const mem_guarantied = true;
|
pub const mem_guarantied = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn toInterface(comptime T: type, elem: *parser.Element) T {
|
pub fn toInterface(comptime T: type, e: *parser.Element) T {
|
||||||
|
const elem: *align(@alignOf(*parser.Element)) parser.Element = @alignCast(e);
|
||||||
const tag = parser.elementHTMLGetTagType(@as(*parser.ElementHTML, @ptrCast(elem)));
|
const tag = parser.elementHTMLGetTagType(@as(*parser.ElementHTML, @ptrCast(elem)));
|
||||||
return switch (tag) {
|
return switch (tag) {
|
||||||
.a => .{ .HTMLAnchorElement = @as(*parser.Anchor, @ptrCast(elem)) },
|
.a => .{ .HTMLAnchorElement = @as(*parser.Anchor, @ptrCast(elem)) },
|
||||||
|
|||||||
65
src/main.zig
65
src/main.zig
@@ -4,8 +4,8 @@ const jsruntime = @import("jsruntime");
|
|||||||
|
|
||||||
const parser = @import("netsurf.zig");
|
const parser = @import("netsurf.zig");
|
||||||
const DOM = @import("dom.zig");
|
const DOM = @import("dom.zig");
|
||||||
|
const docTestExecFn = @import("html/document.zig").testExecFn;
|
||||||
const html_test = @import("html_test.zig").html;
|
const nodeTestExecFn = @import("dom/node.zig").testExecFn;
|
||||||
|
|
||||||
const socket_path = "/tmp/browsercore-server.sock";
|
const socket_path = "/tmp/browsercore-server.sock";
|
||||||
|
|
||||||
@@ -45,6 +45,24 @@ fn execJS(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn testsExecFn(
|
||||||
|
alloc: std.mem.Allocator,
|
||||||
|
js_env: *jsruntime.Env,
|
||||||
|
comptime apis: []jsruntime.API,
|
||||||
|
) !void {
|
||||||
|
|
||||||
|
// start JS env
|
||||||
|
js_env.start(apis);
|
||||||
|
defer js_env.stop();
|
||||||
|
|
||||||
|
// add document object
|
||||||
|
try js_env.addObject(apis, doc, "document");
|
||||||
|
|
||||||
|
// run tests
|
||||||
|
try docTestExecFn(alloc, js_env, apis);
|
||||||
|
try nodeTestExecFn(alloc, js_env, apis);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn main() !void {
|
pub fn main() !void {
|
||||||
|
|
||||||
// generate APIs
|
// generate APIs
|
||||||
@@ -55,30 +73,35 @@ pub fn main() !void {
|
|||||||
defer vm.deinit();
|
defer vm.deinit();
|
||||||
|
|
||||||
// document
|
// document
|
||||||
|
|
||||||
// remove socket file of internal server
|
|
||||||
// reuse_address (SO_REUSEADDR flag) does not seems to work on unix socket
|
|
||||||
// see: https://gavv.net/articles/unix-socket-reuse/
|
|
||||||
// TODO: use a lock file instead
|
|
||||||
std.os.unlink(socket_path) catch |err| {
|
|
||||||
if (err != error.FileNotFound) {
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
var f = "test.html".*;
|
var f = "test.html".*;
|
||||||
doc = parser.documentHTMLParse(&f);
|
doc = parser.documentHTMLParse(&f);
|
||||||
// TODO: defer doc?
|
// TODO: defer doc?
|
||||||
|
|
||||||
|
// // remove socket file of internal server
|
||||||
|
// // reuse_address (SO_REUSEADDR flag) does not seems to work on unix socket
|
||||||
|
// // see: https://gavv.net/articles/unix-socket-reuse/
|
||||||
|
// // TODO: use a lock file instead
|
||||||
|
// std.os.unlink(socket_path) catch |err| {
|
||||||
|
// if (err != error.FileNotFound) {
|
||||||
|
// return err;
|
||||||
|
// }
|
||||||
|
// };
|
||||||
|
|
||||||
// alloc
|
// alloc
|
||||||
var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
|
// var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
|
||||||
defer arena.deinit();
|
// defer arena.deinit();
|
||||||
|
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
|
||||||
|
var bench_alloc = jsruntime.bench_allocator(gpa.allocator());
|
||||||
|
var arena_alloc = std.heap.ArenaAllocator.init(bench_alloc.allocator());
|
||||||
|
defer arena_alloc.deinit();
|
||||||
|
|
||||||
// server
|
// // server
|
||||||
var addr = try std.net.Address.initUnix(socket_path);
|
// var addr = try std.net.Address.initUnix(socket_path);
|
||||||
server = std.net.StreamServer.init(.{});
|
// server = std.net.StreamServer.init(.{});
|
||||||
defer server.deinit();
|
// defer server.deinit();
|
||||||
try server.listen(addr);
|
// try server.listen(addr);
|
||||||
std.debug.print("Listening on: {s}...\n", .{socket_path});
|
// std.debug.print("Listening on: {s}...\n", .{socket_path});
|
||||||
|
|
||||||
try jsruntime.loadEnv(&arena, execJS, apis);
|
// try jsruntime.loadEnv(&arena, execJS, apis);
|
||||||
|
try jsruntime.loadEnv(&arena_alloc, testsExecFn, apis);
|
||||||
}
|
}
|
||||||
|
|||||||
285
src/netsurf.zig
285
src/netsurf.zig
@@ -1,62 +1,17 @@
|
|||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
|
|
||||||
const cp = @cImport({
|
const c = @cImport({
|
||||||
@cInclude("wrapper.h");
|
@cInclude("wrapper.h");
|
||||||
});
|
});
|
||||||
|
|
||||||
const c = @cImport({
|
// Internal
|
||||||
@cInclude("core/node.h");
|
|
||||||
@cInclude("core/document.h");
|
|
||||||
@cInclude("core/element.h");
|
|
||||||
|
|
||||||
@cInclude("html/html_document.h");
|
inline fn getVtable(comptime VtableT: type, comptime NodeT: type, node: anytype) VtableT {
|
||||||
@cInclude("html/html_element.h");
|
const node_algined: *align(@alignOf([*c]c.dom_node)) NodeT = @alignCast(node);
|
||||||
@cInclude("html/html_anchor_element.h");
|
const base = @as([*c]c.dom_node, @ptrCast(node_algined));
|
||||||
@cInclude("html/html_area_element.h");
|
const vtable_aligned: *align(@alignOf([*c]VtableT)) const anyopaque = @alignCast(base.*.vtable.?);
|
||||||
@cInclude("html/html_br_element.h");
|
return @as([*c]const VtableT, @ptrCast(vtable_aligned)).*;
|
||||||
@cInclude("html/html_base_element.h");
|
}
|
||||||
@cInclude("html/html_body_element.h");
|
|
||||||
@cInclude("html/html_button_element.h");
|
|
||||||
@cInclude("html/html_canvas_element.h");
|
|
||||||
@cInclude("html/html_dlist_element.h");
|
|
||||||
@cInclude("html/html_div_element.h");
|
|
||||||
@cInclude("html/html_fieldset_element.h");
|
|
||||||
@cInclude("html/html_form_element.h");
|
|
||||||
@cInclude("html/html_frameset_element.h");
|
|
||||||
@cInclude("html/html_hr_element.h");
|
|
||||||
@cInclude("html/html_head_element.h");
|
|
||||||
@cInclude("html/html_heading_element.h");
|
|
||||||
@cInclude("html/html_html_element.h");
|
|
||||||
@cInclude("html/html_iframe_element.h");
|
|
||||||
@cInclude("html/html_image_element.h");
|
|
||||||
@cInclude("html/html_input_element.h");
|
|
||||||
@cInclude("html/html_li_element.h");
|
|
||||||
@cInclude("html/html_label_element.h");
|
|
||||||
@cInclude("html/html_legend_element.h");
|
|
||||||
@cInclude("html/html_link_element.h");
|
|
||||||
@cInclude("html/html_map_element.h");
|
|
||||||
@cInclude("html/html_meta_element.h");
|
|
||||||
@cInclude("html/html_mod_element.h");
|
|
||||||
@cInclude("html/html_olist_element.h");
|
|
||||||
@cInclude("html/html_object_element.h");
|
|
||||||
@cInclude("html/html_opt_group_element.h");
|
|
||||||
@cInclude("html/html_option_element.h");
|
|
||||||
@cInclude("html/html_paragraph_element.h");
|
|
||||||
@cInclude("html/html_pre_element.h");
|
|
||||||
@cInclude("html/html_quote_element.h");
|
|
||||||
@cInclude("html/html_script_element.h");
|
|
||||||
@cInclude("html/html_select_element.h");
|
|
||||||
@cInclude("html/html_style_element.h");
|
|
||||||
@cInclude("html/html_table_element.h");
|
|
||||||
@cInclude("html/html_tablecaption_element.h");
|
|
||||||
@cInclude("html/html_tablecell_element.h");
|
|
||||||
@cInclude("html/html_tablecol_element.h");
|
|
||||||
@cInclude("html/html_tablerow_element.h");
|
|
||||||
@cInclude("html/html_tablesection_element.h");
|
|
||||||
@cInclude("html/html_text_area_element.h");
|
|
||||||
@cInclude("html/html_title_element.h");
|
|
||||||
@cInclude("html/html_ulist_element.h");
|
|
||||||
});
|
|
||||||
|
|
||||||
// Utils
|
// Utils
|
||||||
const String = c.dom_string;
|
const String = c.dom_string;
|
||||||
@@ -67,7 +22,7 @@ inline fn stringToData(s: *String) []const u8 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
inline fn stringFromData(data: []const u8) *String {
|
inline fn stringFromData(data: []const u8) *String {
|
||||||
var s: ?*String = null;
|
var s: ?*String = undefined;
|
||||||
_ = c.dom_string_create(data.ptr, data.len, &s);
|
_ = c.dom_string_create(data.ptr, data.len, &s);
|
||||||
return s.?;
|
return s.?;
|
||||||
}
|
}
|
||||||
@@ -218,49 +173,187 @@ pub const Tag = enum(u8) {
|
|||||||
// EventTarget
|
// EventTarget
|
||||||
pub const EventTarget = c.dom_event_target;
|
pub const EventTarget = c.dom_event_target;
|
||||||
|
|
||||||
|
// NodeType
|
||||||
|
|
||||||
|
pub const NodeType = enum(u4) {
|
||||||
|
element = c.DOM_ELEMENT_NODE,
|
||||||
|
attribute = c.DOM_ATTRIBUTE_NODE,
|
||||||
|
text = c.DOM_TEXT_NODE,
|
||||||
|
cdata_section = c.DOM_CDATA_SECTION_NODE,
|
||||||
|
entity_reference = c.DOM_ENTITY_REFERENCE_NODE, // historical
|
||||||
|
entity = c.DOM_ENTITY_NODE, // historical
|
||||||
|
processing_instruction = c.DOM_PROCESSING_INSTRUCTION_NODE,
|
||||||
|
comment = c.DOM_COMMENT_NODE,
|
||||||
|
document = c.DOM_DOCUMENT_NODE,
|
||||||
|
document_type = c.DOM_DOCUMENT_TYPE_NODE,
|
||||||
|
document_fragment = c.DOM_DOCUMENT_FRAGMENT_NODE,
|
||||||
|
notation = c.DOM_NOTATION_NODE, // historical
|
||||||
|
};
|
||||||
|
|
||||||
// Node
|
// Node
|
||||||
pub const Node = c.dom_node_internal;
|
pub const Node = c.dom_node_internal;
|
||||||
|
|
||||||
|
fn nodeVtable(node: *Node) c.dom_node_vtable {
|
||||||
|
return getVtable(c.dom_node_vtable, Node, node);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn nodeLocalName(node: *Node) []const u8 {
|
||||||
|
var s: ?*String = undefined;
|
||||||
|
_ = nodeVtable(node).dom_node_get_local_name.?(node, &s);
|
||||||
|
var s_lower: ?*String = undefined;
|
||||||
|
_ = c.dom_string_tolower(s, true, &s_lower);
|
||||||
|
return stringToData(s_lower.?);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn nodeType(node: *Node) NodeType {
|
||||||
|
var node_type: c.dom_node_type = undefined;
|
||||||
|
_ = nodeVtable(node).dom_node_get_node_type.?(node, &node_type);
|
||||||
|
return @as(NodeType, @enumFromInt(node_type));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn nodeFirstChild(node: *Node) ?*Node {
|
||||||
|
var res: ?*Node = undefined;
|
||||||
|
_ = nodeVtable(node).dom_node_get_first_child.?(node, &res);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn nodeLastChild(node: *Node) ?*Node {
|
||||||
|
var res: ?*Node = undefined;
|
||||||
|
_ = nodeVtable(node).dom_node_get_last_child.?(node, &res);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn nodeNextSibling(node: *Node) ?*Node {
|
||||||
|
var res: ?*Node = undefined;
|
||||||
|
_ = nodeVtable(node).dom_node_get_next_sibling.?(node, &res);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn nodePreviousSibling(node: *Node) ?*Node {
|
||||||
|
var res: ?*Node = undefined;
|
||||||
|
_ = nodeVtable(node).dom_node_get_previous_sibling.?(node, &res);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn nodeParentNode(node: *Node) ?*Node {
|
||||||
|
var res: ?*Node = undefined;
|
||||||
|
_ = nodeVtable(node).dom_node_get_parent_node.?(node, &res);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn nodeParentElement(node: *Node) ?*Element {
|
||||||
|
const res = nodeParentNode(node);
|
||||||
|
if (res) |value| {
|
||||||
|
if (nodeType(value) == .element) {
|
||||||
|
return @as(*Element, @ptrCast(value));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn nodeName(node: *Node) []const u8 {
|
||||||
|
var s: ?*String = undefined;
|
||||||
|
_ = nodeVtable(node).dom_node_get_node_name.?(node, &s);
|
||||||
|
return stringToData(s.?);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn nodeOwnerDocument(node: *Node) ?*Document {
|
||||||
|
var doc: ?*Document = undefined;
|
||||||
|
_ = nodeVtable(node).dom_node_get_owner_document.?(node, &doc);
|
||||||
|
return doc;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn nodeValue(node: *Node) ?[]const u8 {
|
||||||
|
var s: ?*String = undefined;
|
||||||
|
_ = nodeVtable(node).dom_node_get_node_value.?(node, &s);
|
||||||
|
if (s == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return stringToData(s.?);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn nodeSetValue(node: *Node, value: []const u8) void {
|
||||||
|
const s = stringFromData(value);
|
||||||
|
_ = nodeVtable(node).dom_node_set_node_value.?(node, s);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn nodeTextContent(node: *Node) ?[]const u8 {
|
||||||
|
var s: ?*String = undefined;
|
||||||
|
_ = nodeVtable(node).dom_node_get_text_content.?(node, &s);
|
||||||
|
if (s == null) {
|
||||||
|
// NOTE: it seems that there is a bug in netsurf implem
|
||||||
|
// an empty Element should return an empty string and not null
|
||||||
|
if (nodeType(node) == .element) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return stringToData(s.?);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn nodeSetTextContent(node: *Node, value: []const u8) void {
|
||||||
|
const s = stringFromData(value);
|
||||||
|
_ = nodeVtable(node).dom_node_set_text_content.?(node, s);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn nodeAppendChild(node: *Node, child: *Node) *Node {
|
||||||
|
var res: ?*Node = undefined;
|
||||||
|
_ = nodeVtable(node).dom_node_append_child.?(node, child, &res);
|
||||||
|
return res.?;
|
||||||
|
}
|
||||||
|
|
||||||
|
// CharacterData
|
||||||
|
pub const CharacterData = c.dom_characterdata;
|
||||||
|
|
||||||
|
// Text
|
||||||
|
pub const Text = c.dom_text;
|
||||||
|
|
||||||
|
// Comment
|
||||||
|
pub const Comment = c.dom_comment;
|
||||||
|
|
||||||
// Element
|
// Element
|
||||||
pub const Element = c.dom_element;
|
pub const Element = c.dom_element;
|
||||||
|
|
||||||
|
fn elementVtable(elem: *Element) c.dom_element_vtable {
|
||||||
|
return getVtable(c.dom_element_vtable, Element, elem);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn elementLocalName(elem: *Element) []const u8 {
|
pub fn elementLocalName(elem: *Element) []const u8 {
|
||||||
const elem_aligned: *align(8) Element = @alignCast(elem);
|
const node = @as(*Node, @ptrCast(elem));
|
||||||
const node = @as(*Node, @ptrCast(elem_aligned));
|
return nodeLocalName(node);
|
||||||
var s: ?*String = null;
|
|
||||||
_ = c._dom_node_get_local_name(node, &s);
|
|
||||||
var s_lower: ?*String = null;
|
|
||||||
_ = c.dom_string_tolower(s, true, &s_lower);
|
|
||||||
return stringToData(s_lower.?);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ElementHTML
|
// ElementHTML
|
||||||
pub const ElementHTML = c.dom_html_element;
|
pub const ElementHTML = c.dom_html_element;
|
||||||
|
|
||||||
|
fn elementHTMLVtable(elem_html: *ElementHTML) c.dom_html_element_vtable {
|
||||||
|
return getVtable(c.dom_html_element_vtable, ElementHTML, elem_html);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn elementHTMLGetTagType(elem_html: *ElementHTML) Tag {
|
pub fn elementHTMLGetTagType(elem_html: *ElementHTML) Tag {
|
||||||
var tag_type: c.dom_html_element_type = undefined;
|
var tag_type: c.dom_html_element_type = undefined;
|
||||||
_ = c._dom_html_element_get_tag_type(elem_html, &tag_type);
|
_ = elementHTMLVtable(elem_html).dom_html_element_get_tag_type.?(elem_html, &tag_type);
|
||||||
return @as(Tag, @enumFromInt(tag_type));
|
return @as(Tag, @enumFromInt(tag_type));
|
||||||
}
|
}
|
||||||
|
|
||||||
// ElementsHTML
|
// ElementsHTML
|
||||||
|
|
||||||
pub const MediaElement = struct { base: c.dom_html_element };
|
pub const MediaElement = struct { base: *c.dom_html_element };
|
||||||
|
|
||||||
pub const Unknown = struct { base: c.dom_html_element };
|
pub const Unknown = struct { base: *c.dom_html_element };
|
||||||
pub const Anchor = c.dom_html_anchor_element;
|
pub const Anchor = c.dom_html_anchor_element;
|
||||||
pub const Area = c.dom_html_area_element;
|
pub const Area = c.dom_html_area_element;
|
||||||
pub const Audio = struct { base: c.dom_html_element };
|
pub const Audio = struct { base: *c.dom_html_element };
|
||||||
pub const BR = c.dom_html_br_element;
|
pub const BR = c.dom_html_br_element;
|
||||||
pub const Base = c.dom_html_base_element;
|
pub const Base = c.dom_html_base_element;
|
||||||
pub const Body = c.dom_html_body_element;
|
pub const Body = c.dom_html_body_element;
|
||||||
pub const Button = c.dom_html_button_element;
|
pub const Button = c.dom_html_button_element;
|
||||||
pub const Canvas = c.dom_html_canvas_element;
|
pub const Canvas = c.dom_html_canvas_element;
|
||||||
pub const DList = c.dom_html_dlist_element;
|
pub const DList = c.dom_html_dlist_element;
|
||||||
pub const Data = struct { base: c.dom_html_element };
|
pub const Data = struct { base: *c.dom_html_element };
|
||||||
pub const Dialog = struct { base: c.dom_html_element };
|
pub const Dialog = struct { base: *c.dom_html_element };
|
||||||
pub const Div = c.dom_html_div_element;
|
pub const Div = c.dom_html_div_element;
|
||||||
pub const Embed = struct { base: c.dom_html_element };
|
pub const Embed = struct { base: *c.dom_html_element };
|
||||||
pub const FieldSet = c.dom_html_field_set_element;
|
pub const FieldSet = c.dom_html_field_set_element;
|
||||||
pub const Form = c.dom_html_form_element;
|
pub const Form = c.dom_html_form_element;
|
||||||
pub const FrameSet = c.dom_html_frame_set_element;
|
pub const FrameSet = c.dom_html_frame_set_element;
|
||||||
@@ -277,22 +370,22 @@ pub const Legend = c.dom_html_legend_element;
|
|||||||
pub const Link = c.dom_html_link_element;
|
pub const Link = c.dom_html_link_element;
|
||||||
pub const Map = c.dom_html_map_element;
|
pub const Map = c.dom_html_map_element;
|
||||||
pub const Meta = c.dom_html_meta_element;
|
pub const Meta = c.dom_html_meta_element;
|
||||||
pub const Meter = struct { base: c.dom_html_element };
|
pub const Meter = struct { base: *c.dom_html_element };
|
||||||
pub const Mod = c.dom_html_mod_element;
|
pub const Mod = c.dom_html_mod_element;
|
||||||
pub const OList = c.dom_html_olist_element;
|
pub const OList = c.dom_html_olist_element;
|
||||||
pub const Object = c.dom_html_object_element;
|
pub const Object = c.dom_html_object_element;
|
||||||
pub const OptGroup = c.dom_html_opt_group_element;
|
pub const OptGroup = c.dom_html_opt_group_element;
|
||||||
pub const Option = c.dom_html_option_element;
|
pub const Option = c.dom_html_option_element;
|
||||||
pub const Output = struct { base: c.dom_html_element };
|
pub const Output = struct { base: *c.dom_html_element };
|
||||||
pub const Paragraph = c.dom_html_paragraph_element;
|
pub const Paragraph = c.dom_html_paragraph_element;
|
||||||
pub const Picture = struct { base: c.dom_html_element };
|
pub const Picture = struct { base: *c.dom_html_element };
|
||||||
pub const Pre = c.dom_html_pre_element;
|
pub const Pre = c.dom_html_pre_element;
|
||||||
pub const Progress = struct { base: c.dom_html_element };
|
pub const Progress = struct { base: *c.dom_html_element };
|
||||||
pub const Quote = c.dom_html_quote_element;
|
pub const Quote = c.dom_html_quote_element;
|
||||||
pub const Script = c.dom_html_script_element;
|
pub const Script = c.dom_html_script_element;
|
||||||
pub const Select = c.dom_html_select_element;
|
pub const Select = c.dom_html_select_element;
|
||||||
pub const Source = struct { base: c.dom_html_element };
|
pub const Source = struct { base: *c.dom_html_element };
|
||||||
pub const Span = struct { base: c.dom_html_element };
|
pub const Span = struct { base: *c.dom_html_element };
|
||||||
pub const Style = c.dom_html_style_element;
|
pub const Style = c.dom_html_style_element;
|
||||||
pub const Table = c.dom_html_table_element;
|
pub const Table = c.dom_html_table_element;
|
||||||
pub const TableCaption = c.dom_html_table_caption_element;
|
pub const TableCaption = c.dom_html_table_caption_element;
|
||||||
@@ -300,39 +393,57 @@ pub const TableCell = c.dom_html_table_cell_element;
|
|||||||
pub const TableCol = c.dom_html_table_col_element;
|
pub const TableCol = c.dom_html_table_col_element;
|
||||||
pub const TableRow = c.dom_html_table_row_element;
|
pub const TableRow = c.dom_html_table_row_element;
|
||||||
pub const TableSection = c.dom_html_table_section_element;
|
pub const TableSection = c.dom_html_table_section_element;
|
||||||
pub const Template = struct { base: c.dom_html_element };
|
pub const Template = struct { base: *c.dom_html_element };
|
||||||
pub const TextArea = c.dom_html_text_area_element;
|
pub const TextArea = c.dom_html_text_area_element;
|
||||||
pub const Time = struct { base: c.dom_html_element };
|
pub const Time = struct { base: *c.dom_html_element };
|
||||||
pub const Title = c.dom_html_title_element;
|
pub const Title = c.dom_html_title_element;
|
||||||
pub const Track = struct { base: c.dom_html_element };
|
pub const Track = struct { base: *c.dom_html_element };
|
||||||
pub const UList = c.dom_html_u_list_element;
|
pub const UList = c.dom_html_u_list_element;
|
||||||
pub const Video = struct { base: c.dom_html_element };
|
pub const Video = struct { base: *c.dom_html_element };
|
||||||
|
|
||||||
|
// Document Position
|
||||||
|
|
||||||
|
pub const DocumentPosition = enum(u2) {
|
||||||
|
disconnected = c.DOM_DOCUMENT_POSITION_DISCONNECTED,
|
||||||
|
preceding = c.DOM_DOCUMENT_POSITION_PRECEDING,
|
||||||
|
following = c.DOM_DOCUMENT_POSITION_FOLLOWING,
|
||||||
|
contains = c.DOM_DOCUMENT_POSITION_CONTAINS,
|
||||||
|
contained_by = c.DOM_DOCUMENT_POSITION_CONTAINED_BY,
|
||||||
|
implementation_specific = c.DOM_DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC,
|
||||||
|
};
|
||||||
|
|
||||||
// Document
|
// Document
|
||||||
pub const Document = c.dom_document;
|
pub const Document = c.dom_document;
|
||||||
|
|
||||||
|
fn documentVtable(doc: *Document) c.dom_document_vtable {
|
||||||
|
return getVtable(c.dom_document_vtable, Document, doc);
|
||||||
|
}
|
||||||
|
|
||||||
pub inline fn documentGetElementById(doc: *Document, id: []const u8) ?*Element {
|
pub inline fn documentGetElementById(doc: *Document, id: []const u8) ?*Element {
|
||||||
var elem: ?*Element = undefined;
|
var elem: ?*Element = undefined;
|
||||||
_ = c._dom_document_get_element_by_id(doc, stringFromData(id), &elem);
|
_ = documentVtable(doc).dom_document_get_element_by_id.?(doc, stringFromData(id), &elem);
|
||||||
return elem;
|
return elem;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub inline fn documentCreateElement(doc: *Document, tag_name: []const u8) *Element {
|
pub inline fn documentCreateElement(doc: *Document, tag_name: []const u8) *Element {
|
||||||
var elem: ?*Element = undefined;
|
var elem: ?*Element = undefined;
|
||||||
_ = c._dom_html_document_create_element(doc, stringFromData(tag_name), &elem);
|
_ = documentVtable(doc).dom_document_create_element.?(doc, stringFromData(tag_name), &elem);
|
||||||
return elem.?;
|
return elem.?;
|
||||||
}
|
}
|
||||||
|
|
||||||
// DocumentHTML
|
// DocumentHTML
|
||||||
pub const DocumentHTML = c.dom_html_document;
|
pub const DocumentHTML = c.dom_html_document;
|
||||||
|
|
||||||
|
fn documentHTMLVtable(doc_html: *DocumentHTML) c.dom_html_document_vtable {
|
||||||
|
return getVtable(c.dom_html_document_vtable, DocumentHTML, doc_html);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn documentHTMLParse(filename: []u8) *DocumentHTML {
|
pub fn documentHTMLParse(filename: []u8) *DocumentHTML {
|
||||||
const doc = cp.wr_create_doc_dom_from_file(filename.ptr);
|
const doc = c.wr_create_doc_dom_from_file(filename.ptr);
|
||||||
if (doc == null) {
|
if (doc == null) {
|
||||||
@panic("error parser");
|
@panic("error parser");
|
||||||
}
|
}
|
||||||
const doc_aligned: *align(@alignOf((DocumentHTML))) cp.dom_document = @alignCast(doc.?);
|
return @as(*DocumentHTML, @ptrCast(doc.?));
|
||||||
return @as(*DocumentHTML, @ptrCast(doc_aligned));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub inline fn documentHTMLToDocument(doc_html: *DocumentHTML) *Document {
|
pub inline fn documentHTMLToDocument(doc_html: *DocumentHTML) *Document {
|
||||||
@@ -341,9 +452,9 @@ pub inline fn documentHTMLToDocument(doc_html: *DocumentHTML) *Document {
|
|||||||
|
|
||||||
pub inline fn documentHTMLBody(doc_html: *DocumentHTML) ?*Body {
|
pub inline fn documentHTMLBody(doc_html: *DocumentHTML) ?*Body {
|
||||||
var body: ?*ElementHTML = undefined;
|
var body: ?*ElementHTML = undefined;
|
||||||
_ = c._dom_html_document_get_body(doc_html, &body);
|
_ = documentHTMLVtable(doc_html).get_body.?(doc_html, &body);
|
||||||
if (body) |value| {
|
if (body == null) {
|
||||||
return @as(*Body, @ptrCast(value));
|
return null;
|
||||||
}
|
}
|
||||||
return null;
|
return @as(*Body, @ptrCast(body.?));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,8 @@ const generate = @import("generate.zig");
|
|||||||
|
|
||||||
const parser = @import("netsurf.zig");
|
const parser = @import("netsurf.zig");
|
||||||
const DOM = @import("dom.zig");
|
const DOM = @import("dom.zig");
|
||||||
const testExecFn = @import("html/document.zig").testExecFn;
|
const docTestExecFn = @import("html/document.zig").testExecFn;
|
||||||
|
const nodeTestExecFn = @import("html/document.zig").testExecFn;
|
||||||
|
|
||||||
var doc: *parser.DocumentHTML = undefined;
|
var doc: *parser.DocumentHTML = undefined;
|
||||||
|
|
||||||
@@ -23,7 +24,8 @@ fn testsExecFn(
|
|||||||
try js_env.addObject(apis, doc, "document");
|
try js_env.addObject(apis, doc, "document");
|
||||||
|
|
||||||
// run tests
|
// run tests
|
||||||
try testExecFn(alloc, js_env, apis);
|
try docTestExecFn(alloc, js_env, apis);
|
||||||
|
try nodeTestExecFn(alloc, js_env, apis);
|
||||||
}
|
}
|
||||||
|
|
||||||
test {
|
test {
|
||||||
|
|||||||
@@ -1,8 +1 @@
|
|||||||
<div id='content'>
|
<div id='content'><a id='link' href='foo'>OK</a><p id='para-empty'><span id='para-empty-child'></span></p><p id='para'> And</p><!--comment--></div>
|
||||||
<a id='link' href='foo'>OK</a>
|
|
||||||
<p id='para-empty'>
|
|
||||||
<span id='para-empty-child'></span>
|
|
||||||
</p>
|
|
||||||
<p id='para'> And</p>
|
|
||||||
<!--comment-->
|
|
||||||
</div>
|
|
||||||
Reference in New Issue
Block a user