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