LCOV - code coverage report
Current view: top level - toolkit/components/find - nsFind.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 0 526 0.0 %
Date: 2017-07-14 16:53:18 Functions: 0 58 0.0 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.13