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 +}