Merge pull request #27 from Browsercore/characterdata

CharacterData properties and methods
This commit is contained in:
Francis Bouvier
2023-10-09 15:47:10 +02:00
committed by GitHub
3 changed files with 248 additions and 9 deletions

View File

@@ -1,14 +1,79 @@
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 Node = @import("node.zig").Node; const Node = @import("node.zig").Node;
const Comment = @import("comment.zig").Comment; const Comment = @import("comment.zig").Comment;
const Text = @import("text.zig").Text; const Text = @import("text.zig").Text;
const HTMLElem = @import("../html/elements.zig");
pub const CharacterData = struct { pub const CharacterData = struct {
pub const Self = parser.CharacterData; pub const Self = parser.CharacterData;
pub const prototype = *Node; pub const prototype = *Node;
pub const mem_guarantied = true; pub const mem_guarantied = true;
// JS funcs
// --------
// Read attributes
pub fn get_length(self: *parser.CharacterData) u32 {
return parser.characterDataLength(self);
}
pub fn get_nextElementSibling(self: *parser.CharacterData) ?HTMLElem.Union {
const res = parser.nodeNextElementSibling(parser.characterDataToNode(self));
if (res == null) {
return null;
}
return HTMLElem.toInterface(HTMLElem.Union, res.?);
}
pub fn get_previousElementSibling(self: *parser.CharacterData) ?HTMLElem.Union {
const res = parser.nodePreviousElementSibling(parser.characterDataToNode(self));
if (res == null) {
return null;
}
return HTMLElem.toInterface(HTMLElem.Union, res.?);
}
// Read/Write attributes
pub fn get_data(self: *parser.CharacterData) []const u8 {
return parser.characterDataData(self);
}
pub fn set_data(self: *parser.CharacterData, data: []const u8) void {
return parser.characterDataSetData(self, data);
}
// JS methods
// ----------
pub fn _appendData(self: *parser.CharacterData, data: []const u8) void {
return parser.characterDataAppendData(self, data);
}
pub fn _deleteData(self: *parser.CharacterData, offset: u32, count: u32) void {
return parser.characterDataDeleteData(self, offset, count);
}
pub fn _insertData(self: *parser.CharacterData, offset: u32, data: []const u8) void {
return parser.characterDataInsertData(self, offset, data);
}
pub fn _replaceData(self: *parser.CharacterData, offset: u32, count: u32, data: []const u8) void {
return parser.characterDataReplaceData(self, offset, count, data);
}
pub fn _substringData(self: *parser.CharacterData, offset: u32, count: u32) []const u8 {
return parser.characterDataSubstringData(self, offset, count);
}
}; };
pub const Types = generate.Tuple(.{ pub const Types = generate.Tuple(.{
@@ -18,3 +83,79 @@ pub const Types = generate.Tuple(.{
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 get_data = [_]Case{
.{ .src = "let link = document.getElementById('link')", .ex = "undefined" },
.{ .src = "let cdata = link.firstChild", .ex = "undefined" },
.{ .src = "cdata.data", .ex = "OK" },
};
try checkCases(js_env, &get_data);
var set_data = [_]Case{
.{ .src = "cdata.data = 'OK modified'", .ex = "OK modified" },
.{ .src = "cdata.data === 'OK modified'", .ex = "true" },
.{ .src = "cdata.data = 'OK'", .ex = "OK" },
};
try checkCases(js_env, &set_data);
var get_length = [_]Case{
.{ .src = "cdata.length === 2", .ex = "true" },
};
try checkCases(js_env, &get_length);
var get_next_elem_sibling = [_]Case{
.{ .src = "cdata.nextElementSibling === null", .ex = "true" },
// create a next element
.{ .src = "let next = document.createElement('a')", .ex = "undefined" },
.{ .src = "link.appendChild(next, cdata) !== undefined", .ex = "true" },
.{ .src = "cdata.nextElementSibling.localName === 'a' ", .ex = "true" },
};
try checkCases(js_env, &get_next_elem_sibling);
var get_prev_elem_sibling = [_]Case{
.{ .src = "cdata.previousElementSibling === null", .ex = "true" },
// create a prev element
.{ .src = "let prev = document.createElement('div')", .ex = "undefined" },
.{ .src = "link.insertBefore(prev, cdata) !== undefined", .ex = "true" },
.{ .src = "cdata.previousElementSibling.localName === 'div' ", .ex = "true" },
};
try checkCases(js_env, &get_prev_elem_sibling);
var append_data = [_]Case{
.{ .src = "cdata.appendData(' modified')", .ex = "undefined" },
.{ .src = "cdata.data === 'OK modified' ", .ex = "true" },
};
try checkCases(js_env, &append_data);
var delete_data = [_]Case{
.{ .src = "cdata.deleteData('OK'.length, ' modified'.length)", .ex = "undefined" },
.{ .src = "cdata.data == 'OK'", .ex = "true" },
};
try checkCases(js_env, &delete_data);
var insert_data = [_]Case{
.{ .src = "cdata.insertData('OK'.length-1, 'modified')", .ex = "undefined" },
.{ .src = "cdata.data == 'OmodifiedK'", .ex = "true" },
};
try checkCases(js_env, &insert_data);
var replace_data = [_]Case{
.{ .src = "cdata.replaceData('OK'.length-1, 'modified'.length, 'replaced')", .ex = "undefined" },
.{ .src = "cdata.data == 'OreplacedK'", .ex = "true" },
};
try checkCases(js_env, &replace_data);
var substring_data = [_]Case{
.{ .src = "cdata.substringData('OK'.length-1, 'replaced'.length) == 'replaced'", .ex = "true" },
.{ .src = "cdata.substringData('OK'.length-1, 0) == ''", .ex = "true" },
};
try checkCases(js_env, &substring_data);
}

View File

@@ -267,12 +267,42 @@ pub fn nodeNextSibling(node: *Node) ?*Node {
return res; return res;
} }
pub fn nodeNextElementSibling(node: *Node) ?*Element {
var n = node;
while (true) {
const res = nodeNextSibling(n);
if (res == null) {
return null;
}
if (nodeType(res.?) == .element) {
return @as(*Element, @ptrCast(res.?));
}
n = res.?;
}
return null;
}
pub fn nodePreviousSibling(node: *Node) ?*Node { pub fn nodePreviousSibling(node: *Node) ?*Node {
var res: ?*Node = undefined; var res: ?*Node = undefined;
_ = nodeVtable(node).dom_node_get_previous_sibling.?(node, &res); _ = nodeVtable(node).dom_node_get_previous_sibling.?(node, &res);
return res; return res;
} }
pub fn nodePreviousElementSibling(node: *Node) ?*Element {
var n = node;
while (true) {
const res = nodePreviousSibling(n);
if (res == null) {
return null;
}
if (nodeType(res.?) == .element) {
return @as(*Element, @ptrCast(res.?));
}
n = res.?;
}
return null;
}
pub fn nodeParentNode(node: *Node) ?*Node { pub fn nodeParentNode(node: *Node) ?*Node {
var res: ?*Node = undefined; var res: ?*Node = undefined;
_ = nodeVtable(node).dom_node_get_parent_node.?(node, &res); _ = nodeVtable(node).dom_node_get_parent_node.?(node, &res);
@@ -420,6 +450,56 @@ pub fn nodeReplaceChild(node: *Node, new_child: *Node, old_child: *Node) *Node {
// CharacterData // CharacterData
pub const CharacterData = c.dom_characterdata; pub const CharacterData = c.dom_characterdata;
fn characterDataVtable(data: *CharacterData) c.dom_characterdata_vtable {
return getVtable(c.dom_characterdata_vtable, CharacterData, data);
}
pub inline fn characterDataToNode(cdata: *CharacterData) *Node {
return @as(*Node, @ptrCast(cdata));
}
pub fn characterDataData(cdata: *CharacterData) []const u8 {
var s: ?*String = undefined;
_ = characterDataVtable(cdata).dom_characterdata_get_data.?(cdata, &s);
return stringToData(s.?);
}
pub fn characterDataSetData(cdata: *CharacterData, data: []const u8) void {
const s = stringFromData(data);
_ = characterDataVtable(cdata).dom_characterdata_set_data.?(cdata, s);
}
pub fn characterDataLength(cdata: *CharacterData) u32 {
var n: u32 = undefined;
_ = characterDataVtable(cdata).dom_characterdata_get_length.?(cdata, &n);
return n;
}
pub fn characterDataAppendData(cdata: *CharacterData, data: []const u8) void {
const s = stringFromData(data);
_ = characterDataVtable(cdata).dom_characterdata_append_data.?(cdata, s);
}
pub fn characterDataDeleteData(cdata: *CharacterData, offset: u32, count: u32) void {
_ = characterDataVtable(cdata).dom_characterdata_delete_data.?(cdata, offset, count);
}
pub fn characterDataInsertData(cdata: *CharacterData, offset: u32, data: []const u8) void {
const s = stringFromData(data);
_ = characterDataVtable(cdata).dom_characterdata_insert_data.?(cdata, offset, s);
}
pub fn characterDataReplaceData(cdata: *CharacterData, offset: u32, count: u32, data: []const u8) void {
const s = stringFromData(data);
_ = characterDataVtable(cdata).dom_characterdata_replace_data.?(cdata, offset, count, s);
}
pub fn characterDataSubstringData(cdata: *CharacterData, offset: u32, count: u32) []const u8 {
var s: ?*String = undefined;
_ = characterDataVtable(cdata).dom_characterdata_substring_data.?(cdata, offset, count, &s);
return stringToData(s.?);
}
// Text // Text
pub const Text = c.dom_text; pub const Text = c.dom_text;

View File

@@ -5,27 +5,49 @@ 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 docTestExecFn = @import("html/document.zig").testExecFn; const docTestExecFn = @import("html/document.zig").testExecFn;
const nodeTestExecFn = @import("dom/node.zig").testExecFn; const nodeTestExecFn = @import("dom/node.zig").testExecFn;
const characterDataTestExecFn = @import("dom/character_data.zig").testExecFn;
var doc: *parser.DocumentHTML = undefined; var doc: *parser.DocumentHTML = undefined;
fn testsExecFn( fn testExecFn(
alloc: std.mem.Allocator, alloc: std.mem.Allocator,
js_env: *jsruntime.Env, js_env: *jsruntime.Env,
comptime apis: []jsruntime.API, comptime apis: []jsruntime.API,
comptime execFn: jsruntime.ContextExecFn,
) !void { ) !void {
// start JS env // start JS env
js_env.start(apis); js_env.start(apis);
defer js_env.stop(); defer js_env.stop();
// document
doc = try parser.documentHTMLParseFromFileAlloc(std.testing.allocator, "test.html");
defer parser.documentHTMLClose(doc);
// add document object // add document object
try js_env.addObject(apis, doc, "document"); try js_env.addObject(apis, doc, "document");
// run tests // run test
try docTestExecFn(alloc, js_env, apis); try execFn(alloc, js_env, apis);
try nodeTestExecFn(alloc, js_env, apis); }
fn testsAllExecFn(
alloc: std.mem.Allocator,
js_env: *jsruntime.Env,
comptime apis: []jsruntime.API,
) !void {
const testFns = [_]jsruntime.ContextExecFn{
docTestExecFn,
nodeTestExecFn,
characterDataTestExecFn,
};
inline for (testFns) |testFn| {
try testExecFn(alloc, js_env, apis, testFn);
}
} }
test { test {
@@ -37,10 +59,6 @@ test {
// generate APIs // generate APIs
const apis = jsruntime.compile(DOM.Interfaces); const apis = jsruntime.compile(DOM.Interfaces);
// document
doc = try parser.documentHTMLParseFromFileAlloc(std.testing.allocator, "test.html");
defer parser.documentHTMLClose(doc);
// create JS vm // create JS vm
const vm = jsruntime.VM.init(); const vm = jsruntime.VM.init();
defer vm.deinit(); defer vm.deinit();
@@ -49,5 +67,5 @@ test {
var arena_alloc = std.heap.ArenaAllocator.init(bench_alloc.allocator()); var arena_alloc = std.heap.ArenaAllocator.init(bench_alloc.allocator());
defer arena_alloc.deinit(); defer arena_alloc.deinit();
try jsruntime.loadEnv(&arena_alloc, testsExecFn, apis); try jsruntime.loadEnv(&arena_alloc, testsAllExecFn, apis);
} }