@@ -26,6 +26,7 @@ const Page = @import("../page.zig").Page;
2626const urlStitch = @import ("../../url.zig" ).URL .stitch ;
2727const URL = @import ("../url/url.zig" ).URL ;
2828const Node = @import ("../dom/node.zig" ).Node ;
29+ const NodeUnion = @import ("../dom/node.zig" ).Union ;
2930const Element = @import ("../dom/element.zig" ).Element ;
3031const DataSet = @import ("DataSet.zig" );
3132
@@ -85,6 +86,7 @@ pub const Interfaces = .{
8586 HTMLScriptElement ,
8687 HTMLSourceElement ,
8788 HTMLSpanElement ,
89+ HTMLSlotElement ,
8890 HTMLStyleElement ,
8991 HTMLTableElement ,
9092 HTMLTableCaptionElement ,
@@ -1008,6 +1010,101 @@ pub const HTMLSpanElement = struct {
10081010 pub const subtype = .node ;
10091011};
10101012
1013+ pub const HTMLSlotElement = struct {
1014+ pub const Self = parser .Slot ;
1015+ pub const prototype = * HTMLElement ;
1016+ pub const subtype = .node ;
1017+
1018+ pub fn get_name (self : * parser.Slot ) ! ? []const u8 {
1019+ return (try parser .elementGetAttribute (@ptrCast (@alignCast (self )), "name" )) orelse "" ;
1020+ }
1021+
1022+ pub fn set_name (self : * parser.Slot , value : []const u8 ) ! void {
1023+ return parser .elementSetAttribute (@ptrCast (@alignCast (self )), "name" , value );
1024+ }
1025+
1026+ const AssignedNodesOpts = struct {
1027+ flatten : bool = false ,
1028+ };
1029+ pub fn _assignedNodes (self : * parser.Slot , opts_ : ? AssignedNodesOpts , page : * Page ) ! []NodeUnion {
1030+ const opts = opts_ orelse AssignedNodesOpts { .flatten = false };
1031+
1032+ if (try findAssignedSlotNodes (self , opts , page )) | nodes | {
1033+ return nodes ;
1034+ }
1035+
1036+ if (! opts .flatten ) {
1037+ return &.{};
1038+ }
1039+
1040+ const node : * parser.Node = @ptrCast (@alignCast (self ));
1041+ const nl = try parser .nodeGetChildNodes (node );
1042+ const len = try parser .nodeListLength (nl );
1043+ if (len == 0 ) {
1044+ return &.{};
1045+ }
1046+
1047+ var assigned = try page .call_arena .alloc (NodeUnion , len );
1048+ var i : usize = 0 ;
1049+ while (true ) : (i += 1 ) {
1050+ const child = try parser .nodeListItem (nl , @intCast (i )) orelse break ;
1051+ assigned [i ] = try Node .toInterface (child );
1052+ }
1053+ return assigned [0.. i ];
1054+ }
1055+
1056+ fn findAssignedSlotNodes (self : * parser.Slot , opts : AssignedNodesOpts , page : * Page ) ! ? []NodeUnion {
1057+ if (opts .flatten ) {
1058+ log .warn (.web_api , "not implemented" , .{ .feature = "HTMLSlotElement flatten assignedNodes" });
1059+ }
1060+
1061+ const slot_name = try parser .elementGetAttribute (@ptrCast (@alignCast (self )), "name" );
1062+ const node : * parser.Node = @ptrCast (@alignCast (self ));
1063+ var root = try parser .nodeGetRootNode (node );
1064+ if (page .getNodeState (root )) | state | {
1065+ if (state .shadow_root ) | sr | {
1066+ root = @ptrCast (@alignCast (sr .host ));
1067+ }
1068+ }
1069+
1070+ var arr : std .ArrayList (NodeUnion ) = .empty ;
1071+ const w = @import ("../dom/walker.zig" ).WalkerChildren {};
1072+ var next : ? * parser.Node = null ;
1073+ while (true ) {
1074+ next = try w .get_next (root , next ) orelse break ;
1075+ if (try parser .nodeType (next .? ) != .element ) {
1076+ if (slot_name == null ) {
1077+ // default slot (with no name), takes everything
1078+ try arr .append (page .call_arena , try Node .toInterface (next .? ));
1079+ }
1080+ continue ;
1081+ }
1082+ const el : * parser.Element = @ptrCast (@alignCast (next .? ));
1083+ const element_slot = try parser .elementGetAttribute (el , "slot" );
1084+
1085+ if (nullableStringsAreEqual (slot_name , element_slot )) {
1086+ // either they're the same string or they are both null
1087+ try arr .append (page .call_arena , try Node .toInterface (next .? ));
1088+ continue ;
1089+ }
1090+ }
1091+ return if (arr .items .len == 0 ) null else arr .items ;
1092+ }
1093+
1094+ fn nullableStringsAreEqual (a : ? []const u8 , b : ? []const u8 ) bool {
1095+ if (a == null and b == null ) {
1096+ return true ;
1097+ }
1098+ if (a ) | aa | {
1099+ const bb = b orelse return false ;
1100+ return std .mem .eql (u8 , aa , bb );
1101+ }
1102+
1103+ // a is null, but b isn't (else the first guard clause would have hit)
1104+ return false ;
1105+ }
1106+ };
1107+
10111108pub const HTMLStyleElement = struct {
10121109 pub const Self = parser .Style ;
10131110 pub const prototype = * HTMLElement ;
@@ -1168,6 +1265,7 @@ pub fn toInterfaceFromTag(comptime T: type, e: *parser.Element, tag: parser.Tag)
11681265 .select = > .{ .HTMLSelectElement = @as (* parser .Select , @ptrCast (e )) },
11691266 .source = > .{ .HTMLSourceElement = @as (* parser .Source , @ptrCast (e )) },
11701267 .span = > .{ .HTMLSpanElement = @as (* parser .Span , @ptrCast (e )) },
1268+ .slot = > .{ .HTMLSlotElement = @as (* parser .Slot , @ptrCast (e )) },
11711269 .style = > .{ .HTMLStyleElement = @as (* parser .Style , @ptrCast (e )) },
11721270 .table = > .{ .HTMLTableElement = @as (* parser .Table , @ptrCast (e )) },
11731271 .caption = > .{ .HTMLTableCaptionElement = @as (* parser .TableCaption , @ptrCast (e )) },
@@ -1484,6 +1582,10 @@ test "Browser: HTML.HTMLScriptElement" {
14841582 try testing .htmlRunner ("html/script/inline_defer.html" );
14851583}
14861584
1585+ test "Browser: HTML.HTMLSlotElement" {
1586+ try testing .htmlRunner ("html/html_slot_element.html" );
1587+ }
1588+
14871589const Check = struct {
14881590 input : []const u8 ,
14891591 expected : ? []const u8 = null , // Needed when input != expected
0 commit comments