From bbbdd59649b3208740ac7f140e48988c4526768a Mon Sep 17 00:00:00 2001 From: "aaronl%netscape.com" Date: Tue, 9 Jul 2002 18:38:41 +0000 Subject: [PATCH] Bug 30088. More checkins to extensions/typeaheadfind. Support for docs with frames and iframes. Not part of build. --- extensions/typeaheadfind/nsTypeAheadFind.cpp | 492 ++++++++++++------- extensions/typeaheadfind/nsTypeAheadFind.h | 13 +- 2 files changed, 318 insertions(+), 187 deletions(-) diff --git a/extensions/typeaheadfind/nsTypeAheadFind.cpp b/extensions/typeaheadfind/nsTypeAheadFind.cpp index d5c5b4893801..67bdeb24031a 100644 --- a/extensions/typeaheadfind/nsTypeAheadFind.cpp +++ b/extensions/typeaheadfind/nsTypeAheadFind.cpp @@ -49,6 +49,7 @@ #include "nsCURILoader.h" #include "nsIDocShell.h" #include "nsIDocShellTreeOwner.h" +#include "nsISimpleEnumerator.h" #include "nsIDOMWindow.h" #include "nsIDOMWindowInternal.h" #include "nsIDOMEventTarget.h" @@ -134,17 +135,18 @@ nsTypeAheadFind::nsTypeAheadFind(): #endif nsCOMPtr prefs(do_GetService(NS_PREF_CONTRACTID)); - if (prefs) { + if (mFind && prefs) { + AddRef(); + // ----------- Listen to prefs prefs->RegisterCallback("accessibility.typeaheadfind", (PrefChangedFunc)nsTypeAheadFind::TypeAheadFindPrefCallback, (void*)this); prefs->RegisterCallback("accessibility.typeaheadfind.linksonly", (PrefChangedFunc)nsTypeAheadFind::TypeAheadFindPrefCallback, (void*)this); prefs->RegisterCallback("accessibility.typeaheadfind.timeout", (PrefChangedFunc)nsTypeAheadFind::TypeAheadFindPrefCallback, (void*)this); prefs->RegisterCallback("accessibility.browsewithcaret", (PrefChangedFunc)nsTypeAheadFind::TypeAheadFindPrefCallback, (void*)this); - AddRef(); + // ----------- Set search options --------------- + mFind->SetFindBackwards(PR_FALSE); + mFind->SetCaseSensitive(PR_FALSE); + mFind->SetWordBreaker(nsnull); } - - nsCOMPtr stringBundleService(do_GetService(kStringBundleServiceCID)); - if (stringBundleService) - stringBundleService->CreateBundle(TYPEAHEADFIND_BUNDLE_URL, getter_AddRefs(mStringBundle)); } @@ -201,8 +203,13 @@ int PR_CALLBACK nsTypeAheadFind::TypeAheadFindPrefCallback(const char* aPrefName typeAheadFind->RemoveCurrentWindowFocusListener(); progress->RemoveProgressListener(NS_STATIC_CAST(nsIWebProgressListener*, typeAheadFind)); } - else if (typeAheadFind->mIsTypeAheadOn != wasTypeAheadOn) - progress->AddProgressListener(NS_STATIC_CAST(nsIWebProgressListener*, typeAheadFind), nsIWebProgress::NOTIFY_STATE_DOCUMENT); + else if (typeAheadFind->mIsTypeAheadOn != wasTypeAheadOn) { + progress->AddProgressListener(NS_STATIC_CAST(nsIWebProgressListener*, typeAheadFind), nsIWebProgress::NOTIFY_STATE_DOCUMENT); + // Initialize string bundle + nsCOMPtr stringBundleService(do_GetService(kStringBundleServiceCID)); + if (stringBundleService) + stringBundleService->CreateBundle(TYPEAHEADFIND_BUNDLE_URL, getter_AddRefs(typeAheadFind->mStringBundle)); + } } } if (!nsCRT::strcasecmp(aPrefName,"accessibility.typeaheadfind.linksonly")) @@ -243,7 +250,7 @@ NS_IMETHODIMP nsTypeAheadFind::OnStateChange(nsIWebProgress *aWebProgress, * */ - if (!mFind || (aStateFlags & STATE_IS_DOCUMENT|STATE_TRANSFERRING) != (STATE_IS_DOCUMENT|STATE_TRANSFERRING)) + if ((aStateFlags & STATE_IS_DOCUMENT|STATE_TRANSFERRING) != (STATE_IS_DOCUMENT|STATE_TRANSFERRING)) return NS_OK; nsCOMPtr domWindow; @@ -330,9 +337,10 @@ NS_IMETHODIMP nsTypeAheadFind::Focus(nsIDOMEvent* aEvent) nsCOMPtr domEventTarget; aEvent->GetTarget(getter_AddRefs(domEventTarget)); nsCOMPtr domNode(do_QueryInterface(domEventTarget)); - if (!domNode || gIsFindingText) + if (!domNode) return NS_OK; // prevents listener callbacks from resetting us during typeahead find processing - CancelFind(); + if (!gIsFindingText) + CancelFind(); nsCOMPtr domDoc; domNode->GetOwnerDocument(getter_AddRefs(domDoc)); @@ -347,7 +355,7 @@ NS_IMETHODIMP nsTypeAheadFind::Focus(nsIDOMEvent* aEvent) nsCOMPtr domWin(do_QueryInterface(ourGlobal)); nsCOMPtr rootTarget(do_QueryInterface(domWin)); - if (!rootTarget || domWin == mCurrentWindow) + if (!rootTarget || domWin == mFocusedWindow) return NS_OK; // Focus event -- possibly trigger keypress event listening @@ -360,8 +368,8 @@ NS_IMETHODIMP nsTypeAheadFind::Focus(nsIDOMEvent* aEvent) RemoveCurrentScrollPositionListener(); RemoveCurrentKeypressListener(); - mCurrentWindow = domWin; - mWeakShell = do_GetWeakReference(presShell); + mFocusedWindow = domWin; + mFocusedWeakShell = do_GetWeakReference(presShell); PRInt16 isEditor; presShell->GetSelectionFlags(&isEditor); @@ -372,7 +380,7 @@ NS_IMETHODIMP nsTypeAheadFind::Focus(nsIDOMEvent* aEvent) } // Add scroll position and selection listeners, so we can cancel current find when user navigates - GetSelection(presShell, nsnull, getter_AddRefs(mDocSelection)); // cache for reuse + GetSelection(presShell, nsnull, getter_AddRefs(mFocusedDocSelection)); // cache for reuse AttachNewScrollPositionListener(presShell); AttachNewSelectionListener(); @@ -385,8 +393,6 @@ NS_IMETHODIMP nsTypeAheadFind::Focus(nsIDOMEvent* aEvent) NS_IMETHODIMP nsTypeAheadFind::Blur(nsIDOMEvent* aEvent) { - if (gIsFindingText) - return NS_OK; // prevents listener callbacks from resetting us during typeahead find processing nsCOMPtr domEventTarget; aEvent->GetTarget(getter_AddRefs(domEventTarget)); nsCOMPtr domWindow(do_QueryInterface(domEventTarget)); @@ -473,7 +479,7 @@ NS_IMETHODIMP nsTypeAheadFind::KeyPress(nsIDOMEvent* aEvent) nsCOMPtr doc(do_QueryInterface(domDoc)); if (!doc) return NS_OK; - nsCOMPtr presShell(do_QueryReferent(mWeakShell)); + nsCOMPtr presShell(do_QueryReferent(mFocusedWeakShell)); if (!presShell) return NS_OK; nsCOMPtr presContext; @@ -484,14 +490,14 @@ NS_IMETHODIMP nsTypeAheadFind::KeyPress(nsIDOMEvent* aEvent) nsCOMPtr localSelection; GetSelection(presShell, targetNode, getter_AddRefs(localSelection)); - if (localSelection != mDocSelection || !mDocSelection) + if (localSelection != mFocusedDocSelection || !mFocusedDocSelection) return NS_OK; // Different selection for this node/frame. Must be in editable control. // ------------- Escape pressed --------------------- if (keyCode == nsIDOMKeyEvent::DOM_VK_ESCAPE) { // Escape accomplishes 2 things: // 1. it is a way for the user to deselect with the keyboard - mDocSelection->CollapseToStart(); + mFocusedDocSelection->CollapseToStart(); // 2. it is a way for the user to cancel incremental find with visual feedback (the selection disappears) if (mTypeAheadBuffer.IsEmpty()) return NS_OK; @@ -511,10 +517,10 @@ NS_IMETHODIMP nsTypeAheadFind::KeyPress(nsIDOMEvent* aEvent) aEvent->PreventDefault(); // If back space is normally used for a command, don't do it if (mTypeAheadBuffer.Length() == 1) { if (mStartFindRange) { - mDocSelection->RemoveAllRanges(); - mDocSelection->AddRange(mStartFindRange); + mFocusedDocSelection->RemoveAllRanges(); + mFocusedDocSelection->AddRange(mStartFindRange); } - mDocSelection->CollapseToStart(); + mFocusedDocSelection->CollapseToStart(); CancelFind(); return NS_OK; } @@ -551,154 +557,24 @@ NS_IMETHODIMP nsTypeAheadFind::KeyPress(nsIDOMEvent* aEvent) // If you can see the selection (either not collapsed or through caret browsing), start there // Otherwise we're going to start at the first visible element PRBool isSelectionCollapsed; - mDocSelection->GetIsCollapsed(&isSelectionCollapsed); + mFocusedDocSelection->GetIsCollapsed(&isSelectionCollapsed); isFirstVisiblePreferred = !mCaretBrowsingOn && isSelectionCollapsed; } } - FindItNow(isLinksOnly, isFirstVisiblePreferred, isBackspace); - return NS_OK; -} - -void nsTypeAheadFind::FindItNow(PRBool aIsLinksOnly, PRBool aIsFirstVisiblePreferred, PRBool aIsBackspace) -{ gIsFindingText = PR_TRUE; // prevents listener callbacks from resetting us during typeahead find processing + nsresult rv = FindItNow(isLinksOnly, isFirstVisiblePreferred, isBackspace); + gIsFindingText = PR_FALSE; - nsCOMPtr presShell(do_QueryReferent(mWeakShell)); - if (!presShell) - return; - nsCOMPtr presContext; - presShell->GetPresContext(getter_AddRefs(presContext)); - if (!presContext) - return; - - // ----------- Set search options --------------- - - mFind->SetFindBackwards(PR_FALSE); - mFind->SetCaseSensitive(PR_FALSE); - mFind->SetWordBreaker(nsnull); - - // ------------ Get ranges ready ---------------- - nsCOMPtr searchRange(do_CreateInstance(kRangeCID)), selectionRange, - returnRange, startPointRange(do_CreateInstance(kRangeCID)), endPointRange(do_CreateInstance(kRangeCID)); - - nsCOMPtr doc; - presShell->GetDocument(getter_AddRefs(doc)); - if (!doc) - return; - nsCOMPtr rootContent; - doc->GetRootContent(getter_AddRefs(rootContent)); - nsCOMPtr rootNode(do_QueryInterface(rootContent)); - if (!rootNode) - return; - PRInt32 childCount; - if (NS_FAILED(rootContent->ChildCount(childCount))) - return; - - searchRange->SetStart(rootNode, 0); - searchRange->SetEnd(rootNode, childCount); - endPointRange->SetStart(rootNode, childCount); - endPointRange->SetEnd(rootNode, childCount); - - mDocSelection->GetRangeAt(0, getter_AddRefs(selectionRange)); - if (!selectionRange || aIsFirstVisiblePreferred) { - nsCOMPtr rootRange(do_CreateInstance(kRangeCID)); - rootRange->SetStart(rootNode, 0); - rootRange->SetEnd(rootNode, 0); - // Ensure visible range, move forward if necessary - IsRangeVisible(presShell, presContext, rootRange, aIsFirstVisiblePreferred, getter_AddRefs(startPointRange)); - } - else if (aIsBackspace && mStartFindRange) // when backspace is pressed - mStartFindRange->CloneRange(getter_AddRefs(startPointRange)); - else { - PRInt32 startOffset; - nsCOMPtr startNode; - if (mIsRepeatingSameChar) { - selectionRange->GetEndContainer(getter_AddRefs(startNode)); - selectionRange->GetEndOffset(&startOffset); - } - else { - selectionRange->GetStartContainer(getter_AddRefs(startNode)); - selectionRange->GetStartOffset(&startOffset); - } - if (!startNode) - startNode = rootNode; - startPointRange->SetStart(startNode, startOffset); - startPointRange->SetEnd(startNode, startOffset); - } - - PRBool hasWrapped = PR_FALSE; - - // -------- Find text loop ----------- - - nsAutoString findBuffer; - if (mIsRepeatingSameChar) - findBuffer = mTypeAheadBuffer.First(); - else - findBuffer = PromiseFlatString(mTypeAheadBuffer); - - do { - mFind->Find(findBuffer.get(), searchRange, - startPointRange, endPointRange, getter_AddRefs(returnRange)); - if (!returnRange) { - if (!hasWrapped || aIsFirstVisiblePreferred) { - searchRange->SetStart(rootNode, 0); - searchRange->SetEnd(rootNode, childCount); - startPointRange->SetStart(rootNode, 0); - startPointRange->SetEnd(rootNode, 0); - if (aIsFirstVisiblePreferred) - aIsFirstVisiblePreferred = PR_FALSE; - else if (aIsFirstVisiblePreferred) - break; // No need to wrap, started at beginning - else - hasWrapped = PR_TRUE; - continue; - } - if (mTypeAheadBuffer.First() == mTypeAheadBuffer.CharAt(1) && - mTypeAheadBuffer.Last() != mTypeAheadBuffer.First()) { - // The aardvark rule, if they repeat the same character and then change - // first find exactly what they typed, if not there start over with new char - mTypeAheadBuffer = mTypeAheadBuffer.Last(); - FindItNow(PR_TRUE, aIsFirstVisiblePreferred, aIsBackspace); - return; - } - // ----- Nothing found ----- - nsCOMPtr soundInterface(do_CreateInstance("@mozilla.org/sound;1")); - DisplayStatus(PR_FALSE, PR_FALSE); // Display failure status - if (soundInterface) - soundInterface->Beep(); - // Remove bad character from buffer, so we can continue typing from last matched character - mTypeAheadBuffer = Substring(mTypeAheadBuffer, 0, mTypeAheadBuffer.Length() - 1); - break; - } - - // ------- Test resulting found range for success conditions ------ - PRBool isInsideLink, isStartingLink, isInsideViewPort = PR_TRUE; - RangeStartsInsideLink(returnRange, presShell, &isInsideLink, &isStartingLink); - - if (!IsRangeVisible(presShell, presContext, returnRange, aIsFirstVisiblePreferred, getter_AddRefs(startPointRange)) || - (mIsRepeatingSameChar && !isStartingLink) || (aIsLinksOnly && !isInsideLink)) { - // ------ Failure ------ - // Start find again from here - startPointRange->Collapse(PR_FALSE); // collapse to end - continue; - } - - // ------ Success! ------- - mKeepSelectionOnCancel = PR_FALSE; - mDocSelection->RemoveAllRanges(); - mDocSelection->AddRange(returnRange); - nsCOMPtr esm; - presContext->GetEventStateManager(getter_AddRefs(esm)); - if (esm) { - PRBool isSelectionWithFocus; - esm->MoveFocusToCaret(PR_TRUE, &isSelectionWithFocus); - } - DisplayStatus(PR_TRUE, PR_FALSE); // display success status + if (NS_SUCCEEDED(rv)) { + // ------- Success!!! --------------------------------------------------------------------------------- + mKeepSelectionOnCancel = PR_FALSE; // Next time CancelFind() is called, selection will be collapsed + DisplayStatus(PR_TRUE, PR_FALSE); // display success status // ------- Store current find string for regular find usage: find-next or find dialog text field ------ if (mFindService) { #ifdef RESTORE_SEARCH + // Store old find settings for later restoration if (mTypeAheadBuffer.Length() == 1 && mOldSearchString.IsEmpty()) { mFindService->GetSearchString(mOldSearchString); mFindService->GetWrapFind(&mOldWrapSetting); @@ -721,23 +597,275 @@ void nsTypeAheadFind::FindItNow(PRBool aIsLinksOnly, PRBool aIsFirstVisiblePrefe if (mTimer) mTimer->Init(NS_STATIC_CAST(nsITimerCallback*, this), mTimeoutLength); } - break; + if (mTypeAheadBuffer.Length() == 1) { // If first letter, store where the first find succeeded (mStartFindRange) + mStartFindRange = nsnull; + nsCOMPtr startFindRange; + mFocusedDocSelection->GetRangeAt(0, getter_AddRefs(startFindRange)); + if (startFindRange) + startFindRange->CloneRange(getter_AddRefs(mStartFindRange)); + } } - while (PR_TRUE); - - // ----------- Clean up ---------------- - - if (mTypeAheadBuffer.Length() == 1) { // If first letter, store where the first find succeeded (mStartFindRange) - mStartFindRange = nsnull; - nsCOMPtr startFindRange; - mDocSelection->GetRangeAt(0, getter_AddRefs(startFindRange)); - startFindRange->CloneRange(getter_AddRefs(mStartFindRange)); + else { + // ----- Nothing found ----- + nsCOMPtr soundInterface(do_CreateInstance("@mozilla.org/sound;1")); + DisplayStatus(PR_FALSE, PR_FALSE); // Display failure status + if (soundInterface) + soundInterface->Beep(); + // Remove bad character from buffer, so we can continue typing from last matched character + mTypeAheadBuffer = Substring(mTypeAheadBuffer, 0, mTypeAheadBuffer.Length() - 1); } - gIsFindingText = PR_FALSE; + return NS_OK; } +nsresult nsTypeAheadFind::FindItNow(PRBool aIsLinksOnly, PRBool aIsFirstVisiblePreferred, PRBool aIsBackspace) +{ + nsCOMPtr presShell, startingPresShell(do_QueryReferent(mFocusedWeakShell)); + if (aIsBackspace && mStartFindRange) { // when backspace is pressed, start from where first char was found + nsCOMPtr startNode; + mStartFindRange->GetStartContainer(getter_AddRefs(startNode)); + if (startNode) { + nsCOMPtr domDoc; + startNode->GetOwnerDocument(getter_AddRefs(domDoc)); + nsCOMPtr doc(do_QueryInterface(domDoc)); + if (doc) + doc->GetShellAt(0, getter_AddRefs(presShell)); + } + } + if (!presShell) + presShell = startingPresShell; // this is the current document + if (!presShell) + return NS_ERROR_FAILURE; + nsCOMPtr presContext; + presShell->GetPresContext(getter_AddRefs(presContext)); + if (!presContext) + return NS_ERROR_FAILURE; + + nsCOMPtr currentContainer, startingContainer; + presContext->GetContainer(getter_AddRefs(startingContainer)); + nsCOMPtr treeItem(do_QueryInterface(startingContainer)), rootContentTreeItem; + nsCOMPtr currentDocShell, startingDocShell(do_QueryInterface(startingContainer)); + + treeItem->GetSameTypeRootTreeItem(getter_AddRefs(rootContentTreeItem)); + nsCOMPtr rootContentDocShell(do_QueryInterface(rootContentTreeItem)); + if (!rootContentDocShell) + return NS_ERROR_FAILURE; + + nsCOMPtr docShellEnumerator; + rootContentDocShell->GetDocShellEnumerator(nsIDocShellTreeItem::typeContent, nsIDocShell::ENUMERATE_FORWARDS, + getter_AddRefs(docShellEnumerator)); + + // Default: can start at the current document + currentContainer = startingContainer = do_QueryInterface(rootContentDocShell); + + // Iterate up to current shell, if there's more than 1 that we're dealing with + PRBool hasMoreDocShells; + while (NS_SUCCEEDED(docShellEnumerator->HasMoreElements(&hasMoreDocShells)) && hasMoreDocShells) { + docShellEnumerator->GetNext(getter_AddRefs(currentContainer)); + currentDocShell = do_QueryInterface(currentContainer); + if (!currentDocShell || currentDocShell == startingDocShell || aIsFirstVisiblePreferred) + break; + } + + // ------------ Get ranges ready ---------------- + nsCOMPtr searchRange, startPointRange, endPointRange, rootRange, returnRange; + if (NS_FAILED(GetSearchContainers(currentContainer, aIsFirstVisiblePreferred, + getter_AddRefs(presShell), getter_AddRefs(presContext), + getter_AddRefs(searchRange), getter_AddRefs(startPointRange), + getter_AddRefs(endPointRange)))) + return NS_ERROR_FAILURE; + + if (aIsBackspace && mStartFindRange && startingDocShell == currentDocShell) { + // when backspace is pressed, start where first char was found + mStartFindRange->CloneRange(getter_AddRefs(startPointRange)); + } + + PRInt32 rangeCompareResult = 0; + startPointRange->CompareBoundaryPoints(nsIDOMRange::START_TO_START , searchRange, &rangeCompareResult); + PRBool hasWrapped = (rangeCompareResult <= 0); // No need to wrap find in doc if starting at beginning + + nsAutoString findBuffer; + if (mIsRepeatingSameChar) + findBuffer = mTypeAheadBuffer.First(); + else + findBuffer = PromiseFlatString(mTypeAheadBuffer); + + while (PR_TRUE) { // ----- Outer while loop: go through all docs ----- + rootRange = searchRange; + + // =============== Inner while loop: go through a single document ================== + while (PR_TRUE) { + mFind->Find(findBuffer.get(), searchRange, + startPointRange, endPointRange, getter_AddRefs(returnRange)); + if (!returnRange) + break; + // ------- Test resulting found range for success conditions ------ + PRBool isInsideLink, isStartingLink, isInsideViewPort = PR_TRUE; + RangeStartsInsideLink(returnRange, presShell, &isInsideLink, &isStartingLink); + + if (!IsRangeVisible(presShell, presContext, returnRange, aIsFirstVisiblePreferred, getter_AddRefs(startPointRange)) || + (mIsRepeatingSameChar && !isStartingLink) || (aIsLinksOnly && !isInsideLink)) { + // ------ Failure ------ + // Start find again from here + startPointRange->Collapse(PR_FALSE); // collapse to end + continue; + } + + // ------ Success! ------- + // Make sure new document is selected + if (presShell != startingPresShell) { + // We are in a new document + mFocusedDocSelection->CollapseToStart(); // hide old doc's selection + nsCOMPtr doc; + presShell->GetDocument(getter_AddRefs(doc)); + if (!doc) + return NS_ERROR_FAILURE; + mFocusedWeakShell = do_GetWeakReference(presShell); + nsCOMPtr docContent; + doc->GetRootContent(getter_AddRefs(docContent)); + if (docContent) + docContent->SetFocus(presContext); + } + + // Select the found text and focus it + mFocusedDocSelection->RemoveAllRanges(); + mFocusedDocSelection->AddRange(returnRange); + nsCOMPtr esm; + presContext->GetEventStateManager(getter_AddRefs(esm)); + if (esm) { + PRBool isSelectionWithFocus; + esm->MoveFocusToCaret(PR_TRUE, &isSelectionWithFocus); + } + return NS_OK; + } + // ================= end-inner-while: go through a single document ================== + + // ---------- Nothing found yet ------------- + do { + if (NS_SUCCEEDED(docShellEnumerator->HasMoreElements(&hasMoreDocShells)) && hasMoreDocShells) { + docShellEnumerator->GetNext(getter_AddRefs(currentContainer)); + if (NS_FAILED(GetSearchContainers(currentContainer, aIsFirstVisiblePreferred, + getter_AddRefs(presShell), getter_AddRefs(presContext), + getter_AddRefs(searchRange), getter_AddRefs(startPointRange), + getter_AddRefs(endPointRange)))) + return NS_ERROR_FAILURE; + currentDocShell = do_QueryInterface(currentContainer); + if (currentDocShell) + break; + } + // Reached last doc shell, loop around back to first doc shell + rootContentDocShell->GetDocShellEnumerator(nsIDocShellTreeItem::typeContent, nsIDocShell::ENUMERATE_FORWARDS, + getter_AddRefs(docShellEnumerator)); + } + while (docShellEnumerator); + + if (currentDocShell != startingDocShell) + continue; // Try next document + + // Finished searching through docshells: + // If aFirstVisiblePreferred == PR_TRUE, we may need to go through all docshells twice + // Once to look for visible matches, the second time for any match + if (!hasWrapped || aIsFirstVisiblePreferred) { + rootRange->CloneRange(getter_AddRefs(searchRange)); + rootRange->CloneRange(getter_AddRefs(startPointRange)); + startPointRange->Collapse(PR_TRUE); // Collapse to beginning + aIsFirstVisiblePreferred = PR_FALSE; + hasWrapped = PR_TRUE; + continue; // Go through all docs again + } + + // Last resort, the aardvark rule + if (mTypeAheadBuffer.First() == mTypeAheadBuffer.CharAt(1) && + mTypeAheadBuffer.Last() != mTypeAheadBuffer.First()) { + // The aardvark rule: if they repeat the same character and then change + // first find exactly what they typed, if not there start over with new char + mTypeAheadBuffer = mTypeAheadBuffer.Last(); + return FindItNow(PR_TRUE, aIsFirstVisiblePreferred, aIsBackspace); + } + + // ------------- Failed -------------- + break; + } // end-outer-while: go through all docs + + return NS_ERROR_FAILURE; +} + + +nsresult nsTypeAheadFind::GetSearchContainers(nsISupports *aContainer, PRBool aIsFirstVisiblePreferred, + nsIPresShell **aPresShell, nsIPresContext **aPresContext, + nsIDOMRange **aSearchRange, nsIDOMRange **aStartPointRange, + nsIDOMRange **aEndPointRange) +{ + *aSearchRange = nsnull; + *aStartPointRange = nsnull; + *aEndPointRange = nsnull; + *aPresShell = nsnull; + *aPresContext = nsnull; + + nsCOMPtr docShell(do_QueryInterface(aContainer)); + if (!docShell) + return NS_ERROR_FAILURE; + + docShell->GetPresShell(aPresShell); + docShell->GetPresContext(aPresContext); + if (!*aPresShell || !*aPresContext) + return NS_ERROR_FAILURE; + + nsCOMPtr doc; + (*aPresShell)->GetDocument(getter_AddRefs(doc)); + if (!doc) + return NS_ERROR_FAILURE; + nsCOMPtr rootContent; + doc->GetRootContent(getter_AddRefs(rootContent)); + nsCOMPtr rootNode(do_QueryInterface(rootContent)); + if (!rootNode) + return NS_ERROR_FAILURE; + PRInt32 childCount; + if (NS_FAILED(rootContent->ChildCount(childCount))) + return NS_ERROR_FAILURE; + + nsCOMPtr searchRange(do_CreateInstance(kRangeCID)); + nsCOMPtr startPointRange(do_CreateInstance(kRangeCID)); + nsCOMPtr endPointRange(do_CreateInstance(kRangeCID)); + + searchRange->SelectNodeContents(rootNode); + endPointRange->SetStart(rootNode, childCount); + endPointRange->SetEnd(rootNode, childCount); + + // Consider current selection as null if it's not in the currently focused document + nsCOMPtr currentSelectionRange; + nsCOMPtr selectionPresShell = do_QueryReferent(mFocusedWeakShell); + if (selectionPresShell == *aPresShell) + mFocusedDocSelection->GetRangeAt(0, getter_AddRefs(currentSelectionRange)); + + if (!currentSelectionRange || aIsFirstVisiblePreferred) { + // Ensure visible range, move forward if necessary + IsRangeVisible(*aPresShell, *aPresContext, searchRange, aIsFirstVisiblePreferred, getter_AddRefs(startPointRange)); + } + else { + PRInt32 startOffset; + nsCOMPtr startNode; + if (mIsRepeatingSameChar) { + currentSelectionRange->GetEndContainer(getter_AddRefs(startNode)); + currentSelectionRange->GetEndOffset(&startOffset); + } + else { + currentSelectionRange->GetStartContainer(getter_AddRefs(startNode)); + currentSelectionRange->GetStartOffset(&startOffset); + } + if (!startNode) + startNode = rootNode; + startPointRange->SetStart(startNode, startOffset); + startPointRange->SetEnd(startNode, startOffset); + } + + NS_ADDREF(*aSearchRange = searchRange); + NS_ADDREF(*aStartPointRange = startPointRange); + NS_ADDREF(*aEndPointRange = endPointRange); + return NS_OK; +} + void nsTypeAheadFind::RangeStartsInsideLink(nsIDOMRange *aRange, nsIPresShell *aPresShell, PRBool *aIsInsideLink, PRBool *aIsStartingLink) @@ -870,7 +998,7 @@ void nsTypeAheadFind::CancelFind() mTypeAheadBuffer.Truncate(); DisplayStatus(PR_FALSE, PR_TRUE); // Clear status if (!mKeepSelectionOnCancel) - mDocSelection->CollapseToStart(); + mFocusedDocSelection->CollapseToStart(); #ifdef RESTORE_SEARCH // Restore old find settings // not working to well, because timer cancels find @@ -895,7 +1023,7 @@ void nsTypeAheadFind::CancelFind() void nsTypeAheadFind::RemoveCurrentScrollPositionListener() { - nsCOMPtr presShell(do_QueryReferent(mWeakShell)); + nsCOMPtr presShell(do_QueryReferent(mFocusedWeakShell)); nsCOMPtr vm; if (presShell) @@ -907,7 +1035,7 @@ void nsTypeAheadFind::RemoveCurrentScrollPositionListener() if (scrollableView) scrollableView->RemoveScrollPositionListener(NS_STATIC_CAST(nsIScrollPositionListener *, this)); - mWeakShell = nsnull; + mFocusedWeakShell = nsnull; } @@ -926,17 +1054,17 @@ void nsTypeAheadFind::AttachNewScrollPositionListener(nsIPresShell *aPresShell) void nsTypeAheadFind::RemoveCurrentSelectionListener() { - nsCOMPtr selPrivate(do_QueryInterface(mDocSelection)); + nsCOMPtr selPrivate(do_QueryInterface(mFocusedDocSelection)); if (selPrivate) selPrivate->RemoveSelectionListener(this); // remove us if we are a listener - mDocSelection = nsnull; + mFocusedDocSelection = nsnull; } void nsTypeAheadFind::AttachNewSelectionListener() { // Only add selection listener if caret browsing is on - nsCOMPtr selPrivate(do_QueryInterface(mDocSelection)); + nsCOMPtr selPrivate(do_QueryInterface(mFocusedDocSelection)); if (selPrivate) selPrivate->AddSelectionListener(this); } @@ -944,7 +1072,7 @@ void nsTypeAheadFind::AttachNewSelectionListener() void nsTypeAheadFind::RemoveCurrentKeypressListener() { - nsCOMPtr target(do_QueryInterface(mCurrentWindow)); + nsCOMPtr target(do_QueryInterface(mFocusedWindow)); if (target) target->RemoveEventListener(NS_LITERAL_STRING("keypress"), NS_STATIC_CAST(nsIDOMKeyListener*, this), PR_FALSE); } @@ -959,12 +1087,12 @@ void nsTypeAheadFind::AttachNewKeypressListener(nsIDOMEventTarget *aTarget) void nsTypeAheadFind::RemoveCurrentWindowFocusListener() { // Remove focus listener, and unload listener as well, since it's sole purpose was to remove focus listener - nsCOMPtr target(do_QueryInterface(mCurrentWindow)); + nsCOMPtr target(do_QueryInterface(mFocusedWindow)); if (target) { target->RemoveEventListener(NS_LITERAL_STRING("focus"), NS_STATIC_CAST(nsIDOMFocusListener*, this), PR_FALSE); target->RemoveEventListener(NS_LITERAL_STRING("unload"), NS_STATIC_CAST(nsIDOMLoadListener*, this), PR_FALSE); } - mCurrentWindow = nsnull; + mFocusedWindow = nsnull; } @@ -1109,7 +1237,7 @@ nsresult nsTypeAheadFind::GetTranslatedString(const nsAString& aKey, nsAString& void nsTypeAheadFind::DisplayStatus(PRBool aSuccess, PRBool aClearStatus) { // Cache mBrowserChrome, used for showing status messages - nsCOMPtr presShell(do_QueryReferent(mWeakShell)); + nsCOMPtr presShell(do_QueryReferent(mFocusedWeakShell)); if (!presShell) return; nsCOMPtr presContext; diff --git a/extensions/typeaheadfind/nsTypeAheadFind.h b/extensions/typeaheadfind/nsTypeAheadFind.h index 481c2f0bf8a1..bfc6a7bd5721 100644 --- a/extensions/typeaheadfind/nsTypeAheadFind.h +++ b/extensions/typeaheadfind/nsTypeAheadFind.h @@ -131,7 +131,10 @@ protected: void CancelFind(); PRBool IsRangeVisible(nsIPresShell *aPresShell, nsIPresContext *aPresContext, nsIDOMRange *aRange, PRBool aMustBeVisible, nsIDOMRange **aNewRange); - void FindItNow(PRBool aIsLinksOnly, PRBool aIsFirstVisiblePreferred, PRBool aIsBackspace); + nsresult FindItNow(PRBool aIsLinksOnly, PRBool aIsFirstVisiblePreferred, PRBool aIsBackspace); + nsresult GetSearchContainers(nsISupports *aContainer, PRBool aIsFirstVisiblePreferred, + nsIPresShell **aPresShell, nsIPresContext **aPresContext, + nsIDOMRange **aSearchRange, nsIDOMRange **aStartRange, nsIDOMRange **aEndRange); void DisplayStatus(PRBool aSuccess, PRBool aClearStatus); nsresult GetTranslatedString(const nsAString& aKey, nsAString& aStringOut); @@ -160,8 +163,8 @@ protected: nsCOMPtr mStringBundle; nsCOMPtr mTimer; - // The current content window that we're listening to and it's cached objects - nsCOMPtr mDocSelection; - nsCOMPtr mCurrentWindow; - nsCOMPtr mWeakShell; + // The focused content window that we're listening to and it's cached objects + nsCOMPtr mFocusedDocSelection; + nsCOMPtr mFocusedWindow; + nsCOMPtr mFocusedWeakShell; };