Skip to content

Commit 26fada3

Browse files
committed
Separate NodeIterator impl, fix _filter
1 parent cf5815e commit 26fada3

File tree

4 files changed

+156
-78
lines changed

4 files changed

+156
-78
lines changed

src/browser/dom/document.zig

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -265,7 +265,7 @@ pub const Document = struct {
265265
return try TreeWalker.init(root, what_to_show, filter);
266266
}
267267

268-
pub fn _createNodeIterator(_: *parser.Document, root: *parser.Node, what_to_show: ?u32, filter: ?TreeWalker.TreeWalkerOpts) !NodeIterator {
268+
pub fn _createNodeIterator(_: *parser.Document, root: *parser.Node, what_to_show: ?u32, filter: ?NodeIterator.NodeIteratorOpts) !NodeIterator {
269269
return try NodeIterator.init(root, what_to_show, filter);
270270
}
271271

src/browser/dom/node_filter.zig

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717
// along with this program. If not, see <https://www.gnu.org/licenses/>.
1818

1919
const std = @import("std");
20+
const parser = @import("../netsurf.zig");
21+
const Env = @import("../env.zig").Env;
2022

2123
pub const NodeFilter = struct {
2224
pub const _FILTER_ACCEPT: u16 = 1;
@@ -38,6 +40,39 @@ pub const NodeFilter = struct {
3840
pub const _SHOW_NOTATION: u32 = 0b100000000000;
3941
};
4042

43+
const VerifyResult = enum { accept, skip, reject };
44+
45+
pub fn verify(what_to_show: u32, filter: ?Env.Function, node: *parser.Node) !VerifyResult {
46+
const node_type = try parser.nodeType(node);
47+
48+
// Verify that we can show this node type.
49+
if (!switch (node_type) {
50+
.attribute => what_to_show & NodeFilter._SHOW_ATTRIBUTE != 0,
51+
.cdata_section => what_to_show & NodeFilter._SHOW_CDATA_SECTION != 0,
52+
.comment => what_to_show & NodeFilter._SHOW_COMMENT != 0,
53+
.document => what_to_show & NodeFilter._SHOW_DOCUMENT != 0,
54+
.document_fragment => what_to_show & NodeFilter._SHOW_DOCUMENT_FRAGMENT != 0,
55+
.document_type => what_to_show & NodeFilter._SHOW_DOCUMENT_TYPE != 0,
56+
.element => what_to_show & NodeFilter._SHOW_ELEMENT != 0,
57+
.entity => what_to_show & NodeFilter._SHOW_ENTITY != 0,
58+
.entity_reference => what_to_show & NodeFilter._SHOW_ENTITY_REFERENCE != 0,
59+
.notation => what_to_show & NodeFilter._SHOW_NOTATION != 0,
60+
.processing_instruction => what_to_show & NodeFilter._SHOW_PROCESSING_INSTRUCTION != 0,
61+
.text => what_to_show & NodeFilter._SHOW_TEXT != 0,
62+
}) return .reject;
63+
64+
// Verify that we aren't filtering it out.
65+
if (filter) |f| {
66+
const acceptance = try f.call(u16, .{node});
67+
return switch (acceptance) {
68+
NodeFilter._FILTER_ACCEPT => .accept,
69+
NodeFilter._FILTER_REJECT => .reject,
70+
NodeFilter._FILTER_SKIP => .skip,
71+
else => .reject,
72+
};
73+
} else return .accept;
74+
}
75+
4176
const testing = @import("../../testing.zig");
4277
test "Browser.DOM.NodeFilter" {
4378
var runner = try testing.jsRunner(testing.tracking_allocator, .{});

src/browser/dom/node_iterator.zig

Lines changed: 105 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -19,54 +19,82 @@
1919
const std = @import("std");
2020
const parser = @import("../netsurf.zig");
2121
const Env = @import("../env.zig").Env;
22-
const TreeWalker = @import("tree_walker.zig").TreeWalker;
22+
const NodeFilter = @import("node_filter.zig");
2323

2424
// https://developer.mozilla.org/en-US/docs/Web/API/NodeIterator
25+
// While this is similar to TreeWalker it has it's own implementation as there are several suttle differences
26+
// For example:
27+
// - nextNode returns the reference node, whereas TreeWalker returns the next node
28+
// - Skip and reject are equivalent for NodeIterator, for TreeWalker they are different
2529
pub const NodeIterator = struct {
26-
walker: TreeWalker,
30+
root: *parser.Node,
31+
reference_node: *parser.Node,
32+
what_to_show: u32,
33+
filter: ?NodeIteratorOpts,
34+
filter_func: ?Env.Function,
35+
2736
pointer_before_current: bool = true,
2837

29-
pub fn init(node: *parser.Node, what_to_show: ?u32, filter: ?TreeWalker.TreeWalkerOpts) !NodeIterator {
30-
return .{ .walker = try TreeWalker.init(node, what_to_show, filter) };
38+
pub const NodeIteratorOpts = union(enum) {
39+
function: Env.Function,
40+
object: struct { acceptNode: Env.Function },
41+
};
42+
43+
pub fn init(node: *parser.Node, what_to_show: ?u32, filter: ?NodeIteratorOpts) !NodeIterator {
44+
var filter_func: ?Env.Function = null;
45+
if (filter) |f| {
46+
filter_func = switch (f) {
47+
.function => |func| func,
48+
.object => |o| o.acceptNode,
49+
};
50+
}
51+
52+
return .{
53+
.root = node,
54+
.reference_node = node,
55+
.what_to_show = what_to_show orelse NodeFilter.NodeFilter._SHOW_ALL,
56+
.filter = filter,
57+
.filter_func = filter_func,
58+
};
3159
}
3260

33-
pub fn get_filter(self: *const NodeIterator) ?Env.Function {
34-
return self.walker.filter;
61+
pub fn get_filter(self: *const NodeIterator) ?NodeIteratorOpts {
62+
return self.filter;
3563
}
3664

3765
pub fn get_pointerBeforeReferenceNode(self: *const NodeIterator) bool {
3866
return self.pointer_before_current;
3967
}
4068

4169
pub fn get_referenceNode(self: *const NodeIterator) *parser.Node {
42-
return self.walker.current_node;
70+
return self.reference_node;
4371
}
4472

4573
pub fn get_root(self: *const NodeIterator) *parser.Node {
46-
return self.walker.root;
74+
return self.root;
4775
}
4876

4977
pub fn get_whatToShow(self: *const NodeIterator) u32 {
50-
return self.walker.what_to_show;
78+
return self.what_to_show;
5179
}
5280

5381
pub fn _nextNode(self: *NodeIterator) !?*parser.Node {
5482
if (self.pointer_before_current) { // Unlike TreeWalker, NodeIterator starts at the first node
5583
self.pointer_before_current = false;
56-
if (.accept == try self.walker.verify(self.walker.current_node)) {
57-
return self.walker.current_node;
84+
if (.accept == try NodeFilter.verify(self.what_to_show, self.filter_func, self.reference_node)) {
85+
return self.reference_node;
5886
}
5987
}
6088

61-
if (try self.firstChild(self.walker.current_node)) |child| {
62-
self.walker.current_node = child;
89+
if (try self.firstChild(self.reference_node)) |child| {
90+
self.reference_node = child;
6391
return child;
6492
}
6593

66-
var current = self.walker.current_node;
67-
while (current != self.walker.root) {
68-
if (try self.walker.nextSibling(current)) |sibling| {
69-
self.walker.current_node = sibling;
94+
var current = self.reference_node;
95+
while (current != self.root) {
96+
if (try self.nextSibling(current)) |sibling| {
97+
self.reference_node = sibling;
7098
return sibling;
7199
}
72100

@@ -79,41 +107,41 @@ pub const NodeIterator = struct {
79107
pub fn _previousNode(self: *NodeIterator) !?*parser.Node {
80108
if (!self.pointer_before_current) {
81109
self.pointer_before_current = true;
82-
if (.accept == try self.walker.verify(self.walker.current_node)) {
83-
return self.walker.current_node; // Still need to verify as last may be first as well
110+
if (.accept == try NodeFilter.verify(self.what_to_show, self.filter_func, self.reference_node)) {
111+
return self.reference_node; // Still need to verify as last may be first as well
84112
}
85113
}
86-
if (self.walker.current_node == self.walker.root) return null;
114+
if (self.reference_node == self.root) return null;
87115

88-
var current = self.walker.current_node;
116+
var current = self.reference_node;
89117
while (try parser.nodePreviousSibling(current)) |previous| {
90118
current = previous;
91119

92-
switch (try self.walker.verify(current)) {
120+
switch (try NodeFilter.verify(self.what_to_show, self.filter_func, current)) {
93121
.accept => {
94122
// Get last child if it has one.
95123
if (try self.lastChild(current)) |child| {
96-
self.walker.current_node = child;
124+
self.reference_node = child;
97125
return child;
98126
}
99127

100128
// Otherwise, this node is our previous one.
101-
self.walker.current_node = current;
129+
self.reference_node = current;
102130
return current;
103131
},
104132
.reject, .skip => {
105133
// Get last child if it has one.
106134
if (try self.lastChild(current)) |child| {
107-
self.walker.current_node = child;
135+
self.reference_node = child;
108136
return child;
109137
}
110138
},
111139
}
112140
}
113141

114-
if (current != self.walker.root) {
115-
if (try self.walker.parentNode(current)) |parent| {
116-
self.walker.current_node = parent;
142+
if (current != self.root) {
143+
if (try self.parentNode(current)) |parent| {
144+
self.reference_node = parent;
117145
return parent;
118146
}
119147
}
@@ -129,7 +157,7 @@ pub const NodeIterator = struct {
129157
const index: u32 = @intCast(i);
130158
const child = (try parser.nodeListItem(children, index)) orelse return null;
131159

132-
switch (try self.walker.verify(child)) {
160+
switch (try NodeFilter.verify(self.what_to_show, self.filter_func, child)) {
133161
.accept => return child, // NOTE: Skip and reject are equivalent for NodeIterator, this is different from TreeWalker
134162
.reject, .skip => if (try self.firstChild(child)) |gchild| return gchild,
135163
}
@@ -147,14 +175,46 @@ pub const NodeIterator = struct {
147175
index -= 1;
148176
const child = (try parser.nodeListItem(children, index)) orelse return null;
149177

150-
switch (try self.walker.verify(child)) {
178+
switch (try NodeFilter.verify(self.what_to_show, self.filter_func, child)) {
151179
.accept => return child, // NOTE: Skip and reject are equivalent for NodeIterator, this is different from TreeWalker
152180
.reject, .skip => if (try self.lastChild(child)) |gchild| return gchild,
153181
}
154182
}
155183

156184
return null;
157185
}
186+
187+
// This implementation is actually the same as :TreeWalker
188+
fn parentNode(self: *const NodeIterator, node: *parser.Node) !?*parser.Node {
189+
if (self.root == node) return null;
190+
191+
var current = node;
192+
while (true) {
193+
if (current == self.root) return null;
194+
current = (try parser.nodeParentNode(current)) orelse return null;
195+
196+
switch (try NodeFilter.verify(self.what_to_show, self.filter_func, current)) {
197+
.accept => return current,
198+
.reject, .skip => continue,
199+
}
200+
}
201+
}
202+
203+
// This implementation is actually the same as :TreeWalker
204+
fn nextSibling(self: *const NodeIterator, node: *parser.Node) !?*parser.Node {
205+
var current = node;
206+
207+
while (true) {
208+
current = (try parser.nodeNextSibling(current)) orelse return null;
209+
210+
switch (try NodeFilter.verify(self.what_to_show, self.filter_func, current)) {
211+
.accept => return current,
212+
.skip, .reject => continue,
213+
}
214+
}
215+
216+
return null;
217+
}
158218
};
159219

160220
const testing = @import("../../testing.zig");
@@ -210,4 +270,19 @@ test "Browser.DOM.NodeFilter" {
210270
},
211271
.{ "notationIterator.previousNode()", "null" },
212272
}, .{});
273+
274+
try runner.testCases(&.{
275+
.{ "nodeIterator.filter.acceptNode(document.body)", "1" },
276+
.{ "notationIterator.filter", "null" },
277+
.{
278+
\\ const rejectIterator = document.createNodeIterator(
279+
\\ document.body,
280+
\\ NodeFilter.SHOW_ALL,
281+
\\ (e => { return NodeFilter.FILTER_REJECT}),
282+
\\ );
283+
\\ rejectIterator.filter(document.body);
284+
,
285+
"2",
286+
},
287+
}, .{});
213288
}

0 commit comments

Comments
 (0)