toolkit/components/typeaheadfind/nsTypeAheadFind.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

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

mercurial