Bumping gaia.json for 8 gaia revision(s) a=gaia-bump
[gecko.git] / editor / txtsvc / nsFilteredContentIterator.cpp
blob3aec262ab1d8eb4c41504915bf86b82d88d52394
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "mozilla/mozalloc.h"
7 #include "nsComponentManagerUtils.h"
8 #include "nsContentUtils.h"
9 #include "nsDebug.h"
10 #include "nsError.h"
11 #include "nsFilteredContentIterator.h"
12 #include "nsIAtom.h"
13 #include "nsIContent.h"
14 #include "nsIContentIterator.h"
15 #include "nsIDOMNode.h"
16 #include "nsINode.h"
17 #include "nsISupportsBase.h"
18 #include "nsISupportsUtils.h"
19 #include "nsITextServicesFilter.h"
20 #include "nsRange.h"
22 //------------------------------------------------------------
23 nsFilteredContentIterator::nsFilteredContentIterator(nsITextServicesFilter* aFilter) :
24 mFilter(aFilter),
25 mDidSkip(false),
26 mIsOutOfRange(false),
27 mDirection(eDirNotSet)
29 mIterator = do_CreateInstance("@mozilla.org/content/post-content-iterator;1");
30 mPreIterator = do_CreateInstance("@mozilla.org/content/pre-content-iterator;1");
33 //------------------------------------------------------------
34 nsFilteredContentIterator::~nsFilteredContentIterator()
38 //------------------------------------------------------------
39 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsFilteredContentIterator)
40 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsFilteredContentIterator)
42 NS_INTERFACE_MAP_BEGIN(nsFilteredContentIterator)
43 NS_INTERFACE_MAP_ENTRY(nsIContentIterator)
44 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIContentIterator)
45 NS_INTERFACE_MAP_ENTRIES_CYCLE_COLLECTION(nsFilteredContentIterator)
46 NS_INTERFACE_MAP_END
48 NS_IMPL_CYCLE_COLLECTION(nsFilteredContentIterator,
49 mCurrentIterator,
50 mIterator,
51 mPreIterator,
52 mFilter,
53 mRange)
55 //------------------------------------------------------------
56 nsresult
57 nsFilteredContentIterator::Init(nsINode* aRoot)
59 NS_ENSURE_ARG_POINTER(aRoot);
60 NS_ENSURE_TRUE(mPreIterator, NS_ERROR_FAILURE);
61 NS_ENSURE_TRUE(mIterator, NS_ERROR_FAILURE);
62 mIsOutOfRange = false;
63 mDirection = eForward;
64 mCurrentIterator = mPreIterator;
66 mRange = new nsRange(aRoot);
67 nsCOMPtr<nsIDOMNode> domNode(do_QueryInterface(aRoot));
68 if (domNode) {
69 mRange->SelectNode(domNode);
72 nsresult rv = mPreIterator->Init(mRange);
73 NS_ENSURE_SUCCESS(rv, rv);
74 return mIterator->Init(mRange);
77 //------------------------------------------------------------
78 nsresult
79 nsFilteredContentIterator::Init(nsIDOMRange* aRange)
81 NS_ENSURE_TRUE(mPreIterator, NS_ERROR_FAILURE);
82 NS_ENSURE_TRUE(mIterator, NS_ERROR_FAILURE);
83 NS_ENSURE_ARG_POINTER(aRange);
84 mIsOutOfRange = false;
85 mDirection = eForward;
86 mCurrentIterator = mPreIterator;
88 mRange = static_cast<nsRange*>(aRange)->CloneRange();
90 nsresult rv = mPreIterator->Init(mRange);
91 NS_ENSURE_SUCCESS(rv, rv);
92 return mIterator->Init(mRange);
95 //------------------------------------------------------------
96 nsresult
97 nsFilteredContentIterator::SwitchDirections(bool aChangeToForward)
99 nsINode *node = mCurrentIterator->GetCurrentNode();
101 if (aChangeToForward) {
102 mCurrentIterator = mPreIterator;
103 mDirection = eForward;
104 } else {
105 mCurrentIterator = mIterator;
106 mDirection = eBackward;
109 if (node) {
110 nsresult rv = mCurrentIterator->PositionAt(node);
111 if (NS_FAILED(rv)) {
112 mIsOutOfRange = true;
113 return rv;
116 return NS_OK;
119 //------------------------------------------------------------
120 void
121 nsFilteredContentIterator::First()
123 if (!mCurrentIterator) {
124 NS_ERROR("Missing iterator!");
126 return;
129 // If we are switching directions then
130 // we need to switch how we process the nodes
131 if (mDirection != eForward) {
132 mCurrentIterator = mPreIterator;
133 mDirection = eForward;
134 mIsOutOfRange = false;
137 mCurrentIterator->First();
139 if (mCurrentIterator->IsDone()) {
140 return;
143 nsINode *currentNode = mCurrentIterator->GetCurrentNode();
144 nsCOMPtr<nsIDOMNode> node(do_QueryInterface(currentNode));
146 bool didCross;
147 CheckAdvNode(node, didCross, eForward);
150 //------------------------------------------------------------
151 void
152 nsFilteredContentIterator::Last()
154 if (!mCurrentIterator) {
155 NS_ERROR("Missing iterator!");
157 return;
160 // If we are switching directions then
161 // we need to switch how we process the nodes
162 if (mDirection != eBackward) {
163 mCurrentIterator = mIterator;
164 mDirection = eBackward;
165 mIsOutOfRange = false;
168 mCurrentIterator->Last();
170 if (mCurrentIterator->IsDone()) {
171 return;
174 nsINode *currentNode = mCurrentIterator->GetCurrentNode();
175 nsCOMPtr<nsIDOMNode> node(do_QueryInterface(currentNode));
177 bool didCross;
178 CheckAdvNode(node, didCross, eBackward);
181 ///////////////////////////////////////////////////////////////////////////
182 // ContentToParentOffset: returns the content node's parent and offset.
184 static void
185 ContentToParentOffset(nsIContent *aContent, nsIDOMNode **aParent,
186 int32_t *aOffset)
188 if (!aParent || !aOffset)
189 return;
191 *aParent = nullptr;
192 *aOffset = 0;
194 if (!aContent)
195 return;
197 nsIContent* parent = aContent->GetParent();
199 if (!parent)
200 return;
202 *aOffset = parent->IndexOf(aContent);
204 CallQueryInterface(parent, aParent);
207 ///////////////////////////////////////////////////////////////////////////
208 // ContentIsInTraversalRange: returns true if content is visited during
209 // the traversal of the range in the specified mode.
211 static bool
212 ContentIsInTraversalRange(nsIContent *aContent, bool aIsPreMode,
213 nsIDOMNode *aStartNode, int32_t aStartOffset,
214 nsIDOMNode *aEndNode, int32_t aEndOffset)
216 NS_ENSURE_TRUE(aStartNode && aEndNode && aContent, false);
218 nsCOMPtr<nsIDOMNode> parentNode;
219 int32_t indx = 0;
221 ContentToParentOffset(aContent, getter_AddRefs(parentNode), &indx);
223 NS_ENSURE_TRUE(parentNode, false);
225 if (!aIsPreMode)
226 ++indx;
228 int32_t startRes = nsContentUtils::ComparePoints(aStartNode, aStartOffset,
229 parentNode, indx);
230 int32_t endRes = nsContentUtils::ComparePoints(aEndNode, aEndOffset,
231 parentNode, indx);
232 return (startRes <= 0) && (endRes >= 0);
235 static bool
236 ContentIsInTraversalRange(nsRange* aRange, nsIDOMNode* aNextNode, bool aIsPreMode)
238 nsCOMPtr<nsIContent> content(do_QueryInterface(aNextNode));
239 NS_ENSURE_TRUE(content && aRange, false);
241 nsCOMPtr<nsIDOMNode> sNode;
242 nsCOMPtr<nsIDOMNode> eNode;
243 int32_t sOffset;
244 int32_t eOffset;
245 aRange->GetStartContainer(getter_AddRefs(sNode));
246 aRange->GetStartOffset(&sOffset);
247 aRange->GetEndContainer(getter_AddRefs(eNode));
248 aRange->GetEndOffset(&eOffset);
249 return ContentIsInTraversalRange(content, aIsPreMode, sNode, sOffset, eNode, eOffset);
252 //------------------------------------------------------------
253 // Helper function to advance to the next or previous node
254 nsresult
255 nsFilteredContentIterator::AdvanceNode(nsIDOMNode* aNode, nsIDOMNode*& aNewNode, eDirectionType aDir)
257 nsCOMPtr<nsIDOMNode> nextNode;
258 if (aDir == eForward) {
259 aNode->GetNextSibling(getter_AddRefs(nextNode));
260 } else {
261 aNode->GetPreviousSibling(getter_AddRefs(nextNode));
264 if (nextNode) {
265 // If we got here, that means we found the nxt/prv node
266 // make sure it is in our DOMRange
267 bool intersects = ContentIsInTraversalRange(mRange, nextNode, aDir == eForward);
268 if (intersects) {
269 aNewNode = nextNode;
270 NS_ADDREF(aNewNode);
271 return NS_OK;
273 } else {
274 // The next node was null so we need to walk up the parent(s)
275 nsCOMPtr<nsIDOMNode> parent;
276 aNode->GetParentNode(getter_AddRefs(parent));
277 NS_ASSERTION(parent, "parent can't be nullptr");
279 // Make sure the parent is in the DOMRange before going further
280 bool intersects = ContentIsInTraversalRange(mRange, nextNode, aDir == eForward);
281 if (intersects) {
282 // Now find the nxt/prv node after/before this node
283 nsresult rv = AdvanceNode(parent, aNewNode, aDir);
284 if (NS_SUCCEEDED(rv) && aNewNode) {
285 return NS_OK;
290 // if we get here it pretty much means
291 // we went out of the DOM Range
292 mIsOutOfRange = true;
294 return NS_ERROR_FAILURE;
297 //------------------------------------------------------------
298 // Helper function to see if the next/prev node should be skipped
299 void
300 nsFilteredContentIterator::CheckAdvNode(nsIDOMNode* aNode, bool& aDidSkip, eDirectionType aDir)
302 aDidSkip = false;
303 mIsOutOfRange = false;
305 if (aNode && mFilter) {
306 nsCOMPtr<nsIDOMNode> currentNode = aNode;
307 bool skipIt;
308 while (1) {
309 nsresult rv = mFilter->Skip(aNode, &skipIt);
310 if (NS_SUCCEEDED(rv) && skipIt) {
311 aDidSkip = true;
312 // Get the next/prev node and then
313 // see if we should skip that
314 nsCOMPtr<nsIDOMNode> advNode;
315 rv = AdvanceNode(aNode, *getter_AddRefs(advNode), aDir);
316 if (NS_SUCCEEDED(rv) && advNode) {
317 aNode = advNode;
318 } else {
319 return; // fell out of range
321 } else {
322 if (aNode != currentNode) {
323 nsCOMPtr<nsIContent> content(do_QueryInterface(aNode));
324 mCurrentIterator->PositionAt(content);
326 return; // found something
332 void
333 nsFilteredContentIterator::Next()
335 if (mIsOutOfRange || !mCurrentIterator) {
336 NS_ASSERTION(mCurrentIterator, "Missing iterator!");
338 return;
341 // If we are switching directions then
342 // we need to switch how we process the nodes
343 if (mDirection != eForward) {
344 nsresult rv = SwitchDirections(true);
345 if (NS_FAILED(rv)) {
346 return;
350 mCurrentIterator->Next();
352 if (mCurrentIterator->IsDone()) {
353 return;
356 // If we can't get the current node then
357 // don't check to see if we can skip it
358 nsINode *currentNode = mCurrentIterator->GetCurrentNode();
360 nsCOMPtr<nsIDOMNode> node(do_QueryInterface(currentNode));
361 CheckAdvNode(node, mDidSkip, eForward);
364 void
365 nsFilteredContentIterator::Prev()
367 if (mIsOutOfRange || !mCurrentIterator) {
368 NS_ASSERTION(mCurrentIterator, "Missing iterator!");
370 return;
373 // If we are switching directions then
374 // we need to switch how we process the nodes
375 if (mDirection != eBackward) {
376 nsresult rv = SwitchDirections(false);
377 if (NS_FAILED(rv)) {
378 return;
382 mCurrentIterator->Prev();
384 if (mCurrentIterator->IsDone()) {
385 return;
388 // If we can't get the current node then
389 // don't check to see if we can skip it
390 nsINode *currentNode = mCurrentIterator->GetCurrentNode();
392 nsCOMPtr<nsIDOMNode> node(do_QueryInterface(currentNode));
393 CheckAdvNode(node, mDidSkip, eBackward);
396 nsINode *
397 nsFilteredContentIterator::GetCurrentNode()
399 if (mIsOutOfRange || !mCurrentIterator) {
400 return nullptr;
403 return mCurrentIterator->GetCurrentNode();
406 bool
407 nsFilteredContentIterator::IsDone()
409 if (mIsOutOfRange || !mCurrentIterator) {
410 return true;
413 return mCurrentIterator->IsDone();
416 nsresult
417 nsFilteredContentIterator::PositionAt(nsINode* aCurNode)
419 NS_ENSURE_TRUE(mCurrentIterator, NS_ERROR_FAILURE);
420 mIsOutOfRange = false;
421 return mCurrentIterator->PositionAt(aCurNode);