Back to index

lightning-sunbird  0.9+nobinonly
nsFind.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 the guts of the find algorithm.
00016  *
00017  * The Initial Developer of the Original Code is Akkana Peck.
00018  *
00019  * Portions created by the Initial Developer are Copyright (C) 2002
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *   Akkana Peck <akkana@netscape.com>
00024  *   Roger B. Sidje <rbs@maths.uq.edu.au> (added find in <textarea> & text <input>)
00025  *
00026  * Alternatively, the contents of this file may be used under the terms of
00027  * either the GNU General Public License Version 2 or later (the "GPL"), or
00028  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00029  * in which case the provisions of the GPL or the LGPL are applicable instead
00030  * of those above. If you wish to allow use of your version of this file only
00031  * under the terms of either the GPL or the LGPL, and not to allow others to
00032  * use your version of this file under the terms of the MPL, indicate your
00033  * decision by deleting the provisions above and replace them with the notice
00034  * and other provisions required by the GPL or the LGPL. If you do not delete
00035  * the provisions above, a recipient may use your version of this file under
00036  * the terms of any one of the MPL, the GPL or the LGPL.
00037  *
00038  * ***** END LICENSE BLOCK ***** */
00039 
00040 //#define DEBUG_FIND 1
00041 
00042 #include "nsFind.h"
00043 #include "nsContentCID.h"
00044 #include "nsIEnumerator.h"
00045 #include "nsITextContent.h"
00046 #include "nsIDOMNode.h"
00047 #include "nsIDOMNodeList.h"
00048 #include "nsIDOMDocumentRange.h"
00049 #include "nsIDOMDocumentTraversal.h"
00050 #include "nsISelection.h"
00051 #include "nsISelectionController.h"
00052 #include "nsIPresShell.h"
00053 #include "nsIFrame.h"
00054 #include "nsITextControlFrame.h"
00055 #include "nsIFormControl.h"
00056 #include "nsIEditor.h"
00057 #include "nsIPlaintextEditor.h"
00058 #include "nsIDocument.h"
00059 #include "nsTextFragment.h"
00060 #include "nsString.h"
00061 #include "nsIAtom.h"
00062 #include "nsParserCIID.h"
00063 #include "nsServiceManagerUtils.h"
00064 #include "nsUnicharUtils.h"
00065 #include "nsIDOMElement.h"
00066 #include "nsCRT.h"
00067 
00068 // Yikes!  Casting a char to unichar can fill with ones!
00069 #define CHAR_TO_UNICHAR(c) ((PRUnichar)(const unsigned char)c)
00070 
00071 static NS_DEFINE_CID(kCContentIteratorCID, NS_CONTENTITERATOR_CID);
00072 static NS_DEFINE_CID(kCPreContentIteratorCID, NS_PRECONTENTITERATOR_CID);
00073 static NS_DEFINE_IID(kRangeCID, NS_RANGE_CID);
00074 
00075 // -----------------------------------------------------------------------
00076 // nsFindContentIterator is a special iterator that also goes through
00077 // any existing <textarea>'s or text <input>'s editor to lookup the
00078 // anonymous DOM content there.
00079 //
00080 // Details:
00081 // 1) We use two iterators: The "outer-iterator" goes through the
00082 // normal DOM. The "inner-iterator" goes through the anonymous DOM
00083 // inside the editor.
00084 //
00085 // 2) [MaybeSetupInnerIterator] As soon as the outer-iterator's current
00086 // node is changed, a check is made to see if the node is a <textarea> or
00087 // a text <input> node. If so, an inner-iterator is created to lookup the
00088 // anynomous contents of the editor underneath the text control.
00089 //
00090 // 3) When the inner-iterator is created, we position the outer-iterator
00091 // 'after' (or 'before' in backward search) the text control to avoid
00092 // revisiting that control.
00093 //
00094 // 4) As a consequence of searching through text controls, we can be
00095 // called via FindNext with the current selection inside a <textarea>
00096 // or a text <input>. This means that we can be given an initial search
00097 // range that stretches across the anonymous DOM and the normal DOM. To
00098 // cater for this situation, we split the anonymous part into the 
00099 // inner-iterator and then reposition the outer-iterator outside.
00100 //
00101 // 5) The implementation assumes that First() and Next() are only called
00102 // in find-forward mode, while Last() and Prev() are used in find-backward.
00103 
00104 class nsFindContentIterator : public nsIContentIterator
00105 {
00106 public:
00107   nsFindContentIterator(PRBool aFindBackward)
00108     : mOuterIterator(nsnull)
00109     , mInnerIterator(nsnull)
00110     , mRange(nsnull)
00111     , mStartOuterNode(nsnull)
00112     , mEndOuterNode(nsnull)
00113     , mFindBackward(aFindBackward)
00114   {
00115   }
00116 
00117   virtual ~nsFindContentIterator()
00118   {
00119   }
00120 
00121   // nsISupports
00122   NS_DECL_ISUPPORTS
00123 
00124   // nsIContentIterator
00125   virtual nsresult Init(nsIContent* aRoot)
00126   {
00127     NS_NOTREACHED("internal error");
00128     return NS_ERROR_NOT_IMPLEMENTED;
00129   }
00130   virtual nsresult Init(nsIDOMRange* aRange);
00131   virtual void First();
00132   virtual void Last();
00133   virtual void Next();
00134   virtual void Prev();
00135   virtual nsIContent* GetCurrentNode();
00136   virtual PRBool IsDone();
00137   virtual nsresult PositionAt(nsIContent* aCurNode);
00138 
00139 private:
00140   nsCOMPtr<nsIContentIterator> mOuterIterator;
00141   nsCOMPtr<nsIContentIterator> mInnerIterator;
00142   nsCOMPtr<nsIDOMRange> mRange;
00143   nsCOMPtr<nsIDOMNode> mStartOuterNode;
00144   nsCOMPtr<nsIDOMNode> mEndOuterNode;
00145   PRBool mFindBackward;
00146 
00147   void Reset();
00148   void MaybeSetupInnerIterator();
00149   void SetupInnerIterator(nsIContent* aContent);
00150 };
00151 
00152 NS_IMPL_ISUPPORTS1(nsFindContentIterator, nsIContentIterator)
00153 
00154 nsresult
00155 nsFindContentIterator::Init(nsIDOMRange* aRange)
00156 {
00157   if (!mOuterIterator) {
00158     if (mFindBackward) {
00159       // Use post-order in the reverse case, so we get parents
00160       // before children in case we want to prevent descending
00161       // into a node.
00162       mOuterIterator = do_CreateInstance(kCContentIteratorCID);
00163     }
00164     else {
00165       // Use pre-order in the forward case, so we get parents
00166       // before children in case we want to prevent descending
00167       // into a node.
00168       mOuterIterator = do_CreateInstance(kCPreContentIteratorCID);
00169     }
00170     NS_ENSURE_ARG_POINTER(mOuterIterator);
00171   }
00172 
00173   // mRange is the search range that we will examine
00174   return aRange->CloneRange(getter_AddRefs(mRange));
00175 }
00176 
00177 void
00178 nsFindContentIterator::First()
00179 {
00180   Reset();
00181 }
00182 
00183 void
00184 nsFindContentIterator::Last()
00185 {
00186   Reset();
00187 }
00188 
00189 void
00190 nsFindContentIterator::Next()
00191 {
00192   if (mInnerIterator) {
00193     mInnerIterator->Next();
00194     if (!mInnerIterator->IsDone())
00195       return;
00196 
00197     // by construction, mOuterIterator is already on the next node
00198   }
00199   else {
00200     mOuterIterator->Next();
00201   }
00202   MaybeSetupInnerIterator();  
00203 }
00204 
00205 void
00206 nsFindContentIterator::Prev()
00207 {
00208   if (mInnerIterator) {
00209     mInnerIterator->Prev();
00210     if (!mInnerIterator->IsDone())
00211       return;
00212 
00213     // by construction, mOuterIterator is already on the previous node
00214   }
00215   else {
00216     mOuterIterator->Prev();
00217   }
00218   MaybeSetupInnerIterator();
00219 }
00220 
00221 nsIContent*
00222 nsFindContentIterator::GetCurrentNode()
00223 {
00224   if (mInnerIterator && !mInnerIterator->IsDone()) {
00225     return mInnerIterator->GetCurrentNode();
00226   }
00227   return mOuterIterator->GetCurrentNode();
00228 }
00229 
00230 PRBool
00231 nsFindContentIterator::IsDone() {
00232   if (mInnerIterator && !mInnerIterator->IsDone()) {
00233     return PR_FALSE;
00234   }
00235   return mOuterIterator->IsDone();
00236 }
00237 
00238 nsresult
00239 nsFindContentIterator::PositionAt(nsIContent* aCurNode)
00240 {
00241   nsIContent* oldNode = mOuterIterator->GetCurrentNode();
00242   nsresult rv = mOuterIterator->PositionAt(aCurNode);
00243   if (NS_SUCCEEDED(rv)) {
00244     MaybeSetupInnerIterator();
00245   }
00246   else {
00247     mOuterIterator->PositionAt(oldNode);
00248     if (mInnerIterator)
00249       rv = mInnerIterator->PositionAt(aCurNode);
00250   }
00251   return rv;
00252 }
00253 
00254 void
00255 nsFindContentIterator::Reset()
00256 {
00257   mInnerIterator = nsnull;
00258   mStartOuterNode = nsnull;
00259   mEndOuterNode = nsnull;
00260 
00261   // As a consequence of searching through text controls, we may have been
00262   // initialized with a selection inside a <textarea> or a text <input>.
00263 
00264   // see if the start node is an anonymous text node inside a text control
00265   nsCOMPtr<nsIDOMNode> startNode;
00266   mRange->GetStartContainer(getter_AddRefs(startNode));
00267   nsCOMPtr<nsIContent> startContent(do_QueryInterface(startNode));
00268   for ( ; startContent; startContent = startContent->GetParent()) {
00269     if (!startContent->IsNativeAnonymous()) {
00270       mStartOuterNode = do_QueryInterface(startContent);
00271       break;
00272     }
00273   }
00274 
00275   // see if the end node is an anonymous text node inside a text control
00276   nsCOMPtr<nsIDOMNode> endNode;
00277   mRange->GetEndContainer(getter_AddRefs(endNode));
00278   nsCOMPtr<nsIContent> endContent(do_QueryInterface(endNode));
00279   for ( ; endContent; endContent = endContent->GetParent()) {
00280     if (!endContent->IsNativeAnonymous()) {
00281       mEndOuterNode = do_QueryInterface(endContent);
00282       break;
00283     }
00284   }
00285 
00286   mOuterIterator->Init(mRange);
00287 
00288   if (!mFindBackward) {
00289     if (mStartOuterNode != startNode) {
00290       // the start node was an anonymous text node
00291       SetupInnerIterator(startContent);
00292       if (mInnerIterator)
00293         mInnerIterator->First();
00294     }
00295     mOuterIterator->First();
00296   }
00297   else {
00298     if (mEndOuterNode != endNode) {
00299       // the end node was an anonymous text node
00300       SetupInnerIterator(endContent);
00301       if (mInnerIterator)
00302         mInnerIterator->Last();
00303     }
00304     mOuterIterator->Last();
00305   }
00306 
00307   // if we didn't create an inner-iterator, the boundary node could still be
00308   // a text control, in which case we also need an inner-iterator straightaway
00309   if (!mInnerIterator) {
00310     MaybeSetupInnerIterator();
00311   }
00312 }
00313 
00314 void
00315 nsFindContentIterator::MaybeSetupInnerIterator()
00316 {
00317   mInnerIterator = nsnull;
00318 
00319   nsIContent* content = mOuterIterator->GetCurrentNode();
00320   if (!content || !content->IsContentOfType(nsIContent::eHTML_FORM_CONTROL))
00321     return;
00322 
00323   nsCOMPtr<nsIFormControl> formControl(do_QueryInterface(content));
00324   PRInt32 controlType = formControl->GetType();
00325   if (controlType != NS_FORM_TEXTAREA && 
00326       controlType != NS_FORM_INPUT_TEXT)
00327     return;
00328 
00329   SetupInnerIterator(content);
00330   if (mInnerIterator) {
00331     if (!mFindBackward) {
00332       mInnerIterator->First();
00333       // finish setup: position mOuterIterator on the actual "next"
00334       // node (this completes its re-init, @see SetupInnerIterator)
00335       mOuterIterator->First();
00336     }
00337     else {
00338       mInnerIterator->Last();
00339       // finish setup: position mOuterIterator on the actual "previous"
00340       // node (this completes its re-init, @see SetupInnerIterator)
00341       mOuterIterator->Last();
00342     }
00343   }
00344 }
00345 
00346 void
00347 nsFindContentIterator::SetupInnerIterator(nsIContent* aContent)
00348 {
00349   NS_ASSERTION(aContent && !aContent->IsNativeAnonymous(), "invalid call");
00350 
00351   nsIDocument* doc = aContent->GetDocument();
00352   nsIPresShell* shell = doc ? doc->GetShellAt(0) : nsnull;
00353   if (!shell)
00354     return;
00355 
00356   nsIFrame* frame = nsnull;
00357   shell->GetPrimaryFrameFor(aContent, &frame);
00358   if (!frame)
00359     return;
00360 
00361   nsITextControlFrame* tcFrame = nsnull;
00362   CallQueryInterface(frame, &tcFrame);
00363   if (!tcFrame)
00364     return;
00365 
00366   nsCOMPtr<nsIEditor> editor;
00367   tcFrame->GetEditor(getter_AddRefs(editor));
00368   if (!editor)
00369     return;
00370 
00371   // don't mess with disabled input fields
00372   PRUint32 editorFlags = 0;
00373   editor->GetFlags(&editorFlags);
00374   if (editorFlags & nsIPlaintextEditor::eEditorDisabledMask)
00375     return;
00376 
00377   nsCOMPtr<nsIDOMElement> rootElement;
00378   editor->GetRootElement(getter_AddRefs(rootElement));
00379   nsCOMPtr<nsIContent> rootContent(do_QueryInterface(rootElement));
00380 
00381   // now create the inner-iterator
00382   mInnerIterator = do_CreateInstance(kCPreContentIteratorCID);
00383 
00384   if (mInnerIterator) {
00385     nsCOMPtr<nsIDOMNode> node(do_QueryInterface(rootContent));
00386     nsCOMPtr<nsIDOMRange> range(do_CreateInstance(kRangeCID));
00387     range->SelectNodeContents(node);
00388 
00389     // fix up the inner bounds, we may have to only lookup a portion
00390     // of the text control if the current node is a boundary point
00391     PRInt32 offset;
00392     nsCOMPtr<nsIDOMNode> outerNode(do_QueryInterface(aContent));
00393     if (outerNode == mStartOuterNode) {
00394       mRange->GetStartOffset(&offset);
00395       mRange->GetStartContainer(getter_AddRefs(node));
00396       range->SetStart(node, offset);
00397     }
00398     if (outerNode == mEndOuterNode) {
00399       mRange->GetEndOffset(&offset);
00400       mRange->GetEndContainer(getter_AddRefs(node));
00401       range->SetEnd(node, offset);
00402     }
00403     // Note: we just init here. We do First() or Last() later. 
00404     mInnerIterator->Init(range);
00405 
00406     // make sure to place the outer-iterator outside
00407     // the text control so that we don't go there again.
00408     nsresult res;
00409     mRange->CloneRange(getter_AddRefs(range));
00410     if (!mFindBackward) { // find forward
00411       // cut the outer-iterator after the current node
00412       res = range->SetStartAfter(outerNode);
00413     }
00414     else { // find backward
00415       // cut the outer-iterator before the current node
00416       res = range->SetEndBefore(outerNode);
00417     }
00418     if (NS_FAILED(res)) {
00419       // we are done with the outer-iterator, the 
00420       // inner-iterator will traverse what we want
00421       range->Collapse(PR_TRUE);
00422     }
00423     // Note: we just re-init here, using the segment of mRange that is
00424     // yet to be visited. Thus when we later do mOuterIterator->First() 
00425     // [or mOuterIterator->Last()], we will effectively be on the next
00426     // node [or the previous node] _with respect to_ mRange. 
00427     mOuterIterator->Init(range);
00428   }
00429 }
00430 
00431 nsresult
00432 NS_NewFindContentIterator(PRBool aFindBackward,
00433                           nsIContentIterator** aResult)
00434 {
00435   NS_ENSURE_ARG_POINTER(aResult);
00436   if (!aResult) {
00437     return NS_ERROR_NULL_POINTER;
00438   }
00439 
00440   nsFindContentIterator* it = new nsFindContentIterator(aFindBackward);
00441   if (!it) {
00442     return NS_ERROR_OUT_OF_MEMORY;
00443   }
00444   return it->QueryInterface(NS_GET_IID(nsIContentIterator), (void **)aResult);
00445 }
00446 // --------------------------------------------------------------------
00447 
00448 // Sure would be nice if we could just get these from somewhere else!
00449 PRInt32 nsFind::sInstanceCount = 0;
00450 nsIAtom* nsFind::sImgAtom = nsnull;
00451 nsIAtom* nsFind::sHRAtom = nsnull;
00452 nsIAtom* nsFind::sScriptAtom = nsnull;
00453 nsIAtom* nsFind::sNoframesAtom = nsnull;
00454 nsIAtom* nsFind::sSelectAtom = nsnull;
00455 nsIAtom* nsFind::sTextareaAtom = nsnull;
00456 nsIAtom* nsFind::sThAtom = nsnull;
00457 nsIAtom* nsFind::sTdAtom = nsnull;
00458 
00459 NS_IMPL_ISUPPORTS1(nsFind, nsIFind)
00460 
00461 nsFind::nsFind()
00462   : mFindBackward(PR_FALSE)
00463   , mCaseSensitive(PR_FALSE)
00464   , mIterOffset(0)
00465 {
00466   // Initialize the atoms if they aren't already:
00467   if (sInstanceCount <= 0)
00468   {
00469     sImgAtom = NS_NewAtom("img");
00470     sHRAtom = NS_NewAtom("hr");
00471     sScriptAtom = NS_NewAtom("script");
00472     sNoframesAtom = NS_NewAtom("noframes");
00473     sSelectAtom = NS_NewAtom("select");
00474     sTextareaAtom = NS_NewAtom("textarea");
00475     sThAtom = NS_NewAtom("th");
00476     sTdAtom = NS_NewAtom("td");
00477   }
00478   ++sInstanceCount;
00479 }
00480 
00481 nsFind::~nsFind()
00482 {
00483   if (sInstanceCount <= 1)
00484   {
00485     NS_IF_RELEASE(sImgAtom);
00486     NS_IF_RELEASE(sHRAtom);
00487     NS_IF_RELEASE(sScriptAtom);
00488     NS_IF_RELEASE(sNoframesAtom);
00489     NS_IF_RELEASE(sSelectAtom);
00490     NS_IF_RELEASE(sTextareaAtom);
00491     NS_IF_RELEASE(sThAtom);
00492     NS_IF_RELEASE(sTdAtom);
00493   }
00494   --sInstanceCount;
00495 }
00496 
00497 #ifdef DEBUG_FIND
00498 static void DumpNode(nsIDOMNode* aNode)
00499 {
00500   if (!aNode)
00501   {
00502     printf(">>>> Node: NULL\n");
00503     return;
00504   }
00505   nsAutoString nodeName;
00506   aNode->GetNodeName(nodeName);
00507   nsCOMPtr<nsITextContent> textContent (do_QueryInterface(aNode));
00508   if (textContent)
00509   {
00510     nsAutoString newText;
00511     textContent->CopyText(newText);
00512     printf(">>>> Text node (node name %s): '%s'\n",
00513            NS_LossyConvertUCS2toASCII(nodeName).get(),
00514            NS_LossyConvertUCS2toASCII(newText).get());
00515   }
00516   else
00517     printf(">>>> Node: %s\n", NS_LossyConvertUCS2toASCII(nodeName).get());
00518 }
00519 
00520 static void DumpRange(nsIDOMRange* aRange)
00521 {
00522   nsCOMPtr<nsIDOMNode> startN;
00523   nsCOMPtr<nsIDOMNode> endN;
00524   PRInt32 startO, endO;
00525   aRange->GetStartContainer(getter_AddRefs(startN));
00526   aRange->GetStartOffset(&startO);
00527   aRange->GetEndContainer(getter_AddRefs(endN));
00528   aRange->GetEndOffset(&endO);
00529   printf(" -- start %d, ", startO); DumpNode(startN);
00530   printf(" -- end %d, ", endO); DumpNode(endN);
00531 }
00532 #endif
00533 
00534 nsresult
00535 nsFind::InitIterator(nsIDOMRange* aSearchRange)
00536 {
00537   nsresult rv;
00538   if (!mIterator)
00539   {
00540     rv = NS_NewFindContentIterator(mFindBackward, getter_AddRefs(mIterator));
00541     NS_ENSURE_SUCCESS(rv, rv);
00542     NS_ENSURE_ARG_POINTER(mIterator);
00543   }
00544 
00545   NS_ENSURE_ARG_POINTER(aSearchRange);
00546 
00547 #ifdef DEBUG_FIND
00548   printf("InitIterator search range:\n"); DumpRange(aSearchRange);
00549 #endif
00550 
00551   rv = mIterator->Init(aSearchRange);
00552   NS_ENSURE_SUCCESS(rv, rv);
00553   if (mFindBackward) {
00554     mIterator->Last();
00555   }
00556   else {
00557     mIterator->First();
00558   }
00559   return NS_OK;
00560 }
00561 
00562 /* attribute boolean findBackward; */
00563 NS_IMETHODIMP
00564 nsFind::GetFindBackwards(PRBool *aFindBackward)
00565 {
00566   if (!aFindBackward)
00567     return NS_ERROR_NULL_POINTER;
00568 
00569   *aFindBackward = mFindBackward;
00570   return NS_OK;
00571 }
00572 NS_IMETHODIMP
00573 nsFind::SetFindBackwards(PRBool aFindBackward)
00574 {
00575   mFindBackward = aFindBackward;
00576   return NS_OK;
00577 }
00578 
00579 /* attribute boolean caseSensitive; */
00580 NS_IMETHODIMP
00581 nsFind::GetCaseSensitive(PRBool *aCaseSensitive)
00582 {
00583   if (!aCaseSensitive)
00584     return NS_ERROR_NULL_POINTER;
00585 
00586   *aCaseSensitive = mCaseSensitive;
00587   return NS_OK;
00588 }
00589 NS_IMETHODIMP
00590 nsFind::SetCaseSensitive(PRBool aCaseSensitive)
00591 {
00592   mCaseSensitive = aCaseSensitive;
00593   return NS_OK;
00594 }
00595 
00596 NS_IMETHODIMP
00597 nsFind::GetWordBreaker(nsIWordBreaker** aWordBreaker)
00598 {
00599   *aWordBreaker = mWordBreaker;
00600   NS_IF_ADDREF(*aWordBreaker);
00601   return NS_OK;
00602 }
00603 
00604 NS_IMETHODIMP
00605 nsFind::SetWordBreaker(nsIWordBreaker* aWordBreaker)
00606 {
00607   mWordBreaker = aWordBreaker;
00608   return NS_OK;
00609 }
00610 
00611 //
00612 // Here begins the find code.
00613 // A ten-thousand-foot view of how it works:
00614 // Find needs to be able to compare across inline (but not block) nodes,
00615 // e.g. find for "abc" should match a<b>b</b>c.
00616 // So after we've searched a node, we're not done with it;
00617 // in the case of a partial match we may need to reset the
00618 // iterator to go back to a previously visited node,
00619 // so we always save the "match anchor" node and offset.
00620 //
00621 // Text nodes store their text in an nsTextFragment, which is 
00622 // effectively a union of a one-byte string or a two-byte string.
00623 // Single and double strings are intermixed in the dom.
00624 // We don't have string classes which can deal with intermixed strings,
00625 // so all the handling is done explicitly here.
00626 //
00627 
00628 nsresult
00629 nsFind::NextNode(nsIDOMRange* aSearchRange,
00630                  nsIDOMRange* aStartPoint, nsIDOMRange* aEndPoint,
00631                  PRBool aContinueOk)
00632 {
00633   nsresult rv;
00634 
00635   nsIContent *content = nsnull;
00636   nsCOMPtr<nsITextContent> tc;
00637 
00638   if (!mIterator || aContinueOk)
00639   {
00640       // If we are continuing, that means we have a match in progress.
00641       // In that case, we want to continue from the end point
00642       // (where we are now) to the beginning/end of the search range.
00643     nsCOMPtr<nsIDOMRange> newRange (do_CreateInstance(kRangeCID));
00644     if (aContinueOk)
00645     {
00646 #ifdef DEBUG_FIND
00647       printf("Match in progress: continuing past endpoint\n");
00648 #endif
00649       nsCOMPtr<nsIDOMNode> startNode;
00650       nsCOMPtr<nsIDOMNode> endNode;
00651       PRInt32 startOffset, endOffset;
00652       if (mFindBackward) {
00653         aSearchRange->GetStartContainer(getter_AddRefs(startNode));
00654         aSearchRange->GetStartOffset(&startOffset);
00655         aEndPoint->GetStartContainer(getter_AddRefs(endNode));
00656         aEndPoint->GetStartOffset(&endOffset);
00657       } else {     // forward
00658         aEndPoint->GetEndContainer(getter_AddRefs(startNode));
00659         aEndPoint->GetEndOffset(&startOffset);
00660         aSearchRange->GetEndContainer(getter_AddRefs(endNode));
00661         aSearchRange->GetEndOffset(&endOffset);
00662       }
00663       newRange->SetStart(startNode, startOffset);
00664       newRange->SetEnd(endNode, endOffset);
00665     }
00666     else  // Normal, not continuing
00667     {
00668       nsCOMPtr<nsIDOMNode> startNode;
00669       nsCOMPtr<nsIDOMNode> endNode;
00670       PRInt32 startOffset, endOffset;
00671       if (mFindBackward) {
00672         aSearchRange->GetStartContainer(getter_AddRefs(startNode));
00673         aSearchRange->GetStartOffset(&startOffset);
00674         aStartPoint->GetEndContainer(getter_AddRefs(endNode));
00675         aStartPoint->GetEndOffset(&endOffset);
00676         // XXX Needs work:
00677         // Problem with this approach: if there is a match which starts
00678         // just before the current selection and continues into the selection,
00679         // we will miss it, because our search algorithm only starts
00680         // searching from the end of the word, so we would have to
00681         // search the current selection but discount any matches
00682         // that fall entirely inside it.
00683       } else {     // forward
00684         aStartPoint->GetStartContainer(getter_AddRefs(startNode));
00685         aStartPoint->GetStartOffset(&startOffset);
00686         aEndPoint->GetEndContainer(getter_AddRefs(endNode));
00687         aEndPoint->GetEndOffset(&endOffset);
00688       }
00689       newRange->SetStart(startNode, startOffset);
00690       newRange->SetEnd(endNode, endOffset);
00691     }
00692 
00693     rv = InitIterator(newRange);
00694     NS_ENSURE_SUCCESS(rv, rv);
00695     if (!aStartPoint)
00696       aStartPoint = aSearchRange;
00697 
00698     content = mIterator->GetCurrentNode();
00699 #ifdef DEBUG_FIND
00700     nsCOMPtr<nsIDOMNode> dnode (do_QueryInterface(content));
00701     printf(":::::: Got the first node "); DumpNode(dnode);
00702 #endif
00703     tc = do_QueryInterface(content);
00704     if (tc && !SkipNode(content))
00705     {
00706       mIterNode = do_QueryInterface(content);
00707       // Also set mIterOffset if appropriate:
00708       nsCOMPtr<nsIDOMNode> node;
00709       if (mFindBackward) {
00710         aStartPoint->GetEndContainer(getter_AddRefs(node));
00711         if (mIterNode.get() == node.get())
00712           aStartPoint->GetEndOffset(&mIterOffset);
00713         else
00714           mIterOffset = -1;   // sign to start from end
00715       }
00716       else
00717       {
00718         aStartPoint->GetStartContainer(getter_AddRefs(node));
00719         if (mIterNode.get() == node.get())
00720           aStartPoint->GetStartOffset(&mIterOffset);
00721         else
00722           mIterOffset = 0;
00723       }
00724 #ifdef DEBUG_FIND
00725       printf("Setting initial offset to %d\n", mIterOffset);
00726 #endif
00727       return NS_OK;
00728     }
00729   }
00730 
00731   while (1)
00732   {
00733     if (mFindBackward)
00734       mIterator->Prev();
00735     else
00736       mIterator->Next();
00737 
00738     content = mIterator->GetCurrentNode();
00739     if (!content)
00740       break;
00741 
00742 #ifdef DEBUG_FIND
00743     nsCOMPtr<nsIDOMNode> dnode (do_QueryInterface(content));
00744     printf(":::::: Got another node "); DumpNode(dnode);
00745 #endif
00746 
00747     // If we ever cross a block node, we might want to reset
00748     // the match anchor:
00749     // we don't match patterns extending across block boundaries.
00750     // But we can't depend on this test here now, because the iterator
00751     // doesn't give us the parent going in and going out, and we
00752     // need it both times to depend on this.
00753     //if (IsBlockNode(content))
00754 
00755     // Now see if we need to skip this node --
00756     // e.g. is it part of a script or other invisible node?
00757     // Note that we don't ask for CSS information;
00758     // a node can be invisible due to CSS, and we'd still find it.
00759     if (SkipNode(content))
00760       continue;
00761 
00762     tc = do_QueryInterface(content);
00763     if (tc)
00764       break;
00765 #ifdef DEBUG_FIND
00766     dnode = do_QueryInterface(content);
00767     printf("Not a text node: "); DumpNode(dnode);
00768 #endif
00769   }
00770 
00771   if (content)
00772     mIterNode = do_QueryInterface(content);
00773   else
00774     mIterNode = nsnull;
00775   mIterOffset = -1;
00776 
00777 #ifdef DEBUG_FIND
00778   printf("Iterator gave: "); DumpNode(mIterNode);
00779 #endif
00780   return NS_OK;
00781 }
00782 
00783 PRBool nsFind::IsBlockNode(nsIContent* aContent)
00784 {
00785   if (!aContent->IsContentOfType(nsIContent::eHTML)) {
00786     return PR_FALSE;
00787   }
00788 
00789   nsIAtom *atom = aContent->Tag();
00790 
00791   if (atom == sImgAtom ||
00792       atom == sHRAtom ||
00793       atom == sThAtom ||
00794       atom == sTdAtom)
00795     return PR_TRUE;
00796 
00797   if (!mParserService) {
00798     mParserService = do_GetService(NS_PARSERSERVICE_CONTRACTID);
00799     if (!mParserService)
00800       return PR_FALSE;
00801   }
00802 
00803   PRBool isBlock = PR_FALSE;
00804   mParserService->IsBlock(mParserService->HTMLAtomTagToId(atom), isBlock);
00805   return isBlock;
00806 }
00807 
00808 PRBool nsFind::IsTextNode(nsIDOMNode* aNode)
00809 {
00810   // Can't just QI for nsITextContent, because nsCommentNode
00811   // also implements that interface.
00812   nsCOMPtr<nsIContent> content (do_QueryInterface(aNode));
00813 
00814   return content && content->IsContentOfType(nsIContent::eTEXT);
00815 }
00816 
00817 PRBool nsFind::IsVisibleNode(nsIDOMNode *aDOMNode)
00818 {
00819   nsCOMPtr<nsIContent> content(do_QueryInterface(aDOMNode));
00820   if (!content)
00821     return PR_FALSE;
00822 
00823   nsCOMPtr<nsIDocument> doc = content->GetDocument();
00824   if (!doc)
00825     return PR_FALSE;
00826 
00827   nsIPresShell *presShell = doc->GetShellAt(0);
00828   if (!presShell)
00829     return PR_FALSE;
00830 
00831   nsIFrame *frame = nsnull;
00832   presShell->GetPrimaryFrameFor(content, &frame);
00833   if (!frame) {
00834     // No frame! Not visible then.
00835     return PR_FALSE;
00836   }
00837 
00838   return frame->GetStyleVisibility()->IsVisible();
00839 }
00840 
00841 PRBool nsFind::SkipNode(nsIContent* aContent)
00842 {
00843   nsIAtom *atom;
00844 
00845 #ifdef HAVE_BIDI_ITERATOR
00846   atom = aContent->Tag();
00847 
00848   // We may not need to skip comment nodes,
00849   // now that IsTextNode distinguishes them from real text nodes.
00850   return (aContent->IsContentOfType(nsIContent::eCOMMENT) ||
00851           (aContent->IsContentOfType(nsIContent::eHTML) &&
00852            (atom == sScriptAtom ||
00853             atom == sNoframesAtom ||
00854             atom == sSelectAtom)));
00855 
00856 #else /* HAVE_BIDI_ITERATOR */
00857   // Temporary: eventually we will have an iterator to do this,
00858   // but for now, we have to climb up the tree for each node
00859   // and see whether any parent is a skipped node,
00860   // and take the performance hit.
00861 
00862   nsIContent *content = aContent;
00863   while (content)
00864   {
00865     atom = content->Tag();
00866 
00867     if (aContent->IsContentOfType(nsIContent::eCOMMENT) ||
00868         (content->IsContentOfType(nsIContent::eHTML) &&
00869          (atom == sScriptAtom ||
00870           atom == sNoframesAtom ||
00871           atom == sSelectAtom)))
00872     {
00873 #ifdef DEBUG_FIND
00874       printf("Skipping node: ");
00875       nsCOMPtr<nsIDOMNode> node (do_QueryInterface(content));
00876       DumpNode(node);
00877 #endif
00878 
00879       return PR_TRUE;
00880     }
00881 
00882     // Only climb to the nearest block node
00883     if (IsBlockNode(content))
00884       return PR_FALSE;
00885 
00886     content = content->GetParent();
00887   }
00888 
00889   return PR_FALSE;
00890 #endif /* HAVE_BIDI_ITERATOR */
00891 }
00892 
00893 nsresult nsFind::GetBlockParent(nsIDOMNode* aNode, nsIDOMNode** aParent)
00894 {
00895   while (aNode)
00896   {
00897     nsCOMPtr<nsIDOMNode> parent;
00898     nsresult rv = aNode->GetParentNode(getter_AddRefs(parent));
00899     NS_ENSURE_SUCCESS(rv, rv);
00900     nsCOMPtr<nsIContent> content (do_QueryInterface(parent));
00901     if (content && IsBlockNode(content))
00902     {
00903       *aParent = parent;
00904       NS_ADDREF(*aParent);
00905       return NS_OK;
00906     }
00907     aNode = parent;
00908   }
00909   return NS_ERROR_FAILURE;
00910 }
00911 
00912 // Call ResetAll before returning,
00913 // to remove all references to external objects.
00914 void nsFind::ResetAll()
00915 {
00916   mIterator = nsnull;
00917   mLastBlockParent = nsnull;
00918 }
00919 
00920 #define NBSP_CHARCODE (CHAR_TO_UNICHAR(160))
00921 #define IsSpace(c) (nsCRT::IsAsciiSpace(c) || (c) == NBSP_CHARCODE)
00922 #define OVERFLOW_PINDEX (mFindBackward ? pindex < 0 : pindex > patLen)
00923 #define DONE_WITH_PINDEX (mFindBackward ? pindex <= 0 : pindex >= patLen)
00924 #define ALMOST_DONE_WITH_PINDEX (mFindBackward ? pindex <= 0 : pindex >= patLen-1)
00925 
00926 //
00927 // Find:
00928 // Take nodes out of the tree with NextNode,
00929 // until null (NextNode will return 0 at the end of our range).
00930 //
00931 NS_IMETHODIMP
00932 nsFind::Find(const PRUnichar *aPatText, nsIDOMRange* aSearchRange,
00933              nsIDOMRange* aStartPoint, nsIDOMRange* aEndPoint,
00934              nsIDOMRange** aRangeRet)
00935 {
00936 #ifdef DEBUG_FIND
00937   printf("============== nsFind::Find('%s'%s, %p, %p, %p)\n",
00938          NS_LossyConvertUCS2toASCII(aPatText).get(),
00939          mFindBackward ? " (backward)" : " (forward)",
00940          (void*)aSearchRange, (void*)aStartPoint, (void*)aEndPoint);
00941 #endif
00942 
00943   NS_ENSURE_ARG_POINTER(aRangeRet);
00944   *aRangeRet = 0;
00945 
00946   if (!aPatText)
00947     return NS_ERROR_NULL_POINTER;
00948 
00949   ResetAll();
00950 
00951   nsAutoString patAutoStr(aPatText);
00952   if (!mCaseSensitive)
00953     ToLowerCase(patAutoStr);
00954   const PRUnichar* patStr = patAutoStr.get();
00955   PRInt32 patLen = patAutoStr.Length() - 1;
00956 
00957   // current offset into the pattern -- reset to beginning/end:
00958   PRInt32 pindex = (mFindBackward ? patLen : 0);
00959 
00960   // Current offset into the fragment
00961   PRInt32 findex = 0;
00962 
00963   // Direction to move pindex and ptr*
00964   int incr = (mFindBackward ? -1 : 1);
00965 
00966   nsCOMPtr<nsITextContent> tc;
00967   const nsTextFragment *frag = nsnull;
00968   PRInt32 fragLen = 0;
00969 
00970   // Pointers into the current fragment:
00971   const PRUnichar *t2b = nsnull;
00972   const char      *t1b = nsnull;
00973 
00974   // Keep track of when we're in whitespace:
00975   // (only matters when we're matching)
00976   PRBool inWhitespace = PR_FALSE;
00977 
00978   // Have we extended a search past the endpoint?
00979   PRBool continuing = PR_FALSE;
00980 
00981   // Place to save the range start point in case we find a match:
00982   nsCOMPtr<nsIDOMNode> matchAnchorNode;
00983   PRInt32 matchAnchorOffset = 0;
00984 
00985   // Get the end point, so we know when to end searches:
00986   nsCOMPtr<nsIDOMNode> endNode;
00987   PRInt32 endOffset;
00988   aEndPoint->GetEndContainer(getter_AddRefs(endNode));
00989   aEndPoint->GetEndOffset(&endOffset);
00990 
00991   PRUnichar prevChar = 0;
00992   while (1)
00993   {
00994 #ifdef DEBUG_FIND
00995     printf("Loop ...\n");
00996 #endif
00997 
00998     // If this is our first time on a new node, reset the pointers:
00999     if (!frag)
01000     {
01001 
01002       tc = nsnull;
01003       NextNode(aSearchRange, aStartPoint, aEndPoint, PR_FALSE);
01004       if (!mIterNode)    // Out of nodes
01005       {
01006         // Are we in the middle of a match?
01007         // If so, try again with continuation.
01008         if (matchAnchorNode && !continuing)
01009           NextNode(aSearchRange, aStartPoint, aEndPoint, PR_TRUE);
01010 
01011         // Reset the iterator, so this nsFind will be usable if
01012         // the user wants to search again (from beginning/end).
01013         ResetAll();
01014         return NS_OK;
01015       }
01016 
01017       // We have a new text content.  If its block parent is different
01018       // from the block parent of the last text content, then we
01019       // need to clear the match since we don't want to find
01020       // across block boundaries.
01021       nsCOMPtr<nsIDOMNode> blockParent;
01022       GetBlockParent(mIterNode, getter_AddRefs(blockParent));
01023 #ifdef DEBUG_FIND
01024       printf("New node: old blockparent = %p, new = %p\n",
01025              (void*)mLastBlockParent.get(), (void*)blockParent.get());
01026 #endif
01027       if (blockParent != mLastBlockParent)
01028       {
01029 #ifdef DEBUG_FIND
01030         printf("Different block parent!\n");
01031 #endif
01032         mLastBlockParent = blockParent;
01033         // End any pending match:
01034         matchAnchorNode = nsnull;
01035         matchAnchorOffset = 0;
01036         pindex = (mFindBackward ? patLen : 0);
01037       }
01038  
01039       // Get the text content:
01040       tc = do_QueryInterface(mIterNode);
01041       if (!tc)         // Out of nodes
01042       {
01043         mIterator = nsnull;
01044         mLastBlockParent = 0;
01045         ResetAll();
01046         return NS_OK;
01047       }
01048 
01049       frag = tc->Text();
01050 
01051       fragLen = frag->GetLength();
01052 
01053       // Set our starting point in this node.
01054       // If we're going back to the anchor node, which means that we
01055       // just ended a partial match, use the saved offset:
01056       if (mIterNode == matchAnchorNode)
01057         findex = matchAnchorOffset + (mFindBackward ? 1 : 0);
01058 
01059       // mIterOffset, if set, is the range's idea of an offset,
01060       // and points between characters.  But when translated
01061       // to a string index, it points to a character.  If we're
01062       // going backward, this is one character too late and
01063       // we'll match part of our previous pattern.
01064       else if (mIterOffset >= 0)
01065         findex = mIterOffset - (mFindBackward ? 1 : 0);
01066 
01067       // Otherwise, just start at the appropriate end of the fragment:
01068       else if (mFindBackward)
01069         findex = fragLen - 1;
01070       else
01071         findex = 0;
01072 
01073       // Offset can only apply to the first node:
01074       mIterOffset = -1;
01075 
01076       // If this is outside the bounds of the string, then skip this node:
01077       if (findex < 0 || findex > fragLen-1)
01078       {
01079 #ifdef DEBUG_FIND
01080         printf("At the end of a text node -- skipping to the next\n");
01081 #endif
01082         frag = 0;
01083         continue;
01084       }
01085 
01086 #ifdef DEBUG_FIND
01087       printf("Starting from offset %d\n", findex);
01088 #endif
01089       if (frag->Is2b())
01090       {
01091         t2b = frag->Get2b();
01092         t1b = nsnull;
01093 #ifdef DEBUG_FIND
01094         nsAutoString str2(t2b, fragLen);
01095         printf("2 byte, '%s'\n", NS_LossyConvertUCS2toASCII(str2).get());
01096 #endif
01097       }
01098       else
01099       {
01100         t1b = frag->Get1b();
01101         t2b = nsnull;
01102 #ifdef DEBUG_FIND
01103         nsCAutoString str1(t1b, fragLen);
01104         printf("1 byte, '%s'\n", str1.get());
01105 #endif
01106       }
01107     }
01108     else // still on the old node
01109     {
01110       // Still on the old node.  Advance the pointers,
01111       // then see if we need to pull a new node.
01112       findex += incr;
01113 #ifdef DEBUG_FIND
01114       printf("Same node -- (%d, %d)\n", pindex, findex);
01115 #endif
01116       if (mFindBackward ? (findex < 0) : (findex >= fragLen))
01117       {
01118 #ifdef DEBUG_FIND
01119         printf("Will need to pull a new node: mAO = %d, frag len=%d\n",
01120                matchAnchorOffset, fragLen);
01121 #endif
01122         // Done with this node.  Pull a new one.
01123         frag = nsnull;
01124         continue;
01125       }
01126     }
01127 
01128     // Have we gone past the endpoint yet?
01129     // If we have, and we're not in the middle of a match, return.
01130     if (mIterNode == endNode && !continuing &&
01131         ((mFindBackward && (findex < endOffset)) ||
01132          (!mFindBackward && (findex > endOffset))))
01133     {
01134       ResetAll();
01135       return NS_OK;
01136     }
01137 
01138     // The two characters we'll be comparing:
01139     PRUnichar c = (t2b ? t2b[findex] : CHAR_TO_UNICHAR(t1b[findex]));
01140     PRUnichar patc = patStr[pindex];
01141 #ifdef DEBUG_FIND
01142     printf("Comparing '%c'=%x to '%c' (%d of %d), findex=%d%s\n",
01143            (char)c, (int)c, patc, pindex, patLen, findex,
01144            inWhitespace ? " (inWhitespace)" : "");
01145 #endif
01146 
01147     // Do we need to go back to non-whitespace mode?
01148     // If inWhitespace, then this space in the pat str
01149     // has already matched at least one space in the document.
01150     if (inWhitespace && !IsSpace(c))
01151     {
01152       inWhitespace = PR_FALSE;
01153       pindex += incr;
01154 #ifdef DEBUG
01155       // This shouldn't happen -- if we were still matching, and we
01156       // were at the end of the pat string, then we should have
01157       // caught it in the last iteration and returned success.
01158       if (OVERFLOW_PINDEX)
01159         NS_ASSERTION(PR_FALSE, "Missed a whitespace match\n");
01160 #endif
01161       patc = patStr[pindex];
01162     }
01163     if (!inWhitespace && IsSpace(patc))
01164       inWhitespace = PR_TRUE;
01165 
01166     // convert to lower case if necessary
01167     else if (!inWhitespace && !mCaseSensitive && IsUpperCase(c))
01168       c = ToLowerCase(c);
01169 
01170     // a '\n' between CJ characters is ignored
01171     if (pindex != (mFindBackward ? patLen : 0) && c != patc && !inWhitespace) {
01172       if (c == '\n' && t2b && IS_CJ_CHAR(prevChar)) {
01173         PRInt32 nindex = findex + incr;
01174         if (mFindBackward ? (nindex >= 0) : (nindex < fragLen)) {
01175           if (IS_CJ_CHAR(t2b[nindex]))
01176             continue;
01177         }
01178       }
01179     }
01180 
01181     // Compare
01182     if ((c == patc) || (inWhitespace && IsSpace(c)))
01183     {
01184       prevChar = c;
01185 #ifdef DEBUG_FIND
01186       if (inWhitespace)
01187         printf("YES (whitespace)(%d of %d)\n", pindex, patLen);
01188       else
01189         printf("YES! '%c' == '%c' (%d of %d)\n", c, patc, pindex, patLen);
01190 #endif
01191 
01192       // Save the range anchors if we haven't already:
01193       if (!matchAnchorNode) {
01194         matchAnchorNode = mIterNode;
01195         matchAnchorOffset = findex;
01196       }
01197 
01198       // Are we done?
01199       if (DONE_WITH_PINDEX)
01200         // Matched the whole string!
01201       {
01202 #ifdef DEBUG_FIND
01203         printf("Found a match!\n");
01204 #endif
01205 
01206         // Make the range:
01207         nsCOMPtr<nsIDOMNode> startParent;
01208         nsCOMPtr<nsIDOMNode> endParent;
01209         nsCOMPtr<nsIDOMRange> range (do_CreateInstance(kRangeCID));
01210         if (range)
01211         {
01212           PRInt32 matchStartOffset, matchEndOffset;
01213           // convert char index to range point:
01214           PRInt32 mao = matchAnchorOffset + (mFindBackward ? 1 : 0);
01215           if (mFindBackward)
01216           {
01217             startParent = do_QueryInterface(tc);
01218             endParent = matchAnchorNode;
01219             matchStartOffset = findex;
01220             matchEndOffset = mao;
01221           }
01222           else
01223           {
01224             startParent = matchAnchorNode;
01225             endParent = do_QueryInterface(tc);
01226             matchStartOffset = mao;
01227             matchEndOffset = findex+1;
01228           }
01229           if (startParent && endParent && 
01230               IsVisibleNode(startParent) && IsVisibleNode(endParent))
01231           {
01232             range->SetStart(startParent, matchStartOffset);
01233             range->SetEnd(endParent, matchEndOffset);
01234             *aRangeRet = range.get();
01235             NS_ADDREF(*aRangeRet);
01236           }
01237           else {
01238             startParent = nsnull; // This match is no good -- invisible or bad range
01239           }
01240         }
01241 
01242         if (startParent) {
01243           // If startParent == nsnull, we didn't successfully make range
01244           // or, we didn't make a range because the start or end node were invisible
01245           // Reset the offset to the other end of the found string:
01246           mIterOffset = findex + (mFindBackward ? 1 : 0);
01247   #ifdef DEBUG_FIND
01248           printf("mIterOffset = %d, mIterNode = ", mIterOffset);
01249           DumpNode(mIterNode);
01250   #endif
01251 
01252           ResetAll();
01253           return NS_OK;
01254         }
01255         matchAnchorNode = nsnull;  // This match is no good, continue on in document
01256       }
01257 
01258       if (matchAnchorNode) {
01259         // Not done, but still matching.
01260         // Advance and loop around for the next characters.
01261         // But don't advance from a space to a non-space:
01262         if (!inWhitespace || DONE_WITH_PINDEX || IsSpace(patStr[pindex+incr]))
01263         {
01264           pindex += incr;
01265           inWhitespace = PR_FALSE;
01266 #ifdef DEBUG_FIND
01267           printf("Advancing pindex to %d\n", pindex);
01268 #endif
01269         }
01270       
01271         continue;
01272       }
01273     }
01274 
01275 #ifdef DEBUG_FIND
01276     printf("NOT: %c == %c\n", c, patc);
01277 #endif
01278     // If we were continuing, then this ends our search.
01279     if (continuing) {
01280       ResetAll();
01281       return NS_OK;
01282     }
01283 
01284     // If we didn't match, go back to the beginning of patStr,
01285     // and set findex back to the next char after
01286     // we started the current match.
01287     if (matchAnchorNode)    // we're ending a partial match
01288     {
01289       findex = matchAnchorOffset;
01290       mIterOffset = matchAnchorOffset;
01291           // +incr will be added to findex when we continue
01292 
01293       // Are we going back to a previous node?
01294       if (matchAnchorNode != mIterNode)
01295       {
01296         nsCOMPtr<nsIContent> content (do_QueryInterface(matchAnchorNode));
01297         nsresult rv = NS_ERROR_UNEXPECTED;
01298         if (content)
01299           rv = mIterator->PositionAt(content);
01300         frag = 0;
01301         NS_ASSERTION(NS_SUCCEEDED(rv), "Text content wasn't nsIContent!");
01302 #ifdef DEBUG_FIND
01303         printf("Repositioned anchor node\n");
01304 #endif
01305       }
01306 #ifdef DEBUG_FIND
01307       printf("Ending a partial match; findex -> %d, mIterOffset -> %d\n",
01308              findex, mIterOffset);
01309 #endif
01310     }
01311     matchAnchorNode = nsnull;
01312     matchAnchorOffset = 0;
01313     inWhitespace = PR_FALSE;
01314     pindex = (mFindBackward ? patLen : 0);
01315 #ifdef DEBUG_FIND
01316     printf("Setting findex back to %d, pindex to %d\n", findex, pindex);
01317            
01318 #endif
01319   } // end while loop
01320 
01321   // Out of nodes, and didn't match.
01322   ResetAll();
01323   return NS_OK;
01324 }
01325 
01326