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 // DOM
const EventTarget = @import("dom/event_target.zig").EventTarget; 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 Element = @import("dom/element.zig").Element;
const Document = @import("dom/document.zig").Document; const Document = @import("dom/document.zig").Document;
// HTML // HTML
pub const HTMLDocument = @import("html/document.zig").HTMLDocument; pub const HTMLDocument = @import("html/document.zig").HTMLDocument;
const HTMLElem = @import("html/elements.zig");
const E = @import("html/elements.zig"); const E = @import("html/elements.zig");
@@ -19,14 +20,15 @@ const interfaces = .{
// DOM // DOM
EventTarget, EventTarget,
Node, N.Node,
N.Types,
Element, Element,
Document, Document,
// HTML // HTML
HTMLDocument, HTMLDocument,
E.HTMLElement, HTMLElem.HTMLElement,
E.HTMLMediaElement, HTMLElem.HTMLMediaElement,
E.HTMLElementsTypes, 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 std = @import("std");
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 HTMLDocument = @import("../html/document.zig").HTMLDocument;
const HTMLElem = @import("../html/elements.zig");
pub const Node = struct { 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 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 std = @import("std");
const builtin = @import("builtin"); 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 { fn fmtName(comptime T: type) []const u8 {
var it = std.mem.splitBackwards(u8, @typeName(T), "."); var it = std.mem.splitBackwards(u8, @typeName(T), ".");
return it.first(); return it.first();
} }
// Union
// -----
// Generate a flatten tagged Union from various structs and union of structs // Generate a flatten tagged Union from various structs and union of structs
// TODO: make this function more generic // TODO: make this function more generic
// TODO: dedup
pub const Union = struct { pub const Union = struct {
_enum: type, _enum: type,
_union: type, _union: type,
@@ -152,38 +172,15 @@ pub const Union = struct {
} }
}; };
fn itoa(comptime i: u8) ![]u8 { // Tuple
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});
}
// Generate a flatten tuple type from various structs and tuple of structs. fn tupleNb(comptime tuple: anytype) usize {
// TODO: make this function more generic var nb = 0;
pub fn TupleT(comptime tuple: anytype) type { for (@typeInfo(@TypeOf(tuple)).Struct.fields) |member| {
// 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| {
const member_T = @field(tuple, member.name); const member_T = @field(tuple, member.name);
if (@TypeOf(member_T) == type) { if (@TypeOf(member_T) == type) {
members_nb += 1; nb += 1;
} else { } else {
const member_info = @typeInfo(@TypeOf(member_T)); const member_info = @typeInfo(@TypeOf(member_T));
if (member_info != .Struct and !member_info.Struct.is_tuple) { if (member_info != .Struct and !member_info.Struct.is_tuple) {
@@ -194,14 +191,82 @@ pub fn TupleT(comptime tuple: anytype) type {
@compileError("GenerateMemberTupleChildNotType"); @compileError("GenerateMemberTupleChildNotType");
} }
} }
members_nb += member_info.Struct.fields.len; nb += member_info.Struct.fields.len;
} }
} }
return nb;
}
// second iteration to generate the tuple type fn tupleTypes(comptime nb: usize, comptime tuple: anytype) [nb]type {
var fields: [members_nb]std.builtin.Type.StructField = undefined; var types: [nb]type = undefined;
var done = 0; 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] = .{ fields[done] = .{
.name = try itoa(done), .name = try itoa(done),
.type = type, .type = type,
@@ -221,39 +286,36 @@ pub fn TupleT(comptime tuple: anytype) type {
return @Type(std.builtin.Type{ .Struct = info }); return @Type(std.builtin.Type{ .Struct = info });
} }
// Instantiate a flatten tuple from various structs and tuple of structs // Create a flatten tuple from various structs and tuple of structs
// You need to call first TupleT to generate the according type // Duplicates will be removed.
// TODO: make this function more generic // 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 // check types provided
const tuple_T = @TypeOf(tuple); const tuple_T = @TypeOf(tuple);
const tuple_info = @typeInfo(tuple_T); 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 // instantiate the tuple
var t: T = undefined; var t: T = undefined;
var done = 0; var done = 0;
for (tuple_members) |member| { for (dedup_indexes) |index| {
const member_T = @field(tuple, member.name); if (index == -1) {
var member_info: std.builtin.Type = undefined; continue;
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;
} }
const name = try itoa(done);
@field(t, name) = types[index];
done += 1;
} }
return t; return t;
} }
@@ -322,7 +384,7 @@ pub fn tests() !void {
// Tuple from structs // Tuple from structs
const tuple_structs = .{ Astruct, Bstruct }; 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)); const t_from_structs = @typeInfo(@TypeOf(tFromStructs));
try std.testing.expect(t_from_structs == .Struct); try std.testing.expect(t_from_structs == .Struct);
try std.testing.expect(t_from_structs.Struct.is_tuple); try std.testing.expect(t_from_structs.Struct.is_tuple);
@@ -332,7 +394,7 @@ pub fn tests() !void {
// Tuple from tuple and structs // Tuple from tuple and structs
const tuple_mix = .{ tFromStructs, Cstruct }; 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)); const t_from_mix = @typeInfo(@TypeOf(tFromMix));
try std.testing.expect(t_from_mix == .Struct); try std.testing.expect(t_from_mix == .Struct);
try std.testing.expect(t_from_mix.Struct.is_tuple); 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, "1") == Bstruct);
try std.testing.expect(@field(tFromMix, "2") == Cstruct); 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", .{}); 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 checkCases = jsruntime.test_utils.checkCases;
const Document = @import("../dom/document.zig").Document; const Document = @import("../dom/document.zig").Document;
const HTMLElem = @import("elements.zig");
const E = @import("elements.zig");
pub const HTMLDocument = struct { pub const HTMLDocument = struct {
pub const Self = parser.DocumentHTML; pub const Self = parser.DocumentHTML;
@@ -22,19 +21,19 @@ pub const HTMLDocument = struct {
return parser.documentHTMLBody(self); 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 doc = parser.documentHTMLToDocument(self);
const elem = parser.documentGetElementById(doc, id); const elem_dom = parser.documentGetElementById(doc, id);
if (elem) |value| { if (elem_dom) |elem| {
return @as(*parser.ElementHTML, @ptrCast(value)); return HTMLElem.toInterface(HTMLElem.Union, elem);
} }
return null; 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 doc_dom = parser.documentHTMLToDocument(self);
const base = parser.documentCreateElement(doc_dom, tag_name); 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__.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" },
}; };
try checkCases(js_env, &constructor); try checkCases(js_env, &constructor);
var getElementById = [_]Case{ var getElementById = [_]Case{
.{ .src = "let getElementById = document.getElementById('content')", .ex = "undefined" }, .{ .src = "let getElementById = document.getElementById('content')", .ex = "undefined" },
.{ .src = "getElementById.constructor.name", .ex = "HTMLElement" }, .{ .src = "getElementById.constructor.name", .ex = "HTMLDivElement" },
.{ .src = "getElementById.localName", .ex = "main" }, .{ .src = "getElementById.localName", .ex = "div" },
}; };
try checkCases(js_env, &getElementById); try checkCases(js_env, &getElementById);

View File

@@ -12,7 +12,7 @@ pub const HTMLElement = struct {
pub const mem_guarantied = true; pub const mem_guarantied = true;
}; };
pub const HTMLElementsTypes = .{ pub const Types = .{
HTMLUnknownElement, HTMLUnknownElement,
HTMLAnchorElement, HTMLAnchorElement,
HTMLAreaElement, HTMLAreaElement,
@@ -74,9 +74,9 @@ pub const HTMLElementsTypes = .{
HTMLUListElement, HTMLUListElement,
HTMLVideoElement, HTMLVideoElement,
}; };
const HTMLElementsGenerated = generate.Union.compile(HTMLElementsTypes); const Generated = generate.Union.compile(Types);
pub const HTMLElements = HTMLElementsGenerated._union; pub const Union = Generated._union;
pub const HTMLElementsTags = HTMLElementsGenerated._enum; pub const Tags = Generated._enum;
// Deprecated HTMLElements in Chrome (2023/03/15) // Deprecated HTMLElements in Chrome (2023/03/15)
// HTMLContentelement // HTMLContentelement
@@ -454,7 +454,7 @@ pub const HTMLVideoElement = struct {
pub const mem_guarantied = true; 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))); 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)) },