Backed out changeset 9d8b4c0b99ed (bug 1945683) for causing btime failures. CLOSED...
[gecko.git] / dom / base / TreeWalker.cpp
blob19360ee96180489d1855cb5e3a69172a3a89a659
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=4 et sw=2 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/. */
7 /*
8 * Implementation of DOM Traversal's TreeWalker
9 */
11 #include "mozilla/dom/TreeWalker.h"
13 #include "nsIContent.h"
14 #include "nsError.h"
15 #include "nsINode.h"
16 #include "nsContentUtils.h"
17 #include "mozilla/dom/NodeFilterBinding.h"
18 #include "mozilla/dom/TreeWalkerBinding.h"
20 namespace mozilla::dom {
23 * Factories, constructors and destructors
26 TreeWalker::TreeWalker(nsINode* aRoot, uint32_t aWhatToShow,
27 NodeFilter* aFilter)
28 : nsTraversal(aRoot, aWhatToShow, aFilter), mCurrentNode(aRoot) {}
30 TreeWalker::~TreeWalker() { /* destructor code */ }
33 * nsISupports and cycle collection stuff
36 NS_IMPL_CYCLE_COLLECTION(TreeWalker, mFilter, mCurrentNode, mRoot)
38 // QueryInterface implementation for TreeWalker
39 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(TreeWalker)
40 NS_INTERFACE_MAP_ENTRY(nsISupports)
41 NS_INTERFACE_MAP_END
43 // Have to pass in dom::TreeWalker because a11y has an a11y::TreeWalker that
44 // passes TreeWalker so refcount logging would get confused on the name
45 // collision.
46 NS_IMPL_CYCLE_COLLECTING_ADDREF(dom::TreeWalker)
47 NS_IMPL_CYCLE_COLLECTING_RELEASE(dom::TreeWalker)
49 void TreeWalker::SetCurrentNode(nsINode& aNode, ErrorResult& aResult) {
50 aResult = nsContentUtils::CheckSameOrigin(mRoot, &aNode);
51 if (aResult.Failed()) {
52 return;
55 mCurrentNode = &aNode;
58 already_AddRefed<nsINode> TreeWalker::ParentNode(ErrorResult& aResult) {
59 nsCOMPtr<nsINode> node = mCurrentNode;
61 while (node && node != mRoot) {
62 node = node->GetParentNode();
64 if (node) {
65 int16_t filtered = TestNode(node, aResult);
66 if (aResult.Failed()) {
67 return nullptr;
69 if (filtered == NodeFilter_Binding::FILTER_ACCEPT) {
70 mCurrentNode = node;
71 return node.forget();
76 return nullptr;
79 already_AddRefed<nsINode> TreeWalker::FirstChild(ErrorResult& aResult) {
80 return FirstChildInternal(false, aResult);
83 already_AddRefed<nsINode> TreeWalker::LastChild(ErrorResult& aResult) {
84 return FirstChildInternal(true, aResult);
87 already_AddRefed<nsINode> TreeWalker::PreviousSibling(ErrorResult& aResult) {
88 return NextSiblingInternal(true, aResult);
91 already_AddRefed<nsINode> TreeWalker::NextSibling(ErrorResult& aResult) {
92 return NextSiblingInternal(false, aResult);
95 already_AddRefed<nsINode> TreeWalker::PreviousNode(ErrorResult& aResult) {
96 nsCOMPtr<nsINode> node = mCurrentNode;
98 while (node != mRoot) {
99 while (nsINode* previousSibling = node->GetPreviousSibling()) {
100 node = previousSibling;
102 int16_t filtered = TestNode(node, aResult);
103 if (aResult.Failed()) {
104 return nullptr;
107 nsINode* lastChild;
108 while (filtered != NodeFilter_Binding::FILTER_REJECT &&
109 (lastChild = node->GetLastChild())) {
110 node = lastChild;
111 filtered = TestNode(node, aResult);
112 if (aResult.Failed()) {
113 return nullptr;
117 if (filtered == NodeFilter_Binding::FILTER_ACCEPT) {
118 mCurrentNode = node;
119 return node.forget();
123 if (node == mRoot) {
124 break;
127 node = node->GetParentNode();
128 if (!node) {
129 break;
132 int16_t filtered = TestNode(node, aResult);
133 if (aResult.Failed()) {
134 return nullptr;
137 if (filtered == NodeFilter_Binding::FILTER_ACCEPT) {
138 mCurrentNode = node;
139 return node.forget();
143 return nullptr;
146 already_AddRefed<nsINode> TreeWalker::NextNode(ErrorResult& aResult) {
147 int16_t filtered =
148 NodeFilter_Binding::FILTER_ACCEPT; // pre-init for inner loop
150 nsCOMPtr<nsINode> node = mCurrentNode;
152 while (1) {
153 nsINode* firstChild;
154 while (filtered != NodeFilter_Binding::FILTER_REJECT &&
155 (firstChild = node->GetFirstChild())) {
156 node = firstChild;
158 filtered = TestNode(node, aResult);
159 if (aResult.Failed()) {
160 return nullptr;
163 if (filtered == NodeFilter_Binding::FILTER_ACCEPT) {
164 // Node found
165 mCurrentNode = node;
166 return node.forget();
170 nsINode* sibling = nullptr;
171 nsINode* temp = node;
172 do {
173 if (temp == mRoot) break;
175 sibling = temp->GetNextSibling();
176 if (sibling) break;
178 temp = temp->GetParentNode();
179 } while (temp);
181 if (!sibling) break;
183 node = sibling;
185 // Found a sibling. Either ours or ancestor's
186 filtered = TestNode(node, aResult);
187 if (aResult.Failed()) {
188 return nullptr;
191 if (filtered == NodeFilter_Binding::FILTER_ACCEPT) {
192 // Node found
193 mCurrentNode = node;
194 return node.forget();
198 return nullptr;
202 * TreeWalker helper functions
206 * Implements FirstChild and LastChild which only vary in which direction
207 * they search.
208 * @param aReversed Controls whether we search forwards or backwards
209 * @param aResult Whether we threw or not.
210 * @returns The desired node. Null if no child is found
212 already_AddRefed<nsINode> TreeWalker::FirstChildInternal(bool aReversed,
213 ErrorResult& aResult) {
214 nsCOMPtr<nsINode> node =
215 aReversed ? mCurrentNode->GetLastChild() : mCurrentNode->GetFirstChild();
217 while (node) {
218 int16_t filtered = TestNode(node, aResult);
219 if (aResult.Failed()) {
220 return nullptr;
223 switch (filtered) {
224 case NodeFilter_Binding::FILTER_ACCEPT:
225 // Node found
226 mCurrentNode = node;
227 return node.forget();
228 case NodeFilter_Binding::FILTER_SKIP: {
229 nsINode* child =
230 aReversed ? node->GetLastChild() : node->GetFirstChild();
231 if (child) {
232 node = child;
233 continue;
235 break;
237 case NodeFilter_Binding::FILTER_REJECT:
238 // Keep searching
239 break;
242 do {
243 nsINode* sibling =
244 aReversed ? node->GetPreviousSibling() : node->GetNextSibling();
245 if (sibling) {
246 node = sibling;
247 break;
250 nsINode* parent = node->GetParentNode();
252 if (!parent || parent == mRoot || parent == mCurrentNode) {
253 return nullptr;
256 node = parent;
258 } while (node);
261 return nullptr;
265 * Implements NextSibling and PreviousSibling which only vary in which
266 * direction they search.
267 * @param aReversed Controls whether we search forwards or backwards
268 * @param aResult Whether we threw or not.
269 * @returns The desired node. Null if no child is found
271 already_AddRefed<nsINode> TreeWalker::NextSiblingInternal(
272 bool aReversed, ErrorResult& aResult) {
273 nsCOMPtr<nsINode> node = mCurrentNode;
275 if (node == mRoot) {
276 return nullptr;
279 while (1) {
280 nsINode* sibling =
281 aReversed ? node->GetPreviousSibling() : node->GetNextSibling();
283 while (sibling) {
284 node = sibling;
286 int16_t filtered = TestNode(node, aResult);
287 if (aResult.Failed()) {
288 return nullptr;
291 if (filtered == NodeFilter_Binding::FILTER_ACCEPT) {
292 // Node found
293 mCurrentNode = node;
294 return node.forget();
297 // If rejected or no children, try a sibling
298 if (filtered == NodeFilter_Binding::FILTER_REJECT ||
299 !(sibling =
300 aReversed ? node->GetLastChild() : node->GetFirstChild())) {
301 sibling =
302 aReversed ? node->GetPreviousSibling() : node->GetNextSibling();
306 node = node->GetParentNode();
308 if (!node || node == mRoot) {
309 return nullptr;
312 // Is parent transparent in filtered view?
313 int16_t filtered = TestNode(node, aResult);
314 if (aResult.Failed()) {
315 return nullptr;
317 if (filtered == NodeFilter_Binding::FILTER_ACCEPT) {
318 return nullptr;
323 bool TreeWalker::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto,
324 JS::MutableHandle<JSObject*> aReflector) {
325 return TreeWalker_Binding::Wrap(aCx, this, aGivenProto, aReflector);
328 } // namespace mozilla::dom