~ubuntu-branches/ubuntu/precise/kompozer/precise

« back to all changes in this revision

Viewing changes to mozilla/embedding/components/find/src/nsFind.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Anthony Yarusso
  • Date: 2007-08-27 01:11:03 UTC
  • Revision ID: james.westby@ubuntu.com-20070827011103-2jgf4s6532gqu2ka
Tags: upstream-0.7.10
ImportĀ upstreamĀ versionĀ 0.7.10

Show diffs side-by-side

added added

removed removed

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