Back to index

lightning-sunbird  0.9+nobinonly
nsTypeAheadFind.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is mozilla.org code.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Netscape Communications Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 1998
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *   Aaron Leventhal (aaronl@netscape.com)
00024  *   Blake Ross      (blake@cs.stanford.edu)
00025  *   Masayuki Nakano (masayuki@d-toybox.com)
00026  *
00027  * Alternatively, the contents of this file may be used under the terms of
00028  * either the GNU General Public License Version 2 or later (the "GPL"), or
00029  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00030  * in which case the provisions of the GPL or the LGPL are applicable instead
00031  * of those above. If you wish to allow use of your version of this file only
00032  * under the terms of either the GPL or the LGPL, and not to allow others to
00033  * use your version of this file under the terms of the MPL, indicate your
00034  * decision by deleting the provisions above and replace them with the notice
00035  * and other provisions required by the GPL or the LGPL. If you do not delete
00036  * the provisions above, a recipient may use your version of this file under
00037  * the terms of any one of the MPL, the GPL or the LGPL.
00038  *
00039  * ***** END LICENSE BLOCK ***** */
00040 
00041 #include "nsCOMPtr.h"
00042 #include "nsMemory.h"
00043 #include "nsIServiceManager.h"
00044 #include "nsIGenericFactory.h"
00045 #include "nsIWebBrowserChrome.h"
00046 #include "nsIDocumentLoader.h"
00047 #include "nsCURILoader.h"
00048 #include "nsNetUtil.h"
00049 #include "nsIURL.h"
00050 #include "nsIURI.h"
00051 #include "nsIDocShell.h"
00052 #include "nsIDocShellTreeOwner.h"
00053 #include "nsIEditorDocShell.h"
00054 #include "nsISimpleEnumerator.h"
00055 #include "nsIDOMWindow.h"
00056 #include "nsIDOMWindowInternal.h"
00057 #include "nsPIDOMWindow.h"
00058 #include "nsIDOMNSEvent.h"
00059 #include "nsIPrefBranch.h"
00060 #include "nsIPrefBranch2.h"
00061 #include "nsIPrefService.h"
00062 #include "nsString.h"
00063 #include "nsCRT.h"
00064 
00065 #include "nsIDOMNode.h"
00066 #include "nsIContent.h"
00067 #include "nsIFrame.h"
00068 #include "nsFrameTraversal.h"
00069 #include "nsIDOMDocument.h"
00070 #include "nsIImageDocument.h"
00071 #include "nsIDOMHTMLDocument.h"
00072 #include "nsIDOMNSHTMLDocument.h"
00073 #include "nsIDOMHTMLElement.h"
00074 #include "nsIEventStateManager.h"
00075 #include "nsIFocusController.h"
00076 #include "nsIViewManager.h"
00077 #include "nsIScrollableView.h"
00078 #include "nsIDocument.h"
00079 #include "nsISelection.h"
00080 #include "nsISelectElement.h"
00081 #include "nsILink.h"
00082 #include "nsITextContent.h"
00083 #include "nsTextFragment.h"
00084 #include "nsIDOMNSEditableElement.h"
00085 #include "nsIDOMNSHTMLElement.h"
00086 #include "nsIEditor.h"
00087 
00088 #include "nsICaret.h"
00089 #include "nsIScriptGlobalObject.h"
00090 #include "nsIDocShellTreeItem.h"
00091 #include "nsIWebNavigation.h"
00092 #include "nsIInterfaceRequestor.h"
00093 #include "nsIInterfaceRequestorUtils.h"
00094 #include "nsContentCID.h"
00095 #include "nsLayoutCID.h"
00096 #include "nsWidgetsCID.h"
00097 #include "nsIFormControl.h"
00098 #include "nsINameSpaceManager.h"
00099 #include "nsIWindowWatcher.h"
00100 #include "nsIObserverService.h"
00101 #include "nsLayoutAtoms.h"
00102 
00103 #include "nsTypeAheadFind.h"
00104 
00105 NS_INTERFACE_MAP_BEGIN(nsTypeAheadFind)
00106   NS_INTERFACE_MAP_ENTRY(nsITypeAheadFind)
00107   NS_INTERFACE_MAP_ENTRY(nsITypeAheadFind_MOZILLA_1_8_BRANCH)
00108   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsITypeAheadFind)
00109   NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
00110   NS_INTERFACE_MAP_ENTRY(nsIObserver)
00111 NS_INTERFACE_MAP_END
00112 
00113 NS_IMPL_ADDREF(nsTypeAheadFind)
00114 NS_IMPL_RELEASE(nsTypeAheadFind)
00115 
00116 static NS_DEFINE_IID(kRangeCID, NS_RANGE_CID);
00117 static NS_DEFINE_CID(kFrameTraversalCID, NS_FRAMETRAVERSAL_CID);
00118 
00119 #define NS_FIND_CONTRACTID "@mozilla.org/embedcomp/rangefind;1"
00120 
00121 nsTypeAheadFind::nsTypeAheadFind():
00122   mLinksOnlyPref(PR_FALSE), mStartLinksOnlyPref(PR_FALSE),
00123   mLinksOnly(PR_FALSE), mCaretBrowsingOn(PR_FALSE),
00124   mLiteralTextSearchOnly(PR_FALSE), mDontTryExactMatch(PR_FALSE), 
00125   mAllTheSameChar(PR_TRUE), mRepeatingMode(eRepeatingNone), 
00126   mLastFindLength(0), mIsSoundInitialized(PR_FALSE)
00127 {
00128 }
00129 
00130 nsTypeAheadFind::~nsTypeAheadFind()
00131 {
00132   Cancel();
00133 
00134   nsCOMPtr<nsIPrefBranch2> prefInternal(do_GetService(NS_PREFSERVICE_CONTRACTID));
00135   if (prefInternal) {
00136     prefInternal->RemoveObserver("accessibility.typeaheadfind", this);
00137     prefInternal->RemoveObserver("accessibility.browsewithcaret", this);
00138   }
00139 }
00140 
00141 nsresult
00142 nsTypeAheadFind::Init(nsIDocShell* aDocShell)
00143 {
00144   nsCOMPtr<nsIPrefBranch2> prefInternal(do_GetService(NS_PREFSERVICE_CONTRACTID));
00145   mSearchRange = do_CreateInstance(kRangeCID);
00146   mStartPointRange = do_CreateInstance(kRangeCID);
00147   mEndPointRange = do_CreateInstance(kRangeCID);
00148   mFind = do_CreateInstance(NS_FIND_CONTRACTID);
00149   if (!prefInternal || !mSearchRange || !mStartPointRange || !mEndPointRange || !mFind)
00150     return NS_ERROR_FAILURE;
00151 
00152   SetDocShell(aDocShell);
00153 
00154   // ----------- Listen to prefs ------------------
00155   nsresult rv = prefInternal->AddObserver("accessibility.browsewithcaret", this, PR_FALSE);
00156   NS_ENSURE_SUCCESS(rv, rv);
00157 
00158   // ----------- Get initial preferences ----------
00159   PrefsReset();
00160 
00161   // ----------- Set search options ---------------
00162   mFind->SetCaseSensitive(PR_FALSE);
00163   mFind->SetWordBreaker(nsnull);
00164 
00165   return rv;
00166 }
00167 
00168 nsresult
00169 nsTypeAheadFind::PrefsReset()
00170 {
00171   nsCOMPtr<nsIPrefBranch> prefBranch(do_GetService(NS_PREFSERVICE_CONTRACTID));
00172   NS_ENSURE_TRUE(prefBranch, NS_ERROR_FAILURE);
00173 
00174   prefBranch->GetBoolPref("accessibility.typeaheadfind.linksonly",
00175                           &mLinksOnlyPref);
00176 
00177   prefBranch->GetBoolPref("accessibility.typeaheadfind.startlinksonly",
00178                           &mStartLinksOnlyPref);
00179 
00180   PRBool isSoundEnabled = PR_TRUE;
00181   prefBranch->GetBoolPref("accessibility.typeaheadfind.enablesound",
00182                            &isSoundEnabled);
00183   nsXPIDLCString soundStr;
00184   if (isSoundEnabled)
00185     prefBranch->GetCharPref("accessibility.typeaheadfind.soundURL", getter_Copies(soundStr));
00186 
00187   mNotFoundSoundURL = soundStr;
00188 
00189   prefBranch->GetBoolPref("accessibility.browsewithcaret",
00190                           &mCaretBrowsingOn);
00191 
00192   return NS_OK;
00193 }
00194 
00195 NS_IMETHODIMP
00196 nsTypeAheadFind::SetCaseSensitive(PRBool isCaseSensitive)
00197 {
00198   mFind->SetCaseSensitive(isCaseSensitive);
00199   return NS_OK;
00200 }
00201 
00202 NS_IMETHODIMP
00203 nsTypeAheadFind::GetCaseSensitive(PRBool* isCaseSensitive)
00204 {
00205   mFind->GetCaseSensitive(isCaseSensitive);
00206   return NS_OK;
00207 }
00208 
00209 NS_IMETHODIMP
00210 nsTypeAheadFind::SetDocShell(nsIDocShell* aDocShell)
00211 {
00212   mDocShell = do_GetWeakReference(aDocShell);
00213 
00214   mWebBrowserFind = do_GetInterface(aDocShell);
00215   NS_ENSURE_TRUE(mWebBrowserFind, NS_ERROR_FAILURE);
00216 
00217   nsCOMPtr<nsIPresShell> presShell;
00218   aDocShell->GetPresShell(getter_AddRefs(presShell));
00219   mPresShell = do_GetWeakReference(presShell);      
00220 
00221   mStartFindRange = nsnull;
00222   mStartPointRange = do_CreateInstance(kRangeCID);
00223   mSearchRange = do_CreateInstance(kRangeCID);
00224 
00225   mFoundLink = nsnull;
00226   mFoundEditable = nsnull;
00227   mCurrentWindow = nsnull;
00228 
00229   mSelectionController = nsnull;
00230 
00231   return NS_OK;
00232 }
00233 
00234 NS_IMETHODIMP
00235 nsTypeAheadFind::SetSelectionModeAndRepaint(PRInt16 aToggle)
00236 {
00237   nsCOMPtr<nsISelectionController> selectionController = 
00238     do_QueryReferent(mSelectionController);
00239   if (!selectionController) {
00240     return NS_OK;
00241   }
00242 
00243   selectionController->SetDisplaySelection(aToggle);
00244   selectionController->RepaintSelection(nsISelectionController::SELECTION_NORMAL);
00245 
00246   return NS_OK;
00247 }
00248 
00249 NS_IMETHODIMP
00250 nsTypeAheadFind::CollapseSelection()
00251 {
00252   nsCOMPtr<nsISelectionController> selectionController = 
00253     do_QueryReferent(mSelectionController);
00254   if (!selectionController) {
00255     return NS_OK;
00256   }
00257 
00258   nsCOMPtr<nsISelection> selection;
00259   selectionController->GetSelection(nsISelectionController::SELECTION_NORMAL,
00260                                      getter_AddRefs(selection));
00261   if (selection)
00262     selection->CollapseToStart();
00263 
00264   return NS_OK;
00265 }
00266 
00267 NS_IMETHODIMP
00268 nsTypeAheadFind::Observe(nsISupports *aSubject, const char *aTopic,
00269                          const PRUnichar *aData)
00270 {
00271   if (!nsCRT::strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID))
00272     return PrefsReset();
00273 
00274   return NS_OK;
00275 }
00276 
00277 void
00278 nsTypeAheadFind::SaveFind()
00279 {
00280   if (mWebBrowserFind)
00281     mWebBrowserFind->SetSearchString(PromiseFlatString(mTypeAheadBuffer).get());
00282   
00283   // save the length of this find for "not found" sound
00284   mLastFindLength = mTypeAheadBuffer.Length();
00285 }
00286 
00287 void
00288 nsTypeAheadFind::PlayNotFoundSound()
00289 {
00290   if (mNotFoundSoundURL.IsEmpty())    // no sound
00291     return;
00292 
00293   if (!mSoundInterface)
00294     mSoundInterface = do_CreateInstance("@mozilla.org/sound;1");
00295 
00296   if (mSoundInterface) {
00297     mIsSoundInitialized = PR_TRUE;
00298 
00299     if (mNotFoundSoundURL.Equals("beep")) {
00300       mSoundInterface->Beep();
00301       return;
00302     }
00303 
00304     nsCOMPtr<nsIURI> soundURI;
00305     if (mNotFoundSoundURL.Equals("default"))
00306       NS_NewURI(getter_AddRefs(soundURI), NS_LITERAL_CSTRING(TYPEAHEADFIND_NOTFOUND_WAV_URL));
00307     else
00308       NS_NewURI(getter_AddRefs(soundURI), mNotFoundSoundURL);
00309 
00310     nsCOMPtr<nsIURL> soundURL(do_QueryInterface(soundURI));
00311     if (soundURL)
00312       mSoundInterface->Play(soundURL);
00313   }
00314 }
00315 
00316 nsresult
00317 nsTypeAheadFind::FindItNow(nsIPresShell *aPresShell,
00318                            PRBool aIsRepeatingSameChar, PRBool aIsLinksOnly,
00319                            PRBool aIsFirstVisiblePreferred, PRBool aFindNext,
00320                            PRUint16* aResult)
00321 {
00322   *aResult = FIND_NOTFOUND;
00323   mFoundLink = nsnull;
00324   mFoundEditable = nsnull;
00325   mCurrentWindow = nsnull;
00326   nsCOMPtr<nsIPresShell> startingPresShell (GetPresShell());
00327   if (!startingPresShell) {    
00328     nsCOMPtr<nsIDocShell> ds = do_QueryReferent(mDocShell);
00329     NS_ENSURE_TRUE(ds, NS_ERROR_FAILURE);
00330 
00331     ds->GetPresShell(getter_AddRefs(startingPresShell));
00332     mPresShell = do_GetWeakReference(startingPresShell);    
00333   }  
00334 
00335   nsCOMPtr<nsIPresShell> presShell(aPresShell);
00336 
00337   if (!presShell) {
00338     presShell = startingPresShell;  // this is the current document
00339 
00340     if (!presShell)
00341       return NS_ERROR_FAILURE;
00342   }
00343 
00344   nsRefPtr<nsPresContext> presContext = presShell->GetPresContext();
00345 
00346   if (!presContext)
00347     return NS_ERROR_FAILURE;
00348 
00349   nsCOMPtr<nsISelection> selection;
00350   nsCOMPtr<nsISelectionController> selectionController = 
00351     do_QueryReferent(mSelectionController);
00352   if (!selectionController) {
00353     GetSelection(presShell, getter_AddRefs(selectionController),
00354                  getter_AddRefs(selection)); // cache for reuse
00355     mSelectionController = do_GetWeakReference(selectionController);
00356   } else {
00357     selectionController->GetSelection(
00358       nsISelectionController::SELECTION_NORMAL, getter_AddRefs(selection));
00359   }
00360  
00361   nsCOMPtr<nsISupports> startingContainer = presContext->GetContainer();
00362   nsCOMPtr<nsIDocShellTreeItem> treeItem(do_QueryInterface(startingContainer));
00363   NS_ASSERTION(treeItem, "Bug 175321 Crashes with Type Ahead Find [@ nsTypeAheadFind::FindItNow]");
00364   if (!treeItem)
00365     return NS_ERROR_FAILURE;
00366 
00367   nsCOMPtr<nsIDocShellTreeItem> rootContentTreeItem;
00368   nsCOMPtr<nsIDocShell> currentDocShell;
00369   nsCOMPtr<nsIDocShell> startingDocShell(do_QueryInterface(startingContainer));
00370 
00371   treeItem->GetSameTypeRootTreeItem(getter_AddRefs(rootContentTreeItem));
00372   nsCOMPtr<nsIDocShell> rootContentDocShell =
00373     do_QueryInterface(rootContentTreeItem);
00374 
00375   if (!rootContentDocShell)
00376     return NS_ERROR_FAILURE;
00377 
00378   nsCOMPtr<nsISimpleEnumerator> docShellEnumerator;
00379   rootContentDocShell->GetDocShellEnumerator(nsIDocShellTreeItem::typeContent,
00380                                              nsIDocShell::ENUMERATE_FORWARDS,
00381                                              getter_AddRefs(docShellEnumerator));
00382 
00383   // Default: can start at the current document
00384   nsCOMPtr<nsISupports> currentContainer = startingContainer =
00385     do_QueryInterface(rootContentDocShell);
00386 
00387   // Iterate up to current shell, if there's more than 1 that we're
00388   // dealing with
00389   PRBool hasMoreDocShells;
00390 
00391   while (NS_SUCCEEDED(docShellEnumerator->HasMoreElements(&hasMoreDocShells)) && hasMoreDocShells) {
00392     docShellEnumerator->GetNext(getter_AddRefs(currentContainer));
00393     currentDocShell = do_QueryInterface(currentContainer);
00394     if (!currentDocShell || currentDocShell == startingDocShell || aIsFirstVisiblePreferred)
00395       break;    
00396   }
00397 
00398   // ------------ Get ranges ready ----------------
00399   nsCOMPtr<nsIDOMRange> returnRange;
00400   nsCOMPtr<nsIPresShell> focusedPS;
00401   if (NS_FAILED(GetSearchContainers(currentContainer,
00402                                     (!aIsFirstVisiblePreferred ||
00403                                      mStartFindRange) ?
00404                                     selectionController.get() : nsnull,
00405                                     aIsRepeatingSameChar,
00406                                     aIsFirstVisiblePreferred, 
00407                                     getter_AddRefs(presShell),
00408                                     getter_AddRefs(presContext)))) {
00409     return NS_ERROR_FAILURE;
00410   }
00411 
00412   PRInt16 rangeCompareResult = 0;
00413   mStartPointRange->CompareBoundaryPoints(nsIDOMRange::START_TO_START, mSearchRange, &rangeCompareResult);
00414   // No need to wrap find in doc if starting at beginning
00415   PRBool hasWrapped = (rangeCompareResult < 0);
00416 
00417   nsAutoString findBuffer;
00418   if (aIsRepeatingSameChar)
00419     findBuffer = mTypeAheadBuffer.First();
00420   else
00421     findBuffer = PromiseFlatString(mTypeAheadBuffer);
00422 
00423   if (findBuffer.IsEmpty())
00424     return NS_ERROR_FAILURE;
00425 
00426   mFind->SetFindBackwards(mRepeatingMode == eRepeatingCharReverse || mRepeatingMode == eRepeatingReverse);
00427 
00428   while (PR_TRUE) {    // ----- Outer while loop: go through all docs -----
00429     while (PR_TRUE) {  // === Inner while loop: go through a single doc ===
00430       mFind->Find(findBuffer.get(), mSearchRange, mStartPointRange,
00431                   mEndPointRange, getter_AddRefs(returnRange));            
00432       
00433       if (!returnRange)
00434         break;  // Nothing found in this doc, go to outer loop (try next doc)
00435 
00436       // ------- Test resulting found range for success conditions ------
00437       PRBool isInsideLink = PR_FALSE, isStartingLink = PR_FALSE;
00438 
00439       if (aIsLinksOnly) {
00440         // Don't check if inside link when searching all text
00441         RangeStartsInsideLink(returnRange, presShell, &isInsideLink,
00442                               &isStartingLink);
00443       }
00444 
00445       PRBool usesIndependentSelection;
00446       if (!IsRangeVisible(presShell, presContext, returnRange,
00447                           aIsFirstVisiblePreferred, PR_FALSE,
00448                           getter_AddRefs(mStartPointRange), 
00449                           &usesIndependentSelection) ||
00450           (aIsRepeatingSameChar && !isStartingLink) ||
00451           (aIsLinksOnly && !isInsideLink) ||
00452           (mStartLinksOnlyPref && aIsLinksOnly && !isStartingLink)) {
00453         // ------ Failure ------
00454         // Start find again from here
00455         returnRange->CloneRange(getter_AddRefs(mStartPointRange));
00456 
00457         // Collapse to end
00458         mStartPointRange->Collapse(mRepeatingMode == eRepeatingReverse || mRepeatingMode == eRepeatingCharReverse);
00459 
00460         continue;
00461       }
00462 
00463       // ------ Success! -------
00464       // Hide old selection (new one may be on a different controller)
00465       if (selection) {
00466         selection->CollapseToStart();
00467         SetSelectionModeAndRepaint(nsISelectionController::SELECTION_ON);
00468       }
00469 
00470       // Make sure new document is selected
00471       if (presShell != startingPresShell) {
00472         // We are in a new document (because of frames/iframes)
00473         mPresShell = do_GetWeakReference(presShell);
00474       }
00475 
00476       if (usesIndependentSelection) {
00477         // We may be inside an editable element, and therefore the selection
00478         // may be controlled by a different selection controller.  Walk up the
00479         // chain of parent nodes to see if we find one.
00480         nsCOMPtr<nsIDOMNode> node;
00481         returnRange->GetStartContainer(getter_AddRefs(node));
00482         while (node) {
00483           nsCOMPtr<nsIDOMNSEditableElement> editable = do_QueryInterface(node);
00484           if (editable) {
00485             // Inside an editable element.  Get the correct selection 
00486             // controller and selection.
00487             nsCOMPtr<nsIEditor> editor;
00488             editable->GetEditor(getter_AddRefs(editor));
00489             NS_ASSERTION(editor, "Editable element has no editor!");
00490             if (!editor) {
00491               break;
00492             }
00493             editor->GetSelectionController(
00494               getter_AddRefs(selectionController));
00495             if (selectionController) {
00496               selectionController->GetSelection(
00497                 nsISelectionController::SELECTION_NORMAL, 
00498                 getter_AddRefs(selection));
00499             }
00500             mFoundEditable = do_QueryInterface(node);
00501 
00502             // Check if find field is focused, if so do nothing
00503             if (FindFieldHasFocus(presContext)) {
00504               break;
00505             }
00506 
00507             // Otherwise move focus/caret to editable element
00508             nsCOMPtr<nsIContent> content = do_QueryInterface(mFoundEditable);
00509             if (content) {
00510               content->SetFocus(presContext);
00511               presContext->EventStateManager()->MoveCaretToFocus();
00512             }
00513             break;
00514           }
00515           nsIDOMNode* tmp = node;
00516           tmp->GetParentNode(getter_AddRefs(node));
00517         }
00518 
00519         // If we reach here without setting mFoundEditable, then something
00520         // besides editable elements can cause us to have an independent
00521         // selection controller.  I don't know whether this is possible.
00522         // Currently, we simply fall back to grabbing the document's selection
00523         // controller in this case.  Perhaps we should reject this find match
00524         // and search again.
00525         NS_ASSERTION(mFoundEditable, "Independent selection controller on "
00526                      "non-editable element!");
00527       }
00528 
00529       if (!mFoundEditable) {
00530         // Not using a separate selection controller, so just get the
00531         // document's controller and selection.
00532         GetSelection(presShell, getter_AddRefs(selectionController), 
00533                      getter_AddRefs(selection));
00534       }
00535       mSelectionController = do_GetWeakReference(selectionController);
00536 
00537       // Select the found text
00538       if (selection) {
00539         selection->RemoveAllRanges();
00540         selection->AddRange(returnRange);
00541       }
00542 
00543       if (!mFoundEditable) {
00544         currentDocShell->SetHasFocus(PR_TRUE);  // What does this do?
00545 
00546         // Keep track of whether we've found a link, so we can focus it, jump
00547         // to its target, etc.
00548         nsIEventStateManager *esm = presContext->EventStateManager();
00549         PRBool isSelectionWithFocus;
00550         esm->MoveFocusToCaret(PR_TRUE, &isSelectionWithFocus);
00551         if (isSelectionWithFocus) {
00552           nsCOMPtr<nsIContent> lastFocusedContent;
00553           esm->GetLastFocusedContent(getter_AddRefs(lastFocusedContent));
00554           nsCOMPtr<nsIDOMElement>
00555             lastFocusedElement(do_QueryInterface(lastFocusedContent));
00556           mFoundLink = lastFocusedElement;
00557         }
00558       }
00559 
00560       // Change selection color to ATTENTION and scroll to it.  Careful: we
00561       // must wait until after we goof with focus above before changing to
00562       // ATTENTION, or when we MoveFocusToCaret() and the selection is not on a
00563       // link, we'll blur, which will lose the ATTENTION.
00564       if (selectionController) {
00565         SetSelectionModeAndRepaint(nsISelectionController::SELECTION_ATTENTION);
00566         selectionController->ScrollSelectionIntoView(
00567           nsISelectionController::SELECTION_NORMAL, 
00568           nsISelectionController::SELECTION_FOCUS_REGION, PR_TRUE);
00569       }
00570 
00571       nsCOMPtr<nsIDocument> doc =
00572         do_QueryInterface(presShell->GetDocument());
00573       NS_ASSERTION(doc, "Wow, presShell doesn't have document!");
00574       mCurrentWindow = do_QueryInterface(doc->GetScriptGlobalObject());
00575 
00576       if (hasWrapped) {
00577         *aResult = FIND_WRAPPED;
00578       } else {
00579         *aResult = FIND_FOUND;
00580       }
00581 
00582       return NS_OK;
00583     }
00584 
00585     // ======= end-inner-while (go through a single document) ==========
00586 
00587     // ---------- Nothing found yet, try next document  -------------
00588     PRBool hasTriedFirstDoc = PR_FALSE;
00589     do {
00590       // ==== Second inner loop - get another while  ====
00591       if (NS_SUCCEEDED(docShellEnumerator->HasMoreElements(&hasMoreDocShells))
00592           && hasMoreDocShells) {
00593         docShellEnumerator->GetNext(getter_AddRefs(currentContainer));
00594         NS_ASSERTION(currentContainer, "HasMoreElements lied to us!");
00595         currentDocShell = do_QueryInterface(currentContainer);
00596 
00597         if (currentDocShell)
00598           break;
00599       }
00600       else if (hasTriedFirstDoc)  // Avoid potential infinite loop
00601         return NS_ERROR_FAILURE;  // No content doc shells
00602 
00603       // Reached last doc shell, loop around back to first doc shell
00604       rootContentDocShell->GetDocShellEnumerator(nsIDocShellTreeItem::typeContent,
00605                                                  nsIDocShell::ENUMERATE_FORWARDS,
00606                                                  getter_AddRefs(docShellEnumerator));
00607       hasTriedFirstDoc = PR_TRUE;      
00608     } while (docShellEnumerator);  // ==== end second inner while  ===
00609 
00610     PRBool continueLoop = PR_FALSE;
00611     if (currentDocShell != startingDocShell)
00612       continueLoop = PR_TRUE;  // Try next document
00613     else if (!hasWrapped || aIsFirstVisiblePreferred) {
00614       // Finished searching through docshells:
00615       // If aFirstVisiblePreferred == PR_TRUE, we may need to go through all
00616       // docshells twice -once to look for visible matches, the second time
00617       // for any match
00618       aIsFirstVisiblePreferred = PR_FALSE;
00619       hasWrapped = PR_TRUE;
00620       continueLoop = PR_TRUE; // Go through all docs again
00621     }
00622 
00623     if (continueLoop) {
00624       if (NS_FAILED(GetSearchContainers(currentContainer,
00625                                         nsnull,
00626                                         aIsRepeatingSameChar,
00627                                         aIsFirstVisiblePreferred,
00628                                         getter_AddRefs(presShell),
00629                                         getter_AddRefs(presContext)))) {
00630         continue;
00631       }
00632 
00633       if (mRepeatingMode == eRepeatingCharReverse || mRepeatingMode == eRepeatingReverse) {
00634         // Reverse mode: swap start and end points, so that we start
00635         // at end of document and go to beginning
00636         nsCOMPtr<nsIDOMRange> tempRange;
00637         mStartPointRange->CloneRange(getter_AddRefs(tempRange));
00638         mStartPointRange = mEndPointRange;
00639         mEndPointRange = tempRange;
00640       }
00641 
00642       continue;
00643     }
00644 
00645     // ------------- Failed --------------
00646     break;
00647   }   // end-outer-while: go through all docs
00648 
00649   return NS_ERROR_FAILURE;
00650 }
00651 
00652 NS_IMETHODIMP
00653 nsTypeAheadFind::GetSearchString(nsAString& aSearchString)
00654 {
00655   aSearchString = mTypeAheadBuffer;
00656   return NS_OK;
00657 }
00658 
00659 NS_IMETHODIMP
00660 nsTypeAheadFind::GetFoundLink(nsIDOMElement** aFoundLink)
00661 {
00662   NS_ENSURE_ARG_POINTER(aFoundLink);
00663   *aFoundLink = mFoundLink;
00664   NS_IF_ADDREF(*aFoundLink);
00665   return NS_OK;
00666 }
00667 
00668 NS_IMETHODIMP
00669 nsTypeAheadFind::GetFoundEditable(nsIDOMElement** aFoundEditable)
00670 {
00671   NS_ENSURE_ARG_POINTER(aFoundEditable);
00672   *aFoundEditable = mFoundEditable;
00673   NS_IF_ADDREF(*aFoundEditable);
00674   return NS_OK;
00675 }
00676 
00677 NS_IMETHODIMP
00678 nsTypeAheadFind::GetCurrentWindow(nsIDOMWindow** aCurrentWindow)
00679 {
00680   NS_ENSURE_ARG_POINTER(aCurrentWindow);
00681   *aCurrentWindow = mCurrentWindow;
00682   NS_IF_ADDREF(*aCurrentWindow);
00683   return NS_OK;
00684 }
00685 
00686 NS_IMETHODIMP
00687 nsTypeAheadFind::GetFocusLinks(PRBool* aFocusLinks)
00688 {
00689   return NS_OK;
00690 }
00691 
00692 NS_IMETHODIMP
00693 nsTypeAheadFind::SetFocusLinks(PRBool aFocusLinks)
00694 {
00695   return NS_OK;
00696 }
00697 
00698 nsresult
00699 nsTypeAheadFind::GetSearchContainers(nsISupports *aContainer,
00700                                      nsISelectionController *aSelectionController,
00701                                      PRBool aIsRepeatingSameChar,
00702                                      PRBool aIsFirstVisiblePreferred,
00703                                      nsIPresShell **aPresShell,
00704                                      nsPresContext **aPresContext)
00705 {
00706   NS_ENSURE_ARG_POINTER(aContainer);
00707   NS_ENSURE_ARG_POINTER(aPresShell);
00708   NS_ENSURE_ARG_POINTER(aPresContext);
00709 
00710   *aPresShell = nsnull;
00711   *aPresContext = nsnull;
00712 
00713   nsCOMPtr<nsIDocShell> docShell(do_QueryInterface(aContainer));
00714   if (!docShell)
00715     return NS_ERROR_FAILURE;
00716 
00717   nsCOMPtr<nsIPresShell> presShell;
00718   docShell->GetPresShell(getter_AddRefs(presShell));
00719 
00720   nsRefPtr<nsPresContext> presContext;
00721   docShell->GetPresContext(getter_AddRefs(presContext));
00722 
00723   if (!presShell || !presContext)
00724     return NS_ERROR_FAILURE;
00725 
00726   nsIDocument* doc = presShell->GetDocument();
00727 
00728   if (!doc)
00729     return NS_ERROR_FAILURE;
00730 
00731   nsCOMPtr<nsIContent> rootContent;
00732   nsCOMPtr<nsIDOMHTMLDocument> htmlDoc(do_QueryInterface(doc));
00733   if (htmlDoc) {
00734     nsCOMPtr<nsIDOMHTMLElement> bodyEl;
00735     htmlDoc->GetBody(getter_AddRefs(bodyEl));
00736     rootContent = do_QueryInterface(bodyEl);
00737   }
00738 
00739   if (!rootContent)
00740     rootContent = doc->GetRootContent();
00741  
00742   nsCOMPtr<nsIDOMNode> rootNode(do_QueryInterface(rootContent));
00743 
00744   if (!rootNode)
00745     return NS_ERROR_FAILURE;
00746 
00747   PRUint32 childCount = rootContent->GetChildCount();
00748 
00749   mSearchRange->SelectNodeContents(rootNode);
00750 
00751   mEndPointRange->SetEnd(rootNode, childCount);
00752   mEndPointRange->Collapse(PR_FALSE); // collapse to end
00753 
00754   // Consider current selection as null if
00755   // it's not in the currently focused document
00756   nsCOMPtr<nsIDOMRange> currentSelectionRange;
00757   nsCOMPtr<nsIPresShell> selectionPresShell = GetPresShell();
00758   if (aSelectionController && selectionPresShell && selectionPresShell == presShell) {
00759     nsCOMPtr<nsISelection> selection;
00760     aSelectionController->GetSelection(
00761       nsISelectionController::SELECTION_NORMAL, getter_AddRefs(selection));
00762     if (selection)
00763       selection->GetRangeAt(0, getter_AddRefs(currentSelectionRange));
00764   }
00765 
00766   if (!currentSelectionRange) {
00767     // Ensure visible range, move forward if necessary
00768     // This uses ignores the return value, but usese the side effect of
00769     // IsRangeVisible. It returns the first visible range after searchRange
00770     IsRangeVisible(presShell, presContext, mSearchRange, 
00771                    aIsFirstVisiblePreferred, PR_TRUE, 
00772                    getter_AddRefs(mStartPointRange), nsnull);
00773   }
00774   else {
00775     PRInt32 startOffset;
00776     nsCOMPtr<nsIDOMNode> startNode;
00777     if ((aIsRepeatingSameChar && mRepeatingMode != eRepeatingCharReverse) || 
00778         mRepeatingMode == eRepeatingForward) {
00779       currentSelectionRange->GetEndContainer(getter_AddRefs(startNode));
00780       currentSelectionRange->GetEndOffset(&startOffset);
00781     }
00782     else {
00783       currentSelectionRange->GetStartContainer(getter_AddRefs(startNode));
00784       currentSelectionRange->GetStartOffset(&startOffset);
00785     }
00786     if (!startNode)
00787       startNode = rootNode;    
00788 
00789     // We need to set the start point this way, other methods haven't worked
00790     mStartPointRange->SelectNode(startNode);
00791     mStartPointRange->SetStart(startNode, startOffset);
00792   }
00793 
00794   mStartPointRange->Collapse(PR_TRUE); // collapse to start
00795 
00796   *aPresShell = presShell;
00797   NS_ADDREF(*aPresShell);
00798 
00799   *aPresContext = presContext;
00800   NS_ADDREF(*aPresContext);
00801 
00802   return NS_OK;
00803 }
00804 
00805 void
00806 nsTypeAheadFind::RangeStartsInsideLink(nsIDOMRange *aRange,
00807                                        nsIPresShell *aPresShell,
00808                                        PRBool *aIsInsideLink,
00809                                        PRBool *aIsStartingLink)
00810 {
00811   *aIsInsideLink = PR_FALSE;
00812   *aIsStartingLink = PR_TRUE;
00813 
00814   // ------- Get nsIContent to test -------
00815   nsCOMPtr<nsIDOMNode> startNode;
00816   nsCOMPtr<nsIContent> startContent, origContent;
00817   aRange->GetStartContainer(getter_AddRefs(startNode));
00818   PRInt32 startOffset;
00819   aRange->GetStartOffset(&startOffset);
00820 
00821   startContent = do_QueryInterface(startNode);
00822   if (!startContent) {
00823     NS_NOTREACHED("startContent should never be null");
00824     return;
00825   }
00826   origContent = startContent;
00827 
00828   if (startContent->IsContentOfType(nsIContent::eELEMENT)) {
00829     nsIContent *childContent = startContent->GetChildAt(startOffset);
00830     if (childContent) {
00831       startContent = childContent;
00832     }
00833   }
00834   else if (startOffset > 0) {
00835     nsCOMPtr<nsITextContent> textContent(do_QueryInterface(startContent));
00836 
00837     if (textContent) {
00838       // look for non whitespace character before start offset
00839       const nsTextFragment *textFrag = textContent->Text();
00840 
00841       for (PRInt32 index = 0; index < startOffset; index++) {
00842         if (!XP_IS_SPACE(textFrag->CharAt(index))) {
00843           *aIsStartingLink = PR_FALSE;  // not at start of a node
00844 
00845           break;
00846         }
00847       }
00848     }
00849   }
00850 
00851   // ------- Check to see if inside link ---------
00852 
00853   // We now have the correct start node for the range
00854   // Search for links, starting with startNode, and going up parent chain
00855 
00856   nsCOMPtr<nsIAtom> tag, hrefAtom(do_GetAtom("href"));
00857   nsCOMPtr<nsIAtom> typeAtom(do_GetAtom("type"));
00858 
00859   while (PR_TRUE) {
00860     // Keep testing while textContent is equal to something,
00861     // eventually we'll run out of ancestors
00862 
00863     if (startContent->IsContentOfType(nsIContent::eHTML)) {
00864       nsCOMPtr<nsILink> link(do_QueryInterface(startContent));
00865       if (link) {
00866         // Check to see if inside HTML link
00867         *aIsInsideLink = startContent->HasAttr(kNameSpaceID_None, hrefAtom);
00868         return;
00869       }
00870     }
00871     else {
00872       // Any xml element can be an xlink
00873       *aIsInsideLink = startContent->HasAttr(kNameSpaceID_XLink, hrefAtom);
00874       if (*aIsInsideLink) {
00875         nsAutoString xlinkType;
00876         startContent->GetAttr(kNameSpaceID_XLink, typeAtom, xlinkType);
00877         if (!xlinkType.Equals(NS_LITERAL_STRING("simple"))) {
00878           *aIsInsideLink = PR_FALSE;  // Xlink must be type="simple"
00879         }
00880 
00881         return;
00882       }
00883     }
00884 
00885     // Get the parent
00886     nsCOMPtr<nsIContent> parent = startContent->GetParent();
00887     if (!parent)
00888       break;
00889 
00890     nsIContent *parentsFirstChild = parent->GetChildAt(0);
00891     nsCOMPtr<nsITextContent> textContent (do_QueryInterface(parentsFirstChild));
00892 
00893     if (textContent) {
00894       // We don't want to look at a whitespace-only first child
00895       if (textContent->IsOnlyWhitespace())
00896         parentsFirstChild = parent->GetChildAt(1);
00897     }
00898 
00899     if (parentsFirstChild != startContent) {
00900       // startContent wasn't a first child, so we conclude that
00901       // if this is inside a link, it's not at the beginning of it
00902       *aIsStartingLink = PR_FALSE;
00903     }
00904 
00905     startContent = parent;
00906   }
00907 
00908   *aIsStartingLink = PR_FALSE;
00909 }
00910 
00911 NS_IMETHODIMP
00912 nsTypeAheadFind::FindPrevious(PRUint16* aResult)
00913 {
00914   return FindInternal(PR_TRUE, aResult);
00915 }
00916 
00917 NS_IMETHODIMP
00918 nsTypeAheadFind::FindNext(PRUint16* aResult)
00919 {
00920   return FindInternal(PR_FALSE, aResult); 
00921 }
00922 
00923 nsresult
00924 nsTypeAheadFind::FindInternal(PRBool aFindBackwards, PRUint16* aResult)
00925 {
00926   *aResult = FIND_NOTFOUND;
00927 
00928   if (mTypeAheadBuffer.IsEmpty())
00929     return NS_OK;
00930 
00931   PRBool repeatingSameChar = PR_FALSE;
00932 
00933   if (mRepeatingMode == eRepeatingChar || 
00934       mRepeatingMode == eRepeatingCharReverse) {
00935     mRepeatingMode = aFindBackwards? eRepeatingCharReverse: eRepeatingChar;
00936     repeatingSameChar = PR_TRUE;
00937   }
00938   else {
00939     mRepeatingMode = aFindBackwards? eRepeatingReverse: eRepeatingForward;
00940   }
00941   mLiteralTextSearchOnly = PR_TRUE;
00942 
00943   if (NS_FAILED(FindItNow(nsnull, repeatingSameChar, mLinksOnly, PR_FALSE,
00944                           !aFindBackwards, aResult)))
00945     mRepeatingMode = eRepeatingNone;
00946 
00947   return NS_OK;
00948 }
00949 
00950 nsresult
00951 nsTypeAheadFind::Cancel()
00952 {
00953   if (mRepeatingMode != eRepeatingNone)
00954     mTypeAheadBuffer.Truncate();  
00955 
00956   // These will be initialized to their true values after
00957   // the first character is typed
00958   mCaretBrowsingOn = PR_FALSE;
00959   mLiteralTextSearchOnly = PR_FALSE;
00960   mDontTryExactMatch = PR_FALSE;
00961   mStartFindRange = nsnull;
00962   mAllTheSameChar = PR_TRUE; // Until at least 2 different chars are typed
00963 
00964   mSelectionController = nsnull;
00965 
00966   return NS_OK;
00967 }
00968 
00969 NS_IMETHODIMP
00970 nsTypeAheadFind::Find(const nsAString& aSearchString, PRBool aLinksOnly,
00971                       PRUint16* aResult)
00972 {
00973   *aResult = FIND_NOTFOUND;
00974 
00975   nsCOMPtr<nsIPresShell> presShell (GetPresShell());
00976   if (!presShell) {    
00977     nsCOMPtr<nsIDocShell> ds (do_QueryReferent(mDocShell));
00978     NS_ENSURE_TRUE(ds, NS_ERROR_FAILURE);
00979 
00980     ds->GetPresShell(getter_AddRefs(presShell));
00981     mPresShell = do_GetWeakReference(presShell);    
00982   }  
00983   nsCOMPtr<nsISelection> selection;
00984   nsCOMPtr<nsISelectionController> selectionController = 
00985     do_QueryReferent(mSelectionController);
00986   if (!selectionController) {
00987     GetSelection(presShell, getter_AddRefs(selectionController),
00988                  getter_AddRefs(selection)); // cache for reuse
00989     mSelectionController = do_GetWeakReference(selectionController);
00990   } else {
00991     selectionController->GetSelection(
00992       nsISelectionController::SELECTION_NORMAL, getter_AddRefs(selection));
00993   }
00994 
00995   if (selection)
00996     selection->CollapseToStart();
00997 
00998   if (aSearchString.IsEmpty()) {
00999     mTypeAheadBuffer = aSearchString;
01000     *aResult = FIND_FOUND;
01001     return Cancel();
01002   }
01003 
01004   PRBool atEnd = PR_FALSE;    
01005   if (mTypeAheadBuffer.Length()) {
01006     const nsAString& oldStr = Substring(mTypeAheadBuffer, 0, mTypeAheadBuffer.Length());
01007     const nsAString& newStr = Substring(aSearchString, 0, mTypeAheadBuffer.Length());
01008     if (oldStr.Equals(newStr))
01009       atEnd = PR_TRUE;
01010   
01011     const nsAString& newStr2 = Substring(aSearchString, 0, aSearchString.Length());
01012     const nsAString& oldStr2 = Substring(mTypeAheadBuffer, 0, aSearchString.Length());
01013     if (oldStr2.Equals(newStr2))
01014       atEnd = PR_TRUE;
01015     
01016     if (!atEnd)
01017       mStartFindRange = nsnull;
01018   }
01019 
01020   if (!mIsSoundInitialized && !mNotFoundSoundURL.IsEmpty()) {
01021     // This makes sure system sound library is loaded so that
01022     // there's no lag before the first sound is played
01023     // by waiting for the first keystroke, we still get the startup time benefits.
01024     mIsSoundInitialized = PR_TRUE;
01025     mSoundInterface = do_CreateInstance("@mozilla.org/sound;1");
01026     if (mSoundInterface && !mNotFoundSoundURL.Equals(NS_LITERAL_CSTRING("beep"))) {
01027       mSoundInterface->Init();
01028     }
01029   }
01030 
01031   mLinksOnly = aLinksOnly;  
01032 
01033 #ifdef XP_WIN
01034   // After each keystroke, ensure sound object is destroyed, to free up memory 
01035   // allocated for error sound, otherwise Windows' nsISound impl 
01036   // holds onto the last played sound, using up memory.
01037   mSoundInterface = nsnull;
01038 #endif
01039 
01040   PRInt32 bufferLength = mTypeAheadBuffer.Length();
01041 
01042   // --------- New char in repeated char mode ---------
01043   if ((mRepeatingMode == eRepeatingChar ||
01044            mRepeatingMode == eRepeatingCharReverse) && 
01045            bufferLength > 1/* && aChar != mTypeAheadBuffer.First()*/) {
01046     // If they repeat the same character and then change, such as aaaab
01047     // start over with new char as a repeated char find
01048     //mTypeAheadBuffer = aChar;
01049   }
01050   // ------- Set repeating mode ---------
01051   else if (bufferLength > 0) /*mTypeAheadBuffer.First() != aChar*/ {
01052     mRepeatingMode = eRepeatingNone;
01053     mAllTheSameChar = PR_FALSE; 
01054   }
01055 
01056   mTypeAheadBuffer = aSearchString;
01057 
01058   PRBool isFirstVisiblePreferred = PR_FALSE;
01059 
01060   // --------- Initialize find if 1st char ----------
01061   if (bufferLength == 0) {
01062     // Reset links only to default, if not manually set
01063     // by the user via ' or / keypress at beginning
01064     if (!mLinksOnly)
01065       mLinksOnly = mLinksOnlyPref;
01066  
01067     mRepeatingMode = eRepeatingNone;
01068 
01069     // If you can see the selection (not collapsed or thru caret browsing),
01070     // or if already focused on a page element, start there.
01071     // Otherwise we're going to start at the first visible element
01072     PRBool isSelectionCollapsed = PR_TRUE;
01073     if (selection)
01074       selection->GetIsCollapsed(&isSelectionCollapsed);
01075 
01076     // If true, we will scan from top left of visible area
01077     // If false, we will scan from start of selection
01078     isFirstVisiblePreferred = !atEnd && !mCaretBrowsingOn && isSelectionCollapsed;
01079     if (isFirstVisiblePreferred) {
01080       // Get focused content from esm. If it's null, the document is focused.
01081       // If not, make sure the selection is in sync with the focus, so we can 
01082       // start our search from there.
01083       nsCOMPtr<nsIContent> focusedContent;
01084       nsPresContext* presContext = presShell->GetPresContext();
01085       NS_ENSURE_TRUE(presContext, NS_OK);
01086 
01087       nsIEventStateManager *esm = presContext->EventStateManager();
01088       esm->GetFocusedContent(getter_AddRefs(focusedContent));
01089       if (focusedContent) {
01090         esm->MoveCaretToFocus();
01091         isFirstVisiblePreferred = PR_FALSE;
01092       }
01093     }
01094   }
01095 
01096   // ----------- Find the text! ---------------------
01097   nsresult rv = NS_ERROR_FAILURE;
01098 
01099   if (!mDontTryExactMatch) {
01100     // Regular find, not repeated char find
01101 
01102     // Prefer to find exact match
01103     rv = FindItNow(nsnull, PR_FALSE, mLinksOnly, isFirstVisiblePreferred,
01104                    PR_FALSE, aResult);
01105   }
01106 
01107   // ---------Handle success or failure ---------------
01108   if (NS_SUCCEEDED(rv)) {
01109     if (mTypeAheadBuffer.Length() == 1) {
01110       // If first letter, store where the first find succeeded
01111       // (mStartFindRange)
01112 
01113       mStartFindRange = nsnull;
01114       if (selection) {
01115         nsCOMPtr<nsIDOMRange> startFindRange;
01116         selection->GetRangeAt(0, getter_AddRefs(startFindRange));
01117         if (startFindRange)
01118           startFindRange->CloneRange(getter_AddRefs(mStartFindRange));
01119       }
01120     }
01121   }
01122   else {
01123     mRepeatingMode = eRepeatingNone;
01124 
01125     // Error sound
01126     if (mTypeAheadBuffer.Length() > mLastFindLength)
01127       PlayNotFoundSound();
01128   }
01129 
01130   SaveFind();
01131   return NS_OK;
01132 }
01133 
01134 void
01135 nsTypeAheadFind::GetSelection(nsIPresShell *aPresShell,
01136                               nsISelectionController **aSelCon,
01137                               nsISelection **aDOMSel)
01138 {
01139   if (!aPresShell)
01140     return;
01141 
01142   // if aCurrentNode is nsnull, get selection for document
01143   *aDOMSel = nsnull;
01144 
01145   nsPresContext* presContext = aPresShell->GetPresContext();
01146 
01147   nsIFrame *frame = aPresShell->GetRootFrame();
01148 
01149   if (presContext && frame) {
01150     frame->GetSelectionController(presContext, aSelCon);
01151     if (*aSelCon) {
01152       (*aSelCon)->GetSelection(nsISelectionController::SELECTION_NORMAL,
01153                                aDOMSel);
01154     }
01155   }
01156 }
01157 
01158 PRBool
01159 nsTypeAheadFind::FindFieldHasFocus(nsPresContext *aPresContext)
01160 {
01161   NS_ENSURE_ARG_POINTER(aPresContext);
01162 
01163   nsCOMPtr<nsISupports> container = aPresContext->GetContainer();
01164   nsCOMPtr<nsPIDOMWindow> window = do_GetInterface(container);
01165   if (!window) {
01166     return PR_FALSE;
01167   }
01168 
01169   nsIFocusController* focusController = window->GetRootFocusController();
01170   if (!focusController) {
01171     return PR_FALSE;
01172   }
01173 
01174   nsCOMPtr<nsIDOMElement> focusedElement;
01175   focusController->GetFocusedElement(getter_AddRefs(focusedElement));
01176   nsCOMPtr<nsIContent> content = do_QueryInterface(focusedElement);
01177   if (!content) {
01178     return PR_FALSE;
01179   }
01180 
01181   nsCOMPtr<nsIDOMElement> boundElement =
01182     do_QueryInterface(content->GetBindingParent());
01183   if (!boundElement) {
01184     return PR_FALSE;
01185   }
01186 
01187   nsAutoString idStr;
01188   return (NS_SUCCEEDED(boundElement->GetAttribute(NS_LITERAL_STRING("id"),
01189                                                   idStr)) &&
01190           idStr.EqualsLiteral("find-field"));
01191 }
01192 
01193 PRBool
01194 nsTypeAheadFind::IsRangeVisible(nsIPresShell *aPresShell,
01195                                 nsPresContext *aPresContext,
01196                                 nsIDOMRange *aRange, PRBool aMustBeInViewPort,
01197                                 PRBool aGetTopVisibleLeaf,
01198                                 nsIDOMRange **aFirstVisibleRange,
01199                                 PRBool *aUsesIndependentSelection)
01200 {
01201   NS_ENSURE_ARG_POINTER(aPresShell);
01202   NS_ENSURE_ARG_POINTER(aPresContext);
01203   NS_ENSURE_ARG_POINTER(aRange);
01204   NS_ENSURE_ARG_POINTER(aFirstVisibleRange);
01205 
01206   // We need to know if the range start is visible.
01207   // Otherwise, return a the first visible range start 
01208   // in aFirstVisibleRange
01209 
01210   aRange->CloneRange(aFirstVisibleRange);
01211   nsCOMPtr<nsIDOMNode> node;
01212   aRange->GetStartContainer(getter_AddRefs(node));
01213 
01214   nsCOMPtr<nsIContent> content(do_QueryInterface(node));
01215   if (!content)
01216     return PR_FALSE;
01217 
01218   nsIFrame *frame = nsnull;
01219   aPresShell->GetPrimaryFrameFor(content, &frame);
01220   if (!frame)    
01221     return PR_FALSE;  // No frame! Not visible then.
01222 
01223   if (!frame->GetStyleVisibility()->IsVisible())
01224     return PR_FALSE;
01225 
01226   // Detect if we are _inside_ a text control, or something else with its own
01227   // selection controller.
01228   if (aUsesIndependentSelection) {
01229     *aUsesIndependentSelection = 
01230       (frame->GetStateBits() & NS_FRAME_INDEPENDENT_SELECTION);
01231   }
01232 
01233   // ---- We have a frame ----
01234   if (!aMustBeInViewPort)   
01235     return PR_TRUE; //  Don't need it to be on screen, just in rendering tree
01236 
01237   // Get the next in flow frame that contains the range start
01238   PRInt32 startRangeOffset, startFrameOffset, endFrameOffset;
01239   aRange->GetStartOffset(&startRangeOffset);
01240   while (PR_TRUE) {
01241     frame->GetOffsets(startFrameOffset, endFrameOffset);
01242     if (startRangeOffset < endFrameOffset)
01243       break;
01244 
01245     nsIFrame *nextInFlowFrame = frame->GetNextInFlow();
01246     if (nextInFlowFrame)
01247       frame = nextInFlowFrame;
01248     else
01249       break;
01250   }
01251 
01252   // Set up the variables we need, return true if we can't get at them all
01253   const PRUint16 kMinPixels  = 12;
01254 
01255   nsIViewManager* viewManager = aPresShell->GetViewManager();
01256   if (!viewManager)
01257     return PR_TRUE;
01258 
01259   // Get the bounds of the current frame, relative to the current view.
01260   // We don't use the more accurate AccGetBounds, because that is
01261   // more expensive and the STATE_OFFSCREEN flag that this is used
01262   // for only needs to be a rough indicator
01263   nsIView *containingView = nsnull;
01264   nsPoint frameOffset;
01265   float p2t;
01266   p2t = aPresContext->PixelsToTwips();
01267   nsRectVisibility rectVisibility = nsRectVisibility_kAboveViewport;
01268 
01269   if (!aGetTopVisibleLeaf) {
01270     nsRect relFrameRect = frame->GetRect();
01271     frame->GetOffsetFromView(frameOffset, &containingView);
01272     if (!containingView)      
01273       return PR_FALSE;  // no view -- not visible    
01274 
01275     relFrameRect.x = frameOffset.x;
01276     relFrameRect.y = frameOffset.y;
01277 
01278     viewManager->GetRectVisibility(containingView, relFrameRect,
01279                                    NS_STATIC_CAST(PRUint16, (kMinPixels * p2t)),
01280                                    &rectVisibility);
01281 
01282     if (rectVisibility != nsRectVisibility_kAboveViewport &&
01283         rectVisibility != nsRectVisibility_kZeroAreaRect) {
01284       return PR_TRUE;
01285     }
01286   }
01287 
01288   // We know that the target range isn't usable because it's not in the
01289   // view port. Move range forward to first visible point,
01290   // this speeds us up a lot in long documents
01291   nsCOMPtr<nsIBidirectionalEnumerator> frameTraversal;
01292   nsCOMPtr<nsIFrameTraversal> trav(do_CreateInstance(kFrameTraversalCID));
01293   if (trav)
01294     trav->NewFrameTraversal(getter_AddRefs(frameTraversal), LEAF, aPresContext, frame);
01295 
01296   if (!frameTraversal)
01297     return PR_FALSE;
01298 
01299   while (rectVisibility == nsRectVisibility_kAboveViewport || rectVisibility == nsRectVisibility_kZeroAreaRect) {
01300     frameTraversal->Next();
01301     nsISupports* currentItem;
01302     frameTraversal->CurrentItem(&currentItem);
01303     frame = NS_STATIC_CAST(nsIFrame*, currentItem);
01304     if (!frame)
01305       return PR_FALSE;
01306 
01307     nsRect relFrameRect = frame->GetRect();
01308     frame->GetOffsetFromView(frameOffset, &containingView);
01309     if (containingView) {
01310       relFrameRect.x = frameOffset.x;
01311       relFrameRect.y = frameOffset.y;
01312       viewManager->GetRectVisibility(containingView, relFrameRect,
01313                                      NS_STATIC_CAST(PRUint16, (kMinPixels * p2t)),
01314                                      &rectVisibility);
01315     }
01316   }
01317 
01318   if (frame) {
01319     nsCOMPtr<nsIDOMNode> firstVisibleNode = do_QueryInterface(frame->GetContent());
01320 
01321     if (firstVisibleNode) {
01322       (*aFirstVisibleRange)->SelectNode(firstVisibleNode);
01323       frame->GetOffsets(startFrameOffset, endFrameOffset);
01324       (*aFirstVisibleRange)->SetStart(firstVisibleNode, startFrameOffset);
01325       (*aFirstVisibleRange)->Collapse(PR_TRUE);  // Collapse to start
01326     }
01327   }
01328 
01329   return PR_FALSE;
01330 }
01331 
01332 already_AddRefed<nsIPresShell>
01333 nsTypeAheadFind::GetPresShell()
01334 {
01335   if (!mPresShell)
01336     return nsnull;
01337 
01338   nsIPresShell *shell = nsnull;
01339   CallQueryReferent(mPresShell.get(), &shell);
01340   if (shell) {
01341     nsPresContext *pc = shell->GetPresContext();
01342     if (!pc || !nsCOMPtr<nsISupports>(pc->GetContainer())) {
01343       NS_RELEASE(shell);
01344     }
01345   }
01346 
01347   return shell;
01348 }