diff --git a/src/browser/html/elements.zig b/src/browser/html/elements.zig index 5b5fdcd7..5a912155 100644 --- a/src/browser/html/elements.zig +++ b/src/browser/html/elements.zig @@ -1042,84 +1042,68 @@ pub const HTMLSlotElement = struct { flatten: bool = false, }; pub fn _assignedNodes(self: *parser.Slot, opts_: ?AssignedNodesOpts, page: *Page) ![]NodeUnion { - return findAssignedSlotNodes(self, opts_, false, page); - } - - // This should return Union, instead of NodeUnion, but we want to re-use - // findAssignedSlotNodes. Returning NodeUnion is fine, as long as every element - // within is an Element. This could be more efficient - pub fn _assignedElements(self: *parser.Slot, opts_: ?AssignedNodesOpts, page: *Page) ![]NodeUnion { - return findAssignedSlotNodes(self, opts_, true, page); - } - - fn findAssignedSlotNodes(self: *parser.Slot, opts_: ?AssignedNodesOpts, element_only: bool, page: *Page) ![]NodeUnion { const opts = opts_ orelse AssignedNodesOpts{ .flatten = false }; - if (opts.flatten) { - log.debug(.web_api, "not implemented", .{ .feature = "HTMLSlotElement flatten assignedNodes" }); + if (try findAssignedSlotNodes(self, opts, page)) |nodes| { + return nodes; + } + + if (!opts.flatten) { + return &.{}; } const node: *parser.Node = @ptrCast(@alignCast(self)); + const nl = try parser.nodeGetChildNodes(node); + const len = try parser.nodeListLength(nl); + if (len == 0) { + return &.{}; + } - // First we look for any explicitly assigned nodes (via the slot attribute) - { - const slot_name = try parser.elementGetAttribute(@ptrCast(@alignCast(self)), "name"); - var root = try parser.nodeGetRootNode(node); - if (page.getNodeState(root)) |state| { - if (state.shadow_root) |sr| { - root = @ptrCast(@alignCast(sr.host)); - } + var assigned = try page.call_arena.alloc(NodeUnion, len); + var i: usize = 0; + while (true) : (i += 1) { + const child = try parser.nodeListItem(nl, @intCast(i)) orelse break; + assigned[i] = try Node.toInterface(child); + } + return assigned[0..i]; + } + + fn findAssignedSlotNodes(self: *parser.Slot, opts: AssignedNodesOpts, page: *Page) !?[]NodeUnion { + if (opts.flatten) { + log.warn(.web_api, "not implemented", .{ .feature = "HTMLSlotElement flatten assignedNodes" }); + } + + const slot_name = try parser.elementGetAttribute(@ptrCast(@alignCast(self)), "name"); + const node: *parser.Node = @ptrCast(@alignCast(self)); + var root = try parser.nodeGetRootNode(node); + if (page.getNodeState(root)) |state| { + if (state.shadow_root) |sr| { + root = @ptrCast(@alignCast(sr.host)); } + } - var arr: std.ArrayList(NodeUnion) = .empty; - const w = @import("../dom/walker.zig").WalkerChildren{}; - var next: ?*parser.Node = null; - while (true) { - next = try w.get_next(root, next) orelse break; - if (try parser.nodeType(next.?) != .element) { - if (slot_name == null and !element_only) { - // default slot (with no name), takes everything - try arr.append(page.call_arena, try Node.toInterface(next.?)); - } - continue; - } - const el: *parser.Element = @ptrCast(@alignCast(next.?)); - const element_slot = try parser.elementGetAttribute(el, "slot"); - - if (nullableStringsAreEqual(slot_name, element_slot)) { - // either they're the same string or they are both null + var arr: std.ArrayList(NodeUnion) = .empty; + const w = @import("../dom/walker.zig").WalkerChildren{}; + var next: ?*parser.Node = null; + while (true) { + next = try w.get_next(root, next) orelse break; + if (try parser.nodeType(next.?) != .element) { + if (slot_name == null) { + // default slot (with no name), takes everything try arr.append(page.call_arena, try Node.toInterface(next.?)); - continue; } + continue; } - if (arr.items.len > 0) { - return arr.items; - } + const el: *parser.Element = @ptrCast(@alignCast(next.?)); + const element_slot = try parser.elementGetAttribute(el, "slot"); - if (!opts.flatten) { - return &.{}; + if (nullableStringsAreEqual(slot_name, element_slot)) { + // either they're the same string or they are both null + try arr.append(page.call_arena, try Node.toInterface(next.?)); + continue; } } - - // Since, we have no explicitly assigned nodes and flatten == false, - // we'll collect the children of the slot - the defaults. - { - const nl = try parser.nodeGetChildNodes(node); - const len = try parser.nodeListLength(nl); - if (len == 0) { - return &.{}; - } - - var assigned = try page.call_arena.alloc(NodeUnion, len); - var i: usize = 0; - while (true) : (i += 1) { - const child = try parser.nodeListItem(nl, @intCast(i)) orelse break; - if (!element_only or try parser.nodeType(child) == .element) { - assigned[i] = try Node.toInterface(child); - } - } - return assigned[0..i]; - } + return if (arr.items.len == 0) null else arr.items; } fn nullableStringsAreEqual(a: ?[]const u8, b: ?[]const u8) bool { @@ -1345,6 +1329,39 @@ test "Browser: HTML.HtmlScriptElement" { try testing.htmlRunner("html/script/inline_defer.html"); } -test "Browser: HTML.HtmlSlotElement" { - try testing.htmlRunner("html/slot.html"); +test "Browser: HTML.HTMLSlotElement" { + try testing.htmlRunner("html/html_slot_element.html"); +} + +const Check = struct { + input: []const u8, + expected: ?[]const u8 = null, // Needed when input != expected +}; +const bool_valids = [_]Check{ + .{ .input = "true" }, + .{ .input = "''", .expected = "false" }, + .{ .input = "13.5", .expected = "true" }, +}; +const str_valids = [_]Check{ + .{ .input = "'foo'", .expected = "foo" }, + .{ .input = "5", .expected = "5" }, + .{ .input = "''", .expected = "" }, + .{ .input = "document", .expected = "[object HTMLDocument]" }, +}; + +// .{ "elem.type = '5'", "5" }, +// .{ "elem.type", "text" }, +fn testProperty( + arena: std.mem.Allocator, + runner: *testing.JsRunner, + elem_dot_prop: []const u8, + always: ?[]const u8, // Ignores checks' expected if set + checks: []const Check, +) !void { + for (checks) |check| { + try runner.testCases(&.{ + .{ try std.mem.concat(arena, u8, &.{ elem_dot_prop, " = ", check.input }), null }, + .{ elem_dot_prop, always orelse check.expected orelse check.input }, + }, .{}); + } } diff --git a/src/tests/html/html_slot_element.html b/src/tests/html/html_slot_element.html new file mode 100644 index 00000000..058b6785 --- /dev/null +++ b/src/tests/html/html_slot_element.html @@ -0,0 +1,66 @@ + + + + + +default +

default

+

default

xx other
+More

default2

!!
+ +