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 #include "nsWebBrowserFind.h"
8
9 // Only need this for NS_FIND_CONTRACTID,
10 // else we could use nsRange.h and nsIFind.h.
11 #include "nsFind.h"
12
13 #include "mozilla/dom/ScriptSettings.h"
14 #include "nsIInterfaceRequestor.h"
15 #include "nsIInterfaceRequestorUtils.h"
16 #include "nsPIDOMWindow.h"
17 #include "nsIDocShell.h"
18 #include "nsPresContext.h"
19 #include "mozilla/dom/Document.h"
20 #include "nsISelectionController.h"
21 #include "nsIFrame.h"
22 #include "nsITextControlFrame.h"
23 #include "nsReadableUtils.h"
24 #include "nsIContent.h"
25 #include "nsContentCID.h"
26 #include "nsIObserverService.h"
27 #include "nsISupportsPrimitives.h"
28 #include "nsFind.h"
29 #include "nsError.h"
30 #include "nsFocusManager.h"
31 #include "nsRange.h"
32 #include "mozilla/PresShell.h"
33 #include "mozilla/Services.h"
34 #include "mozilla/dom/Element.h"
35 #include "mozilla/dom/Selection.h"
36 #include "nsISimpleEnumerator.h"
37 #include "nsComponentManagerUtils.h"
38 #include "nsContentUtils.h"
39 #include "nsGenericHTMLElement.h"
40
41 #if DEBUG
42 # include "nsIWebNavigation.h"
43 # include "nsString.h"
44 #endif
45
46 using mozilla::dom::Document;
47 using mozilla::dom::Element;
48 using mozilla::dom::Selection;
49
nsWebBrowserFind()50 nsWebBrowserFind::nsWebBrowserFind()
51 : mFindBackwards(false),
52 mWrapFind(false),
53 mEntireWord(false),
54 mMatchCase(false),
55 mMatchDiacritics(false),
56 mSearchSubFrames(true),
57 mSearchParentFrames(true) {}
58
59 nsWebBrowserFind::~nsWebBrowserFind() = default;
60
NS_IMPL_ISUPPORTS(nsWebBrowserFind,nsIWebBrowserFind,nsIWebBrowserFindInFrames)61 NS_IMPL_ISUPPORTS(nsWebBrowserFind, nsIWebBrowserFind,
62 nsIWebBrowserFindInFrames)
63
64 NS_IMETHODIMP
65 nsWebBrowserFind::FindNext(bool* aResult) {
66 NS_ENSURE_ARG_POINTER(aResult);
67 *aResult = false;
68
69 NS_ENSURE_TRUE(CanFindNext(), NS_ERROR_NOT_INITIALIZED);
70
71 nsresult rv = NS_OK;
72 nsCOMPtr<nsPIDOMWindowOuter> searchFrame =
73 do_QueryReferent(mCurrentSearchFrame);
74 NS_ENSURE_TRUE(searchFrame, NS_ERROR_NOT_INITIALIZED);
75
76 nsCOMPtr<nsPIDOMWindowOuter> rootFrame = do_QueryReferent(mRootSearchFrame);
77 NS_ENSURE_TRUE(rootFrame, NS_ERROR_NOT_INITIALIZED);
78
79 // first, if there's a "cmd_findagain" observer around, check to see if it
80 // wants to perform the find again command . If it performs the find again
81 // it will return true, in which case we exit ::FindNext() early.
82 // Otherwise, nsWebBrowserFind needs to perform the find again command itself
83 // this is used by nsTypeAheadFind, which controls find again when it was
84 // the last executed find in the current window.
85 nsCOMPtr<nsIObserverService> observerSvc =
86 mozilla::services::GetObserverService();
87 if (observerSvc) {
88 nsCOMPtr<nsISupportsInterfacePointer> windowSupportsData =
89 do_CreateInstance(NS_SUPPORTS_INTERFACE_POINTER_CONTRACTID, &rv);
90 NS_ENSURE_SUCCESS(rv, rv);
91 nsCOMPtr<nsISupports> searchWindowSupports = do_QueryInterface(rootFrame);
92 windowSupportsData->SetData(searchWindowSupports);
93 observerSvc->NotifyObservers(windowSupportsData,
94 "nsWebBrowserFind_FindAgain",
95 mFindBackwards ? u"up" : u"down");
96 windowSupportsData->GetData(getter_AddRefs(searchWindowSupports));
97 // findnext performed if search window data cleared out
98 *aResult = searchWindowSupports == nullptr;
99 if (*aResult) {
100 return NS_OK;
101 }
102 }
103
104 // next, look in the current frame. If found, return.
105
106 // Beware! This may flush notifications via synchronous
107 // ScrollSelectionIntoView.
108 rv = SearchInFrame(searchFrame, false, aResult);
109 if (NS_FAILED(rv)) {
110 return rv;
111 }
112 if (*aResult) {
113 return OnFind(searchFrame); // we are done
114 }
115
116 // if we are not searching other frames, return
117 if (!mSearchSubFrames && !mSearchParentFrames) {
118 return NS_OK;
119 }
120
121 nsIDocShell* rootDocShell = rootFrame->GetDocShell();
122 if (!rootDocShell) {
123 return NS_ERROR_FAILURE;
124 }
125
126 auto enumDirection = mFindBackwards ? nsIDocShell::ENUMERATE_BACKWARDS
127 : nsIDocShell::ENUMERATE_FORWARDS;
128
129 nsTArray<RefPtr<nsIDocShell>> docShells;
130 rv = rootDocShell->GetAllDocShellsInSubtree(nsIDocShellTreeItem::typeAll,
131 enumDirection, docShells);
132 if (NS_FAILED(rv)) {
133 return rv;
134 }
135
136 // remember where we started
137 nsCOMPtr<nsIDocShellTreeItem> startingItem = searchFrame->GetDocShell();
138
139 // XXX We should avoid searching in frameset documents here.
140 // We also need to honour mSearchSubFrames and mSearchParentFrames.
141 bool doFind = false;
142 for (const auto& curItem : docShells) {
143 if (doFind) {
144 searchFrame = curItem->GetWindow();
145 if (!searchFrame) {
146 break;
147 }
148
149 OnStartSearchFrame(searchFrame);
150
151 // Beware! This may flush notifications via synchronous
152 // ScrollSelectionIntoView.
153 rv = SearchInFrame(searchFrame, false, aResult);
154 if (NS_FAILED(rv)) {
155 return rv;
156 }
157 if (*aResult) {
158 return OnFind(searchFrame); // we are done
159 }
160
161 OnEndSearchFrame(searchFrame);
162 }
163
164 if (curItem.get() == startingItem.get()) {
165 doFind = true; // start looking in frames after this one
166 }
167 }
168
169 if (!mWrapFind) {
170 // remember where we left off
171 SetCurrentSearchFrame(searchFrame);
172 return NS_OK;
173 }
174
175 // From here on, we're wrapping, first through the other frames, then finally
176 // from the beginning of the starting frame back to the starting point.
177
178 // because nsISimpleEnumerator is bad and isn't resettable, I have to
179 // make a new one
180 rv = rootDocShell->GetAllDocShellsInSubtree(nsIDocShellTreeItem::typeAll,
181 enumDirection, docShells);
182 if (NS_FAILED(rv)) {
183 return rv;
184 }
185
186 for (const auto& curItem : docShells) {
187 searchFrame = curItem->GetWindow();
188 if (!searchFrame) {
189 rv = NS_ERROR_FAILURE;
190 break;
191 }
192
193 if (curItem.get() == startingItem.get()) {
194 // Beware! This may flush notifications via synchronous
195 // ScrollSelectionIntoView.
196 rv = SearchInFrame(searchFrame, true, aResult);
197 if (NS_FAILED(rv)) {
198 return rv;
199 }
200 if (*aResult) {
201 return OnFind(searchFrame); // we are done
202 }
203 break;
204 }
205
206 OnStartSearchFrame(searchFrame);
207
208 // Beware! This may flush notifications via synchronous
209 // ScrollSelectionIntoView.
210 rv = SearchInFrame(searchFrame, false, aResult);
211 if (NS_FAILED(rv)) {
212 return rv;
213 }
214 if (*aResult) {
215 return OnFind(searchFrame); // we are done
216 }
217
218 OnEndSearchFrame(searchFrame);
219 }
220
221 // remember where we left off
222 SetCurrentSearchFrame(searchFrame);
223
224 NS_ASSERTION(NS_SUCCEEDED(rv), "Something failed");
225 return rv;
226 }
227
228 NS_IMETHODIMP
GetSearchString(nsAString & aSearchString)229 nsWebBrowserFind::GetSearchString(nsAString& aSearchString) {
230 aSearchString = mSearchString;
231 return NS_OK;
232 }
233
234 NS_IMETHODIMP
SetSearchString(const nsAString & aSearchString)235 nsWebBrowserFind::SetSearchString(const nsAString& aSearchString) {
236 mSearchString = aSearchString;
237 return NS_OK;
238 }
239
240 NS_IMETHODIMP
GetFindBackwards(bool * aFindBackwards)241 nsWebBrowserFind::GetFindBackwards(bool* aFindBackwards) {
242 NS_ENSURE_ARG_POINTER(aFindBackwards);
243 *aFindBackwards = mFindBackwards;
244 return NS_OK;
245 }
246
247 NS_IMETHODIMP
SetFindBackwards(bool aFindBackwards)248 nsWebBrowserFind::SetFindBackwards(bool aFindBackwards) {
249 mFindBackwards = aFindBackwards;
250 return NS_OK;
251 }
252
253 NS_IMETHODIMP
GetWrapFind(bool * aWrapFind)254 nsWebBrowserFind::GetWrapFind(bool* aWrapFind) {
255 NS_ENSURE_ARG_POINTER(aWrapFind);
256 *aWrapFind = mWrapFind;
257 return NS_OK;
258 }
259
260 NS_IMETHODIMP
SetWrapFind(bool aWrapFind)261 nsWebBrowserFind::SetWrapFind(bool aWrapFind) {
262 mWrapFind = aWrapFind;
263 return NS_OK;
264 }
265
266 NS_IMETHODIMP
GetEntireWord(bool * aEntireWord)267 nsWebBrowserFind::GetEntireWord(bool* aEntireWord) {
268 NS_ENSURE_ARG_POINTER(aEntireWord);
269 *aEntireWord = mEntireWord;
270 return NS_OK;
271 }
272
273 NS_IMETHODIMP
SetEntireWord(bool aEntireWord)274 nsWebBrowserFind::SetEntireWord(bool aEntireWord) {
275 mEntireWord = aEntireWord;
276 return NS_OK;
277 }
278
279 NS_IMETHODIMP
GetMatchCase(bool * aMatchCase)280 nsWebBrowserFind::GetMatchCase(bool* aMatchCase) {
281 NS_ENSURE_ARG_POINTER(aMatchCase);
282 *aMatchCase = mMatchCase;
283 return NS_OK;
284 }
285
286 NS_IMETHODIMP
SetMatchCase(bool aMatchCase)287 nsWebBrowserFind::SetMatchCase(bool aMatchCase) {
288 mMatchCase = aMatchCase;
289 return NS_OK;
290 }
291
292 NS_IMETHODIMP
GetMatchDiacritics(bool * aMatchDiacritics)293 nsWebBrowserFind::GetMatchDiacritics(bool* aMatchDiacritics) {
294 NS_ENSURE_ARG_POINTER(aMatchDiacritics);
295 *aMatchDiacritics = mMatchDiacritics;
296 return NS_OK;
297 }
298
299 NS_IMETHODIMP
SetMatchDiacritics(bool aMatchDiacritics)300 nsWebBrowserFind::SetMatchDiacritics(bool aMatchDiacritics) {
301 mMatchDiacritics = aMatchDiacritics;
302 return NS_OK;
303 }
304
SetSelectionAndScroll(nsPIDOMWindowOuter * aWindow,nsRange * aRange)305 void nsWebBrowserFind::SetSelectionAndScroll(nsPIDOMWindowOuter* aWindow,
306 nsRange* aRange) {
307 RefPtr<Document> doc = aWindow->GetDoc();
308 if (!doc) {
309 return;
310 }
311
312 PresShell* presShell = doc->GetPresShell();
313 if (!presShell) {
314 return;
315 }
316
317 nsCOMPtr<nsINode> node = aRange->GetStartContainer();
318 nsCOMPtr<nsIContent> content(do_QueryInterface(node));
319 nsIFrame* frame = content->GetPrimaryFrame();
320 if (!frame) {
321 return;
322 }
323 nsCOMPtr<nsISelectionController> selCon;
324 frame->GetSelectionController(presShell->GetPresContext(),
325 getter_AddRefs(selCon));
326
327 // since the match could be an anonymous textnode inside a
328 // <textarea> or text <input>, we need to get the outer frame
329 nsITextControlFrame* tcFrame = nullptr;
330 for (; content; content = content->GetParent()) {
331 if (!content->IsInNativeAnonymousSubtree()) {
332 nsIFrame* f = content->GetPrimaryFrame();
333 if (!f) {
334 return;
335 }
336 tcFrame = do_QueryFrame(f);
337 break;
338 }
339 }
340
341 selCon->SetDisplaySelection(nsISelectionController::SELECTION_ON);
342 RefPtr<Selection> selection =
343 selCon->GetSelection(nsISelectionController::SELECTION_NORMAL);
344 if (selection) {
345 selection->RemoveAllRanges(IgnoreErrors());
346 selection->AddRangeAndSelectFramesAndNotifyListeners(*aRange,
347 IgnoreErrors());
348
349 if (RefPtr<nsFocusManager> fm = nsFocusManager::GetFocusManager()) {
350 if (tcFrame) {
351 RefPtr<Element> newFocusedElement = Element::FromNode(content);
352 fm->SetFocus(newFocusedElement, nsIFocusManager::FLAG_NOSCROLL);
353 } else {
354 RefPtr<Element> result;
355 fm->MoveFocus(aWindow, nullptr, nsIFocusManager::MOVEFOCUS_CARET,
356 nsIFocusManager::FLAG_NOSCROLL, getter_AddRefs(result));
357 }
358 }
359
360 // Scroll if necessary to make the selection visible:
361 // Must be the last thing to do - bug 242056
362
363 // After ScrollSelectionIntoView(), the pending notifications might be
364 // flushed and PresShell/PresContext/Frames may be dead. See bug 418470.
365 selCon->ScrollSelectionIntoView(
366 nsISelectionController::SELECTION_NORMAL,
367 nsISelectionController::SELECTION_WHOLE_SELECTION,
368 nsISelectionController::SCROLL_CENTER_VERTICALLY |
369 nsISelectionController::SCROLL_SYNCHRONOUS);
370 }
371 }
372
373 // Adapted from TextServicesDocument::GetDocumentContentRootNode
GetRootNode(Document * aDoc,Element ** aNode)374 nsresult nsWebBrowserFind::GetRootNode(Document* aDoc, Element** aNode) {
375 NS_ENSURE_ARG_POINTER(aDoc);
376 NS_ENSURE_ARG_POINTER(aNode);
377 *aNode = 0;
378
379 if (aDoc->IsHTMLOrXHTML()) {
380 Element* body = aDoc->GetBody();
381 NS_ENSURE_ARG_POINTER(body);
382 NS_ADDREF(*aNode = body);
383 return NS_OK;
384 }
385
386 // For non-HTML documents, the content root node will be the doc element.
387 Element* root = aDoc->GetDocumentElement();
388 NS_ENSURE_ARG_POINTER(root);
389 NS_ADDREF(*aNode = root);
390 return NS_OK;
391 }
392
SetRangeAroundDocument(nsRange * aSearchRange,nsRange * aStartPt,nsRange * aEndPt,Document * aDoc)393 nsresult nsWebBrowserFind::SetRangeAroundDocument(nsRange* aSearchRange,
394 nsRange* aStartPt,
395 nsRange* aEndPt,
396 Document* aDoc) {
397 RefPtr<Element> bodyContent;
398 nsresult rv = GetRootNode(aDoc, getter_AddRefs(bodyContent));
399 NS_ENSURE_SUCCESS(rv, rv);
400 NS_ENSURE_ARG_POINTER(bodyContent);
401
402 uint32_t childCount = bodyContent->GetChildCount();
403
404 aSearchRange->SetStart(*bodyContent, 0, IgnoreErrors());
405 aSearchRange->SetEnd(*bodyContent, childCount, IgnoreErrors());
406
407 if (mFindBackwards) {
408 aStartPt->SetStart(*bodyContent, childCount, IgnoreErrors());
409 aStartPt->SetEnd(*bodyContent, childCount, IgnoreErrors());
410 aEndPt->SetStart(*bodyContent, 0, IgnoreErrors());
411 aEndPt->SetEnd(*bodyContent, 0, IgnoreErrors());
412 } else {
413 aStartPt->SetStart(*bodyContent, 0, IgnoreErrors());
414 aStartPt->SetEnd(*bodyContent, 0, IgnoreErrors());
415 aEndPt->SetStart(*bodyContent, childCount, IgnoreErrors());
416 aEndPt->SetEnd(*bodyContent, childCount, IgnoreErrors());
417 }
418
419 return NS_OK;
420 }
421
422 // Set the range to go from the end of the current selection to the end of the
423 // document (forward), or beginning to beginning (reverse). or around the whole
424 // document if there's no selection.
GetSearchLimits(nsRange * aSearchRange,nsRange * aStartPt,nsRange * aEndPt,Document * aDoc,Selection * aSel,bool aWrap)425 nsresult nsWebBrowserFind::GetSearchLimits(nsRange* aSearchRange,
426 nsRange* aStartPt, nsRange* aEndPt,
427 Document* aDoc, Selection* aSel,
428 bool aWrap) {
429 NS_ENSURE_ARG_POINTER(aSel);
430
431 // There is a selection.
432 uint32_t count = aSel->RangeCount();
433 if (count < 1) {
434 return SetRangeAroundDocument(aSearchRange, aStartPt, aEndPt, aDoc);
435 }
436
437 // Need bodyContent, for the start/end of the document
438 RefPtr<Element> bodyContent;
439 nsresult rv = GetRootNode(aDoc, getter_AddRefs(bodyContent));
440 NS_ENSURE_SUCCESS(rv, rv);
441 NS_ENSURE_ARG_POINTER(bodyContent);
442
443 uint32_t childCount = bodyContent->GetChildCount();
444
445 // There are four possible range endpoints we might use:
446 // DocumentStart, SelectionStart, SelectionEnd, DocumentEnd.
447
448 RefPtr<const nsRange> range;
449 nsCOMPtr<nsINode> node;
450 uint32_t offset;
451
452 // Prevent the security checks in nsRange from getting into effect for the
453 // purposes of determining the search range. These ranges will never be
454 // exposed to content.
455 mozilla::dom::AutoNoJSAPI nojsapi;
456
457 // Forward, not wrapping: SelEnd to DocEnd
458 if (!mFindBackwards && !aWrap) {
459 // This isn't quite right, since the selection's ranges aren't
460 // necessarily in order; but they usually will be.
461 range = aSel->GetRangeAt(count - 1);
462 if (!range) {
463 return NS_ERROR_UNEXPECTED;
464 }
465 node = range->GetEndContainer();
466 if (!node) {
467 return NS_ERROR_UNEXPECTED;
468 }
469 offset = range->EndOffset();
470
471 aSearchRange->SetStart(*node, offset, IgnoreErrors());
472 aSearchRange->SetEnd(*bodyContent, childCount, IgnoreErrors());
473 aStartPt->SetStart(*node, offset, IgnoreErrors());
474 aStartPt->SetEnd(*node, offset, IgnoreErrors());
475 aEndPt->SetStart(*bodyContent, childCount, IgnoreErrors());
476 aEndPt->SetEnd(*bodyContent, childCount, IgnoreErrors());
477 }
478 // Backward, not wrapping: DocStart to SelStart
479 else if (mFindBackwards && !aWrap) {
480 range = aSel->GetRangeAt(0);
481 if (!range) {
482 return NS_ERROR_UNEXPECTED;
483 }
484 node = range->GetStartContainer();
485 if (!node) {
486 return NS_ERROR_UNEXPECTED;
487 }
488 offset = range->StartOffset();
489
490 aSearchRange->SetStart(*bodyContent, 0, IgnoreErrors());
491 aSearchRange->SetEnd(*bodyContent, childCount, IgnoreErrors());
492 aStartPt->SetStart(*node, offset, IgnoreErrors());
493 aStartPt->SetEnd(*node, offset, IgnoreErrors());
494 aEndPt->SetStart(*bodyContent, 0, IgnoreErrors());
495 aEndPt->SetEnd(*bodyContent, 0, IgnoreErrors());
496 }
497 // Forward, wrapping: DocStart to SelEnd
498 else if (!mFindBackwards && aWrap) {
499 range = aSel->GetRangeAt(count - 1);
500 if (!range) {
501 return NS_ERROR_UNEXPECTED;
502 }
503 node = range->GetEndContainer();
504 if (!node) {
505 return NS_ERROR_UNEXPECTED;
506 }
507 offset = range->EndOffset();
508
509 aSearchRange->SetStart(*bodyContent, 0, IgnoreErrors());
510 aSearchRange->SetEnd(*bodyContent, childCount, IgnoreErrors());
511 aStartPt->SetStart(*bodyContent, 0, IgnoreErrors());
512 aStartPt->SetEnd(*bodyContent, 0, IgnoreErrors());
513 aEndPt->SetStart(*node, offset, IgnoreErrors());
514 aEndPt->SetEnd(*node, offset, IgnoreErrors());
515 }
516 // Backward, wrapping: SelStart to DocEnd
517 else if (mFindBackwards && aWrap) {
518 range = aSel->GetRangeAt(0);
519 if (!range) {
520 return NS_ERROR_UNEXPECTED;
521 }
522 node = range->GetStartContainer();
523 if (!node) {
524 return NS_ERROR_UNEXPECTED;
525 }
526 offset = range->StartOffset();
527
528 aSearchRange->SetStart(*bodyContent, 0, IgnoreErrors());
529 aSearchRange->SetEnd(*bodyContent, childCount, IgnoreErrors());
530 aStartPt->SetStart(*bodyContent, childCount, IgnoreErrors());
531 aStartPt->SetEnd(*bodyContent, childCount, IgnoreErrors());
532 aEndPt->SetStart(*node, offset, IgnoreErrors());
533 aEndPt->SetEnd(*node, offset, IgnoreErrors());
534 }
535 return NS_OK;
536 }
537
538 NS_IMETHODIMP
GetSearchFrames(bool * aSearchFrames)539 nsWebBrowserFind::GetSearchFrames(bool* aSearchFrames) {
540 NS_ENSURE_ARG_POINTER(aSearchFrames);
541 // this only returns true if we are searching both sub and parent frames.
542 // There is ambiguity if the caller has previously set one, but not both of
543 // these.
544 *aSearchFrames = mSearchSubFrames && mSearchParentFrames;
545 return NS_OK;
546 }
547
548 NS_IMETHODIMP
SetSearchFrames(bool aSearchFrames)549 nsWebBrowserFind::SetSearchFrames(bool aSearchFrames) {
550 mSearchSubFrames = aSearchFrames;
551 mSearchParentFrames = aSearchFrames;
552 return NS_OK;
553 }
554
555 NS_IMETHODIMP
GetCurrentSearchFrame(mozIDOMWindowProxy ** aCurrentSearchFrame)556 nsWebBrowserFind::GetCurrentSearchFrame(
557 mozIDOMWindowProxy** aCurrentSearchFrame) {
558 NS_ENSURE_ARG_POINTER(aCurrentSearchFrame);
559 nsCOMPtr<mozIDOMWindowProxy> searchFrame =
560 do_QueryReferent(mCurrentSearchFrame);
561 searchFrame.forget(aCurrentSearchFrame);
562 return (*aCurrentSearchFrame) ? NS_OK : NS_ERROR_NOT_INITIALIZED;
563 }
564
565 NS_IMETHODIMP
SetCurrentSearchFrame(mozIDOMWindowProxy * aCurrentSearchFrame)566 nsWebBrowserFind::SetCurrentSearchFrame(
567 mozIDOMWindowProxy* aCurrentSearchFrame) {
568 // is it ever valid to set this to null?
569 NS_ENSURE_ARG(aCurrentSearchFrame);
570 mCurrentSearchFrame = do_GetWeakReference(aCurrentSearchFrame);
571 return NS_OK;
572 }
573
574 NS_IMETHODIMP
GetRootSearchFrame(mozIDOMWindowProxy ** aRootSearchFrame)575 nsWebBrowserFind::GetRootSearchFrame(mozIDOMWindowProxy** aRootSearchFrame) {
576 NS_ENSURE_ARG_POINTER(aRootSearchFrame);
577 nsCOMPtr<mozIDOMWindowProxy> searchFrame = do_QueryReferent(mRootSearchFrame);
578 searchFrame.forget(aRootSearchFrame);
579 return (*aRootSearchFrame) ? NS_OK : NS_ERROR_NOT_INITIALIZED;
580 }
581
582 NS_IMETHODIMP
SetRootSearchFrame(mozIDOMWindowProxy * aRootSearchFrame)583 nsWebBrowserFind::SetRootSearchFrame(mozIDOMWindowProxy* aRootSearchFrame) {
584 // is it ever valid to set this to null?
585 NS_ENSURE_ARG(aRootSearchFrame);
586 mRootSearchFrame = do_GetWeakReference(aRootSearchFrame);
587 return NS_OK;
588 }
589
590 NS_IMETHODIMP
GetSearchSubframes(bool * aSearchSubframes)591 nsWebBrowserFind::GetSearchSubframes(bool* aSearchSubframes) {
592 NS_ENSURE_ARG_POINTER(aSearchSubframes);
593 *aSearchSubframes = mSearchSubFrames;
594 return NS_OK;
595 }
596
597 NS_IMETHODIMP
SetSearchSubframes(bool aSearchSubframes)598 nsWebBrowserFind::SetSearchSubframes(bool aSearchSubframes) {
599 mSearchSubFrames = aSearchSubframes;
600 return NS_OK;
601 }
602
603 NS_IMETHODIMP
GetSearchParentFrames(bool * aSearchParentFrames)604 nsWebBrowserFind::GetSearchParentFrames(bool* aSearchParentFrames) {
605 NS_ENSURE_ARG_POINTER(aSearchParentFrames);
606 *aSearchParentFrames = mSearchParentFrames;
607 return NS_OK;
608 }
609
610 NS_IMETHODIMP
SetSearchParentFrames(bool aSearchParentFrames)611 nsWebBrowserFind::SetSearchParentFrames(bool aSearchParentFrames) {
612 mSearchParentFrames = aSearchParentFrames;
613 return NS_OK;
614 }
615
616 /*
617 This method handles finding in a single window (aka frame).
618
619 */
SearchInFrame(nsPIDOMWindowOuter * aWindow,bool aWrapping,bool * aDidFind)620 nsresult nsWebBrowserFind::SearchInFrame(nsPIDOMWindowOuter* aWindow,
621 bool aWrapping, bool* aDidFind) {
622 NS_ENSURE_ARG(aWindow);
623 NS_ENSURE_ARG_POINTER(aDidFind);
624
625 *aDidFind = false;
626
627 // Do security check, to ensure that the frame we're searching is
628 // accessible from the frame where the Find is being run.
629
630 // get a uri for the window
631 RefPtr<Document> theDoc = aWindow->GetDoc();
632 if (!theDoc) {
633 return NS_ERROR_FAILURE;
634 }
635
636 if (!nsContentUtils::SubjectPrincipal()->Subsumes(theDoc->NodePrincipal())) {
637 return NS_ERROR_DOM_PROP_ACCESS_DENIED;
638 }
639
640 nsresult rv;
641 nsCOMPtr<nsIFind> find = do_CreateInstance(NS_FIND_CONTRACTID, &rv);
642 NS_ENSURE_SUCCESS(rv, rv);
643
644 (void)find->SetCaseSensitive(mMatchCase);
645 (void)find->SetMatchDiacritics(mMatchDiacritics);
646 (void)find->SetFindBackwards(mFindBackwards);
647
648 (void)find->SetEntireWord(mEntireWord);
649
650 // Now make sure the content (for actual finding) and frame (for
651 // selection) models are up to date.
652 theDoc->FlushPendingNotifications(FlushType::Frames);
653
654 RefPtr<Selection> sel = GetFrameSelection(aWindow);
655 NS_ENSURE_ARG_POINTER(sel);
656
657 RefPtr<nsRange> searchRange = nsRange::Create(theDoc);
658 RefPtr<nsRange> startPt = nsRange::Create(theDoc);
659 RefPtr<nsRange> endPt = nsRange::Create(theDoc);
660
661 RefPtr<nsRange> foundRange;
662
663 rv = GetSearchLimits(searchRange, startPt, endPt, theDoc, sel, aWrapping);
664 NS_ENSURE_SUCCESS(rv, rv);
665
666 rv = find->Find(mSearchString, searchRange, startPt, endPt,
667 getter_AddRefs(foundRange));
668
669 if (NS_SUCCEEDED(rv) && foundRange) {
670 *aDidFind = true;
671 sel->RemoveAllRanges(IgnoreErrors());
672 // Beware! This may flush notifications via synchronous
673 // ScrollSelectionIntoView.
674 SetSelectionAndScroll(aWindow, foundRange);
675 }
676
677 return rv;
678 }
679
680 // called when we start searching a frame that is not the initial focussed
681 // frame. Prepare the frame to be searched. we clear the selection, so that the
682 // search starts from the top of the frame.
OnStartSearchFrame(nsPIDOMWindowOuter * aWindow)683 nsresult nsWebBrowserFind::OnStartSearchFrame(nsPIDOMWindowOuter* aWindow) {
684 return ClearFrameSelection(aWindow);
685 }
686
687 // called when we are done searching a frame and didn't find anything, and about
688 // about to start searching the next frame.
OnEndSearchFrame(nsPIDOMWindowOuter * aWindow)689 nsresult nsWebBrowserFind::OnEndSearchFrame(nsPIDOMWindowOuter* aWindow) {
690 return NS_OK;
691 }
692
GetFrameSelection(nsPIDOMWindowOuter * aWindow)693 already_AddRefed<Selection> nsWebBrowserFind::GetFrameSelection(
694 nsPIDOMWindowOuter* aWindow) {
695 RefPtr<Document> doc = aWindow->GetDoc();
696 if (!doc) {
697 return nullptr;
698 }
699
700 PresShell* presShell = doc->GetPresShell();
701 if (!presShell) {
702 return nullptr;
703 }
704
705 // text input controls have their independent selection controllers that we
706 // must use when they have focus.
707 nsPresContext* presContext = presShell->GetPresContext();
708
709 nsCOMPtr<nsPIDOMWindowOuter> focusedWindow;
710 nsCOMPtr<nsIContent> focusedContent = nsFocusManager::GetFocusedDescendant(
711 aWindow, nsFocusManager::eOnlyCurrentWindow,
712 getter_AddRefs(focusedWindow));
713
714 nsIFrame* frame =
715 focusedContent ? focusedContent->GetPrimaryFrame() : nullptr;
716
717 nsCOMPtr<nsISelectionController> selCon;
718 RefPtr<Selection> sel;
719 if (frame) {
720 frame->GetSelectionController(presContext, getter_AddRefs(selCon));
721 sel = selCon->GetSelection(nsISelectionController::SELECTION_NORMAL);
722 if (sel && sel->RangeCount() > 0) {
723 return sel.forget();
724 }
725 }
726
727 sel = presShell->GetSelection(nsISelectionController::SELECTION_NORMAL);
728 return sel.forget();
729 }
730
ClearFrameSelection(nsPIDOMWindowOuter * aWindow)731 nsresult nsWebBrowserFind::ClearFrameSelection(nsPIDOMWindowOuter* aWindow) {
732 NS_ENSURE_ARG(aWindow);
733 RefPtr<Selection> selection = GetFrameSelection(aWindow);
734 if (selection) {
735 selection->RemoveAllRanges(IgnoreErrors());
736 }
737
738 return NS_OK;
739 }
740
OnFind(nsPIDOMWindowOuter * aFoundWindow)741 nsresult nsWebBrowserFind::OnFind(nsPIDOMWindowOuter* aFoundWindow) {
742 SetCurrentSearchFrame(aFoundWindow);
743
744 // We don't want a selection to appear in two frames simultaneously
745 nsCOMPtr<nsPIDOMWindowOuter> lastFocusedWindow =
746 do_QueryReferent(mLastFocusedWindow);
747 if (lastFocusedWindow && lastFocusedWindow != aFoundWindow) {
748 ClearFrameSelection(lastFocusedWindow);
749 }
750
751 if (RefPtr<nsFocusManager> fm = nsFocusManager::GetFocusManager()) {
752 // get the containing frame and focus it. For top-level windows, the right
753 // window should already be focused.
754 if (RefPtr<Element> frameElement =
755 aFoundWindow->GetFrameElementInternal()) {
756 fm->SetFocus(frameElement, 0);
757 }
758
759 mLastFocusedWindow = do_GetWeakReference(aFoundWindow);
760 }
761
762 return NS_OK;
763 }
764