Back to index

lightning-sunbird  0.9+nobinonly
nsFilteredContentIterator.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is mozilla.org code.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Netscape Communications Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 1998
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *
00024  * Alternatively, the contents of this file may be used under the terms of
00025  * either of the GNU General Public License Version 2 or later (the "GPL"),
00026  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00027  * in which case the provisions of the GPL or the LGPL are applicable instead
00028  * of those above. If you wish to allow use of your version of this file only
00029  * under the terms of either the GPL or the LGPL, and not to allow others to
00030  * use your version of this file under the terms of the MPL, indicate your
00031  * decision by deleting the provisions above and replace them with the notice
00032  * and other provisions required by the GPL or the LGPL. If you do not delete
00033  * the provisions above, a recipient may use your version of this file under
00034  * the terms of any one of the MPL, the GPL or the LGPL.
00035  *
00036  * ***** END LICENSE BLOCK ***** */
00037 
00038 #include "nsFilteredContentIterator.h"
00039 #include "nsIContentIterator.h"
00040 #include "nsComponentManagerUtils.h"
00041 #include "nsIContent.h"
00042 #include "nsString.h"
00043 #include "nsIEnumerator.h"
00044 
00045 #include "nsTextServicesDocument.h"
00046 
00047 #include "nsIDOMNode.h"
00048 #include "nsIDOMRange.h"
00049 
00050 //------------------------------------------------------------
00051 nsFilteredContentIterator::nsFilteredContentIterator(nsITextServicesFilter* aFilter) :
00052   mFilter(aFilter),
00053   mDidSkip(PR_FALSE),
00054   mIsOutOfRange(PR_FALSE),
00055   mDirection(eDirNotSet)
00056 {
00057   mIterator = do_CreateInstance("@mozilla.org/content/post-content-iterator;1");
00058   mPreIterator = do_CreateInstance("@mozilla.org/content/pre-content-iterator;1");
00059 }
00060 
00061 //------------------------------------------------------------
00062 nsFilteredContentIterator::~nsFilteredContentIterator()
00063 {
00064 }
00065 
00066 //------------------------------------------------------------
00067 NS_IMPL_ISUPPORTS1(nsFilteredContentIterator, nsIContentIterator)
00068 
00069 //------------------------------------------------------------
00070 nsresult
00071 nsFilteredContentIterator::Init(nsIContent* aRoot)
00072 {
00073   NS_ENSURE_TRUE(mPreIterator, NS_ERROR_FAILURE);
00074   NS_ENSURE_TRUE(mIterator, NS_ERROR_FAILURE);
00075   mIsOutOfRange    = PR_FALSE;
00076   mDirection       = eForward;
00077   mCurrentIterator = mPreIterator;
00078 
00079   nsresult rv;
00080   mRange = do_CreateInstance("@mozilla.org/content/range;1", &rv);
00081   NS_ENSURE_SUCCESS(rv, rv);
00082   nsCOMPtr<nsIDOMRange> domRange(do_QueryInterface(mRange));
00083   nsCOMPtr<nsIDOMNode> domNode(do_QueryInterface(aRoot));
00084   if (domRange && domNode) {
00085     domRange->SelectNode(domNode);
00086   }
00087 
00088   rv = mPreIterator->Init(domRange);
00089   NS_ENSURE_SUCCESS(rv, rv);
00090   return mIterator->Init(domRange);
00091 }
00092 
00093 //------------------------------------------------------------
00094 nsresult
00095 nsFilteredContentIterator::Init(nsIDOMRange* aRange)
00096 {
00097   NS_ENSURE_TRUE(mPreIterator, NS_ERROR_FAILURE);
00098   NS_ENSURE_TRUE(mIterator, NS_ERROR_FAILURE);
00099   NS_ENSURE_ARG_POINTER(aRange);
00100   mIsOutOfRange    = PR_FALSE;
00101   mDirection       = eForward;
00102   mCurrentIterator = mPreIterator;
00103 
00104   nsCOMPtr<nsIDOMRange> domRange;
00105   nsresult rv = aRange->CloneRange(getter_AddRefs(domRange));
00106   NS_ENSURE_SUCCESS(rv, rv);
00107   mRange = do_QueryInterface(domRange);
00108 
00109   rv = mPreIterator->Init(domRange);
00110   NS_ENSURE_SUCCESS(rv, rv);
00111   return mIterator->Init(domRange);
00112 }
00113 
00114 //------------------------------------------------------------
00115 nsresult 
00116 nsFilteredContentIterator::SwitchDirections(PRPackedBool aChangeToForward)
00117 {
00118   nsIContent *node = mCurrentIterator->GetCurrentNode();
00119 
00120   if (aChangeToForward) {
00121     mCurrentIterator = mPreIterator;
00122     mDirection       = eForward;
00123   } else {
00124     mCurrentIterator = mIterator;
00125     mDirection       = eBackward;
00126   }
00127 
00128   if (node) {
00129     nsresult rv = mCurrentIterator->PositionAt(node);
00130     if (NS_FAILED(rv)) {
00131       mIsOutOfRange = PR_TRUE;
00132       return rv;
00133     }
00134   }
00135   return NS_OK;
00136 }
00137 
00138 //------------------------------------------------------------
00139 void
00140 nsFilteredContentIterator::First()
00141 {
00142   if (!mCurrentIterator) {
00143     NS_ERROR("Missing iterator!");
00144 
00145     return;
00146   }
00147 
00148   // If we are switching directions then
00149   // we need to switch how we process the nodes
00150   if (mDirection != eForward) {
00151     mCurrentIterator = mPreIterator;
00152     mDirection       = eForward;
00153     mIsOutOfRange    = PR_FALSE;
00154   }
00155 
00156   mCurrentIterator->First();
00157 
00158   if (mCurrentIterator->IsDone()) {
00159     return;
00160   }
00161 
00162   nsIContent *currentContent = mCurrentIterator->GetCurrentNode();
00163   nsCOMPtr<nsIDOMNode> node(do_QueryInterface(currentContent));
00164 
00165   PRPackedBool didCross;
00166   CheckAdvNode(node, didCross, eForward);
00167 }
00168 
00169 //------------------------------------------------------------
00170 void
00171 nsFilteredContentIterator::Last()
00172 {
00173   if (!mCurrentIterator) {
00174     NS_ERROR("Missing iterator!");
00175 
00176     return;
00177   }
00178 
00179   // If we are switching directions then
00180   // we need to switch how we process the nodes
00181   if (mDirection != eBackward) {
00182     mCurrentIterator = mIterator;
00183     mDirection       = eBackward;
00184     mIsOutOfRange    = PR_FALSE;
00185   }
00186 
00187   mCurrentIterator->Last();
00188 
00189   if (mCurrentIterator->IsDone()) {
00190     return;
00191   }
00192 
00193   nsIContent *currentContent = mCurrentIterator->GetCurrentNode();
00194   nsCOMPtr<nsIDOMNode> node(do_QueryInterface(currentContent));
00195 
00196   PRPackedBool didCross;
00197   CheckAdvNode(node, didCross, eBackward);
00198 }
00199 
00201 // ContentToParentOffset: returns the content node's parent and offset.
00202 //
00203 static void
00204 ContentToParentOffset(nsIContent *aContent, nsIDOMNode **aParent,
00205                       PRInt32 *aOffset)
00206 {
00207   if (!aParent || !aOffset)
00208     return;
00209 
00210   *aParent = nsnull;
00211   *aOffset  = 0;
00212 
00213   if (!aContent)
00214     return;
00215 
00216   nsIContent* parent = aContent->GetParent();
00217 
00218   if (!parent)
00219     return;
00220 
00221   *aOffset = parent->IndexOf(aContent);
00222 
00223   CallQueryInterface(parent, aParent);
00224 }
00225 
00227 // ContentIsInTraversalRange: returns true if content is visited during
00228 // the traversal of the range in the specified mode.
00229 //
00230 static PRBool
00231 ContentIsInTraversalRange(nsIContent *aContent,   PRBool aIsPreMode,
00232                           nsIDOMNode *aStartNode, PRInt32 aStartOffset,
00233                           nsIDOMNode *aEndNode,   PRInt32 aEndOffset)
00234 {
00235   if (!aStartNode || !aEndNode || !aContent)
00236     return PR_FALSE;
00237 
00238   nsCOMPtr<nsIDOMNode> parentNode;
00239   PRInt32 indx = 0;
00240 
00241   ContentToParentOffset(aContent, getter_AddRefs(parentNode), &indx);
00242 
00243   if (!parentNode)
00244     return PR_FALSE;
00245 
00246   if (!aIsPreMode)
00247     ++indx;
00248 
00249   PRInt32 startRes;
00250   PRInt32 endRes;
00251   nsresult rv = nsTextServicesDocument::ComparePoints(aStartNode, aStartOffset, parentNode, indx, &startRes);
00252   if (NS_FAILED(rv)) return PR_FALSE;
00253 
00254   rv = nsTextServicesDocument::ComparePoints(aEndNode,   aEndOffset,   parentNode, indx,  &endRes);
00255   if (NS_FAILED(rv)) return PR_FALSE;
00256 
00257   return (startRes <= 0) && (endRes >= 0);
00258 }
00259 
00260 static PRBool
00261 ContentIsInTraversalRange(nsIDOMNSRange *aRange, nsIDOMNode* aNextNode, PRBool aIsPreMode)
00262 {
00263   nsCOMPtr<nsIContent>  content(do_QueryInterface(aNextNode));
00264   nsCOMPtr<nsIDOMRange> range(do_QueryInterface(aRange));
00265   if (!content || !range)
00266     return PR_FALSE;
00267 
00268 
00269 
00270   nsCOMPtr<nsIDOMNode> sNode;
00271   nsCOMPtr<nsIDOMNode> eNode;
00272   PRInt32 sOffset;
00273   PRInt32 eOffset;
00274   range->GetStartContainer(getter_AddRefs(sNode));
00275   range->GetStartOffset(&sOffset);
00276   range->GetEndContainer(getter_AddRefs(eNode));
00277   range->GetEndOffset(&eOffset);
00278   return ContentIsInTraversalRange(content, aIsPreMode, sNode, sOffset, eNode, eOffset);
00279 }
00280 
00281 //------------------------------------------------------------
00282 // Helper function to advance to the next or previous node
00283 nsresult 
00284 nsFilteredContentIterator::AdvanceNode(nsIDOMNode* aNode, nsIDOMNode*& aNewNode, eDirectionType aDir)
00285 {
00286   nsCOMPtr<nsIDOMNode> nextNode;
00287   if (aDir == eForward) {
00288     aNode->GetNextSibling(getter_AddRefs(nextNode));
00289   } else {
00290     aNode->GetPreviousSibling(getter_AddRefs(nextNode));
00291   }
00292 
00293   if (nextNode) {
00294     // If we got here, that means we found the nxt/prv node
00295     // make sure it is in our DOMRange
00296     PRBool intersects = ContentIsInTraversalRange(mRange, nextNode, aDir == eForward);
00297     if (intersects) {
00298       aNewNode = nextNode;
00299       NS_ADDREF(aNewNode);
00300       return NS_OK;
00301     }
00302   } else {
00303     // The next node was null so we need to walk up the parent(s)
00304     nsCOMPtr<nsIDOMNode> parent;
00305     aNode->GetParentNode(getter_AddRefs(parent));
00306     NS_ASSERTION(parent, "parent can't be NULL");
00307 
00308     // Make sure the parent is in the DOMRange before going further
00309     PRBool intersects = ContentIsInTraversalRange(mRange, nextNode, aDir == eForward);
00310     if (intersects) {
00311       // Now find the nxt/prv node after/before this node
00312       nsresult rv = AdvanceNode(parent, aNewNode, aDir);
00313       if (NS_SUCCEEDED(rv) && aNewNode) {
00314         return NS_OK;
00315       }
00316     }
00317   }
00318 
00319   // if we get here it pretty much means 
00320   // we went out of the DOM Range
00321   mIsOutOfRange = PR_TRUE;
00322 
00323   return NS_ERROR_FAILURE;
00324 }
00325 
00326 //------------------------------------------------------------
00327 // Helper function to see if the next/prev node should be skipped
00328 void
00329 nsFilteredContentIterator::CheckAdvNode(nsIDOMNode* aNode, PRPackedBool& aDidSkip, eDirectionType aDir)
00330 {
00331   aDidSkip      = PR_FALSE;
00332   mIsOutOfRange = PR_FALSE;
00333 
00334   if (aNode && mFilter) {
00335     nsCOMPtr<nsIDOMNode> currentNode = aNode;
00336     PRBool skipIt;
00337     while (1) {
00338       nsresult rv = mFilter->Skip(aNode, &skipIt);
00339       if (NS_SUCCEEDED(rv) && skipIt) {
00340         aDidSkip = PR_TRUE;
00341         // Get the next/prev node and then 
00342         // see if we should skip that
00343         nsCOMPtr<nsIDOMNode> advNode;
00344         rv = AdvanceNode(aNode, *getter_AddRefs(advNode), aDir);
00345         if (NS_SUCCEEDED(rv) && advNode) {
00346           aNode = advNode;
00347         } else {
00348           return; // fell out of range
00349         }
00350       } else {
00351         if (aNode != currentNode) {
00352           nsCOMPtr<nsIContent> content(do_QueryInterface(aNode));
00353           mCurrentIterator->PositionAt(content);
00354         }
00355         return; // found something
00356       }
00357     }
00358   }
00359 }
00360 
00361 void
00362 nsFilteredContentIterator::Next()
00363 {
00364   if (mIsOutOfRange || !mCurrentIterator) {
00365     NS_ASSERTION(mCurrentIterator, "Missing iterator!");
00366 
00367     return;
00368   }
00369 
00370   // If we are switching directions then
00371   // we need to switch how we process the nodes
00372   if (mDirection != eForward) {
00373     nsresult rv = SwitchDirections(PR_TRUE);
00374     if (NS_FAILED(rv)) {
00375       return;
00376     }
00377   }
00378 
00379   mCurrentIterator->Next();
00380 
00381   if (mCurrentIterator->IsDone()) {
00382     return;
00383   }
00384 
00385   // If we can't get the current node then 
00386   // don't check to see if we can skip it
00387   nsIContent *currentContent = mCurrentIterator->GetCurrentNode();
00388 
00389   nsCOMPtr<nsIDOMNode> node(do_QueryInterface(currentContent));
00390   CheckAdvNode(node, mDidSkip, eForward);
00391 }
00392 
00393 void
00394 nsFilteredContentIterator::Prev()
00395 {
00396   if (mIsOutOfRange || !mCurrentIterator) {
00397     NS_ASSERTION(mCurrentIterator, "Missing iterator!");
00398 
00399     return;
00400   }
00401 
00402   // If we are switching directions then
00403   // we need to switch how we process the nodes
00404   if (mDirection != eBackward) {
00405     nsresult rv = SwitchDirections(PR_FALSE);
00406     if (NS_FAILED(rv)) {
00407       return;
00408     }
00409   }
00410 
00411   mCurrentIterator->Prev();
00412 
00413   if (mCurrentIterator->IsDone()) {
00414     return;
00415   }
00416 
00417   // If we can't get the current node then 
00418   // don't check to see if we can skip it
00419   nsIContent *currentContent = mCurrentIterator->GetCurrentNode();
00420 
00421   nsCOMPtr<nsIDOMNode> node(do_QueryInterface(currentContent));
00422   CheckAdvNode(node, mDidSkip, eBackward);
00423 }
00424 
00425 nsIContent *
00426 nsFilteredContentIterator::GetCurrentNode()
00427 {
00428   if (mIsOutOfRange || !mCurrentIterator) {
00429     return nsnull;
00430   }
00431 
00432   return mCurrentIterator->GetCurrentNode();
00433 }
00434 
00435 PRBool
00436 nsFilteredContentIterator::IsDone()
00437 {
00438   if (mIsOutOfRange || !mCurrentIterator) {
00439     return PR_TRUE;
00440   }
00441 
00442   return mCurrentIterator->IsDone();
00443 }
00444 
00445 nsresult
00446 nsFilteredContentIterator::PositionAt(nsIContent* aCurNode)
00447 {
00448   NS_ENSURE_TRUE(mCurrentIterator, NS_ERROR_FAILURE);
00449   mIsOutOfRange = PR_FALSE;
00450   return mCurrentIterator->PositionAt(aCurNode);
00451 }