backport some node_lexbor modifications

Signed-off-by: Francis Bouvier <francis.bouvier@gmail.com>
This commit is contained in:
Francis Bouvier
2023-09-21 17:05:38 +02:00
parent 4708dbd679
commit b5b4659c87
5 changed files with 166 additions and 79 deletions

View File

@@ -4,12 +4,13 @@ const Console = @import("jsruntime").Console;
// DOM
const EventTarget = @import("dom/event_target.zig").EventTarget;
const Node = @import("dom/node.zig").Node;
const N = @import("dom/node.zig");
const Element = @import("dom/element.zig").Element;
const Document = @import("dom/document.zig").Document;
// HTML
pub const HTMLDocument = @import("html/document.zig").HTMLDocument;
const HTMLElem = @import("html/elements.zig");
const E = @import("html/elements.zig");
@@ -19,14 +20,15 @@ const interfaces = .{
// DOM
EventTarget,
Node,
N.Node,
N.Types,
Element,
Document,
// HTML
HTMLDocument,
E.HTMLElement,
E.HTMLMediaElement,
E.HTMLElementsTypes,
HTMLElem.HTMLElement,
HTMLElem.HTMLMediaElement,
HTMLElem.Types,
};
pub const Interfaces = generate.TupleInst(generate.TupleT(interfaces), interfaces);
pub const Interfaces = generate.Tuple(interfaces);

View File

@@ -1,11 +1,23 @@
const std = @import("std");
const generate = @import("../generate.zig");
const parser = @import("../netsurf.zig");
const EventTarget = @import("event_target.zig").EventTarget;
const HTMLDocument = @import("../html/document.zig").HTMLDocument;
const HTMLElem = @import("../html/elements.zig");
pub const Node = struct {
pub const Self = parser.Node;
pub const prototype = *EventTarget;
pub const mem_guarantied = true;
};
pub const Types = generate.Tuple(.{
HTMLElem.Types,
HTMLDocument,
});
const Generated = generate.Union.compile(Types);
pub const Union = Generated._union;
pub const Tags = Generated._enum;

View File

