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