LCOV - code coverage report
Current view: directory - embedding/components/find/src - nsFind.cpp (source / functions) Found Hit Coverage
Test: app.info Lines: 474 0 0.0 %
Date: 2012-06-02 Functions: 42 0 0.0 %

       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 the guts of the find algorithm.
      16                 :  *
      17                 :  * The Initial Developer of the Original Code is Akkana Peck.
      18                 :  *
      19                 :  * Portions created by the Initial Developer are Copyright (C) 2002
      20                 :  * the Initial Developer. All Rights Reserved.
      21                 :  *
      22                 :  * Contributor(s):
      23                 :  *   Akkana Peck <akkana@netscape.com>
      24                 :  *   Roger B. Sidje <rbs@maths.uq.edu.au> (added find in <textarea> & text <input>)
      25                 :  *
      26                 :  * Alternatively, the contents of this file may be used under the terms of
      27                 :  * either the GNU General Public License Version 2 or later (the "GPL"), or
      28                 :  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
      29                 :  * in which case the provisions of the GPL or the LGPL are applicable instead
      30                 :  * of those above. If you wish to allow use of your version of this file only
      31                 :  * under the terms of either the GPL or the LGPL, and not to allow others to
      32                 :  * use your version of this file under the terms of the MPL, indicate your
      33                 :  * decision by deleting the provisions above and replace them with the notice
      34                 :  * and other provisions required by the GPL or the LGPL. If you do not delete
      35                 :  * the provisions above, a recipient may use your version of this file under
      36                 :  * the terms of any one of the MPL, the GPL or the LGPL.
      37                 :  *
      38                 :  * ***** END LICENSE BLOCK ***** */
      39                 : 
      40                 : //#define DEBUG_FIND 1
      41                 : 
      42                 : #include "nsFind.h"
      43                 : #include "nsContentCID.h"
      44                 : #include "nsIEnumerator.h"
      45                 : #include "nsIDOMNode.h"
      46                 : #include "nsIDOMNodeList.h"
      47                 : #include "nsISelection.h"
      48                 : #include "nsISelectionController.h"
      49                 : #include "nsIFrame.h"
      50                 : #include "nsITextControlFrame.h"
      51                 : #include "nsIFormControl.h"
      52                 : #include "nsIEditor.h"
      53                 : #include "nsIPlaintextEditor.h"
      54                 : #include "nsIDocument.h"
      55                 : #include "nsTextFragment.h"
      56                 : #include "nsString.h"
      57                 : #include "nsIAtom.h"
      58                 : #include "nsParserCIID.h"
      59                 : #include "nsServiceManagerUtils.h"
      60                 : #include "nsUnicharUtils.h"
      61                 : #include "nsIDOMElement.h"
      62                 : #include "nsIWordBreaker.h"
      63                 : #include "nsCRT.h"
      64                 : #include "nsRange.h"
      65                 : 
      66                 : // Yikes!  Casting a char to unichar can fill with ones!
      67                 : #define CHAR_TO_UNICHAR(c) ((PRUnichar)(const unsigned char)c)
      68                 : 
      69                 : static NS_DEFINE_CID(kCContentIteratorCID, NS_CONTENTITERATOR_CID);
      70                 : static NS_DEFINE_CID(kCPreContentIteratorCID, NS_PRECONTENTITERATOR_CID);
      71                 : 
      72                 : #define CH_SHY ((PRUnichar) 0xAD)
      73                 : 
      74                 : // nsFind::Find casts CH_SHY to char before calling StripChars
      75                 : // This works correctly if and only if CH_SHY <= 255
      76                 : PR_STATIC_ASSERT(CH_SHY <= 255);
      77                 : 
      78                 : // -----------------------------------------------------------------------
      79                 : // nsFindContentIterator is a special iterator that also goes through
      80                 : // any existing <textarea>'s or text <input>'s editor to lookup the
      81                 : // anonymous DOM content there.
      82                 : //
      83                 : // Details:
      84                 : // 1) We use two iterators: The "outer-iterator" goes through the
      85                 : // normal DOM. The "inner-iterator" goes through the anonymous DOM
      86                 : // inside the editor.
      87                 : //
      88                 : // 2) [MaybeSetupInnerIterator] As soon as the outer-iterator's current
      89                 : // node is changed, a check is made to see if the node is a <textarea> or
      90                 : // a text <input> node. If so, an inner-iterator is created to lookup the
      91                 : // anynomous contents of the editor underneath the text control.
      92                 : //
      93                 : // 3) When the inner-iterator is created, we position the outer-iterator
      94                 : // 'after' (or 'before' in backward search) the text control to avoid
      95                 : // revisiting that control.
      96                 : //
      97                 : // 4) As a consequence of searching through text controls, we can be
      98                 : // called via FindNext with the current selection inside a <textarea>
      99                 : // or a text <input>. This means that we can be given an initial search
     100                 : // range that stretches across the anonymous DOM and the normal DOM. To
     101                 : // cater for this situation, we split the anonymous part into the 
     102                 : // inner-iterator and then reposition the outer-iterator outside.
     103                 : //
     104                 : // 5) The implementation assumes that First() and Next() are only called
     105                 : // in find-forward mode, while Last() and Prev() are used in find-backward.
     106                 : 
     107                 : class nsFindContentIterator : public nsIContentIterator
     108                 : {
     109                 : public:
     110               0 :   nsFindContentIterator(bool aFindBackward)
     111                 :     : mStartOffset(0),
     112                 :       mEndOffset(0),
     113               0 :       mFindBackward(aFindBackward)
     114                 :   {
     115               0 :   }
     116                 : 
     117               0 :   virtual ~nsFindContentIterator()
     118               0 :   {
     119               0 :   }
     120                 : 
     121                 :   // nsISupports
     122                 :   NS_DECL_ISUPPORTS
     123                 : 
     124                 :   // nsIContentIterator
     125               0 :   virtual nsresult Init(nsINode* aRoot)
     126                 :   {
     127               0 :     NS_NOTREACHED("internal error");
     128               0 :     return NS_ERROR_NOT_IMPLEMENTED;
     129                 :   }
     130               0 :   virtual nsresult Init(nsIDOMRange* aRange)
     131                 :   {
     132               0 :     NS_NOTREACHED("internal error");
     133               0 :     return NS_ERROR_NOT_IMPLEMENTED;
     134                 :   }
     135                 :   // Not a range because one of the endpoints may be anonymous.
     136                 :   nsresult Init(nsIDOMNode* aStartNode, PRInt32 aStartOffset,
     137                 :                 nsIDOMNode* aEndNode, PRInt32 aEndOffset);
     138                 :   virtual void First();
     139                 :   virtual void Last();
     140                 :   virtual void Next();
     141                 :   virtual void Prev();
     142                 :   virtual nsINode* GetCurrentNode();
     143                 :   virtual bool IsDone();
     144                 :   virtual nsresult PositionAt(nsINode* aCurNode);
     145                 : 
     146                 : private:
     147                 :   nsCOMPtr<nsIContentIterator> mOuterIterator;
     148                 :   nsCOMPtr<nsIContentIterator> mInnerIterator;
     149                 :   // Can't use a range here, since we want to represent part of the
     150                 :   // flattened tree, including native anonymous content.
     151                 :   nsCOMPtr<nsIDOMNode> mStartNode;
     152                 :   PRInt32 mStartOffset;
     153                 :   nsCOMPtr<nsIDOMNode> mEndNode;
     154                 :   PRInt32 mEndOffset;
     155                 :   
     156                 :   nsCOMPtr<nsIContent> mStartOuterContent;
     157                 :   nsCOMPtr<nsIContent> mEndOuterContent;
     158                 :   bool mFindBackward;
     159                 : 
     160                 :   void Reset();
     161                 :   void MaybeSetupInnerIterator();
     162                 :   void SetupInnerIterator(nsIContent* aContent);
     163                 : };
     164                 : 
     165               0 : NS_IMPL_ISUPPORTS1(nsFindContentIterator, nsIContentIterator)
     166                 : 
     167                 : nsresult
     168               0 : nsFindContentIterator::Init(nsIDOMNode* aStartNode, PRInt32 aStartOffset,
     169                 :                             nsIDOMNode* aEndNode, PRInt32 aEndOffset)
     170                 : {
     171               0 :   if (!mOuterIterator) {
     172               0 :     if (mFindBackward) {
     173                 :       // Use post-order in the reverse case, so we get parents
     174                 :       // before children in case we want to prevent descending
     175                 :       // into a node.
     176               0 :       mOuterIterator = do_CreateInstance(kCContentIteratorCID);
     177                 :     }
     178                 :     else {
     179                 :       // Use pre-order in the forward case, so we get parents
     180                 :       // before children in case we want to prevent descending
     181                 :       // into a node.
     182               0 :       mOuterIterator = do_CreateInstance(kCPreContentIteratorCID);
     183                 :     }
     184               0 :     NS_ENSURE_ARG_POINTER(mOuterIterator);
     185                 :   }
     186                 : 
     187                 :   // Set up the search "range" that we will examine
     188               0 :   mStartNode = aStartNode;
     189               0 :   mStartOffset = aStartOffset;
     190               0 :   mEndNode = aEndNode;
     191               0 :   mEndOffset = aEndOffset;
     192                 : 
     193               0 :   return NS_OK;
     194                 : }
     195                 : 
     196                 : void
     197               0 : nsFindContentIterator::First()
     198                 : {
     199               0 :   Reset();
     200               0 : }
     201                 : 
     202                 : void
     203               0 : nsFindContentIterator::Last()
     204                 : {
     205               0 :   Reset();
     206               0 : }
     207                 : 
     208                 : void
     209               0 : nsFindContentIterator::Next()
     210                 : {
     211               0 :   if (mInnerIterator) {
     212               0 :     mInnerIterator->Next();
     213               0 :     if (!mInnerIterator->IsDone())
     214               0 :       return;
     215                 : 
     216                 :     // by construction, mOuterIterator is already on the next node
     217                 :   }
     218                 :   else {
     219               0 :     mOuterIterator->Next();
     220                 :   }
     221               0 :   MaybeSetupInnerIterator();  
     222                 : }
     223                 : 
     224                 : void
     225               0 : nsFindContentIterator::Prev()
     226                 : {
     227               0 :   if (mInnerIterator) {
     228               0 :     mInnerIterator->Prev();
     229               0 :     if (!mInnerIterator->IsDone())
     230               0 :       return;
     231                 : 
     232                 :     // by construction, mOuterIterator is already on the previous node
     233                 :   }
     234                 :   else {
     235               0 :     mOuterIterator->Prev();
     236                 :   }
     237               0 :   MaybeSetupInnerIterator();
     238                 : }
     239                 : 
     240                 : nsINode*
     241               0 : nsFindContentIterator::GetCurrentNode()
     242                 : {
     243               0 :   if (mInnerIterator && !mInnerIterator->IsDone()) {
     244               0 :     return mInnerIterator->GetCurrentNode();
     245                 :   }
     246               0 :   return mOuterIterator->GetCurrentNode();
     247                 : }
     248                 : 
     249                 : bool
     250               0 : nsFindContentIterator::IsDone() {
     251               0 :   if (mInnerIterator && !mInnerIterator->IsDone()) {
     252               0 :     return false;
     253                 :   }
     254               0 :   return mOuterIterator->IsDone();
     255                 : }
     256                 : 
     257                 : nsresult
     258               0 : nsFindContentIterator::PositionAt(nsINode* aCurNode)
     259                 : {
     260               0 :   nsINode* oldNode = mOuterIterator->GetCurrentNode();
     261               0 :   nsresult rv = mOuterIterator->PositionAt(aCurNode);
     262               0 :   if (NS_SUCCEEDED(rv)) {
     263               0 :     MaybeSetupInnerIterator();
     264                 :   }
     265                 :   else {
     266               0 :     mOuterIterator->PositionAt(oldNode);
     267               0 :     if (mInnerIterator)
     268               0 :       rv = mInnerIterator->PositionAt(aCurNode);
     269                 :   }
     270               0 :   return rv;
     271                 : }
     272                 : 
     273                 : void
     274               0 : nsFindContentIterator::Reset()
     275                 : {
     276               0 :   mInnerIterator = nsnull;
     277               0 :   mStartOuterContent = nsnull;
     278               0 :   mEndOuterContent = nsnull;
     279                 : 
     280                 :   // As a consequence of searching through text controls, we may have been
     281                 :   // initialized with a selection inside a <textarea> or a text <input>.
     282                 : 
     283                 :   // see if the start node is an anonymous text node inside a text control
     284               0 :   nsCOMPtr<nsIContent> startContent(do_QueryInterface(mStartNode));
     285               0 :   if (startContent) {
     286               0 :     mStartOuterContent = startContent->FindFirstNonNativeAnonymous();
     287                 :   }
     288                 : 
     289                 :   // see if the end node is an anonymous text node inside a text control
     290               0 :   nsCOMPtr<nsIContent> endContent(do_QueryInterface(mEndNode));
     291               0 :   if (endContent) {
     292               0 :     mEndOuterContent = endContent->FindFirstNonNativeAnonymous();
     293                 :   }
     294                 : 
     295                 :   // Note: OK to just set up the outer iterator here; if our range has a native
     296                 :   // anonymous endpoint we'll end up setting up an inner iterator, and
     297                 :   // reset the outer one in the process.
     298               0 :   nsCOMPtr<nsIDOMRange> range = nsFind::CreateRange();
     299               0 :   range->SetStart(mStartNode, mStartOffset);
     300               0 :   range->SetEnd(mEndNode, mEndOffset);
     301               0 :   mOuterIterator->Init(range);
     302                 : 
     303               0 :   if (!mFindBackward) {
     304               0 :     if (mStartOuterContent != startContent) {
     305                 :       // the start node was an anonymous text node
     306               0 :       SetupInnerIterator(mStartOuterContent);
     307               0 :       if (mInnerIterator)
     308               0 :         mInnerIterator->First();
     309                 :     }
     310               0 :     if (!mOuterIterator->IsDone())
     311               0 :       mOuterIterator->First();
     312                 :   }
     313                 :   else {
     314               0 :     if (mEndOuterContent != endContent) {
     315                 :       // the end node was an anonymous text node
     316               0 :       SetupInnerIterator(mEndOuterContent);
     317               0 :       if (mInnerIterator)
     318               0 :         mInnerIterator->Last();
     319                 :     }
     320               0 :     if (!mOuterIterator->IsDone())
     321               0 :       mOuterIterator->Last();
     322                 :   }
     323                 : 
     324                 :   // if we didn't create an inner-iterator, the boundary node could still be
     325                 :   // a text control, in which case we also need an inner-iterator straightaway
     326               0 :   if (!mInnerIterator) {
     327               0 :     MaybeSetupInnerIterator();
     328                 :   }
     329               0 : }
     330                 : 
     331                 : void
     332               0 : nsFindContentIterator::MaybeSetupInnerIterator()
     333                 : {
     334               0 :   mInnerIterator = nsnull;
     335                 : 
     336                 :   nsCOMPtr<nsIContent> content =
     337               0 :     do_QueryInterface(mOuterIterator->GetCurrentNode());
     338               0 :   if (!content || !content->IsNodeOfType(nsINode::eHTML_FORM_CONTROL))
     339                 :     return;
     340                 : 
     341               0 :   nsCOMPtr<nsIFormControl> formControl(do_QueryInterface(content));
     342               0 :   if (!formControl->IsTextControl(true)) {
     343                 :     return;
     344                 :   }
     345                 : 
     346               0 :   SetupInnerIterator(content);
     347               0 :   if (mInnerIterator) {
     348               0 :     if (!mFindBackward) {
     349               0 :       mInnerIterator->First();
     350                 :       // finish setup: position mOuterIterator on the actual "next"
     351                 :       // node (this completes its re-init, @see SetupInnerIterator)
     352               0 :       if (!mOuterIterator->IsDone())
     353               0 :         mOuterIterator->First();
     354                 :     }
     355                 :     else {
     356               0 :       mInnerIterator->Last();
     357                 :       // finish setup: position mOuterIterator on the actual "previous"
     358                 :       // node (this completes its re-init, @see SetupInnerIterator)
     359               0 :       if (!mOuterIterator->IsDone())
     360               0 :         mOuterIterator->Last();
     361                 :     }
     362                 :   }
     363                 : }
     364                 : 
     365                 : void
     366               0 : nsFindContentIterator::SetupInnerIterator(nsIContent* aContent)
     367                 : {
     368               0 :   if (!aContent) {
     369               0 :     return;
     370                 :   }
     371               0 :   NS_ASSERTION(!aContent->IsRootOfNativeAnonymousSubtree(), "invalid call");
     372                 : 
     373               0 :   nsITextControlFrame* tcFrame = do_QueryFrame(aContent->GetPrimaryFrame());
     374               0 :   if (!tcFrame)
     375               0 :     return;
     376                 : 
     377               0 :   nsCOMPtr<nsIEditor> editor;
     378               0 :   tcFrame->GetEditor(getter_AddRefs(editor));
     379               0 :   if (!editor)
     380                 :     return;
     381                 : 
     382                 :   // don't mess with disabled input fields
     383               0 :   PRUint32 editorFlags = 0;
     384               0 :   editor->GetFlags(&editorFlags);
     385               0 :   if (editorFlags & nsIPlaintextEditor::eEditorDisabledMask)
     386                 :     return;
     387                 : 
     388               0 :   nsCOMPtr<nsIDOMElement> rootElement;
     389               0 :   editor->GetRootElement(getter_AddRefs(rootElement));
     390               0 :   nsCOMPtr<nsIContent> rootContent(do_QueryInterface(rootElement));
     391                 : 
     392               0 :   nsCOMPtr<nsIDOMRange> innerRange = nsFind::CreateRange();
     393               0 :   nsCOMPtr<nsIDOMRange> outerRange = nsFind::CreateRange();
     394               0 :   if (!innerRange || !outerRange) {
     395                 :     return;
     396                 :   }
     397                 :      
     398                 :   // now create the inner-iterator
     399               0 :   mInnerIterator = do_CreateInstance(kCPreContentIteratorCID);
     400                 : 
     401               0 :   if (mInnerIterator) {
     402               0 :     innerRange->SelectNodeContents(rootElement);
     403                 : 
     404                 :     // fix up the inner bounds, we may have to only lookup a portion
     405                 :     // of the text control if the current node is a boundary point
     406               0 :     if (aContent == mStartOuterContent) {
     407               0 :       innerRange->SetStart(mStartNode, mStartOffset);
     408                 :     }
     409               0 :     if (aContent == mEndOuterContent) {
     410               0 :       innerRange->SetEnd(mEndNode, mEndOffset);
     411                 :     }
     412                 :     // Note: we just init here. We do First() or Last() later. 
     413               0 :     mInnerIterator->Init(innerRange);
     414                 : 
     415                 :     // make sure to place the outer-iterator outside
     416                 :     // the text control so that we don't go there again.
     417                 :     nsresult res;
     418               0 :     nsCOMPtr<nsIDOMNode> outerNode(do_QueryInterface(aContent));
     419               0 :     if (!mFindBackward) { // find forward
     420                 :       // cut the outer-iterator after the current node
     421               0 :       res = outerRange->SetEnd(mEndNode, mEndOffset);
     422               0 :       res |= outerRange->SetStartAfter(outerNode);
     423                 :     }
     424                 :     else { // find backward
     425                 :       // cut the outer-iterator before the current node
     426               0 :       res = outerRange->SetStart(mStartNode, mStartOffset);
     427               0 :       res |= outerRange->SetEndBefore(outerNode);
     428                 :     }
     429               0 :     if (NS_FAILED(res)) {
     430                 :       // we are done with the outer-iterator, the 
     431                 :       // inner-iterator will traverse what we want
     432               0 :       outerRange->Collapse(true);
     433                 :     }
     434                 : 
     435                 :     // Note: we just re-init here, using the segment of our search range that
     436                 :     // is yet to be visited. Thus when we later do mOuterIterator->First() [or
     437                 :     // mOuterIterator->Last()], we will effectively be on the next node [or
     438                 :     // the previous node] _with respect to_ the search range.
     439               0 :     mOuterIterator->Init(outerRange);
     440                 :   }
     441                 : }
     442                 : 
     443                 : nsresult
     444               0 : NS_NewFindContentIterator(bool aFindBackward,
     445                 :                           nsIContentIterator** aResult)
     446                 : {
     447               0 :   NS_ENSURE_ARG_POINTER(aResult);
     448               0 :   if (!aResult) {
     449               0 :     return NS_ERROR_NULL_POINTER;
     450                 :   }
     451                 : 
     452               0 :   nsFindContentIterator* it = new nsFindContentIterator(aFindBackward);
     453               0 :   if (!it) {
     454               0 :     return NS_ERROR_OUT_OF_MEMORY;
     455                 :   }
     456               0 :   return it->QueryInterface(NS_GET_IID(nsIContentIterator), (void **)aResult);
     457                 : }
     458                 : // --------------------------------------------------------------------
     459                 : 
     460                 : // Sure would be nice if we could just get these from somewhere else!
     461                 : PRInt32 nsFind::sInstanceCount = 0;
     462                 : nsIAtom* nsFind::sImgAtom = nsnull;
     463                 : nsIAtom* nsFind::sHRAtom = nsnull;
     464                 : nsIAtom* nsFind::sScriptAtom = nsnull;
     465                 : nsIAtom* nsFind::sNoframesAtom = nsnull;
     466                 : nsIAtom* nsFind::sSelectAtom = nsnull;
     467                 : nsIAtom* nsFind::sTextareaAtom = nsnull;
     468                 : nsIAtom* nsFind::sThAtom = nsnull;
     469                 : nsIAtom* nsFind::sTdAtom = nsnull;
     470                 : 
     471               0 : NS_IMPL_ISUPPORTS1(nsFind, nsIFind)
     472                 : 
     473               0 : nsFind::nsFind()
     474                 :   : mFindBackward(false)
     475                 :   , mCaseSensitive(false)
     476               0 :   , mIterOffset(0)
     477                 : {
     478                 :   // Initialize the atoms if they aren't already:
     479               0 :   if (sInstanceCount <= 0)
     480                 :   {
     481               0 :     sImgAtom = NS_NewAtom("img");
     482               0 :     sHRAtom = NS_NewAtom("hr");
     483               0 :     sScriptAtom = NS_NewAtom("script");
     484               0 :     sNoframesAtom = NS_NewAtom("noframes");
     485               0 :     sSelectAtom = NS_NewAtom("select");
     486               0 :     sTextareaAtom = NS_NewAtom("textarea");
     487               0 :     sThAtom = NS_NewAtom("th");
     488               0 :     sTdAtom = NS_NewAtom("td");
     489                 :   }
     490               0 :   ++sInstanceCount;
     491               0 : }
     492                 : 
     493               0 : nsFind::~nsFind()
     494                 : {
     495               0 :   if (sInstanceCount <= 1)
     496                 :   {
     497               0 :     NS_IF_RELEASE(sImgAtom);
     498               0 :     NS_IF_RELEASE(sHRAtom);
     499               0 :     NS_IF_RELEASE(sScriptAtom);
     500               0 :     NS_IF_RELEASE(sNoframesAtom);
     501               0 :     NS_IF_RELEASE(sSelectAtom);
     502               0 :     NS_IF_RELEASE(sTextareaAtom);
     503               0 :     NS_IF_RELEASE(sThAtom);
     504               0 :     NS_IF_RELEASE(sTdAtom);
     505                 :   }
     506               0 :   --sInstanceCount;
     507               0 : }
     508                 : 
     509                 : #ifdef DEBUG_FIND
     510                 : static void DumpNode(nsIDOMNode* aNode)
     511                 : {
     512                 :   if (!aNode)
     513                 :   {
     514                 :     printf(">>>> Node: NULL\n");
     515                 :     return;
     516                 :   }
     517                 :   nsAutoString nodeName;
     518                 :   aNode->GetNodeName(nodeName);
     519                 :   nsCOMPtr<nsIContent> textContent (do_QueryInterface(aNode));
     520                 :   if (textContent && textContent->IsNodeOfType(nsINode::eTEXT))
     521                 :   {
     522                 :     nsAutoString newText;
     523                 :     textContent->AppendTextTo(newText);
     524                 :     printf(">>>> Text node (node name %s): '%s'\n",
     525                 :            NS_LossyConvertUTF16toASCII(nodeName).get(),
     526                 :            NS_LossyConvertUTF16toASCII(newText).get());
     527                 :   }
     528                 :   else
     529                 :     printf(">>>> Node: %s\n", NS_LossyConvertUTF16toASCII(nodeName).get());
     530                 : }
     531                 : #endif
     532                 : 
     533                 : nsresult
     534               0 : nsFind::InitIterator(nsIDOMNode* aStartNode, PRInt32 aStartOffset,
     535                 :                      nsIDOMNode* aEndNode, PRInt32 aEndOffset)
     536                 : {
     537               0 :   if (!mIterator)
     538                 :   {
     539               0 :     mIterator = new nsFindContentIterator(mFindBackward);
     540               0 :     NS_ENSURE_TRUE(mIterator, NS_ERROR_OUT_OF_MEMORY);
     541                 :   }
     542                 : 
     543               0 :   NS_ENSURE_ARG_POINTER(aStartNode);
     544               0 :   NS_ENSURE_ARG_POINTER(aEndNode);
     545                 : 
     546                 : #ifdef DEBUG_FIND
     547                 :   printf("InitIterator search range:\n"); 
     548                 :   printf(" -- start %d, ", aStartOffset); DumpNode(aStartNode);
     549                 :   printf(" -- end %d, ", aEndOffset); DumpNode(aEndNode);
     550                 : #endif
     551                 : 
     552                 :   nsresult rv =
     553               0 :     mIterator->Init(aStartNode, aStartOffset, aEndNode, aEndOffset);
     554               0 :   NS_ENSURE_SUCCESS(rv, rv);
     555               0 :   if (mFindBackward) {
     556               0 :     mIterator->Last();
     557                 :   }
     558                 :   else {
     559               0 :     mIterator->First();
     560                 :   }
     561               0 :   return NS_OK;
     562                 : }
     563                 : 
     564                 : /* attribute boolean findBackward; */
     565                 : NS_IMETHODIMP
     566               0 : nsFind::GetFindBackwards(bool *aFindBackward)
     567                 : {
     568               0 :   if (!aFindBackward)
     569               0 :     return NS_ERROR_NULL_POINTER;
     570                 : 
     571               0 :   *aFindBackward = mFindBackward;
     572               0 :   return NS_OK;
     573                 : }
     574                 : NS_IMETHODIMP
     575               0 : nsFind::SetFindBackwards(bool aFindBackward)
     576                 : {
     577               0 :   mFindBackward = aFindBackward;
     578               0 :   return NS_OK;
     579                 : }
     580                 : 
     581                 : /* attribute boolean caseSensitive; */
     582                 : NS_IMETHODIMP
     583               0 : nsFind::GetCaseSensitive(bool *aCaseSensitive)
     584                 : {
     585               0 :   if (!aCaseSensitive)
     586               0 :     return NS_ERROR_NULL_POINTER;
     587                 : 
     588               0 :   *aCaseSensitive = mCaseSensitive;
     589               0 :   return NS_OK;
     590                 : }
     591                 : NS_IMETHODIMP
     592               0 : nsFind::SetCaseSensitive(bool aCaseSensitive)
     593                 : {
     594               0 :   mCaseSensitive = aCaseSensitive;
     595               0 :   return NS_OK;
     596                 : }
     597                 : 
     598                 : NS_IMETHODIMP
     599               0 : nsFind::GetWordBreaker(nsIWordBreaker** aWordBreaker)
     600                 : {
     601               0 :   *aWordBreaker = mWordBreaker;
     602               0 :   NS_IF_ADDREF(*aWordBreaker);
     603               0 :   return NS_OK;
     604                 : }
     605                 : 
     606                 : NS_IMETHODIMP
     607               0 : nsFind::SetWordBreaker(nsIWordBreaker* aWordBreaker)
     608                 : {
     609               0 :   mWordBreaker = aWordBreaker;
     610               0 :   return NS_OK;
     611                 : }
     612                 : 
     613                 : //
     614                 : // Here begins the find code.
     615                 : // A ten-thousand-foot view of how it works:
     616                 : // Find needs to be able to compare across inline (but not block) nodes,
     617                 : // e.g. find for "abc" should match a<b>b</b>c.
     618                 : // So after we've searched a node, we're not done with it;
     619                 : // in the case of a partial match we may need to reset the
     620                 : // iterator to go back to a previously visited node,
     621                 : // so we always save the "match anchor" node and offset.
     622                 : //
     623                 : // Text nodes store their text in an nsTextFragment, which is 
     624                 : // effectively a union of a one-byte string or a two-byte string.
     625                 : // Single and double strings are intermixed in the dom.
     626                 : // We don't have string classes which can deal with intermixed strings,
     627                 : // so all the handling is done explicitly here.
     628                 : //
     629                 : 
     630                 : nsresult
     631               0 : nsFind::NextNode(nsIDOMRange* aSearchRange,
     632                 :                  nsIDOMRange* aStartPoint, nsIDOMRange* aEndPoint,
     633                 :                  bool aContinueOk)
     634                 : {
     635                 :   nsresult rv;
     636                 : 
     637               0 :   nsCOMPtr<nsIContent> content;
     638                 : 
     639               0 :   if (!mIterator || aContinueOk)
     640                 :   {
     641                 :     // If we are continuing, that means we have a match in progress.
     642                 :     // In that case, we want to continue from the end point
     643                 :     // (where we are now) to the beginning/end of the search range.
     644               0 :     nsCOMPtr<nsIDOMNode> startNode;
     645               0 :     nsCOMPtr<nsIDOMNode> endNode;
     646                 :     PRInt32 startOffset, endOffset;
     647               0 :     if (aContinueOk)
     648                 :     {
     649                 : #ifdef DEBUG_FIND
     650                 :       printf("Match in progress: continuing past endpoint\n");
     651                 : #endif
     652               0 :       if (mFindBackward) {
     653               0 :         aSearchRange->GetStartContainer(getter_AddRefs(startNode));
     654               0 :         aSearchRange->GetStartOffset(&startOffset);
     655               0 :         aEndPoint->GetStartContainer(getter_AddRefs(endNode));
     656               0 :         aEndPoint->GetStartOffset(&endOffset);
     657                 :       } else {     // forward
     658               0 :         aEndPoint->GetEndContainer(getter_AddRefs(startNode));
     659               0 :         aEndPoint->GetEndOffset(&startOffset);
     660               0 :         aSearchRange->GetEndContainer(getter_AddRefs(endNode));
     661               0 :         aSearchRange->GetEndOffset(&endOffset);
     662                 :       }
     663                 :     }
     664                 :     else  // Normal, not continuing
     665                 :     {
     666               0 :       if (mFindBackward) {
     667               0 :         aSearchRange->GetStartContainer(getter_AddRefs(startNode));
     668               0 :         aSearchRange->GetStartOffset(&startOffset);
     669               0 :         aStartPoint->GetEndContainer(getter_AddRefs(endNode));
     670               0 :         aStartPoint->GetEndOffset(&endOffset);
     671                 :         // XXX Needs work:
     672                 :         // Problem with this approach: if there is a match which starts
     673                 :         // just before the current selection and continues into the selection,
     674                 :         // we will miss it, because our search algorithm only starts
     675                 :         // searching from the end of the word, so we would have to
     676                 :         // search the current selection but discount any matches
     677                 :         // that fall entirely inside it.
     678                 :       } else {     // forward
     679               0 :         aStartPoint->GetStartContainer(getter_AddRefs(startNode));
     680               0 :         aStartPoint->GetStartOffset(&startOffset);
     681               0 :         aEndPoint->GetEndContainer(getter_AddRefs(endNode));
     682               0 :         aEndPoint->GetEndOffset(&endOffset);
     683                 :       }
     684                 :     }
     685                 : 
     686               0 :     rv = InitIterator(startNode, startOffset, endNode, endOffset);
     687               0 :     NS_ENSURE_SUCCESS(rv, rv);
     688               0 :     if (!aStartPoint)
     689               0 :       aStartPoint = aSearchRange;
     690                 : 
     691               0 :     content = do_QueryInterface(mIterator->GetCurrentNode());
     692                 : #ifdef DEBUG_FIND
     693                 :     nsCOMPtr<nsIDOMNode> dnode (do_QueryInterface(content));
     694                 :     printf(":::::: Got the first node "); DumpNode(dnode);
     695                 : #endif
     696               0 :     if (content && content->IsNodeOfType(nsINode::eTEXT) &&
     697               0 :         !SkipNode(content))
     698                 :     {
     699               0 :       mIterNode = do_QueryInterface(content);
     700                 :       // Also set mIterOffset if appropriate:
     701               0 :       nsCOMPtr<nsIDOMNode> node;
     702               0 :       if (mFindBackward) {
     703               0 :         aStartPoint->GetEndContainer(getter_AddRefs(node));
     704               0 :         if (mIterNode.get() == node.get())
     705               0 :           aStartPoint->GetEndOffset(&mIterOffset);
     706                 :         else
     707               0 :           mIterOffset = -1;   // sign to start from end
     708                 :       }
     709                 :       else
     710                 :       {
     711               0 :         aStartPoint->GetStartContainer(getter_AddRefs(node));
     712               0 :         if (mIterNode.get() == node.get())
     713               0 :           aStartPoint->GetStartOffset(&mIterOffset);
     714                 :         else
     715               0 :           mIterOffset = 0;
     716                 :       }
     717                 : #ifdef DEBUG_FIND
     718                 :       printf("Setting initial offset to %d\n", mIterOffset);
     719                 : #endif
     720               0 :       return NS_OK;
     721                 :     }
     722                 :   }
     723                 : 
     724               0 :   while (1)
     725                 :   {
     726               0 :     if (mFindBackward)
     727               0 :       mIterator->Prev();
     728                 :     else
     729               0 :       mIterator->Next();
     730                 : 
     731               0 :     content = do_QueryInterface(mIterator->GetCurrentNode());
     732               0 :     if (!content)
     733               0 :       break;
     734                 : 
     735                 : #ifdef DEBUG_FIND
     736                 :     nsCOMPtr<nsIDOMNode> dnode (do_QueryInterface(content));
     737                 :     printf(":::::: Got another node "); DumpNode(dnode);
     738                 : #endif
     739                 : 
     740                 :     // If we ever cross a block node, we might want to reset
     741                 :     // the match anchor:
     742                 :     // we don't match patterns extending across block boundaries.
     743                 :     // But we can't depend on this test here now, because the iterator
     744                 :     // doesn't give us the parent going in and going out, and we
     745                 :     // need it both times to depend on this.
     746                 :     //if (IsBlockNode(content))
     747                 : 
     748                 :     // Now see if we need to skip this node --
     749                 :     // e.g. is it part of a script or other invisible node?
     750                 :     // Note that we don't ask for CSS information;
     751                 :     // a node can be invisible due to CSS, and we'd still find it.
     752               0 :     if (SkipNode(content))
     753               0 :       continue;
     754                 : 
     755               0 :     if (content->IsNodeOfType(nsINode::eTEXT))
     756               0 :       break;
     757                 : #ifdef DEBUG_FIND
     758                 :     dnode = do_QueryInterface(content);
     759                 :     printf("Not a text node: "); DumpNode(dnode);
     760                 : #endif
     761                 :   }
     762                 : 
     763               0 :   if (content)
     764               0 :     mIterNode = do_QueryInterface(content);
     765                 :   else
     766               0 :     mIterNode = nsnull;
     767               0 :   mIterOffset = -1;
     768                 : 
     769                 : #ifdef DEBUG_FIND
     770                 :   printf("Iterator gave: "); DumpNode(mIterNode);
     771                 : #endif
     772               0 :   return NS_OK;
     773                 : }
     774                 : 
     775               0 : bool nsFind::IsBlockNode(nsIContent* aContent)
     776                 : {
     777               0 :   if (!aContent->IsHTML()) {
     778               0 :     return false;
     779                 :   }
     780                 : 
     781               0 :   nsIAtom *atom = aContent->Tag();
     782                 : 
     783               0 :   if (atom == sImgAtom ||
     784                 :       atom == sHRAtom ||
     785                 :       atom == sThAtom ||
     786                 :       atom == sTdAtom)
     787               0 :     return true;
     788                 : 
     789               0 :   if (!mParserService) {
     790               0 :     mParserService = do_GetService(NS_PARSERSERVICE_CONTRACTID);
     791               0 :     if (!mParserService)
     792               0 :       return false;
     793                 :   }
     794                 : 
     795               0 :   bool isBlock = false;
     796               0 :   mParserService->IsBlock(mParserService->HTMLAtomTagToId(atom), isBlock);
     797               0 :   return isBlock;
     798                 : }
     799                 : 
     800               0 : bool nsFind::IsTextNode(nsIDOMNode* aNode)
     801                 : {
     802                 :   PRUint16 nodeType;
     803               0 :   aNode->GetNodeType(&nodeType);
     804                 : 
     805                 :   return nodeType == nsIDOMNode::TEXT_NODE ||
     806               0 :          nodeType == nsIDOMNode::CDATA_SECTION_NODE;
     807                 : }
     808                 : 
     809               0 : bool nsFind::IsVisibleNode(nsIDOMNode *aDOMNode)
     810                 : {
     811               0 :   nsCOMPtr<nsIContent> content(do_QueryInterface(aDOMNode));
     812               0 :   if (!content)
     813               0 :     return false;
     814                 : 
     815               0 :   nsIFrame *frame = content->GetPrimaryFrame();
     816               0 :   if (!frame) {
     817                 :     // No frame! Not visible then.
     818               0 :     return false;
     819                 :   }
     820                 : 
     821               0 :   return frame->GetStyleVisibility()->IsVisible();
     822                 : }
     823                 : 
     824               0 : bool nsFind::SkipNode(nsIContent* aContent)
     825                 : {
     826                 :   nsIAtom *atom;
     827                 : 
     828                 : #ifdef HAVE_BIDI_ITERATOR
     829                 :   atom = aContent->Tag();
     830                 : 
     831                 :   // We may not need to skip comment nodes,
     832                 :   // now that IsTextNode distinguishes them from real text nodes.
     833                 :   return (aContent->IsNodeOfType(nsINode::eCOMMENT) ||
     834                 :           (aContent->IsHTML() &&
     835                 :            (atom == sScriptAtom ||
     836                 :             atom == sNoframesAtom ||
     837                 :             atom == sSelectAtom)));
     838                 : 
     839                 : #else /* HAVE_BIDI_ITERATOR */
     840                 :   // Temporary: eventually we will have an iterator to do this,
     841                 :   // but for now, we have to climb up the tree for each node
     842                 :   // and see whether any parent is a skipped node,
     843                 :   // and take the performance hit.
     844                 : 
     845               0 :   nsIContent *content = aContent;
     846               0 :   while (content)
     847                 :   {
     848               0 :     atom = content->Tag();
     849                 : 
     850               0 :     if (aContent->IsNodeOfType(nsINode::eCOMMENT) ||
     851               0 :         (content->IsHTML() &&
     852                 :          (atom == sScriptAtom ||
     853                 :           atom == sNoframesAtom ||
     854                 :           atom == sSelectAtom)))
     855                 :     {
     856                 : #ifdef DEBUG_FIND
     857                 :       printf("Skipping node: ");
     858                 :       nsCOMPtr<nsIDOMNode> node (do_QueryInterface(content));
     859                 :       DumpNode(node);
     860                 : #endif
     861                 : 
     862               0 :       return true;
     863                 :     }
     864                 : 
     865                 :     // Only climb to the nearest block node
     866               0 :     if (IsBlockNode(content))
     867               0 :       return false;
     868                 : 
     869               0 :     content = content->GetParent();
     870                 :   }
     871                 : 
     872               0 :   return false;
     873                 : #endif /* HAVE_BIDI_ITERATOR */
     874                 : }
     875                 : 
     876               0 : nsresult nsFind::GetBlockParent(nsIDOMNode* aNode, nsIDOMNode** aParent)
     877                 : {
     878               0 :   while (aNode)
     879                 :   {
     880               0 :     nsCOMPtr<nsIDOMNode> parent;
     881               0 :     nsresult rv = aNode->GetParentNode(getter_AddRefs(parent));
     882               0 :     NS_ENSURE_SUCCESS(rv, rv);
     883               0 :     nsCOMPtr<nsIContent> content (do_QueryInterface(parent));
     884               0 :     if (content && IsBlockNode(content))
     885                 :     {
     886               0 :       *aParent = parent;
     887               0 :       NS_ADDREF(*aParent);
     888               0 :       return NS_OK;
     889                 :     }
     890               0 :     aNode = parent;
     891                 :   }
     892               0 :   return NS_ERROR_FAILURE;
     893                 : }
     894                 : 
     895                 : // Call ResetAll before returning,
     896                 : // to remove all references to external objects.
     897               0 : void nsFind::ResetAll()
     898                 : {
     899               0 :   mIterator = nsnull;
     900               0 :   mLastBlockParent = nsnull;
     901               0 : }
     902                 : 
     903                 : #define NBSP_CHARCODE (CHAR_TO_UNICHAR(160))
     904                 : #define IsSpace(c) (nsCRT::IsAsciiSpace(c) || (c) == NBSP_CHARCODE)
     905                 : #define OVERFLOW_PINDEX (mFindBackward ? pindex < 0 : pindex > patLen)
     906                 : #define DONE_WITH_PINDEX (mFindBackward ? pindex <= 0 : pindex >= patLen)
     907                 : #define ALMOST_DONE_WITH_PINDEX (mFindBackward ? pindex <= 0 : pindex >= patLen-1)
     908                 : 
     909                 : //
     910                 : // Find:
     911                 : // Take nodes out of the tree with NextNode,
     912                 : // until null (NextNode will return 0 at the end of our range).
     913                 : //
     914                 : NS_IMETHODIMP
     915               0 : nsFind::Find(const PRUnichar *aPatText, nsIDOMRange* aSearchRange,
     916                 :              nsIDOMRange* aStartPoint, nsIDOMRange* aEndPoint,
     917                 :              nsIDOMRange** aRangeRet)
     918                 : {
     919                 : #ifdef DEBUG_FIND
     920                 :   printf("============== nsFind::Find('%s'%s, %p, %p, %p)\n",
     921                 :          NS_LossyConvertUTF16toASCII(aPatText).get(),
     922                 :          mFindBackward ? " (backward)" : " (forward)",
     923                 :          (void*)aSearchRange, (void*)aStartPoint, (void*)aEndPoint);
     924                 : #endif
     925                 : 
     926               0 :   NS_ENSURE_ARG(aSearchRange);
     927               0 :   NS_ENSURE_ARG(aStartPoint);
     928               0 :   NS_ENSURE_ARG(aEndPoint);
     929               0 :   NS_ENSURE_ARG_POINTER(aRangeRet);
     930               0 :   *aRangeRet = 0;
     931                 : 
     932               0 :   if (!aPatText)
     933               0 :     return NS_ERROR_NULL_POINTER;
     934                 : 
     935               0 :   ResetAll();
     936                 : 
     937               0 :   nsAutoString patAutoStr(aPatText);
     938               0 :   if (!mCaseSensitive)
     939               0 :     ToLowerCase(patAutoStr);
     940                 : 
     941                 :   // Ignore soft hyphens in the pattern  
     942                 :   static const char kShy[] = { char(CH_SHY), 0 };
     943               0 :   patAutoStr.StripChars(kShy);
     944                 : 
     945               0 :   const PRUnichar* patStr = patAutoStr.get();
     946               0 :   PRInt32 patLen = patAutoStr.Length() - 1;
     947                 : 
     948                 :   // current offset into the pattern -- reset to beginning/end:
     949               0 :   PRInt32 pindex = (mFindBackward ? patLen : 0);
     950                 : 
     951                 :   // Current offset into the fragment
     952               0 :   PRInt32 findex = 0;
     953                 : 
     954                 :   // Direction to move pindex and ptr*
     955               0 :   int incr = (mFindBackward ? -1 : 1);
     956                 : 
     957               0 :   nsCOMPtr<nsIContent> tc;
     958               0 :   const nsTextFragment *frag = nsnull;
     959               0 :   PRInt32 fragLen = 0;
     960                 : 
     961                 :   // Pointers into the current fragment:
     962               0 :   const PRUnichar *t2b = nsnull;
     963               0 :   const char      *t1b = nsnull;
     964                 : 
     965                 :   // Keep track of when we're in whitespace:
     966                 :   // (only matters when we're matching)
     967               0 :   bool inWhitespace = false;
     968                 : 
     969                 :   // Place to save the range start point in case we find a match:
     970               0 :   nsCOMPtr<nsIDOMNode> matchAnchorNode;
     971               0 :   PRInt32 matchAnchorOffset = 0;
     972                 : 
     973                 :   // Get the end point, so we know when to end searches:
     974               0 :   nsCOMPtr<nsIDOMNode> endNode;
     975                 :   PRInt32 endOffset;
     976               0 :   aEndPoint->GetEndContainer(getter_AddRefs(endNode));
     977               0 :   aEndPoint->GetEndOffset(&endOffset);
     978                 : 
     979               0 :   PRUnichar prevChar = 0;
     980               0 :   while (1)
     981                 :   {
     982                 : #ifdef DEBUG_FIND
     983                 :     printf("Loop ...\n");
     984                 : #endif
     985                 : 
     986                 :     // If this is our first time on a new node, reset the pointers:
     987               0 :     if (!frag)
     988                 :     {
     989                 : 
     990               0 :       tc = nsnull;
     991               0 :       NextNode(aSearchRange, aStartPoint, aEndPoint, false);
     992               0 :       if (!mIterNode)    // Out of nodes
     993                 :       {
     994                 :         // Are we in the middle of a match?
     995                 :         // If so, try again with continuation.
     996               0 :         if (matchAnchorNode)
     997               0 :           NextNode(aSearchRange, aStartPoint, aEndPoint, true);
     998                 : 
     999                 :         // Reset the iterator, so this nsFind will be usable if
    1000                 :         // the user wants to search again (from beginning/end).
    1001               0 :         ResetAll();
    1002               0 :         return NS_OK;
    1003                 :       }
    1004                 : 
    1005                 :       // We have a new text content.  If its block parent is different
    1006                 :       // from the block parent of the last text content, then we
    1007                 :       // need to clear the match since we don't want to find
    1008                 :       // across block boundaries.
    1009               0 :       nsCOMPtr<nsIDOMNode> blockParent;
    1010               0 :       GetBlockParent(mIterNode, getter_AddRefs(blockParent));
    1011                 : #ifdef DEBUG_FIND
    1012                 :       printf("New node: old blockparent = %p, new = %p\n",
    1013                 :              (void*)mLastBlockParent.get(), (void*)blockParent.get());
    1014                 : #endif
    1015               0 :       if (blockParent != mLastBlockParent)
    1016                 :       {
    1017                 : #ifdef DEBUG_FIND
    1018                 :         printf("Different block parent!\n");
    1019                 : #endif
    1020               0 :         mLastBlockParent = blockParent;
    1021                 :         // End any pending match:
    1022               0 :         matchAnchorNode = nsnull;
    1023               0 :         matchAnchorOffset = 0;
    1024               0 :         pindex = (mFindBackward ? patLen : 0);
    1025               0 :         inWhitespace = false;
    1026                 :       }
    1027                 :  
    1028                 :       // Get the text content:
    1029               0 :       tc = do_QueryInterface(mIterNode);
    1030               0 :       if (!tc || !(frag = tc->GetText())) // Out of nodes
    1031                 :       {
    1032               0 :         mIterator = nsnull;
    1033               0 :         mLastBlockParent = 0;
    1034               0 :         ResetAll();
    1035               0 :         return NS_OK;
    1036                 :       }
    1037                 : 
    1038               0 :       fragLen = frag->GetLength();
    1039                 : 
    1040                 :       // Set our starting point in this node.
    1041                 :       // If we're going back to the anchor node, which means that we
    1042                 :       // just ended a partial match, use the saved offset:
    1043               0 :       if (mIterNode == matchAnchorNode)
    1044               0 :         findex = matchAnchorOffset + (mFindBackward ? 1 : 0);
    1045                 : 
    1046                 :       // mIterOffset, if set, is the range's idea of an offset,
    1047                 :       // and points between characters.  But when translated
    1048                 :       // to a string index, it points to a character.  If we're
    1049                 :       // going backward, this is one character too late and
    1050                 :       // we'll match part of our previous pattern.
    1051               0 :       else if (mIterOffset >= 0)
    1052               0 :         findex = mIterOffset - (mFindBackward ? 1 : 0);
    1053                 : 
    1054                 :       // Otherwise, just start at the appropriate end of the fragment:
    1055               0 :       else if (mFindBackward)
    1056               0 :         findex = fragLen - 1;
    1057                 :       else
    1058               0 :         findex = 0;
    1059                 : 
    1060                 :       // Offset can only apply to the first node:
    1061               0 :       mIterOffset = -1;
    1062                 : 
    1063                 :       // If this is outside the bounds of the string, then skip this node:
    1064               0 :       if (findex < 0 || findex > fragLen-1)
    1065                 :       {
    1066                 : #ifdef DEBUG_FIND
    1067                 :         printf("At the end of a text node -- skipping to the next\n");
    1068                 : #endif
    1069               0 :         frag = 0;
    1070               0 :         continue;
    1071                 :       }
    1072                 : 
    1073                 : #ifdef DEBUG_FIND
    1074                 :       printf("Starting from offset %d\n", findex);
    1075                 : #endif
    1076               0 :       if (frag->Is2b())
    1077                 :       {
    1078               0 :         t2b = frag->Get2b();
    1079               0 :         t1b = nsnull;
    1080                 : #ifdef DEBUG_FIND
    1081                 :         nsAutoString str2(t2b, fragLen);
    1082                 :         printf("2 byte, '%s'\n", NS_LossyConvertUTF16toASCII(str2).get());
    1083                 : #endif
    1084                 :       }
    1085                 :       else
    1086                 :       {
    1087               0 :         t1b = frag->Get1b();
    1088               0 :         t2b = nsnull;
    1089                 : #ifdef DEBUG_FIND
    1090                 :         nsCAutoString str1(t1b, fragLen);
    1091                 :         printf("1 byte, '%s'\n", str1.get());
    1092                 : #endif
    1093                 :       }
    1094                 :     }
    1095                 :     else // still on the old node
    1096                 :     {
    1097                 :       // Still on the old node.  Advance the pointers,
    1098                 :       // then see if we need to pull a new node.
    1099               0 :       findex += incr;
    1100                 : #ifdef DEBUG_FIND
    1101                 :       printf("Same node -- (%d, %d)\n", pindex, findex);
    1102                 : #endif
    1103               0 :       if (mFindBackward ? (findex < 0) : (findex >= fragLen))
    1104                 :       {
    1105                 : #ifdef DEBUG_FIND
    1106                 :         printf("Will need to pull a new node: mAO = %d, frag len=%d\n",
    1107                 :                matchAnchorOffset, fragLen);
    1108                 : #endif
    1109                 :         // Done with this node.  Pull a new one.
    1110               0 :         frag = nsnull;
    1111               0 :         continue;
    1112                 :       }
    1113                 :     }
    1114                 : 
    1115                 :     // Have we gone past the endpoint yet?
    1116                 :     // If we have, and we're not in the middle of a match, return.
    1117               0 :     if (mIterNode == endNode &&
    1118                 :         ((mFindBackward && (findex < endOffset)) ||
    1119               0 :          (!mFindBackward && (findex > endOffset))))
    1120                 :     {
    1121               0 :       ResetAll();
    1122               0 :       return NS_OK;
    1123                 :     }
    1124                 : 
    1125                 :     // The two characters we'll be comparing:
    1126               0 :     PRUnichar c = (t2b ? t2b[findex] : CHAR_TO_UNICHAR(t1b[findex]));
    1127               0 :     PRUnichar patc = patStr[pindex];
    1128                 : 
    1129                 : #ifdef DEBUG_FIND
    1130                 :     printf("Comparing '%c'=%x to '%c' (%d of %d), findex=%d%s\n",
    1131                 :            (char)c, (int)c, patc, pindex, patLen, findex,
    1132                 :            inWhitespace ? " (inWhitespace)" : "");
    1133                 : #endif
    1134                 : 
    1135                 :     // Do we need to go back to non-whitespace mode?
    1136                 :     // If inWhitespace, then this space in the pat str
    1137                 :     // has already matched at least one space in the document.
    1138               0 :     if (inWhitespace && !IsSpace(c))
    1139                 :     {
    1140               0 :       inWhitespace = false;
    1141               0 :       pindex += incr;
    1142                 : #ifdef DEBUG
    1143                 :       // This shouldn't happen -- if we were still matching, and we
    1144                 :       // were at the end of the pat string, then we should have
    1145                 :       // caught it in the last iteration and returned success.
    1146               0 :       if (OVERFLOW_PINDEX)
    1147               0 :         NS_ASSERTION(false, "Missed a whitespace match\n");
    1148                 : #endif
    1149               0 :       patc = patStr[pindex];
    1150                 :     }
    1151               0 :     if (!inWhitespace && IsSpace(patc))
    1152               0 :       inWhitespace = true;
    1153                 : 
    1154                 :     // convert to lower case if necessary
    1155               0 :     else if (!inWhitespace && !mCaseSensitive && IsUpperCase(c))
    1156               0 :       c = ToLowerCase(c);
    1157                 : 
    1158                 :     // ignore soft hyphens in the document
    1159               0 :     if (c == CH_SHY)
    1160               0 :       continue;
    1161                 : 
    1162                 :     // a '\n' between CJ characters is ignored
    1163               0 :     if (pindex != (mFindBackward ? patLen : 0) && c != patc && !inWhitespace) {
    1164               0 :       if (c == '\n' && t2b && IS_CJ_CHAR(prevChar)) {
    1165               0 :         PRInt32 nindex = findex + incr;
    1166               0 :         if (mFindBackward ? (nindex >= 0) : (nindex < fragLen)) {
    1167               0 :           if (IS_CJ_CHAR(t2b[nindex]))
    1168               0 :             continue;
    1169                 :         }
    1170                 :       }
    1171                 :     }
    1172                 : 
    1173                 :     // Compare
    1174               0 :     if ((c == patc) || (inWhitespace && IsSpace(c)))
    1175                 :     {
    1176               0 :       prevChar = c;
    1177                 : #ifdef DEBUG_FIND
    1178                 :       if (inWhitespace)
    1179                 :         printf("YES (whitespace)(%d of %d)\n", pindex, patLen);
    1180                 :       else
    1181                 :         printf("YES! '%c' == '%c' (%d of %d)\n", c, patc, pindex, patLen);
    1182                 : #endif
    1183                 : 
    1184                 :       // Save the range anchors if we haven't already:
    1185               0 :       if (!matchAnchorNode) {
    1186               0 :         matchAnchorNode = mIterNode;
    1187               0 :         matchAnchorOffset = findex;
    1188                 :       }
    1189                 : 
    1190                 :       // Are we done?
    1191               0 :       if (DONE_WITH_PINDEX)
    1192                 :         // Matched the whole string!
    1193                 :       {
    1194                 : #ifdef DEBUG_FIND
    1195                 :         printf("Found a match!\n");
    1196                 : #endif
    1197                 : 
    1198                 :         // Make the range:
    1199               0 :         nsCOMPtr<nsIDOMNode> startParent;
    1200               0 :         nsCOMPtr<nsIDOMNode> endParent;
    1201               0 :         nsCOMPtr<nsIDOMRange> range = CreateRange();
    1202               0 :         if (range)
    1203                 :         {
    1204                 :           PRInt32 matchStartOffset, matchEndOffset;
    1205                 :           // convert char index to range point:
    1206               0 :           PRInt32 mao = matchAnchorOffset + (mFindBackward ? 1 : 0);
    1207               0 :           if (mFindBackward)
    1208                 :           {
    1209               0 :             startParent = do_QueryInterface(tc);
    1210               0 :             endParent = matchAnchorNode;
    1211               0 :             matchStartOffset = findex;
    1212               0 :             matchEndOffset = mao;
    1213                 :           }
    1214                 :           else
    1215                 :           {
    1216               0 :             startParent = matchAnchorNode;
    1217               0 :             endParent = do_QueryInterface(tc);
    1218               0 :             matchStartOffset = mao;
    1219               0 :             matchEndOffset = findex+1;
    1220                 :           }
    1221               0 :           if (startParent && endParent && 
    1222               0 :               IsVisibleNode(startParent) && IsVisibleNode(endParent))
    1223                 :           {
    1224               0 :             range->SetStart(startParent, matchStartOffset);
    1225               0 :             range->SetEnd(endParent, matchEndOffset);
    1226               0 :             *aRangeRet = range.get();
    1227               0 :             NS_ADDREF(*aRangeRet);
    1228                 :           }
    1229                 :           else {
    1230               0 :             startParent = nsnull; // This match is no good -- invisible or bad range
    1231                 :           }
    1232                 :         }
    1233                 : 
    1234               0 :         if (startParent) {
    1235                 :           // If startParent == nsnull, we didn't successfully make range
    1236                 :           // or, we didn't make a range because the start or end node were invisible
    1237                 :           // Reset the offset to the other end of the found string:
    1238               0 :           mIterOffset = findex + (mFindBackward ? 1 : 0);
    1239                 :   #ifdef DEBUG_FIND
    1240                 :           printf("mIterOffset = %d, mIterNode = ", mIterOffset);
    1241                 :           DumpNode(mIterNode);
    1242                 :   #endif
    1243                 : 
    1244               0 :           ResetAll();
    1245               0 :           return NS_OK;
    1246                 :         }
    1247               0 :         matchAnchorNode = nsnull;  // This match is no good, continue on in document
    1248                 :       }
    1249                 : 
    1250               0 :       if (matchAnchorNode) {
    1251                 :         // Not done, but still matching.
    1252                 :         // Advance and loop around for the next characters.
    1253                 :         // But don't advance from a space to a non-space:
    1254               0 :         if (!inWhitespace || DONE_WITH_PINDEX || IsSpace(patStr[pindex+incr]))
    1255                 :         {
    1256               0 :           pindex += incr;
    1257               0 :           inWhitespace = false;
    1258                 : #ifdef DEBUG_FIND
    1259                 :           printf("Advancing pindex to %d\n", pindex);
    1260                 : #endif
    1261                 :         }
    1262                 :       
    1263               0 :         continue;
    1264                 :       }
    1265                 :     }
    1266                 : 
    1267                 : #ifdef DEBUG_FIND
    1268                 :     printf("NOT: %c == %c\n", c, patc);
    1269                 : #endif
    1270                 : 
    1271                 :     // If we didn't match, go back to the beginning of patStr,
    1272                 :     // and set findex back to the next char after
    1273                 :     // we started the current match.
    1274               0 :     if (matchAnchorNode)    // we're ending a partial match
    1275                 :     {
    1276               0 :       findex = matchAnchorOffset;
    1277               0 :       mIterOffset = matchAnchorOffset;
    1278                 :           // +incr will be added to findex when we continue
    1279                 : 
    1280                 :       // Are we going back to a previous node?
    1281               0 :       if (matchAnchorNode != mIterNode)
    1282                 :       {
    1283               0 :         nsCOMPtr<nsIContent> content (do_QueryInterface(matchAnchorNode));
    1284               0 :         nsresult rv = NS_ERROR_UNEXPECTED;
    1285               0 :         if (content)
    1286               0 :           rv = mIterator->PositionAt(content);
    1287               0 :         frag = 0;
    1288               0 :         NS_ASSERTION(NS_SUCCEEDED(rv), "Text content wasn't nsIContent!");
    1289                 : #ifdef DEBUG_FIND
    1290                 :         printf("Repositioned anchor node\n");
    1291                 : #endif
    1292                 :       }
    1293                 : #ifdef DEBUG_FIND
    1294                 :       printf("Ending a partial match; findex -> %d, mIterOffset -> %d\n",
    1295                 :              findex, mIterOffset);
    1296                 : #endif
    1297                 :     }
    1298               0 :     matchAnchorNode = nsnull;
    1299               0 :     matchAnchorOffset = 0;
    1300               0 :     inWhitespace = false;
    1301               0 :     pindex = (mFindBackward ? patLen : 0);
    1302                 : #ifdef DEBUG_FIND
    1303                 :     printf("Setting findex back to %d, pindex to %d\n", findex, pindex);
    1304                 :            
    1305                 : #endif
    1306                 :   } // end while loop
    1307                 : 
    1308                 :   // Out of nodes, and didn't match.
    1309                 :   ResetAll();
    1310                 :   return NS_OK;
    1311                 : }
    1312                 : 
    1313                 : /* static */
    1314                 : already_AddRefed<nsIDOMRange>
    1315               0 : nsFind::CreateRange()
    1316                 : {
    1317               0 :   nsRefPtr<nsRange> range = new nsRange();
    1318               0 :   range->SetMaySpanAnonymousSubtrees(true);
    1319               0 :   return range.forget();
    1320                 : }

Generated by: LCOV version 1.7