toolkit/components/typeaheadfind/nsTypeAheadFind.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/toolkit/components/typeaheadfind/nsTypeAheadFind.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,1231 @@
     1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.8 +
     1.9 +#include "nsCOMPtr.h"
    1.10 +#include "nsMemory.h"
    1.11 +#include "nsIServiceManager.h"
    1.12 +#include "mozilla/ModuleUtils.h"
    1.13 +#include "nsIWebBrowserChrome.h"
    1.14 +#include "nsCURILoader.h"
    1.15 +#include "nsCycleCollectionParticipant.h"
    1.16 +#include "nsNetUtil.h"
    1.17 +#include "nsIURL.h"
    1.18 +#include "nsIURI.h"
    1.19 +#include "nsIDocShell.h"
    1.20 +#include "nsIDocShellTreeOwner.h"
    1.21 +#include "nsISimpleEnumerator.h"
    1.22 +#include "nsPIDOMWindow.h"
    1.23 +#include "nsIPrefBranch.h"
    1.24 +#include "nsIPrefService.h"
    1.25 +#include "nsString.h"
    1.26 +#include "nsCRT.h"
    1.27 +
    1.28 +#include "nsIDOMNode.h"
    1.29 +#include "mozilla/dom/Element.h"
    1.30 +#include "nsIFrame.h"
    1.31 +#include "nsFrameTraversal.h"
    1.32 +#include "nsIImageDocument.h"
    1.33 +#include "nsIDOMHTMLDocument.h"
    1.34 +#include "nsIDOMHTMLElement.h"
    1.35 +#include "nsIDocument.h"
    1.36 +#include "nsISelection.h"
    1.37 +#include "nsTextFragment.h"
    1.38 +#include "nsIDOMNSEditableElement.h"
    1.39 +#include "nsIEditor.h"
    1.40 +
    1.41 +#include "nsIDocShellTreeItem.h"
    1.42 +#include "nsIWebNavigation.h"
    1.43 +#include "nsIInterfaceRequestor.h"
    1.44 +#include "nsIInterfaceRequestorUtils.h"
    1.45 +#include "nsContentCID.h"
    1.46 +#include "nsLayoutCID.h"
    1.47 +#include "nsWidgetsCID.h"
    1.48 +#include "nsIFormControl.h"
    1.49 +#include "nsNameSpaceManager.h"
    1.50 +#include "nsIWindowWatcher.h"
    1.51 +#include "nsIObserverService.h"
    1.52 +#include "nsFocusManager.h"
    1.53 +#include "mozilla/dom/Element.h"
    1.54 +#include "mozilla/dom/Link.h"
    1.55 +#include "nsRange.h"
    1.56 +
    1.57 +#include "nsTypeAheadFind.h"
    1.58 +
    1.59 +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsTypeAheadFind)
    1.60 +  NS_INTERFACE_MAP_ENTRY(nsITypeAheadFind)
    1.61 +  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsITypeAheadFind)
    1.62 +  NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
    1.63 +  NS_INTERFACE_MAP_ENTRY(nsIObserver)
    1.64 +NS_INTERFACE_MAP_END
    1.65 +
    1.66 +NS_IMPL_CYCLE_COLLECTING_ADDREF(nsTypeAheadFind)
    1.67 +NS_IMPL_CYCLE_COLLECTING_RELEASE(nsTypeAheadFind)
    1.68 +
    1.69 +NS_IMPL_CYCLE_COLLECTION(nsTypeAheadFind, mFoundLink, mFoundEditable,
    1.70 +                         mCurrentWindow, mStartFindRange, mSearchRange,
    1.71 +                         mStartPointRange, mEndPointRange, mSoundInterface,
    1.72 +                         mFind)
    1.73 +
    1.74 +static NS_DEFINE_CID(kFrameTraversalCID, NS_FRAMETRAVERSAL_CID);
    1.75 +
    1.76 +#define NS_FIND_CONTRACTID "@mozilla.org/embedcomp/rangefind;1"
    1.77 +
    1.78 +nsTypeAheadFind::nsTypeAheadFind():
    1.79 +  mStartLinksOnlyPref(false),
    1.80 +  mCaretBrowsingOn(false),
    1.81 +  mLastFindLength(0),
    1.82 +  mIsSoundInitialized(false),
    1.83 +  mCaseSensitive(false)
    1.84 +{
    1.85 +}
    1.86 +
    1.87 +nsTypeAheadFind::~nsTypeAheadFind()
    1.88 +{
    1.89 +  nsCOMPtr<nsIPrefBranch> prefInternal(do_GetService(NS_PREFSERVICE_CONTRACTID));
    1.90 +  if (prefInternal) {
    1.91 +    prefInternal->RemoveObserver("accessibility.typeaheadfind", this);
    1.92 +    prefInternal->RemoveObserver("accessibility.browsewithcaret", this);
    1.93 +  }
    1.94 +}
    1.95 +
    1.96 +nsresult
    1.97 +nsTypeAheadFind::Init(nsIDocShell* aDocShell)
    1.98 +{
    1.99 +  nsCOMPtr<nsIPrefBranch> prefInternal(do_GetService(NS_PREFSERVICE_CONTRACTID));
   1.100 +
   1.101 +  mSearchRange = nullptr;
   1.102 +  mStartPointRange = nullptr;
   1.103 +  mEndPointRange = nullptr;
   1.104 +  if (!prefInternal || !EnsureFind())
   1.105 +    return NS_ERROR_FAILURE;
   1.106 +
   1.107 +  SetDocShell(aDocShell);
   1.108 +
   1.109 +  // ----------- Listen to prefs ------------------
   1.110 +  nsresult rv = prefInternal->AddObserver("accessibility.browsewithcaret", this, true);
   1.111 +  NS_ENSURE_SUCCESS(rv, rv);
   1.112 +
   1.113 +  // ----------- Get initial preferences ----------
   1.114 +  PrefsReset();
   1.115 +
   1.116 +  return rv;
   1.117 +}
   1.118 +
   1.119 +nsresult
   1.120 +nsTypeAheadFind::PrefsReset()
   1.121 +{
   1.122 +  nsCOMPtr<nsIPrefBranch> prefBranch(do_GetService(NS_PREFSERVICE_CONTRACTID));
   1.123 +  NS_ENSURE_TRUE(prefBranch, NS_ERROR_FAILURE);
   1.124 +
   1.125 +  prefBranch->GetBoolPref("accessibility.typeaheadfind.startlinksonly",
   1.126 +                          &mStartLinksOnlyPref);
   1.127 +
   1.128 +  bool isSoundEnabled = true;
   1.129 +  prefBranch->GetBoolPref("accessibility.typeaheadfind.enablesound",
   1.130 +                           &isSoundEnabled);
   1.131 +  nsXPIDLCString soundStr;
   1.132 +  if (isSoundEnabled)
   1.133 +    prefBranch->GetCharPref("accessibility.typeaheadfind.soundURL", getter_Copies(soundStr));
   1.134 +
   1.135 +  mNotFoundSoundURL = soundStr;
   1.136 +
   1.137 +  prefBranch->GetBoolPref("accessibility.browsewithcaret",
   1.138 +                          &mCaretBrowsingOn);
   1.139 +
   1.140 +  return NS_OK;
   1.141 +}
   1.142 +
   1.143 +NS_IMETHODIMP
   1.144 +nsTypeAheadFind::SetCaseSensitive(bool isCaseSensitive)
   1.145 +{
   1.146 +  mCaseSensitive = isCaseSensitive;
   1.147 +
   1.148 +  if (mFind) {
   1.149 +    mFind->SetCaseSensitive(mCaseSensitive);
   1.150 +  }
   1.151 +
   1.152 +  return NS_OK;
   1.153 +}
   1.154 +
   1.155 +NS_IMETHODIMP
   1.156 +nsTypeAheadFind::GetCaseSensitive(bool* isCaseSensitive)
   1.157 +{
   1.158 +  *isCaseSensitive = mCaseSensitive;
   1.159 +
   1.160 +  return NS_OK;
   1.161 +}
   1.162 +
   1.163 +NS_IMETHODIMP
   1.164 +nsTypeAheadFind::SetDocShell(nsIDocShell* aDocShell)
   1.165 +{
   1.166 +  mDocShell = do_GetWeakReference(aDocShell);
   1.167 +
   1.168 +  mWebBrowserFind = do_GetInterface(aDocShell);
   1.169 +  NS_ENSURE_TRUE(mWebBrowserFind, NS_ERROR_FAILURE);
   1.170 +
   1.171 +  nsCOMPtr<nsIPresShell> presShell;
   1.172 +  presShell = aDocShell->GetPresShell();
   1.173 +  mPresShell = do_GetWeakReference(presShell);
   1.174 +
   1.175 +  mStartFindRange = nullptr;
   1.176 +  mStartPointRange = nullptr;
   1.177 +  mSearchRange = nullptr;
   1.178 +  mEndPointRange = nullptr;
   1.179 +
   1.180 +  mFoundLink = nullptr;
   1.181 +  mFoundEditable = nullptr;
   1.182 +  mCurrentWindow = nullptr;
   1.183 +
   1.184 +  mSelectionController = nullptr;
   1.185 +
   1.186 +  mFind = nullptr;
   1.187 +
   1.188 +  return NS_OK;
   1.189 +}
   1.190 +
   1.191 +NS_IMETHODIMP
   1.192 +nsTypeAheadFind::SetSelectionModeAndRepaint(int16_t aToggle)
   1.193 +{
   1.194 +  nsCOMPtr<nsISelectionController> selectionController = 
   1.195 +    do_QueryReferent(mSelectionController);
   1.196 +  if (!selectionController) {
   1.197 +    return NS_OK;
   1.198 +  }
   1.199 +
   1.200 +  selectionController->SetDisplaySelection(aToggle);
   1.201 +  selectionController->RepaintSelection(nsISelectionController::SELECTION_NORMAL);
   1.202 +
   1.203 +  return NS_OK;
   1.204 +}
   1.205 +
   1.206 +NS_IMETHODIMP
   1.207 +nsTypeAheadFind::CollapseSelection()
   1.208 +{
   1.209 +  nsCOMPtr<nsISelectionController> selectionController = 
   1.210 +    do_QueryReferent(mSelectionController);
   1.211 +  if (!selectionController) {
   1.212 +    return NS_OK;
   1.213 +  }
   1.214 +
   1.215 +  nsCOMPtr<nsISelection> selection;
   1.216 +  selectionController->GetSelection(nsISelectionController::SELECTION_NORMAL,
   1.217 +                                     getter_AddRefs(selection));
   1.218 +  if (selection)
   1.219 +    selection->CollapseToStart();
   1.220 +
   1.221 +  return NS_OK;
   1.222 +}
   1.223 +
   1.224 +NS_IMETHODIMP
   1.225 +nsTypeAheadFind::Observe(nsISupports *aSubject, const char *aTopic,
   1.226 +                         const char16_t *aData)
   1.227 +{
   1.228 +  if (!nsCRT::strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID))
   1.229 +    return PrefsReset();
   1.230 +
   1.231 +  return NS_OK;
   1.232 +}
   1.233 +
   1.234 +void
   1.235 +nsTypeAheadFind::SaveFind()
   1.236 +{
   1.237 +  if (mWebBrowserFind)
   1.238 +    mWebBrowserFind->SetSearchString(mTypeAheadBuffer.get());
   1.239 +  
   1.240 +  // save the length of this find for "not found" sound
   1.241 +  mLastFindLength = mTypeAheadBuffer.Length();
   1.242 +}
   1.243 +
   1.244 +void
   1.245 +nsTypeAheadFind::PlayNotFoundSound()
   1.246 +{
   1.247 +  if (mNotFoundSoundURL.IsEmpty())    // no sound
   1.248 +    return;
   1.249 +
   1.250 +  if (!mSoundInterface)
   1.251 +    mSoundInterface = do_CreateInstance("@mozilla.org/sound;1");
   1.252 +
   1.253 +  if (mSoundInterface) {
   1.254 +    mIsSoundInitialized = true;
   1.255 +
   1.256 +    if (mNotFoundSoundURL.Equals("beep")) {
   1.257 +      mSoundInterface->Beep();
   1.258 +      return;
   1.259 +    }
   1.260 +
   1.261 +    nsCOMPtr<nsIURI> soundURI;
   1.262 +    if (mNotFoundSoundURL.Equals("default"))
   1.263 +      NS_NewURI(getter_AddRefs(soundURI), NS_LITERAL_CSTRING(TYPEAHEADFIND_NOTFOUND_WAV_URL));
   1.264 +    else
   1.265 +      NS_NewURI(getter_AddRefs(soundURI), mNotFoundSoundURL);
   1.266 +
   1.267 +    nsCOMPtr<nsIURL> soundURL(do_QueryInterface(soundURI));
   1.268 +    if (soundURL)
   1.269 +      mSoundInterface->Play(soundURL);
   1.270 +  }
   1.271 +}
   1.272 +
   1.273 +nsresult
   1.274 +nsTypeAheadFind::FindItNow(nsIPresShell *aPresShell, bool aIsLinksOnly,
   1.275 +                           bool aIsFirstVisiblePreferred, bool aFindPrev,
   1.276 +                           uint16_t* aResult)
   1.277 +{
   1.278 +  *aResult = FIND_NOTFOUND;
   1.279 +  mFoundLink = nullptr;
   1.280 +  mFoundEditable = nullptr;
   1.281 +  mCurrentWindow = nullptr;
   1.282 +  nsCOMPtr<nsIPresShell> startingPresShell (GetPresShell());
   1.283 +  if (!startingPresShell) {    
   1.284 +    nsCOMPtr<nsIDocShell> ds = do_QueryReferent(mDocShell);
   1.285 +    NS_ENSURE_TRUE(ds, NS_ERROR_FAILURE);
   1.286 +
   1.287 +    startingPresShell = ds->GetPresShell();
   1.288 +    mPresShell = do_GetWeakReference(startingPresShell);    
   1.289 +  }  
   1.290 +
   1.291 +  nsCOMPtr<nsIPresShell> presShell(aPresShell);
   1.292 +
   1.293 +  if (!presShell) {
   1.294 +    presShell = startingPresShell;  // this is the current document
   1.295 +
   1.296 +    if (!presShell)
   1.297 +      return NS_ERROR_FAILURE;
   1.298 +  }
   1.299 +
   1.300 +  nsRefPtr<nsPresContext> presContext = presShell->GetPresContext();
   1.301 +
   1.302 +  if (!presContext)
   1.303 +    return NS_ERROR_FAILURE;
   1.304 +
   1.305 +  nsCOMPtr<nsISelection> selection;
   1.306 +  nsCOMPtr<nsISelectionController> selectionController = 
   1.307 +    do_QueryReferent(mSelectionController);
   1.308 +  if (!selectionController) {
   1.309 +    GetSelection(presShell, getter_AddRefs(selectionController),
   1.310 +                 getter_AddRefs(selection)); // cache for reuse
   1.311 +    mSelectionController = do_GetWeakReference(selectionController);
   1.312 +  } else {
   1.313 +    selectionController->GetSelection(
   1.314 +      nsISelectionController::SELECTION_NORMAL, getter_AddRefs(selection));
   1.315 +  }
   1.316 + 
   1.317 +  nsCOMPtr<nsIDocShell> startingDocShell(presContext->GetDocShell());
   1.318 +  NS_ASSERTION(startingDocShell, "Bug 175321 Crashes with Type Ahead Find [@ nsTypeAheadFind::FindItNow]");
   1.319 +  if (!startingDocShell)
   1.320 +    return NS_ERROR_FAILURE;
   1.321 +
   1.322 +  nsCOMPtr<nsIDocShellTreeItem> rootContentTreeItem;
   1.323 +  nsCOMPtr<nsIDocShell> currentDocShell;
   1.324 +
   1.325 +  startingDocShell->GetSameTypeRootTreeItem(getter_AddRefs(rootContentTreeItem));
   1.326 +  nsCOMPtr<nsIDocShell> rootContentDocShell =
   1.327 +    do_QueryInterface(rootContentTreeItem);
   1.328 +
   1.329 +  if (!rootContentDocShell)
   1.330 +    return NS_ERROR_FAILURE;
   1.331 +
   1.332 +  nsCOMPtr<nsISimpleEnumerator> docShellEnumerator;
   1.333 +  rootContentDocShell->GetDocShellEnumerator(nsIDocShellTreeItem::typeContent,
   1.334 +                                             nsIDocShell::ENUMERATE_FORWARDS,
   1.335 +                                             getter_AddRefs(docShellEnumerator));
   1.336 +
   1.337 +  // Default: can start at the current document
   1.338 +  nsCOMPtr<nsISupports> currentContainer =
   1.339 +    do_QueryInterface(rootContentDocShell);
   1.340 +
   1.341 +  // Iterate up to current shell, if there's more than 1 that we're
   1.342 +  // dealing with
   1.343 +  bool hasMoreDocShells;
   1.344 +
   1.345 +  while (NS_SUCCEEDED(docShellEnumerator->HasMoreElements(&hasMoreDocShells)) && hasMoreDocShells) {
   1.346 +    docShellEnumerator->GetNext(getter_AddRefs(currentContainer));
   1.347 +    currentDocShell = do_QueryInterface(currentContainer);
   1.348 +    if (!currentDocShell || currentDocShell == startingDocShell || aIsFirstVisiblePreferred)
   1.349 +      break;    
   1.350 +  }
   1.351 +
   1.352 +  // ------------ Get ranges ready ----------------
   1.353 +  nsCOMPtr<nsIDOMRange> returnRange;
   1.354 +  nsCOMPtr<nsIPresShell> focusedPS;
   1.355 +  if (NS_FAILED(GetSearchContainers(currentContainer,
   1.356 +                                    (!aIsFirstVisiblePreferred ||
   1.357 +                                     mStartFindRange) ?
   1.358 +                                    selectionController.get() : nullptr,
   1.359 +                                    aIsFirstVisiblePreferred,  aFindPrev,
   1.360 +                                    getter_AddRefs(presShell),
   1.361 +                                    getter_AddRefs(presContext)))) {
   1.362 +    return NS_ERROR_FAILURE;
   1.363 +  }
   1.364 +
   1.365 +  int16_t rangeCompareResult = 0;
   1.366 +  if (!mStartPointRange) {
   1.367 +    mStartPointRange = new nsRange(presShell->GetDocument());
   1.368 +  }
   1.369 +
   1.370 +  mStartPointRange->CompareBoundaryPoints(nsIDOMRange::START_TO_START, mSearchRange, &rangeCompareResult);
   1.371 +  // No need to wrap find in doc if starting at beginning
   1.372 +  bool hasWrapped = (rangeCompareResult < 0);
   1.373 +
   1.374 +  if (mTypeAheadBuffer.IsEmpty() || !EnsureFind())
   1.375 +    return NS_ERROR_FAILURE;
   1.376 +
   1.377 +  mFind->SetFindBackwards(aFindPrev);
   1.378 +
   1.379 +  while (true) {    // ----- Outer while loop: go through all docs -----
   1.380 +    while (true) {  // === Inner while loop: go through a single doc ===
   1.381 +      mFind->Find(mTypeAheadBuffer.get(), mSearchRange, mStartPointRange,
   1.382 +                  mEndPointRange, getter_AddRefs(returnRange));
   1.383 +      
   1.384 +      if (!returnRange)
   1.385 +        break;  // Nothing found in this doc, go to outer loop (try next doc)
   1.386 +
   1.387 +      // ------- Test resulting found range for success conditions ------
   1.388 +      bool isInsideLink = false, isStartingLink = false;
   1.389 +
   1.390 +      if (aIsLinksOnly) {
   1.391 +        // Don't check if inside link when searching all text
   1.392 +        RangeStartsInsideLink(returnRange, presShell, &isInsideLink,
   1.393 +                              &isStartingLink);
   1.394 +      }
   1.395 +
   1.396 +      bool usesIndependentSelection;
   1.397 +      if (!IsRangeVisible(presShell, presContext, returnRange,
   1.398 +                          aIsFirstVisiblePreferred, false,
   1.399 +                          getter_AddRefs(mStartPointRange), 
   1.400 +                          &usesIndependentSelection) ||
   1.401 +          (aIsLinksOnly && !isInsideLink) ||
   1.402 +          (mStartLinksOnlyPref && aIsLinksOnly && !isStartingLink)) {
   1.403 +        // ------ Failure ------
   1.404 +        // At this point mStartPointRange got updated to the first
   1.405 +        // visible range in the viewport.  We _may_ be able to just
   1.406 +        // start there, if it's not taking us in the wrong direction.
   1.407 +        if (aFindPrev) {
   1.408 +          // We can continue at the end of mStartPointRange if its end is before
   1.409 +          // the start of returnRange or coincides with it.  Otherwise, we need
   1.410 +          // to continue at the start of returnRange.
   1.411 +          int16_t compareResult;
   1.412 +          nsresult rv =
   1.413 +            mStartPointRange->CompareBoundaryPoints(nsIDOMRange::START_TO_END,
   1.414 +                                                    returnRange, &compareResult);
   1.415 +          if (NS_SUCCEEDED(rv) && compareResult <= 0) {
   1.416 +            // OK to start at the end of mStartPointRange
   1.417 +            mStartPointRange->Collapse(false);
   1.418 +          } else {
   1.419 +            // Start at the beginning of returnRange
   1.420 +            returnRange->CloneRange(getter_AddRefs(mStartPointRange));
   1.421 +            mStartPointRange->Collapse(true);
   1.422 +          }
   1.423 +        } else {
   1.424 +          // We can continue at the start of mStartPointRange if its start is
   1.425 +          // after the end of returnRange or coincides with it.  Otherwise, we
   1.426 +          // need to continue at the end of returnRange.
   1.427 +          int16_t compareResult;
   1.428 +          nsresult rv =
   1.429 +            mStartPointRange->CompareBoundaryPoints(nsIDOMRange::END_TO_START,
   1.430 +                                                    returnRange, &compareResult);
   1.431 +          if (NS_SUCCEEDED(rv) && compareResult >= 0) {
   1.432 +            // OK to start at the start of mStartPointRange
   1.433 +            mStartPointRange->Collapse(true);
   1.434 +          } else {
   1.435 +            // Start at the end of returnRange
   1.436 +            returnRange->CloneRange(getter_AddRefs(mStartPointRange));
   1.437 +            mStartPointRange->Collapse(false);
   1.438 +          }
   1.439 +        }
   1.440 +        continue;
   1.441 +      }
   1.442 +
   1.443 +      // ------ Success! -------
   1.444 +      // Hide old selection (new one may be on a different controller)
   1.445 +      if (selection) {
   1.446 +        selection->CollapseToStart();
   1.447 +        SetSelectionModeAndRepaint(nsISelectionController::SELECTION_ON);
   1.448 +      }
   1.449 +
   1.450 +      // Make sure new document is selected
   1.451 +      if (presShell != startingPresShell) {
   1.452 +        // We are in a new document (because of frames/iframes)
   1.453 +        mPresShell = do_GetWeakReference(presShell);
   1.454 +      }
   1.455 +
   1.456 +      nsCOMPtr<nsIDocument> document =
   1.457 +        do_QueryInterface(presShell->GetDocument());
   1.458 +      NS_ASSERTION(document, "Wow, presShell doesn't have document!");
   1.459 +      if (!document)
   1.460 +        return NS_ERROR_UNEXPECTED;
   1.461 +
   1.462 +      nsCOMPtr<nsPIDOMWindow> window = document->GetWindow();
   1.463 +      NS_ASSERTION(window, "document has no window");
   1.464 +      if (!window)
   1.465 +        return NS_ERROR_UNEXPECTED;
   1.466 +
   1.467 +      nsCOMPtr<nsIFocusManager> fm = do_GetService(FOCUSMANAGER_CONTRACTID);
   1.468 +      if (usesIndependentSelection) {
   1.469 +        /* If a search result is found inside an editable element, we'll focus
   1.470 +         * the element only if focus is in our content window, i.e.
   1.471 +         * |if (focusedWindow.top == ourWindow.top)| */
   1.472 +        bool shouldFocusEditableElement = false;
   1.473 +        if (fm) {
   1.474 +          nsCOMPtr<nsIDOMWindow> focusedWindow;
   1.475 +          nsresult rv = fm->GetFocusedWindow(getter_AddRefs(focusedWindow));
   1.476 +          if (NS_SUCCEEDED(rv)) {
   1.477 +            nsCOMPtr<nsPIDOMWindow> fwPI(do_QueryInterface(focusedWindow, &rv));
   1.478 +            if (NS_SUCCEEDED(rv)) {
   1.479 +              nsCOMPtr<nsIDocShellTreeItem> fwTreeItem
   1.480 +                (do_QueryInterface(fwPI->GetDocShell(), &rv));
   1.481 +              if (NS_SUCCEEDED(rv)) {
   1.482 +                nsCOMPtr<nsIDocShellTreeItem> fwRootTreeItem;
   1.483 +                rv = fwTreeItem->GetSameTypeRootTreeItem(getter_AddRefs(fwRootTreeItem));
   1.484 +                if (NS_SUCCEEDED(rv) && fwRootTreeItem == rootContentTreeItem)
   1.485 +                  shouldFocusEditableElement = true;
   1.486 +              }
   1.487 +            }
   1.488 +          }
   1.489 +        }
   1.490 +
   1.491 +        // We may be inside an editable element, and therefore the selection
   1.492 +        // may be controlled by a different selection controller.  Walk up the
   1.493 +        // chain of parent nodes to see if we find one.
   1.494 +        nsCOMPtr<nsIDOMNode> node;
   1.495 +        returnRange->GetStartContainer(getter_AddRefs(node));
   1.496 +        while (node) {
   1.497 +          nsCOMPtr<nsIDOMNSEditableElement> editable = do_QueryInterface(node);
   1.498 +          if (editable) {
   1.499 +            // Inside an editable element.  Get the correct selection 
   1.500 +            // controller and selection.
   1.501 +            nsCOMPtr<nsIEditor> editor;
   1.502 +            editable->GetEditor(getter_AddRefs(editor));
   1.503 +            NS_ASSERTION(editor, "Editable element has no editor!");
   1.504 +            if (!editor) {
   1.505 +              break;
   1.506 +            }
   1.507 +            editor->GetSelectionController(
   1.508 +              getter_AddRefs(selectionController));
   1.509 +            if (selectionController) {
   1.510 +              selectionController->GetSelection(
   1.511 +                nsISelectionController::SELECTION_NORMAL, 
   1.512 +                getter_AddRefs(selection));
   1.513 +            }
   1.514 +            mFoundEditable = do_QueryInterface(node);
   1.515 +
   1.516 +            if (!shouldFocusEditableElement)
   1.517 +              break;
   1.518 +
   1.519 +            // Otherwise move focus/caret to editable element
   1.520 +            if (fm)
   1.521 +              fm->SetFocus(mFoundEditable, 0);
   1.522 +            break;
   1.523 +          }
   1.524 +          nsIDOMNode* tmp = node;
   1.525 +          tmp->GetParentNode(getter_AddRefs(node));
   1.526 +        }
   1.527 +
   1.528 +        // If we reach here without setting mFoundEditable, then something
   1.529 +        // besides editable elements can cause us to have an independent
   1.530 +        // selection controller.  I don't know whether this is possible.
   1.531 +        // Currently, we simply fall back to grabbing the document's selection
   1.532 +        // controller in this case.  Perhaps we should reject this find match
   1.533 +        // and search again.
   1.534 +        NS_ASSERTION(mFoundEditable, "Independent selection controller on "
   1.535 +                     "non-editable element!");
   1.536 +      }
   1.537 +
   1.538 +      if (!mFoundEditable) {
   1.539 +        // Not using a separate selection controller, so just get the
   1.540 +        // document's controller and selection.
   1.541 +        GetSelection(presShell, getter_AddRefs(selectionController), 
   1.542 +                     getter_AddRefs(selection));
   1.543 +      }
   1.544 +      mSelectionController = do_GetWeakReference(selectionController);
   1.545 +
   1.546 +      // Select the found text
   1.547 +      if (selection) {
   1.548 +        selection->RemoveAllRanges();
   1.549 +        selection->AddRange(returnRange);
   1.550 +      }
   1.551 +
   1.552 +      if (!mFoundEditable && fm) {
   1.553 +        nsCOMPtr<nsIDOMWindow> win = do_QueryInterface(window);
   1.554 +        fm->MoveFocus(win, nullptr, nsIFocusManager::MOVEFOCUS_CARET,
   1.555 +                      nsIFocusManager::FLAG_NOSCROLL | nsIFocusManager::FLAG_NOSWITCHFRAME,
   1.556 +                      getter_AddRefs(mFoundLink));
   1.557 +      }
   1.558 +
   1.559 +      // Change selection color to ATTENTION and scroll to it.  Careful: we
   1.560 +      // must wait until after we goof with focus above before changing to
   1.561 +      // ATTENTION, or when we MoveFocus() and the selection is not on a
   1.562 +      // link, we'll blur, which will lose the ATTENTION.
   1.563 +      if (selectionController) {
   1.564 +        // Beware! This may flush notifications via synchronous
   1.565 +        // ScrollSelectionIntoView.
   1.566 +        SetSelectionModeAndRepaint(nsISelectionController::SELECTION_ATTENTION);
   1.567 +        selectionController->ScrollSelectionIntoView(
   1.568 +          nsISelectionController::SELECTION_NORMAL, 
   1.569 +          nsISelectionController::SELECTION_WHOLE_SELECTION,
   1.570 +          nsISelectionController::SCROLL_CENTER_VERTICALLY |
   1.571 +          nsISelectionController::SCROLL_SYNCHRONOUS);
   1.572 +      }
   1.573 +
   1.574 +      mCurrentWindow = window;
   1.575 +      *aResult = hasWrapped ? FIND_WRAPPED : FIND_FOUND;
   1.576 +      return NS_OK;
   1.577 +    }
   1.578 +
   1.579 +    // ======= end-inner-while (go through a single document) ==========
   1.580 +
   1.581 +    // ---------- Nothing found yet, try next document  -------------
   1.582 +    bool hasTriedFirstDoc = false;
   1.583 +    do {
   1.584 +      // ==== Second inner loop - get another while  ====
   1.585 +      if (NS_SUCCEEDED(docShellEnumerator->HasMoreElements(&hasMoreDocShells))
   1.586 +          && hasMoreDocShells) {
   1.587 +        docShellEnumerator->GetNext(getter_AddRefs(currentContainer));
   1.588 +        NS_ASSERTION(currentContainer, "HasMoreElements lied to us!");
   1.589 +        currentDocShell = do_QueryInterface(currentContainer);
   1.590 +
   1.591 +        if (currentDocShell)
   1.592 +          break;
   1.593 +      }
   1.594 +      else if (hasTriedFirstDoc)  // Avoid potential infinite loop
   1.595 +        return NS_ERROR_FAILURE;  // No content doc shells
   1.596 +
   1.597 +      // Reached last doc shell, loop around back to first doc shell
   1.598 +      rootContentDocShell->GetDocShellEnumerator(nsIDocShellTreeItem::typeContent,
   1.599 +                                                 nsIDocShell::ENUMERATE_FORWARDS,
   1.600 +                                                 getter_AddRefs(docShellEnumerator));
   1.601 +      hasTriedFirstDoc = true;      
   1.602 +    } while (docShellEnumerator);  // ==== end second inner while  ===
   1.603 +
   1.604 +    bool continueLoop = false;
   1.605 +    if (currentDocShell != startingDocShell)
   1.606 +      continueLoop = true;  // Try next document
   1.607 +    else if (!hasWrapped || aIsFirstVisiblePreferred) {
   1.608 +      // Finished searching through docshells:
   1.609 +      // If aFirstVisiblePreferred == true, we may need to go through all
   1.610 +      // docshells twice -once to look for visible matches, the second time
   1.611 +      // for any match
   1.612 +      aIsFirstVisiblePreferred = false;
   1.613 +      hasWrapped = true;
   1.614 +      continueLoop = true; // Go through all docs again
   1.615 +    }
   1.616 +
   1.617 +    if (continueLoop) {
   1.618 +      if (NS_FAILED(GetSearchContainers(currentContainer, nullptr,
   1.619 +                                        aIsFirstVisiblePreferred, aFindPrev,
   1.620 +                                        getter_AddRefs(presShell),
   1.621 +                                        getter_AddRefs(presContext)))) {
   1.622 +        continue;
   1.623 +      }
   1.624 +
   1.625 +      if (aFindPrev) {
   1.626 +        // Reverse mode: swap start and end points, so that we start
   1.627 +        // at end of document and go to beginning
   1.628 +        nsCOMPtr<nsIDOMRange> tempRange;
   1.629 +        mStartPointRange->CloneRange(getter_AddRefs(tempRange));
   1.630 +        if (!mEndPointRange) {
   1.631 +          mEndPointRange = new nsRange(presShell->GetDocument());
   1.632 +        }
   1.633 +
   1.634 +        mStartPointRange = mEndPointRange;
   1.635 +        mEndPointRange = tempRange;
   1.636 +      }
   1.637 +
   1.638 +      continue;
   1.639 +    }
   1.640 +
   1.641 +    // ------------- Failed --------------
   1.642 +    break;
   1.643 +  }   // end-outer-while: go through all docs
   1.644 +
   1.645 +  return NS_ERROR_FAILURE;
   1.646 +}
   1.647 +
   1.648 +NS_IMETHODIMP
   1.649 +nsTypeAheadFind::GetSearchString(nsAString& aSearchString)
   1.650 +{
   1.651 +  aSearchString = mTypeAheadBuffer;
   1.652 +  return NS_OK;
   1.653 +}
   1.654 +
   1.655 +NS_IMETHODIMP
   1.656 +nsTypeAheadFind::GetFoundLink(nsIDOMElement** aFoundLink)
   1.657 +{
   1.658 +  NS_ENSURE_ARG_POINTER(aFoundLink);
   1.659 +  *aFoundLink = mFoundLink;
   1.660 +  NS_IF_ADDREF(*aFoundLink);
   1.661 +  return NS_OK;
   1.662 +}
   1.663 +
   1.664 +NS_IMETHODIMP
   1.665 +nsTypeAheadFind::GetFoundEditable(nsIDOMElement** aFoundEditable)
   1.666 +{
   1.667 +  NS_ENSURE_ARG_POINTER(aFoundEditable);
   1.668 +  *aFoundEditable = mFoundEditable;
   1.669 +  NS_IF_ADDREF(*aFoundEditable);
   1.670 +  return NS_OK;
   1.671 +}
   1.672 +
   1.673 +NS_IMETHODIMP
   1.674 +nsTypeAheadFind::GetCurrentWindow(nsIDOMWindow** aCurrentWindow)
   1.675 +{
   1.676 +  NS_ENSURE_ARG_POINTER(aCurrentWindow);
   1.677 +  *aCurrentWindow = mCurrentWindow;
   1.678 +  NS_IF_ADDREF(*aCurrentWindow);
   1.679 +  return NS_OK;
   1.680 +}
   1.681 +
   1.682 +nsresult
   1.683 +nsTypeAheadFind::GetSearchContainers(nsISupports *aContainer,
   1.684 +                                     nsISelectionController *aSelectionController,
   1.685 +                                     bool aIsFirstVisiblePreferred,
   1.686 +                                     bool aFindPrev,
   1.687 +                                     nsIPresShell **aPresShell,
   1.688 +                                     nsPresContext **aPresContext)
   1.689 +{
   1.690 +  NS_ENSURE_ARG_POINTER(aContainer);
   1.691 +  NS_ENSURE_ARG_POINTER(aPresShell);
   1.692 +  NS_ENSURE_ARG_POINTER(aPresContext);
   1.693 +
   1.694 +  *aPresShell = nullptr;
   1.695 +  *aPresContext = nullptr;
   1.696 +
   1.697 +  nsCOMPtr<nsIDocShell> docShell(do_QueryInterface(aContainer));
   1.698 +  if (!docShell)
   1.699 +    return NS_ERROR_FAILURE;
   1.700 +
   1.701 +  nsCOMPtr<nsIPresShell> presShell = docShell->GetPresShell();
   1.702 +
   1.703 +  nsRefPtr<nsPresContext> presContext;
   1.704 +  docShell->GetPresContext(getter_AddRefs(presContext));
   1.705 +
   1.706 +  if (!presShell || !presContext)
   1.707 +    return NS_ERROR_FAILURE;
   1.708 +
   1.709 +  nsIDocument* doc = presShell->GetDocument();
   1.710 +
   1.711 +  if (!doc)
   1.712 +    return NS_ERROR_FAILURE;
   1.713 +
   1.714 +  nsCOMPtr<nsIContent> rootContent;
   1.715 +  nsCOMPtr<nsIDOMHTMLDocument> htmlDoc(do_QueryInterface(doc));
   1.716 +  if (htmlDoc) {
   1.717 +    nsCOMPtr<nsIDOMHTMLElement> bodyEl;
   1.718 +    htmlDoc->GetBody(getter_AddRefs(bodyEl));
   1.719 +    rootContent = do_QueryInterface(bodyEl);
   1.720 +  }
   1.721 +
   1.722 +  if (!rootContent)
   1.723 +    rootContent = doc->GetRootElement();
   1.724 + 
   1.725 +  nsCOMPtr<nsIDOMNode> rootNode(do_QueryInterface(rootContent));
   1.726 +
   1.727 +  if (!rootNode)
   1.728 +    return NS_ERROR_FAILURE;
   1.729 +
   1.730 +  uint32_t childCount = rootContent->GetChildCount();
   1.731 +
   1.732 +  if (!mSearchRange) {
   1.733 +    mSearchRange = new nsRange(rootContent);
   1.734 +  }
   1.735 +
   1.736 +  if (!mEndPointRange) {
   1.737 +    mEndPointRange = new nsRange(rootContent);
   1.738 +  }
   1.739 +
   1.740 +  mSearchRange->SelectNodeContents(rootNode);
   1.741 +
   1.742 +  mEndPointRange->SetEnd(rootNode, childCount);
   1.743 +  mEndPointRange->Collapse(false); // collapse to end
   1.744 +
   1.745 +  // Consider current selection as null if
   1.746 +  // it's not in the currently focused document
   1.747 +  nsCOMPtr<nsIDOMRange> currentSelectionRange;
   1.748 +  nsCOMPtr<nsIPresShell> selectionPresShell = GetPresShell();
   1.749 +  if (aSelectionController && selectionPresShell && selectionPresShell == presShell) {
   1.750 +    nsCOMPtr<nsISelection> selection;
   1.751 +    aSelectionController->GetSelection(
   1.752 +      nsISelectionController::SELECTION_NORMAL, getter_AddRefs(selection));
   1.753 +    if (selection)
   1.754 +      selection->GetRangeAt(0, getter_AddRefs(currentSelectionRange));
   1.755 +  }
   1.756 +
   1.757 +  if (!mStartPointRange) {
   1.758 +    mStartPointRange = new nsRange(doc);
   1.759 +  }
   1.760 +
   1.761 +  if (!currentSelectionRange) {
   1.762 +    // Ensure visible range, move forward if necessary
   1.763 +    // This uses ignores the return value, but usese the side effect of
   1.764 +    // IsRangeVisible. It returns the first visible range after searchRange
   1.765 +    IsRangeVisible(presShell, presContext, mSearchRange, 
   1.766 +                   aIsFirstVisiblePreferred, true, 
   1.767 +                   getter_AddRefs(mStartPointRange), nullptr);
   1.768 +  }
   1.769 +  else {
   1.770 +    int32_t startOffset;
   1.771 +    nsCOMPtr<nsIDOMNode> startNode;
   1.772 +    if (aFindPrev) {
   1.773 +      currentSelectionRange->GetStartContainer(getter_AddRefs(startNode));
   1.774 +      currentSelectionRange->GetStartOffset(&startOffset);
   1.775 +    } else {
   1.776 +      currentSelectionRange->GetEndContainer(getter_AddRefs(startNode));
   1.777 +      currentSelectionRange->GetEndOffset(&startOffset);
   1.778 +    }
   1.779 +    if (!startNode)
   1.780 +      startNode = rootNode;    
   1.781 +
   1.782 +    // We need to set the start point this way, other methods haven't worked
   1.783 +    mStartPointRange->SelectNode(startNode);
   1.784 +    mStartPointRange->SetStart(startNode, startOffset);
   1.785 +  }
   1.786 +
   1.787 +  mStartPointRange->Collapse(true); // collapse to start
   1.788 +
   1.789 +  *aPresShell = presShell;
   1.790 +  NS_ADDREF(*aPresShell);
   1.791 +
   1.792 +  *aPresContext = presContext;
   1.793 +  NS_ADDREF(*aPresContext);
   1.794 +
   1.795 +  return NS_OK;
   1.796 +}
   1.797 +
   1.798 +void
   1.799 +nsTypeAheadFind::RangeStartsInsideLink(nsIDOMRange *aRange,
   1.800 +                                       nsIPresShell *aPresShell,
   1.801 +                                       bool *aIsInsideLink,
   1.802 +                                       bool *aIsStartingLink)
   1.803 +{
   1.804 +  *aIsInsideLink = false;
   1.805 +  *aIsStartingLink = true;
   1.806 +
   1.807 +  // ------- Get nsIContent to test -------
   1.808 +  nsCOMPtr<nsIDOMNode> startNode;
   1.809 +  nsCOMPtr<nsIContent> startContent, origContent;
   1.810 +  aRange->GetStartContainer(getter_AddRefs(startNode));
   1.811 +  int32_t startOffset;
   1.812 +  aRange->GetStartOffset(&startOffset);
   1.813 +
   1.814 +  startContent = do_QueryInterface(startNode);
   1.815 +  if (!startContent) {
   1.816 +    NS_NOTREACHED("startContent should never be null");
   1.817 +    return;
   1.818 +  }
   1.819 +  origContent = startContent;
   1.820 +
   1.821 +  if (startContent->IsElement()) {
   1.822 +    nsIContent *childContent = startContent->GetChildAt(startOffset);
   1.823 +    if (childContent) {
   1.824 +      startContent = childContent;
   1.825 +    }
   1.826 +  }
   1.827 +  else if (startOffset > 0) {
   1.828 +    const nsTextFragment *textFrag = startContent->GetText();
   1.829 +    if (textFrag) {
   1.830 +      // look for non whitespace character before start offset
   1.831 +      for (int32_t index = 0; index < startOffset; index++) {
   1.832 +        // FIXME: take content language into account when deciding whitespace.
   1.833 +        if (!mozilla::dom::IsSpaceCharacter(textFrag->CharAt(index))) {
   1.834 +          *aIsStartingLink = false;  // not at start of a node
   1.835 +
   1.836 +          break;
   1.837 +        }
   1.838 +      }
   1.839 +    }
   1.840 +  }
   1.841 +
   1.842 +  // ------- Check to see if inside link ---------
   1.843 +
   1.844 +  // We now have the correct start node for the range
   1.845 +  // Search for links, starting with startNode, and going up parent chain
   1.846 +
   1.847 +  nsCOMPtr<nsIAtom> tag, hrefAtom(do_GetAtom("href"));
   1.848 +  nsCOMPtr<nsIAtom> typeAtom(do_GetAtom("type"));
   1.849 +
   1.850 +  while (true) {
   1.851 +    // Keep testing while startContent is equal to something,
   1.852 +    // eventually we'll run out of ancestors
   1.853 +
   1.854 +    if (startContent->IsHTML()) {
   1.855 +      nsCOMPtr<mozilla::dom::Link> link(do_QueryInterface(startContent));
   1.856 +      if (link) {
   1.857 +        // Check to see if inside HTML link
   1.858 +        *aIsInsideLink = startContent->HasAttr(kNameSpaceID_None, hrefAtom);
   1.859 +        return;
   1.860 +      }
   1.861 +    }
   1.862 +    else {
   1.863 +      // Any xml element can be an xlink
   1.864 +      *aIsInsideLink = startContent->HasAttr(kNameSpaceID_XLink, hrefAtom);
   1.865 +      if (*aIsInsideLink) {
   1.866 +        if (!startContent->AttrValueIs(kNameSpaceID_XLink, typeAtom,
   1.867 +                                       NS_LITERAL_STRING("simple"),
   1.868 +                                       eCaseMatters)) {
   1.869 +          *aIsInsideLink = false;  // Xlink must be type="simple"
   1.870 +        }
   1.871 +
   1.872 +        return;
   1.873 +      }
   1.874 +    }
   1.875 +
   1.876 +    // Get the parent
   1.877 +    nsCOMPtr<nsIContent> parent = startContent->GetParent();
   1.878 +    if (!parent)
   1.879 +      break;
   1.880 +
   1.881 +    nsIContent* parentsFirstChild = parent->GetFirstChild();
   1.882 +
   1.883 +    // We don't want to look at a whitespace-only first child
   1.884 +    if (parentsFirstChild && parentsFirstChild->TextIsOnlyWhitespace()) {
   1.885 +      parentsFirstChild = parentsFirstChild->GetNextSibling();
   1.886 +    }
   1.887 +
   1.888 +    if (parentsFirstChild != startContent) {
   1.889 +      // startContent wasn't a first child, so we conclude that
   1.890 +      // if this is inside a link, it's not at the beginning of it
   1.891 +      *aIsStartingLink = false;
   1.892 +    }
   1.893 +
   1.894 +    startContent = parent;
   1.895 +  }
   1.896 +
   1.897 +  *aIsStartingLink = false;
   1.898 +}
   1.899 +
   1.900 +/* Find another match in the page. */
   1.901 +NS_IMETHODIMP
   1.902 +nsTypeAheadFind::FindAgain(bool aFindBackwards, bool aLinksOnly,
   1.903 +                           uint16_t* aResult)
   1.904 +
   1.905 +{
   1.906 +  *aResult = FIND_NOTFOUND;
   1.907 +
   1.908 +  if (!mTypeAheadBuffer.IsEmpty())
   1.909 +    // Beware! This may flush notifications via synchronous
   1.910 +    // ScrollSelectionIntoView.
   1.911 +    FindItNow(nullptr, aLinksOnly, false, aFindBackwards, aResult);
   1.912 +
   1.913 +  return NS_OK;
   1.914 +}
   1.915 +
   1.916 +NS_IMETHODIMP
   1.917 +nsTypeAheadFind::Find(const nsAString& aSearchString, bool aLinksOnly,
   1.918 +                      uint16_t* aResult)
   1.919 +{
   1.920 +  *aResult = FIND_NOTFOUND;
   1.921 +
   1.922 +  nsCOMPtr<nsIPresShell> presShell (GetPresShell());
   1.923 +  if (!presShell) {
   1.924 +    nsCOMPtr<nsIDocShell> ds (do_QueryReferent(mDocShell));
   1.925 +    NS_ENSURE_TRUE(ds, NS_ERROR_FAILURE);
   1.926 +
   1.927 +    presShell = ds->GetPresShell();
   1.928 +    NS_ENSURE_TRUE(presShell, NS_ERROR_FAILURE);
   1.929 +    mPresShell = do_GetWeakReference(presShell);
   1.930 +  }
   1.931 +
   1.932 +  nsCOMPtr<nsISelection> selection;
   1.933 +  nsCOMPtr<nsISelectionController> selectionController =
   1.934 +    do_QueryReferent(mSelectionController);
   1.935 +  if (!selectionController) {
   1.936 +    GetSelection(presShell, getter_AddRefs(selectionController),
   1.937 +                 getter_AddRefs(selection)); // cache for reuse
   1.938 +    mSelectionController = do_GetWeakReference(selectionController);
   1.939 +  } else {
   1.940 +    selectionController->GetSelection(
   1.941 +      nsISelectionController::SELECTION_NORMAL, getter_AddRefs(selection));
   1.942 +  }
   1.943 +
   1.944 +  if (selection)
   1.945 +    selection->CollapseToStart();
   1.946 +
   1.947 +  if (aSearchString.IsEmpty()) {
   1.948 +    mTypeAheadBuffer.Truncate();
   1.949 +
   1.950 +    // These will be initialized to their true values after the first character
   1.951 +    // is typed
   1.952 +    mStartFindRange = nullptr;
   1.953 +    mSelectionController = nullptr;
   1.954 +
   1.955 +    *aResult = FIND_FOUND;
   1.956 +    return NS_OK;
   1.957 +  }
   1.958 +
   1.959 +  bool atEnd = false;    
   1.960 +  if (mTypeAheadBuffer.Length()) {
   1.961 +    const nsAString& oldStr = Substring(mTypeAheadBuffer, 0, mTypeAheadBuffer.Length());
   1.962 +    const nsAString& newStr = Substring(aSearchString, 0, mTypeAheadBuffer.Length());
   1.963 +    if (oldStr.Equals(newStr))
   1.964 +      atEnd = true;
   1.965 +  
   1.966 +    const nsAString& newStr2 = Substring(aSearchString, 0, aSearchString.Length());
   1.967 +    const nsAString& oldStr2 = Substring(mTypeAheadBuffer, 0, aSearchString.Length());
   1.968 +    if (oldStr2.Equals(newStr2))
   1.969 +      atEnd = true;
   1.970 +    
   1.971 +    if (!atEnd)
   1.972 +      mStartFindRange = nullptr;
   1.973 +  }
   1.974 +
   1.975 +  if (!mIsSoundInitialized && !mNotFoundSoundURL.IsEmpty()) {
   1.976 +    // This makes sure system sound library is loaded so that
   1.977 +    // there's no lag before the first sound is played
   1.978 +    // by waiting for the first keystroke, we still get the startup time benefits.
   1.979 +    mIsSoundInitialized = true;
   1.980 +    mSoundInterface = do_CreateInstance("@mozilla.org/sound;1");
   1.981 +    if (mSoundInterface && !mNotFoundSoundURL.Equals(NS_LITERAL_CSTRING("beep"))) {
   1.982 +      mSoundInterface->Init();
   1.983 +    }
   1.984 +  }
   1.985 +
   1.986 +#ifdef XP_WIN
   1.987 +  // After each keystroke, ensure sound object is destroyed, to free up memory 
   1.988 +  // allocated for error sound, otherwise Windows' nsISound impl 
   1.989 +  // holds onto the last played sound, using up memory.
   1.990 +  mSoundInterface = nullptr;
   1.991 +#endif
   1.992 +
   1.993 +  int32_t bufferLength = mTypeAheadBuffer.Length();
   1.994 +
   1.995 +  mTypeAheadBuffer = aSearchString;
   1.996 +
   1.997 +  bool isFirstVisiblePreferred = false;
   1.998 +
   1.999 +  // --------- Initialize find if 1st char ----------
  1.1000 +  if (bufferLength == 0) {
  1.1001 +    // If you can see the selection (not collapsed or thru caret browsing),
  1.1002 +    // or if already focused on a page element, start there.
  1.1003 +    // Otherwise we're going to start at the first visible element
  1.1004 +    bool isSelectionCollapsed = true;
  1.1005 +    if (selection)
  1.1006 +      selection->GetIsCollapsed(&isSelectionCollapsed);
  1.1007 +
  1.1008 +    // If true, we will scan from top left of visible area
  1.1009 +    // If false, we will scan from start of selection
  1.1010 +    isFirstVisiblePreferred = !atEnd && !mCaretBrowsingOn && isSelectionCollapsed;
  1.1011 +    if (isFirstVisiblePreferred) {
  1.1012 +      // Get the focused content. If there is a focused node, ensure the
  1.1013 +      // selection is at that point. Otherwise, we will just want to start
  1.1014 +      // from the caret position or the beginning of the document.
  1.1015 +      nsPresContext* presContext = presShell->GetPresContext();
  1.1016 +      NS_ENSURE_TRUE(presContext, NS_OK);
  1.1017 +
  1.1018 +      nsCOMPtr<nsIDocument> document =
  1.1019 +        do_QueryInterface(presShell->GetDocument());
  1.1020 +      if (!document)
  1.1021 +        return NS_ERROR_UNEXPECTED;
  1.1022 +
  1.1023 +      nsCOMPtr<nsIDOMWindow> window = do_QueryInterface(document->GetWindow());
  1.1024 +
  1.1025 +      nsCOMPtr<nsIFocusManager> fm = do_GetService(FOCUSMANAGER_CONTRACTID);
  1.1026 +      if (fm) {
  1.1027 +        nsCOMPtr<nsIDOMElement> focusedElement;
  1.1028 +        nsCOMPtr<nsIDOMWindow> focusedWindow;
  1.1029 +        fm->GetFocusedElementForWindow(window, false, getter_AddRefs(focusedWindow),
  1.1030 +                                       getter_AddRefs(focusedElement));
  1.1031 +        // If the root element is focused, then it's actually the document
  1.1032 +        // that has the focus, so ignore this.
  1.1033 +        if (focusedElement &&
  1.1034 +            !SameCOMIdentity(focusedElement, document->GetRootElement())) {
  1.1035 +          fm->MoveCaretToFocus(window);
  1.1036 +          isFirstVisiblePreferred = false;
  1.1037 +        }
  1.1038 +      }
  1.1039 +    }
  1.1040 +  }
  1.1041 +
  1.1042 +  // ----------- Find the text! ---------------------
  1.1043 +  // Beware! This may flush notifications via synchronous
  1.1044 +  // ScrollSelectionIntoView.
  1.1045 +  nsresult rv = FindItNow(nullptr, aLinksOnly, isFirstVisiblePreferred,
  1.1046 +                          false, aResult);
  1.1047 +
  1.1048 +  // ---------Handle success or failure ---------------
  1.1049 +  if (NS_SUCCEEDED(rv)) {
  1.1050 +    if (mTypeAheadBuffer.Length() == 1) {
  1.1051 +      // If first letter, store where the first find succeeded
  1.1052 +      // (mStartFindRange)
  1.1053 +
  1.1054 +      mStartFindRange = nullptr;
  1.1055 +      if (selection) {
  1.1056 +        nsCOMPtr<nsIDOMRange> startFindRange;
  1.1057 +        selection->GetRangeAt(0, getter_AddRefs(startFindRange));
  1.1058 +        if (startFindRange)
  1.1059 +          startFindRange->CloneRange(getter_AddRefs(mStartFindRange));
  1.1060 +      }
  1.1061 +    }
  1.1062 +  }
  1.1063 +  else {
  1.1064 +    // Error sound
  1.1065 +    if (mTypeAheadBuffer.Length() > mLastFindLength)
  1.1066 +      PlayNotFoundSound();
  1.1067 +  }
  1.1068 +
  1.1069 +  SaveFind();
  1.1070 +  return NS_OK;
  1.1071 +}
  1.1072 +
  1.1073 +void
  1.1074 +nsTypeAheadFind::GetSelection(nsIPresShell *aPresShell,
  1.1075 +                              nsISelectionController **aSelCon,
  1.1076 +                              nsISelection **aDOMSel)
  1.1077 +{
  1.1078 +  if (!aPresShell)
  1.1079 +    return;
  1.1080 +
  1.1081 +  // if aCurrentNode is nullptr, get selection for document
  1.1082 +  *aDOMSel = nullptr;
  1.1083 +
  1.1084 +  nsPresContext* presContext = aPresShell->GetPresContext();
  1.1085 +
  1.1086 +  nsIFrame *frame = aPresShell->GetRootFrame();
  1.1087 +
  1.1088 +  if (presContext && frame) {
  1.1089 +    frame->GetSelectionController(presContext, aSelCon);
  1.1090 +    if (*aSelCon) {
  1.1091 +      (*aSelCon)->GetSelection(nsISelectionController::SELECTION_NORMAL,
  1.1092 +                               aDOMSel);
  1.1093 +    }
  1.1094 +  }
  1.1095 +}
  1.1096 +
  1.1097 +
  1.1098 +bool
  1.1099 +nsTypeAheadFind::IsRangeVisible(nsIPresShell *aPresShell,
  1.1100 +                                nsPresContext *aPresContext,
  1.1101 +                                nsIDOMRange *aRange, bool aMustBeInViewPort,
  1.1102 +                                bool aGetTopVisibleLeaf,
  1.1103 +                                nsIDOMRange **aFirstVisibleRange,
  1.1104 +                                bool *aUsesIndependentSelection)
  1.1105 +{
  1.1106 +  NS_ASSERTION(aPresShell && aPresContext && aRange && aFirstVisibleRange, 
  1.1107 +               "params are invalid");
  1.1108 +
  1.1109 +  // We need to know if the range start is visible.
  1.1110 +  // Otherwise, return the first visible range start 
  1.1111 +  // in aFirstVisibleRange
  1.1112 +
  1.1113 +  aRange->CloneRange(aFirstVisibleRange);
  1.1114 +  nsCOMPtr<nsIDOMNode> node;
  1.1115 +  aRange->GetStartContainer(getter_AddRefs(node));
  1.1116 +
  1.1117 +  nsCOMPtr<nsIContent> content(do_QueryInterface(node));
  1.1118 +  if (!content)
  1.1119 +    return false;
  1.1120 +
  1.1121 +  nsIFrame *frame = content->GetPrimaryFrame();
  1.1122 +  if (!frame)    
  1.1123 +    return false;  // No frame! Not visible then.
  1.1124 +
  1.1125 +  if (!frame->StyleVisibility()->IsVisible())
  1.1126 +    return false;
  1.1127 +
  1.1128 +  // Detect if we are _inside_ a text control, or something else with its own
  1.1129 +  // selection controller.
  1.1130 +  if (aUsesIndependentSelection) {
  1.1131 +    *aUsesIndependentSelection = 
  1.1132 +      (frame->GetStateBits() & NS_FRAME_INDEPENDENT_SELECTION);
  1.1133 +  }
  1.1134 +
  1.1135 +  // ---- We have a frame ----
  1.1136 +  if (!aMustBeInViewPort)   
  1.1137 +    return true; //  Don't need it to be on screen, just in rendering tree
  1.1138 +
  1.1139 +  // Get the next in flow frame that contains the range start
  1.1140 +  int32_t startRangeOffset, startFrameOffset, endFrameOffset;
  1.1141 +  aRange->GetStartOffset(&startRangeOffset);
  1.1142 +  while (true) {
  1.1143 +    frame->GetOffsets(startFrameOffset, endFrameOffset);
  1.1144 +    if (startRangeOffset < endFrameOffset)
  1.1145 +      break;
  1.1146 +
  1.1147 +    nsIFrame *nextContinuationFrame = frame->GetNextContinuation();
  1.1148 +    if (nextContinuationFrame)
  1.1149 +      frame = nextContinuationFrame;
  1.1150 +    else
  1.1151 +      break;
  1.1152 +  }
  1.1153 +
  1.1154 +  // Set up the variables we need, return true if we can't get at them all
  1.1155 +  const uint16_t kMinPixels  = 12;
  1.1156 +  nscoord minDistance = nsPresContext::CSSPixelsToAppUnits(kMinPixels);
  1.1157 +
  1.1158 +  // Get the bounds of the current frame, relative to the current view.
  1.1159 +  // We don't use the more accurate AccGetBounds, because that is
  1.1160 +  // more expensive and the STATE_OFFSCREEN flag that this is used
  1.1161 +  // for only needs to be a rough indicator
  1.1162 +  nsRectVisibility rectVisibility = nsRectVisibility_kAboveViewport;
  1.1163 +
  1.1164 +  if (!aGetTopVisibleLeaf && !frame->GetRect().IsEmpty()) {
  1.1165 +    rectVisibility =
  1.1166 +      aPresShell->GetRectVisibility(frame,
  1.1167 +                                    nsRect(nsPoint(0,0), frame->GetSize()),
  1.1168 +                                    minDistance);
  1.1169 +
  1.1170 +    if (rectVisibility != nsRectVisibility_kAboveViewport) {
  1.1171 +      return true;
  1.1172 +    }
  1.1173 +  }
  1.1174 +
  1.1175 +  // We know that the target range isn't usable because it's not in the
  1.1176 +  // view port. Move range forward to first visible point,
  1.1177 +  // this speeds us up a lot in long documents
  1.1178 +  nsCOMPtr<nsIFrameEnumerator> frameTraversal;
  1.1179 +  nsCOMPtr<nsIFrameTraversal> trav(do_CreateInstance(kFrameTraversalCID));
  1.1180 +  if (trav)
  1.1181 +    trav->NewFrameTraversal(getter_AddRefs(frameTraversal),
  1.1182 +                            aPresContext, frame,
  1.1183 +                            eLeaf,
  1.1184 +                            false, // aVisual
  1.1185 +                            false, // aLockInScrollView
  1.1186 +                            false     // aFollowOOFs
  1.1187 +                            );
  1.1188 +
  1.1189 +  if (!frameTraversal)
  1.1190 +    return false;
  1.1191 +
  1.1192 +  while (rectVisibility == nsRectVisibility_kAboveViewport) {
  1.1193 +    frameTraversal->Next();
  1.1194 +    frame = frameTraversal->CurrentItem();
  1.1195 +    if (!frame)
  1.1196 +      return false;
  1.1197 +
  1.1198 +    if (!frame->GetRect().IsEmpty()) {
  1.1199 +      rectVisibility =
  1.1200 +        aPresShell->GetRectVisibility(frame,
  1.1201 +                                      nsRect(nsPoint(0,0), frame->GetSize()),
  1.1202 +                                      minDistance);
  1.1203 +    }
  1.1204 +  }
  1.1205 +
  1.1206 +  if (frame) {
  1.1207 +    nsCOMPtr<nsIDOMNode> firstVisibleNode = do_QueryInterface(frame->GetContent());
  1.1208 +
  1.1209 +    if (firstVisibleNode) {
  1.1210 +      frame->GetOffsets(startFrameOffset, endFrameOffset);
  1.1211 +      (*aFirstVisibleRange)->SetStart(firstVisibleNode, startFrameOffset);
  1.1212 +      (*aFirstVisibleRange)->SetEnd(firstVisibleNode, endFrameOffset);
  1.1213 +    }
  1.1214 +  }
  1.1215 +
  1.1216 +  return false;
  1.1217 +}
  1.1218 +
  1.1219 +already_AddRefed<nsIPresShell>
  1.1220 +nsTypeAheadFind::GetPresShell()
  1.1221 +{
  1.1222 +  if (!mPresShell)
  1.1223 +    return nullptr;
  1.1224 +
  1.1225 +  nsCOMPtr<nsIPresShell> shell = do_QueryReferent(mPresShell);
  1.1226 +  if (shell) {
  1.1227 +    nsPresContext *pc = shell->GetPresContext();
  1.1228 +    if (!pc || !pc->GetContainerWeak()) {
  1.1229 +      return nullptr;
  1.1230 +    }
  1.1231 +  }
  1.1232 +
  1.1233 +  return shell.forget();
  1.1234 +}

mercurial