1717// along with this program. If not, see <https://www.gnu.org/licenses/>.
1818
1919const std = @import ("std" );
20+
2021const parser = @import ("../netsurf.zig" );
2122const Env = @import ("../env.zig" ).Env ;
2223const NodeFilter = @import ("node_filter.zig" );
2324const Node = @import ("node.zig" ).Node ;
2425const NodeUnion = @import ("node.zig" ).Union ;
26+ const DOMException = @import ("exceptions.zig" ).DOMException ;
2527
2628// https://developer.mozilla.org/en-US/docs/Web/API/NodeIterator
2729// While this is similar to TreeWalker it has its own implementation as there are several subtle differences
2830// For example:
2931// - nextNode returns the reference node, whereas TreeWalker returns the next node
3032// - Skip and reject are equivalent for NodeIterator, for TreeWalker they are different
3133pub const NodeIterator = struct {
34+ pub const Exception = DOMException ;
35+
3236 root : * parser.Node ,
3337 reference_node : * parser.Node ,
3438 what_to_show : u32 ,
3539 filter : ? NodeIteratorOpts ,
3640 filter_func : ? Env.Function ,
37-
3841 pointer_before_current : bool = true ,
42+ // used to track / block recursive filters
43+ is_in_callback : bool = false ,
44+
45+ // One of the few cases where null and undefined resolve to different default.
46+ // We need the raw JsObject so that we can probe the tri state:
47+ // null, undefined or i32.
48+ pub const WhatToShow = Env .JsObject ;
3949
4050 pub const NodeIteratorOpts = union (enum ) {
4151 function : Env.Function ,
4252 object : struct { acceptNode : Env .Function },
4353 };
4454
45- pub fn init (node : * parser.Node , what_to_show : ? u32 , filter : ? NodeIteratorOpts ) ! NodeIterator {
55+ pub fn init (node : * parser.Node , what_to_show_ : ? WhatToShow , filter : ? NodeIteratorOpts ) ! NodeIterator {
4656 var filter_func : ? Env.Function = null ;
4757 if (filter ) | f | {
4858 filter_func = switch (f ) {
@@ -51,10 +61,21 @@ pub const NodeIterator = struct {
5161 };
5262 }
5363
64+ var what_to_show : u32 = undefined ;
65+ if (what_to_show_ ) | wts | {
66+ switch (try wts .triState (NodeIterator , "what_to_show" , u32 )) {
67+ .null = > what_to_show = 0 ,
68+ .undefined = > what_to_show = NodeFilter .NodeFilter ._SHOW_ALL ,
69+ .value = > | v | what_to_show = v ,
70+ }
71+ } else {
72+ what_to_show = NodeFilter .NodeFilter ._SHOW_ALL ;
73+ }
74+
5475 return .{
5576 .root = node ,
5677 .reference_node = node ,
57- .what_to_show = what_to_show orelse NodeFilter . NodeFilter . _SHOW_ALL ,
78+ .what_to_show = what_to_show ,
5879 .filter = filter ,
5980 .filter_func = filter_func ,
6081 };
@@ -81,9 +102,13 @@ pub const NodeIterator = struct {
81102 }
82103
83104 pub fn _nextNode (self : * NodeIterator ) ! ? NodeUnion {
84- if (self .pointer_before_current ) { // Unlike TreeWalker, NodeIterator starts at the first node
85- self .pointer_before_current = false ;
105+ try self .callbackStart ();
106+ defer self .callbackEnd ();
107+
108+ if (self .pointer_before_current ) {
109+ // Unlike TreeWalker, NodeIterator starts at the first node
86110 if (.accept == try NodeFilter .verify (self .what_to_show , self .filter_func , self .reference_node )) {
111+ self .pointer_before_current = false ;
87112 return try Node .toInterface (self .reference_node );
88113 }
89114 }
@@ -107,13 +132,19 @@ pub const NodeIterator = struct {
107132 }
108133
109134 pub fn _previousNode (self : * NodeIterator ) ! ? NodeUnion {
135+ try self .callbackStart ();
136+ defer self .callbackEnd ();
137+
110138 if (! self .pointer_before_current ) {
111- self .pointer_before_current = true ;
112139 if (.accept == try NodeFilter .verify (self .what_to_show , self .filter_func , self .reference_node )) {
113- return try Node .toInterface (self .reference_node ); // Still need to verify as last may be first as well
140+ self .pointer_before_current = true ;
141+ // Still need to verify as last may be first as well
142+ return try Node .toInterface (self .reference_node );
114143 }
115144 }
116- if (self .reference_node == self .root ) return null ;
145+ if (self .reference_node == self .root ) {
146+ return null ;
147+ }
117148
118149 var current = self .reference_node ;
119150 while (try parser .nodePreviousSibling (current )) | previous | {
@@ -151,6 +182,11 @@ pub const NodeIterator = struct {
151182 return null ;
152183 }
153184
185+ pub fn _detach (self : * const NodeIterator ) void {
186+ // no-op as per spec
187+ _ = self ;
188+ }
189+
154190 fn firstChild (self : * const NodeIterator , node : * parser.Node ) ! ? * parser.Node {
155191 const children = try parser .nodeGetChildNodes (node );
156192 const child_count = try parser .nodeListLength (children );
@@ -217,6 +253,18 @@ pub const NodeIterator = struct {
217253
218254 return null ;
219255 }
256+
257+ fn callbackStart (self : * NodeIterator ) ! void {
258+ if (self .is_in_callback ) {
259+ // this is the correct DOMExeption
260+ return error .InvalidState ;
261+ }
262+ self .is_in_callback = true ;
263+ }
264+
265+ fn callbackEnd (self : * NodeIterator ) void {
266+ self .is_in_callback = false ;
267+ }
220268};
221269
222270const testing = @import ("../../testing.zig" );
0 commit comments