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