Line data Source code
1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : /* vim: set ts=4 et sw=4 tw=80: */
3 : /* This Source Code Form is subject to the terms of the Mozilla Public
4 : * License, v. 2.0. If a copy of the MPL was not distributed with this
5 : * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 :
7 : /*
8 : * Implementation of DOM Traversal's nsIDOMTreeWalker
9 : */
10 :
11 : #include "mozilla/dom/TreeWalker.h"
12 :
13 : #include "nsIContent.h"
14 : #include "nsIDOMNode.h"
15 : #include "nsError.h"
16 : #include "nsINode.h"
17 : #include "nsContentUtils.h"
18 : #include "mozilla/dom/TreeWalkerBinding.h"
19 :
20 : namespace mozilla {
21 : namespace dom {
22 :
23 : /*
24 : * Factories, constructors and destructors
25 : */
26 :
27 0 : TreeWalker::TreeWalker(nsINode *aRoot,
28 : uint32_t aWhatToShow,
29 0 : NodeFilterHolder aFilter) :
30 0 : nsTraversal(aRoot, aWhatToShow, Move(aFilter)),
31 0 : mCurrentNode(aRoot)
32 : {
33 0 : }
34 :
35 0 : TreeWalker::~TreeWalker()
36 : {
37 : /* destructor code */
38 0 : }
39 :
40 : /*
41 : * nsISupports and cycle collection stuff
42 : */
43 :
44 0 : NS_IMPL_CYCLE_COLLECTION(TreeWalker, mFilter, mCurrentNode, mRoot)
45 :
46 : // QueryInterface implementation for TreeWalker
47 0 : NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(TreeWalker)
48 0 : NS_INTERFACE_MAP_ENTRY(nsIDOMTreeWalker)
49 0 : NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMTreeWalker)
50 0 : NS_INTERFACE_MAP_END
51 :
52 : // Have to pass in dom::TreeWalker because a11y has an a11y::TreeWalker that
53 : // passes TreeWalker so refcount logging would get confused on the name
54 : // collision.
55 0 : NS_IMPL_CYCLE_COLLECTING_ADDREF(dom::TreeWalker)
56 0 : NS_IMPL_CYCLE_COLLECTING_RELEASE(dom::TreeWalker)
57 :
58 :
59 :
60 : /*
61 : * nsIDOMTreeWalker Getters/Setters
62 : */
63 :
64 0 : NS_IMETHODIMP TreeWalker::GetRoot(nsIDOMNode * *aRoot)
65 : {
66 0 : NS_ADDREF(*aRoot = Root()->AsDOMNode());
67 0 : return NS_OK;
68 : }
69 :
70 0 : NS_IMETHODIMP TreeWalker::GetWhatToShow(uint32_t *aWhatToShow)
71 : {
72 0 : *aWhatToShow = WhatToShow();
73 0 : return NS_OK;
74 : }
75 :
76 0 : NS_IMETHODIMP TreeWalker::GetFilter(nsIDOMNodeFilter * *aFilter)
77 : {
78 0 : NS_ENSURE_ARG_POINTER(aFilter);
79 :
80 0 : *aFilter = mFilter.ToXPCOMCallback().take();
81 :
82 0 : return NS_OK;
83 : }
84 :
85 0 : NS_IMETHODIMP TreeWalker::GetCurrentNode(nsIDOMNode * *aCurrentNode)
86 : {
87 0 : if (mCurrentNode) {
88 0 : return CallQueryInterface(mCurrentNode, aCurrentNode);
89 : }
90 :
91 0 : *aCurrentNode = nullptr;
92 :
93 0 : return NS_OK;
94 : }
95 0 : NS_IMETHODIMP TreeWalker::SetCurrentNode(nsIDOMNode * aCurrentNode)
96 : {
97 0 : NS_ENSURE_TRUE(aCurrentNode, NS_ERROR_DOM_NOT_SUPPORTED_ERR);
98 0 : NS_ENSURE_TRUE(mRoot, NS_ERROR_UNEXPECTED);
99 :
100 0 : nsCOMPtr<nsINode> node = do_QueryInterface(aCurrentNode);
101 0 : NS_ENSURE_TRUE(node, NS_ERROR_UNEXPECTED);
102 :
103 0 : ErrorResult rv;
104 0 : SetCurrentNode(*node, rv);
105 0 : return rv.StealNSResult();
106 : }
107 :
108 : void
109 0 : TreeWalker::SetCurrentNode(nsINode& aNode, ErrorResult& aResult)
110 : {
111 0 : aResult = nsContentUtils::CheckSameOrigin(mRoot, &aNode);
112 0 : if (aResult.Failed()) {
113 0 : return;
114 : }
115 :
116 0 : mCurrentNode = &aNode;
117 : }
118 :
119 : /*
120 : * nsIDOMTreeWalker functions
121 : */
122 :
123 0 : NS_IMETHODIMP TreeWalker::ParentNode(nsIDOMNode **_retval)
124 : {
125 0 : return ImplNodeGetter(&TreeWalker::ParentNode, _retval);
126 : }
127 :
128 : already_AddRefed<nsINode>
129 0 : TreeWalker::ParentNode(ErrorResult& aResult)
130 : {
131 0 : nsCOMPtr<nsINode> node = mCurrentNode;
132 :
133 0 : while (node && node != mRoot) {
134 0 : node = node->GetParentNode();
135 :
136 0 : if (node) {
137 0 : int16_t filtered = TestNode(node, aResult);
138 0 : if (aResult.Failed()) {
139 0 : return nullptr;
140 : }
141 0 : if (filtered == nsIDOMNodeFilter::FILTER_ACCEPT) {
142 0 : mCurrentNode = node;
143 0 : return node.forget();
144 : }
145 : }
146 : }
147 :
148 0 : return nullptr;
149 : }
150 :
151 0 : NS_IMETHODIMP TreeWalker::FirstChild(nsIDOMNode **_retval)
152 : {
153 0 : return ImplNodeGetter(&TreeWalker::FirstChild, _retval);
154 : }
155 :
156 : already_AddRefed<nsINode>
157 0 : TreeWalker::FirstChild(ErrorResult& aResult)
158 : {
159 0 : return FirstChildInternal(false, aResult);
160 : }
161 :
162 0 : NS_IMETHODIMP TreeWalker::LastChild(nsIDOMNode **_retval)
163 : {
164 0 : return ImplNodeGetter(&TreeWalker::LastChild, _retval);
165 : }
166 :
167 : already_AddRefed<nsINode>
168 0 : TreeWalker::LastChild(ErrorResult& aResult)
169 : {
170 0 : return FirstChildInternal(true, aResult);
171 : }
172 :
173 0 : NS_IMETHODIMP TreeWalker::PreviousSibling(nsIDOMNode **_retval)
174 : {
175 0 : return ImplNodeGetter(&TreeWalker::PreviousSibling, _retval);
176 : }
177 :
178 : already_AddRefed<nsINode>
179 0 : TreeWalker::PreviousSibling(ErrorResult& aResult)
180 : {
181 0 : return NextSiblingInternal(true, aResult);
182 : }
183 :
184 0 : NS_IMETHODIMP TreeWalker::NextSibling(nsIDOMNode **_retval)
185 : {
186 0 : return ImplNodeGetter(&TreeWalker::NextSibling, _retval);
187 : }
188 :
189 : already_AddRefed<nsINode>
190 0 : TreeWalker::NextSibling(ErrorResult& aResult)
191 : {
192 0 : return NextSiblingInternal(false, aResult);
193 : }
194 :
195 0 : NS_IMETHODIMP TreeWalker::PreviousNode(nsIDOMNode **_retval)
196 : {
197 0 : return ImplNodeGetter(&TreeWalker::PreviousNode, _retval);
198 : }
199 :
200 : already_AddRefed<nsINode>
201 0 : TreeWalker::PreviousNode(ErrorResult& aResult)
202 : {
203 0 : nsCOMPtr<nsINode> node = mCurrentNode;
204 :
205 0 : while (node != mRoot) {
206 0 : while (nsINode *previousSibling = node->GetPreviousSibling()) {
207 0 : node = previousSibling;
208 :
209 0 : int16_t filtered = TestNode(node, aResult);
210 0 : if (aResult.Failed()) {
211 0 : return nullptr;
212 : }
213 :
214 : nsINode *lastChild;
215 0 : while (filtered != nsIDOMNodeFilter::FILTER_REJECT &&
216 0 : (lastChild = node->GetLastChild())) {
217 0 : node = lastChild;
218 0 : filtered = TestNode(node, aResult);
219 0 : if (aResult.Failed()) {
220 0 : return nullptr;
221 : }
222 : }
223 :
224 0 : if (filtered == nsIDOMNodeFilter::FILTER_ACCEPT) {
225 0 : mCurrentNode = node;
226 0 : return node.forget();
227 : }
228 0 : }
229 :
230 0 : if (node == mRoot) {
231 0 : break;
232 : }
233 :
234 0 : node = node->GetParentNode();
235 0 : if (!node) {
236 0 : break;
237 : }
238 :
239 0 : int16_t filtered = TestNode(node, aResult);
240 0 : if (aResult.Failed()) {
241 0 : return nullptr;
242 : }
243 :
244 0 : if (filtered == nsIDOMNodeFilter::FILTER_ACCEPT) {
245 0 : mCurrentNode = node;
246 0 : return node.forget();
247 : }
248 : }
249 :
250 0 : return nullptr;
251 : }
252 :
253 0 : NS_IMETHODIMP TreeWalker::NextNode(nsIDOMNode **_retval)
254 : {
255 0 : return ImplNodeGetter(&TreeWalker::NextNode, _retval);
256 : }
257 :
258 : already_AddRefed<nsINode>
259 0 : TreeWalker::NextNode(ErrorResult& aResult)
260 : {
261 0 : int16_t filtered = nsIDOMNodeFilter::FILTER_ACCEPT; // pre-init for inner loop
262 :
263 0 : nsCOMPtr<nsINode> node = mCurrentNode;
264 :
265 : while (1) {
266 :
267 : nsINode *firstChild;
268 0 : while (filtered != nsIDOMNodeFilter::FILTER_REJECT &&
269 0 : (firstChild = node->GetFirstChild())) {
270 0 : node = firstChild;
271 :
272 0 : filtered = TestNode(node, aResult);
273 0 : if (aResult.Failed()) {
274 0 : return nullptr;
275 : }
276 :
277 0 : if (filtered == nsIDOMNodeFilter::FILTER_ACCEPT) {
278 : // Node found
279 0 : mCurrentNode = node;
280 0 : return node.forget();
281 : }
282 : }
283 :
284 0 : nsINode *sibling = nullptr;
285 0 : nsINode *temp = node;
286 0 : do {
287 0 : if (temp == mRoot)
288 0 : break;
289 :
290 0 : sibling = temp->GetNextSibling();
291 0 : if (sibling)
292 0 : break;
293 :
294 0 : temp = temp->GetParentNode();
295 0 : } while (temp);
296 :
297 0 : if (!sibling)
298 0 : break;
299 :
300 0 : node = sibling;
301 :
302 : // Found a sibling. Either ours or ancestor's
303 0 : filtered = TestNode(node, aResult);
304 0 : if (aResult.Failed()) {
305 0 : return nullptr;
306 : }
307 :
308 0 : if (filtered == nsIDOMNodeFilter::FILTER_ACCEPT) {
309 : // Node found
310 0 : mCurrentNode = node;
311 0 : return node.forget();
312 : }
313 0 : }
314 :
315 0 : return nullptr;
316 : }
317 :
318 : /*
319 : * TreeWalker helper functions
320 : */
321 :
322 : /*
323 : * Implements FirstChild and LastChild which only vary in which direction
324 : * they search.
325 : * @param aReversed Controls whether we search forwards or backwards
326 : * @param aResult Whether we threw or not.
327 : * @returns The desired node. Null if no child is found
328 : */
329 : already_AddRefed<nsINode>
330 0 : TreeWalker::FirstChildInternal(bool aReversed, ErrorResult& aResult)
331 : {
332 0 : nsCOMPtr<nsINode> node = aReversed ? mCurrentNode->GetLastChild()
333 0 : : mCurrentNode->GetFirstChild();
334 :
335 0 : while (node) {
336 0 : int16_t filtered = TestNode(node, aResult);
337 0 : if (aResult.Failed()) {
338 0 : return nullptr;
339 : }
340 :
341 0 : switch (filtered) {
342 : case nsIDOMNodeFilter::FILTER_ACCEPT:
343 : // Node found
344 0 : mCurrentNode = node;
345 0 : return node.forget();
346 : case nsIDOMNodeFilter::FILTER_SKIP: {
347 0 : nsINode *child = aReversed ? node->GetLastChild()
348 0 : : node->GetFirstChild();
349 0 : if (child) {
350 0 : node = child;
351 0 : continue;
352 : }
353 0 : break;
354 : }
355 : case nsIDOMNodeFilter::FILTER_REJECT:
356 : // Keep searching
357 0 : break;
358 : }
359 :
360 0 : do {
361 0 : nsINode *sibling = aReversed ? node->GetPreviousSibling()
362 0 : : node->GetNextSibling();
363 0 : if (sibling) {
364 0 : node = sibling;
365 0 : break;
366 : }
367 :
368 0 : nsINode *parent = node->GetParentNode();
369 :
370 0 : if (!parent || parent == mRoot || parent == mCurrentNode) {
371 0 : return nullptr;
372 : }
373 :
374 0 : node = parent;
375 :
376 : } while (node);
377 : }
378 :
379 0 : return nullptr;
380 : }
381 :
382 : /*
383 : * Implements NextSibling and PreviousSibling which only vary in which
384 : * direction they search.
385 : * @param aReversed Controls whether we search forwards or backwards
386 : * @param aResult Whether we threw or not.
387 : * @returns The desired node. Null if no child is found
388 : */
389 : already_AddRefed<nsINode>
390 0 : TreeWalker::NextSiblingInternal(bool aReversed, ErrorResult& aResult)
391 : {
392 0 : nsCOMPtr<nsINode> node = mCurrentNode;
393 :
394 0 : if (node == mRoot) {
395 0 : return nullptr;
396 : }
397 :
398 : while (1) {
399 0 : nsINode* sibling = aReversed ? node->GetPreviousSibling()
400 0 : : node->GetNextSibling();
401 :
402 0 : while (sibling) {
403 0 : node = sibling;
404 :
405 0 : int16_t filtered = TestNode(node, aResult);
406 0 : if (aResult.Failed()) {
407 0 : return nullptr;
408 : }
409 :
410 0 : if (filtered == nsIDOMNodeFilter::FILTER_ACCEPT) {
411 : // Node found
412 0 : mCurrentNode = node;
413 0 : return node.forget();
414 : }
415 :
416 : // If rejected or no children, try a sibling
417 0 : if (filtered == nsIDOMNodeFilter::FILTER_REJECT ||
418 0 : !(sibling = aReversed ? node->GetLastChild()
419 0 : : node->GetFirstChild())) {
420 0 : sibling = aReversed ? node->GetPreviousSibling()
421 0 : : node->GetNextSibling();
422 : }
423 : }
424 :
425 0 : node = node->GetParentNode();
426 :
427 0 : if (!node || node == mRoot) {
428 0 : return nullptr;
429 : }
430 :
431 : // Is parent transparent in filtered view?
432 0 : int16_t filtered = TestNode(node, aResult);
433 0 : if (aResult.Failed()) {
434 0 : return nullptr;
435 : }
436 0 : if (filtered == nsIDOMNodeFilter::FILTER_ACCEPT) {
437 0 : return nullptr;
438 : }
439 0 : }
440 : }
441 :
442 : bool
443 0 : TreeWalker::WrapObject(JSContext *aCx, JS::Handle<JSObject*> aGivenProto, JS::MutableHandle<JSObject*> aReflector)
444 : {
445 0 : return TreeWalkerBinding::Wrap(aCx, this, aGivenProto, aReflector);
446 : }
447 :
448 : } // namespace dom
449 : } // namespace mozilla
|