diff --git a/src/browser/ScriptManager.zig b/src/browser/ScriptManager.zig index ee8ee01c..06e94083 100644 --- a/src/browser/ScriptManager.zig +++ b/src/browser/ScriptManager.zig @@ -684,7 +684,7 @@ const Script = struct { fn errorCallback(ctx: *anyopaque, err: anyerror) void { const self: *Script = @ptrCast(@alignCast(ctx)); log.warn(.http, "script fetch error", .{ - .err = err , + .err = err, .req = self.url, .mode = self.mode, .kind = self.kind, diff --git a/src/browser/js/bridge.zig b/src/browser/js/bridge.zig index faa61a2a..7f67f3a0 100644 --- a/src/browser/js/bridge.zig +++ b/src/browser/js/bridge.zig @@ -288,6 +288,7 @@ pub const Iterator = struct { const Opts = struct { async: bool = false, + null_as_undefined: bool = false, }; fn init(comptime T: type, comptime struct_or_func: anytype, comptime opts: Opts) Iterator { diff --git a/src/browser/tests/element/class_list.html b/src/browser/tests/element/class_list.html index 91a5d18d..d29d7612 100644 --- a/src/browser/tests/element/class_list.html +++ b/src/browser/tests/element/class_list.html @@ -332,3 +332,272 @@ testing.expectEqual('a c e b f', div.className); } + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/browser/webapi/collections.zig b/src/browser/webapi/collections.zig index eead81b9..3e36e05f 100644 --- a/src/browser/webapi/collections.zig +++ b/src/browser/webapi/collections.zig @@ -34,6 +34,8 @@ pub fn registerTypes() []const type { @import("collections/HTMLAllCollection.zig").Iterator, HTMLOptionsCollection, DOMTokenList, - DOMTokenList.Iterator, + DOMTokenList.KeyIterator, + DOMTokenList.ValueIterator, + DOMTokenList.EntryIterator, }; } diff --git a/src/browser/webapi/collections/DOMTokenList.zig b/src/browser/webapi/collections/DOMTokenList.zig index 67ba027f..b47ae78d 100644 --- a/src/browser/webapi/collections/DOMTokenList.zig +++ b/src/browser/webapi/collections/DOMTokenList.zig @@ -17,11 +17,12 @@ // along with this program. If not, see . const std = @import("std"); +const log = @import("../../../log.zig"); const js = @import("../../js/js.zig"); +const Page = @import("../../Page.zig"); const Element = @import("../Element.zig"); const GenericIterator = @import("iterator.zig").Entry; -const Page = @import("../../Page.zig"); pub const DOMTokenList = @This(); @@ -32,6 +33,10 @@ pub const DOMTokenList = @This(); _element: *Element, _attribute_name: []const u8, +pub const KeyIterator = GenericIterator(Iterator, "0"); +pub const ValueIterator = GenericIterator(Iterator, "1"); +pub const EntryIterator = GenericIterator(Iterator, null); + const Lookup = std.StringArrayHashMapUnmanaged(void); const WHITESPACE = " \t\n\r\x0C"; @@ -158,21 +163,40 @@ pub fn setValue(self: *DOMTokenList, value: []const u8, page: *Page) !void { try self._element.setAttribute(self._attribute_name, value, page); } -pub fn iterator(self: *const DOMTokenList, page: *Page) !*Iterator { - return Iterator.init(.{ .list = self }, page); +pub fn keys(self: *DOMTokenList, page: *Page) !*KeyIterator { + return .init(.{ .list = self }, page); } -pub const Iterator = GenericIterator(struct { - index: usize = 0, - list: *const DOMTokenList, +pub fn values(self: *DOMTokenList, page: *Page) !*ValueIterator { + return .init(.{ .list = self }, page); +} - // TODO: the underlying list.iten is very inefficient! - pub fn next(self: *@This(), page: *Page) !?[]const u8 { - const index = self.index; - self.index = index + 1; - return self.list.item(index, page); +pub fn entries(self: *DOMTokenList, page: *Page) !*EntryIterator { + return .init(.{ .list = self }, page); +} + +pub fn forEach(self: *DOMTokenList, cb_: js.Function, js_this_: ?js.Object, page: *Page) !void { + const cb = if (js_this_) |js_this| try cb_.withThis(js_this) else cb_; + + const allocator = page.call_arena; + + var i: i32 = 0; + var seen: std.StringArrayHashMapUnmanaged(void) = .empty; + + var it = std.mem.tokenizeAny(u8, self.getValue(), WHITESPACE); + while (it.next()) |token| { + const gop = try seen.getOrPut(allocator, token); + if (gop.found_existing) { + continue; + } + var result: js.Function.Result = undefined; + cb.tryCall(void, .{ token, i, self }, &result) catch { + log.debug(.js, "forEach callback", .{ .err = result.exception, .stack = result.stack, .source = "DOMTokenList" }); + return; + }; + i += 1; } -}, null); +} fn getTokens(self: *const DOMTokenList, page: *Page) !Lookup { const value = self.getValue(); @@ -205,6 +229,20 @@ fn updateAttribute(self: *DOMTokenList, tokens: Lookup, page: *Page) !void { try self._element.setAttribute(self._attribute_name, joined, page); } +const Iterator = struct { + index: u32 = 0, + list: *DOMTokenList, + + const Entry = struct { u32, []const u8 }; + + pub fn next(self: *Iterator, page: *Page) !?Entry { + const index = self.index; + const node = try self.list.item(index, page) orelse return null; + self.index = index + 1; + return .{ index, node }; + } +}; + pub const JsApi = struct { pub const bridge = js.Bridge(DOMTokenList); @@ -229,6 +267,11 @@ pub const JsApi = struct { pub const toggle = bridge.function(DOMTokenList.toggle, .{ .dom_exception = true }); pub const replace = bridge.function(DOMTokenList.replace, .{ .dom_exception = true }); pub const value = bridge.accessor(DOMTokenList.getValue, DOMTokenList.setValue, .{}); - pub const symbol_iterator = bridge.iterator(DOMTokenList.iterator, .{}); + pub const toString = bridge.function(DOMTokenList.getValue, .{}); + pub const keys = bridge.function(DOMTokenList.keys, .{}); + pub const values = bridge.function(DOMTokenList.values, .{}); + pub const entries = bridge.function(DOMTokenList.entries, .{}); + pub const symbol_iterator = bridge.iterator(DOMTokenList.values, .{}); + pub const forEach = bridge.function(DOMTokenList.forEach, .{}); pub const @"[]" = bridge.indexed(DOMTokenList.item, .{ .null_as_undefined = true }); }; diff --git a/src/browser/webapi/collections/iterator.zig b/src/browser/webapi/collections/iterator.zig index 2c16ed85..0c063e84 100644 --- a/src/browser/webapi/collections/iterator.zig +++ b/src/browser/webapi/collections/iterator.zig @@ -21,11 +21,6 @@ const js = @import("../../js/js.zig"); const Page = @import("../../Page.zig"); pub fn Entry(comptime Inner: type, comptime field: ?[]const u8) type { - // const InnerStruct = switch (@typeInfo(Inner)) { - // .@"struct" => Inner, - // .pointer => |ptr| ptr.child, - // else => @compileError("invalid iterator type"), - // }; const InnerStruct = Inner; const R = reflect(InnerStruct, field); @@ -68,7 +63,7 @@ pub fn Entry(comptime Inner: type, comptime field: ?[]const u8) type { pub var class_id: bridge.ClassId = undefined; }; - pub const next = bridge.function(Self.next, .{}); + pub const next = bridge.function(Self.next, .{ .null_as_undefined = true }); pub const symbol_iterator = bridge.iterator(Self, .{}); }; };