Bumping manifests a=b2g-bump
[gecko.git] / editor / txtsvc / nsFilteredContentIterator.cpp
blobcc066b67664167a4f21a68d8a1479d41017c921d
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 "nsIDOMRange.h"
17 #include "nsINode.h"
18 #include "nsISupportsBase.h"
19 #include "nsISupportsUtils.h"
20 #include "nsITextServicesFilter.h"
21 #include "nsRange.h"
23 //------------------------------------------------------------
24 nsFilteredContentIterator::nsFilteredContentIterator(nsITextServicesFilter* aFilter) :
25 mFilter(aFilter),
26 mDidSkip(false),
27 mIsOutOfRange(false),
28 mDirection(eDirNotSet)
30 mIterator = do_CreateInstance("@mozilla.org/content/post-content-iterator;1");
31 mPreIterator = do_CreateInstance("@mozilla.org/content/pre-content-iterator;1");
34 //------------------------------------------------------------
35 nsFilteredContentIterator::~nsFilteredContentIterator()
39 //------------------------------------------------------------
40 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsFilteredContentIterator)
41 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsFilteredContentIterator)
43 NS_INTERFACE_MAP_BEGIN(nsFilteredContentIterator)
44 NS_INTERFACE_MAP_ENTRY(nsIContentIterator)
45 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIContentIterator)
46 NS_INTERFACE_MAP_ENTRIES_CYCLE_COLLECTION(nsFilteredContentIterator)
47 NS_INTERFACE_MAP_END
49 NS_IMPL_CYCLE_COLLECTION(nsFilteredContentIterator,
50 mCurrentIterator,
51 mIterator,
52 mPreIterator,
53 mFilter,
54 mRange)
56 //------------------------------------------------------------
57 nsresult
58 nsFilteredContentIterator::Init(nsINode* aRoot)
60 NS_ENSURE_ARG_POINTER(aRoot);
61 NS_ENSURE_TRUE(mPreIterator, NS_ERROR_FAILURE);
62 NS_ENSURE_TRUE(mIterator, NS_ERROR_FAILURE);
63 mIsOutOfRange = false;
64 mDirection = eForward;
65 mCurrentIterator = mPreIterator;
67 mRange = new nsRange(aRoot);
68 nsCOMPtr<nsIDOMNode> domNode(do_QueryInterface(aRoot));
69 if (domNode) {
70 mRange->SelectNode(domNode);
73 nsresult rv = mPreIterator->Init(mRange);
74 NS_ENSURE_SUCCESS(rv, rv);
75 return mIterator->Init(mRange);
78 //------------------------------------------------------------
79 nsresult
80 nsFilteredContentIterator::Init(nsIDOMRange* aRange)
82 NS_ENSURE_TRUE(mPreIterator, NS_ERROR_FAILURE);
83 NS_ENSURE_TRUE(mIterator, NS_ERROR_FAILURE);
84 NS_ENSURE_ARG_POINTER(aRange);
85 mIsOutOfRange = false;
86 mDirection = eForward;
87 mCurrentIterator = mPreIterator;
89 nsCOMPtr<nsIDOMRange> domRange;
90 nsresult rv = aRange->CloneRange(getter_AddRefs(domRange));
91 NS_ENSURE_SUCCESS(rv, rv);
92 mRange = do_QueryInterface(domRange);
94 rv = mPreIterator->Init(domRange);
95 NS_ENSURE_SUCCESS(rv, rv);
96 return mIterator->Init(domRange);
99 //------------------------------------------------------------
100 nsresult
101 nsFilteredContentIterator::SwitchDirections(bool aChangeToForward)
103 nsINode *node = mCurrentIterator->GetCurrentNode();
105 if (aChangeToForward) {
106 mCurrentIterator = mPreIterator;
107 mDirection = eForward;
108 } else {
109 mCurrentIterator = mIterator;
110 mDirection = eBackward;
113 if (node) {
114 nsresult rv = mCurrentIterator->PositionAt(node);
115 if (NS_FAILED(rv)) {
116 mIsOutOfRange = true;
117 return rv;
120 return NS_OK;
123 //------------------------------------------------------------
124 void
125 nsFilteredContentIterator::First()
127 if (!mCurrentIterator) {
128 NS_ERROR("Missing iterator!");
130 return;
133 // If we are switching directions then
134 // we need to switch how we process the nodes
135 if (mDirection != eForward) {
136 mCurrentIterator = mPreIterator;
137 mDirection = eForward;
138 mIsOutOfRange = false;
141 mCurrentIterator->First();
143 if (mCurrentIterator->IsDone()) {
144 return;
147 nsINode *currentNode = mCurrentIterator->GetCurrentNode();
148 nsCOMPtr<nsIDOMNode> node(do_QueryInterface(currentNode));
150 bool didCross;
151 CheckAdvNode(node, didCross, eForward);
154 //------------------------------------------------------------
155 void
156 nsFilteredContentIterator::Last()
158 if (!mCurrentIterator) {
159 NS_ERROR("Missing iterator!");
161 return;
164 // If we are switching directions then
165 // we need to switch how we process the nodes
166 if (mDirection != eBackward) {
167 mCurrentIterator = mIterator;
168 mDirection = eBackward;
169 mIsOutOfRange = false;
172 mCurrentIterator->Last();
174 if (mCurrentIterator->IsDone()) {
175 return;
178 nsINode *currentNode = mCurrentIterator->GetCurrentNode();
179 nsCOMPtr<nsIDOMNode> node(do_QueryInterface(currentNode));
181 bool didCross;
182 CheckAdvNode(node, didCross, eBackward);
185 ///////////////////////////////////////////////////////////////////////////
186 // ContentToParentOffset: returns the content node's parent and offset.
188 static void
189 ContentToParentOffset(nsIContent *aContent, nsIDOMNode **aParent,
190 int32_t *aOffset)
192 if (!aParent || !aOffset)
193 return;
195 *aParent = nullptr;
196 *aOffset = 0;
198 if (!aContent)
199 return;
201 nsIContent* parent = aContent->GetParent();
203 if (!parent)
204 return;
206 *aOffset = parent->IndexOf(aContent);
208 CallQueryInterface(parent, aParent);
211 ///////////////////////////////////////////////////////////////////////////
212 // ContentIsInTraversalRange: returns true if content is visited during
213 // the traversal of the range in the specified mode.
215 static bool
216 ContentIsInTraversalRange(nsIContent *aContent, bool aIsPreMode,
217 nsIDOMNode *aStartNode, int32_t aStartOffset,
218 nsIDOMNode *aEndNode, int32_t aEndOffset)
220 NS_ENSURE_TRUE(aStartNode && aEndNode && aContent, false);
222 nsCOMPtr<nsIDOMNode> parentNode;
223 int32_t indx = 0;
225 ContentToParentOffset(aContent, getter_AddRefs(parentNode), &indx);
227 NS_ENSURE_TRUE(parentNode, false);
229 if (!aIsPreMode)
230 ++indx;
232 int32_t startRes = nsContentUtils::ComparePoints(aStartNode, aStartOffset,
233 parentNode, indx);
234 int32_t endRes = nsContentUtils::ComparePoints(aEndNode, aEndOffset,
235 parentNode, indx);
236 return (startRes <= 0) && (endRes >= 0);
239 static bool
240 ContentIsInTraversalRange(nsIDOMRange *aRange, nsIDOMNode* aNextNode, bool aIsPreMode)
242 nsCOMPtr<nsIContent> content(do_QueryInterface(aNextNode));
243 NS_ENSURE_TRUE(content && aRange, false);
245 nsCOMPtr<nsIDOMNode> sNode;
246 nsCOMPtr<nsIDOMNode> eNode;
247 int32_t sOffset;
248 int32_t eOffset;
249 aRange->GetStartContainer(getter_AddRefs(sNode));
250 aRange->GetStartOffset(&sOffset);
251 aRange->GetEndContainer(getter_AddRefs(eNode));
252 aRange->GetEndOffset(&eOffset);
253 return ContentIsInTraversalRange(content, aIsPreMode, sNode, sOffset, eNode, eOffset);
256 //------------------------------------------------------------
257 // Helper function to advance to the next or previous node
258 nsresult
259 nsFilteredContentIterator::AdvanceNode(nsIDOMNode* aNode, nsIDOMNode*& aNewNode, eDirectionType aDir)
261 nsCOMPtr<nsIDOMNode> nextNode;
262 if (aDir == eForward) {
263 aNode->GetNextSibling(getter_AddRefs(nextNode));
264 } else {
265 aNode->GetPreviousSibling(getter_AddRefs(nextNode));
268 if (nextNode) {
269 // If we got here, that means we found the nxt/prv node
270 // make sure it is in our DOMRange
271 bool intersects = ContentIsInTraversalRange(mRange, nextNode, aDir == eForward);
272 if (intersects) {
273 aNewNode = nextNode;
274 NS_ADDREF(aNewNode);
275 return NS_OK;
277 } else {
278 // The next node was null so we need to walk up the parent(s)
279 nsCOMPtr<nsIDOMNode> parent;
280 aNode->GetParentNode(getter_AddRefs(parent));
281 NS_ASSERTION(parent, "parent can't be nullptr");
283 // Make sure the parent is in the DOMRange before going further
284 bool intersects = ContentIsInTraversalRange(mRange, nextNode, aDir == eForward);
285 if (intersects) {
286 // Now find the nxt/prv node after/before this node
287 nsresult rv = AdvanceNode(parent, aNewNode, aDir);
288 if (NS_SUCCEEDED(rv) && aNewNode) {
289 return NS_OK;
294 // if we get here it pretty much means
295 // we went out of the DOM Range
296 mIsOutOfRange = true;
298 return NS_ERROR_FAILURE;
301 //------------------------------------------------------------
302 // Helper function to see if the next/prev node should be skipped
303 void
304 nsFilteredContentIterator::CheckAdvNode(nsIDOMNode* aNode, bool& aDidSkip, eDirectionType aDir)
306 aDidSkip = false;
307 mIsOutOfRange = false;
309 if (aNode && mFilter) {
310 nsCOMPtr<nsIDOMNode> currentNode = aNode;
311 bool skipIt;
312 while (1) {
313 nsresult rv = mFilter->Skip(aNode, &skipIt);
314 if (NS_SUCCEEDED(rv) && skipIt) {
315 aDidSkip = true;
316 // Get the next/prev node and then
317 // see if we should skip that
318 nsCOMPtr<nsIDOMNode> advNode;
319 rv = AdvanceNode(aNode, *getter_AddRefs(advNode), aDir);
320 if (NS_SUCCEEDED(rv) && advNode) {
321 aNode = advNode;
322 } else {
323 return; // fell out of range
325 } else {
326 if (aNode != currentNode) {
327 nsCOMPtr<nsIContent> content(do_QueryInterface(aNode));
328 mCurrentIterator->PositionAt(content);
330 return; // found something
336 void
337 nsFilteredContentIterator::Next()
339 if (mIsOutOfRange || !mCurrentIterator) {
340 NS_ASSERTION(mCurrentIterator, "Missing iterator!");
342 return;
345 // If we are switching directions then
346 // we need to switch how we process the nodes
347 if (mDirection != eForward) {
348 nsresult rv = SwitchDirections(true);
349 if (NS_FAILED(rv)) {
350 return;
354 mCurrentIterator->Next();
356 if (mCurrentIterator->IsDone()) {
357 return;
360 // If we can't get the current node then
361 // don't check to see if we can skip it
362 nsINode *currentNode = mCurrentIterator->GetCurrentNode();
364 nsCOMPtr<nsIDOMNode> node(do_QueryInterface(currentNode));
365 CheckAdvNode(node, mDidSkip, eForward);
368 void
369 nsFilteredContentIterator::Prev()
371 if (mIsOutOfRange || !mCurrentIterator) {
372 NS_ASSERTION(mCurrentIterator, "Missing iterator!");
374 return;
377 // If we are switching directions then
378 // we need to switch how we process the nodes
379 if (mDirection != eBackward) {
380 nsresult rv = SwitchDirections(false);
381 if (NS_FAILED(rv)) {
382 return;
386 mCurrentIterator->Prev();
388 if (mCurrentIterator->IsDone()) {
389 return;
392 // If we can't get the current node then
393 // don't check to see if we can skip it
394 nsINode *currentNode = mCurrentIterator->GetCurrentNode();
396 nsCOMPtr<nsIDOMNode> node(do_QueryInterface(currentNode));
397 CheckAdvNode(node, mDidSkip, eBackward);
400 nsINode *
401 nsFilteredContentIterator::GetCurrentNode()
403 if (mIsOutOfRange || !mCurrentIterator) {
404 return nullptr;
407 return mCurrentIterator->GetCurrentNode();
410 bool
411 nsFilteredContentIterator::IsDone()
413 if (mIsOutOfRange || !mCurrentIterator) {
414 return true;
417 return mCurrentIterator->IsDone();
420 nsresult
421 nsFilteredContentIterator::PositionAt(nsINode* aCurNode)
423 NS_ENSURE_TRUE(mCurrentIterator, NS_ERROR_FAILURE);
424 mIsOutOfRange = false;
425 return mCurrentIterator->PositionAt(aCurNode);