embedding/components/find/src/nsFind.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/embedding/components/find/src/nsFind.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,1297 @@
     1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.8 +
     1.9 +//#define DEBUG_FIND 1
    1.10 +
    1.11 +#include "nsFind.h"
    1.12 +#include "nsContentCID.h"
    1.13 +#include "nsIContent.h"
    1.14 +#include "nsIDOMNode.h"
    1.15 +#include "nsIDOMNodeList.h"
    1.16 +#include "nsISelection.h"
    1.17 +#include "nsISelectionController.h"
    1.18 +#include "nsIFrame.h"
    1.19 +#include "nsITextControlFrame.h"
    1.20 +#include "nsIFormControl.h"
    1.21 +#include "nsIEditor.h"
    1.22 +#include "nsIPlaintextEditor.h"
    1.23 +#include "nsTextFragment.h"
    1.24 +#include "nsString.h"
    1.25 +#include "nsIAtom.h"
    1.26 +#include "nsServiceManagerUtils.h"
    1.27 +#include "nsUnicharUtils.h"
    1.28 +#include "nsIDOMElement.h"
    1.29 +#include "nsIWordBreaker.h"
    1.30 +#include "nsCRT.h"
    1.31 +#include "nsRange.h"
    1.32 +#include "nsContentUtils.h"
    1.33 +#include "mozilla/DebugOnly.h"
    1.34 +
    1.35 +using namespace mozilla;
    1.36 +
    1.37 +// Yikes!  Casting a char to unichar can fill with ones!
    1.38 +#define CHAR_TO_UNICHAR(c) ((char16_t)(const unsigned char)c)
    1.39 +
    1.40 +static NS_DEFINE_CID(kCContentIteratorCID, NS_CONTENTITERATOR_CID);
    1.41 +static NS_DEFINE_CID(kCPreContentIteratorCID, NS_PRECONTENTITERATOR_CID);
    1.42 +
    1.43 +#define CH_QUOTE ((char16_t) 0x22)
    1.44 +#define CH_APOSTROPHE ((char16_t) 0x27)
    1.45 +#define CH_LEFT_SINGLE_QUOTE ((char16_t) 0x2018)
    1.46 +#define CH_RIGHT_SINGLE_QUOTE ((char16_t) 0x2019)
    1.47 +#define CH_LEFT_DOUBLE_QUOTE ((char16_t) 0x201C)
    1.48 +#define CH_RIGHT_DOUBLE_QUOTE ((char16_t) 0x201D)
    1.49 +
    1.50 +#define CH_SHY ((char16_t) 0xAD)
    1.51 +
    1.52 +// nsFind::Find casts CH_SHY to char before calling StripChars
    1.53 +// This works correctly if and only if CH_SHY <= 255
    1.54 +PR_STATIC_ASSERT(CH_SHY <= 255);
    1.55 +
    1.56 +// -----------------------------------------------------------------------
    1.57 +// nsFindContentIterator is a special iterator that also goes through
    1.58 +// any existing <textarea>'s or text <input>'s editor to lookup the
    1.59 +// anonymous DOM content there.
    1.60 +//
    1.61 +// Details:
    1.62 +// 1) We use two iterators: The "outer-iterator" goes through the
    1.63 +// normal DOM. The "inner-iterator" goes through the anonymous DOM
    1.64 +// inside the editor.
    1.65 +//
    1.66 +// 2) [MaybeSetupInnerIterator] As soon as the outer-iterator's current
    1.67 +// node is changed, a check is made to see if the node is a <textarea> or
    1.68 +// a text <input> node. If so, an inner-iterator is created to lookup the
    1.69 +// anynomous contents of the editor underneath the text control.
    1.70 +//
    1.71 +// 3) When the inner-iterator is created, we position the outer-iterator
    1.72 +// 'after' (or 'before' in backward search) the text control to avoid
    1.73 +// revisiting that control.
    1.74 +//
    1.75 +// 4) As a consequence of searching through text controls, we can be
    1.76 +// called via FindNext with the current selection inside a <textarea>
    1.77 +// or a text <input>. This means that we can be given an initial search
    1.78 +// range that stretches across the anonymous DOM and the normal DOM. To
    1.79 +// cater for this situation, we split the anonymous part into the
    1.80 +// inner-iterator and then reposition the outer-iterator outside.
    1.81 +//
    1.82 +// 5) The implementation assumes that First() and Next() are only called
    1.83 +// in find-forward mode, while Last() and Prev() are used in find-backward.
    1.84 +
    1.85 +class nsFindContentIterator : public nsIContentIterator
    1.86 +{
    1.87 +public:
    1.88 +  nsFindContentIterator(bool aFindBackward)
    1.89 +    : mStartOffset(0),
    1.90 +      mEndOffset(0),
    1.91 +      mFindBackward(aFindBackward)
    1.92 +  {
    1.93 +  }
    1.94 +
    1.95 +  virtual ~nsFindContentIterator()
    1.96 +  {
    1.97 +  }
    1.98 +
    1.99 +  // nsISupports
   1.100 +  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   1.101 +    NS_DECL_CYCLE_COLLECTION_CLASS(nsFindContentIterator)
   1.102 +
   1.103 +  // nsIContentIterator
   1.104 +  virtual nsresult Init(nsINode* aRoot)
   1.105 +  {
   1.106 +    NS_NOTREACHED("internal error");
   1.107 +    return NS_ERROR_NOT_IMPLEMENTED;
   1.108 +  }
   1.109 +  virtual nsresult Init(nsIDOMRange* aRange)
   1.110 +  {
   1.111 +    NS_NOTREACHED("internal error");
   1.112 +    return NS_ERROR_NOT_IMPLEMENTED;
   1.113 +  }
   1.114 +  // Not a range because one of the endpoints may be anonymous.
   1.115 +  nsresult Init(nsIDOMNode* aStartNode, int32_t aStartOffset,
   1.116 +                nsIDOMNode* aEndNode, int32_t aEndOffset);
   1.117 +  virtual void First();
   1.118 +  virtual void Last();
   1.119 +  virtual void Next();
   1.120 +  virtual void Prev();
   1.121 +  virtual nsINode* GetCurrentNode();
   1.122 +  virtual bool IsDone();
   1.123 +  virtual nsresult PositionAt(nsINode* aCurNode);
   1.124 +
   1.125 +private:
   1.126 +  nsCOMPtr<nsIContentIterator> mOuterIterator;
   1.127 +  nsCOMPtr<nsIContentIterator> mInnerIterator;
   1.128 +  // Can't use a range here, since we want to represent part of the
   1.129 +  // flattened tree, including native anonymous content.
   1.130 +  nsCOMPtr<nsIDOMNode> mStartNode;
   1.131 +  int32_t mStartOffset;
   1.132 +  nsCOMPtr<nsIDOMNode> mEndNode;
   1.133 +  int32_t mEndOffset;
   1.134 +
   1.135 +  nsCOMPtr<nsIContent> mStartOuterContent;
   1.136 +  nsCOMPtr<nsIContent> mEndOuterContent;
   1.137 +  bool mFindBackward;
   1.138 +
   1.139 +  void Reset();
   1.140 +  void MaybeSetupInnerIterator();
   1.141 +  void SetupInnerIterator(nsIContent* aContent);
   1.142 +};
   1.143 +
   1.144 +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsFindContentIterator)
   1.145 +  NS_INTERFACE_MAP_ENTRY(nsIContentIterator)
   1.146 +  NS_INTERFACE_MAP_ENTRY(nsISupports)
   1.147 +NS_INTERFACE_MAP_END
   1.148 +
   1.149 +NS_IMPL_CYCLE_COLLECTING_ADDREF(nsFindContentIterator)
   1.150 +NS_IMPL_CYCLE_COLLECTING_RELEASE(nsFindContentIterator)
   1.151 +
   1.152 +NS_IMPL_CYCLE_COLLECTION(nsFindContentIterator, mOuterIterator, mInnerIterator,
   1.153 +                         mStartOuterContent, mEndOuterContent, mEndNode, mStartNode)
   1.154 +
   1.155 +
   1.156 +nsresult
   1.157 +nsFindContentIterator::Init(nsIDOMNode* aStartNode, int32_t aStartOffset,
   1.158 +                            nsIDOMNode* aEndNode, int32_t aEndOffset)
   1.159 +{
   1.160 +  NS_ENSURE_ARG_POINTER(aStartNode);
   1.161 +  NS_ENSURE_ARG_POINTER(aEndNode);
   1.162 +  if (!mOuterIterator) {
   1.163 +    if (mFindBackward) {
   1.164 +      // Use post-order in the reverse case, so we get parents
   1.165 +      // before children in case we want to prevent descending
   1.166 +      // into a node.
   1.167 +      mOuterIterator = do_CreateInstance(kCContentIteratorCID);
   1.168 +    }
   1.169 +    else {
   1.170 +      // Use pre-order in the forward case, so we get parents
   1.171 +      // before children in case we want to prevent descending
   1.172 +      // into a node.
   1.173 +      mOuterIterator = do_CreateInstance(kCPreContentIteratorCID);
   1.174 +    }
   1.175 +    NS_ENSURE_ARG_POINTER(mOuterIterator);
   1.176 +  }
   1.177 +
   1.178 +  // Set up the search "range" that we will examine
   1.179 +  mStartNode = aStartNode;
   1.180 +  mStartOffset = aStartOffset;
   1.181 +  mEndNode = aEndNode;
   1.182 +  mEndOffset = aEndOffset;
   1.183 +
   1.184 +  return NS_OK;
   1.185 +}
   1.186 +
   1.187 +void
   1.188 +nsFindContentIterator::First()
   1.189 +{
   1.190 +  Reset();
   1.191 +}
   1.192 +
   1.193 +void
   1.194 +nsFindContentIterator::Last()
   1.195 +{
   1.196 +  Reset();
   1.197 +}
   1.198 +
   1.199 +void
   1.200 +nsFindContentIterator::Next()
   1.201 +{
   1.202 +  if (mInnerIterator) {
   1.203 +    mInnerIterator->Next();
   1.204 +    if (!mInnerIterator->IsDone())
   1.205 +      return;
   1.206 +
   1.207 +    // by construction, mOuterIterator is already on the next node
   1.208 +  }
   1.209 +  else {
   1.210 +    mOuterIterator->Next();
   1.211 +  }
   1.212 +  MaybeSetupInnerIterator();
   1.213 +}
   1.214 +
   1.215 +void
   1.216 +nsFindContentIterator::Prev()
   1.217 +{
   1.218 +  if (mInnerIterator) {
   1.219 +    mInnerIterator->Prev();
   1.220 +    if (!mInnerIterator->IsDone())
   1.221 +      return;
   1.222 +
   1.223 +    // by construction, mOuterIterator is already on the previous node
   1.224 +  }
   1.225 +  else {
   1.226 +    mOuterIterator->Prev();
   1.227 +  }
   1.228 +  MaybeSetupInnerIterator();
   1.229 +}
   1.230 +
   1.231 +nsINode*
   1.232 +nsFindContentIterator::GetCurrentNode()
   1.233 +{
   1.234 +  if (mInnerIterator && !mInnerIterator->IsDone()) {
   1.235 +    return mInnerIterator->GetCurrentNode();
   1.236 +  }
   1.237 +  return mOuterIterator->GetCurrentNode();
   1.238 +}
   1.239 +
   1.240 +bool
   1.241 +nsFindContentIterator::IsDone() {
   1.242 +  if (mInnerIterator && !mInnerIterator->IsDone()) {
   1.243 +    return false;
   1.244 +  }
   1.245 +  return mOuterIterator->IsDone();
   1.246 +}
   1.247 +
   1.248 +nsresult
   1.249 +nsFindContentIterator::PositionAt(nsINode* aCurNode)
   1.250 +{
   1.251 +  nsINode* oldNode = mOuterIterator->GetCurrentNode();
   1.252 +  nsresult rv = mOuterIterator->PositionAt(aCurNode);
   1.253 +  if (NS_SUCCEEDED(rv)) {
   1.254 +    MaybeSetupInnerIterator();
   1.255 +  }
   1.256 +  else {
   1.257 +    mOuterIterator->PositionAt(oldNode);
   1.258 +    if (mInnerIterator)
   1.259 +      rv = mInnerIterator->PositionAt(aCurNode);
   1.260 +  }
   1.261 +  return rv;
   1.262 +}
   1.263 +
   1.264 +void
   1.265 +nsFindContentIterator::Reset()
   1.266 +{
   1.267 +  mInnerIterator = nullptr;
   1.268 +  mStartOuterContent = nullptr;
   1.269 +  mEndOuterContent = nullptr;
   1.270 +
   1.271 +  // As a consequence of searching through text controls, we may have been
   1.272 +  // initialized with a selection inside a <textarea> or a text <input>.
   1.273 +
   1.274 +  // see if the start node is an anonymous text node inside a text control
   1.275 +  nsCOMPtr<nsIContent> startContent(do_QueryInterface(mStartNode));
   1.276 +  if (startContent) {
   1.277 +    mStartOuterContent = startContent->FindFirstNonChromeOnlyAccessContent();
   1.278 +  }
   1.279 +
   1.280 +  // see if the end node is an anonymous text node inside a text control
   1.281 +  nsCOMPtr<nsIContent> endContent(do_QueryInterface(mEndNode));
   1.282 +  if (endContent) {
   1.283 +    mEndOuterContent = endContent->FindFirstNonChromeOnlyAccessContent();
   1.284 +  }
   1.285 +
   1.286 +  // Note: OK to just set up the outer iterator here; if our range has a native
   1.287 +  // anonymous endpoint we'll end up setting up an inner iterator, and
   1.288 +  // reset the outer one in the process.
   1.289 +  nsCOMPtr<nsINode> node = do_QueryInterface(mStartNode);
   1.290 +  NS_ENSURE_TRUE_VOID(node);
   1.291 +
   1.292 +  nsCOMPtr<nsIDOMRange> range = nsFind::CreateRange(node);
   1.293 +  range->SetStart(mStartNode, mStartOffset);
   1.294 +  range->SetEnd(mEndNode, mEndOffset);
   1.295 +  mOuterIterator->Init(range);
   1.296 +
   1.297 +  if (!mFindBackward) {
   1.298 +    if (mStartOuterContent != startContent) {
   1.299 +      // the start node was an anonymous text node
   1.300 +      SetupInnerIterator(mStartOuterContent);
   1.301 +      if (mInnerIterator)
   1.302 +        mInnerIterator->First();
   1.303 +    }
   1.304 +    if (!mOuterIterator->IsDone())
   1.305 +      mOuterIterator->First();
   1.306 +  }
   1.307 +  else {
   1.308 +    if (mEndOuterContent != endContent) {
   1.309 +      // the end node was an anonymous text node
   1.310 +      SetupInnerIterator(mEndOuterContent);
   1.311 +      if (mInnerIterator)
   1.312 +        mInnerIterator->Last();
   1.313 +    }
   1.314 +    if (!mOuterIterator->IsDone())
   1.315 +      mOuterIterator->Last();
   1.316 +  }
   1.317 +
   1.318 +  // if we didn't create an inner-iterator, the boundary node could still be
   1.319 +  // a text control, in which case we also need an inner-iterator straightaway
   1.320 +  if (!mInnerIterator) {
   1.321 +    MaybeSetupInnerIterator();
   1.322 +  }
   1.323 +}
   1.324 +
   1.325 +void
   1.326 +nsFindContentIterator::MaybeSetupInnerIterator()
   1.327 +{
   1.328 +  mInnerIterator = nullptr;
   1.329 +
   1.330 +  nsCOMPtr<nsIContent> content =
   1.331 +    do_QueryInterface(mOuterIterator->GetCurrentNode());
   1.332 +  if (!content || !content->IsNodeOfType(nsINode::eHTML_FORM_CONTROL))
   1.333 +    return;
   1.334 +
   1.335 +  nsCOMPtr<nsIFormControl> formControl(do_QueryInterface(content));
   1.336 +  if (!formControl->IsTextControl(true)) {
   1.337 +    return;
   1.338 +  }
   1.339 +
   1.340 +  SetupInnerIterator(content);
   1.341 +  if (mInnerIterator) {
   1.342 +    if (!mFindBackward) {
   1.343 +      mInnerIterator->First();
   1.344 +      // finish setup: position mOuterIterator on the actual "next"
   1.345 +      // node (this completes its re-init, @see SetupInnerIterator)
   1.346 +      if (!mOuterIterator->IsDone())
   1.347 +        mOuterIterator->First();
   1.348 +    }
   1.349 +    else {
   1.350 +      mInnerIterator->Last();
   1.351 +      // finish setup: position mOuterIterator on the actual "previous"
   1.352 +      // node (this completes its re-init, @see SetupInnerIterator)
   1.353 +      if (!mOuterIterator->IsDone())
   1.354 +        mOuterIterator->Last();
   1.355 +    }
   1.356 +  }
   1.357 +}
   1.358 +
   1.359 +void
   1.360 +nsFindContentIterator::SetupInnerIterator(nsIContent* aContent)
   1.361 +{
   1.362 +  if (!aContent) {
   1.363 +    return;
   1.364 +  }
   1.365 +  NS_ASSERTION(!aContent->IsRootOfNativeAnonymousSubtree(), "invalid call");
   1.366 +
   1.367 +  nsITextControlFrame* tcFrame = do_QueryFrame(aContent->GetPrimaryFrame());
   1.368 +  if (!tcFrame)
   1.369 +    return;
   1.370 +
   1.371 +  nsCOMPtr<nsIEditor> editor;
   1.372 +  tcFrame->GetEditor(getter_AddRefs(editor));
   1.373 +  if (!editor)
   1.374 +    return;
   1.375 +
   1.376 +  // don't mess with disabled input fields
   1.377 +  uint32_t editorFlags = 0;
   1.378 +  editor->GetFlags(&editorFlags);
   1.379 +  if (editorFlags & nsIPlaintextEditor::eEditorDisabledMask)
   1.380 +    return;
   1.381 +
   1.382 +  nsCOMPtr<nsIDOMElement> rootElement;
   1.383 +  editor->GetRootElement(getter_AddRefs(rootElement));
   1.384 +
   1.385 +  nsCOMPtr<nsIDOMRange> innerRange = nsFind::CreateRange(aContent);
   1.386 +  nsCOMPtr<nsIDOMRange> outerRange = nsFind::CreateRange(aContent);
   1.387 +  if (!innerRange || !outerRange) {
   1.388 +    return;
   1.389 +  }
   1.390 +
   1.391 +  // now create the inner-iterator
   1.392 +  mInnerIterator = do_CreateInstance(kCPreContentIteratorCID);
   1.393 +
   1.394 +  if (mInnerIterator) {
   1.395 +    innerRange->SelectNodeContents(rootElement);
   1.396 +
   1.397 +    // fix up the inner bounds, we may have to only lookup a portion
   1.398 +    // of the text control if the current node is a boundary point
   1.399 +    if (aContent == mStartOuterContent) {
   1.400 +      innerRange->SetStart(mStartNode, mStartOffset);
   1.401 +    }
   1.402 +    if (aContent == mEndOuterContent) {
   1.403 +      innerRange->SetEnd(mEndNode, mEndOffset);
   1.404 +    }
   1.405 +    // Note: we just init here. We do First() or Last() later.
   1.406 +    mInnerIterator->Init(innerRange);
   1.407 +
   1.408 +    // make sure to place the outer-iterator outside
   1.409 +    // the text control so that we don't go there again.
   1.410 +    nsresult res1, res2;
   1.411 +    nsCOMPtr<nsIDOMNode> outerNode(do_QueryInterface(aContent));
   1.412 +    if (!mFindBackward) { // find forward
   1.413 +      // cut the outer-iterator after the current node
   1.414 +      res1 = outerRange->SetEnd(mEndNode, mEndOffset);
   1.415 +      res2 = outerRange->SetStartAfter(outerNode);
   1.416 +    }
   1.417 +    else { // find backward
   1.418 +      // cut the outer-iterator before the current node
   1.419 +      res1 = outerRange->SetStart(mStartNode, mStartOffset);
   1.420 +      res2 = outerRange->SetEndBefore(outerNode);
   1.421 +    }
   1.422 +    if (NS_FAILED(res1) || NS_FAILED(res2)) {
   1.423 +      // we are done with the outer-iterator, the
   1.424 +      // inner-iterator will traverse what we want
   1.425 +      outerRange->Collapse(true);
   1.426 +    }
   1.427 +
   1.428 +    // Note: we just re-init here, using the segment of our search range that
   1.429 +    // is yet to be visited. Thus when we later do mOuterIterator->First() [or
   1.430 +    // mOuterIterator->Last()], we will effectively be on the next node [or
   1.431 +    // the previous node] _with respect to_ the search range.
   1.432 +    mOuterIterator->Init(outerRange);
   1.433 +  }
   1.434 +}
   1.435 +
   1.436 +nsresult
   1.437 +NS_NewFindContentIterator(bool aFindBackward,
   1.438 +                          nsIContentIterator** aResult)
   1.439 +{
   1.440 +  NS_ENSURE_ARG_POINTER(aResult);
   1.441 +  if (!aResult) {
   1.442 +    return NS_ERROR_NULL_POINTER;
   1.443 +  }
   1.444 +
   1.445 +  nsFindContentIterator* it = new nsFindContentIterator(aFindBackward);
   1.446 +  if (!it) {
   1.447 +    return NS_ERROR_OUT_OF_MEMORY;
   1.448 +  }
   1.449 +  return it->QueryInterface(NS_GET_IID(nsIContentIterator), (void **)aResult);
   1.450 +}
   1.451 +// --------------------------------------------------------------------
   1.452 +
   1.453 +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsFind)
   1.454 +  NS_INTERFACE_MAP_ENTRY(nsIFind)
   1.455 +  NS_INTERFACE_MAP_ENTRY(nsISupports)
   1.456 +NS_INTERFACE_MAP_END
   1.457 +
   1.458 +NS_IMPL_CYCLE_COLLECTING_ADDREF(nsFind)
   1.459 +NS_IMPL_CYCLE_COLLECTING_RELEASE(nsFind)
   1.460 +
   1.461 +  NS_IMPL_CYCLE_COLLECTION(nsFind, mLastBlockParent, mIterNode, mIterator)
   1.462 +
   1.463 +nsFind::nsFind()
   1.464 +  : mFindBackward(false)
   1.465 +  , mCaseSensitive(false)
   1.466 +  , mIterOffset(0)
   1.467 +{
   1.468 +}
   1.469 +
   1.470 +nsFind::~nsFind()
   1.471 +{
   1.472 +}
   1.473 +
   1.474 +#ifdef DEBUG_FIND
   1.475 +static void DumpNode(nsIDOMNode* aNode)
   1.476 +{
   1.477 +  if (!aNode)
   1.478 +  {
   1.479 +    printf(">>>> Node: NULL\n");
   1.480 +    return;
   1.481 +  }
   1.482 +  nsAutoString nodeName;
   1.483 +  aNode->GetNodeName(nodeName);
   1.484 +  nsCOMPtr<nsIContent> textContent (do_QueryInterface(aNode));
   1.485 +  if (textContent && textContent->IsNodeOfType(nsINode::eTEXT))
   1.486 +  {
   1.487 +    nsAutoString newText;
   1.488 +    textContent->AppendTextTo(newText);
   1.489 +    printf(">>>> Text node (node name %s): '%s'\n",
   1.490 +           NS_LossyConvertUTF16toASCII(nodeName).get(),
   1.491 +           NS_LossyConvertUTF16toASCII(newText).get());
   1.492 +  }
   1.493 +  else
   1.494 +    printf(">>>> Node: %s\n", NS_LossyConvertUTF16toASCII(nodeName).get());
   1.495 +}
   1.496 +#endif
   1.497 +
   1.498 +nsresult
   1.499 +nsFind::InitIterator(nsIDOMNode* aStartNode, int32_t aStartOffset,
   1.500 +                     nsIDOMNode* aEndNode, int32_t aEndOffset)
   1.501 +{
   1.502 +  if (!mIterator)
   1.503 +  {
   1.504 +    mIterator = new nsFindContentIterator(mFindBackward);
   1.505 +    NS_ENSURE_TRUE(mIterator, NS_ERROR_OUT_OF_MEMORY);
   1.506 +  }
   1.507 +
   1.508 +  NS_ENSURE_ARG_POINTER(aStartNode);
   1.509 +  NS_ENSURE_ARG_POINTER(aEndNode);
   1.510 +
   1.511 +#ifdef DEBUG_FIND
   1.512 +  printf("InitIterator search range:\n");
   1.513 +  printf(" -- start %d, ", aStartOffset); DumpNode(aStartNode);
   1.514 +  printf(" -- end %d, ", aEndOffset); DumpNode(aEndNode);
   1.515 +#endif
   1.516 +
   1.517 +  nsresult rv =
   1.518 +    mIterator->Init(aStartNode, aStartOffset, aEndNode, aEndOffset);
   1.519 +  NS_ENSURE_SUCCESS(rv, rv);
   1.520 +  if (mFindBackward) {
   1.521 +    mIterator->Last();
   1.522 +  }
   1.523 +  else {
   1.524 +    mIterator->First();
   1.525 +  }
   1.526 +  return NS_OK;
   1.527 +}
   1.528 +
   1.529 +/* attribute boolean findBackward; */
   1.530 +NS_IMETHODIMP
   1.531 +nsFind::GetFindBackwards(bool *aFindBackward)
   1.532 +{
   1.533 +  if (!aFindBackward)
   1.534 +    return NS_ERROR_NULL_POINTER;
   1.535 +
   1.536 +  *aFindBackward = mFindBackward;
   1.537 +  return NS_OK;
   1.538 +}
   1.539 +NS_IMETHODIMP
   1.540 +nsFind::SetFindBackwards(bool aFindBackward)
   1.541 +{
   1.542 +  mFindBackward = aFindBackward;
   1.543 +  return NS_OK;
   1.544 +}
   1.545 +
   1.546 +/* attribute boolean caseSensitive; */
   1.547 +NS_IMETHODIMP
   1.548 +nsFind::GetCaseSensitive(bool *aCaseSensitive)
   1.549 +{
   1.550 +  if (!aCaseSensitive)
   1.551 +    return NS_ERROR_NULL_POINTER;
   1.552 +
   1.553 +  *aCaseSensitive = mCaseSensitive;
   1.554 +  return NS_OK;
   1.555 +}
   1.556 +NS_IMETHODIMP
   1.557 +nsFind::SetCaseSensitive(bool aCaseSensitive)
   1.558 +{
   1.559 +  mCaseSensitive = aCaseSensitive;
   1.560 +  return NS_OK;
   1.561 +}
   1.562 +
   1.563 +NS_IMETHODIMP
   1.564 +nsFind::GetWordBreaker(nsIWordBreaker** aWordBreaker)
   1.565 +{
   1.566 +  *aWordBreaker = mWordBreaker;
   1.567 +  NS_IF_ADDREF(*aWordBreaker);
   1.568 +  return NS_OK;
   1.569 +}
   1.570 +
   1.571 +NS_IMETHODIMP
   1.572 +nsFind::SetWordBreaker(nsIWordBreaker* aWordBreaker)
   1.573 +{
   1.574 +  mWordBreaker = aWordBreaker;
   1.575 +  return NS_OK;
   1.576 +}
   1.577 +
   1.578 +//
   1.579 +// Here begins the find code.
   1.580 +// A ten-thousand-foot view of how it works:
   1.581 +// Find needs to be able to compare across inline (but not block) nodes,
   1.582 +// e.g. find for "abc" should match a<b>b</b>c.
   1.583 +// So after we've searched a node, we're not done with it;
   1.584 +// in the case of a partial match we may need to reset the
   1.585 +// iterator to go back to a previously visited node,
   1.586 +// so we always save the "match anchor" node and offset.
   1.587 +//
   1.588 +// Text nodes store their text in an nsTextFragment, which is
   1.589 +// effectively a union of a one-byte string or a two-byte string.
   1.590 +// Single and double strings are intermixed in the dom.
   1.591 +// We don't have string classes which can deal with intermixed strings,
   1.592 +// so all the handling is done explicitly here.
   1.593 +//
   1.594 +
   1.595 +nsresult
   1.596 +nsFind::NextNode(nsIDOMRange* aSearchRange,
   1.597 +                 nsIDOMRange* aStartPoint, nsIDOMRange* aEndPoint,
   1.598 +                 bool aContinueOk)
   1.599 +{
   1.600 +  nsresult rv;
   1.601 +
   1.602 +  nsCOMPtr<nsIContent> content;
   1.603 +
   1.604 +  if (!mIterator || aContinueOk)
   1.605 +  {
   1.606 +    // If we are continuing, that means we have a match in progress.
   1.607 +    // In that case, we want to continue from the end point
   1.608 +    // (where we are now) to the beginning/end of the search range.
   1.609 +    nsCOMPtr<nsIDOMNode> startNode;
   1.610 +    nsCOMPtr<nsIDOMNode> endNode;
   1.611 +    int32_t startOffset, endOffset;
   1.612 +    if (aContinueOk)
   1.613 +    {
   1.614 +#ifdef DEBUG_FIND
   1.615 +      printf("Match in progress: continuing past endpoint\n");
   1.616 +#endif
   1.617 +      if (mFindBackward) {
   1.618 +        aSearchRange->GetStartContainer(getter_AddRefs(startNode));
   1.619 +        aSearchRange->GetStartOffset(&startOffset);
   1.620 +        aEndPoint->GetStartContainer(getter_AddRefs(endNode));
   1.621 +        aEndPoint->GetStartOffset(&endOffset);
   1.622 +      } else {     // forward
   1.623 +        aEndPoint->GetEndContainer(getter_AddRefs(startNode));
   1.624 +        aEndPoint->GetEndOffset(&startOffset);
   1.625 +        aSearchRange->GetEndContainer(getter_AddRefs(endNode));
   1.626 +        aSearchRange->GetEndOffset(&endOffset);
   1.627 +      }
   1.628 +    }
   1.629 +    else  // Normal, not continuing
   1.630 +    {
   1.631 +      if (mFindBackward) {
   1.632 +        aSearchRange->GetStartContainer(getter_AddRefs(startNode));
   1.633 +        aSearchRange->GetStartOffset(&startOffset);
   1.634 +        aStartPoint->GetEndContainer(getter_AddRefs(endNode));
   1.635 +        aStartPoint->GetEndOffset(&endOffset);
   1.636 +        // XXX Needs work:
   1.637 +        // Problem with this approach: if there is a match which starts
   1.638 +        // just before the current selection and continues into the selection,
   1.639 +        // we will miss it, because our search algorithm only starts
   1.640 +        // searching from the end of the word, so we would have to
   1.641 +        // search the current selection but discount any matches
   1.642 +        // that fall entirely inside it.
   1.643 +      } else {     // forward
   1.644 +        aStartPoint->GetStartContainer(getter_AddRefs(startNode));
   1.645 +        aStartPoint->GetStartOffset(&startOffset);
   1.646 +        aEndPoint->GetEndContainer(getter_AddRefs(endNode));
   1.647 +        aEndPoint->GetEndOffset(&endOffset);
   1.648 +      }
   1.649 +    }
   1.650 +
   1.651 +    rv = InitIterator(startNode, startOffset, endNode, endOffset);
   1.652 +    NS_ENSURE_SUCCESS(rv, rv);
   1.653 +    if (!aStartPoint)
   1.654 +      aStartPoint = aSearchRange;
   1.655 +
   1.656 +    content = do_QueryInterface(mIterator->GetCurrentNode());
   1.657 +#ifdef DEBUG_FIND
   1.658 +    nsCOMPtr<nsIDOMNode> dnode (do_QueryInterface(content));
   1.659 +    printf(":::::: Got the first node "); DumpNode(dnode);
   1.660 +#endif
   1.661 +    if (content && content->IsNodeOfType(nsINode::eTEXT) &&
   1.662 +        !SkipNode(content))
   1.663 +    {
   1.664 +      mIterNode = do_QueryInterface(content);
   1.665 +      // Also set mIterOffset if appropriate:
   1.666 +      nsCOMPtr<nsIDOMNode> node;
   1.667 +      if (mFindBackward) {
   1.668 +        aStartPoint->GetEndContainer(getter_AddRefs(node));
   1.669 +        if (mIterNode.get() == node.get())
   1.670 +          aStartPoint->GetEndOffset(&mIterOffset);
   1.671 +        else
   1.672 +          mIterOffset = -1;   // sign to start from end
   1.673 +      }
   1.674 +      else
   1.675 +      {
   1.676 +        aStartPoint->GetStartContainer(getter_AddRefs(node));
   1.677 +        if (mIterNode.get() == node.get())
   1.678 +          aStartPoint->GetStartOffset(&mIterOffset);
   1.679 +        else
   1.680 +          mIterOffset = 0;
   1.681 +      }
   1.682 +#ifdef DEBUG_FIND
   1.683 +      printf("Setting initial offset to %d\n", mIterOffset);
   1.684 +#endif
   1.685 +      return NS_OK;
   1.686 +    }
   1.687 +  }
   1.688 +
   1.689 +  while (1)
   1.690 +  {
   1.691 +    if (mFindBackward)
   1.692 +      mIterator->Prev();
   1.693 +    else
   1.694 +      mIterator->Next();
   1.695 +
   1.696 +    content = do_QueryInterface(mIterator->GetCurrentNode());
   1.697 +    if (!content)
   1.698 +      break;
   1.699 +
   1.700 +#ifdef DEBUG_FIND
   1.701 +    nsCOMPtr<nsIDOMNode> dnode (do_QueryInterface(content));
   1.702 +    printf(":::::: Got another node "); DumpNode(dnode);
   1.703 +#endif
   1.704 +
   1.705 +    // If we ever cross a block node, we might want to reset
   1.706 +    // the match anchor:
   1.707 +    // we don't match patterns extending across block boundaries.
   1.708 +    // But we can't depend on this test here now, because the iterator
   1.709 +    // doesn't give us the parent going in and going out, and we
   1.710 +    // need it both times to depend on this.
   1.711 +    //if (IsBlockNode(content))
   1.712 +
   1.713 +    // Now see if we need to skip this node --
   1.714 +    // e.g. is it part of a script or other invisible node?
   1.715 +    // Note that we don't ask for CSS information;
   1.716 +    // a node can be invisible due to CSS, and we'd still find it.
   1.717 +    if (SkipNode(content))
   1.718 +      continue;
   1.719 +
   1.720 +    if (content->IsNodeOfType(nsINode::eTEXT))
   1.721 +      break;
   1.722 +#ifdef DEBUG_FIND
   1.723 +    dnode = do_QueryInterface(content);
   1.724 +    printf("Not a text node: "); DumpNode(dnode);
   1.725 +#endif
   1.726 +  }
   1.727 +
   1.728 +  if (content)
   1.729 +    mIterNode = do_QueryInterface(content);
   1.730 +  else
   1.731 +    mIterNode = nullptr;
   1.732 +  mIterOffset = -1;
   1.733 +
   1.734 +#ifdef DEBUG_FIND
   1.735 +  printf("Iterator gave: "); DumpNode(mIterNode);
   1.736 +#endif
   1.737 +  return NS_OK;
   1.738 +}
   1.739 +
   1.740 +bool nsFind::IsBlockNode(nsIContent* aContent)
   1.741 +{
   1.742 +  if (!aContent->IsHTML()) {
   1.743 +    return false;
   1.744 +  }
   1.745 +
   1.746 +  nsIAtom *atom = aContent->Tag();
   1.747 +
   1.748 +  if (atom == nsGkAtoms::img ||
   1.749 +      atom == nsGkAtoms::hr ||
   1.750 +      atom == nsGkAtoms::th ||
   1.751 +      atom == nsGkAtoms::td)
   1.752 +    return true;
   1.753 +
   1.754 +  return nsContentUtils::IsHTMLBlock(atom);
   1.755 +}
   1.756 +
   1.757 +bool nsFind::IsTextNode(nsIDOMNode* aNode)
   1.758 +{
   1.759 +  uint16_t nodeType;
   1.760 +  aNode->GetNodeType(&nodeType);
   1.761 +
   1.762 +  return nodeType == nsIDOMNode::TEXT_NODE ||
   1.763 +         nodeType == nsIDOMNode::CDATA_SECTION_NODE;
   1.764 +}
   1.765 +
   1.766 +bool nsFind::IsVisibleNode(nsIDOMNode *aDOMNode)
   1.767 +{
   1.768 +  nsCOMPtr<nsIContent> content(do_QueryInterface(aDOMNode));
   1.769 +  if (!content)
   1.770 +    return false;
   1.771 +
   1.772 +  nsIFrame *frame = content->GetPrimaryFrame();
   1.773 +  if (!frame) {
   1.774 +    // No frame! Not visible then.
   1.775 +    return false;
   1.776 +  }
   1.777 +
   1.778 +  return frame->StyleVisibility()->IsVisible();
   1.779 +}
   1.780 +
   1.781 +bool nsFind::SkipNode(nsIContent* aContent)
   1.782 +{
   1.783 +  nsIAtom *atom;
   1.784 +
   1.785 +#ifdef HAVE_BIDI_ITERATOR
   1.786 +  atom = aContent->Tag();
   1.787 +
   1.788 +  // We may not need to skip comment nodes,
   1.789 +  // now that IsTextNode distinguishes them from real text nodes.
   1.790 +  return (aContent->IsNodeOfType(nsINode::eCOMMENT) ||
   1.791 +          (aContent->IsHTML() &&
   1.792 +           (atom == sScriptAtom ||
   1.793 +            atom == sNoframesAtom ||
   1.794 +            atom == sSelectAtom)));
   1.795 +
   1.796 +#else /* HAVE_BIDI_ITERATOR */
   1.797 +  // Temporary: eventually we will have an iterator to do this,
   1.798 +  // but for now, we have to climb up the tree for each node
   1.799 +  // and see whether any parent is a skipped node,
   1.800 +  // and take the performance hit.
   1.801 +
   1.802 +  nsIContent *content = aContent;
   1.803 +  while (content)
   1.804 +  {
   1.805 +    atom = content->Tag();
   1.806 +
   1.807 +    if (aContent->IsNodeOfType(nsINode::eCOMMENT) ||
   1.808 +        (content->IsHTML() &&
   1.809 +         (atom == nsGkAtoms::script ||
   1.810 +          atom == nsGkAtoms::noframes ||
   1.811 +          atom == nsGkAtoms::select)))
   1.812 +    {
   1.813 +#ifdef DEBUG_FIND
   1.814 +      printf("Skipping node: ");
   1.815 +      nsCOMPtr<nsIDOMNode> node (do_QueryInterface(content));
   1.816 +      DumpNode(node);
   1.817 +#endif
   1.818 +
   1.819 +      return true;
   1.820 +    }
   1.821 +
   1.822 +    // Only climb to the nearest block node
   1.823 +    if (IsBlockNode(content))
   1.824 +      return false;
   1.825 +
   1.826 +    content = content->GetParent();
   1.827 +  }
   1.828 +
   1.829 +  return false;
   1.830 +#endif /* HAVE_BIDI_ITERATOR */
   1.831 +}
   1.832 +
   1.833 +nsresult nsFind::GetBlockParent(nsIDOMNode* aNode, nsIDOMNode** aParent)
   1.834 +{
   1.835 +  while (aNode)
   1.836 +  {
   1.837 +    nsCOMPtr<nsIDOMNode> parent;
   1.838 +    nsresult rv = aNode->GetParentNode(getter_AddRefs(parent));
   1.839 +    NS_ENSURE_SUCCESS(rv, rv);
   1.840 +    nsCOMPtr<nsIContent> content (do_QueryInterface(parent));
   1.841 +    if (content && IsBlockNode(content))
   1.842 +    {
   1.843 +      *aParent = parent;
   1.844 +      NS_ADDREF(*aParent);
   1.845 +      return NS_OK;
   1.846 +    }
   1.847 +    aNode = parent;
   1.848 +  }
   1.849 +  return NS_ERROR_FAILURE;
   1.850 +}
   1.851 +
   1.852 +// Call ResetAll before returning,
   1.853 +// to remove all references to external objects.
   1.854 +void nsFind::ResetAll()
   1.855 +{
   1.856 +  mIterator = nullptr;
   1.857 +  mLastBlockParent = nullptr;
   1.858 +}
   1.859 +
   1.860 +#define NBSP_CHARCODE (CHAR_TO_UNICHAR(160))
   1.861 +#define IsSpace(c) (nsCRT::IsAsciiSpace(c) || (c) == NBSP_CHARCODE)
   1.862 +#define OVERFLOW_PINDEX (mFindBackward ? pindex < 0 : pindex > patLen)
   1.863 +#define DONE_WITH_PINDEX (mFindBackward ? pindex <= 0 : pindex >= patLen)
   1.864 +#define ALMOST_DONE_WITH_PINDEX (mFindBackward ? pindex <= 0 : pindex >= patLen-1)
   1.865 +
   1.866 +//
   1.867 +// Find:
   1.868 +// Take nodes out of the tree with NextNode,
   1.869 +// until null (NextNode will return 0 at the end of our range).
   1.870 +//
   1.871 +NS_IMETHODIMP
   1.872 +nsFind::Find(const char16_t *aPatText, nsIDOMRange* aSearchRange,
   1.873 +             nsIDOMRange* aStartPoint, nsIDOMRange* aEndPoint,
   1.874 +             nsIDOMRange** aRangeRet)
   1.875 +{
   1.876 +#ifdef DEBUG_FIND
   1.877 +  printf("============== nsFind::Find('%s'%s, %p, %p, %p)\n",
   1.878 +         NS_LossyConvertUTF16toASCII(aPatText).get(),
   1.879 +         mFindBackward ? " (backward)" : " (forward)",
   1.880 +         (void*)aSearchRange, (void*)aStartPoint, (void*)aEndPoint);
   1.881 +#endif
   1.882 +
   1.883 +  NS_ENSURE_ARG(aSearchRange);
   1.884 +  NS_ENSURE_ARG(aStartPoint);
   1.885 +  NS_ENSURE_ARG(aEndPoint);
   1.886 +  NS_ENSURE_ARG_POINTER(aRangeRet);
   1.887 +  *aRangeRet = 0;
   1.888 +
   1.889 +  if (!aPatText)
   1.890 +    return NS_ERROR_NULL_POINTER;
   1.891 +
   1.892 +  ResetAll();
   1.893 +
   1.894 +  nsAutoString patAutoStr(aPatText);
   1.895 +  if (!mCaseSensitive)
   1.896 +    ToLowerCase(patAutoStr);
   1.897 +
   1.898 +  // Ignore soft hyphens in the pattern
   1.899 +  static const char kShy[] = { char(CH_SHY), 0 };
   1.900 +  patAutoStr.StripChars(kShy);
   1.901 +
   1.902 +  const char16_t* patStr = patAutoStr.get();
   1.903 +  int32_t patLen = patAutoStr.Length() - 1;
   1.904 +
   1.905 +  // current offset into the pattern -- reset to beginning/end:
   1.906 +  int32_t pindex = (mFindBackward ? patLen : 0);
   1.907 +
   1.908 +  // Current offset into the fragment
   1.909 +  int32_t findex = 0;
   1.910 +
   1.911 +  // Direction to move pindex and ptr*
   1.912 +  int incr = (mFindBackward ? -1 : 1);
   1.913 +
   1.914 +  nsCOMPtr<nsIContent> tc;
   1.915 +  const nsTextFragment *frag = nullptr;
   1.916 +  int32_t fragLen = 0;
   1.917 +
   1.918 +  // Pointers into the current fragment:
   1.919 +  const char16_t *t2b = nullptr;
   1.920 +  const char      *t1b = nullptr;
   1.921 +
   1.922 +  // Keep track of when we're in whitespace:
   1.923 +  // (only matters when we're matching)
   1.924 +  bool inWhitespace = false;
   1.925 +
   1.926 +  // Place to save the range start point in case we find a match:
   1.927 +  nsCOMPtr<nsIDOMNode> matchAnchorNode;
   1.928 +  int32_t matchAnchorOffset = 0;
   1.929 +
   1.930 +  // Get the end point, so we know when to end searches:
   1.931 +  nsCOMPtr<nsIDOMNode> endNode;
   1.932 +  int32_t endOffset;
   1.933 +  aEndPoint->GetEndContainer(getter_AddRefs(endNode));
   1.934 +  aEndPoint->GetEndOffset(&endOffset);
   1.935 +
   1.936 +  char16_t prevChar = 0;
   1.937 +  while (1)
   1.938 +  {
   1.939 +#ifdef DEBUG_FIND
   1.940 +    printf("Loop ...\n");
   1.941 +#endif
   1.942 +
   1.943 +    // If this is our first time on a new node, reset the pointers:
   1.944 +    if (!frag)
   1.945 +    {
   1.946 +
   1.947 +      tc = nullptr;
   1.948 +      NextNode(aSearchRange, aStartPoint, aEndPoint, false);
   1.949 +      if (!mIterNode)    // Out of nodes
   1.950 +      {
   1.951 +        // Are we in the middle of a match?
   1.952 +        // If so, try again with continuation.
   1.953 +        if (matchAnchorNode)
   1.954 +          NextNode(aSearchRange, aStartPoint, aEndPoint, true);
   1.955 +
   1.956 +        // Reset the iterator, so this nsFind will be usable if
   1.957 +        // the user wants to search again (from beginning/end).
   1.958 +        ResetAll();
   1.959 +        return NS_OK;
   1.960 +      }
   1.961 +
   1.962 +      // We have a new text content.  If its block parent is different
   1.963 +      // from the block parent of the last text content, then we
   1.964 +      // need to clear the match since we don't want to find
   1.965 +      // across block boundaries.
   1.966 +      nsCOMPtr<nsIDOMNode> blockParent;
   1.967 +      GetBlockParent(mIterNode, getter_AddRefs(blockParent));
   1.968 +#ifdef DEBUG_FIND
   1.969 +      printf("New node: old blockparent = %p, new = %p\n",
   1.970 +             (void*)mLastBlockParent.get(), (void*)blockParent.get());
   1.971 +#endif
   1.972 +      if (blockParent != mLastBlockParent)
   1.973 +      {
   1.974 +#ifdef DEBUG_FIND
   1.975 +        printf("Different block parent!\n");
   1.976 +#endif
   1.977 +        mLastBlockParent = blockParent;
   1.978 +        // End any pending match:
   1.979 +        matchAnchorNode = nullptr;
   1.980 +        matchAnchorOffset = 0;
   1.981 +        pindex = (mFindBackward ? patLen : 0);
   1.982 +        inWhitespace = false;
   1.983 +      }
   1.984 +
   1.985 +      // Get the text content:
   1.986 +      tc = do_QueryInterface(mIterNode);
   1.987 +      if (!tc || !(frag = tc->GetText())) // Out of nodes
   1.988 +      {
   1.989 +        mIterator = nullptr;
   1.990 +        mLastBlockParent = 0;
   1.991 +        ResetAll();
   1.992 +        return NS_OK;
   1.993 +      }
   1.994 +
   1.995 +      fragLen = frag->GetLength();
   1.996 +
   1.997 +      // Set our starting point in this node.
   1.998 +      // If we're going back to the anchor node, which means that we
   1.999 +      // just ended a partial match, use the saved offset:
  1.1000 +      if (mIterNode == matchAnchorNode)
  1.1001 +        findex = matchAnchorOffset + (mFindBackward ? 1 : 0);
  1.1002 +
  1.1003 +      // mIterOffset, if set, is the range's idea of an offset,
  1.1004 +      // and points between characters.  But when translated
  1.1005 +      // to a string index, it points to a character.  If we're
  1.1006 +      // going backward, this is one character too late and
  1.1007 +      // we'll match part of our previous pattern.
  1.1008 +      else if (mIterOffset >= 0)
  1.1009 +        findex = mIterOffset - (mFindBackward ? 1 : 0);
  1.1010 +
  1.1011 +      // Otherwise, just start at the appropriate end of the fragment:
  1.1012 +      else if (mFindBackward)
  1.1013 +        findex = fragLen - 1;
  1.1014 +      else
  1.1015 +        findex = 0;
  1.1016 +
  1.1017 +      // Offset can only apply to the first node:
  1.1018 +      mIterOffset = -1;
  1.1019 +
  1.1020 +      // If this is outside the bounds of the string, then skip this node:
  1.1021 +      if (findex < 0 || findex > fragLen-1)
  1.1022 +      {
  1.1023 +#ifdef DEBUG_FIND
  1.1024 +        printf("At the end of a text node -- skipping to the next\n");
  1.1025 +#endif
  1.1026 +        frag = 0;
  1.1027 +        continue;
  1.1028 +      }
  1.1029 +
  1.1030 +#ifdef DEBUG_FIND
  1.1031 +      printf("Starting from offset %d\n", findex);
  1.1032 +#endif
  1.1033 +      if (frag->Is2b())
  1.1034 +      {
  1.1035 +        t2b = frag->Get2b();
  1.1036 +        t1b = nullptr;
  1.1037 +#ifdef DEBUG_FIND
  1.1038 +        nsAutoString str2(t2b, fragLen);
  1.1039 +        printf("2 byte, '%s'\n", NS_LossyConvertUTF16toASCII(str2).get());
  1.1040 +#endif
  1.1041 +      }
  1.1042 +      else
  1.1043 +      {
  1.1044 +        t1b = frag->Get1b();
  1.1045 +        t2b = nullptr;
  1.1046 +#ifdef DEBUG_FIND
  1.1047 +        nsAutoCString str1(t1b, fragLen);
  1.1048 +        printf("1 byte, '%s'\n", str1.get());
  1.1049 +#endif
  1.1050 +      }
  1.1051 +    }
  1.1052 +    else // still on the old node
  1.1053 +    {
  1.1054 +      // Still on the old node.  Advance the pointers,
  1.1055 +      // then see if we need to pull a new node.
  1.1056 +      findex += incr;
  1.1057 +#ifdef DEBUG_FIND
  1.1058 +      printf("Same node -- (%d, %d)\n", pindex, findex);
  1.1059 +#endif
  1.1060 +      if (mFindBackward ? (findex < 0) : (findex >= fragLen))
  1.1061 +      {
  1.1062 +#ifdef DEBUG_FIND
  1.1063 +        printf("Will need to pull a new node: mAO = %d, frag len=%d\n",
  1.1064 +               matchAnchorOffset, fragLen);
  1.1065 +#endif
  1.1066 +        // Done with this node.  Pull a new one.
  1.1067 +        frag = nullptr;
  1.1068 +        continue;
  1.1069 +      }
  1.1070 +    }
  1.1071 +
  1.1072 +    // Have we gone past the endpoint yet?
  1.1073 +    // If we have, and we're not in the middle of a match, return.
  1.1074 +    if (mIterNode == endNode &&
  1.1075 +        ((mFindBackward && (findex < endOffset)) ||
  1.1076 +         (!mFindBackward && (findex > endOffset))))
  1.1077 +    {
  1.1078 +      ResetAll();
  1.1079 +      return NS_OK;
  1.1080 +    }
  1.1081 +
  1.1082 +    // The two characters we'll be comparing:
  1.1083 +    char16_t c = (t2b ? t2b[findex] : CHAR_TO_UNICHAR(t1b[findex]));
  1.1084 +    char16_t patc = patStr[pindex];
  1.1085 +
  1.1086 +#ifdef DEBUG_FIND
  1.1087 +    printf("Comparing '%c'=%x to '%c' (%d of %d), findex=%d%s\n",
  1.1088 +           (char)c, (int)c, patc, pindex, patLen, findex,
  1.1089 +           inWhitespace ? " (inWhitespace)" : "");
  1.1090 +#endif
  1.1091 +
  1.1092 +    // Do we need to go back to non-whitespace mode?
  1.1093 +    // If inWhitespace, then this space in the pat str
  1.1094 +    // has already matched at least one space in the document.
  1.1095 +    if (inWhitespace && !IsSpace(c))
  1.1096 +    {
  1.1097 +      inWhitespace = false;
  1.1098 +      pindex += incr;
  1.1099 +#ifdef DEBUG
  1.1100 +      // This shouldn't happen -- if we were still matching, and we
  1.1101 +      // were at the end of the pat string, then we should have
  1.1102 +      // caught it in the last iteration and returned success.
  1.1103 +      if (OVERFLOW_PINDEX)
  1.1104 +        NS_ASSERTION(false, "Missed a whitespace match");
  1.1105 +#endif
  1.1106 +      patc = patStr[pindex];
  1.1107 +    }
  1.1108 +    if (!inWhitespace && IsSpace(patc))
  1.1109 +      inWhitespace = true;
  1.1110 +
  1.1111 +    // convert to lower case if necessary
  1.1112 +    else if (!inWhitespace && !mCaseSensitive && IsUpperCase(c))
  1.1113 +      c = ToLowerCase(c);
  1.1114 +
  1.1115 +    switch (c) {
  1.1116 +      // ignore soft hyphens in the document
  1.1117 +      case CH_SHY:
  1.1118 +        continue;
  1.1119 +      // treat curly and straight quotes as identical
  1.1120 +      case CH_LEFT_SINGLE_QUOTE:
  1.1121 +      case CH_RIGHT_SINGLE_QUOTE:
  1.1122 +        c = CH_APOSTROPHE;
  1.1123 +        break;
  1.1124 +      case CH_LEFT_DOUBLE_QUOTE:
  1.1125 +      case CH_RIGHT_DOUBLE_QUOTE:
  1.1126 +        c = CH_QUOTE;
  1.1127 +        break;
  1.1128 +    }
  1.1129 +
  1.1130 +    switch (patc) {
  1.1131 +      // treat curly and straight quotes as identical
  1.1132 +      case CH_LEFT_SINGLE_QUOTE:
  1.1133 +      case CH_RIGHT_SINGLE_QUOTE:
  1.1134 +        patc = CH_APOSTROPHE;
  1.1135 +        break;
  1.1136 +      case CH_LEFT_DOUBLE_QUOTE:
  1.1137 +      case CH_RIGHT_DOUBLE_QUOTE:
  1.1138 +        patc = CH_QUOTE;
  1.1139 +        break;
  1.1140 +    }
  1.1141 +
  1.1142 +    // a '\n' between CJ characters is ignored
  1.1143 +    if (pindex != (mFindBackward ? patLen : 0) && c != patc && !inWhitespace) {
  1.1144 +      if (c == '\n' && t2b && IS_CJ_CHAR(prevChar)) {
  1.1145 +        int32_t nindex = findex + incr;
  1.1146 +        if (mFindBackward ? (nindex >= 0) : (nindex < fragLen)) {
  1.1147 +          if (IS_CJ_CHAR(t2b[nindex]))
  1.1148 +            continue;
  1.1149 +        }
  1.1150 +      }
  1.1151 +    }
  1.1152 +
  1.1153 +    // Compare
  1.1154 +    if ((c == patc) || (inWhitespace && IsSpace(c)))
  1.1155 +    {
  1.1156 +      prevChar = c;
  1.1157 +#ifdef DEBUG_FIND
  1.1158 +      if (inWhitespace)
  1.1159 +        printf("YES (whitespace)(%d of %d)\n", pindex, patLen);
  1.1160 +      else
  1.1161 +        printf("YES! '%c' == '%c' (%d of %d)\n", c, patc, pindex, patLen);
  1.1162 +#endif
  1.1163 +
  1.1164 +      // Save the range anchors if we haven't already:
  1.1165 +      if (!matchAnchorNode) {
  1.1166 +        matchAnchorNode = mIterNode;
  1.1167 +        matchAnchorOffset = findex;
  1.1168 +      }
  1.1169 +
  1.1170 +      // Are we done?
  1.1171 +      if (DONE_WITH_PINDEX)
  1.1172 +        // Matched the whole string!
  1.1173 +      {
  1.1174 +#ifdef DEBUG_FIND
  1.1175 +        printf("Found a match!\n");
  1.1176 +#endif
  1.1177 +
  1.1178 +        // Make the range:
  1.1179 +        nsCOMPtr<nsIDOMNode> startParent;
  1.1180 +        nsCOMPtr<nsIDOMNode> endParent;
  1.1181 +        nsCOMPtr<nsIDOMRange> range = CreateRange(tc);
  1.1182 +        if (range)
  1.1183 +        {
  1.1184 +          int32_t matchStartOffset, matchEndOffset;
  1.1185 +          // convert char index to range point:
  1.1186 +          int32_t mao = matchAnchorOffset + (mFindBackward ? 1 : 0);
  1.1187 +          if (mFindBackward)
  1.1188 +          {
  1.1189 +            startParent = do_QueryInterface(tc);
  1.1190 +            endParent = matchAnchorNode;
  1.1191 +            matchStartOffset = findex;
  1.1192 +            matchEndOffset = mao;
  1.1193 +          }
  1.1194 +          else
  1.1195 +          {
  1.1196 +            startParent = matchAnchorNode;
  1.1197 +            endParent = do_QueryInterface(tc);
  1.1198 +            matchStartOffset = mao;
  1.1199 +            matchEndOffset = findex+1;
  1.1200 +          }
  1.1201 +          if (startParent && endParent &&
  1.1202 +              IsVisibleNode(startParent) && IsVisibleNode(endParent))
  1.1203 +          {
  1.1204 +            range->SetStart(startParent, matchStartOffset);
  1.1205 +            range->SetEnd(endParent, matchEndOffset);
  1.1206 +            *aRangeRet = range.get();
  1.1207 +            NS_ADDREF(*aRangeRet);
  1.1208 +          }
  1.1209 +          else {
  1.1210 +            startParent = nullptr; // This match is no good -- invisible or bad range
  1.1211 +          }
  1.1212 +        }
  1.1213 +
  1.1214 +        if (startParent) {
  1.1215 +          // If startParent == nullptr, we didn't successfully make range
  1.1216 +          // or, we didn't make a range because the start or end node were invisible
  1.1217 +          // Reset the offset to the other end of the found string:
  1.1218 +          mIterOffset = findex + (mFindBackward ? 1 : 0);
  1.1219 +  #ifdef DEBUG_FIND
  1.1220 +          printf("mIterOffset = %d, mIterNode = ", mIterOffset);
  1.1221 +          DumpNode(mIterNode);
  1.1222 +  #endif
  1.1223 +
  1.1224 +          ResetAll();
  1.1225 +          return NS_OK;
  1.1226 +        }
  1.1227 +        matchAnchorNode = nullptr;  // This match is no good, continue on in document
  1.1228 +      }
  1.1229 +
  1.1230 +      if (matchAnchorNode) {
  1.1231 +        // Not done, but still matching.
  1.1232 +        // Advance and loop around for the next characters.
  1.1233 +        // But don't advance from a space to a non-space:
  1.1234 +        if (!inWhitespace || DONE_WITH_PINDEX || IsSpace(patStr[pindex+incr]))
  1.1235 +        {
  1.1236 +          pindex += incr;
  1.1237 +          inWhitespace = false;
  1.1238 +#ifdef DEBUG_FIND
  1.1239 +          printf("Advancing pindex to %d\n", pindex);
  1.1240 +#endif
  1.1241 +        }
  1.1242 +
  1.1243 +        continue;
  1.1244 +      }
  1.1245 +    }
  1.1246 +
  1.1247 +#ifdef DEBUG_FIND
  1.1248 +    printf("NOT: %c == %c\n", c, patc);
  1.1249 +#endif
  1.1250 +
  1.1251 +    // If we didn't match, go back to the beginning of patStr,
  1.1252 +    // and set findex back to the next char after
  1.1253 +    // we started the current match.
  1.1254 +    if (matchAnchorNode)    // we're ending a partial match
  1.1255 +    {
  1.1256 +      findex = matchAnchorOffset;
  1.1257 +      mIterOffset = matchAnchorOffset;
  1.1258 +          // +incr will be added to findex when we continue
  1.1259 +
  1.1260 +      // Are we going back to a previous node?
  1.1261 +      if (matchAnchorNode != mIterNode)
  1.1262 +      {
  1.1263 +        nsCOMPtr<nsIContent> content (do_QueryInterface(matchAnchorNode));
  1.1264 +        DebugOnly<nsresult> rv = NS_ERROR_UNEXPECTED;
  1.1265 +        if (content)
  1.1266 +          rv = mIterator->PositionAt(content);
  1.1267 +        frag = 0;
  1.1268 +        NS_ASSERTION(NS_SUCCEEDED(rv), "Text content wasn't nsIContent!");
  1.1269 +#ifdef DEBUG_FIND
  1.1270 +        printf("Repositioned anchor node\n");
  1.1271 +#endif
  1.1272 +      }
  1.1273 +#ifdef DEBUG_FIND
  1.1274 +      printf("Ending a partial match; findex -> %d, mIterOffset -> %d\n",
  1.1275 +             findex, mIterOffset);
  1.1276 +#endif
  1.1277 +    }
  1.1278 +    matchAnchorNode = nullptr;
  1.1279 +    matchAnchorOffset = 0;
  1.1280 +    inWhitespace = false;
  1.1281 +    pindex = (mFindBackward ? patLen : 0);
  1.1282 +#ifdef DEBUG_FIND
  1.1283 +    printf("Setting findex back to %d, pindex to %d\n", findex, pindex);
  1.1284 +
  1.1285 +#endif
  1.1286 +  } // end while loop
  1.1287 +
  1.1288 +  // Out of nodes, and didn't match.
  1.1289 +  ResetAll();
  1.1290 +  return NS_OK;
  1.1291 +}
  1.1292 +
  1.1293 +/* static */
  1.1294 +already_AddRefed<nsIDOMRange>
  1.1295 +nsFind::CreateRange(nsINode* aNode)
  1.1296 +{
  1.1297 +  nsRefPtr<nsRange> range = new nsRange(aNode);
  1.1298 +  range->SetMaySpanAnonymousSubtrees(true);
  1.1299 +  return range.forget();
  1.1300 +}

mercurial