1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* ***** BEGIN LICENSE BLOCK *****
3 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 : *
5 : * The contents of this file are subject to the Mozilla Public License Version
6 : * 1.1 (the "License"); you may not use this file except in compliance with
7 : * the License. You may obtain a copy of the License at
8 : * http://www.mozilla.org/MPL/
9 : *
10 : * Software distributed under the License is distributed on an "AS IS" basis,
11 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 : * for the specific language governing rights and limitations under the
13 : * License.
14 : *
15 : * The Original Code is mozilla.org code.
16 : *
17 : * The Initial Developer of the Original Code is
18 : * Netscape Communications Corporation.
19 : * Portions created by the Initial Developer are Copyright (C) 1998
20 : * the Initial Developer. All Rights Reserved.
21 : *
22 : * Contributor(s):
23 : *
24 : * Alternatively, the contents of this file may be used under the terms of
25 : * either of the GNU General Public License Version 2 or later (the "GPL"),
26 : * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
27 : * in which case the provisions of the GPL or the LGPL are applicable instead
28 : * of those above. If you wish to allow use of your version of this file only
29 : * under the terms of either the GPL or the LGPL, and not to allow others to
30 : * use your version of this file under the terms of the MPL, indicate your
31 : * decision by deleting the provisions above and replace them with the notice
32 : * and other provisions required by the GPL or the LGPL. If you do not delete
33 : * the provisions above, a recipient may use your version of this file under
34 : * the terms of any one of the MPL, the GPL or the LGPL.
35 : *
36 : * ***** END LICENSE BLOCK ***** */
37 :
38 : #include "nsFilteredContentIterator.h"
39 : #include "nsIContentIterator.h"
40 : #include "nsComponentManagerUtils.h"
41 : #include "nsIContent.h"
42 : #include "nsString.h"
43 : #include "nsIEnumerator.h"
44 :
45 : #include "nsContentUtils.h"
46 :
47 : #include "nsIDOMNode.h"
48 :
49 : //------------------------------------------------------------
50 0 : nsFilteredContentIterator::nsFilteredContentIterator(nsITextServicesFilter* aFilter) :
51 : mFilter(aFilter),
52 : mDidSkip(false),
53 : mIsOutOfRange(false),
54 0 : mDirection(eDirNotSet)
55 : {
56 0 : mIterator = do_CreateInstance("@mozilla.org/content/post-content-iterator;1");
57 0 : mPreIterator = do_CreateInstance("@mozilla.org/content/pre-content-iterator;1");
58 0 : }
59 :
60 : //------------------------------------------------------------
61 0 : nsFilteredContentIterator::~nsFilteredContentIterator()
62 : {
63 0 : }
64 :
65 : //------------------------------------------------------------
66 0 : NS_IMPL_CYCLE_COLLECTING_ADDREF(nsFilteredContentIterator)
67 0 : NS_IMPL_CYCLE_COLLECTING_RELEASE(nsFilteredContentIterator)
68 :
69 0 : NS_INTERFACE_MAP_BEGIN(nsFilteredContentIterator)
70 0 : NS_INTERFACE_MAP_ENTRY(nsIContentIterator)
71 0 : NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIContentIterator)
72 0 : NS_INTERFACE_MAP_ENTRIES_CYCLE_COLLECTION(nsFilteredContentIterator)
73 0 : NS_INTERFACE_MAP_END
74 :
75 1464 : NS_IMPL_CYCLE_COLLECTION_5(nsFilteredContentIterator,
76 : mCurrentIterator,
77 : mIterator,
78 : mPreIterator,
79 : mFilter,
80 : mRange)
81 :
82 : //------------------------------------------------------------
83 : nsresult
84 0 : nsFilteredContentIterator::Init(nsINode* aRoot)
85 : {
86 0 : NS_ENSURE_TRUE(mPreIterator, NS_ERROR_FAILURE);
87 0 : NS_ENSURE_TRUE(mIterator, NS_ERROR_FAILURE);
88 0 : mIsOutOfRange = false;
89 0 : mDirection = eForward;
90 0 : mCurrentIterator = mPreIterator;
91 :
92 0 : mRange = new nsRange();
93 0 : nsCOMPtr<nsIDOMNode> domNode(do_QueryInterface(aRoot));
94 0 : if (domNode) {
95 0 : mRange->SelectNode(domNode);
96 : }
97 :
98 0 : nsresult rv = mPreIterator->Init(mRange);
99 0 : NS_ENSURE_SUCCESS(rv, rv);
100 0 : return mIterator->Init(mRange);
101 : }
102 :
103 : //------------------------------------------------------------
104 : nsresult
105 0 : nsFilteredContentIterator::Init(nsIDOMRange* aRange)
106 : {
107 0 : NS_ENSURE_TRUE(mPreIterator, NS_ERROR_FAILURE);
108 0 : NS_ENSURE_TRUE(mIterator, NS_ERROR_FAILURE);
109 0 : NS_ENSURE_ARG_POINTER(aRange);
110 0 : mIsOutOfRange = false;
111 0 : mDirection = eForward;
112 0 : mCurrentIterator = mPreIterator;
113 :
114 0 : nsCOMPtr<nsIDOMRange> domRange;
115 0 : nsresult rv = aRange->CloneRange(getter_AddRefs(domRange));
116 0 : NS_ENSURE_SUCCESS(rv, rv);
117 0 : mRange = do_QueryInterface(domRange);
118 :
119 0 : rv = mPreIterator->Init(domRange);
120 0 : NS_ENSURE_SUCCESS(rv, rv);
121 0 : return mIterator->Init(domRange);
122 : }
123 :
124 : //------------------------------------------------------------
125 : nsresult
126 0 : nsFilteredContentIterator::SwitchDirections(bool aChangeToForward)
127 : {
128 0 : nsINode *node = mCurrentIterator->GetCurrentNode();
129 :
130 0 : if (aChangeToForward) {
131 0 : mCurrentIterator = mPreIterator;
132 0 : mDirection = eForward;
133 : } else {
134 0 : mCurrentIterator = mIterator;
135 0 : mDirection = eBackward;
136 : }
137 :
138 0 : if (node) {
139 0 : nsresult rv = mCurrentIterator->PositionAt(node);
140 0 : if (NS_FAILED(rv)) {
141 0 : mIsOutOfRange = true;
142 0 : return rv;
143 : }
144 : }
145 0 : return NS_OK;
146 : }
147 :
148 : //------------------------------------------------------------
149 : void
150 0 : nsFilteredContentIterator::First()
151 : {
152 0 : if (!mCurrentIterator) {
153 0 : NS_ERROR("Missing iterator!");
154 :
155 0 : return;
156 : }
157 :
158 : // If we are switching directions then
159 : // we need to switch how we process the nodes
160 0 : if (mDirection != eForward) {
161 0 : mCurrentIterator = mPreIterator;
162 0 : mDirection = eForward;
163 0 : mIsOutOfRange = false;
164 : }
165 :
166 0 : mCurrentIterator->First();
167 :
168 0 : if (mCurrentIterator->IsDone()) {
169 0 : return;
170 : }
171 :
172 0 : nsINode *currentNode = mCurrentIterator->GetCurrentNode();
173 0 : nsCOMPtr<nsIDOMNode> node(do_QueryInterface(currentNode));
174 :
175 : bool didCross;
176 0 : CheckAdvNode(node, didCross, eForward);
177 : }
178 :
179 : //------------------------------------------------------------
180 : void
181 0 : nsFilteredContentIterator::Last()
182 : {
183 0 : if (!mCurrentIterator) {
184 0 : NS_ERROR("Missing iterator!");
185 :
186 0 : return;
187 : }
188 :
189 : // If we are switching directions then
190 : // we need to switch how we process the nodes
191 0 : if (mDirection != eBackward) {
192 0 : mCurrentIterator = mIterator;
193 0 : mDirection = eBackward;
194 0 : mIsOutOfRange = false;
195 : }
196 :
197 0 : mCurrentIterator->Last();
198 :
199 0 : if (mCurrentIterator->IsDone()) {
200 0 : return;
201 : }
202 :
203 0 : nsINode *currentNode = mCurrentIterator->GetCurrentNode();
204 0 : nsCOMPtr<nsIDOMNode> node(do_QueryInterface(currentNode));
205 :
206 : bool didCross;
207 0 : CheckAdvNode(node, didCross, eBackward);
208 : }
209 :
210 : ///////////////////////////////////////////////////////////////////////////
211 : // ContentToParentOffset: returns the content node's parent and offset.
212 : //
213 : static void
214 0 : ContentToParentOffset(nsIContent *aContent, nsIDOMNode **aParent,
215 : PRInt32 *aOffset)
216 : {
217 0 : if (!aParent || !aOffset)
218 0 : return;
219 :
220 0 : *aParent = nsnull;
221 0 : *aOffset = 0;
222 :
223 0 : if (!aContent)
224 0 : return;
225 :
226 0 : nsIContent* parent = aContent->GetParent();
227 :
228 0 : if (!parent)
229 0 : return;
230 :
231 0 : *aOffset = parent->IndexOf(aContent);
232 :
233 0 : CallQueryInterface(parent, aParent);
234 : }
235 :
236 : ///////////////////////////////////////////////////////////////////////////
237 : // ContentIsInTraversalRange: returns true if content is visited during
238 : // the traversal of the range in the specified mode.
239 : //
240 : static bool
241 0 : ContentIsInTraversalRange(nsIContent *aContent, bool aIsPreMode,
242 : nsIDOMNode *aStartNode, PRInt32 aStartOffset,
243 : nsIDOMNode *aEndNode, PRInt32 aEndOffset)
244 : {
245 0 : NS_ENSURE_TRUE(aStartNode && aEndNode && aContent, false);
246 :
247 0 : nsCOMPtr<nsIDOMNode> parentNode;
248 0 : PRInt32 indx = 0;
249 :
250 0 : ContentToParentOffset(aContent, getter_AddRefs(parentNode), &indx);
251 :
252 0 : NS_ENSURE_TRUE(parentNode, false);
253 :
254 0 : if (!aIsPreMode)
255 0 : ++indx;
256 :
257 : PRInt32 startRes = nsContentUtils::ComparePoints(aStartNode, aStartOffset,
258 0 : parentNode, indx);
259 : PRInt32 endRes = nsContentUtils::ComparePoints(aEndNode, aEndOffset,
260 0 : parentNode, indx);
261 0 : return (startRes <= 0) && (endRes >= 0);
262 : }
263 :
264 : static bool
265 0 : ContentIsInTraversalRange(nsIDOMRange *aRange, nsIDOMNode* aNextNode, bool aIsPreMode)
266 : {
267 0 : nsCOMPtr<nsIContent> content(do_QueryInterface(aNextNode));
268 0 : NS_ENSURE_TRUE(content && aRange, false);
269 :
270 0 : nsCOMPtr<nsIDOMNode> sNode;
271 0 : nsCOMPtr<nsIDOMNode> eNode;
272 : PRInt32 sOffset;
273 : PRInt32 eOffset;
274 0 : aRange->GetStartContainer(getter_AddRefs(sNode));
275 0 : aRange->GetStartOffset(&sOffset);
276 0 : aRange->GetEndContainer(getter_AddRefs(eNode));
277 0 : aRange->GetEndOffset(&eOffset);
278 0 : return ContentIsInTraversalRange(content, aIsPreMode, sNode, sOffset, eNode, eOffset);
279 : }
280 :
281 : //------------------------------------------------------------
282 : // Helper function to advance to the next or previous node
283 : nsresult
284 0 : nsFilteredContentIterator::AdvanceNode(nsIDOMNode* aNode, nsIDOMNode*& aNewNode, eDirectionType aDir)
285 : {
286 0 : nsCOMPtr<nsIDOMNode> nextNode;
287 0 : if (aDir == eForward) {
288 0 : aNode->GetNextSibling(getter_AddRefs(nextNode));
289 : } else {
290 0 : aNode->GetPreviousSibling(getter_AddRefs(nextNode));
291 : }
292 :
293 0 : if (nextNode) {
294 : // If we got here, that means we found the nxt/prv node
295 : // make sure it is in our DOMRange
296 0 : bool intersects = ContentIsInTraversalRange(mRange, nextNode, aDir == eForward);
297 0 : if (intersects) {
298 0 : aNewNode = nextNode;
299 0 : NS_ADDREF(aNewNode);
300 0 : return NS_OK;
301 : }
302 : } else {
303 : // The next node was null so we need to walk up the parent(s)
304 0 : nsCOMPtr<nsIDOMNode> parent;
305 0 : aNode->GetParentNode(getter_AddRefs(parent));
306 0 : NS_ASSERTION(parent, "parent can't be NULL");
307 :
308 : // Make sure the parent is in the DOMRange before going further
309 0 : bool intersects = ContentIsInTraversalRange(mRange, nextNode, aDir == eForward);
310 0 : if (intersects) {
311 : // Now find the nxt/prv node after/before this node
312 0 : nsresult rv = AdvanceNode(parent, aNewNode, aDir);
313 0 : if (NS_SUCCEEDED(rv) && aNewNode) {
314 0 : return NS_OK;
315 : }
316 : }
317 : }
318 :
319 : // if we get here it pretty much means
320 : // we went out of the DOM Range
321 0 : mIsOutOfRange = true;
322 :
323 0 : return NS_ERROR_FAILURE;
324 : }
325 :
326 : //------------------------------------------------------------
327 : // Helper function to see if the next/prev node should be skipped
328 : void
329 0 : nsFilteredContentIterator::CheckAdvNode(nsIDOMNode* aNode, bool& aDidSkip, eDirectionType aDir)
330 : {
331 0 : aDidSkip = false;
332 0 : mIsOutOfRange = false;
333 :
334 0 : if (aNode && mFilter) {
335 0 : nsCOMPtr<nsIDOMNode> currentNode = aNode;
336 : bool skipIt;
337 0 : while (1) {
338 0 : nsresult rv = mFilter->Skip(aNode, &skipIt);
339 0 : if (NS_SUCCEEDED(rv) && skipIt) {
340 0 : aDidSkip = true;
341 : // Get the next/prev node and then
342 : // see if we should skip that
343 0 : nsCOMPtr<nsIDOMNode> advNode;
344 0 : rv = AdvanceNode(aNode, *getter_AddRefs(advNode), aDir);
345 0 : if (NS_SUCCEEDED(rv) && advNode) {
346 0 : aNode = advNode;
347 : } else {
348 : return; // fell out of range
349 : }
350 : } else {
351 0 : if (aNode != currentNode) {
352 0 : nsCOMPtr<nsIContent> content(do_QueryInterface(aNode));
353 0 : mCurrentIterator->PositionAt(content);
354 : }
355 : return; // found something
356 : }
357 : }
358 : }
359 : }
360 :
361 : void
362 0 : nsFilteredContentIterator::Next()
363 : {
364 0 : if (mIsOutOfRange || !mCurrentIterator) {
365 0 : NS_ASSERTION(mCurrentIterator, "Missing iterator!");
366 :
367 0 : return;
368 : }
369 :
370 : // If we are switching directions then
371 : // we need to switch how we process the nodes
372 0 : if (mDirection != eForward) {
373 0 : nsresult rv = SwitchDirections(true);
374 0 : if (NS_FAILED(rv)) {
375 0 : return;
376 : }
377 : }
378 :
379 0 : mCurrentIterator->Next();
380 :
381 0 : if (mCurrentIterator->IsDone()) {
382 0 : return;
383 : }
384 :
385 : // If we can't get the current node then
386 : // don't check to see if we can skip it
387 0 : nsINode *currentNode = mCurrentIterator->GetCurrentNode();
388 :
389 0 : nsCOMPtr<nsIDOMNode> node(do_QueryInterface(currentNode));
390 0 : CheckAdvNode(node, mDidSkip, eForward);
391 : }
392 :
393 : void
394 0 : nsFilteredContentIterator::Prev()
395 : {
396 0 : if (mIsOutOfRange || !mCurrentIterator) {
397 0 : NS_ASSERTION(mCurrentIterator, "Missing iterator!");
398 :
399 0 : return;
400 : }
401 :
402 : // If we are switching directions then
403 : // we need to switch how we process the nodes
404 0 : if (mDirection != eBackward) {
405 0 : nsresult rv = SwitchDirections(false);
406 0 : if (NS_FAILED(rv)) {
407 0 : return;
408 : }
409 : }
410 :
411 0 : mCurrentIterator->Prev();
412 :
413 0 : if (mCurrentIterator->IsDone()) {
414 0 : return;
415 : }
416 :
417 : // If we can't get the current node then
418 : // don't check to see if we can skip it
419 0 : nsINode *currentNode = mCurrentIterator->GetCurrentNode();
420 :
421 0 : nsCOMPtr<nsIDOMNode> node(do_QueryInterface(currentNode));
422 0 : CheckAdvNode(node, mDidSkip, eBackward);
423 : }
424 :
425 : nsINode *
426 0 : nsFilteredContentIterator::GetCurrentNode()
427 : {
428 0 : if (mIsOutOfRange || !mCurrentIterator) {
429 0 : return nsnull;
430 : }
431 :
432 0 : return mCurrentIterator->GetCurrentNode();
433 : }
434 :
435 : bool
436 0 : nsFilteredContentIterator::IsDone()
437 : {
438 0 : if (mIsOutOfRange || !mCurrentIterator) {
439 0 : return true;
440 : }
441 :
442 0 : return mCurrentIterator->IsDone();
443 : }
444 :
445 : nsresult
446 0 : nsFilteredContentIterator::PositionAt(nsINode* aCurNode)
447 : {
448 0 : NS_ENSURE_TRUE(mCurrentIterator, NS_ERROR_FAILURE);
449 0 : mIsOutOfRange = false;
450 0 : return mCurrentIterator->PositionAt(aCurNode);
451 4392 : }
|