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