Reinstate active flag for iterators
[WebKit-https.git] / LayoutTests / imported / w3c / web-platform-tests / dom / traversal / TreeWalker.html
1 <!doctype html>
2 <title>TreeWalker tests</title>
3 <link rel="author" title="Aryeh Gregor" href=ayg@aryeh.name>
4 <meta name=timeout content=long>
5 <div id=log></div>
6 <script src=/resources/testharness.js></script>
7 <script src=/resources/testharnessreport.js></script>
8 <script src=../common.js></script>
9 <script>
10 "use strict";
11
12 // TODO .previousNode, .nextNode
13
14 test(function() {
15   var depth = 0;
16   var walker = document.createTreeWalker(document, NodeFilter.SHOW_ALL,
17     function() {
18       if (depth == 0) {
19         depth++;
20         walker.firstChild();
21       }
22       return NodeFilter.FILTER_ACCEPT;
23     });
24   walker.currentNode = document.body;
25   assert_throws("InvalidStateError", function() { walker.parentNode() });
26   depth--;
27   assert_throws("InvalidStateError", function() { walker.firstChild() });
28   depth--;
29   assert_throws("InvalidStateError", function() { walker.lastChild() });
30   depth--;
31   assert_throws("InvalidStateError", function() { walker.previousSibling() });
32   depth--;
33   assert_throws("InvalidStateError", function() { walker.nextSibling() });
34   depth--;
35   assert_throws("InvalidStateError", function() { walker.previousNode() });
36   depth--;
37   assert_throws("InvalidStateError", function() { walker.nextNode() });
38 }, "Recursive filters need to throw");
39
40 function filterNode(node, whatToShow, filter) {
41     // "If active flag is set throw an "InvalidStateError"."
42     // Ignore active flag for these tests, we aren't calling recursively
43     // TODO Test me
44
45     // "Let n be node's nodeType attribute value minus 1."
46     var n = node.nodeType - 1;
47
48     // "If the nth bit (where 0 is the least significant bit) of whatToShow is
49     // not set, return FILTER_SKIP."
50     if (!(whatToShow & (1 << n))) {
51         return NodeFilter.FILTER_SKIP;
52     }
53
54     // "If filter is null, return FILTER_ACCEPT."
55     if (!filter) {
56         return NodeFilter.FILTER_ACCEPT;
57     }
58
59     // "Set the active flag."
60     //
61     // "Let result be the return value of invoking filter."
62     //
63     // "Unset the active flag."
64     //
65     // "If an exception was thrown, re-throw the exception."
66     // TODO Test me
67     //
68     // "Return result."
69     return filter(node);
70 }
71
72 function testTraverseChildren(type, walker, root, whatToShow, filter) {
73     // TODO We don't test .currentNode other than the root
74     walker.currentNode = root;
75     assert_equals(walker.currentNode, root, "Setting .currentNode");
76
77     var expectedReturn = null;
78     var expectedCurrentNode = root;
79
80     // "To traverse children of type type, run these steps:
81     //
82     // "Let node be the value of the currentNode attribute."
83     var node = walker.currentNode;
84
85     // "Set node to node's first child if type is first, and node's last child
86     // if type is last."
87     node = type == "first" ? node.firstChild : node.lastChild;
88
89     // "Main: While node is not null, run these substeps:"
90     while (node) {
91         // "Filter node and let result be the return value."
92         var result = filterNode(node, whatToShow, filter);
93
94         // "If result is FILTER_ACCEPT, then set the currentNode attribute to
95         // node and return node."
96         if (result == NodeFilter.FILTER_ACCEPT) {
97             expectedCurrentNode = expectedReturn = node;
98             break;
99         }
100
101         // "If result is FILTER_SKIP, run these subsubsteps:"
102         if (result == NodeFilter.FILTER_SKIP) {
103             // "Let child be node's first child if type is first, and node's
104             // last child if type is last."
105             var child = type == "first" ? node.firstChild : node.lastChild;
106
107             // "If child is not null, set node to child and goto Main."
108             if (child) {
109                 node = child;
110                 continue;
111             }
112         }
113
114         // "While node is not null, run these subsubsteps:"
115         while (node) {
116             // "Let sibling be node's next sibling if type is first, and node's
117             // previous sibling if type is last."
118             var sibling = type == "first" ? node.nextSibling
119                 : node.previousSibling;
120
121             // "If sibling is not null, set node to sibling and goto Main."
122             if (sibling) {
123                 node = sibling;
124                 break;
125             }
126
127             // "Let parent be node's parent."
128             var parent = node.parentNode;
129
130             // "If parent is null, parent is root, or parent is currentNode
131             // attribute's value, return null."
132             if (!parent || parent == root || parent == walker.currentNode) {
133                 expectedReturn = node = null;
134                 break;
135             } else {
136             // "Otherwise, set node to parent."
137                 node = parent;
138             }
139         }
140     }
141
142     if (type == "first") {
143         assert_equals(walker.firstChild(), expectedReturn, ".firstChild()");
144         assert_equals(walker.currentNode, expectedCurrentNode,
145             ".currentNode after .firstChild()");
146     } else {
147         assert_equals(walker.lastChild(), expectedReturn, ".lastChild()");
148         assert_equals(walker.currentNode, expectedCurrentNode,
149         ".currentNode after .lastChild()");
150     }
151 }
152
153 function testTraverseSiblings(type, walker, root, whatToShow, filter) {
154     // TODO We don't test .currentNode other than the root's first or last child
155     if (!root.firstChild) {
156         // Nothing much to test
157
158         walker.currentNode = root;
159         assert_equals(walker.currentNode, root, "Setting .currentNode");
160
161         if (type == "next") {
162             assert_equals(walker.nextSibling(), null, ".nextSibling()");
163             assert_equals(walker.currentNode, root,
164                 ".currentNode after .nextSibling()")
165         } else {
166             assert_equals(walker.previousSibling(), null, ".previousSibling()");
167             assert_equals(walker.currentNode, root,
168                 ".currentNode after .previousSibling()")
169         }
170         return;
171     }
172
173     if (type == "next") {
174         walker.currentNode = root.firstChild;
175         assert_equals(walker.currentNode, root.firstChild,
176             "Setting .currentNode");
177     } else {
178         walker.currentNode = root.lastChild;
179         assert_equals(walker.currentNode, root.lastChild,
180             "Setting .currentNode");
181     }
182
183     var expectedReturn = null;
184     var expectedCurrentNode = type == "next" ? root.firstChild : root.lastChild;
185
186     // "To traverse siblings of type type run these steps:"
187     (function() {
188         // "Let node be the value of the currentNode attribute."
189         var node = type == "next" ? root.firstChild : root.lastChild;
190
191         // "If node is root, return null.
192         //
193         // "Run these substeps:
194         do {
195             // "Let sibling be node's next sibling if type is next, and node's
196             // previous sibling if type is previous."
197             var sibling = type == "next" ? node.nextSibling :
198                 node.previousSibling;
199
200             // "While sibling is not null, run these subsubsteps:"
201             while (sibling) {
202                 // "Set node to sibling."
203                 node = sibling;
204
205                 // "Filter node and let result be the return value."
206                 var result = filterNode(node, whatToShow, filter);
207
208                 // "If result is FILTER_ACCEPT, then set the currentNode
209                 // attribute to node and return node."
210                 if (result == NodeFilter.FILTER_ACCEPT) {
211                     expectedCurrentNode = expectedReturn = node;
212                     return;
213                 }
214
215                 // "Set sibling to node's first child if type is next, and
216                 // node's last child if type is previous."
217                 sibling = type == "next" ? node.firstChild : node.lastChild;
218
219                 // "If result is FILTER_REJECT or sibling is null, then set
220                 // sibling to node's next sibling if type is next, and node's
221                 // previous sibling if type is previous."
222                 if (result == NodeFilter.FILTER_REJECT || !sibling) {
223                     sibling = type == "next" ? node.nextSibling :
224                         node.previousSibling;
225                 }
226             }
227
228             // "Set node to its parent."
229             node = node.parentNode;
230
231             // "If node is null or is root, return null.
232             if (!node || node == root) {
233                 return;
234             }
235             // "Filter node and if the return value is FILTER_ACCEPT, then
236             // return null."
237             if (filterNode(node, whatToShow, filter)) {
238                 return;
239             }
240
241             // "Run these substeps again."
242         } while (true);
243     })();
244
245     if (type == "next") {
246         assert_equals(walker.nextSibling(), expectedReturn, ".nextSibling()");
247         assert_equals(walker.currentNode, expectedCurrentNode,
248             ".currentNode after .nextSibling()");
249     } else {
250         assert_equals(walker.previousSibling(), expectedReturn, ".previousSibling()");
251         assert_equals(walker.currentNode, expectedCurrentNode,
252             ".currentNode after .previousSibling()");
253     }
254 }
255
256 function testWalker(root, whatToShow, filter) {
257     var walker = document.createTreeWalker(root, whatToShow, filter);
258
259     assert_equals(walker.root, root, ".root");
260     assert_equals(walker.whatToShow, whatToShow, ".whatToShow");
261     assert_equals(walker.filter, filter, ".filter");
262     assert_equals(walker.currentNode, root, ".currentNode");
263
264     var expectedReturn = null;
265     var expectedCurrentNode = walker.currentNode;
266     // "The parentNode() method must run these steps:"
267     //
268     // "Let node be the value of the currentNode attribute."
269     var node = walker.currentNode;
270
271     // "While node is not null and is not root, run these substeps:"
272     while (node && node != root) {
273         // "Let node be node's parent."
274         node = node.parentNode;
275
276         // "If node is not null and filtering node returns FILTER_ACCEPT, then
277         // set the currentNode attribute to node, return node."
278         if (node && filterNode(node, whatToShow, filter) ==
279         NodeFilter.FILTER_ACCEPT) {
280             expectedCurrentNode = expectedReturn = node;
281         }
282     }
283     assert_equals(walker.parentNode(), expectedReturn, ".parentNode()");
284     assert_equals(walker.currentNode, expectedCurrentNode,
285         ".currentNode after .parentNode()");
286
287     testTraverseChildren("first", walker, root, whatToShow, filter);
288     testTraverseChildren("last", walker, root, whatToShow, filter);
289
290     testTraverseSiblings("next", walker, root, whatToShow, filter);
291     testTraverseSiblings("previous", walker, root, whatToShow, filter);
292 }
293
294 var whatToShows = [
295     "0",
296     "0xFFFFFFFF",
297     "NodeFilter.SHOW_ELEMENT",
298     "NodeFilter.SHOW_ATTRIBUTE",
299     "NodeFilter.SHOW_ELEMENT | NodeFilter.SHOW_DOCUMENT",
300 ];
301
302 var callbacks = [
303     "null",
304     "(function(node) { return true })",
305     "(function(node) { return false })",
306     "(function(node) { return node.nodeName[0] == '#' })",
307 ];
308
309 var tests = [];
310 for (var i = 0; i < testNodes.length; i++) {
311     for (var j = 0; j < whatToShows.length; j++) {
312         for (var k = 0; k < callbacks.length; k++) {
313             tests.push([
314                 "document.createTreeWalker(" + testNodes[i] +
315                     ", " + whatToShows[j] + ", " + callbacks[k] + ")",
316                 eval(testNodes[i]), eval(whatToShows[j]), eval(callbacks[k])
317             ]);
318         }
319     }
320 }
321 generate_tests(testWalker, tests);
322
323 testDiv.style.display = "none";
324 </script>