@@ -1,13 +1,33 @@
const std = @import("std");
const builtin = @import("builtin");
// Utils
// -----
fn itoa(comptime i: u8) ![]u8 {
var len: usize = undefined;
if (i < 10) {
len = 1;
} else if (i < 100) {
len = 2;
} else {
return error.GenerateTooMuchMembers;
}
var buf: [len]u8 = undefined;
return try std.fmt.bufPrint(buf[0..], "{d}", .{i});
}
fn fmtName(comptime T: type) []const u8 {
var it = std.mem.splitBackwards(u8, @typeName(T), ".");
return it.first();
}
// Union
// -----
// Generate a flatten tagged Union from various structs and union of structs
// TODO: make this function more generic
// TODO: dedup
pub const Union = struct {
_enum: type,
_union: type,
@@ -152,38 +172,15 @@ pub const Union = struct {
}
};
fn itoa(comptime i: u8) ![]u8 {
var len: usize = undefined;
if (i < 10) {
len = 1;
} else if (i < 100) {
len = 2;
} else {
return error.GenerateTooMuchMembers;
}
var buf: [len]u8 = undefined;
return try std.fmt.bufPrint(buf[0..], "{d}", .{i});
}
// Tuple
// -----
// Generate a flatten tuple type from various structs and tuple of structs.
// TODO: make this function more generic
pub fn TupleT(comptime tuple: anytype) type {
// check types provided
const tuple_T = @TypeOf(tuple);
const tuple_info = @typeInfo(tuple_T);
if (tuple_info != .Struct or !tuple_info.Struct.is_tuple) {
@compileError("GenerateArgNotTuple");
}
const tuple_members = tuple_info.Struct.fields;
// first iteration to get the total number of members
var members_nb = 0;
for (tuple_members) |member| {
fn tupleNb(comptime tuple: anytype) usize {
var nb = 0;
for (@typeInfo(@TypeOf(tuple)).Struct.fields) |member| {
const member_T = @field(tuple, member.name);
if (@TypeOf(member_T) == type) {
members_nb += 1;
nb += 1;
} else {
const member_info = @typeInfo(@TypeOf(member_T));
if (member_info != .Struct and !member_info.Struct.is_tuple) {
@@ -194,14 +191,82 @@ pub fn TupleT(comptime tuple: anytype) type {
@compileError("GenerateMemberTupleChildNotType");
}
}
members_nb += member_info.Struct.fields.len;
nb += member_info.Struct.fields.len;
}
}
return nb;
}
// second iteration to generate the tuple type
var fields: [members_nb]std.builtin.Type.StructField = undefined;
fn tupleTypes(comptime nb: usize, comptime tuple: anytype) [nb]type {
var types: [nb]type = undefined;
var done = 0;
while (done < members_nb) {
for (@typeInfo(@TypeOf(tuple)).Struct.fields) |member| {
const T = @field(tuple, member.name);
if (@TypeOf(T) == type) {
types[done] = T;
done += 1;
} else {
const info = @typeInfo(@TypeOf(T));
for (info.Struct.fields) |field| {
types[done] = @field(T, field.name);
done += 1;
}
}
}
return types;
}
fn isDup(comptime nb: usize, comptime list: [nb]type, comptime T: type, comptime i: usize) bool {
for (list, 0..) |item, index| {
if (i >= index) {
// check sequentially
continue;
}
if (T == item) {
return true;
}
}
return false;
}
fn dedupIndexes(comptime nb: usize, comptime types: [nb]type) [nb]i32 {
var dedup_indexes: [nb]i32 = undefined;
for (types, 0..) |T, i| {
if (isDup(nb, types, T, i)) {
dedup_indexes[i] = -1;
} else {
dedup_indexes[i] = i;
}
}
return dedup_indexes;
}
fn dedupNb(comptime nb: usize, comptime dedup_indexes: [nb]i32) usize {
var dedup_nb = 0;
for (dedup_indexes) |index| {
if (index != -1) {
dedup_nb += 1;
}
}
return dedup_nb;
}
fn TupleT(comptime tuple: anytype) type {
@setEvalBranchQuota(100000);
// logic
const nb = tupleNb(tuple);
const types = tupleTypes(nb, tuple);
const dedup_indexes = dedupIndexes(nb, types);
const dedup_nb = dedupNb(nb, dedup_indexes);
// generate the tuple type
var fields: [dedup_nb]std.builtin.Type.StructField = undefined;
var done = 0;
for (dedup_indexes) |index| {
if (index == -1) {
continue;
}
fields[done] = .{
.name = try itoa(done),
.type = type,
@@ -221,39 +286,36 @@ pub fn TupleT(comptime tuple: anytype) type {
return @Type(std.builtin.Type{ .Struct = info });
}
// Instantiate a flatten tuple from various structs and tuple of structs
// You need to call first TupleT to generate the according type
// Create a flatten tuple from various structs and tuple of structs
// Duplicates will be removed.
// TODO: make this function more generic
pub fn TupleInst(comptime T: type, comptime tuple: anytype) T {
pub fn Tuple(comptime tuple: anytype) TupleT(tuple) {
// check types provided
const tuple_T = @TypeOf(tuple);
const tuple_info = @typeInfo(tuple_T);
const tuple_members = tuple_info.Struct.fields;
if (tuple_info != .Struct or !tuple_info.Struct.is_tuple) {
@compileError("GenerateArgNotTuple");
}
// generate the type
const T = TupleT(tuple);
// logic
const nb = tupleNb(tuple);
const types = tupleTypes(nb, tuple);
const dedup_indexes = dedupIndexes(nb, types);
// instantiate the tuple
var t: T = undefined;
var done = 0;
for (tuple_members) |member| {
const member_T = @field(tuple, member.name);
var member_info: std.builtin.Type = undefined;
if (@TypeOf(member_T) == type) {
member_info = @typeInfo(member_T);
} else {
member_info = @typeInfo(@TypeOf(member_T));
}
var member_detail = member_info.Struct;
if (member_detail.is_tuple) {
for (member_detail.fields) |field| {
const name = try itoa(done);
@field(t, name) = @field(member_T, field.name);
done += 1;
}
} else {
const name = try itoa(done);
@field(t, name) = @field(tuple, member.name);
done += 1;
for (dedup_indexes) |index| {
if (index == -1) {
continue;
}
const name = try itoa(done);
@field(t, name) = types[index];
done += 1;
}
return t;
}
@@ -322,7 +384,7 @@ pub fn tests() !void {
// Tuple from structs
const tuple_structs = .{ Astruct, Bstruct };
const tFromStructs = TupleInst(TupleT(tuple_structs), tuple_structs);
const tFromStructs = Tuple(tuple_structs);
const t_from_structs = @typeInfo(@TypeOf(tFromStructs));
try std.testing.expect(t_from_structs == .Struct);
try std.testing.expect(t_from_structs.Struct.is_tuple);
@@ -332,7 +394,7 @@ pub fn tests() !void {
// Tuple from tuple and structs
const tuple_mix = .{ tFromStructs, Cstruct };
const tFromMix = TupleInst(TupleT(tuple_mix), tuple_mix);
const tFromMix = Tuple(tuple_mix);
const t_from_mix = @typeInfo(@TypeOf(tFromMix));
try std.testing.expect(t_from_mix == .Struct);
try std.testing.expect(t_from_mix.Struct.is_tuple);
@@ -341,5 +403,16 @@ pub fn tests() !void {
try std.testing.expect(@field(tFromMix, "1") == Bstruct);
try std.testing.expect(@field(tFromMix, "2") == Cstruct);
// Tuple with dedup
const tuple_dedup = .{ Cstruct, Astruct, tFromStructs, Bstruct };
const tFromDedup = Tuple(tuple_dedup);
const t_from_dedup = @typeInfo(@TypeOf(tFromDedup));
try std.testing.expect(t_from_dedup == .Struct);
try std.testing.expect(t_from_dedup.Struct.is_tuple);
try std.testing.expect(t_from_dedup.Struct.fields.len == 3);
try std.testing.expect(@field(tFromDedup, "0") == Cstruct);
try std.testing.expect(@field(tFromDedup, "1") == Astruct);
try std.testing.expect(@field(tFromDedup, "2") == Bstruct);
std.debug.print("Generate Tuple: OK\n", .{});
}

View File

@@ -7,8 +7,7 @@ const Case = jsruntime.test_utils.Case;
const checkCases = jsruntime.test_utils.checkCases;
const Document = @import("../dom/document.zig").Document;
const E = @import("elements.zig");
const HTMLElem = @import("elements.zig");
pub const HTMLDocument = struct {
pub const Self = parser.DocumentHTML;
@@ -22,19 +21,19 @@ pub const HTMLDocument = struct {
return parser.documentHTMLBody(self);
}
pub fn _getElementById(self: *parser.DocumentHTML, id: []u8) ?*parser.ElementHTML {
pub fn _getElementById(self: *parser.DocumentHTML, id: []u8) ?HTMLElem.Union {
const doc = parser.documentHTMLToDocument(self);
const elem = parser.documentGetElementById(doc, id);
if (elem) |value| {
return @as(*parser.ElementHTML, @ptrCast(value));
const elem_dom = parser.documentGetElementById(doc, id);
if (elem_dom) |elem| {
return HTMLElem.toInterface(HTMLElem.Union, elem);
}
return null;
}
pub fn _createElement(self: *parser.DocumentHTML, tag_name: []const u8) E.HTMLElements {
pub fn _createElement(self: *parser.DocumentHTML, tag_name: []const u8) HTMLElem.Union {
const doc_dom = parser.documentHTMLToDocument(self);
const base = parser.documentCreateElement(doc_dom, tag_name);
return E.ElementToHTMLElementInterface(base);
return HTMLElem.toInterface(HTMLElem.Union, base);
}
};
@@ -51,13 +50,14 @@ pub fn testExecFn(
.{ .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" },
.{ .src = "document.body.localName === 'body'", .ex = "true" },
};
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" },
.{ .src = "getElementById.constructor.name", .ex = "HTMLDivElement" },
.{ .src = "getElementById.localName", .ex = "div" },
};
try checkCases(js_env, &getElementById);

View File

@@ -12,7 +12,7 @@ pub const HTMLElement = struct {
pub const mem_guarantied = true;
};
pub const HTMLElementsTypes = .{
pub const Types = .{
HTMLUnknownElement,
HTMLAnchorElement,
HTMLAreaElement,
@@ -74,9 +74,9 @@ pub const HTMLElementsTypes = .{
HTMLUListElement,
HTMLVideoElement,
};
const HTMLElementsGenerated = generate.Union.compile(HTMLElementsTypes);
pub const HTMLElements = HTMLElementsGenerated._union;
pub const HTMLElementsTags = HTMLElementsGenerated._enum;
const Generated = generate.Union.compile(Types);
pub const Union = Generated._union;
pub const Tags = Generated._enum;
// Deprecated HTMLElements in Chrome (2023/03/15)
// HTMLContentelement
@@ -454,7 +454,7 @@ pub const HTMLVideoElement = struct {
pub const mem_guarantied = true;
};
pub fn ElementToHTMLElementInterface(elem: *parser.Element) HTMLElements {
pub fn toInterface(comptime T: type, elem: *parser.Element) T {
const tag = parser.elementHTMLGetTagType(@as(*parser.ElementHTML, @ptrCast(elem)));
return switch (tag) {
.a => .{ .HTMLAnchorElement = @as(*parser.Anchor, @ptrCast(elem)) },