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/. */
5
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"
21
22 //------------------------------------------------------------
nsFilteredContentIterator(nsITextServicesFilter * aFilter)23 nsFilteredContentIterator::nsFilteredContentIterator(nsITextServicesFilter* aFilter) :
24 mFilter(aFilter),
25 mDidSkip(false),
26 mIsOutOfRange(false),
27 mDirection(eDirNotSet)
28 {
29 mIterator = do_CreateInstance("@mozilla.org/content/post-content-iterator;1");
30 mPreIterator = do_CreateInstance("@mozilla.org/content/pre-content-iterator;1");
31 }
32
33 //------------------------------------------------------------
~nsFilteredContentIterator()34 nsFilteredContentIterator::~nsFilteredContentIterator()
35 {
36 }
37
38 //------------------------------------------------------------
39 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsFilteredContentIterator)
NS_IMPL_CYCLE_COLLECTING_RELEASE(nsFilteredContentIterator)40 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsFilteredContentIterator)
41
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
47
48 NS_IMPL_CYCLE_COLLECTION(nsFilteredContentIterator,
49 mCurrentIterator,
50 mIterator,
51 mPreIterator,
52 mFilter,
53 mRange)
54
55 //------------------------------------------------------------
56 nsresult
57 nsFilteredContentIterator::Init(nsINode* aRoot)
58 {
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;
65
66 mRange = new nsRange(aRoot);
67 nsCOMPtr<nsIDOMNode> domNode(do_QueryInterface(aRoot));
68 if (domNode) {
69 mRange->SelectNode(domNode);
70 }
71
72 nsresult rv = mPreIterator->Init(mRange);
73 NS_ENSURE_SUCCESS(rv, rv);
74 return mIterator->Init(mRange);
75 }
76
77 //------------------------------------------------------------
78 nsresult
Init(nsIDOMRange * aRange)79 nsFilteredContentIterator::Init(nsIDOMRange* aRange)
80 {
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;
87
88 mRange = static_cast<nsRange*>(aRange)->CloneRange();
89
90 nsresult rv = mPreIterator->Init(mRange);
91 NS_ENSURE_SUCCESS(rv, rv);
92 return mIterator->Init(mRange);
93 }
94
95 //------------------------------------------------------------
96 nsresult
SwitchDirections(bool aChangeToForward)97 nsFilteredContentIterator::SwitchDirections(bool aChangeToForward)
98 {
99 nsINode *node = mCurrentIterator->GetCurrentNode();
100
101 if (aChangeToForward) {
102 mCurrentIterator = mPreIterator;
103 mDirection = eForward;
104 } else {
105 mCurrentIterator = mIterator;
106 mDirection = eBackward;
107 }
108
109 if (node) {
110 nsresult rv = mCurrentIterator->PositionAt(node);
111 if (NS_FAILED(rv)) {
112 mIsOutOfRange = true;
113 return rv;
114 }
115 }
116 return NS_OK;
117 }
118
119 //------------------------------------------------------------
120 void
First()121 nsFilteredContentIterator::First()
122 {
123 if (!mCurrentIterator) {
124 NS_ERROR("Missing iterator!");
125
126 return;
127 }
128
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;
135 }
136
137 mCurrentIterator->First();
138
139 if (mCurrentIterator->IsDone()) {
140 return;
141 }
142
143 nsINode *currentNode = mCurrentIterator->GetCurrentNode();
144 nsCOMPtr<nsIDOMNode> node(do_QueryInterface(currentNode));
145
146 bool didCross;
147 CheckAdvNode(node, didCross, eForward);
148 }
149
150 //------------------------------------------------------------
151 void
Last()152 nsFilteredContentIterator::Last()
153 {
154 if (!mCurrentIterator) {
155 NS_ERROR("Missing iterator!");
156
157 return;
158 }
159
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;
166 }
167
168 mCurrentIterator->Last();
169
170 if (mCurrentIterator->IsDone()) {
171 return;
172 }
173
174 nsINode *currentNode = mCurrentIterator->GetCurrentNode();
175 nsCOMPtr<nsIDOMNode> node(do_QueryInterface(currentNode));
176
177 bool didCross;
178 CheckAdvNode(node, didCross, eBackward);
179 }
180
181 ///////////////////////////////////////////////////////////////////////////
182 // ContentToParentOffset: returns the content node's parent and offset.
183 //
184 static void
ContentToParentOffset(nsIContent * aContent,nsIDOMNode ** aParent,int32_t * aOffset)185 ContentToParentOffset(nsIContent *aContent, nsIDOMNode **aParent,
186 int32_t *aOffset)
187 {
188 if (!aParent || !aOffset)
189 return;
190
191 *aParent = nullptr;
192 *aOffset = 0;
193
194 if (!aContent)
195 return;
196
197 nsIContent* parent = aContent->GetParent();
198
199 if (!parent)
200 return;
201
202 *aOffset = parent->IndexOf(aContent);
203
204 CallQueryInterface(parent, aParent);
205 }
206
207 ///////////////////////////////////////////////////////////////////////////
208 // ContentIsInTraversalRange: returns true if content is visited during
209 // the traversal of the range in the specified mode.
210 //
211 static bool
ContentIsInTraversalRange(nsIContent * aContent,bool aIsPreMode,nsIDOMNode * aStartNode,int32_t aStartOffset,nsIDOMNode * aEndNode,int32_t aEndOffset)212 ContentIsInTraversalRange(nsIContent *aContent, bool aIsPreMode,
213 nsIDOMNode *aStartNode, int32_t aStartOffset,
214 nsIDOMNode *aEndNode, int32_t aEndOffset)
215 {
216 NS_ENSURE_TRUE(aStartNode && aEndNode && aContent, false);
217
218 nsCOMPtr<nsIDOMNode> parentNode;
219 int32_t indx = 0;
220
221 ContentToParentOffset(aContent, getter_AddRefs(parentNode), &indx);
222
223 NS_ENSURE_TRUE(parentNode, false);
224
225 if (!aIsPreMode)
226 ++indx;
227
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);
233 }
234
235 static bool
ContentIsInTraversalRange(nsRange * aRange,nsIDOMNode * aNextNode,bool aIsPreMode)236 ContentIsInTraversalRange(nsRange* aRange, nsIDOMNode* aNextNode, bool aIsPreMode)
237 {
238 nsCOMPtr<nsIContent> content(do_QueryInterface(aNextNode));
239 NS_ENSURE_TRUE(content && aRange, false);
240
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);
250 }
251
252 //------------------------------------------------------------
253 // Helper function to advance to the next or previous node
254 nsresult
AdvanceNode(nsIDOMNode * aNode,nsIDOMNode * & aNewNode,eDirectionType aDir)255 nsFilteredContentIterator::AdvanceNode(nsIDOMNode* aNode, nsIDOMNode*& aNewNode, eDirectionType aDir)
256 {
257 nsCOMPtr<nsIDOMNode> nextNode;
258 if (aDir == eForward) {
259 aNode->GetNextSibling(getter_AddRefs(nextNode));
260 } else {
261 aNode->GetPreviousSibling(getter_AddRefs(nextNode));
262 }
263
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;
272 }
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");
278
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;
286 }
287 }
288 }
289
290 // if we get here it pretty much means
291 // we went out of the DOM Range
292 mIsOutOfRange = true;
293
294 return NS_ERROR_FAILURE;
295 }
296
297 //------------------------------------------------------------
298 // Helper function to see if the next/prev node should be skipped
299 void
CheckAdvNode(nsIDOMNode * aNode,bool & aDidSkip,eDirectionType aDir)300 nsFilteredContentIterator::CheckAdvNode(nsIDOMNode* aNode, bool& aDidSkip, eDirectionType aDir)
301 {
302 aDidSkip = false;
303 mIsOutOfRange = false;
304
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
320 }
321 } else {
322 if (aNode != currentNode) {
323 nsCOMPtr<nsIContent> content(do_QueryInterface(aNode));
324 mCurrentIterator->PositionAt(content);
325 }
326 return; // found something
327 }
328 }
329 }
330 }
331
332 void
Next()333 nsFilteredContentIterator::Next()
334 {
335 if (mIsOutOfRange || !mCurrentIterator) {
336 NS_ASSERTION(mCurrentIterator, "Missing iterator!");
337
338 return;
339 }
340
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;
347 }
348 }
349
350 mCurrentIterator->Next();
351
352 if (mCurrentIterator->IsDone()) {
353 return;
354 }
355
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();
359
360 nsCOMPtr<nsIDOMNode> node(do_QueryInterface(currentNode));
361 CheckAdvNode(node, mDidSkip, eForward);
362 }
363
364 void
Prev()365 nsFilteredContentIterator::Prev()
366 {
367 if (mIsOutOfRange || !mCurrentIterator) {
368 NS_ASSERTION(mCurrentIterator, "Missing iterator!");
369
370 return;
371 }
372
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;
379 }
380 }
381
382 mCurrentIterator->Prev();
383
384 if (mCurrentIterator->IsDone()) {
385 return;
386 }
387
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();
391
392 nsCOMPtr<nsIDOMNode> node(do_QueryInterface(currentNode));
393 CheckAdvNode(node, mDidSkip, eBackward);
394 }
395
396 nsINode *
GetCurrentNode()397 nsFilteredContentIterator::GetCurrentNode()
398 {
399 if (mIsOutOfRange || !mCurrentIterator) {
400 return nullptr;
401 }
402
403 return mCurrentIterator->GetCurrentNode();
404 }
405
406 bool
IsDone()407 nsFilteredContentIterator::IsDone()
408 {
409 if (mIsOutOfRange || !mCurrentIterator) {
410 return true;
411 }
412
413 return mCurrentIterator->IsDone();
414 }
415
416 nsresult
PositionAt(nsINode * aCurNode)417 nsFilteredContentIterator::PositionAt(nsINode* aCurNode)
418 {
419 NS_ENSURE_TRUE(mCurrentIterator, NS_ERROR_FAILURE);
420 mIsOutOfRange = false;
421 return mCurrentIterator->PositionAt(aCurNode);
422 }
423