LCOV - app.info - embedding/components/find/src/nsFind.cpp

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-04-21 | 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