Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 *
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/. */
7 #include "nsWebBrowserFind.h"
9 // Only need this for NS_FIND_CONTRACTID,
10 // else we could use nsIDOMRange.h and nsIFind.h.
11 #include "nsFind.h"
13 #include "nsIComponentManager.h"
14 #include "nsIScriptSecurityManager.h"
15 #include "nsIInterfaceRequestor.h"
16 #include "nsIInterfaceRequestorUtils.h"
17 #include "nsPIDOMWindow.h"
18 #include "nsIURI.h"
19 #include "nsIDocShell.h"
20 #include "nsIPresShell.h"
21 #include "nsPresContext.h"
22 #include "nsIDocument.h"
23 #include "nsIDOMDocument.h"
24 #include "nsISelectionController.h"
25 #include "nsISelection.h"
26 #include "nsIFrame.h"
27 #include "nsITextControlFrame.h"
28 #include "nsReadableUtils.h"
29 #include "nsIDOMHTMLElement.h"
30 #include "nsIDOMHTMLDocument.h"
31 #include "nsIContent.h"
32 #include "nsContentCID.h"
33 #include "nsIServiceManager.h"
34 #include "nsIObserverService.h"
35 #include "nsISupportsPrimitives.h"
36 #include "nsFind.h"
37 #include "nsError.h"
38 #include "nsFocusManager.h"
39 #include "mozilla/Services.h"
40 #include "mozilla/dom/Element.h"
41 #include "nsISimpleEnumerator.h"
43 #if DEBUG
44 #include "nsIWebNavigation.h"
45 #include "nsXPIDLString.h"
46 #endif
48 //*****************************************************************************
49 // nsWebBrowserFind
50 //*****************************************************************************
52 nsWebBrowserFind::nsWebBrowserFind() :
53 mFindBackwards(false),
54 mWrapFind(false),
55 mEntireWord(false),
56 mMatchCase(false),
57 mSearchSubFrames(true),
58 mSearchParentFrames(true)
59 {
60 }
62 nsWebBrowserFind::~nsWebBrowserFind()
63 {
64 }
66 NS_IMPL_ISUPPORTS(nsWebBrowserFind, nsIWebBrowserFind, nsIWebBrowserFindInFrames)
69 /* boolean findNext (); */
70 NS_IMETHODIMP nsWebBrowserFind::FindNext(bool *outDidFind)
71 {
72 NS_ENSURE_ARG_POINTER(outDidFind);
73 *outDidFind = false;
75 NS_ENSURE_TRUE(CanFindNext(), NS_ERROR_NOT_INITIALIZED);
77 nsresult rv = NS_OK;
78 nsCOMPtr<nsIDOMWindow> searchFrame = do_QueryReferent(mCurrentSearchFrame);
79 NS_ENSURE_TRUE(searchFrame, NS_ERROR_NOT_INITIALIZED);
81 nsCOMPtr<nsIDOMWindow> rootFrame = do_QueryReferent(mRootSearchFrame);
82 NS_ENSURE_TRUE(rootFrame, NS_ERROR_NOT_INITIALIZED);
84 // first, if there's a "cmd_findagain" observer around, check to see if it
85 // wants to perform the find again command . If it performs the find again
86 // it will return true, in which case we exit ::FindNext() early.
87 // Otherwise, nsWebBrowserFind needs to perform the find again command itself
88 // this is used by nsTypeAheadFind, which controls find again when it was
89 // the last executed find in the current window.
90 nsCOMPtr<nsIObserverService> observerSvc =
91 mozilla::services::GetObserverService();
92 if (observerSvc) {
93 nsCOMPtr<nsISupportsInterfacePointer> windowSupportsData =
94 do_CreateInstance(NS_SUPPORTS_INTERFACE_POINTER_CONTRACTID, &rv);
95 NS_ENSURE_SUCCESS(rv, rv);
96 nsCOMPtr<nsISupports> searchWindowSupports =
97 do_QueryInterface(rootFrame);
98 windowSupportsData->SetData(searchWindowSupports);
99 NS_NAMED_LITERAL_STRING(dnStr, "down");
100 NS_NAMED_LITERAL_STRING(upStr, "up");
101 observerSvc->NotifyObservers(windowSupportsData,
102 "nsWebBrowserFind_FindAgain",
103 mFindBackwards? upStr.get(): dnStr.get());
104 windowSupportsData->GetData(getter_AddRefs(searchWindowSupports));
105 // findnext performed if search window data cleared out
106 *outDidFind = searchWindowSupports == nullptr;
107 if (*outDidFind)
108 return NS_OK;
109 }
111 // next, look in the current frame. If found, return.
113 // Beware! This may flush notifications via synchronous
114 // ScrollSelectionIntoView.
115 rv = SearchInFrame(searchFrame, false, outDidFind);
116 if (NS_FAILED(rv)) return rv;
117 if (*outDidFind)
118 return OnFind(searchFrame); // we are done
120 // if we are not searching other frames, return
121 if (!mSearchSubFrames && !mSearchParentFrames)
122 return NS_OK;
124 nsIDocShell *rootDocShell = GetDocShellFromWindow(rootFrame);
125 if (!rootDocShell) return NS_ERROR_FAILURE;
127 int32_t enumDirection;
128 if (mFindBackwards)
129 enumDirection = nsIDocShell::ENUMERATE_BACKWARDS;
130 else
131 enumDirection = nsIDocShell::ENUMERATE_FORWARDS;
133 nsCOMPtr<nsISimpleEnumerator> docShellEnumerator;
134 rv = rootDocShell->GetDocShellEnumerator(nsIDocShellTreeItem::typeAll,
135 enumDirection, getter_AddRefs(docShellEnumerator));
136 if (NS_FAILED(rv)) return rv;
138 // remember where we started
139 nsCOMPtr<nsIDocShellTreeItem> startingItem =
140 do_QueryInterface(GetDocShellFromWindow(searchFrame), &rv);
141 if (NS_FAILED(rv)) return rv;
143 nsCOMPtr<nsIDocShellTreeItem> curItem;
145 // XXX We should avoid searching in frameset documents here.
146 // We also need to honour mSearchSubFrames and mSearchParentFrames.
147 bool hasMore, doFind = false;
148 while (NS_SUCCEEDED(docShellEnumerator->HasMoreElements(&hasMore)) && hasMore)
149 {
150 nsCOMPtr<nsISupports> curSupports;
151 rv = docShellEnumerator->GetNext(getter_AddRefs(curSupports));
152 if (NS_FAILED(rv)) break;
153 curItem = do_QueryInterface(curSupports, &rv);
154 if (NS_FAILED(rv)) break;
156 if (doFind)
157 {
158 searchFrame = do_GetInterface(curItem, &rv);
159 if (NS_FAILED(rv)) break;
161 OnStartSearchFrame(searchFrame);
163 // Beware! This may flush notifications via synchronous
164 // ScrollSelectionIntoView.
165 rv = SearchInFrame(searchFrame, false, outDidFind);
166 if (NS_FAILED(rv)) return rv;
167 if (*outDidFind)
168 return OnFind(searchFrame); // we are done
170 OnEndSearchFrame(searchFrame);
171 }
173 if (curItem.get() == startingItem.get())
174 doFind = true; // start looking in frames after this one
175 };
177 if (!mWrapFind)
178 {
179 // remember where we left off
180 SetCurrentSearchFrame(searchFrame);
181 return NS_OK;
182 }
184 // From here on, we're wrapping, first through the other frames,
185 // then finally from the beginning of the starting frame back to
186 // the starting point.
188 // because nsISimpleEnumerator is totally lame and isn't resettable, I
189 // have to make a new one
190 docShellEnumerator = nullptr;
191 rv = rootDocShell->GetDocShellEnumerator(nsIDocShellTreeItem::typeAll,
192 enumDirection, getter_AddRefs(docShellEnumerator));
193 if (NS_FAILED(rv)) return rv;
195 while (NS_SUCCEEDED(docShellEnumerator->HasMoreElements(&hasMore)) && hasMore)
196 {
197 nsCOMPtr<nsISupports> curSupports;
198 rv = docShellEnumerator->GetNext(getter_AddRefs(curSupports));
199 if (NS_FAILED(rv)) break;
200 curItem = do_QueryInterface(curSupports, &rv);
201 if (NS_FAILED(rv)) break;
203 searchFrame = do_GetInterface(curItem, &rv);
204 if (NS_FAILED(rv)) break;
206 if (curItem.get() == startingItem.get())
207 {
208 // Beware! This may flush notifications via synchronous
209 // ScrollSelectionIntoView.
210 rv = SearchInFrame(searchFrame, true, outDidFind);
211 if (NS_FAILED(rv)) return rv;
212 if (*outDidFind)
213 return OnFind(searchFrame); // we are done
214 break;
215 }
217 OnStartSearchFrame(searchFrame);
219 // Beware! This may flush notifications via synchronous
220 // ScrollSelectionIntoView.
221 rv = SearchInFrame(searchFrame, false, outDidFind);
222 if (NS_FAILED(rv)) return rv;
223 if (*outDidFind)
224 return OnFind(searchFrame); // we are done
226 OnEndSearchFrame(searchFrame);
227 }
229 // remember where we left off
230 SetCurrentSearchFrame(searchFrame);
232 NS_ASSERTION(NS_SUCCEEDED(rv), "Something failed");
233 return rv;
234 }
237 /* attribute wstring searchString; */
238 NS_IMETHODIMP nsWebBrowserFind::GetSearchString(char16_t * *aSearchString)
239 {
240 NS_ENSURE_ARG_POINTER(aSearchString);
241 *aSearchString = ToNewUnicode(mSearchString);
242 return NS_OK;
243 }
245 NS_IMETHODIMP nsWebBrowserFind::SetSearchString(const char16_t * aSearchString)
246 {
247 mSearchString.Assign(aSearchString);
248 return NS_OK;
249 }
251 /* attribute boolean findBackwards; */
252 NS_IMETHODIMP nsWebBrowserFind::GetFindBackwards(bool *aFindBackwards)
253 {
254 NS_ENSURE_ARG_POINTER(aFindBackwards);
255 *aFindBackwards = mFindBackwards;
256 return NS_OK;
257 }
259 NS_IMETHODIMP nsWebBrowserFind::SetFindBackwards(bool aFindBackwards)
260 {
261 mFindBackwards = aFindBackwards;
262 return NS_OK;
263 }
265 /* attribute boolean wrapFind; */
266 NS_IMETHODIMP nsWebBrowserFind::GetWrapFind(bool *aWrapFind)
267 {
268 NS_ENSURE_ARG_POINTER(aWrapFind);
269 *aWrapFind = mWrapFind;
270 return NS_OK;
271 }
272 NS_IMETHODIMP nsWebBrowserFind::SetWrapFind(bool aWrapFind)
273 {
274 mWrapFind = aWrapFind;
275 return NS_OK;
276 }
278 /* attribute boolean entireWord; */
279 NS_IMETHODIMP nsWebBrowserFind::GetEntireWord(bool *aEntireWord)
280 {
281 NS_ENSURE_ARG_POINTER(aEntireWord);
282 *aEntireWord = mEntireWord;
283 return NS_OK;
284 }
285 NS_IMETHODIMP nsWebBrowserFind::SetEntireWord(bool aEntireWord)
286 {
287 mEntireWord = aEntireWord;
288 return NS_OK;
289 }
291 /* attribute boolean matchCase; */
292 NS_IMETHODIMP nsWebBrowserFind::GetMatchCase(bool *aMatchCase)
293 {
294 NS_ENSURE_ARG_POINTER(aMatchCase);
295 *aMatchCase = mMatchCase;
296 return NS_OK;
297 }
298 NS_IMETHODIMP nsWebBrowserFind::SetMatchCase(bool aMatchCase)
299 {
300 mMatchCase = aMatchCase;
301 return NS_OK;
302 }
304 static bool
305 IsInNativeAnonymousSubtree(nsIContent* aContent)
306 {
307 while (aContent) {
308 nsIContent* bindingParent = aContent->GetBindingParent();
309 if (bindingParent == aContent) {
310 return true;
311 }
313 aContent = bindingParent;
314 }
316 return false;
317 }
319 void nsWebBrowserFind::SetSelectionAndScroll(nsIDOMWindow* aWindow,
320 nsIDOMRange* aRange)
321 {
322 nsCOMPtr<nsIDOMDocument> domDoc;
323 aWindow->GetDocument(getter_AddRefs(domDoc));
324 if (!domDoc) return;
326 nsCOMPtr<nsIDocument> doc(do_QueryInterface(domDoc));
327 nsIPresShell* presShell = doc->GetShell();
328 if (!presShell) return;
330 nsCOMPtr<nsIDOMNode> node;
331 aRange->GetStartContainer(getter_AddRefs(node));
332 nsCOMPtr<nsIContent> content(do_QueryInterface(node));
333 nsIFrame* frame = content->GetPrimaryFrame();
334 if (!frame)
335 return;
336 nsCOMPtr<nsISelectionController> selCon;
337 frame->GetSelectionController(presShell->GetPresContext(),
338 getter_AddRefs(selCon));
340 // since the match could be an anonymous textnode inside a
341 // <textarea> or text <input>, we need to get the outer frame
342 nsITextControlFrame *tcFrame = nullptr;
343 for ( ; content; content = content->GetParent()) {
344 if (!IsInNativeAnonymousSubtree(content)) {
345 nsIFrame* f = content->GetPrimaryFrame();
346 if (!f)
347 return;
348 tcFrame = do_QueryFrame(f);
349 break;
350 }
351 }
353 nsCOMPtr<nsISelection> selection;
355 selCon->SetDisplaySelection(nsISelectionController::SELECTION_ON);
356 selCon->GetSelection(nsISelectionController::SELECTION_NORMAL,
357 getter_AddRefs(selection));
358 if (selection) {
359 selection->RemoveAllRanges();
360 selection->AddRange(aRange);
362 nsCOMPtr<nsIFocusManager> fm = do_GetService(FOCUSMANAGER_CONTRACTID);
363 if (fm) {
364 if (tcFrame) {
365 nsCOMPtr<nsIDOMElement> newFocusedElement(do_QueryInterface(content));
366 fm->SetFocus(newFocusedElement, nsIFocusManager::FLAG_NOSCROLL);
367 }
368 else {
369 nsCOMPtr<nsIDOMElement> result;
370 fm->MoveFocus(aWindow, nullptr, nsIFocusManager::MOVEFOCUS_CARET,
371 nsIFocusManager::FLAG_NOSCROLL,
372 getter_AddRefs(result));
373 }
374 }
376 // Scroll if necessary to make the selection visible:
377 // Must be the last thing to do - bug 242056
379 // After ScrollSelectionIntoView(), the pending notifications might be
380 // flushed and PresShell/PresContext/Frames may be dead. See bug 418470.
381 selCon->ScrollSelectionIntoView
382 (nsISelectionController::SELECTION_NORMAL,
383 nsISelectionController::SELECTION_WHOLE_SELECTION,
384 nsISelectionController::SCROLL_CENTER_VERTICALLY |
385 nsISelectionController::SCROLL_SYNCHRONOUS);
386 }
387 }
389 // Adapted from nsTextServicesDocument::GetDocumentContentRootNode
390 nsresult nsWebBrowserFind::GetRootNode(nsIDOMDocument* aDomDoc,
391 nsIDOMNode **aNode)
392 {
393 nsresult rv;
395 NS_ENSURE_ARG_POINTER(aNode);
396 *aNode = 0;
398 nsCOMPtr<nsIDOMHTMLDocument> htmlDoc = do_QueryInterface(aDomDoc);
399 if (htmlDoc)
400 {
401 // For HTML documents, the content root node is the body.
402 nsCOMPtr<nsIDOMHTMLElement> bodyElement;
403 rv = htmlDoc->GetBody(getter_AddRefs(bodyElement));
404 NS_ENSURE_SUCCESS(rv, rv);
405 NS_ENSURE_ARG_POINTER(bodyElement);
406 return bodyElement->QueryInterface(NS_GET_IID(nsIDOMNode),
407 (void **)aNode);
408 }
410 // For non-HTML documents, the content root node will be the doc element.
411 nsCOMPtr<nsIDOMElement> docElement;
412 rv = aDomDoc->GetDocumentElement(getter_AddRefs(docElement));
413 NS_ENSURE_SUCCESS(rv, rv);
414 NS_ENSURE_ARG_POINTER(docElement);
415 return docElement->QueryInterface(NS_GET_IID(nsIDOMNode), (void **)aNode);
416 }
418 nsresult nsWebBrowserFind::SetRangeAroundDocument(nsIDOMRange* aSearchRange,
419 nsIDOMRange* aStartPt,
420 nsIDOMRange* aEndPt,
421 nsIDOMDocument* aDoc)
422 {
423 nsCOMPtr<nsIDOMNode> bodyNode;
424 nsresult rv = GetRootNode(aDoc, getter_AddRefs(bodyNode));
425 nsCOMPtr<nsIContent> bodyContent (do_QueryInterface(bodyNode));
426 NS_ENSURE_SUCCESS(rv, rv);
427 NS_ENSURE_ARG_POINTER(bodyContent);
429 uint32_t childCount = bodyContent->GetChildCount();
431 aSearchRange->SetStart(bodyNode, 0);
432 aSearchRange->SetEnd(bodyNode, childCount);
434 if (mFindBackwards)
435 {
436 aStartPt->SetStart(bodyNode, childCount);
437 aStartPt->SetEnd(bodyNode, childCount);
438 aEndPt->SetStart(bodyNode, 0);
439 aEndPt->SetEnd(bodyNode, 0);
440 }
441 else
442 {
443 aStartPt->SetStart(bodyNode, 0);
444 aStartPt->SetEnd(bodyNode, 0);
445 aEndPt->SetStart(bodyNode, childCount);
446 aEndPt->SetEnd(bodyNode, childCount);
447 }
449 return NS_OK;
450 }
452 // Set the range to go from the end of the current selection to the
453 // end of the document (forward), or beginning to beginning (reverse).
454 // or around the whole document if there's no selection.
455 nsresult
456 nsWebBrowserFind::GetSearchLimits(nsIDOMRange* aSearchRange,
457 nsIDOMRange* aStartPt,
458 nsIDOMRange* aEndPt,
459 nsIDOMDocument* aDoc,
460 nsISelection* aSel,
461 bool aWrap)
462 {
463 NS_ENSURE_ARG_POINTER(aSel);
465 // There is a selection.
466 int32_t count = -1;
467 nsresult rv = aSel->GetRangeCount(&count);
468 NS_ENSURE_SUCCESS(rv, rv);
469 if (count < 1)
470 return SetRangeAroundDocument(aSearchRange, aStartPt, aEndPt, aDoc);
472 // Need bodyNode, for the start/end of the document
473 nsCOMPtr<nsIDOMNode> bodyNode;
474 rv = GetRootNode(aDoc, getter_AddRefs(bodyNode));
475 NS_ENSURE_SUCCESS(rv, rv);
477 nsCOMPtr<nsIContent> bodyContent (do_QueryInterface(bodyNode));
478 NS_ENSURE_ARG_POINTER(bodyContent);
480 uint32_t childCount = bodyContent->GetChildCount();
482 // There are four possible range endpoints we might use:
483 // DocumentStart, SelectionStart, SelectionEnd, DocumentEnd.
485 nsCOMPtr<nsIDOMRange> range;
486 nsCOMPtr<nsIDOMNode> node;
487 int32_t offset;
489 // Forward, not wrapping: SelEnd to DocEnd
490 if (!mFindBackwards && !aWrap)
491 {
492 // This isn't quite right, since the selection's ranges aren't
493 // necessarily in order; but they usually will be.
494 aSel->GetRangeAt(count-1, getter_AddRefs(range));
495 if (!range) return NS_ERROR_UNEXPECTED;
496 range->GetEndContainer(getter_AddRefs(node));
497 if (!node) return NS_ERROR_UNEXPECTED;
498 range->GetEndOffset(&offset);
500 aSearchRange->SetStart(node, offset);
501 aSearchRange->SetEnd(bodyNode, childCount);
502 aStartPt->SetStart(node, offset);
503 aStartPt->SetEnd(node, offset);
504 aEndPt->SetStart(bodyNode, childCount);
505 aEndPt->SetEnd(bodyNode, childCount);
506 }
507 // Backward, not wrapping: DocStart to SelStart
508 else if (mFindBackwards && !aWrap)
509 {
510 aSel->GetRangeAt(0, getter_AddRefs(range));
511 if (!range) return NS_ERROR_UNEXPECTED;
512 range->GetStartContainer(getter_AddRefs(node));
513 if (!node) return NS_ERROR_UNEXPECTED;
514 range->GetStartOffset(&offset);
516 aSearchRange->SetStart(bodyNode, 0);
517 aSearchRange->SetEnd(bodyNode, childCount);
518 aStartPt->SetStart(node, offset);
519 aStartPt->SetEnd(node, offset);
520 aEndPt->SetStart(bodyNode, 0);
521 aEndPt->SetEnd(bodyNode, 0);
522 }
523 // Forward, wrapping: DocStart to SelEnd
524 else if (!mFindBackwards && aWrap)
525 {
526 aSel->GetRangeAt(count-1, getter_AddRefs(range));
527 if (!range) return NS_ERROR_UNEXPECTED;
528 range->GetEndContainer(getter_AddRefs(node));
529 if (!node) return NS_ERROR_UNEXPECTED;
530 range->GetEndOffset(&offset);
532 aSearchRange->SetStart(bodyNode, 0);
533 aSearchRange->SetEnd(bodyNode, childCount);
534 aStartPt->SetStart(bodyNode, 0);
535 aStartPt->SetEnd(bodyNode, 0);
536 aEndPt->SetStart(node, offset);
537 aEndPt->SetEnd(node, offset);
538 }
539 // Backward, wrapping: SelStart to DocEnd
540 else if (mFindBackwards && aWrap)
541 {
542 aSel->GetRangeAt(0, getter_AddRefs(range));
543 if (!range) return NS_ERROR_UNEXPECTED;
544 range->GetStartContainer(getter_AddRefs(node));
545 if (!node) return NS_ERROR_UNEXPECTED;
546 range->GetStartOffset(&offset);
548 aSearchRange->SetStart(bodyNode, 0);
549 aSearchRange->SetEnd(bodyNode, childCount);
550 aStartPt->SetStart(bodyNode, childCount);
551 aStartPt->SetEnd(bodyNode, childCount);
552 aEndPt->SetStart(node, offset);
553 aEndPt->SetEnd(node, offset);
554 }
555 return NS_OK;
556 }
558 /* attribute boolean searchFrames; */
559 NS_IMETHODIMP nsWebBrowserFind::GetSearchFrames(bool *aSearchFrames)
560 {
561 NS_ENSURE_ARG_POINTER(aSearchFrames);
562 // this only returns true if we are searching both sub and parent
563 // frames. There is ambiguity if the caller has previously set
564 // one, but not both of these.
565 *aSearchFrames = mSearchSubFrames && mSearchParentFrames;
566 return NS_OK;
567 }
569 NS_IMETHODIMP nsWebBrowserFind::SetSearchFrames(bool aSearchFrames)
570 {
571 mSearchSubFrames = aSearchFrames;
572 mSearchParentFrames = aSearchFrames;
573 return NS_OK;
574 }
576 /* attribute nsIDOMWindow currentSearchFrame; */
577 NS_IMETHODIMP nsWebBrowserFind::GetCurrentSearchFrame(nsIDOMWindow * *aCurrentSearchFrame)
578 {
579 NS_ENSURE_ARG_POINTER(aCurrentSearchFrame);
580 nsCOMPtr<nsIDOMWindow> searchFrame = do_QueryReferent(mCurrentSearchFrame);
581 NS_IF_ADDREF(*aCurrentSearchFrame = searchFrame);
582 return (*aCurrentSearchFrame) ? NS_OK : NS_ERROR_NOT_INITIALIZED;
583 }
585 NS_IMETHODIMP nsWebBrowserFind::SetCurrentSearchFrame(nsIDOMWindow * aCurrentSearchFrame)
586 {
587 // is it ever valid to set this to null?
588 NS_ENSURE_ARG(aCurrentSearchFrame);
589 mCurrentSearchFrame = do_GetWeakReference(aCurrentSearchFrame);
590 return NS_OK;
591 }
593 /* attribute nsIDOMWindow rootSearchFrame; */
594 NS_IMETHODIMP nsWebBrowserFind::GetRootSearchFrame(nsIDOMWindow * *aRootSearchFrame)
595 {
596 NS_ENSURE_ARG_POINTER(aRootSearchFrame);
597 nsCOMPtr<nsIDOMWindow> searchFrame = do_QueryReferent(mRootSearchFrame);
598 NS_IF_ADDREF(*aRootSearchFrame = searchFrame);
599 return (*aRootSearchFrame) ? NS_OK : NS_ERROR_NOT_INITIALIZED;
600 }
602 NS_IMETHODIMP nsWebBrowserFind::SetRootSearchFrame(nsIDOMWindow * aRootSearchFrame)
603 {
604 // is it ever valid to set this to null?
605 NS_ENSURE_ARG(aRootSearchFrame);
606 mRootSearchFrame = do_GetWeakReference(aRootSearchFrame);
607 return NS_OK;
608 }
610 /* attribute boolean searchSubframes; */
611 NS_IMETHODIMP nsWebBrowserFind::GetSearchSubframes(bool *aSearchSubframes)
612 {
613 NS_ENSURE_ARG_POINTER(aSearchSubframes);
614 *aSearchSubframes = mSearchSubFrames;
615 return NS_OK;
616 }
618 NS_IMETHODIMP nsWebBrowserFind::SetSearchSubframes(bool aSearchSubframes)
619 {
620 mSearchSubFrames = aSearchSubframes;
621 return NS_OK;
622 }
624 /* attribute boolean searchParentFrames; */
625 NS_IMETHODIMP nsWebBrowserFind::GetSearchParentFrames(bool *aSearchParentFrames)
626 {
627 NS_ENSURE_ARG_POINTER(aSearchParentFrames);
628 *aSearchParentFrames = mSearchParentFrames;
629 return NS_OK;
630 }
632 NS_IMETHODIMP nsWebBrowserFind::SetSearchParentFrames(bool aSearchParentFrames)
633 {
634 mSearchParentFrames = aSearchParentFrames;
635 return NS_OK;
636 }
638 /*
639 This method handles finding in a single window (aka frame).
641 */
642 nsresult nsWebBrowserFind::SearchInFrame(nsIDOMWindow* aWindow,
643 bool aWrapping,
644 bool* aDidFind)
645 {
646 NS_ENSURE_ARG(aWindow);
647 NS_ENSURE_ARG_POINTER(aDidFind);
649 *aDidFind = false;
651 nsCOMPtr<nsIDOMDocument> domDoc;
652 nsresult rv = aWindow->GetDocument(getter_AddRefs(domDoc));
653 NS_ENSURE_SUCCESS(rv, rv);
654 if (!domDoc) return NS_ERROR_FAILURE;
656 // Do security check, to ensure that the frame we're searching is
657 // acccessible from the frame where the Find is being run.
659 // get a uri for the window
660 nsCOMPtr<nsIDocument> theDoc = do_QueryInterface(domDoc);
661 if (!theDoc) return NS_ERROR_FAILURE;
663 nsCOMPtr<nsIScriptSecurityManager> secMan =
664 do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv);
665 NS_ENSURE_SUCCESS(rv, rv);
667 nsCOMPtr<nsIPrincipal> subject;
668 rv = secMan->GetSubjectPrincipal(getter_AddRefs(subject));
669 NS_ENSURE_SUCCESS(rv, rv);
671 if (subject) {
672 bool subsumes;
673 rv = subject->Subsumes(theDoc->NodePrincipal(), &subsumes);
674 NS_ENSURE_SUCCESS(rv, rv);
675 if (!subsumes) {
676 return NS_ERROR_DOM_PROP_ACCESS_DENIED;
677 }
678 }
680 nsCOMPtr<nsIFind> find = do_CreateInstance(NS_FIND_CONTRACTID, &rv);
681 NS_ENSURE_SUCCESS(rv, rv);
683 (void) find->SetCaseSensitive(mMatchCase);
684 (void) find->SetFindBackwards(mFindBackwards);
686 // XXX Make and set a line breaker here, once that's implemented.
687 (void) find->SetWordBreaker(0);
689 // Now make sure the content (for actual finding) and frame (for
690 // selection) models are up to date.
691 theDoc->FlushPendingNotifications(Flush_Frames);
693 nsCOMPtr<nsISelection> sel;
694 GetFrameSelection(aWindow, getter_AddRefs(sel));
695 NS_ENSURE_ARG_POINTER(sel);
697 nsCOMPtr<nsIDOMRange> searchRange = nsFind::CreateRange(theDoc);
698 NS_ENSURE_ARG_POINTER(searchRange);
699 nsCOMPtr<nsIDOMRange> startPt = nsFind::CreateRange(theDoc);
700 NS_ENSURE_ARG_POINTER(startPt);
701 nsCOMPtr<nsIDOMRange> endPt = nsFind::CreateRange(theDoc);
702 NS_ENSURE_ARG_POINTER(endPt);
704 nsCOMPtr<nsIDOMRange> foundRange;
706 // If !aWrapping, search from selection to end
707 if (!aWrapping)
708 rv = GetSearchLimits(searchRange, startPt, endPt, domDoc, sel,
709 false);
711 // If aWrapping, search the part of the starting frame
712 // up to the point where we left off.
713 else
714 rv = GetSearchLimits(searchRange, startPt, endPt, domDoc, sel,
715 true);
717 NS_ENSURE_SUCCESS(rv, rv);
719 rv = find->Find(mSearchString.get(), searchRange, startPt, endPt,
720 getter_AddRefs(foundRange));
722 if (NS_SUCCEEDED(rv) && foundRange)
723 {
724 *aDidFind = true;
725 sel->RemoveAllRanges();
726 // Beware! This may flush notifications via synchronous
727 // ScrollSelectionIntoView.
728 SetSelectionAndScroll(aWindow, foundRange);
729 }
731 return rv;
732 }
735 // called when we start searching a frame that is not the initial
736 // focussed frame. Prepare the frame to be searched.
737 // we clear the selection, so that the search starts from the top
738 // of the frame.
739 nsresult nsWebBrowserFind::OnStartSearchFrame(nsIDOMWindow *aWindow)
740 {
741 return ClearFrameSelection(aWindow);
742 }
744 // called when we are done searching a frame and didn't find anything,
745 // and about about to start searching the next frame.
746 nsresult nsWebBrowserFind::OnEndSearchFrame(nsIDOMWindow *aWindow)
747 {
748 return NS_OK;
749 }
751 void
752 nsWebBrowserFind::GetFrameSelection(nsIDOMWindow* aWindow,
753 nsISelection** aSel)
754 {
755 *aSel = nullptr;
757 nsCOMPtr<nsIDOMDocument> domDoc;
758 aWindow->GetDocument(getter_AddRefs(domDoc));
759 if (!domDoc) return;
761 nsCOMPtr<nsIDocument> doc(do_QueryInterface(domDoc));
762 nsIPresShell* presShell = doc->GetShell();
763 if (!presShell) return;
765 // text input controls have their independent selection controllers
766 // that we must use when they have focus.
767 nsPresContext *presContext = presShell->GetPresContext();
769 nsCOMPtr<nsPIDOMWindow> window(do_QueryInterface(aWindow));
771 nsCOMPtr<nsPIDOMWindow> focusedWindow;
772 nsCOMPtr<nsIContent> focusedContent =
773 nsFocusManager::GetFocusedDescendant(window, false, getter_AddRefs(focusedWindow));
775 nsIFrame *frame = focusedContent ? focusedContent->GetPrimaryFrame() : nullptr;
777 nsCOMPtr<nsISelectionController> selCon;
778 if (frame) {
779 frame->GetSelectionController(presContext, getter_AddRefs(selCon));
780 selCon->GetSelection(nsISelectionController::SELECTION_NORMAL, aSel);
781 if (*aSel) {
782 int32_t count = -1;
783 (*aSel)->GetRangeCount(&count);
784 if (count > 0) {
785 return;
786 }
787 NS_RELEASE(*aSel);
788 }
789 }
791 selCon = do_QueryInterface(presShell);
792 selCon->GetSelection(nsISelectionController::SELECTION_NORMAL, aSel);
793 }
795 nsresult nsWebBrowserFind::ClearFrameSelection(nsIDOMWindow *aWindow)
796 {
797 NS_ENSURE_ARG(aWindow);
798 nsCOMPtr<nsISelection> selection;
799 GetFrameSelection(aWindow, getter_AddRefs(selection));
800 if (selection)
801 selection->RemoveAllRanges();
803 return NS_OK;
804 }
806 nsresult nsWebBrowserFind::OnFind(nsIDOMWindow *aFoundWindow)
807 {
808 SetCurrentSearchFrame(aFoundWindow);
810 // We don't want a selection to appear in two frames simultaneously
811 nsCOMPtr<nsIDOMWindow> lastFocusedWindow = do_QueryReferent(mLastFocusedWindow);
812 if (lastFocusedWindow && lastFocusedWindow != aFoundWindow)
813 ClearFrameSelection(lastFocusedWindow);
815 nsCOMPtr<nsIFocusManager> fm = do_GetService(FOCUSMANAGER_CONTRACTID);
816 if (fm) {
817 // get the containing frame and focus it. For top-level windows,
818 // the right window should already be focused.
819 nsCOMPtr<nsPIDOMWindow> window(do_QueryInterface(aFoundWindow));
820 NS_ENSURE_TRUE(window, NS_ERROR_FAILURE);
822 nsCOMPtr<nsIDOMElement> frameElement =
823 do_QueryInterface(window->GetFrameElementInternal());
824 if (frameElement)
825 fm->SetFocus(frameElement, 0);
827 mLastFocusedWindow = do_GetWeakReference(aFoundWindow);
828 }
830 return NS_OK;
831 }
833 /*---------------------------------------------------------------------------
835 GetDocShellFromWindow
837 Utility method. This will always return nullptr if no docShell
838 is returned. Oh why isn't there a better way to do this?
839 ----------------------------------------------------------------------------*/
840 nsIDocShell *
841 nsWebBrowserFind::GetDocShellFromWindow(nsIDOMWindow *inWindow)
842 {
843 nsCOMPtr<nsPIDOMWindow> window(do_QueryInterface(inWindow));
844 if (!window) return nullptr;
846 return window->GetDocShell();
847 }