1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* ***** BEGIN LICENSE BLOCK *****
3 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 : *
5 : * The contents of this file are subject to the Mozilla Public License Version
6 : * 1.1 (the "License"); you may not use this file except in compliance with
7 : * the License. You may obtain a copy of the License at
8 : * http://www.mozilla.org/MPL/
9 : *
10 : * Software distributed under the License is distributed on an "AS IS" basis,
11 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 : * for the specific language governing rights and limitations under the
13 : * License.
14 : *
15 : * The Original Code is mozilla.org code.
16 : *
17 : * The Initial Developer of the Original Code is
18 : * Netscape Communications Corporation.
19 : * Portions created by the Initial Developer are Copyright (C) 1998
20 : * the Initial Developer. All Rights Reserved.
21 : *
22 : * Contributor(s):
23 : * Aaron Leventhal (aaronl@netscape.com)
24 : * Blake Ross (blake@cs.stanford.edu)
25 : * Masayuki Nakano (masayuki@d-toybox.com)
26 : * Asaf Romano (mano@mozilla.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 "nsCOMPtr.h"
43 : #include "nsMemory.h"
44 : #include "nsIServiceManager.h"
45 : #include "mozilla/ModuleUtils.h"
46 : #include "nsIWebBrowserChrome.h"
47 : #include "nsCURILoader.h"
48 : #include "nsNetUtil.h"
49 : #include "nsIURL.h"
50 : #include "nsIURI.h"
51 : #include "nsIDocShell.h"
52 : #include "nsIDocShellTreeOwner.h"
53 : #include "nsIEditorDocShell.h"
54 : #include "nsISimpleEnumerator.h"
55 : #include "nsPIDOMWindow.h"
56 : #include "nsIDOMNSEvent.h"
57 : #include "nsIPrefBranch.h"
58 : #include "nsIPrefService.h"
59 : #include "nsString.h"
60 : #include "nsCRT.h"
61 :
62 : #include "nsIDOMNode.h"
63 : #include "mozilla/dom/Element.h"
64 : #include "nsIFrame.h"
65 : #include "nsFrameTraversal.h"
66 : #include "nsIDOMDocument.h"
67 : #include "nsIImageDocument.h"
68 : #include "nsIDOMHTMLDocument.h"
69 : #include "nsIDOMHTMLElement.h"
70 : #include "nsIDocument.h"
71 : #include "nsISelection.h"
72 : #include "nsILink.h"
73 : #include "nsTextFragment.h"
74 : #include "nsIDOMNSEditableElement.h"
75 : #include "nsIEditor.h"
76 :
77 : #include "nsIDocShellTreeItem.h"
78 : #include "nsIWebNavigation.h"
79 : #include "nsIInterfaceRequestor.h"
80 : #include "nsIInterfaceRequestorUtils.h"
81 : #include "nsContentCID.h"
82 : #include "nsLayoutCID.h"
83 : #include "nsWidgetsCID.h"
84 : #include "nsIFormControl.h"
85 : #include "nsINameSpaceManager.h"
86 : #include "nsIWindowWatcher.h"
87 : #include "nsIObserverService.h"
88 : #include "nsFocusManager.h"
89 : #include "mozilla/dom/Element.h"
90 : #include "nsRange.h"
91 :
92 : #include "nsTypeAheadFind.h"
93 :
94 0 : NS_INTERFACE_MAP_BEGIN(nsTypeAheadFind)
95 0 : NS_INTERFACE_MAP_ENTRY(nsITypeAheadFind)
96 0 : NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsITypeAheadFind)
97 0 : NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
98 0 : NS_INTERFACE_MAP_ENTRY(nsIObserver)
99 0 : NS_INTERFACE_MAP_END
100 :
101 0 : NS_IMPL_ADDREF(nsTypeAheadFind)
102 0 : NS_IMPL_RELEASE(nsTypeAheadFind)
103 :
104 : static NS_DEFINE_CID(kFrameTraversalCID, NS_FRAMETRAVERSAL_CID);
105 :
106 : #define NS_FIND_CONTRACTID "@mozilla.org/embedcomp/rangefind;1"
107 :
108 0 : nsTypeAheadFind::nsTypeAheadFind():
109 : mStartLinksOnlyPref(false),
110 : mCaretBrowsingOn(false),
111 : mLastFindLength(0),
112 : mIsSoundInitialized(false),
113 0 : mCaseSensitive(false)
114 : {
115 0 : }
116 :
117 0 : nsTypeAheadFind::~nsTypeAheadFind()
118 : {
119 0 : nsCOMPtr<nsIPrefBranch> prefInternal(do_GetService(NS_PREFSERVICE_CONTRACTID));
120 0 : if (prefInternal) {
121 0 : prefInternal->RemoveObserver("accessibility.typeaheadfind", this);
122 0 : prefInternal->RemoveObserver("accessibility.browsewithcaret", this);
123 : }
124 0 : }
125 :
126 : nsresult
127 0 : nsTypeAheadFind::Init(nsIDocShell* aDocShell)
128 : {
129 0 : nsCOMPtr<nsIPrefBranch> prefInternal(do_GetService(NS_PREFSERVICE_CONTRACTID));
130 0 : mSearchRange = new nsRange();
131 0 : mStartPointRange = new nsRange();
132 0 : mEndPointRange = new nsRange();
133 0 : if (!prefInternal || !EnsureFind())
134 0 : return NS_ERROR_FAILURE;
135 :
136 0 : SetDocShell(aDocShell);
137 :
138 : // ----------- Listen to prefs ------------------
139 0 : nsresult rv = prefInternal->AddObserver("accessibility.browsewithcaret", this, true);
140 0 : NS_ENSURE_SUCCESS(rv, rv);
141 :
142 : // ----------- Get initial preferences ----------
143 0 : PrefsReset();
144 :
145 0 : return rv;
146 : }
147 :
148 : nsresult
149 0 : nsTypeAheadFind::PrefsReset()
150 : {
151 0 : nsCOMPtr<nsIPrefBranch> prefBranch(do_GetService(NS_PREFSERVICE_CONTRACTID));
152 0 : NS_ENSURE_TRUE(prefBranch, NS_ERROR_FAILURE);
153 :
154 0 : prefBranch->GetBoolPref("accessibility.typeaheadfind.startlinksonly",
155 0 : &mStartLinksOnlyPref);
156 :
157 0 : bool isSoundEnabled = true;
158 0 : prefBranch->GetBoolPref("accessibility.typeaheadfind.enablesound",
159 0 : &isSoundEnabled);
160 0 : nsXPIDLCString soundStr;
161 0 : if (isSoundEnabled)
162 0 : prefBranch->GetCharPref("accessibility.typeaheadfind.soundURL", getter_Copies(soundStr));
163 :
164 0 : mNotFoundSoundURL = soundStr;
165 :
166 0 : prefBranch->GetBoolPref("accessibility.browsewithcaret",
167 0 : &mCaretBrowsingOn);
168 :
169 0 : return NS_OK;
170 : }
171 :
172 : NS_IMETHODIMP
173 0 : nsTypeAheadFind::SetCaseSensitive(bool isCaseSensitive)
174 : {
175 0 : mCaseSensitive = isCaseSensitive;
176 :
177 0 : if (mFind) {
178 0 : mFind->SetCaseSensitive(mCaseSensitive);
179 : }
180 :
181 0 : return NS_OK;
182 : }
183 :
184 : NS_IMETHODIMP
185 0 : nsTypeAheadFind::GetCaseSensitive(bool* isCaseSensitive)
186 : {
187 0 : *isCaseSensitive = mCaseSensitive;
188 :
189 0 : return NS_OK;
190 : }
191 :
192 : NS_IMETHODIMP
193 0 : nsTypeAheadFind::SetDocShell(nsIDocShell* aDocShell)
194 : {
195 0 : mDocShell = do_GetWeakReference(aDocShell);
196 :
197 0 : mWebBrowserFind = do_GetInterface(aDocShell);
198 0 : NS_ENSURE_TRUE(mWebBrowserFind, NS_ERROR_FAILURE);
199 :
200 0 : nsCOMPtr<nsIPresShell> presShell;
201 0 : aDocShell->GetPresShell(getter_AddRefs(presShell));
202 0 : mPresShell = do_GetWeakReference(presShell);
203 :
204 0 : mStartFindRange = nsnull;
205 0 : mStartPointRange = new nsRange();
206 0 : mSearchRange = new nsRange();
207 0 : mEndPointRange = new nsRange();
208 :
209 0 : mFoundLink = nsnull;
210 0 : mFoundEditable = nsnull;
211 0 : mCurrentWindow = nsnull;
212 :
213 0 : mSelectionController = nsnull;
214 :
215 0 : mFind = nsnull;
216 :
217 0 : return NS_OK;
218 : }
219 :
220 : NS_IMETHODIMP
221 0 : nsTypeAheadFind::SetSelectionModeAndRepaint(PRInt16 aToggle)
222 : {
223 : nsCOMPtr<nsISelectionController> selectionController =
224 0 : do_QueryReferent(mSelectionController);
225 0 : if (!selectionController) {
226 0 : return NS_OK;
227 : }
228 :
229 0 : selectionController->SetDisplaySelection(aToggle);
230 0 : selectionController->RepaintSelection(nsISelectionController::SELECTION_NORMAL);
231 :
232 0 : return NS_OK;
233 : }
234 :
235 : NS_IMETHODIMP
236 0 : nsTypeAheadFind::CollapseSelection()
237 : {
238 : nsCOMPtr<nsISelectionController> selectionController =
239 0 : do_QueryReferent(mSelectionController);
240 0 : if (!selectionController) {
241 0 : return NS_OK;
242 : }
243 :
244 0 : nsCOMPtr<nsISelection> selection;
245 0 : selectionController->GetSelection(nsISelectionController::SELECTION_NORMAL,
246 0 : getter_AddRefs(selection));
247 0 : if (selection)
248 0 : selection->CollapseToStart();
249 :
250 0 : return NS_OK;
251 : }
252 :
253 : NS_IMETHODIMP
254 0 : nsTypeAheadFind::Observe(nsISupports *aSubject, const char *aTopic,
255 : const PRUnichar *aData)
256 : {
257 0 : if (!nsCRT::strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID))
258 0 : return PrefsReset();
259 :
260 0 : return NS_OK;
261 : }
262 :
263 : void
264 0 : nsTypeAheadFind::SaveFind()
265 : {
266 0 : if (mWebBrowserFind)
267 0 : mWebBrowserFind->SetSearchString(mTypeAheadBuffer.get());
268 :
269 : // save the length of this find for "not found" sound
270 0 : mLastFindLength = mTypeAheadBuffer.Length();
271 0 : }
272 :
273 : void
274 0 : nsTypeAheadFind::PlayNotFoundSound()
275 : {
276 0 : if (mNotFoundSoundURL.IsEmpty()) // no sound
277 0 : return;
278 :
279 0 : if (!mSoundInterface)
280 0 : mSoundInterface = do_CreateInstance("@mozilla.org/sound;1");
281 :
282 0 : if (mSoundInterface) {
283 0 : mIsSoundInitialized = true;
284 :
285 0 : if (mNotFoundSoundURL.Equals("beep")) {
286 0 : mSoundInterface->Beep();
287 0 : return;
288 : }
289 :
290 0 : nsCOMPtr<nsIURI> soundURI;
291 0 : if (mNotFoundSoundURL.Equals("default"))
292 0 : NS_NewURI(getter_AddRefs(soundURI), NS_LITERAL_CSTRING(TYPEAHEADFIND_NOTFOUND_WAV_URL));
293 : else
294 0 : NS_NewURI(getter_AddRefs(soundURI), mNotFoundSoundURL);
295 :
296 0 : nsCOMPtr<nsIURL> soundURL(do_QueryInterface(soundURI));
297 0 : if (soundURL)
298 0 : mSoundInterface->Play(soundURL);
299 : }
300 : }
301 :
302 : nsresult
303 0 : nsTypeAheadFind::FindItNow(nsIPresShell *aPresShell, bool aIsLinksOnly,
304 : bool aIsFirstVisiblePreferred, bool aFindPrev,
305 : PRUint16* aResult)
306 : {
307 0 : *aResult = FIND_NOTFOUND;
308 0 : mFoundLink = nsnull;
309 0 : mFoundEditable = nsnull;
310 0 : mCurrentWindow = nsnull;
311 0 : nsCOMPtr<nsIPresShell> startingPresShell (GetPresShell());
312 0 : if (!startingPresShell) {
313 0 : nsCOMPtr<nsIDocShell> ds = do_QueryReferent(mDocShell);
314 0 : NS_ENSURE_TRUE(ds, NS_ERROR_FAILURE);
315 :
316 0 : ds->GetPresShell(getter_AddRefs(startingPresShell));
317 0 : mPresShell = do_GetWeakReference(startingPresShell);
318 : }
319 :
320 0 : nsCOMPtr<nsIPresShell> presShell(aPresShell);
321 :
322 0 : if (!presShell) {
323 0 : presShell = startingPresShell; // this is the current document
324 :
325 0 : if (!presShell)
326 0 : return NS_ERROR_FAILURE;
327 : }
328 :
329 0 : nsRefPtr<nsPresContext> presContext = presShell->GetPresContext();
330 :
331 0 : if (!presContext)
332 0 : return NS_ERROR_FAILURE;
333 :
334 0 : nsCOMPtr<nsISelection> selection;
335 : nsCOMPtr<nsISelectionController> selectionController =
336 0 : do_QueryReferent(mSelectionController);
337 0 : if (!selectionController) {
338 0 : GetSelection(presShell, getter_AddRefs(selectionController),
339 0 : getter_AddRefs(selection)); // cache for reuse
340 0 : mSelectionController = do_GetWeakReference(selectionController);
341 : } else {
342 0 : selectionController->GetSelection(
343 0 : nsISelectionController::SELECTION_NORMAL, getter_AddRefs(selection));
344 : }
345 :
346 0 : nsCOMPtr<nsISupports> startingContainer = presContext->GetContainer();
347 0 : nsCOMPtr<nsIDocShellTreeItem> treeItem(do_QueryInterface(startingContainer));
348 0 : NS_ASSERTION(treeItem, "Bug 175321 Crashes with Type Ahead Find [@ nsTypeAheadFind::FindItNow]");
349 0 : if (!treeItem)
350 0 : return NS_ERROR_FAILURE;
351 :
352 0 : nsCOMPtr<nsIDocShellTreeItem> rootContentTreeItem;
353 0 : nsCOMPtr<nsIDocShell> currentDocShell;
354 0 : nsCOMPtr<nsIDocShell> startingDocShell(do_QueryInterface(startingContainer));
355 :
356 0 : treeItem->GetSameTypeRootTreeItem(getter_AddRefs(rootContentTreeItem));
357 : nsCOMPtr<nsIDocShell> rootContentDocShell =
358 0 : do_QueryInterface(rootContentTreeItem);
359 :
360 0 : if (!rootContentDocShell)
361 0 : return NS_ERROR_FAILURE;
362 :
363 0 : nsCOMPtr<nsISimpleEnumerator> docShellEnumerator;
364 0 : rootContentDocShell->GetDocShellEnumerator(nsIDocShellTreeItem::typeContent,
365 : nsIDocShell::ENUMERATE_FORWARDS,
366 0 : getter_AddRefs(docShellEnumerator));
367 :
368 : // Default: can start at the current document
369 : nsCOMPtr<nsISupports> currentContainer = startingContainer =
370 0 : do_QueryInterface(rootContentDocShell);
371 :
372 : // Iterate up to current shell, if there's more than 1 that we're
373 : // dealing with
374 : bool hasMoreDocShells;
375 :
376 0 : while (NS_SUCCEEDED(docShellEnumerator->HasMoreElements(&hasMoreDocShells)) && hasMoreDocShells) {
377 0 : docShellEnumerator->GetNext(getter_AddRefs(currentContainer));
378 0 : currentDocShell = do_QueryInterface(currentContainer);
379 0 : if (!currentDocShell || currentDocShell == startingDocShell || aIsFirstVisiblePreferred)
380 0 : break;
381 : }
382 :
383 : // ------------ Get ranges ready ----------------
384 0 : nsCOMPtr<nsIDOMRange> returnRange;
385 0 : nsCOMPtr<nsIPresShell> focusedPS;
386 0 : if (NS_FAILED(GetSearchContainers(currentContainer,
387 : (!aIsFirstVisiblePreferred ||
388 : mStartFindRange) ?
389 : selectionController.get() : nsnull,
390 : aIsFirstVisiblePreferred, aFindPrev,
391 : getter_AddRefs(presShell),
392 : getter_AddRefs(presContext)))) {
393 0 : return NS_ERROR_FAILURE;
394 : }
395 :
396 0 : PRInt16 rangeCompareResult = 0;
397 0 : mStartPointRange->CompareBoundaryPoints(nsIDOMRange::START_TO_START, mSearchRange, &rangeCompareResult);
398 : // No need to wrap find in doc if starting at beginning
399 0 : bool hasWrapped = (rangeCompareResult < 0);
400 :
401 0 : if (mTypeAheadBuffer.IsEmpty() || !EnsureFind())
402 0 : return NS_ERROR_FAILURE;
403 :
404 0 : mFind->SetFindBackwards(aFindPrev);
405 :
406 0 : while (true) { // ----- Outer while loop: go through all docs -----
407 0 : while (true) { // === Inner while loop: go through a single doc ===
408 0 : mFind->Find(mTypeAheadBuffer.get(), mSearchRange, mStartPointRange,
409 0 : mEndPointRange, getter_AddRefs(returnRange));
410 :
411 0 : if (!returnRange)
412 : break; // Nothing found in this doc, go to outer loop (try next doc)
413 :
414 : // ------- Test resulting found range for success conditions ------
415 0 : bool isInsideLink = false, isStartingLink = false;
416 :
417 0 : if (aIsLinksOnly) {
418 : // Don't check if inside link when searching all text
419 : RangeStartsInsideLink(returnRange, presShell, &isInsideLink,
420 0 : &isStartingLink);
421 : }
422 :
423 : bool usesIndependentSelection;
424 0 : if (!IsRangeVisible(presShell, presContext, returnRange,
425 : aIsFirstVisiblePreferred, false,
426 0 : getter_AddRefs(mStartPointRange),
427 0 : &usesIndependentSelection) ||
428 0 : (aIsLinksOnly && !isInsideLink) ||
429 0 : (mStartLinksOnlyPref && aIsLinksOnly && !isStartingLink)) {
430 : // ------ Failure ------
431 : // Start find again from here
432 0 : returnRange->CloneRange(getter_AddRefs(mStartPointRange));
433 :
434 : // Collapse to end
435 0 : mStartPointRange->Collapse(aFindPrev);
436 :
437 0 : continue;
438 : }
439 :
440 : // ------ Success! -------
441 : // Hide old selection (new one may be on a different controller)
442 0 : if (selection) {
443 0 : selection->CollapseToStart();
444 0 : SetSelectionModeAndRepaint(nsISelectionController::SELECTION_ON);
445 : }
446 :
447 : // Make sure new document is selected
448 0 : if (presShell != startingPresShell) {
449 : // We are in a new document (because of frames/iframes)
450 0 : mPresShell = do_GetWeakReference(presShell);
451 : }
452 :
453 : nsCOMPtr<nsIDocument> document =
454 0 : do_QueryInterface(presShell->GetDocument());
455 0 : NS_ASSERTION(document, "Wow, presShell doesn't have document!");
456 0 : if (!document)
457 0 : return NS_ERROR_UNEXPECTED;
458 :
459 0 : nsCOMPtr<nsPIDOMWindow> window = document->GetWindow();
460 0 : NS_ASSERTION(window, "document has no window");
461 0 : if (!window)
462 0 : return NS_ERROR_UNEXPECTED;
463 :
464 0 : nsCOMPtr<nsIFocusManager> fm = do_GetService(FOCUSMANAGER_CONTRACTID);
465 0 : if (usesIndependentSelection) {
466 : /* If a search result is found inside an editable element, we'll focus
467 : * the element only if focus is in our content window, i.e.
468 : * |if (focusedWindow.top == ourWindow.top)| */
469 0 : bool shouldFocusEditableElement = false;
470 0 : if (fm) {
471 0 : nsCOMPtr<nsIDOMWindow> focusedWindow;
472 0 : nsresult rv = fm->GetFocusedWindow(getter_AddRefs(focusedWindow));
473 0 : if (NS_SUCCEEDED(rv)) {
474 0 : nsCOMPtr<nsPIDOMWindow> fwPI(do_QueryInterface(focusedWindow, &rv));
475 0 : if (NS_SUCCEEDED(rv)) {
476 : nsCOMPtr<nsIDocShellTreeItem> fwTreeItem
477 0 : (do_QueryInterface(fwPI->GetDocShell(), &rv));
478 0 : if (NS_SUCCEEDED(rv)) {
479 0 : nsCOMPtr<nsIDocShellTreeItem> fwRootTreeItem;
480 0 : rv = fwTreeItem->GetSameTypeRootTreeItem(getter_AddRefs(fwRootTreeItem));
481 0 : if (NS_SUCCEEDED(rv) && fwRootTreeItem == rootContentTreeItem)
482 0 : shouldFocusEditableElement = true;
483 : }
484 : }
485 : }
486 : }
487 :
488 : // We may be inside an editable element, and therefore the selection
489 : // may be controlled by a different selection controller. Walk up the
490 : // chain of parent nodes to see if we find one.
491 0 : nsCOMPtr<nsIDOMNode> node;
492 0 : returnRange->GetStartContainer(getter_AddRefs(node));
493 0 : while (node) {
494 0 : nsCOMPtr<nsIDOMNSEditableElement> editable = do_QueryInterface(node);
495 0 : if (editable) {
496 : // Inside an editable element. Get the correct selection
497 : // controller and selection.
498 0 : nsCOMPtr<nsIEditor> editor;
499 0 : editable->GetEditor(getter_AddRefs(editor));
500 0 : NS_ASSERTION(editor, "Editable element has no editor!");
501 0 : if (!editor) {
502 : break;
503 : }
504 0 : editor->GetSelectionController(
505 0 : getter_AddRefs(selectionController));
506 0 : if (selectionController) {
507 0 : selectionController->GetSelection(
508 : nsISelectionController::SELECTION_NORMAL,
509 0 : getter_AddRefs(selection));
510 : }
511 0 : mFoundEditable = do_QueryInterface(node);
512 :
513 0 : if (!shouldFocusEditableElement)
514 : break;
515 :
516 : // Otherwise move focus/caret to editable element
517 0 : if (fm)
518 0 : fm->SetFocus(mFoundEditable, 0);
519 : break;
520 : }
521 0 : nsIDOMNode* tmp = node;
522 0 : tmp->GetParentNode(getter_AddRefs(node));
523 : }
524 :
525 : // If we reach here without setting mFoundEditable, then something
526 : // besides editable elements can cause us to have an independent
527 : // selection controller. I don't know whether this is possible.
528 : // Currently, we simply fall back to grabbing the document's selection
529 : // controller in this case. Perhaps we should reject this find match
530 : // and search again.
531 0 : NS_ASSERTION(mFoundEditable, "Independent selection controller on "
532 : "non-editable element!");
533 : }
534 :
535 0 : if (!mFoundEditable) {
536 : // Not using a separate selection controller, so just get the
537 : // document's controller and selection.
538 0 : GetSelection(presShell, getter_AddRefs(selectionController),
539 0 : getter_AddRefs(selection));
540 : }
541 0 : mSelectionController = do_GetWeakReference(selectionController);
542 :
543 : // Select the found text
544 0 : if (selection) {
545 0 : selection->RemoveAllRanges();
546 0 : selection->AddRange(returnRange);
547 : }
548 :
549 0 : if (!mFoundEditable && fm) {
550 0 : nsCOMPtr<nsIDOMWindow> win = do_QueryInterface(window);
551 0 : fm->MoveFocus(win, nsnull, nsIFocusManager::MOVEFOCUS_CARET,
552 : nsIFocusManager::FLAG_NOSCROLL | nsIFocusManager::FLAG_NOSWITCHFRAME,
553 0 : getter_AddRefs(mFoundLink));
554 : }
555 :
556 : // Change selection color to ATTENTION and scroll to it. Careful: we
557 : // must wait until after we goof with focus above before changing to
558 : // ATTENTION, or when we MoveFocus() and the selection is not on a
559 : // link, we'll blur, which will lose the ATTENTION.
560 0 : if (selectionController) {
561 : // Beware! This may flush notifications via synchronous
562 : // ScrollSelectionIntoView.
563 0 : SetSelectionModeAndRepaint(nsISelectionController::SELECTION_ATTENTION);
564 0 : selectionController->ScrollSelectionIntoView(
565 : nsISelectionController::SELECTION_NORMAL,
566 : nsISelectionController::SELECTION_WHOLE_SELECTION,
567 : nsISelectionController::SCROLL_CENTER_VERTICALLY |
568 0 : nsISelectionController::SCROLL_SYNCHRONOUS);
569 : }
570 :
571 0 : mCurrentWindow = window;
572 0 : *aResult = hasWrapped ? FIND_WRAPPED : FIND_FOUND;
573 0 : return NS_OK;
574 : }
575 :
576 : // ======= end-inner-while (go through a single document) ==========
577 :
578 : // ---------- Nothing found yet, try next document -------------
579 0 : bool hasTriedFirstDoc = false;
580 0 : do {
581 : // ==== Second inner loop - get another while ====
582 0 : if (NS_SUCCEEDED(docShellEnumerator->HasMoreElements(&hasMoreDocShells))
583 : && hasMoreDocShells) {
584 0 : docShellEnumerator->GetNext(getter_AddRefs(currentContainer));
585 0 : NS_ASSERTION(currentContainer, "HasMoreElements lied to us!");
586 0 : currentDocShell = do_QueryInterface(currentContainer);
587 :
588 0 : if (currentDocShell)
589 0 : break;
590 : }
591 0 : else if (hasTriedFirstDoc) // Avoid potential infinite loop
592 0 : return NS_ERROR_FAILURE; // No content doc shells
593 :
594 : // Reached last doc shell, loop around back to first doc shell
595 0 : rootContentDocShell->GetDocShellEnumerator(nsIDocShellTreeItem::typeContent,
596 : nsIDocShell::ENUMERATE_FORWARDS,
597 0 : getter_AddRefs(docShellEnumerator));
598 0 : hasTriedFirstDoc = true;
599 0 : } while (docShellEnumerator); // ==== end second inner while ===
600 :
601 0 : bool continueLoop = false;
602 0 : if (currentDocShell != startingDocShell)
603 0 : continueLoop = true; // Try next document
604 0 : else if (!hasWrapped || aIsFirstVisiblePreferred) {
605 : // Finished searching through docshells:
606 : // If aFirstVisiblePreferred == true, we may need to go through all
607 : // docshells twice -once to look for visible matches, the second time
608 : // for any match
609 0 : aIsFirstVisiblePreferred = false;
610 0 : hasWrapped = true;
611 0 : continueLoop = true; // Go through all docs again
612 : }
613 :
614 0 : if (continueLoop) {
615 0 : if (NS_FAILED(GetSearchContainers(currentContainer, nsnull,
616 : aIsFirstVisiblePreferred, aFindPrev,
617 : getter_AddRefs(presShell),
618 : getter_AddRefs(presContext)))) {
619 0 : continue;
620 : }
621 :
622 0 : if (aFindPrev) {
623 : // Reverse mode: swap start and end points, so that we start
624 : // at end of document and go to beginning
625 0 : nsCOMPtr<nsIDOMRange> tempRange;
626 0 : mStartPointRange->CloneRange(getter_AddRefs(tempRange));
627 0 : mStartPointRange = mEndPointRange;
628 0 : mEndPointRange = tempRange;
629 : }
630 :
631 0 : continue;
632 : }
633 :
634 : // ------------- Failed --------------
635 : break;
636 : } // end-outer-while: go through all docs
637 :
638 0 : return NS_ERROR_FAILURE;
639 : }
640 :
641 : NS_IMETHODIMP
642 0 : nsTypeAheadFind::GetSearchString(nsAString& aSearchString)
643 : {
644 0 : aSearchString = mTypeAheadBuffer;
645 0 : return NS_OK;
646 : }
647 :
648 : NS_IMETHODIMP
649 0 : nsTypeAheadFind::GetFoundLink(nsIDOMElement** aFoundLink)
650 : {
651 0 : NS_ENSURE_ARG_POINTER(aFoundLink);
652 0 : *aFoundLink = mFoundLink;
653 0 : NS_IF_ADDREF(*aFoundLink);
654 0 : return NS_OK;
655 : }
656 :
657 : NS_IMETHODIMP
658 0 : nsTypeAheadFind::GetFoundEditable(nsIDOMElement** aFoundEditable)
659 : {
660 0 : NS_ENSURE_ARG_POINTER(aFoundEditable);
661 0 : *aFoundEditable = mFoundEditable;
662 0 : NS_IF_ADDREF(*aFoundEditable);
663 0 : return NS_OK;
664 : }
665 :
666 : NS_IMETHODIMP
667 0 : nsTypeAheadFind::GetCurrentWindow(nsIDOMWindow** aCurrentWindow)
668 : {
669 0 : NS_ENSURE_ARG_POINTER(aCurrentWindow);
670 0 : *aCurrentWindow = mCurrentWindow;
671 0 : NS_IF_ADDREF(*aCurrentWindow);
672 0 : return NS_OK;
673 : }
674 :
675 : nsresult
676 0 : nsTypeAheadFind::GetSearchContainers(nsISupports *aContainer,
677 : nsISelectionController *aSelectionController,
678 : bool aIsFirstVisiblePreferred,
679 : bool aFindPrev,
680 : nsIPresShell **aPresShell,
681 : nsPresContext **aPresContext)
682 : {
683 0 : NS_ENSURE_ARG_POINTER(aContainer);
684 0 : NS_ENSURE_ARG_POINTER(aPresShell);
685 0 : NS_ENSURE_ARG_POINTER(aPresContext);
686 :
687 0 : *aPresShell = nsnull;
688 0 : *aPresContext = nsnull;
689 :
690 0 : nsCOMPtr<nsIDocShell> docShell(do_QueryInterface(aContainer));
691 0 : if (!docShell)
692 0 : return NS_ERROR_FAILURE;
693 :
694 0 : nsCOMPtr<nsIPresShell> presShell;
695 0 : docShell->GetPresShell(getter_AddRefs(presShell));
696 :
697 0 : nsRefPtr<nsPresContext> presContext;
698 0 : docShell->GetPresContext(getter_AddRefs(presContext));
699 :
700 0 : if (!presShell || !presContext)
701 0 : return NS_ERROR_FAILURE;
702 :
703 0 : nsIDocument* doc = presShell->GetDocument();
704 :
705 0 : if (!doc)
706 0 : return NS_ERROR_FAILURE;
707 :
708 0 : nsCOMPtr<nsIContent> rootContent;
709 0 : nsCOMPtr<nsIDOMHTMLDocument> htmlDoc(do_QueryInterface(doc));
710 0 : if (htmlDoc) {
711 0 : nsCOMPtr<nsIDOMHTMLElement> bodyEl;
712 0 : htmlDoc->GetBody(getter_AddRefs(bodyEl));
713 0 : rootContent = do_QueryInterface(bodyEl);
714 : }
715 :
716 0 : if (!rootContent)
717 0 : rootContent = doc->GetRootElement();
718 :
719 0 : nsCOMPtr<nsIDOMNode> rootNode(do_QueryInterface(rootContent));
720 :
721 0 : if (!rootNode)
722 0 : return NS_ERROR_FAILURE;
723 :
724 0 : PRUint32 childCount = rootContent->GetChildCount();
725 :
726 0 : mSearchRange->SelectNodeContents(rootNode);
727 :
728 0 : mEndPointRange->SetEnd(rootNode, childCount);
729 0 : mEndPointRange->Collapse(false); // collapse to end
730 :
731 : // Consider current selection as null if
732 : // it's not in the currently focused document
733 0 : nsCOMPtr<nsIDOMRange> currentSelectionRange;
734 0 : nsCOMPtr<nsIPresShell> selectionPresShell = GetPresShell();
735 0 : if (aSelectionController && selectionPresShell && selectionPresShell == presShell) {
736 0 : nsCOMPtr<nsISelection> selection;
737 : aSelectionController->GetSelection(
738 0 : nsISelectionController::SELECTION_NORMAL, getter_AddRefs(selection));
739 0 : if (selection)
740 0 : selection->GetRangeAt(0, getter_AddRefs(currentSelectionRange));
741 : }
742 :
743 0 : if (!currentSelectionRange) {
744 : // Ensure visible range, move forward if necessary
745 : // This uses ignores the return value, but usese the side effect of
746 : // IsRangeVisible. It returns the first visible range after searchRange
747 : IsRangeVisible(presShell, presContext, mSearchRange,
748 : aIsFirstVisiblePreferred, true,
749 0 : getter_AddRefs(mStartPointRange), nsnull);
750 : }
751 : else {
752 : PRInt32 startOffset;
753 0 : nsCOMPtr<nsIDOMNode> startNode;
754 0 : if (aFindPrev) {
755 0 : currentSelectionRange->GetStartContainer(getter_AddRefs(startNode));
756 0 : currentSelectionRange->GetStartOffset(&startOffset);
757 : } else {
758 0 : currentSelectionRange->GetEndContainer(getter_AddRefs(startNode));
759 0 : currentSelectionRange->GetEndOffset(&startOffset);
760 : }
761 0 : if (!startNode)
762 0 : startNode = rootNode;
763 :
764 : // We need to set the start point this way, other methods haven't worked
765 0 : mStartPointRange->SelectNode(startNode);
766 0 : mStartPointRange->SetStart(startNode, startOffset);
767 : }
768 :
769 0 : mStartPointRange->Collapse(true); // collapse to start
770 :
771 0 : *aPresShell = presShell;
772 0 : NS_ADDREF(*aPresShell);
773 :
774 0 : *aPresContext = presContext;
775 0 : NS_ADDREF(*aPresContext);
776 :
777 0 : return NS_OK;
778 : }
779 :
780 : void
781 0 : nsTypeAheadFind::RangeStartsInsideLink(nsIDOMRange *aRange,
782 : nsIPresShell *aPresShell,
783 : bool *aIsInsideLink,
784 : bool *aIsStartingLink)
785 : {
786 0 : *aIsInsideLink = false;
787 0 : *aIsStartingLink = true;
788 :
789 : // ------- Get nsIContent to test -------
790 0 : nsCOMPtr<nsIDOMNode> startNode;
791 0 : nsCOMPtr<nsIContent> startContent, origContent;
792 0 : aRange->GetStartContainer(getter_AddRefs(startNode));
793 : PRInt32 startOffset;
794 0 : aRange->GetStartOffset(&startOffset);
795 :
796 0 : startContent = do_QueryInterface(startNode);
797 0 : if (!startContent) {
798 0 : NS_NOTREACHED("startContent should never be null");
799 : return;
800 : }
801 0 : origContent = startContent;
802 :
803 0 : if (startContent->IsElement()) {
804 0 : nsIContent *childContent = startContent->GetChildAt(startOffset);
805 0 : if (childContent) {
806 0 : startContent = childContent;
807 : }
808 : }
809 0 : else if (startOffset > 0) {
810 0 : const nsTextFragment *textFrag = startContent->GetText();
811 0 : if (textFrag) {
812 : // look for non whitespace character before start offset
813 0 : for (PRInt32 index = 0; index < startOffset; index++) {
814 0 : if (!XP_IS_SPACE(textFrag->CharAt(index))) {
815 0 : *aIsStartingLink = false; // not at start of a node
816 :
817 0 : break;
818 : }
819 : }
820 : }
821 : }
822 :
823 : // ------- Check to see if inside link ---------
824 :
825 : // We now have the correct start node for the range
826 : // Search for links, starting with startNode, and going up parent chain
827 :
828 0 : nsCOMPtr<nsIAtom> tag, hrefAtom(do_GetAtom("href"));
829 0 : nsCOMPtr<nsIAtom> typeAtom(do_GetAtom("type"));
830 :
831 0 : while (true) {
832 : // Keep testing while startContent is equal to something,
833 : // eventually we'll run out of ancestors
834 :
835 0 : if (startContent->IsHTML()) {
836 0 : nsCOMPtr<nsILink> link(do_QueryInterface(startContent));
837 0 : if (link) {
838 : // Check to see if inside HTML link
839 0 : *aIsInsideLink = startContent->HasAttr(kNameSpaceID_None, hrefAtom);
840 : return;
841 : }
842 : }
843 : else {
844 : // Any xml element can be an xlink
845 0 : *aIsInsideLink = startContent->HasAttr(kNameSpaceID_XLink, hrefAtom);
846 0 : if (*aIsInsideLink) {
847 0 : if (!startContent->AttrValueIs(kNameSpaceID_XLink, typeAtom,
848 0 : NS_LITERAL_STRING("simple"),
849 0 : eCaseMatters)) {
850 0 : *aIsInsideLink = false; // Xlink must be type="simple"
851 : }
852 :
853 : return;
854 : }
855 : }
856 :
857 : // Get the parent
858 0 : nsCOMPtr<nsIContent> parent = startContent->GetParent();
859 0 : if (!parent)
860 : break;
861 :
862 0 : nsIContent* parentsFirstChild = parent->GetFirstChild();
863 :
864 : // We don't want to look at a whitespace-only first child
865 0 : if (parentsFirstChild && parentsFirstChild->TextIsOnlyWhitespace()) {
866 0 : parentsFirstChild = parentsFirstChild->GetNextSibling();
867 : }
868 :
869 0 : if (parentsFirstChild != startContent) {
870 : // startContent wasn't a first child, so we conclude that
871 : // if this is inside a link, it's not at the beginning of it
872 0 : *aIsStartingLink = false;
873 : }
874 :
875 0 : startContent = parent;
876 : }
877 :
878 0 : *aIsStartingLink = false;
879 : }
880 :
881 : /* Find another match in the page. */
882 : NS_IMETHODIMP
883 0 : nsTypeAheadFind::FindAgain(bool aFindBackwards, bool aLinksOnly,
884 : PRUint16* aResult)
885 :
886 : {
887 0 : *aResult = FIND_NOTFOUND;
888 :
889 0 : if (!mTypeAheadBuffer.IsEmpty())
890 : // Beware! This may flush notifications via synchronous
891 : // ScrollSelectionIntoView.
892 0 : FindItNow(nsnull, aLinksOnly, false, aFindBackwards, aResult);
893 :
894 0 : return NS_OK;
895 : }
896 :
897 : NS_IMETHODIMP
898 0 : nsTypeAheadFind::Find(const nsAString& aSearchString, bool aLinksOnly,
899 : PRUint16* aResult)
900 : {
901 0 : *aResult = FIND_NOTFOUND;
902 :
903 0 : nsCOMPtr<nsIPresShell> presShell (GetPresShell());
904 0 : if (!presShell) {
905 0 : nsCOMPtr<nsIDocShell> ds (do_QueryReferent(mDocShell));
906 0 : NS_ENSURE_TRUE(ds, NS_ERROR_FAILURE);
907 :
908 0 : ds->GetPresShell(getter_AddRefs(presShell));
909 0 : mPresShell = do_GetWeakReference(presShell);
910 : }
911 0 : nsCOMPtr<nsISelection> selection;
912 : nsCOMPtr<nsISelectionController> selectionController =
913 0 : do_QueryReferent(mSelectionController);
914 0 : if (!selectionController) {
915 0 : GetSelection(presShell, getter_AddRefs(selectionController),
916 0 : getter_AddRefs(selection)); // cache for reuse
917 0 : mSelectionController = do_GetWeakReference(selectionController);
918 : } else {
919 0 : selectionController->GetSelection(
920 0 : nsISelectionController::SELECTION_NORMAL, getter_AddRefs(selection));
921 : }
922 :
923 0 : if (selection)
924 0 : selection->CollapseToStart();
925 :
926 0 : if (aSearchString.IsEmpty()) {
927 0 : mTypeAheadBuffer.Truncate();
928 :
929 : // These will be initialized to their true values after the first character
930 : // is typed
931 0 : mStartFindRange = nsnull;
932 0 : mSelectionController = nsnull;
933 :
934 0 : *aResult = FIND_FOUND;
935 0 : return NS_OK;
936 : }
937 :
938 0 : bool atEnd = false;
939 0 : if (mTypeAheadBuffer.Length()) {
940 0 : const nsAString& oldStr = Substring(mTypeAheadBuffer, 0, mTypeAheadBuffer.Length());
941 0 : const nsAString& newStr = Substring(aSearchString, 0, mTypeAheadBuffer.Length());
942 0 : if (oldStr.Equals(newStr))
943 0 : atEnd = true;
944 :
945 0 : const nsAString& newStr2 = Substring(aSearchString, 0, aSearchString.Length());
946 0 : const nsAString& oldStr2 = Substring(mTypeAheadBuffer, 0, aSearchString.Length());
947 0 : if (oldStr2.Equals(newStr2))
948 0 : atEnd = true;
949 :
950 0 : if (!atEnd)
951 0 : mStartFindRange = nsnull;
952 : }
953 :
954 0 : if (!mIsSoundInitialized && !mNotFoundSoundURL.IsEmpty()) {
955 : // This makes sure system sound library is loaded so that
956 : // there's no lag before the first sound is played
957 : // by waiting for the first keystroke, we still get the startup time benefits.
958 0 : mIsSoundInitialized = true;
959 0 : mSoundInterface = do_CreateInstance("@mozilla.org/sound;1");
960 0 : if (mSoundInterface && !mNotFoundSoundURL.Equals(NS_LITERAL_CSTRING("beep"))) {
961 0 : mSoundInterface->Init();
962 : }
963 : }
964 :
965 : #ifdef XP_WIN
966 : // After each keystroke, ensure sound object is destroyed, to free up memory
967 : // allocated for error sound, otherwise Windows' nsISound impl
968 : // holds onto the last played sound, using up memory.
969 : mSoundInterface = nsnull;
970 : #endif
971 :
972 0 : PRInt32 bufferLength = mTypeAheadBuffer.Length();
973 :
974 0 : mTypeAheadBuffer = aSearchString;
975 :
976 0 : bool isFirstVisiblePreferred = false;
977 :
978 : // --------- Initialize find if 1st char ----------
979 0 : if (bufferLength == 0) {
980 : // If you can see the selection (not collapsed or thru caret browsing),
981 : // or if already focused on a page element, start there.
982 : // Otherwise we're going to start at the first visible element
983 0 : bool isSelectionCollapsed = true;
984 0 : if (selection)
985 0 : selection->GetIsCollapsed(&isSelectionCollapsed);
986 :
987 : // If true, we will scan from top left of visible area
988 : // If false, we will scan from start of selection
989 0 : isFirstVisiblePreferred = !atEnd && !mCaretBrowsingOn && isSelectionCollapsed;
990 0 : if (isFirstVisiblePreferred) {
991 : // Get the focused content. If there is a focused node, ensure the
992 : // selection is at that point. Otherwise, we will just want to start
993 : // from the caret position or the beginning of the document.
994 0 : nsPresContext* presContext = presShell->GetPresContext();
995 0 : NS_ENSURE_TRUE(presContext, NS_OK);
996 :
997 : nsCOMPtr<nsIDocument> document =
998 0 : do_QueryInterface(presShell->GetDocument());
999 0 : if (!document)
1000 0 : return NS_ERROR_UNEXPECTED;
1001 :
1002 0 : nsCOMPtr<nsIDOMWindow> window = do_QueryInterface(document->GetWindow());
1003 :
1004 0 : nsCOMPtr<nsIFocusManager> fm = do_GetService(FOCUSMANAGER_CONTRACTID);
1005 0 : if (fm) {
1006 0 : nsCOMPtr<nsIDOMElement> focusedElement;
1007 0 : nsCOMPtr<nsIDOMWindow> focusedWindow;
1008 0 : fm->GetFocusedElementForWindow(window, false, getter_AddRefs(focusedWindow),
1009 0 : getter_AddRefs(focusedElement));
1010 : // If the root element is focused, then it's actually the document
1011 : // that has the focus, so ignore this.
1012 0 : if (focusedElement &&
1013 0 : !SameCOMIdentity(focusedElement, document->GetRootElement())) {
1014 0 : fm->MoveCaretToFocus(window);
1015 0 : isFirstVisiblePreferred = false;
1016 : }
1017 : }
1018 : }
1019 : }
1020 :
1021 : // ----------- Find the text! ---------------------
1022 : // Beware! This may flush notifications via synchronous
1023 : // ScrollSelectionIntoView.
1024 : nsresult rv = FindItNow(nsnull, aLinksOnly, isFirstVisiblePreferred,
1025 0 : false, aResult);
1026 :
1027 : // ---------Handle success or failure ---------------
1028 0 : if (NS_SUCCEEDED(rv)) {
1029 0 : if (mTypeAheadBuffer.Length() == 1) {
1030 : // If first letter, store where the first find succeeded
1031 : // (mStartFindRange)
1032 :
1033 0 : mStartFindRange = nsnull;
1034 0 : if (selection) {
1035 0 : nsCOMPtr<nsIDOMRange> startFindRange;
1036 0 : selection->GetRangeAt(0, getter_AddRefs(startFindRange));
1037 0 : if (startFindRange)
1038 0 : startFindRange->CloneRange(getter_AddRefs(mStartFindRange));
1039 : }
1040 : }
1041 : }
1042 : else {
1043 : // Error sound
1044 0 : if (mTypeAheadBuffer.Length() > mLastFindLength)
1045 0 : PlayNotFoundSound();
1046 : }
1047 :
1048 0 : SaveFind();
1049 0 : return NS_OK;
1050 : }
1051 :
1052 : void
1053 0 : nsTypeAheadFind::GetSelection(nsIPresShell *aPresShell,
1054 : nsISelectionController **aSelCon,
1055 : nsISelection **aDOMSel)
1056 : {
1057 0 : if (!aPresShell)
1058 0 : return;
1059 :
1060 : // if aCurrentNode is nsnull, get selection for document
1061 0 : *aDOMSel = nsnull;
1062 :
1063 0 : nsPresContext* presContext = aPresShell->GetPresContext();
1064 :
1065 0 : nsIFrame *frame = aPresShell->GetRootFrame();
1066 :
1067 0 : if (presContext && frame) {
1068 0 : frame->GetSelectionController(presContext, aSelCon);
1069 0 : if (*aSelCon) {
1070 : (*aSelCon)->GetSelection(nsISelectionController::SELECTION_NORMAL,
1071 0 : aDOMSel);
1072 : }
1073 : }
1074 : }
1075 :
1076 :
1077 : bool
1078 0 : nsTypeAheadFind::IsRangeVisible(nsIPresShell *aPresShell,
1079 : nsPresContext *aPresContext,
1080 : nsIDOMRange *aRange, bool aMustBeInViewPort,
1081 : bool aGetTopVisibleLeaf,
1082 : nsIDOMRange **aFirstVisibleRange,
1083 : bool *aUsesIndependentSelection)
1084 : {
1085 0 : NS_ASSERTION(aPresShell && aPresContext && aRange && aFirstVisibleRange,
1086 : "params are invalid");
1087 :
1088 : // We need to know if the range start is visible.
1089 : // Otherwise, return the first visible range start
1090 : // in aFirstVisibleRange
1091 :
1092 0 : aRange->CloneRange(aFirstVisibleRange);
1093 0 : nsCOMPtr<nsIDOMNode> node;
1094 0 : aRange->GetStartContainer(getter_AddRefs(node));
1095 :
1096 0 : nsCOMPtr<nsIContent> content(do_QueryInterface(node));
1097 0 : if (!content)
1098 0 : return false;
1099 :
1100 0 : nsIFrame *frame = content->GetPrimaryFrame();
1101 0 : if (!frame)
1102 0 : return false; // No frame! Not visible then.
1103 :
1104 0 : if (!frame->GetStyleVisibility()->IsVisible())
1105 0 : return false;
1106 :
1107 : // Detect if we are _inside_ a text control, or something else with its own
1108 : // selection controller.
1109 0 : if (aUsesIndependentSelection) {
1110 : *aUsesIndependentSelection =
1111 0 : (frame->GetStateBits() & NS_FRAME_INDEPENDENT_SELECTION);
1112 : }
1113 :
1114 : // ---- We have a frame ----
1115 0 : if (!aMustBeInViewPort)
1116 0 : return true; // Don't need it to be on screen, just in rendering tree
1117 :
1118 : // Get the next in flow frame that contains the range start
1119 : PRInt32 startRangeOffset, startFrameOffset, endFrameOffset;
1120 0 : aRange->GetStartOffset(&startRangeOffset);
1121 0 : while (true) {
1122 0 : frame->GetOffsets(startFrameOffset, endFrameOffset);
1123 0 : if (startRangeOffset < endFrameOffset)
1124 0 : break;
1125 :
1126 0 : nsIFrame *nextContinuationFrame = frame->GetNextContinuation();
1127 0 : if (nextContinuationFrame)
1128 0 : frame = nextContinuationFrame;
1129 : else
1130 0 : break;
1131 : }
1132 :
1133 : // Set up the variables we need, return true if we can't get at them all
1134 0 : const PRUint16 kMinPixels = 12;
1135 0 : nscoord minDistance = nsPresContext::CSSPixelsToAppUnits(kMinPixels);
1136 :
1137 : // Get the bounds of the current frame, relative to the current view.
1138 : // We don't use the more accurate AccGetBounds, because that is
1139 : // more expensive and the STATE_OFFSCREEN flag that this is used
1140 : // for only needs to be a rough indicator
1141 0 : nsRectVisibility rectVisibility = nsRectVisibility_kAboveViewport;
1142 :
1143 0 : if (!aGetTopVisibleLeaf && !frame->GetRect().IsEmpty()) {
1144 : rectVisibility =
1145 : aPresShell->GetRectVisibility(frame,
1146 0 : nsRect(nsPoint(0,0), frame->GetSize()),
1147 0 : minDistance);
1148 :
1149 0 : if (rectVisibility != nsRectVisibility_kAboveViewport) {
1150 0 : return true;
1151 : }
1152 : }
1153 :
1154 : // We know that the target range isn't usable because it's not in the
1155 : // view port. Move range forward to first visible point,
1156 : // this speeds us up a lot in long documents
1157 0 : nsCOMPtr<nsIFrameEnumerator> frameTraversal;
1158 0 : nsCOMPtr<nsIFrameTraversal> trav(do_CreateInstance(kFrameTraversalCID));
1159 0 : if (trav)
1160 0 : trav->NewFrameTraversal(getter_AddRefs(frameTraversal),
1161 : aPresContext, frame,
1162 : eLeaf,
1163 : false, // aVisual
1164 : false, // aLockInScrollView
1165 : false // aFollowOOFs
1166 0 : );
1167 :
1168 0 : if (!frameTraversal)
1169 0 : return false;
1170 :
1171 0 : while (rectVisibility == nsRectVisibility_kAboveViewport) {
1172 0 : frameTraversal->Next();
1173 0 : frame = frameTraversal->CurrentItem();
1174 0 : if (!frame)
1175 0 : return false;
1176 :
1177 0 : if (!frame->GetRect().IsEmpty()) {
1178 : rectVisibility =
1179 : aPresShell->GetRectVisibility(frame,
1180 0 : nsRect(nsPoint(0,0), frame->GetSize()),
1181 0 : minDistance);
1182 : }
1183 : }
1184 :
1185 0 : if (frame) {
1186 0 : nsCOMPtr<nsIDOMNode> firstVisibleNode = do_QueryInterface(frame->GetContent());
1187 :
1188 0 : if (firstVisibleNode) {
1189 0 : (*aFirstVisibleRange)->SelectNode(firstVisibleNode);
1190 0 : frame->GetOffsets(startFrameOffset, endFrameOffset);
1191 0 : (*aFirstVisibleRange)->SetStart(firstVisibleNode, startFrameOffset);
1192 0 : (*aFirstVisibleRange)->Collapse(true); // Collapse to start
1193 : }
1194 : }
1195 :
1196 0 : return false;
1197 : }
1198 :
1199 : already_AddRefed<nsIPresShell>
1200 0 : nsTypeAheadFind::GetPresShell()
1201 : {
1202 0 : if (!mPresShell)
1203 0 : return nsnull;
1204 :
1205 0 : nsIPresShell *shell = nsnull;
1206 0 : CallQueryReferent(mPresShell.get(), &shell);
1207 0 : if (shell) {
1208 0 : nsPresContext *pc = shell->GetPresContext();
1209 0 : if (!pc || !nsCOMPtr<nsISupports>(pc->GetContainer())) {
1210 0 : NS_RELEASE(shell);
1211 : }
1212 : }
1213 :
1214 0 : return shell;
1215 : }
|