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  *   Original Author: Aaron Leventhal (aaronl@netscape.com)
00024  *
00025  * Alternatively, the contents of this file may be used under the terms of
00026  * either the GNU General Public License Version 2 or later (the "GPL"), or
00027  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00028  * in which case the provisions of the GPL or the LGPL are applicable instead
00029  * of those above. If you wish to allow use of your version of this file only
00030  * under the terms of either the GPL or the LGPL, and not to allow others to
00031  * use your version of this file under the terms of the MPL, indicate your
00032  * decision by deleting the provisions above and replace them with the notice
00033  * and other provisions required by the GPL or the LGPL. If you do not delete
00034  * the provisions above, a recipient may use your version of this file under
00035  * the terms of any one of the MPL, the GPL or the LGPL.
00036  *
00037  * ***** END LICENSE BLOCK ***** */
00038 
00039 #include "nsCOMPtr.h"
00040 #include "nsMemory.h"
00041 #include "nsIServiceManager.h"
00042 #include "nsIGenericFactory.h"
00043 #include "nsIWebBrowserChrome.h"
00044 #include "nsIDocumentLoader.h"
00045 #include "nsCURILoader.h"
00046 #include "nsNetUtil.h"
00047 #include "nsIURL.h"
00048 #include "nsIURI.h"
00049 #include "nsIDocShell.h"
00050 #include "nsIDocShellTreeOwner.h"
00051 #include "nsIEditorDocShell.h"
00052 #include "nsISimpleEnumerator.h"
00053 #include "nsIDOMWindow.h"
00054 #include "nsIDOMWindowInternal.h"
00055 #include "nsPIDOMWindow.h"
00056 #include "nsIDOMEventTarget.h"
00057 #include "nsIDOMNSUIEvent.h"
00058 #include "nsIChromeEventHandler.h"
00059 #include "nsIDOMNSEvent.h"
00060 #include "nsIPrefBranch.h"
00061 #include "nsIPrefBranch2.h"
00062 #include "nsIPrefService.h"
00063 #include "nsString.h"
00064 #include "nsCRT.h"
00065 
00066 #include "nsIDOMNode.h"
00067 #include "nsIContent.h"
00068 #include "nsIFrame.h"
00069 #include "nsFrameTraversal.h"
00070 #include "nsIDOMDocument.h"
00071 #include "nsIDOMXULDocument.h"
00072 #include "nsIImageDocument.h"
00073 #include "nsIDOMHTMLDocument.h"
00074 #include "nsIDOMNSHTMLDocument.h"
00075 #include "nsIDOMHTMLElement.h"
00076 #include "nsIEventStateManager.h"
00077 #include "nsIViewManager.h"
00078 #include "nsIScrollableView.h"
00079 #include "nsIDocument.h"
00080 #include "nsISelection.h"
00081 #include "nsISelectionPrivate.h"
00082 #include "nsISelectElement.h"
00083 #include "nsILink.h"
00084 #include "nsITextContent.h"
00085 #include "nsTextFragment.h"
00086 #include "nsILookAndFeel.h"
00087 
00088 #include "nsICaret.h"
00089 #include "nsIScriptGlobalObject.h"
00090 #include "nsIDOMKeyEvent.h"
00091 #include "nsIDocShellTreeItem.h"
00092 #include "nsIWebNavigation.h"
00093 #include "nsIInterfaceRequestor.h"
00094 #include "nsIInterfaceRequestorUtils.h"
00095 #include "nsContentCID.h"
00096 #include "nsLayoutCID.h"
00097 #include "nsWidgetsCID.h"
00098 #include "nsIFormControl.h"
00099 #include "nsINameSpaceManager.h"
00100 #include "nsIWindowWatcher.h"
00101 #include "nsIObserverService.h"
00102 #include "nsLayoutAtoms.h"
00103 
00104 #include "nsIPrivateTextEvent.h"
00105 #include "nsIPrivateCompositionEvent.h"
00106 #include "nsGUIEvent.h"
00107 #include "nsIDOMEventReceiver.h"
00108 #include "nsIDOM3EventTarget.h"
00109 #include "nsIDOMEventGroup.h"
00110 
00111 // Header for this class
00112 #include "nsTypeAheadFind.h"
00113 
00115 
00116 
00117 NS_INTERFACE_MAP_BEGIN(nsTypeAheadFind)
00118   NS_INTERFACE_MAP_ENTRY(nsITypeAheadFind)
00119   NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
00120   NS_INTERFACE_MAP_ENTRY(nsITimerCallback)
00121   NS_INTERFACE_MAP_ENTRY(nsIScrollPositionListener)
00122   NS_INTERFACE_MAP_ENTRY(nsISelectionListener)
00123   NS_INTERFACE_MAP_ENTRY(nsIObserver)
00124   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsISelectionListener)
00125   NS_INTERFACE_MAP_ENTRY(nsIDOMKeyListener)
00126   NS_INTERFACE_MAP_ENTRY(nsIDOMTextListener)
00127   NS_INTERFACE_MAP_ENTRY(nsIDOMCompositionListener)
00128   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsIDOMEventListener, nsIDOMKeyListener)
00129 NS_INTERFACE_MAP_END
00130 
00131 NS_IMPL_ADDREF(nsTypeAheadFind)
00132 NS_IMPL_RELEASE(nsTypeAheadFind)
00133 
00134 static NS_DEFINE_IID(kRangeCID, NS_RANGE_CID);
00135 static NS_DEFINE_CID(kStringBundleServiceCID,  NS_STRINGBUNDLESERVICE_CID);
00136 static NS_DEFINE_CID(kFrameTraversalCID, NS_FRAMETRAVERSAL_CID);
00137 static NS_DEFINE_CID(kLookAndFeelCID, NS_LOOKANDFEEL_CID);
00138 
00139 #define NS_FIND_CONTRACTID "@mozilla.org/embedcomp/rangefind;1"
00140 
00141 nsTypeAheadFind* nsTypeAheadFind::sInstance = nsnull;
00142 PRInt32 nsTypeAheadFind::sAccelKey = -1;  // magic value of -1 when unitialized
00143 
00144 
00145 nsTypeAheadFind::nsTypeAheadFind():
00146   mIsFindAllowedInWindow(PR_FALSE), mAutoStartPref(PR_FALSE),
00147   mLinksOnlyPref(PR_FALSE), mStartLinksOnlyPref(PR_FALSE),
00148   mLinksOnly(PR_FALSE), mIsTypeAheadOn(PR_FALSE), mCaretBrowsingOn(PR_FALSE),
00149   mLiteralTextSearchOnly(PR_FALSE), mDontTryExactMatch(PR_FALSE),
00150   mAllTheSameChar(PR_TRUE),
00151   mLinksOnlyManuallySet(PR_FALSE), mIsFindingText(PR_FALSE),
00152   mIsMenuBarActive(PR_FALSE), mIsMenuPopupActive(PR_FALSE),
00153   mIsFirstVisiblePreferred(PR_FALSE), mIsIMETypeAheadActive(PR_FALSE),
00154   mIsBackspaceProtectOn(PR_FALSE),
00155   mBadKeysSinceMatch(0), mLastBadChar(0),
00156   mRepeatingMode(eRepeatingNone), mTimeoutLength(0),
00157   mSoundInterface(nsnull), mIsSoundInitialized(PR_FALSE)
00158 {
00159 #ifdef DEBUG
00160   // There should only ever be one instance of us
00161   static PRInt32 gInstanceCount;
00162   ++gInstanceCount;
00163   NS_ASSERTION(gInstanceCount == 1,
00164     "There should be only 1 instance of nsTypeAheadFind!");
00165 #endif
00166 }
00167 
00168 
00169 nsTypeAheadFind::~nsTypeAheadFind()
00170 {
00171   RemoveDocListeners();
00172   mTimer = nsnull;
00173 
00174   nsCOMPtr<nsIPrefBranch2> prefInternal(do_GetService(NS_PREFSERVICE_CONTRACTID));
00175   if (prefInternal) {
00176     prefInternal->RemoveObserver("accessibility.typeaheadfind", this);
00177     prefInternal->RemoveObserver("accessibility.browsewithcaret", this);
00178   }
00179 }
00180 
00181 nsresult
00182 nsTypeAheadFind::Init()
00183 {
00184   nsresult rv = NS_NewISupportsArray(getter_AddRefs(mManualFindWindows));
00185   NS_ENSURE_SUCCESS(rv, rv);
00186 
00187 
00188   nsCOMPtr<nsIPrefBranch2> prefInternal(do_GetService(NS_PREFSERVICE_CONTRACTID));
00189   mSearchRange = do_CreateInstance(kRangeCID);
00190   mStartPointRange = do_CreateInstance(kRangeCID);
00191   mEndPointRange = do_CreateInstance(kRangeCID);
00192   mFind = do_CreateInstance(NS_FIND_CONTRACTID);
00193   if (!prefInternal || !mSearchRange || !mStartPointRange ||
00194       !mEndPointRange || !mFind) {
00195     return NS_ERROR_FAILURE;
00196   }
00197 
00198   // ----------- Listen to prefs ------------------
00199   rv = prefInternal->AddObserver("accessibility.typeaheadfind", this, PR_FALSE);
00200   NS_ENSURE_SUCCESS(rv, rv);
00201 
00202   rv = prefInternal->AddObserver("accessibility.browsewithcaret", this, PR_FALSE);
00203   NS_ENSURE_SUCCESS(rv, rv);
00204 
00205 
00206   // ----------- Get accel key --------------------
00207   rv = prefInternal->GetIntPref("ui.key.accelKey", &sAccelKey);
00208   NS_ENSURE_SUCCESS(rv, rv);
00209 
00210   // ----------- Get initial preferences ----------
00211   PrefsReset();
00212 
00213   // ----------- Set search options ---------------
00214   mFind->SetCaseSensitive(PR_FALSE);
00215   mFind->SetWordBreaker(nsnull);
00216 
00217   return rv;
00218 }
00219 
00220 nsTypeAheadFind *
00221 nsTypeAheadFind::GetInstance()
00222 {
00223   if (!sInstance) {
00224     sInstance = new nsTypeAheadFind();
00225     if (!sInstance)
00226       return nsnull;
00227 
00228     NS_ADDREF(sInstance);  // addref for sInstance global
00229 
00230     if (NS_FAILED(sInstance->Init())) {
00231       NS_RELEASE(sInstance);
00232 
00233       return nsnull;
00234     }
00235   }
00236 
00237   NS_ADDREF(sInstance);   // addref for the getter
00238 
00239   return sInstance;
00240 }
00241 
00242 
00243 void
00244 nsTypeAheadFind::ReleaseInstance()
00245 {
00246   NS_IF_RELEASE(sInstance);
00247 }
00248 
00249 
00250 void 
00251 nsTypeAheadFind::Shutdown()
00252 {
00253   // Application shutdown 
00254   mTimer = nsnull;
00255 
00256   nsCOMPtr<nsIWindowWatcher> windowWatcher =
00257     do_GetService(NS_WINDOWWATCHER_CONTRACTID);
00258   if (windowWatcher) {
00259     windowWatcher->UnregisterNotification(this);
00260   }
00261 }
00262 
00263 
00264 // ------- Pref Callbacks (2) ---------------
00265 
00266 nsresult
00267 nsTypeAheadFind::PrefsReset()
00268 {
00269   nsCOMPtr<nsIPrefBranch> prefBranch(do_GetService(NS_PREFSERVICE_CONTRACTID));
00270   NS_ENSURE_TRUE(prefBranch, NS_ERROR_FAILURE);
00271 
00272   PRBool wasTypeAheadOn = mIsTypeAheadOn;
00273 
00274   prefBranch->GetBoolPref("accessibility.typeaheadfind", &mIsTypeAheadOn);
00275 
00276   if (mIsTypeAheadOn != wasTypeAheadOn) {
00277     if (!mIsTypeAheadOn) {
00278       CancelFind();
00279     }
00280     else if (!mStringBundle) {
00281       // Get ready to watch windows
00282       nsresult rv;
00283       nsCOMPtr<nsIWindowWatcher> windowWatcher =
00284         do_GetService(NS_WINDOWWATCHER_CONTRACTID, &rv);
00285       NS_ENSURE_SUCCESS(rv, rv);
00286 
00287       windowWatcher->RegisterNotification(this);
00288 
00289       // Initialize string bundle
00290       nsCOMPtr<nsIStringBundleService> stringBundleService =
00291         do_GetService(kStringBundleServiceCID);
00292 
00293       if (stringBundleService)
00294         stringBundleService->CreateBundle(TYPEAHEADFIND_BUNDLE_URL,
00295                                           getter_AddRefs(mStringBundle));
00296 
00297       // Observe find again commands. We'll handle them if we were the last find
00298       nsCOMPtr<nsIObserverService> observerService = 
00299         do_GetService("@mozilla.org/observer-service;1", &rv);
00300       NS_ENSURE_SUCCESS(rv, rv);
00301       observerService->AddObserver(this, "nsWebBrowserFind_FindAgain", 
00302                                    PR_TRUE);
00303       observerService->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, 
00304                                    PR_TRUE);
00305     }
00306   }
00307 
00308   PRBool oldAutoStartPref = mAutoStartPref;
00309   prefBranch->GetBoolPref("accessibility.typeaheadfind.autostart",
00310                            &mAutoStartPref);
00311   if (mAutoStartPref != oldAutoStartPref) {
00312     ResetGlobalAutoStart(mAutoStartPref);
00313   }
00314  
00315   prefBranch->GetBoolPref("accessibility.typeaheadfind.linksonly",
00316                           &mLinksOnlyPref);
00317 
00318   prefBranch->GetBoolPref("accessibility.typeaheadfind.startlinksonly",
00319                           &mStartLinksOnlyPref);
00320 
00321   PRBool isSoundEnabled = PR_TRUE;
00322   prefBranch->GetBoolPref("accessibility.typeaheadfind.enablesound",
00323                            &isSoundEnabled);
00324   nsXPIDLCString soundStr;
00325   if (isSoundEnabled) {
00326     prefBranch->GetCharPref("accessibility.typeaheadfind.soundURL",
00327                              getter_Copies(soundStr));
00328   }
00329   mNotFoundSoundURL = soundStr;
00330 
00331   PRBool isTimeoutEnabled = PR_FALSE;
00332   prefBranch->GetBoolPref("accessibility.typeaheadfind.enabletimeout",
00333                           &isTimeoutEnabled);
00334   PRInt32 timeoutLength = 0;
00335   if (isTimeoutEnabled) {
00336     prefBranch->GetIntPref("accessibility.typeaheadfind.timeout",
00337                            &timeoutLength);
00338   }
00339   mTimeoutLength = timeoutLength;
00340 
00341 
00342   prefBranch->GetBoolPref("accessibility.browsewithcaret",
00343                           &mCaretBrowsingOn);
00344 
00345   return NS_OK;
00346 }
00347 
00348 
00349 // ------- nsITimer Methods (1) ---------------
00350 
00351 NS_IMETHODIMP
00352 nsTypeAheadFind::Notify(nsITimer *timer)
00353 {
00354   CancelFind();
00355   return NS_OK;
00356 }
00357 
00358 // ----------- nsIObserver Methods (1) -------------------
00359 
00360 NS_IMETHODIMP
00361 nsTypeAheadFind::Observe(nsISupports *aSubject, const char *aTopic,
00362                          const PRUnichar *aData)
00363 {
00364   PRBool isOpening;
00365   if (!nsCRT::strcmp(aTopic,"domwindowopened")) {
00366     isOpening = PR_TRUE;
00367   }
00368   else if (!nsCRT::strcmp(aTopic,"domwindowclosed")) {
00369     isOpening = PR_FALSE;
00370   }
00371   else if (!nsCRT::strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) {
00372     Shutdown();
00373     return NS_OK;
00374   }
00375   else if (!nsCRT::strcmp(aTopic,"nsWebBrowserFind_FindAgain")) {
00376     // A find next command wants to be executed.
00377     // We might want to handle it. If we do, return true in didExecute.
00378     nsCOMPtr<nsISupportsInterfacePointer> callerWindowSupports(do_QueryInterface(aSubject));
00379     return FindNext(NS_LITERAL_STRING("up").Equals(aData), callerWindowSupports);
00380   }
00381   else if (!nsCRT::strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID)) {
00382     return PrefsReset();
00383   }
00384   else {
00385     return NS_OK;
00386   }
00387 
00388   // -- Attach/Remove window listeners --
00389   nsCOMPtr<nsIDOMWindow> topLevelWindow(do_QueryInterface(aSubject));
00390   NS_ENSURE_TRUE(topLevelWindow, NS_OK);
00391   nsCOMPtr<nsPIDOMWindow> privateWindow = do_QueryInterface(aSubject);
00392   nsIFocusController *focusController =
00393     privateWindow->GetRootFocusController();
00394   NS_ENSURE_TRUE(focusController, NS_ERROR_FAILURE);
00395 
00396   if (isOpening) {
00397     if (mAutoStartPref) {
00398       AttachWindowListeners(topLevelWindow);
00399     }
00400 
00401     // Attach nsTypeAheadController to window
00402     // so it can handle / and ' shortcuts to start text and link search
00403     if (privateWindow) {
00404       nsCOMPtr<nsIControllers> controllers;
00405       privateWindow->GetControllers(getter_AddRefs(controllers));
00406       NS_ENSURE_TRUE(controllers, NS_ERROR_FAILURE);
00407 
00408       nsCOMPtr<nsIController> controller = 
00409         new nsTypeAheadController(focusController);
00410       NS_ENSURE_TRUE(controller, NS_ERROR_FAILURE);
00411 
00412       controllers->AppendController(controller);
00413     }
00414  
00415     return NS_OK;
00416   }
00417 
00418   nsCOMPtr<nsIDOMWindowInternal> activeWindowInternal;
00419   focusController->GetFocusedWindow(getter_AddRefs(activeWindowInternal));
00420   nsCOMPtr<nsIDOMWindow> activeWindow = do_QueryInterface(activeWindowInternal);
00421 
00422   RemoveWindowListeners(topLevelWindow);
00423 
00424   // -- Prevent leaks ---
00425   // When a window closes, we have to remove it and all of its subwindows
00426   // from mManualFindWindows so that we don't leak.
00427   // Eek, lots of work for such a simple thing.
00428   nsCOMPtr<nsIInterfaceRequestor> ifreq(do_QueryInterface(aSubject));
00429   NS_ENSURE_TRUE(ifreq, NS_OK);
00430 
00431   nsCOMPtr<nsIWebNavigation> webNav(do_GetInterface(ifreq));
00432   nsCOMPtr<nsIDocShell> docShell(do_QueryInterface(webNav));
00433   NS_ENSURE_TRUE(docShell, NS_OK);
00434 
00435   nsCOMPtr<nsISimpleEnumerator> docShellEnumerator;
00436   docShell->GetDocShellEnumerator(nsIDocShellTreeItem::typeAll,
00437                                   nsIDocShell::ENUMERATE_FORWARDS,
00438                                   getter_AddRefs(docShellEnumerator));
00439 
00440   // Iterate through shells to get windows
00441   PRBool hasMoreDocShells;
00442   while (NS_SUCCEEDED(docShellEnumerator->HasMoreElements(&hasMoreDocShells))
00443          && hasMoreDocShells) {
00444     nsCOMPtr<nsISupports> container;
00445     docShellEnumerator->GetNext(getter_AddRefs(container));
00446     nsCOMPtr<nsIInterfaceRequestor> ifreq(do_QueryInterface(container));
00447 
00448     if (ifreq) {
00449       nsCOMPtr<nsIDOMWindow> domWin(do_GetInterface(ifreq));
00450       nsCOMPtr<nsISupports> windowSupports(do_QueryInterface(domWin));
00451 
00452       if (windowSupports) {
00453         PRInt32 index = mManualFindWindows->IndexOf(windowSupports);
00454 
00455         if (index >= 0) {
00456           mManualFindWindows->RemoveElementAt(index);
00457         }
00458       }
00459 
00460       // Don't hold references to things that will keep objects alive
00461       // longer than they would otherwise be.
00462       if (domWin == mFocusedWindow) {
00463         RemoveDocListeners();
00464         CancelFind();
00465       }
00466       if (domWin == activeWindow) {
00467         // If popup was still open as its parent window closes, don't stay in
00468         // menu active state which prevents us from operating
00469         mIsMenuBarActive = mIsMenuPopupActive = PR_FALSE;
00470       }
00471     }
00472   }
00473 
00474   return NS_OK;
00475 }
00476 
00477 
00478 nsresult
00479 nsTypeAheadFind::UseInWindow(nsIDOMWindow *aDOMWin)
00480 {
00481   NS_ENSURE_ARG_POINTER(aDOMWin);
00482 
00483   // Set member variables and listeners up for new window and doc
00484 
00485   mFindNextBuffer.Truncate();
00486   CancelFind();
00487 
00488   GetStartWindow(aDOMWin, getter_AddRefs(mFocusedWindow));
00489 
00490   nsCOMPtr<nsIDOMDocument> domDoc;
00491   aDOMWin->GetDocument(getter_AddRefs(domDoc));
00492   nsCOMPtr<nsIDocument> doc(do_QueryInterface(domDoc));
00493 
00494   if (!doc) {
00495     return NS_OK;
00496   }
00497 
00498   nsIPresShell *presShell = doc->GetShellAt(0);
00499 
00500   if (!presShell) {
00501     return NS_OK;
00502   }
00503 
00504   nsCOMPtr<nsIPresShell> oldPresShell(GetPresShell());
00505 
00506   if (!oldPresShell || oldPresShell != presShell) {
00507     CancelFind();
00508   } else if (presShell == oldPresShell) {
00509     // Same window, no need to reattach listeners
00510 
00511     return NS_OK;
00512   }
00513 
00514   RemoveDocListeners();
00515 
00516   mIsFindAllowedInWindow = PR_TRUE;
00517   mFocusedWeakShell = do_GetWeakReference(presShell);
00518 
00519   // Add scroll position and selection listeners, so we can cancel
00520   // current find when user navigates
00521   GetSelection(presShell, getter_AddRefs(mFocusedDocSelCon),
00522                getter_AddRefs(mFocusedDocSelection)); // cache for reuse
00523   AttachDocListeners(presShell);
00524 
00525   return NS_OK;
00526 }
00527 
00528 
00529 // ------- nsIDOMEventListener Methods (1) ---------------
00530 
00531 NS_IMETHODIMP
00532 nsTypeAheadFind::HandleEvent(nsIDOMEvent* aEvent)
00533 {
00534   nsAutoString eventType;
00535   aEvent->GetType(eventType);
00536 
00537   if (eventType.EqualsLiteral("DOMMenuBarActive")) {
00538     mIsMenuBarActive = PR_TRUE;
00539   }
00540   else if (eventType.EqualsLiteral("DOMMenuBarInactive")) {
00541     mIsMenuBarActive = PR_FALSE;
00542   }
00543   else if (eventType.EqualsLiteral("popupshown")) {
00544     mIsMenuPopupActive = PR_TRUE;
00545   }
00546   else if (eventType.EqualsLiteral("popuphidden")) {
00547     mIsMenuPopupActive = PR_FALSE;
00548   }
00549   else if (eventType.EqualsLiteral("unload")) {
00550     // When document is unloaded, check to see if it's the 
00551     // current typeahead doc. If it is, cancel find
00552     // and reset member variables so we don't leak
00553     nsCOMPtr<nsIDOMNSEvent> event(do_QueryInterface(aEvent));
00554     NS_ENSURE_TRUE(event, NS_ERROR_FAILURE);
00555 
00556     nsCOMPtr<nsIDOMEventTarget> eventTarget;
00557     event->GetOriginalTarget(getter_AddRefs(eventTarget));
00558     nsCOMPtr<nsIDocument> doc(do_QueryInterface(eventTarget));
00559     nsCOMPtr<nsIPresShell> focusedShell(GetPresShell());
00560     if (!focusedShell || !doc) {
00561       return NS_ERROR_FAILURE;
00562     }
00563 
00564     PRUint32 numShells = doc->GetNumberOfShells();
00565     PRBool cancelFind = PR_FALSE;
00566 
00567     for (PRUint32 count = 0; count < numShells; count ++) {
00568       nsIPresShell *shellToBeDestroyed = doc->GetShellAt(count);
00569       if (shellToBeDestroyed == focusedShell) {
00570         cancelFind = PR_TRUE;
00571         break;
00572       }
00573     }
00574 
00575     if (cancelFind) {
00576       RemoveDocListeners();
00577       mSearchRange = do_CreateInstance(kRangeCID);
00578       mStartPointRange = do_CreateInstance(kRangeCID);
00579       mEndPointRange = do_CreateInstance(kRangeCID);
00580       mFocusedWindow = nsnull;
00581       CancelFind();
00582     }
00583   }
00584 
00585   return NS_OK;
00586 }
00587 
00588 
00589 // ------- nsIDOMKeyListener Methods (3) ---------------
00590 
00591 NS_IMETHODIMP
00592 nsTypeAheadFind::KeyDown(nsIDOMEvent* aEvent)
00593 {
00594   return NS_OK;
00595 }
00596 
00597 
00598 NS_IMETHODIMP
00599 nsTypeAheadFind::KeyUp(nsIDOMEvent* aEvent)
00600 {
00601   return NS_OK;
00602 }
00603 
00604 
00605 NS_IMETHODIMP
00606 nsTypeAheadFind::KeyPress(nsIDOMEvent* aEvent)
00607 {
00608   if (!mIsTypeAheadOn || mIsMenuBarActive || mIsMenuPopupActive) {
00609     return NS_OK;
00610   }
00611 
00612   if (!mIsSoundInitialized && !mNotFoundSoundURL.IsEmpty()) {
00613     // This makes sure system sound library is loaded so that
00614     // there's no lag before the first sound is played
00615     // by waiting for the first keystroke, we still get the startup time benefits.
00616     mIsSoundInitialized = PR_TRUE;
00617     mSoundInterface = do_CreateInstance("@mozilla.org/sound;1");
00618     if (mSoundInterface && !mNotFoundSoundURL.EqualsLiteral("beep")) {
00619       mSoundInterface->Init();
00620     }
00621   }
00622 
00623 #ifdef XP_WIN
00624   // After each keystroke, ensure sound object is destroyed, to free up memory 
00625   // allocated for error sound, otherwise Windows' nsISound impl 
00626   // holds onto the last played sound, using up memory.
00627   mSoundInterface = nsnull;
00628 #endif
00629   nsCOMPtr<nsIContent> targetContent;
00630   nsCOMPtr<nsIPresShell> targetPresShell;
00631   GetTargetIfTypeAheadOkay(aEvent, getter_AddRefs(targetContent), 
00632     getter_AddRefs(targetPresShell));
00633   if (!targetContent || !targetPresShell)
00634     return NS_OK;
00635 
00636   PRUint32 keyCode(0), charCode;
00637   PRBool isShift(PR_FALSE), isCtrl(PR_FALSE), isAlt(PR_FALSE), isMeta(PR_FALSE);
00638   nsCOMPtr<nsIDOMKeyEvent> keyEvent(do_QueryInterface(aEvent));
00639 
00640   // ---------- Analyze keystroke, exit early if possible --------------
00641 
00642   if (!keyEvent ||
00643       NS_FAILED(keyEvent->GetKeyCode(&keyCode)) ||
00644       NS_FAILED(keyEvent->GetCharCode(&charCode)) ||
00645       NS_FAILED(keyEvent->GetShiftKey(&isShift)) ||
00646       NS_FAILED(keyEvent->GetCtrlKey(&isCtrl)) ||
00647       NS_FAILED(keyEvent->GetAltKey(&isAlt)) ||
00648       NS_FAILED(keyEvent->GetMetaKey(&isMeta))) {
00649     return NS_ERROR_FAILURE;
00650   }
00651 
00652   // ---------- Set Backspace Protection --------------------------
00653   // mIsBackspaceProtectOn should be PR_TRUE only if the last key 
00654   // was a backspace and this key is also a backspace. It keeps us 
00655   // from accidentally hitting backspace too many times in a row, going 
00656   // back in history when we really just wanted to clear the find string.
00657   if (keyCode != nsIDOMKeyEvent::DOM_VK_BACK_SPACE) {
00658     mIsBackspaceProtectOn = PR_FALSE;
00659   }
00660 
00661   // ---------- Check the keystroke --------------------------------
00662   if ((isAlt && !isShift) || isCtrl || isMeta) {
00663     // Ignore most modified keys, but alt+shift may be used for
00664     // entering foreign chars.
00665 
00666     return NS_OK;
00667   }
00668 
00669   // ------------- Escape pressed ---------------------
00670   if (keyCode == nsIDOMKeyEvent::DOM_VK_ESCAPE) {
00671     // Escape accomplishes 2 things:
00672     // 1. it is a way for the user to deselect with the keyboard
00673     // 2. it is a way for the user to cancel incremental find with
00674     //    visual feedback
00675     if (mLinksOnlyManuallySet || !mTypeAheadBuffer.IsEmpty()) {
00676       // If Escape is normally used for a command, don't do it
00677       aEvent->PreventDefault();
00678       CancelFind();
00679     }
00680     if (mFocusedDocSelection) {
00681       SetSelectionLook(targetPresShell, PR_FALSE, PR_FALSE);
00682       mFocusedDocSelection->CollapseToStart();
00683     }
00684 
00685     return NS_OK;
00686   }
00687 
00688   // ---------- PreventDefault check ---------------
00689   // If a web page wants to use printable character keys,
00690   // they have to use evt.preventDefault() after they get the key
00691   nsCOMPtr<nsIDOMNSUIEvent> uiEvent(do_QueryInterface(aEvent));
00692   PRBool preventDefault;
00693   uiEvent->GetPreventDefault(&preventDefault);
00694   if (preventDefault) {
00695     return NS_OK;
00696   }
00697 
00698   // ----------- Back space -------------------------
00699   if (keyCode == nsIDOMKeyEvent::DOM_VK_BACK_SPACE) {
00700     // The order of seeing keystrokes is:
00701     // 1) Chrome, 2) Typeahead, 3) [platform]HTMLBindings.xml
00702     // If chrome handles backspace, it needs to do this work
00703     // Otherwise, we handle backspace here.
00704     PRBool backspaceUsed;
00705     BackOneChar(&backspaceUsed);
00706     if (backspaceUsed) {
00707       aEvent->PreventDefault(); // Prevent normal processing of this keystroke
00708     }
00709 
00710     return NS_OK;
00711   }
00712   
00713   // ----------- Other non-printable keys -----------
00714   // We ignore space only if it's the first character
00715   // Function keys, etc. exit here
00716   if (keyCode || charCode < ' ' || 
00717       (charCode == ' ' && mTypeAheadBuffer.IsEmpty())) {
00718     return NS_OK;
00719   }
00720 
00721   // Ignore first / or ' -- they are used to set links/text only
00722   // Needs to come to us through htmlBindings.xml's keybinding
00723   // then via nsTypeAheadController::DoCommand()
00724   if (!mLinksOnlyManuallySet && (charCode == '\'' || charCode == '/') && 
00725       mTypeAheadBuffer.IsEmpty()) {
00726     return NS_OK;
00727   }
00728 
00729   aEvent->StopPropagation();  // We're using this key, no one else should
00730 
00731   return HandleChar(charCode);
00732 }
00733 
00734 
00735 NS_IMETHODIMP
00736 nsTypeAheadFind::BackOneChar(PRBool *aIsBackspaceUsed)
00737 {
00738   if (!mFocusedDocSelection) {
00739     *aIsBackspaceUsed = PR_FALSE;
00740     return NS_OK;
00741   }
00742 
00743   // In normal type ahead find, remove a printable char from 
00744   // mTypeAheadBuffer, then search for buffer contents
00745   // Or, in repeated char find, go backwards
00746 
00747   *aIsBackspaceUsed = PR_TRUE;
00748 
00749   // ---------- No chars in string ------------
00750   if (mTypeAheadBuffer.IsEmpty() || !mStartFindRange) {
00751     if (!mFindNextBuffer.IsEmpty() &&
00752         (mRepeatingMode == eRepeatingChar || 
00753          mRepeatingMode == eRepeatingCharReverse)) {
00754       // Backspace to find previous repeated char
00755       mTypeAheadBuffer = mFindNextBuffer;
00756       mFocusedDocSelection->GetRangeAt(0, getter_AddRefs(mStartFindRange));
00757     }
00758     else {
00759       // No find string to backspace in!
00760       if (mIsBackspaceProtectOn) {
00761         // This flag should be on only if the last key was a backspace.
00762         // It keeps us from accidentally hitting backspace too many times and
00763         // going back in history when we really just wanted to clear 
00764         // the find string.
00765         nsCOMPtr<nsISound> soundInterface =
00766           do_CreateInstance("@mozilla.org/sound;1");
00767         if (soundInterface) {
00768           soundInterface->Beep(); // beep to warn
00769         }
00770         mIsBackspaceProtectOn = PR_FALSE;
00771       }
00772       else {
00773         *aIsBackspaceUsed = PR_FALSE;
00774       }
00775 
00776       return NS_OK;
00777     }
00778   }
00779 
00780   // ---------- Only 1 char in string ------------
00781   if (mTypeAheadBuffer.Length() == 1 && 
00782       mRepeatingMode != eRepeatingCharReverse) {
00783     if (mStartFindRange) {
00784       mIsFindingText = PR_TRUE; // Prevent selection listener side effects
00785       mFocusedDocSelection->RemoveAllRanges();
00786       mFocusedDocSelection->AddRange(mStartFindRange);
00787     }
00788 
00789     mFocusedDocSelection->CollapseToStart();
00790     mIsFindingText = PR_FALSE;      
00791     CancelFind();
00792     mIsBackspaceProtectOn = PR_TRUE;
00793 
00794     return NS_OK;
00795   }
00796 
00797   // ---------- Multiple chars in string ----------
00798   PRBool findBackwards = PR_FALSE;
00799   if (mRepeatingMode == eRepeatingChar ||
00800            mRepeatingMode == eRepeatingCharReverse) {
00801     // Backspace in repeating char mode is like 
00802     mRepeatingMode = eRepeatingCharReverse;
00803     findBackwards = PR_TRUE;
00804   }
00805   else if (!mLastBadChar) {
00806     mTypeAheadBuffer.Truncate(mTypeAheadBuffer.Length() - 1);
00807   }
00808 
00809   mLastBadChar = 0;
00810 
00811   if (mBadKeysSinceMatch > 1) {
00812     --mBadKeysSinceMatch;
00813     DisplayStatus(PR_FALSE, nsnull, PR_FALSE); // Display failure status
00814     SaveFind();
00815     return NS_OK;
00816   }
00817 
00818   mBadKeysSinceMatch = 0;
00819   mDontTryExactMatch = PR_FALSE;
00820 
00821   // ---------- Get new find start ------------------
00822   nsIPresShell *presShell = nsnull;
00823   if (!findBackwards) {
00824     // For backspace, start from where first char was found
00825     // unless in backspacing after repeating char mode
00826     nsCOMPtr<nsIDOMNode> startNode;
00827     mStartFindRange->GetStartContainer(getter_AddRefs(startNode));
00828     if (startNode) {
00829       nsCOMPtr<nsIDOMDocument> domDoc;
00830       startNode->GetOwnerDocument(getter_AddRefs(domDoc));
00831       nsCOMPtr<nsIDocument> doc(do_QueryInterface(domDoc));
00832       if (doc) {
00833         presShell = doc->GetShellAt(0);
00834       }
00835     }
00836     if (!presShell) {
00837       *aIsBackspaceUsed = PR_FALSE;
00838       return NS_ERROR_FAILURE;
00839     }
00840     // Set the selection to the where the first character was found
00841     // so that find starts from there
00842     mIsFindingText = PR_TRUE; // so selection won't call CancelFind()
00843     GetSelection(presShell, getter_AddRefs(mFocusedDocSelCon), 
00844       getter_AddRefs(mFocusedDocSelection));
00845     nsCOMPtr<nsIDOMRange> startFindRange = do_CreateInstance(kRangeCID);
00846     mStartFindRange->CloneRange(getter_AddRefs(startFindRange));
00847     mFocusedDocSelection->RemoveAllRanges();
00848     mFocusedDocSelection->AddRange(startFindRange);
00849     mStartFindRange = startFindRange;
00850   }
00851 
00852   // ----------- Perform the find ------------------
00853   mIsFindingText = PR_TRUE; // so selection won't call CancelFind()
00854   if (NS_FAILED(FindItNow(presShell, findBackwards, mLinksOnly, PR_FALSE))) {
00855     DisplayStatus(PR_FALSE, nsnull, PR_FALSE); // Display failure status
00856   }
00857   mIsFindingText = PR_FALSE;
00858 
00859   SaveFind();
00860 
00861   return NS_OK;   // Backspace handled
00862 }
00863 
00864 
00865 nsresult
00866 nsTypeAheadFind::HandleChar(PRUnichar aChar)
00867 {
00868   // Add a printable char to mTypeAheadBuffer, then search for buffer contents
00869 
00870   // ------------ Million keys protection -------------
00871   if (mBadKeysSinceMatch >= kMaxBadCharsBeforeCancel) {
00872     // If they're just quickly mashing keys onto the keyboard, stop searching
00873     // until typeahead find is canceled via timeout or another normal means
00874     StartTimeout();  // Timeout from last bad key (this one)
00875     DisplayStatus(PR_FALSE, nsnull, PR_TRUE); // Status message to say find stopped
00876     return NS_ERROR_FAILURE;
00877   }
00878 
00879   aChar = ToLowerCase(NS_STATIC_CAST(PRUnichar, aChar));
00880   PRInt32 bufferLength = mTypeAheadBuffer.Length();
00881 
00882   mIsFirstVisiblePreferred = PR_FALSE;
00883 
00884   // --------- No new chars after find again ----------
00885   if (mRepeatingMode == eRepeatingForward ||
00886       mRepeatingMode == eRepeatingReverse) {
00887     // Once Accel+[shift]+G or [shift]+F3 has been used once,
00888     // new typing will start a new find
00889     CancelFind();
00890     bufferLength = 0;
00891     mRepeatingMode = eRepeatingNone;
00892   }
00893   // --------- New char in repeated char mode ---------
00894   else if ((mRepeatingMode == eRepeatingChar ||
00895            mRepeatingMode == eRepeatingCharReverse) && 
00896            bufferLength > 1 && aChar != mTypeAheadBuffer.First()) {
00897     // If they repeat the same character and then change, such as aaaab
00898     // start over with new char as a repeated char find
00899     mTypeAheadBuffer = aChar;
00900   }
00901   // ------- Set repeating mode ---------
00902   else if (bufferLength > 0) {
00903     if (mTypeAheadBuffer.First() != aChar) {
00904       mRepeatingMode = eRepeatingNone;
00905       mAllTheSameChar = PR_FALSE;
00906     }
00907   }
00908 
00909   mTypeAheadBuffer += aChar;    // Add the char!
00910 
00911   // --------- Initialize find if 1st char ----------
00912   if (bufferLength == 0) {
00913     if (!mLinksOnlyManuallySet) {
00914       // Reset links only to default, if not manually set
00915       // by the user via ' or / keypress at beginning
00916       mLinksOnly = mLinksOnlyPref;
00917     }
00918 
00919     mRepeatingMode = eRepeatingNone;
00920 
00921     // If you can see the selection (not collapsed or thru caret browsing),
00922     // or if already focused on a page element, start there.
00923     // Otherwise we're going to start at the first visible element
00924     NS_ENSURE_TRUE(mFocusedDocSelection, NS_ERROR_FAILURE);
00925     PRBool isSelectionCollapsed;
00926     mFocusedDocSelection->GetIsCollapsed(&isSelectionCollapsed);
00927 
00928     // If true, we will scan from top left of visible area
00929     // If false, we will scan from start of selection
00930     mIsFirstVisiblePreferred = !mCaretBrowsingOn && isSelectionCollapsed;
00931     if (mIsFirstVisiblePreferred) {
00932       // Get focused content from esm. If it's null, the document is focused.
00933       // If not, make sure the selection is in sync with the focus, so we can 
00934       // start our search from there.
00935       nsCOMPtr<nsIContent> focusedContent;
00936       nsCOMPtr<nsIPresShell> presShell(GetPresShell());
00937       NS_ENSURE_TRUE(presShell, NS_OK);
00938       nsPresContext *presContext = presShell->GetPresContext();
00939       NS_ENSURE_TRUE(presContext, NS_OK);
00940 
00941       nsIEventStateManager *esm = presContext->EventStateManager();
00942       esm->GetFocusedContent(getter_AddRefs(focusedContent));
00943       if (focusedContent) {
00944         mIsFindingText = PR_TRUE; // prevent selection listener from calling CancelFind()
00945         esm->MoveCaretToFocus();
00946         mIsFindingText = PR_FALSE;
00947         mIsFirstVisiblePreferred = PR_FALSE;
00948       }
00949       else {
00950         nsCOMPtr<nsISupports> container = presContext->GetContainer();
00951         nsCOMPtr<nsIDocShellTreeItem> docShellTreeItem = 
00952           do_QueryInterface(container);
00953         nsCOMPtr<nsIDocShellTreeItem> parentTreeItem;
00954         docShellTreeItem->GetSameTypeParent(getter_AddRefs(parentTreeItem));
00955         if (parentTreeItem) {
00956           mIsFirstVisiblePreferred = PR_FALSE; // focused on a frame or iframe
00957         }
00958       }
00959     }
00960   }
00961 
00962   // ----------- Find the text! ---------------------
00963   mIsFindingText = PR_TRUE; // prevent selection listener from calling CancelFind()
00964 
00965   nsresult rv = NS_ERROR_FAILURE;
00966 
00967   if (mBadKeysSinceMatch <= 1) {   // Don't even try if the last key was already bad
00968     if (!mDontTryExactMatch) {
00969       // Regular find, not repeated char find
00970 
00971       // Prefer to find exact match
00972       rv = FindItNow(nsnull, PR_FALSE, mLinksOnly, mIsFirstVisiblePreferred);
00973     }
00974 
00975 #ifndef NO_LINK_CYCLE_ON_SAME_CHAR
00976     if (NS_FAILED(rv) && !mLiteralTextSearchOnly && mAllTheSameChar && 
00977         mTypeAheadBuffer.Length() > 1) {
00978       mRepeatingMode = eRepeatingChar;
00979       mDontTryExactMatch = PR_TRUE;  // Repeated character find mode
00980       rv = FindItNow(nsnull, PR_TRUE, PR_TRUE, mIsFirstVisiblePreferred);
00981     }
00982 #endif
00983   }
00984 
00985   // ---------Handle success or failure ---------------
00986   mIsFindingText = PR_FALSE;
00987   if (NS_SUCCEEDED(rv)) {
00988     mLastBadChar = 0;
00989     if (mTypeAheadBuffer.Length() == 1) {
00990       // If first letter, store where the first find succeeded
00991       // (mStartFindRange)
00992 
00993       mStartFindRange = nsnull;
00994       nsCOMPtr<nsIDOMRange> startFindRange;
00995       mFocusedDocSelection->GetRangeAt(0, getter_AddRefs(startFindRange));
00996 
00997       if (startFindRange) {
00998         startFindRange->CloneRange(getter_AddRefs(mStartFindRange));
00999       }
01000     }
01001   }
01002   else {
01003     if (aChar == '/' || aChar == '\'') {
01004       // Didn't find / or ' -- use that key to start a new text or link find
01005       return StartNewFind(mFocusedWindow, aChar == '\'');
01006     }
01007     PRUint32 length = mTypeAheadBuffer.Length();
01008     if (mLastBadChar && length >= 1) {
01009       // We have to do this to put the exact typed string in the status 
01010       // Otherwise, it will be missing mLastBadChar, which had been removed
01011       // so that the user could avoid pressing backspace
01012       nsAutoString lastTwoCharsTyped(mLastBadChar);
01013       lastTwoCharsTyped += mTypeAheadBuffer.CharAt(length - 1);
01014       mTypeAheadBuffer.Truncate(length - 1);
01015       mTypeAheadBuffer += lastTwoCharsTyped;
01016       ++length;
01017     }
01018     DisplayStatus(PR_FALSE, nsnull, PR_FALSE); // Display failure status
01019     mRepeatingMode = eRepeatingNone;
01020 
01021     ++mBadKeysSinceMatch;
01022 
01023     // Error sound (don't fire when backspace is pressed, they're 
01024     // trying to correct the mistake!)
01025     PlayNotFoundSound();
01026 
01027     // Remove bad character from buffer, so we can continue typing from
01028     // last matched character
01029     if (length >= 1) {
01030       mLastBadChar = mTypeAheadBuffer.CharAt(length - 1);
01031       mTypeAheadBuffer.Truncate(length - 1);
01032     }
01033   }
01034 
01035   SaveFind();
01036 
01037   return NS_OK;
01038 }
01039 
01040 
01041 void
01042 nsTypeAheadFind::SaveFind()
01043 {
01044   // Store find string for find-next
01045   mFindNextBuffer = mTypeAheadBuffer;
01046   if (mLastBadChar) {
01047     mFindNextBuffer.Append(mLastBadChar);
01048   }
01049 
01050   nsCOMPtr<nsIWebBrowserFind> webBrowserFind;
01051   GetWebBrowserFind(mFocusedWindow, getter_AddRefs(webBrowserFind));
01052   if (webBrowserFind) {
01053     webBrowserFind->SetSearchString(PromiseFlatString(mTypeAheadBuffer).get());
01054   }
01055 
01056   if (!mFindService) {
01057     mFindService = do_GetService("@mozilla.org/find/find_service;1");
01058   }
01059   if (mFindService) {
01060     mFindService->SetSearchString(mFindNextBuffer);
01061   }
01062 
01063   // --- If accessibility.typeaheadfind.timeout is set,
01064   //     cancel find after specified # milliseconds ---
01065   StartTimeout();
01066 }
01067 
01068 
01069 void
01070 nsTypeAheadFind::PlayNotFoundSound()
01071 {
01072   if (mNotFoundSoundURL.IsEmpty())    // no sound
01073     return;
01074   if (!mSoundInterface) {
01075     mSoundInterface = do_CreateInstance("@mozilla.org/sound;1");
01076   }
01077   if (mSoundInterface) {
01078     mIsSoundInitialized = PR_TRUE;
01079 
01080     if (mNotFoundSoundURL.Equals("beep")) {
01081       mSoundInterface->Beep();
01082       return;
01083     }
01084 
01085     nsCOMPtr<nsIURI> soundURI;
01086     if (mNotFoundSoundURL.Equals("default"))
01087       NS_NewURI(getter_AddRefs(soundURI),
01088                 NS_LITERAL_CSTRING(TYPEAHEADFIND_NOTFOUND_WAV_URL));
01089     else
01090       NS_NewURI(getter_AddRefs(soundURI), mNotFoundSoundURL);
01091     nsCOMPtr<nsIURL> soundURL(do_QueryInterface(soundURI));
01092     if (soundURL) {
01093       mSoundInterface->Play(soundURL);
01094     }
01095   }
01096 }
01097 
01098 
01099 NS_IMETHODIMP
01100 nsTypeAheadFind::HandleText(nsIDOMEvent* aTextEvent)
01101 {
01102   // This is called multiple times in the middle of an 
01103   // IME composition
01104 
01105   if (!mIsIMETypeAheadActive) {
01106     return NS_OK;
01107   }
01108 
01109   // ------- Check if Type Ahead can occur here -------------
01110   // (and if it can, get the target content and document)
01111   nsCOMPtr<nsIContent> targetContent;
01112   nsCOMPtr<nsIPresShell> targetPresShell;
01113   GetTargetIfTypeAheadOkay(aTextEvent, getter_AddRefs(targetContent), 
01114     getter_AddRefs(targetPresShell));
01115   if (!targetContent || !targetPresShell) {
01116     mIsIMETypeAheadActive = PR_FALSE;
01117     return NS_OK;
01118   }
01119 
01120   nsCOMPtr<nsIPrivateTextEvent> textEvent(do_QueryInterface(aTextEvent));
01121   if (!textEvent)
01122     return NS_OK;
01123 
01124   textEvent->GetText(mIMEString);
01125 
01126   // show the candidate char/word in the status bar
01127   DisplayStatus(PR_FALSE, nsnull, PR_FALSE, mIMEString.get());
01128 
01129   // --------- Position the IME window --------------
01130   // XXX - what do we do with this, is it even necessary?
01131   //       should we position it in a consistent place?
01132   nsTextEventReply *textEventReply;
01133   textEvent->GetEventReply(&textEventReply);
01134 
01135   nsCOMPtr<nsICaret> caret;
01136   targetPresShell->GetCaret(getter_AddRefs(caret));
01137   NS_ENSURE_TRUE(caret, NS_ERROR_FAILURE);
01138 
01139   // Reset caret coordinates, so that IM window can move with us
01140   caret->GetCaretCoordinates(nsICaret::eIMECoordinates, mFocusedDocSelection,
01141     &(textEventReply->mCursorPosition), &(textEventReply->mCursorIsCollapsed), nsnull);
01142 
01143   return NS_OK;
01144 }
01145 
01146 
01147 NS_IMETHODIMP
01148 nsTypeAheadFind::HandleStartComposition(nsIDOMEvent* aCompositionEvent)
01149 {
01150   // This is called once at the start of an IME composition
01151 
01152   mIsIMETypeAheadActive = PR_TRUE;
01153 
01154   if (!mIsTypeAheadOn || mIsMenuBarActive || mIsMenuPopupActive) {
01155     mIsIMETypeAheadActive = PR_FALSE;
01156     return NS_OK;
01157   }
01158 
01159   // Pause the cancellation timer until IME is finished
01160   // HandleChar() will start it again
01161   if (mTimer) {
01162     mTimer->Cancel();
01163   }
01164 
01165   return NS_OK;
01166 }
01167 
01168 
01169 NS_IMETHODIMP
01170 nsTypeAheadFind::HandleEndComposition(nsIDOMEvent* aCompositionEvent)
01171 {
01172   // This is called once at the end of an IME composition
01173 
01174   if (!mIsIMETypeAheadActive) {
01175     return PR_FALSE;
01176   }
01177 
01178   // -------- Find the composed chars one at a time ---------
01179   nsReadingIterator<PRUnichar> iter;
01180   nsReadingIterator<PRUnichar> iterEnd;
01181 
01182   mIMEString.BeginReading(iter);
01183   mIMEString.EndReading(iterEnd);
01184 
01185   // Handle the characters one at a time
01186   while (iter != iterEnd) {
01187     if (NS_FAILED(HandleChar(*iter))) {
01188       // Character not found, exit loop early
01189       break;
01190     }
01191     ++iter;
01192   }
01193 
01194   mIMEString.Truncate(); // To be safe, so that find won't happen twice
01195 
01196   return NS_OK;
01197 }
01198 
01199 
01200 NS_IMETHODIMP
01201 nsTypeAheadFind::HandleQueryComposition(nsIDOMEvent* aCompositionEvent)
01202 {
01203   return NS_OK;
01204 }
01205 
01206 
01207 NS_IMETHODIMP
01208 nsTypeAheadFind::HandleQueryReconversion(nsIDOMEvent* aCompositionEvent)
01209 {
01210   return NS_OK;
01211 }
01212 
01213 
01214 NS_IMETHODIMP
01215 nsTypeAheadFind::HandleQueryCaretRect(nsIDOMEvent* aCompositionEvent)
01216 {
01217   return NS_OK;
01218 }
01219 
01220 
01221 nsresult
01222 nsTypeAheadFind::FindItNow(nsIPresShell *aPresShell,
01223                            PRBool aIsRepeatingSameChar, PRBool aIsLinksOnly,
01224                            PRBool aIsFirstVisiblePreferred)
01225 {
01226   nsCOMPtr<nsIPresShell> presShell(aPresShell);
01227   nsCOMPtr<nsIPresShell> startingPresShell = GetPresShell();
01228 
01229   if (!presShell) {
01230     presShell = startingPresShell;  // this is the current document
01231 
01232     if (!presShell) {
01233       return NS_ERROR_FAILURE;
01234     }
01235   }
01236 
01237   nsCOMPtr<nsPresContext> presContext = presShell->GetPresContext();
01238   if (!presContext) {
01239     return NS_ERROR_FAILURE;
01240   }
01241 
01242   nsCOMPtr<nsISupports> startingContainer = presContext->GetContainer();
01243   nsCOMPtr<nsIDocShellTreeItem> treeItem(do_QueryInterface(startingContainer));
01244   NS_ASSERTION(treeItem, "Bug 175321 Crashes with Type Ahead Find [@ nsTypeAheadFind::FindItNow]");
01245   if (!treeItem) {
01246     return NS_ERROR_FAILURE;
01247   }
01248 
01249   nsCOMPtr<nsIDocShellTreeItem> rootContentTreeItem;
01250   nsCOMPtr<nsIDocShell> currentDocShell;
01251   nsCOMPtr<nsIDocShell> startingDocShell(do_QueryInterface(startingContainer));
01252 
01253   treeItem->GetSameTypeRootTreeItem(getter_AddRefs(rootContentTreeItem));
01254   nsCOMPtr<nsIDocShell> rootContentDocShell =
01255     do_QueryInterface(rootContentTreeItem);
01256 
01257   if (!rootContentDocShell) {
01258     return NS_ERROR_FAILURE;
01259   }
01260 
01261   nsCOMPtr<nsISimpleEnumerator> docShellEnumerator;
01262   rootContentDocShell->GetDocShellEnumerator(nsIDocShellTreeItem::typeContent,
01263                                              nsIDocShell::ENUMERATE_FORWARDS,
01264                                              getter_AddRefs(docShellEnumerator));
01265 
01266   // Default: can start at the current document
01267   nsCOMPtr<nsISupports> currentContainer = startingContainer =
01268     do_QueryInterface(rootContentDocShell);
01269 
01270   // Iterate up to current shell, if there's more than 1 that we're
01271   // dealing with
01272   PRBool hasMoreDocShells;
01273 
01274   while (NS_SUCCEEDED(docShellEnumerator->HasMoreElements(&hasMoreDocShells))
01275          && hasMoreDocShells) {
01276     docShellEnumerator->GetNext(getter_AddRefs(currentContainer));
01277     currentDocShell = do_QueryInterface(currentContainer);
01278     if (!currentDocShell || currentDocShell == startingDocShell ||
01279         aIsFirstVisiblePreferred) {
01280       break;
01281     }
01282   }
01283 
01284   // ------------ Get ranges ready ----------------
01285   nsCOMPtr<nsIDOMRange> returnRange;
01286   if (NS_FAILED(GetSearchContainers(currentContainer, aIsRepeatingSameChar,
01287                                     aIsFirstVisiblePreferred, 
01288                                     !aIsFirstVisiblePreferred || mStartFindRange,
01289                                     getter_AddRefs(presShell),
01290                                     getter_AddRefs(presContext)))) {
01291     return NS_ERROR_FAILURE;
01292   }
01293 
01294   PRInt16 rangeCompareResult = 0;
01295   mStartPointRange->CompareBoundaryPoints(nsIDOMRange::START_TO_START,
01296                                           mSearchRange, &rangeCompareResult);
01297   // No need to wrap find in doc if starting at beginning
01298   PRBool hasWrapped = (rangeCompareResult <= 0);
01299 
01300   nsAutoString findBuffer;
01301   if (aIsRepeatingSameChar) {
01302     findBuffer = mTypeAheadBuffer.First();
01303   } else {
01304     findBuffer = PromiseFlatString(mTypeAheadBuffer);
01305   }
01306 
01307   if (findBuffer.IsEmpty())
01308     return NS_ERROR_FAILURE;
01309 
01310   mFind->SetFindBackwards(mRepeatingMode == eRepeatingCharReverse ||
01311     mRepeatingMode == eRepeatingReverse);
01312 
01313   while (PR_TRUE) {    // ----- Outer while loop: go through all docs -----
01314     while (PR_TRUE) {  // === Inner while loop: go through a single doc ===
01315       mFind->Find(findBuffer.get(), mSearchRange, mStartPointRange,
01316                   mEndPointRange, getter_AddRefs(returnRange));
01317       if (!returnRange) {
01318         break;  // Nothing found in this doc, go to outer loop (try next doc)
01319       }
01320 
01321       // ------- Test resulting found range for success conditions ------
01322       PRBool isInsideLink = PR_FALSE, isStartingLink = PR_FALSE;
01323 
01324       if (aIsLinksOnly) {
01325         // Don't check if inside link when searching all text
01326 
01327         RangeStartsInsideLink(returnRange, presShell, &isInsideLink,
01328                               &isStartingLink);
01329       }
01330 
01331       if (!IsRangeVisible(presShell, presContext, returnRange,
01332                           aIsFirstVisiblePreferred, PR_FALSE,
01333                           getter_AddRefs(mStartPointRange)) ||
01334           (aIsRepeatingSameChar && !isStartingLink) ||
01335           (aIsLinksOnly && !isInsideLink) ||
01336           (mStartLinksOnlyPref && aIsLinksOnly && !isStartingLink)) {
01337         // ------ Failure ------
01338         // Start find again from here
01339         returnRange->CloneRange(getter_AddRefs(mStartPointRange));
01340 
01341         // Collapse to end
01342         mStartPointRange->Collapse(mRepeatingMode == eRepeatingReverse || 
01343           mRepeatingMode == eRepeatingCharReverse);
01344 
01345         continue;
01346       }
01347 
01348       // ------ Success! -------
01349       // Make sure new document is selected
01350       if (presShell != startingPresShell) {
01351         // We are in a new document (because of frames/iframes)
01352         mFocusedDocSelection->CollapseToStart(); // Hide old doc's selection
01353         SetSelectionLook(startingPresShell, PR_FALSE, PR_FALSE); // hide caret
01354 
01355         nsIDocument *doc = presShell->GetDocument();
01356         if (!doc) {
01357           return NS_ERROR_FAILURE;
01358         }
01359         mFocusedWeakShell = do_GetWeakReference(presShell);
01360 
01361         // Get selection controller and selection for new frame/iframe
01362         GetSelection(presShell, getter_AddRefs(mFocusedDocSelCon), 
01363                      getter_AddRefs(mFocusedDocSelection));
01364       }
01365  
01366      if (!mFocusedDocSelection || !mFocusedDocSelCon) {
01367         // Apparently these can go away even though presshell/prescontext exist
01368         return NS_ERROR_FAILURE;
01369       }
01370 
01371       // Select the found text and focus it
01372       mFocusedDocSelection->RemoveAllRanges();
01373       mFocusedDocSelection->AddRange(returnRange);
01374       mFocusedDocSelCon->ScrollSelectionIntoView(nsISelectionController::SELECTION_NORMAL,
01375                                                  nsISelectionController::SELECTION_FOCUS_REGION, PR_TRUE);
01376       SetSelectionLook(presShell, PR_TRUE, mRepeatingMode != eRepeatingForward 
01377                                            && mRepeatingMode != eRepeatingReverse);
01378 
01379       nsIEventStateManager *esm = presContext->EventStateManager();
01380 
01381       PRBool isSelectionWithFocus;
01382       esm->SetContentState(nsnull, NS_EVENT_STATE_FOCUS); // Start off focusing doc
01383       esm->MoveFocusToCaret(PR_TRUE, &isSelectionWithFocus);
01384 
01385       nsCOMPtr<nsIContent> focusedContent;
01386       esm->GetFocusedContent(getter_AddRefs(focusedContent));
01387 
01388       DisplayStatus(PR_TRUE, focusedContent, PR_FALSE);
01389 
01390       mBadKeysSinceMatch = 0;
01391 
01392       return NS_OK;
01393     }
01394 
01395     // ======= end-inner-while (go through a single document) ==========
01396 
01397     // ---------- Nothing found yet, try next document  -------------
01398     PRBool hasTriedFirstDoc = PR_FALSE;
01399     do {
01400       // ==== Second inner loop - get another while  ====
01401       if (NS_SUCCEEDED(docShellEnumerator->HasMoreElements(&hasMoreDocShells))
01402           && hasMoreDocShells) {
01403         docShellEnumerator->GetNext(getter_AddRefs(currentContainer));
01404         NS_ASSERTION(currentContainer, "HasMoreElements lied to us!");
01405         currentDocShell = do_QueryInterface(currentContainer);
01406 
01407         if (currentDocShell) {
01408           break;
01409         }
01410       }
01411       else if (hasTriedFirstDoc) {  // Avoid potential infinite loop
01412         return NS_ERROR_FAILURE;  // No content doc shells
01413       }
01414 
01415       // Reached last doc shell, loop around back to first doc shell
01416       rootContentDocShell->GetDocShellEnumerator(nsIDocShellTreeItem::typeContent,
01417                                                  nsIDocShell::ENUMERATE_FORWARDS,
01418                                                  getter_AddRefs(docShellEnumerator));
01419       hasTriedFirstDoc = PR_TRUE;
01420     } while (docShellEnumerator);  // ==== end second inner while  ===
01421 
01422     PRBool continueLoop = PR_FALSE;
01423     if (currentDocShell != startingDocShell) {
01424       continueLoop = PR_TRUE;  // Try next document
01425     }
01426     else if (!hasWrapped || aIsFirstVisiblePreferred) {
01427       // Finished searching through docshells:
01428       // If aFirstVisiblePreferred == PR_TRUE, we may need to go through all
01429       // docshells twice -once to look for visible matches, the second time
01430       // for any match
01431       aIsFirstVisiblePreferred = PR_FALSE;
01432       hasWrapped = PR_TRUE;
01433       continueLoop = PR_TRUE; // Go through all docs again
01434     }
01435 
01436     if (continueLoop) {
01437       if (NS_FAILED(GetSearchContainers(currentContainer,
01438                                         aIsRepeatingSameChar,
01439                                         aIsFirstVisiblePreferred, PR_FALSE,
01440                                         getter_AddRefs(presShell),
01441                                         getter_AddRefs(presContext)))) {
01442         return NS_ERROR_FAILURE;
01443       }
01444 
01445       if (mRepeatingMode == eRepeatingCharReverse ||
01446           mRepeatingMode == eRepeatingReverse) {
01447         // Reverse mode:
01448         // swap start and end points, so that we start
01449         // at end of document and go to beginning
01450         nsCOMPtr<nsIDOMRange> tempRange;
01451         mStartPointRange->CloneRange(getter_AddRefs(tempRange));
01452         mStartPointRange = mEndPointRange;
01453         mEndPointRange = tempRange;
01454       }
01455 
01456       continue;
01457     }
01458 
01459     // ------------- Failed --------------
01460     break;
01461   }   // end-outer-while: go through all docs
01462 
01463   return NS_ERROR_FAILURE;
01464 }
01465 
01466 
01467 nsresult
01468 nsTypeAheadFind::GetSearchContainers(nsISupports *aContainer,
01469                                      PRBool aIsRepeatingSameChar,
01470                                      PRBool aIsFirstVisiblePreferred,
01471                                      PRBool aCanUseDocSelection,
01472                                      nsIPresShell **aPresShell,
01473                                      nsPresContext **aPresContext)
01474 {
01475   NS_ENSURE_ARG_POINTER(aContainer);
01476   NS_ENSURE_ARG_POINTER(aPresShell);
01477   NS_ENSURE_ARG_POINTER(aPresContext);
01478 
01479   *aPresShell = nsnull;
01480   *aPresContext = nsnull;
01481 
01482   nsCOMPtr<nsIDocShell> docShell(do_QueryInterface(aContainer));
01483   if (!docShell) {
01484     return NS_ERROR_FAILURE;
01485   }
01486 
01487   nsCOMPtr<nsPresContext> presContext;
01488   nsCOMPtr<nsIPresShell> presShell;
01489 
01490   docShell->GetPresShell(getter_AddRefs(presShell));
01491   docShell->GetPresContext(getter_AddRefs(presContext));
01492 
01493   if (!presShell || !presContext) {
01494     return NS_ERROR_FAILURE;
01495   }
01496 
01497   nsIDocument *doc = presShell->GetDocument();
01498   if (!doc) {
01499     return NS_ERROR_FAILURE;
01500   }
01501 
01502   nsCOMPtr<nsIContent> rootContent;
01503   nsCOMPtr<nsIDOMHTMLDocument> htmlDoc(do_QueryInterface(doc));
01504   if (htmlDoc) {
01505     nsCOMPtr<nsIDOMHTMLElement> bodyEl;
01506     htmlDoc->GetBody(getter_AddRefs(bodyEl));
01507     rootContent = do_QueryInterface(bodyEl);
01508   }
01509   if (!rootContent) {
01510     rootContent = doc->GetRootContent();
01511   }
01512   nsCOMPtr<nsIDOMNode> rootNode(do_QueryInterface(rootContent));
01513 
01514   if (!rootNode) {
01515     return NS_ERROR_FAILURE;
01516   }
01517 
01518   PRUint32 childCount = rootContent->GetChildCount();
01519 
01520   mSearchRange->SelectNodeContents(rootNode);
01521 
01522   mEndPointRange->SetEnd(rootNode, childCount);
01523   mEndPointRange->Collapse(PR_FALSE); // collapse to end
01524 
01525   // Consider current selection as null if
01526   // it's not in the currently focused document
01527   nsCOMPtr<nsIDOMRange> currentSelectionRange;
01528   nsCOMPtr<nsIPresShell> selectionPresShell = GetPresShell();
01529 
01530   if (aCanUseDocSelection && selectionPresShell == presShell && mFocusedDocSelection) {
01531     mFocusedDocSelection->GetRangeAt(0, getter_AddRefs(currentSelectionRange));
01532   }
01533 
01534   if (!currentSelectionRange) {
01535     // Ensure visible range, move forward if necessary
01536     // This uses ignores the return value, but usese the side effect of
01537     // IsRangeVisible. It returns the first visible range after searchRange
01538     IsRangeVisible(presShell, presContext, mSearchRange, 
01539                    aIsFirstVisiblePreferred, PR_TRUE, 
01540                    getter_AddRefs(mStartPointRange));
01541   }
01542   else {
01543     PRInt32 startOffset;
01544     nsCOMPtr<nsIDOMNode> startNode;
01545     if ((aIsRepeatingSameChar && mRepeatingMode != eRepeatingCharReverse) || 
01546         mRepeatingMode == eRepeatingForward) {
01547       currentSelectionRange->GetEndContainer(getter_AddRefs(startNode));
01548       currentSelectionRange->GetEndOffset(&startOffset);
01549     }
01550     else {
01551       currentSelectionRange->GetStartContainer(getter_AddRefs(startNode));
01552       currentSelectionRange->GetStartOffset(&startOffset);
01553     }
01554     if (!startNode) {
01555       startNode = rootNode;
01556     }
01557 
01558     // We need to set the start point this way, other methods haven't worked
01559     mStartPointRange->SelectNode(startNode);
01560     mStartPointRange->SetStart(startNode, startOffset);
01561   }
01562 
01563   mStartPointRange->Collapse(PR_TRUE); // collapse to start
01564 
01565   *aPresShell = presShell;
01566   NS_ADDREF(*aPresShell);
01567 
01568   *aPresContext = presContext;
01569   NS_ADDREF(*aPresContext);
01570 
01571   return NS_OK;
01572 }
01573 
01574 
01575 void
01576 nsTypeAheadFind::RangeStartsInsideLink(nsIDOMRange *aRange,
01577                                        nsIPresShell *aPresShell,
01578                                        PRBool *aIsInsideLink,
01579                                        PRBool *aIsStartingLink)
01580 {
01581   *aIsInsideLink = PR_FALSE;
01582   *aIsStartingLink = PR_TRUE;
01583 
01584   // ------- Get nsIContent to test -------
01585   nsCOMPtr<nsIDOMNode> startNode;
01586   nsCOMPtr<nsIContent> startContent, origContent;
01587   aRange->GetStartContainer(getter_AddRefs(startNode));
01588   PRInt32 startOffset;
01589   aRange->GetStartOffset(&startOffset);
01590 
01591   startContent = do_QueryInterface(startNode);
01592   if (!startContent) {
01593     NS_NOTREACHED("startContent should never be null");
01594     return;
01595   }
01596   origContent = startContent;
01597 
01598   if (startContent->IsContentOfType(nsIContent::eELEMENT)) {
01599     nsIContent *childContent = startContent->GetChildAt(startOffset);
01600     if (childContent) {
01601       startContent = childContent;
01602     }
01603   }
01604   else if (startOffset > 0) {
01605     nsCOMPtr<nsITextContent> textContent(do_QueryInterface(startContent));
01606 
01607     if (textContent) {
01608       // look for non whitespace character before start offset
01609       const nsTextFragment *textFrag = textContent->Text();
01610 
01611       for (PRInt32 index = 0; index < startOffset; index++) {
01612         if (!XP_IS_SPACE(textFrag->CharAt(index))) {
01613           *aIsStartingLink = PR_FALSE;  // not at start of a node
01614 
01615           break;
01616         }
01617       }
01618     }
01619   }
01620 
01621   // ------- Check to see if inside link ---------
01622 
01623   // We now have the correct start node for the range
01624   // Search for links, starting with startNode, and going up parent chain
01625 
01626   nsCOMPtr<nsIAtom> tag, hrefAtom(do_GetAtom("href"));
01627   nsCOMPtr<nsIAtom> typeAtom(do_GetAtom("type"));
01628 
01629   while (PR_TRUE) {
01630     // Keep testing while textContent is equal to something,
01631     // eventually we'll run out of ancestors
01632 
01633     if (startContent->IsContentOfType(nsIContent::eHTML)) {
01634       nsCOMPtr<nsILink> link(do_QueryInterface(startContent));
01635       if (link) {
01636         // Check to see if inside HTML link
01637         *aIsInsideLink = startContent->HasAttr(kNameSpaceID_None, hrefAtom);
01638         return;
01639       }
01640     }
01641     else {
01642       // Any xml element can be an xlink
01643       *aIsInsideLink = startContent->HasAttr(kNameSpaceID_XLink, hrefAtom);
01644       if (*aIsInsideLink) {
01645         nsAutoString xlinkType;
01646         startContent->GetAttr(kNameSpaceID_XLink, typeAtom, xlinkType);
01647         if (!xlinkType.EqualsLiteral("simple")) {
01648           *aIsInsideLink = PR_FALSE;  // Xlink must be type="simple"
01649         }
01650 
01651         return;
01652       }
01653     }
01654 
01655     // Get the parent
01656     nsCOMPtr<nsIContent> parent = startContent->GetParent();
01657     if (parent) {
01658       nsIContent *parentsFirstChild = parent->GetChildAt(0);
01659       nsCOMPtr<nsITextContent> textContent =
01660         do_QueryInterface(parentsFirstChild);
01661 
01662       // We don't want to look at a whitespace-only first child
01663       if (textContent && textContent->IsOnlyWhitespace())
01664         parentsFirstChild = parent->GetChildAt(1);
01665 
01666       if (parentsFirstChild != startContent) {
01667         // startContent wasn't a first child, so we conclude that
01668         // if this is inside a link, it's not at the beginning of it
01669         *aIsStartingLink = PR_FALSE;
01670       }
01671 
01672       startContent = parent;
01673     }
01674     else
01675       break;
01676   }
01677 
01678   *aIsStartingLink = PR_FALSE;
01679 }
01680 
01681 
01682 NS_IMETHODIMP
01683 nsTypeAheadFind::ScrollPositionWillChange(nsIScrollableView *aView,
01684                                           nscoord aX, nscoord aY)
01685 {
01686   return NS_OK;
01687 }
01688 
01689 
01690 NS_IMETHODIMP
01691 nsTypeAheadFind::ScrollPositionDidChange(nsIScrollableView *aScrollableView,
01692                                          nscoord aX, nscoord aY)
01693 {
01694   if (!mIsFindingText)
01695     CancelFind();
01696 
01697   return NS_OK;
01698 }
01699 
01700 
01701 NS_IMETHODIMP
01702 nsTypeAheadFind::NotifySelectionChanged(nsIDOMDocument *aDoc,
01703                                         nsISelection *aSel, PRInt16 aReason)
01704 {
01705   if (!mIsFindingText) {
01706     if (mRepeatingMode != eRepeatingNone) {
01707       // Selection had changed color for Type Ahead Find's version of Accel+G
01708       // We change it back when the selection changes from someone else
01709       nsCOMPtr<nsIPresShell> presShell(GetPresShell());
01710       SetSelectionLook(presShell, PR_FALSE, PR_FALSE);
01711     }
01712     CancelFind();
01713   }
01714 
01715   return NS_OK;
01716 }
01717 
01718 
01719 // ---------------- nsITypeAheadFind --------------------
01720 
01721 NS_IMETHODIMP
01722 nsTypeAheadFind::FindNext(PRBool aFindBackwards, nsISupportsInterfacePointer *aCallerWindowSupports)
01723 {
01724   NS_ENSURE_TRUE(aCallerWindowSupports, NS_ERROR_FAILURE);
01725 
01726   // aCallerWindowSupports holds an nsISupports to the window the 
01727   // find next command was fired in
01728   // We clear out the window pointer when handling the find command ourselves.
01729 
01730   if (!mIsFindAllowedInWindow || mFindNextBuffer.IsEmpty() || !mFocusedWindow) {
01731     return NS_OK;
01732   }
01733 
01734   // Compare the top level content pres shell of typeaheadfind
01735   // with the top level content pres shell window where find next is happening
01736   // If they're different, exit so that webbrowswerfind can handle FindNext()
01737 
01738   nsCOMPtr<nsIPresShell> typeAheadPresShell(GetPresShell());
01739   NS_ENSURE_TRUE(typeAheadPresShell, NS_OK);
01740 
01741   nsPresContext *presContext = typeAheadPresShell->GetPresContext();
01742   NS_ENSURE_TRUE(presContext, NS_OK);
01743 
01744   nsCOMPtr<nsISupports> container = presContext->GetContainer();
01745   nsCOMPtr<nsIDocShellTreeItem> treeItem(do_QueryInterface(container));
01746   NS_ENSURE_TRUE(treeItem, NS_OK);
01747 
01748   // Reget typeAheadPresShell to make sure we're 
01749   // comparing with the top content presshell
01750   GetTopContentPresShell(treeItem, getter_AddRefs(typeAheadPresShell));
01751   NS_ENSURE_TRUE(typeAheadPresShell, NS_OK);
01752 
01753   nsCOMPtr<nsISupports> callerWindowSupports;
01754   aCallerWindowSupports->GetData(getter_AddRefs(callerWindowSupports));
01755   nsCOMPtr<nsIInterfaceRequestor> ifreq(do_QueryInterface(callerWindowSupports));
01756   NS_ENSURE_TRUE(ifreq, NS_ERROR_FAILURE);
01757 
01758   nsCOMPtr<nsIWebNavigation> webNav(do_GetInterface(ifreq));
01759   treeItem = do_QueryInterface(webNav);
01760   NS_ENSURE_TRUE(treeItem, NS_OK);
01761 
01762   nsCOMPtr<nsIPresShell> callerPresShell;
01763   GetTopContentPresShell(treeItem, getter_AddRefs(callerPresShell));
01764   NS_ENSURE_TRUE(callerPresShell, NS_OK);
01765 
01766   if (callerPresShell != typeAheadPresShell) {
01767     // This means typeaheadfind is active in a different window or doc
01768     // So it's not appropriate to find next for the current window
01769     mFindNextBuffer.Truncate();
01770     return NS_OK;
01771   }
01772 
01773   nsCOMPtr<nsIDOMWindow> callerWin(do_QueryInterface(callerWindowSupports));
01774   NS_ENSURE_TRUE(callerWin, NS_OK);
01775 
01776   nsCOMPtr<nsIWebBrowserFind> webBrowserFind;
01777   GetWebBrowserFind(callerWin, getter_AddRefs(webBrowserFind));
01778   NS_ENSURE_TRUE(webBrowserFind, NS_ERROR_FAILURE);
01779 
01780   nsXPIDLString webBrowserFindString;
01781   if (webBrowserFind) {
01782     webBrowserFind->GetSearchString(getter_Copies(webBrowserFindString));
01783     if (!webBrowserFindString.Equals(mFindNextBuffer)) {
01784       // If they're not equal, then the find dialog was used last,
01785       // not typeaheadfind. Typeaheadfind applies to the last find,
01786       // so we should let nsIWebBrowserFind::FindNext() do it.
01787       mFindNextBuffer.Truncate();
01788       return NS_OK;
01789     }
01790 
01791   }
01792 
01793   /* -------------------------------------------------------
01794    * Typeaheadfind is active in the currently focused window,
01795    * so do the find next operation now
01796    */
01797 
01798   // Clear out window data, to indicate we handled the findnext
01799   aCallerWindowSupports->SetData(nsnull); 
01800 
01801   if (mBadKeysSinceMatch > 0) {
01802     // We know it will fail, so just return
01803     return NS_OK;
01804   }
01805 
01806   mTypeAheadBuffer = mFindNextBuffer;
01807   PRBool repeatingSameChar = PR_FALSE;
01808 
01809   if (mRepeatingMode == eRepeatingChar || 
01810       mRepeatingMode == eRepeatingCharReverse) {
01811     mRepeatingMode = aFindBackwards? eRepeatingCharReverse: eRepeatingChar;
01812     repeatingSameChar = PR_TRUE;
01813   }
01814   else {
01815     mRepeatingMode = aFindBackwards? eRepeatingReverse: eRepeatingForward;
01816   }
01817   mLiteralTextSearchOnly = PR_TRUE;
01818 
01819   mIsFindingText = PR_TRUE; // prevent our listeners from calling CancelFind()
01820 
01821   if (NS_FAILED(FindItNow(nsnull, repeatingSameChar, mLinksOnly, PR_FALSE))) {
01822     DisplayStatus(PR_FALSE, nsnull, PR_FALSE); // Display failure status
01823     mRepeatingMode = eRepeatingNone;
01824   }
01825 
01826   mTypeAheadBuffer.Truncate(); // Find buffer is now in mFindNextBuffer
01827   StartTimeout();
01828   mIsFindingText = PR_FALSE;
01829 
01830   return NS_OK;
01831 }
01832 
01833 
01834 NS_IMETHODIMP
01835 nsTypeAheadFind::GetIsActive(PRBool *aIsActive)
01836 {
01837   *aIsActive = mLinksOnlyManuallySet || !mTypeAheadBuffer.IsEmpty();
01838 
01839   return NS_OK;
01840 }
01841 
01842 
01843 /*
01844  * Start new type ahead find manually
01845  */
01846 
01847 NS_IMETHODIMP
01848 nsTypeAheadFind::StartNewFind(nsIDOMWindow *aWindow, PRBool aLinksOnly)
01849 {
01850   if (!mFind || !mIsTypeAheadOn || !aWindow)
01851     return NS_ERROR_FAILURE;  // Type Ahead Find not correctly initialized
01852 
01853   // This routine will set up the doc listeners
01854   // Do it first, it does a CancelFind()
01855   UseInWindow(aWindow);
01856 
01857   mLinksOnly = aLinksOnly;
01858   mLinksOnlyManuallySet = PR_TRUE;
01859   mRepeatingMode = eRepeatingNone;
01860 
01861   PRBool isAutoStartWin;
01862   GetAutoStart(mFocusedWindow, &isAutoStartWin);
01863   if (!isAutoStartWin) {
01864     AttachWindowListeners(mFocusedWindow);
01865   }
01866 
01867   if (mFocusedDocSelection) {
01868     mIsFindingText = PR_TRUE;  // Turn off side effects from selection listener
01869     mFocusedDocSelection->CollapseToStart();
01870     mIsFindingText = PR_FALSE;
01871     nsCOMPtr<nsIPresShell> presShell(GetPresShell());
01872     SetSelectionLook(presShell, PR_TRUE, PR_TRUE);
01873   }
01874   DisplayStatus(PR_TRUE, nsnull, PR_FALSE);
01875   StartTimeout();
01876 
01877   return NS_OK;
01878 }
01879 
01880 void
01881 nsTypeAheadFind::ResetGlobalAutoStart(PRBool aAutoStart)
01882 {
01883   // Enumerate through the current top level windows
01884   // and either attach or remove window listeners
01885 
01886   CancelFind();
01887 
01888   nsCOMPtr<nsIWindowWatcher> windowWatcher =
01889     do_GetService(NS_WINDOWWATCHER_CONTRACTID);
01890   if (!windowWatcher) {
01891     return;
01892   }
01893 
01894   nsCOMPtr<nsISimpleEnumerator> enumerator;
01895   windowWatcher->GetWindowEnumerator(getter_AddRefs(enumerator));
01896   if (!enumerator) {
01897     return;
01898   }
01899 
01900   PRBool hasMoreWindows;
01901   while (NS_SUCCEEDED(enumerator->HasMoreElements(&hasMoreWindows))
01902          && hasMoreWindows) {
01903     nsCOMPtr<nsISupports> supports;
01904     enumerator->GetNext(getter_AddRefs(supports));
01905     nsCOMPtr<nsIDOMWindow> domWin(do_QueryInterface(supports));
01906     if (domWin) {
01907       if (aAutoStart) {
01908         AttachWindowListeners(domWin);
01909       }
01910       else {
01911         RemoveWindowListeners(domWin);
01912       }
01913     }
01914   }
01915 }
01916 
01917 
01918 NS_IMETHODIMP
01919 nsTypeAheadFind::SetAutoStart(nsIDOMWindow *aDOMWin, PRBool aAutoStartOn)
01920 {
01921   if (!aDOMWin) {
01922     return NS_ERROR_FAILURE;
01923   }
01924 
01925   nsCOMPtr<nsISupports> windowSupports(do_QueryInterface(aDOMWin));
01926   PRInt32 index = mManualFindWindows->IndexOf(windowSupports);
01927 
01928   if (aAutoStartOn) {
01929     if (index >= 0) {
01930       // Remove from list of windows requiring manual find
01931       mManualFindWindows->RemoveElementAt(index);
01932     }
01933   }
01934   else {  // Should be in list of windows requiring manual find
01935     if (aDOMWin == mFocusedWindow) {
01936       CancelFind();
01937     }
01938 
01939     if (index < 0) {  // Should be in list of windows requiring manual find
01940       mManualFindWindows->InsertElementAt(windowSupports, 0);
01941     }
01942   }
01943 
01944   return NS_OK;
01945 }
01946 
01947 
01948 NS_IMETHODIMP
01949 nsTypeAheadFind::GetAutoStart(nsIDOMWindow *aDOMWin, PRBool *aIsAutoStartOn)
01950 {
01951   *aIsAutoStartOn = PR_FALSE;
01952 
01953   if (!mAutoStartPref || !aDOMWin)
01954     return NS_OK;
01955 
01956   nsCOMPtr<nsIInterfaceRequestor> ifreq(do_QueryInterface(aDOMWin));
01957   NS_ENSURE_TRUE(ifreq, NS_OK);
01958 
01959   nsCOMPtr<nsIWebNavigation> webNav(do_GetInterface(ifreq));
01960   nsCOMPtr<nsIDocShellTreeItem> treeItem(do_QueryInterface(webNav));
01961   nsCOMPtr<nsIEditorDocShell> editorDocShell(do_QueryInterface(treeItem));
01962   if (editorDocShell) {
01963     PRBool isEditable;
01964     editorDocShell->GetEditable(&isEditable);
01965 
01966     if (isEditable) {
01967       return NS_OK;
01968     }
01969   }
01970 
01971   nsCOMPtr<nsIDOMDocument> domDoc;
01972   aDOMWin->GetDocument(getter_AddRefs(domDoc));
01973   nsCOMPtr<nsIDocument> doc(do_QueryInterface(domDoc));
01974   NS_ENSURE_TRUE(doc, NS_OK);
01975 
01976   nsCOMPtr<nsIDOMXULDocument> xulDoc(do_QueryInterface(doc));
01977   nsCOMPtr<nsIImageDocument> imageDoc(do_QueryInterface(doc));
01978   if (xulDoc || imageDoc) {
01979     return NS_OK; // Avoid any xul docs, whether in chrome or content
01980   }
01981 
01982   if (mLinksOnlyPref) {
01983     nsAutoString contentType;
01984     doc->GetContentType(contentType);
01985     if (contentType.EqualsLiteral("text/plain")) {
01986       return NS_OK; // No auto link search in plain text pages
01987     }
01988   }
01989 
01990   nsIDocument *parentDoc = doc->GetParentDocument();
01991   if (parentDoc) {
01992     // get content for <browser>
01993     nsCOMPtr<nsIDOMElement> browserElement =
01994       do_QueryInterface(parentDoc->FindContentForSubDocument(doc));
01995 
01996     if (browserElement) {
01997       nsAutoString tagName, autoFind, test;
01998       browserElement->GetLocalName(tagName);
01999       browserElement->GetAttribute(NS_LITERAL_STRING("type"), test);
02000       browserElement->GetAttribute(NS_LITERAL_STRING("autofind"), autoFind);
02001       if (tagName.EqualsLiteral("editor") || 
02002           autoFind.EqualsLiteral("false")) {
02003         return NS_OK;
02004       }
02005     }
02006   }
02007 
02008   // Is this window stored in manual find windows list?
02009   nsCOMPtr<nsISupports> windowSupports(do_QueryInterface(aDOMWin));
02010   *aIsAutoStartOn = mManualFindWindows->IndexOf(windowSupports) < 0;
02011 
02012   return NS_OK;
02013 }
02014 
02015 
02016 NS_IMETHODIMP
02017 nsTypeAheadFind::CancelFind()
02018 {
02019   // Stop current find if:
02020   //   1. Escape pressed
02021   //   2. Selection is moved/changed
02022   //   3. User clicks in window (if it changes the selection)
02023   //   4. Window scrolls
02024   //   5. User tabs (this can move the selection)
02025   //   6. Timer expires
02026 
02027   if (mLinksOnlyManuallySet == PR_FALSE && mTypeAheadBuffer.IsEmpty()) {
02028     // Nothing to cancel
02029     return NS_OK;
02030   }
02031 
02032   if (mIsTypeAheadOn || mRepeatingMode != eRepeatingNone) {
02033     mTypeAheadBuffer.Truncate();
02034     DisplayStatus(PR_FALSE, nsnull, PR_TRUE); // Clear status
02035     nsCOMPtr<nsIPresShell> presShell(GetPresShell());
02036     SetSelectionLook(presShell, PR_FALSE, PR_FALSE);
02037   }
02038 
02039   // This is set to true if the user types / (all text) or ' (links only) first
02040   mLinksOnlyManuallySet = PR_FALSE;
02041 
02042   // These will be initialized to their true values after
02043   // the first character is typed
02044   mLiteralTextSearchOnly = PR_FALSE;
02045   mDontTryExactMatch = PR_FALSE;
02046   mStartFindRange = nsnull;
02047   mBadKeysSinceMatch = 0;
02048   mIsBackspaceProtectOn = PR_FALSE;
02049   mLastBadChar = 0;
02050   mAllTheSameChar = PR_TRUE; // Until at least 2 different chars are typed
02051 
02052   if (mTimer) {
02053     mTimer->Cancel();
02054     mTimer = nsnull;
02055   }
02056 
02057   PRBool isAutoStartWin;
02058   GetAutoStart(mFocusedWindow, &isAutoStartWin);
02059   if (!isAutoStartWin) {
02060     RemoveDocListeners();
02061     RemoveWindowListeners(mFocusedWindow);
02062     mIsFindAllowedInWindow = PR_FALSE;
02063     mFocusedWindow = nsnull;
02064   }
02065 
02066   return NS_OK;
02067 }
02068 
02069 
02070 // ------- Helper Methods ---------------
02071 
02072 void 
02073 nsTypeAheadFind::GetTopContentPresShell(nsIDocShellTreeItem *aDocShellTreeItem, 
02074                                         nsIPresShell **aPresShell)
02075 {
02076   *aPresShell = nsnull;
02077 
02078   nsCOMPtr<nsIDocShellTreeItem> topContentTreeItem;
02079   aDocShellTreeItem->GetSameTypeRootTreeItem(getter_AddRefs(topContentTreeItem));
02080   nsCOMPtr<nsIDocShell> topContentDocShell(do_QueryInterface(topContentTreeItem));
02081 
02082   if (!topContentDocShell)
02083     return;
02084 
02085   topContentDocShell->GetPresShell(aPresShell);
02086 }
02087 
02088 void 
02089 nsTypeAheadFind::GetStartWindow(nsIDOMWindow *aWindow, nsIDOMWindow **aStartWindow)
02090 {
02091   // Return the root ancestor content window of aWindow
02092 
02093   *aStartWindow = nsnull;
02094   nsCOMPtr<nsIInterfaceRequestor> ifreq(do_QueryInterface(aWindow));
02095   NS_ASSERTION(ifreq, "Can't get interface requestor");
02096   if (!ifreq)
02097     return;
02098 
02099   nsCOMPtr<nsIWebNavigation> webNav(do_GetInterface(ifreq));
02100   nsCOMPtr<nsIDocShellTreeItem> treeItem(do_QueryInterface(webNav));
02101   NS_ASSERTION(ifreq, "Can't get doc shell tree item");
02102   if (!treeItem)
02103     return;
02104   
02105   PRInt32 docShellType;
02106   treeItem->GetItemType(&docShellType);
02107   if (docShellType == nsIDocShellTreeItem::typeContent) {
02108     nsCOMPtr<nsIDocShellTreeItem> rootContentTreeItem;
02109     treeItem->GetSameTypeRootTreeItem(getter_AddRefs(rootContentTreeItem));
02110     nsCOMPtr<nsIDOMWindow> domWin(do_GetInterface(rootContentTreeItem));
02111     *aStartWindow = domWin;
02112   }
02113   else {
02114     *aStartWindow = aWindow;
02115   }
02116 
02117   NS_IF_ADDREF(*aStartWindow);
02118 }
02119 
02120 nsresult
02121 nsTypeAheadFind::GetWebBrowserFind(nsIDOMWindow *aWin, 
02122                                    nsIWebBrowserFind **aWebBrowserFind)
02123 {
02124   NS_ENSURE_ARG_POINTER(aWin);
02125   NS_ENSURE_ARG_POINTER(aWebBrowserFind);
02126 
02127   *aWebBrowserFind = nsnull;
02128 
02129   nsCOMPtr<nsIInterfaceRequestor> ifreq(do_QueryInterface(aWin));
02130   NS_ENSURE_TRUE(ifreq, NS_ERROR_FAILURE);
02131 
02132   nsCOMPtr<nsIWebNavigation> webNav(do_GetInterface(ifreq));
02133   nsCOMPtr<nsIDocShell> docShell(do_QueryInterface(webNav));
02134   NS_ENSURE_TRUE(docShell, NS_ERROR_FAILURE);
02135 
02136   nsCOMPtr<nsIWebBrowserFind> webBrowserFind(do_GetInterface(docShell));
02137   NS_ENSURE_TRUE(webBrowserFind, NS_ERROR_FAILURE);
02138 
02139   NS_ADDREF(*aWebBrowserFind = webBrowserFind);
02140 
02141   return NS_OK;
02142 }
02143 
02144 
02145 void
02146 nsTypeAheadFind::StartTimeout()
02147 {
02148   if (mTimeoutLength) {
02149     if (!mTimer) {
02150       mTimer = do_CreateInstance("@mozilla.org/timer;1");
02151       if (mTimer) {
02152         mTimer->InitWithCallback(this, mTimeoutLength, nsITimer::TYPE_ONE_SHOT);
02153       }
02154     }
02155     else {
02156       mTimer->SetDelay(mTimeoutLength);
02157     }
02158   }
02159 }
02160 
02161 void
02162 nsTypeAheadFind::SetSelectionLook(nsIPresShell *aPresShell, 
02163                                   PRBool aChangeColor, 
02164                                   PRBool aEnabled)
02165 {
02166   if (!aPresShell || !mFocusedDocSelCon)
02167     return;
02168 
02169   // Show caret when type ahead find is on
02170   // Also paint selection bright (typeaheadfind on) or normal
02171   // (typeaheadfind off)
02172 
02173   if (aChangeColor) {
02174     mFocusedDocSelCon->SetDisplaySelection(nsISelectionController::SELECTION_ATTENTION);
02175   } else {
02176     mFocusedDocSelCon->SetDisplaySelection(nsISelectionController::SELECTION_ON);
02177   }
02178 
02179   mFocusedDocSelCon->RepaintSelection(nsISelectionController::SELECTION_NORMAL);
02180 
02181   if (mCaretBrowsingOn) {
02182     return;  // Leave caret visibility as it is
02183   }
02184 
02185   nsCOMPtr<nsICaret> caret;
02186   aPresShell->GetCaret(getter_AddRefs(caret));
02187   nsCOMPtr<nsILookAndFeel> lookNFeel(do_GetService(kLookAndFeelCID));
02188   if (!caret || !lookNFeel) {
02189     return;
02190   }
02191 
02192   if (aEnabled) {
02193     // Set caret visible so that it's obvious we're in a live mode
02194     caret->SetCaretDOMSelection(mFocusedDocSelection);
02195     caret->SetVisibilityDuringSelection(PR_TRUE);
02196     caret->SetCaretVisible(PR_TRUE);
02197     mFocusedDocSelCon->SetCaretEnabled(PR_TRUE);
02198   }
02199   else {
02200     PRInt32 isCaretVisibleDuringSelection = 0;
02201     lookNFeel->GetMetric(nsILookAndFeel::eMetric_ShowCaretDuringSelection,
02202                          isCaretVisibleDuringSelection);
02203     caret->SetVisibilityDuringSelection(isCaretVisibleDuringSelection != 0);
02204     nsCOMPtr<nsISelection> caretDomSelection;
02205     caret->GetCaretDOMSelection(getter_AddRefs(caretDomSelection));
02206     if (mFocusedDocSelection == caretDomSelection)  {
02207       mFocusedDocSelCon->SetCaretEnabled(isCaretVisibleDuringSelection != 0);
02208     }
02209   }
02210 }
02211 
02212 
02213 void
02214 nsTypeAheadFind::RemoveDocListeners()
02215 {
02216   nsCOMPtr<nsIPresShell> presShell(GetPresShell());
02217   nsIViewManager* vm = nsnull;
02218 
02219   if (presShell) {
02220     vm = presShell->GetViewManager();
02221   }
02222 
02223   nsIScrollableView* scrollableView = nsnull;
02224   if (vm) {
02225     vm->GetRootScrollableView(&scrollableView);
02226   }
02227 
02228   if (scrollableView) {
02229     scrollableView->RemoveScrollPositionListener(this);
02230   }
02231 
02232   mFocusedWeakShell = nsnull;
02233 
02234   // Remove selection listener
02235   nsCOMPtr<nsISelectionPrivate> selPrivate =
02236     do_QueryInterface(mFocusedDocSelection);
02237 
02238   if (selPrivate) {
02239     selPrivate->RemoveSelectionListener(this); // remove us if we're a listener
02240   }
02241 
02242   mFocusedDocSelection = nsnull;
02243   mFocusedDocSelCon = nsnull; // Selection controller owns pres shell!
02244 }
02245 
02246 
02247 void
02248 nsTypeAheadFind::AttachDocListeners(nsIPresShell *aPresShell)
02249 {
02250   if (!aPresShell) {
02251     return;
02252   }
02253 
02254   nsIViewManager* vm = aPresShell->GetViewManager();
02255   if (!vm) {
02256     return;
02257   }
02258 
02259   nsIScrollableView* scrollableView = nsnull;
02260   vm->GetRootScrollableView(&scrollableView);
02261   if (!scrollableView) {
02262     return;
02263   }
02264 
02265   scrollableView->AddScrollPositionListener(this);
02266 
02267   // Attach selection listener
02268   nsCOMPtr<nsISelectionPrivate> selPrivate =
02269     do_QueryInterface(mFocusedDocSelection);
02270 
02271   if (selPrivate) {
02272     selPrivate->AddSelectionListener(this);
02273   }
02274 }
02275 
02276 
02277 void
02278 nsTypeAheadFind::RemoveWindowListeners(nsIDOMWindow *aDOMWin)
02279 {
02280   nsCOMPtr<nsIDOMEventTarget> chromeEventHandler;
02281   GetChromeEventHandler(aDOMWin, getter_AddRefs(chromeEventHandler));
02282   if (!chromeEventHandler) {
02283     return;
02284   }
02285 
02286   // Use capturing, otherwise the normal find next will get activated when ours should
02287   nsCOMPtr<nsIDOMEventReceiver> receiver(do_QueryInterface(chromeEventHandler));
02288   nsCOMPtr<nsIDOMEventGroup> systemGroup;
02289   receiver->GetSystemEventGroup(getter_AddRefs(systemGroup));
02290   nsCOMPtr<nsIDOM3EventTarget> target(do_QueryInterface(receiver));
02291 
02292   target->RemoveGroupedEventListener(NS_LITERAL_STRING("keypress"),
02293                                      NS_STATIC_CAST(nsIDOMKeyListener*, this),
02294                                      PR_FALSE, systemGroup);
02295 
02296   if (aDOMWin == mFocusedWindow) {
02297     mFocusedWindow = nsnull;
02298   }
02299 
02300   // Remove menu listeners
02301   nsIDOMEventListener *genericEventListener = 
02302     NS_STATIC_CAST(nsIDOMEventListener*, NS_STATIC_CAST(nsIDOMKeyListener*, this));
02303 
02304   chromeEventHandler->RemoveEventListener(NS_LITERAL_STRING("popupshown"), 
02305                                           genericEventListener, 
02306                                           PR_TRUE);
02307 
02308   chromeEventHandler->RemoveEventListener(NS_LITERAL_STRING("popuphidden"), 
02309                                           genericEventListener, 
02310                                           PR_TRUE);
02311 
02312   chromeEventHandler->RemoveEventListener(NS_LITERAL_STRING("DOMMenuBarActive"), 
02313                                           genericEventListener, 
02314                                           PR_TRUE);
02315 
02316   chromeEventHandler->RemoveEventListener(NS_LITERAL_STRING("DOMMenuBarInactive"), 
02317                                           genericEventListener, 
02318                                           PR_TRUE);
02319 
02320   chromeEventHandler->RemoveEventListener(NS_LITERAL_STRING("unload"), 
02321                                           genericEventListener, 
02322                                           PR_TRUE);
02323 
02324   // Remove DOM Text listener for IME text events
02325   nsCOMPtr<nsIDOMEventReceiver> chromeEventReceiver = 
02326     do_QueryInterface(chromeEventHandler);
02327   chromeEventReceiver->RemoveEventListenerByIID(NS_STATIC_CAST(nsIDOMTextListener*, this), 
02328     NS_GET_IID(nsIDOMTextListener));
02329   chromeEventReceiver->RemoveEventListenerByIID(NS_STATIC_CAST(nsIDOMCompositionListener*, this), 
02330     NS_GET_IID(nsIDOMCompositionListener));
02331 }
02332 
02333 
02334 void
02335 nsTypeAheadFind::AttachWindowListeners(nsIDOMWindow *aDOMWin)
02336 {
02337   nsCOMPtr<nsIDOMEventTarget> chromeEventHandler;
02338   GetChromeEventHandler(aDOMWin, getter_AddRefs(chromeEventHandler));
02339   if (!chromeEventHandler) {
02340     return;
02341   }
02342 
02343   // Use capturing, otherwise the normal find next will get activated when ours should
02344   nsCOMPtr<nsIDOMEventReceiver> receiver(do_QueryInterface(chromeEventHandler));
02345   nsCOMPtr<nsIDOMEventGroup> systemGroup;
02346   receiver->GetSystemEventGroup(getter_AddRefs(systemGroup));
02347   nsCOMPtr<nsIDOM3EventTarget> target(do_QueryInterface(receiver));
02348 
02349   target->AddGroupedEventListener(NS_LITERAL_STRING("keypress"),
02350                                   NS_STATIC_CAST(nsIDOMKeyListener*, this),
02351                                   PR_FALSE, systemGroup);
02352 
02353   // Attach menu listeners, this will help us ignore keystrokes meant for menus
02354   nsIDOMEventListener *genericEventListener = 
02355     NS_STATIC_CAST(nsIDOMEventListener*, NS_STATIC_CAST(nsIDOMKeyListener*, this));
02356 
02357   chromeEventHandler->AddEventListener(NS_LITERAL_STRING("popupshown"), 
02358                                        genericEventListener, 
02359                                        PR_TRUE);
02360 
02361   chromeEventHandler->AddEventListener(NS_LITERAL_STRING("popuphidden"), 
02362                                         genericEventListener, 
02363                                        PR_TRUE);
02364 
02365   chromeEventHandler->AddEventListener(NS_LITERAL_STRING("DOMMenuBarActive"), 
02366                                        genericEventListener, 
02367                                        PR_TRUE);
02368   
02369   chromeEventHandler->AddEventListener(NS_LITERAL_STRING("DOMMenuBarInactive"), 
02370                                        genericEventListener, 
02371                                        PR_TRUE);
02372 
02373   chromeEventHandler->AddEventListener(NS_LITERAL_STRING("unload"), 
02374                                        genericEventListener, 
02375                                        PR_TRUE);
02376 
02377   // Add DOM Text listener for IME text events
02378   nsCOMPtr<nsIDOMEventReceiver> chromeEventReceiver =
02379     do_QueryInterface(chromeEventHandler);
02380   chromeEventReceiver->AddEventListenerByIID(NS_STATIC_CAST(nsIDOMTextListener*, this), 
02381     NS_GET_IID(nsIDOMTextListener));
02382   chromeEventReceiver->AddEventListenerByIID(NS_STATIC_CAST(nsIDOMCompositionListener*, this), 
02383     NS_GET_IID(nsIDOMCompositionListener));
02384 }
02385 
02386 
02387 void
02388 nsTypeAheadFind::GetChromeEventHandler(nsIDOMWindow *aDOMWin,
02389                                        nsIDOMEventTarget **aChromeTarget)
02390 {
02391   nsCOMPtr<nsPIDOMWindow> privateDOMWindow(do_QueryInterface(aDOMWin));
02392   nsIChromeEventHandler *chromeEventHandler = nsnull;
02393   if (privateDOMWindow) {
02394     chromeEventHandler = privateDOMWindow->GetChromeEventHandler();
02395   }
02396 
02397   nsCOMPtr<nsIDOMEventTarget> target(do_QueryInterface(chromeEventHandler));
02398 
02399   *aChromeTarget = target;
02400   NS_IF_ADDREF(*aChromeTarget);
02401 }
02402 
02403 PRBool
02404 nsTypeAheadFind::IsTargetContentOkay(nsIContent *aContent)
02405 {
02406   if (!aContent) {
02407     return PR_FALSE;
02408   }
02409 
02410   if (aContent->IsContentOfType(nsIContent::eHTML_FORM_CONTROL)) {
02411     nsCOMPtr<nsIFormControl> formControl(do_QueryInterface(aContent));
02412     PRInt32 controlType = formControl->GetType();
02413     if (controlType == NS_FORM_SELECT || 
02414         controlType == NS_FORM_TEXTAREA ||
02415         controlType == NS_FORM_INPUT_TEXT ||
02416         controlType == NS_FORM_INPUT_PASSWORD ||
02417         controlType == NS_FORM_INPUT_FILE) {
02418       // Don't steal keys from these form controls 
02419       // - selects have their own incremental find for options
02420       // - text fields need to allow typing
02421       return PR_FALSE;
02422     }
02423   }
02424   else if (aContent->IsContentOfType(nsIContent::eHTML)) {
02425     // Test for isindex, a deprecated kind of text field. We're using a string 
02426     // compare because <isindex> is not considered a form control, so it does 
02427     // not support nsIFormControl or eHTML_FORM_CONTROL, and it's not worth 
02428     // having a table of atoms just for it. Instead, we're paying for 1 extra 
02429     // string compare per keystroke, which isn't too bad.
02430     const char *tagStr;
02431     aContent->Tag()->GetUTF8String(&tagStr);
02432     if (strcmp(tagStr, "isindex") == 0) {
02433       return PR_FALSE;
02434     }
02435   }
02436 
02437   return PR_TRUE;
02438 }
02439 
02440 
02441 nsresult
02442 nsTypeAheadFind::GetTargetIfTypeAheadOkay(nsIDOMEvent *aEvent, 
02443                                           nsIContent **aTargetContent,
02444                                           nsIPresShell **aTargetPresShell)
02445 {
02446   NS_ENSURE_ARG_POINTER(aEvent);
02447   NS_ENSURE_ARG_POINTER(aTargetContent);
02448   NS_ENSURE_ARG_POINTER(aTargetPresShell);
02449 
02450   *aTargetContent = nsnull;
02451   *aTargetPresShell = nsnull;
02452 
02453   nsCOMPtr<nsIDOMNSEvent> nsEvent(do_QueryInterface(aEvent));
02454   if (!nsEvent) {
02455     return NS_ERROR_FAILURE;
02456   }
02457 
02458   nsCOMPtr<nsIDOMEventTarget> domEventTarget;
02459   nsEvent->GetOriginalTarget(getter_AddRefs(domEventTarget));
02460 
02461   nsCOMPtr<nsIContent> targetContent(do_QueryInterface(domEventTarget));
02462 
02463   // ---- Exit early if in form controls that can be typed in ---------
02464 
02465   if (!IsTargetContentOkay(targetContent)) {
02466     if (!mTypeAheadBuffer.IsEmpty()) {
02467       CancelFind();
02468     }
02469     return NS_OK;
02470   }
02471 
02472   NS_ADDREF(*aTargetContent = targetContent);
02473 
02474   // ---------- Is the keystroke in a new window? -------------------
02475 
02476   nsCOMPtr<nsIDocument> doc = targetContent->GetDocument();
02477   if (!doc) {
02478     return NS_OK;
02479   }
02480 
02481   nsCOMPtr<nsIDOMWindow> domWin(do_QueryInterface(doc->GetScriptGlobalObject()));
02482   nsCOMPtr<nsIDOMWindow> topContentWin;
02483   GetStartWindow(domWin, getter_AddRefs(topContentWin));
02484 
02485   // ---------- Get presshell -----------
02486 
02487   nsIPresShell *presShell = doc->GetShellAt(0);
02488   if (!presShell) {
02489     return NS_OK;
02490   }
02491 
02492   nsCOMPtr<nsIPresShell> lastShell(GetPresShell());
02493 
02494   if (lastShell != presShell || topContentWin != mFocusedWindow) {
02495     GetAutoStart(topContentWin, &mIsFindAllowedInWindow);
02496     if (mIsFindAllowedInWindow) {
02497       UseInWindow(topContentWin);
02498     }
02499     else {
02500       CancelFind();
02501       mFocusedWindow = nsnull;
02502     }
02503   }
02504   if (!mIsFindAllowedInWindow) {
02505     return NS_OK;
02506   }
02507 
02508   if (presShell->GetPresContext()->Type() == nsPresContext::eContext_PrintPreview) {
02509     // Typeaheadfind is not designed to work in print preview.
02510     // You can't navigate through the links there.
02511     if (lastShell != presShell) {
02512       mFocusedWeakShell = do_GetWeakReference(presShell);
02513       CancelFind();
02514       DisplayStatus(PR_FALSE, nsnull, PR_TRUE, EmptyString().get());  // Clear status
02515     }
02516     return NS_OK;
02517   }
02518   
02519   NS_ADDREF(*aTargetPresShell = presShell);  
02520   return NS_OK;
02521 }
02522 
02523 
02524 void
02525 nsTypeAheadFind::GetSelection(nsIPresShell *aPresShell,
02526                               nsISelectionController **aSelCon,
02527                               nsISelection **aDOMSel)
02528 {
02529   // if aCurrentNode is nsnull, get selection for document
02530   *aDOMSel = nsnull;
02531 
02532   nsPresContext *presContext = aPresShell->GetPresContext();
02533 
02534   nsIFrame *frame = aPresShell->GetRootFrame();
02535 
02536   if (presContext && frame) {
02537     frame->GetSelectionController(presContext, aSelCon);
02538     if (*aSelCon) {
02539       (*aSelCon)->GetSelection(nsISelectionController::SELECTION_NORMAL,
02540                                aDOMSel);
02541     }
02542   }
02543 }
02544 
02545 
02546 PRBool
02547 nsTypeAheadFind::IsRangeVisible(nsIPresShell *aPresShell,
02548                                 nsPresContext *aPresContext,
02549                                 nsIDOMRange *aRange, PRBool aMustBeInViewPort,
02550                                 PRBool aGetTopVisibleLeaf,
02551                                 nsIDOMRange **aFirstVisibleRange)
02552 {
02553   NS_ENSURE_ARG_POINTER(aPresShell);
02554   NS_ENSURE_ARG_POINTER(aPresContext);
02555   NS_ENSURE_ARG_POINTER(aRange);
02556   NS_ENSURE_ARG_POINTER(aFirstVisibleRange);
02557 
02558   // We need to know if the range start is visible.
02559   // Otherwise, return a the first visible range start 
02560   // in aFirstVisibleRange
02561 
02562   aRange->CloneRange(aFirstVisibleRange);
02563   nsCOMPtr<nsIDOMNode> node;
02564   aRange->GetStartContainer(getter_AddRefs(node));
02565 
02566   nsCOMPtr<nsIContent> content(do_QueryInterface(node));
02567   if (!content) {
02568     return PR_FALSE;
02569   }
02570 
02571   nsIFrame *frame = nsnull;
02572   aPresShell->GetPrimaryFrameFor(content, &frame);
02573   if (!frame) {
02574     // No frame! Not visible then.
02575 
02576     return PR_FALSE;
02577   }
02578 
02579   if (!frame->GetStyleVisibility()->IsVisible()) {
02580     return PR_FALSE;
02581   }
02582 
02583 
02584   // ---- We have a frame ----
02585   if (!aMustBeInViewPort) {
02586     //  Don't need it to be on screen, just in rendering tree
02587 
02588     return PR_TRUE;
02589   }
02590 
02591   // Get the next in flow frame that contains the range start
02592   PRInt32 startRangeOffset, startFrameOffset, endFrameOffset;
02593   aRange->GetStartOffset(&startRangeOffset);
02594   while (PR_TRUE) {
02595     frame->GetOffsets(startFrameOffset, endFrameOffset);
02596     if (startRangeOffset < endFrameOffset) {
02597       break;
02598     }
02599     nsIFrame *nextInFlowFrame = frame->GetNextInFlow();
02600     if (nextInFlowFrame) {
02601       frame = nextInFlowFrame;
02602     }
02603     else {
02604       break;
02605     }
02606   }
02607 
02608   // Set up the variables we need, return true if we can't get at them all
02609   const PRUint16 kMinPixels  = 12;
02610 
02611   nsIViewManager* viewManager = aPresShell->GetViewManager();
02612   if (!viewManager) {
02613     return PR_TRUE;
02614   }
02615 
02616   // Get the bounds of the current frame, relative to the current view.
02617   // We don't use the more accurate AccGetBounds, because that is
02618   // more expensive and the STATE_OFFSCREEN flag that this is used
02619   // for only needs to be a rough indicator
02620   nsIView *containingView = nsnull;
02621   nsPoint frameOffset;
02622   float p2t;
02623   p2t = aPresContext->PixelsToTwips();
02624   nsRectVisibility rectVisibility = nsRectVisibility_kAboveViewport;
02625 
02626   if (!aGetTopVisibleLeaf) {
02627     nsRect relFrameRect = frame->GetRect();
02628     frame->GetOffsetFromView(frameOffset, &containingView);
02629     if (!containingView) {
02630       // no view -- not visible
02631 
02632       return PR_FALSE;
02633     }
02634 
02635     relFrameRect.x = frameOffset.x;
02636     relFrameRect.y = frameOffset.y;
02637 
02638     viewManager->GetRectVisibility(containingView, relFrameRect,
02639                                    NS_STATIC_CAST(PRUint16, (kMinPixels * p2t)),
02640                                    &rectVisibility);
02641 
02642     if (rectVisibility != nsRectVisibility_kAboveViewport &&
02643         rectVisibility != nsRectVisibility_kZeroAreaRect) {
02644       return PR_TRUE;
02645     }
02646   }
02647 
02648   // We know that the target range isn't usable because it's not in the
02649   // view port. Move range forward to first visible point,
02650   // this speeds us up a lot in long documents
02651   nsCOMPtr<nsIBidirectionalEnumerator> frameTraversal;
02652   nsCOMPtr<nsIFrameTraversal> trav(do_CreateInstance(kFrameTraversalCID));
02653   if (trav) {
02654     trav->NewFrameTraversal(getter_AddRefs(frameTraversal), LEAF,
02655                             aPresContext, frame);
02656   }
02657 
02658   if (!frameTraversal) {
02659     return PR_FALSE;
02660   }
02661 
02662   while (rectVisibility == nsRectVisibility_kAboveViewport ||
02663          rectVisibility == nsRectVisibility_kZeroAreaRect) {
02664     frameTraversal->Next();
02665     nsISupports* currentItem;
02666     frameTraversal->CurrentItem(&currentItem);
02667     frame = NS_STATIC_CAST(nsIFrame*, currentItem);
02668     if (!frame) {
02669       return PR_FALSE;
02670     }
02671 
02672     nsRect relFrameRect = frame->GetRect();
02673     frame->GetOffsetFromView(frameOffset, &containingView);
02674     if (containingView) {
02675       relFrameRect.x = frameOffset.x;
02676       relFrameRect.y = frameOffset.y;
02677       viewManager->GetRectVisibility(containingView, relFrameRect,
02678                                      NS_STATIC_CAST(PRUint16,
02679                                                     (kMinPixels * p2t)),
02680                                      &rectVisibility);
02681     }
02682   }
02683 
02684   if (frame) {
02685     nsCOMPtr<nsIDOMNode> firstVisibleNode =
02686       do_QueryInterface(frame->GetContent());
02687 
02688     if (firstVisibleNode) {
02689       (*aFirstVisibleRange)->SelectNode(firstVisibleNode);
02690       frame->GetOffsets(startFrameOffset, endFrameOffset);
02691       (*aFirstVisibleRange)->SetStart(firstVisibleNode, startFrameOffset);
02692       (*aFirstVisibleRange)->Collapse(PR_TRUE);  // Collapse to start
02693     }
02694   }
02695 
02696   return PR_FALSE;
02697 }
02698 
02699 
02700 nsresult
02701 nsTypeAheadFind::GetTranslatedString(const nsAString& aKey,
02702                                      nsAString& aStringOut)
02703 {
02704   nsXPIDLString xsValue;
02705 
02706   if (!mStringBundle ||
02707     NS_FAILED(mStringBundle->GetStringFromName(PromiseFlatString(aKey).get(),
02708                                                getter_Copies(xsValue)))) {
02709     return NS_ERROR_FAILURE;
02710   }
02711 
02712   aStringOut.Assign(xsValue);
02713 
02714   return NS_OK;
02715 }
02716 
02717 
02718 void
02719 nsTypeAheadFind::DisplayStatus(PRBool aSuccess, nsIContent *aFocusedContent,
02720                                PRBool aClearStatus, const PRUnichar *aText)
02721 {
02722   // pres shell -> pres context -> container -> tree item ->
02723   // tree owner -> browser chrome
02724 
02725   nsCOMPtr<nsIPresShell> presShell(GetPresShell());
02726   if (!presShell) {
02727     return;
02728   }
02729 
02730   nsPresContext *presContext = presShell->GetPresContext();
02731   if (!presContext) {
02732     return;
02733   }
02734 
02735   nsCOMPtr<nsISupports> pcContainer = presContext->GetContainer();
02736   nsCOMPtr<nsIDocShellTreeItem> treeItem(do_QueryInterface(pcContainer));
02737   if (!treeItem) {
02738     return;
02739   }
02740 
02741   nsCOMPtr<nsIDocShellTreeOwner> treeOwner;
02742   treeItem->GetTreeOwner(getter_AddRefs(treeOwner));
02743   if (!treeOwner) {
02744     return;
02745   }
02746 
02747   nsCOMPtr<nsIWebBrowserChrome> browserChrome(do_GetInterface(treeOwner));
02748   if (!browserChrome) {
02749     return;
02750   }
02751 
02752   nsAutoString statusString;
02753   if (aText) 
02754     statusString = aText;
02755   else {
02756     if (aClearStatus) {
02757       GetTranslatedString(NS_LITERAL_STRING("stopfind"), statusString);
02758     } else if (aSuccess && mTypeAheadBuffer.IsEmpty()) {
02759       // When find has been started manually
02760       // but no characters have been typed yet
02761       nsAutoString key;
02762 
02763       if (mLinksOnly) {
02764         key.AssignLiteral("startlinkfind");
02765       } else {
02766         key.AssignLiteral("starttextfind");
02767       }
02768       GetTranslatedString(key, statusString);
02769     } else {
02770       nsAutoString key;
02771 
02772       if (mLinksOnly) {
02773         key.AssignLiteral("link");
02774       } else {
02775         key.AssignLiteral("text");
02776       }
02777 
02778       if (!aSuccess) {
02779         key.AppendLiteral("not");
02780       }
02781 
02782       key.AppendLiteral("found");
02783 
02784       if (NS_SUCCEEDED(GetTranslatedString(key, statusString))) {
02785         if (mRepeatingMode == eRepeatingChar || 
02786             mRepeatingMode == eRepeatingCharReverse) {
02787           statusString += mTypeAheadBuffer.First();
02788         }
02789         else {
02790           statusString += mTypeAheadBuffer;
02791         }
02792 
02793         nsAutoString closeQuoteString, urlString;
02794         GetTranslatedString(NS_LITERAL_STRING("closequote"), closeQuoteString);
02795         statusString += closeQuoteString;
02796 
02797         if (mRepeatingMode != eRepeatingNone) {
02798           if (mRepeatingMode == eRepeatingChar) {
02799             key.AssignLiteral("repeated");
02800           }
02801           else if (mRepeatingMode == eRepeatingForward) {
02802             key.AssignLiteral("nextmatch");
02803           }
02804           else {
02805             key.AssignLiteral("prevmatch");
02806           }
02807           nsAutoString repeatedModeString;
02808           GetTranslatedString(key, repeatedModeString);
02809           statusString += NS_LITERAL_STRING(" ") + repeatedModeString;
02810         }
02811 
02812         nsCOMPtr<nsIDOMNode> focusedNode(do_QueryInterface(aFocusedContent));
02813         if (focusedNode) {
02814           presShell->GetLinkLocation(focusedNode, urlString);
02815         }
02816 
02817         if (!urlString.IsEmpty()) {   // Add URL in parenthesis
02818           nsAutoString openParenString, closeParenString;
02819           GetTranslatedString(NS_LITERAL_STRING("openparen"), openParenString);
02820           GetTranslatedString(NS_LITERAL_STRING("closeparen"), closeParenString);
02821           statusString += NS_LITERAL_STRING("   ") + openParenString
02822                           + urlString + closeParenString;
02823         }
02824       }
02825     }
02826   }
02827 
02828   browserChrome->SetStatus(nsIWebBrowserChrome::STATUS_LINK,
02829                            PromiseFlatString(statusString).get());
02830 }
02831 
02832 
02833 // ------- nsTypeAheadController ---------------
02834 
02835 const char * const sLinkFindString = "cmd_findTypeLinks";
02836 const char * const sTextFindString = "cmd_findTypeText";
02837 
02838 NS_IMPL_ISUPPORTS1(nsTypeAheadController, nsIController)
02839 
02840 nsTypeAheadController::nsTypeAheadController(nsIFocusController *aFocusController):
02841   mFocusController(aFocusController)
02842 {
02843 }
02844 
02845 nsTypeAheadController::~nsTypeAheadController()
02846 {
02847 }
02848 
02849 NS_IMETHODIMP
02850 nsTypeAheadController::IsCommandEnabled(const char *aCommand, PRBool *aResult)
02851 {
02852   NS_ENSURE_ARG_POINTER(aResult);
02853 
02854   *aResult = PR_FALSE;
02855 
02856   NS_ENSURE_TRUE(mFocusController, NS_ERROR_FAILURE);
02857 
02858   nsCOMPtr<nsIDOMElement> focusedElement;
02859   mFocusController->GetFocusedElement(getter_AddRefs(focusedElement));
02860 
02861   nsCOMPtr<nsIContent> focusedContent(do_QueryInterface(focusedElement));
02862 
02863   // Make sure we're not focused on a text field, listbox
02864   // or other form control that needs typeahead keystrokes
02865   if (focusedContent) {
02866     *aResult = nsTypeAheadFind::IsTargetContentOkay(focusedContent);
02867     return NS_OK;
02868   }
02869 
02870   // We're focused on a document
02871   nsCOMPtr<nsIDOMWindowInternal> winInternal;
02872   mFocusController->GetFocusedWindow(getter_AddRefs(winInternal));
02873   nsCOMPtr<nsIDOMWindow> domWin(do_QueryInterface(winInternal));
02874   if (!domWin) {
02875     return NS_OK;
02876   }
02877 
02878   *aResult = PR_TRUE;
02879 
02880   // Make sure we're not in Midas editing mode
02881   nsCOMPtr<nsIDOMDocument> domDoc;
02882   domWin->GetDocument(getter_AddRefs(domDoc));
02883   nsCOMPtr<nsIDOMNSHTMLDocument> htmlDoc(do_QueryInterface(domDoc));
02884   if (htmlDoc) {
02885     nsAutoString designMode;
02886     htmlDoc->GetDesignMode(designMode);
02887     if (designMode.EqualsLiteral("on")) {
02888       *aResult = PR_FALSE;
02889     }
02890   }
02891   
02892   return NS_OK;
02893 }
02894 
02895 NS_IMETHODIMP
02896 nsTypeAheadController::SupportsCommand(const char *aCommand, PRBool *aResult)
02897 {
02898   NS_ENSURE_ARG_POINTER(aResult);
02899 
02900   *aResult = PR_FALSE;
02901 
02902   if (!nsCRT::strcmp(sLinkFindString, aCommand) ||
02903       !nsCRT::strcmp(sTextFindString, aCommand)) {
02904     *aResult = PR_TRUE;
02905   }
02906 
02907   return NS_OK;
02908 }
02909 
02910 NS_IMETHODIMP
02911 nsTypeAheadController::DoCommand(const char *aCommand)
02912 {
02913   PRBool isLinkSearch = PR_FALSE;
02914 
02915   if (nsCRT::strcmp(sLinkFindString, aCommand) == 0) {
02916     isLinkSearch = PR_TRUE;
02917   }
02918   else if (nsCRT::strcmp(sTextFindString, aCommand) != 0) {
02919     return NS_OK;
02920   }
02921 
02922   NS_ENSURE_TRUE(mFocusController, NS_ERROR_FAILURE);
02923 
02924   nsCOMPtr<nsIDOMWindowInternal> domWinInternal;
02925   mFocusController->GetFocusedWindow(getter_AddRefs(domWinInternal));
02926 
02927   nsCOMPtr<nsIDOMWindow> startContentWin;
02928   EnsureContentWindow(domWinInternal, getter_AddRefs(startContentWin));
02929   NS_ENSURE_TRUE(startContentWin, NS_ERROR_FAILURE);
02930 
02931   nsCOMPtr<nsITypeAheadFind> typeAhead = 
02932     do_GetService(NS_TYPEAHEADFIND_CONTRACTID);
02933   NS_ENSURE_TRUE(typeAhead, NS_ERROR_FAILURE);
02934 
02935   return typeAhead->StartNewFind(startContentWin, isLinkSearch);
02936 }
02937 
02938 /* void onEvent (in string eventName); */
02939 NS_IMETHODIMP
02940 nsTypeAheadController::OnEvent(const char *eventName)
02941 {
02942   return NS_OK;
02943 }
02944 
02945 nsresult
02946 nsTypeAheadController::EnsureContentWindow(nsIDOMWindowInternal *aFocusedWin, 
02947                                            nsIDOMWindow **aStartContentWin)
02948 {
02949   NS_ENSURE_ARG_POINTER(aFocusedWin);
02950   NS_ENSURE_ARG_POINTER(aStartContentWin);
02951 
02952   *aStartContentWin = nsnull;
02953   nsCOMPtr<nsIInterfaceRequestor> ifreq(do_QueryInterface(aFocusedWin));
02954   NS_ENSURE_TRUE(ifreq, NS_OK);
02955 
02956   nsCOMPtr<nsIWebNavigation> webNav(do_GetInterface(ifreq));
02957   nsCOMPtr<nsIDocShellTreeItem> treeItem(do_QueryInterface(webNav));
02958   NS_ENSURE_TRUE(treeItem, NS_OK);
02959   PRInt32 treeItemType;
02960   treeItem->GetItemType(&treeItemType);
02961 
02962   nsCOMPtr<nsIDOMWindow> startContentWin;
02963   if (treeItemType == nsIDocShellTreeItem::typeContent) {
02964     startContentWin = do_QueryInterface(aFocusedWin);
02965   }
02966   else {
02967     // Use enumerator tet first content docshell
02968     nsCOMPtr<nsIDocShell> docShell(do_QueryInterface(webNav));
02969     NS_ENSURE_TRUE(docShell, NS_OK);
02970 
02971     nsCOMPtr<nsISimpleEnumerator> docShellEnumerator;
02972     docShell->GetDocShellEnumerator(nsIDocShellTreeItem::typeContent,
02973                                     nsIDocShell::ENUMERATE_FORWARDS,
02974                                     getter_AddRefs(docShellEnumerator));
02975   
02976     PRBool hasMoreDocShells;
02977     if (NS_SUCCEEDED(docShellEnumerator->HasMoreElements(&hasMoreDocShells))
02978         && hasMoreDocShells) {
02979 
02980       // There is a content docshell child, let's use it (focus it and return it)
02981       nsCOMPtr<nsISupports> container;
02982       docShellEnumerator->GetNext(getter_AddRefs(container));
02983       nsCOMPtr<nsIInterfaceRequestor> ifreq(do_QueryInterface(container));
02984 
02985       if (ifreq) {
02986         startContentWin = do_GetInterface(ifreq);
02987         NS_ENSURE_TRUE(startContentWin, NS_ERROR_FAILURE);
02988 
02989         // Set new focus in root content of new window
02990         // This only happens if we weren't already in content
02991         // Using nsIContent to focus makes sure the 
02992         // previous window's focused content gets blurred properly
02993         nsCOMPtr<nsIDOMDocument> domDoc;
02994         startContentWin->GetDocument(getter_AddRefs(domDoc));
02995         nsCOMPtr<nsIDocument> doc(do_QueryInterface(domDoc));
02996         NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE);
02997 
02998         nsCOMPtr<nsIDocShell> docShell(do_QueryInterface(container));
02999         nsCOMPtr<nsPresContext> presContext;
03000         docShell->GetPresContext(getter_AddRefs(presContext));
03001         NS_ENSURE_TRUE(presContext, NS_ERROR_FAILURE);
03002 
03003         nsIContent *rootContent = doc->GetRootContent();
03004         NS_ENSURE_TRUE(rootContent, NS_ERROR_FAILURE);
03005         rootContent->SetFocus(presContext);
03006       }
03007     }
03008   }
03009 
03010   *aStartContentWin = startContentWin;
03011   NS_IF_ADDREF(*aStartContentWin);
03012   return NS_OK;
03013 }
03014 
03015 already_AddRefed<nsIPresShell>
03016 nsTypeAheadFind::GetPresShell()
03017 {
03018   if (!mFocusedWeakShell)
03019     return nsnull;
03020 
03021   nsIPresShell *shell = nsnull;
03022   CallQueryReferent(mFocusedWeakShell.get(), &shell);
03023   if (shell) {
03024     nsPresContext *pc = shell->GetPresContext();
03025     if (!pc || !nsCOMPtr<nsISupports>(pc->GetContainer())) {
03026       NS_RELEASE(shell);
03027     }
03028   }
03029 
03030   return shell;
03031 }