diff --git a/src/browser/webapi/TreeWalker.zig b/src/browser/webapi/TreeWalker.zig index b6df32fd..e4c19de7 100644 --- a/src/browser/webapi/TreeWalker.zig +++ b/src/browser/webapi/TreeWalker.zig @@ -31,6 +31,7 @@ const Mode = enum { pub fn TreeWalker(comptime mode: Mode) type { return struct { + _current: ?*Node = null, _next: ?*Node, _root: *Node, @@ -47,37 +48,74 @@ pub fn TreeWalker(comptime mode: Mode) type { pub fn next(self: *Self) ?*Node { const node = self._next orelse return null; + self._current = node; if (comptime mode == .children) { self._next = Node.linkToNodeOrNull(node._child_link.next); return node; } - if (node._children) |children| { - self._next = children.first(); - } else if (node._child_link.next) |n| { - self._next = Node.linkToNode(n); - } else { - // No children, no next sibling - walk up until we find a next sibling or hit root - var current = node._parent; - while (current) |parent| { - if (parent == self._root) { - self._next = null; - break; - } - if (parent._child_link.next) |next_sibling| { - self._next = Node.linkToNode(next_sibling); - break; - } - current = parent._parent; - } else { - self._next = null; - } - } + self._next = self.computeNextInDocumentOrder(node); return node; } + pub fn skipChildren(self: *Self) void { + if (comptime mode == .children) return; + const current = self._current orelse return; + self._next = self.computeNextSiblingOrUncle(current); + } + + pub fn nextSibling(self: *Self) ?*Node { + const current = self._current orelse return null; + const sibling = Node.linkToNodeOrNull(current._child_link.next) orelse return null; + + self._current = sibling; + if (comptime mode == .children) { + self._next = Node.linkToNodeOrNull(sibling._child_link.next); + } else { + self._next = self.computeNextInDocumentOrder(sibling); + } + return sibling; + } + + pub fn previousSibling(self: *Self) ?*Node { + const current = self._current orelse return null; + const sibling = Node.linkToNodeOrNull(current._child_link.prev) orelse return null; + + self._current = sibling; + if (comptime mode == .children) { + self._next = Node.linkToNodeOrNull(sibling._child_link.next); + } else { + self._next = self.computeNextInDocumentOrder(sibling); + } + return sibling; + } + + fn computeNextInDocumentOrder(self: *Self, node: *Node) ?*Node { + if (node._children) |children| { + return children.first(); + } + return self.computeNextSiblingOrUncle(node); + } + + fn computeNextSiblingOrUncle(self: *Self, node: *Node) ?*Node { + if (node._child_link.next) |n| { + return Node.linkToNode(n); + } + + var current = node._parent; + while (current) |parent| { + if (parent == self._root) return null; + if (parent._child_link.next) |next_sibling| { + return Node.linkToNode(next_sibling); + } + current = parent._parent; + } + return null; + } + pub fn reset(self: *Self) void { + self._current = null; self._next = firstNext(self._root); } @@ -147,3 +185,67 @@ pub fn TreeWalker(comptime mode: Mode) type { }; }; } + +test "TreeWalker: skipChildren" { + const testing = @import("../../testing.zig"); + const page = try testing.test_session.createPage(); + defer testing.test_session.removePage(); + const doc = page.window._document; + + //
B
+ //) + tw.skipChildren(); + try testing.expect(tw.next() == p.asNode()); + + try testing.expect(tw.next() == null); +} + +test "TreeWalker: sibling navigation" { + const testing = @import("../../testing.zig"); + const page = try testing.test_session.createPage(); + defer testing.test_session.removePage(); + const doc = page.window._document; + + //
B
+ //