Back to index

lightning-sunbird  0.9+nobinonly
nsWebBrowserFind.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
00002  *
00003  * ***** BEGIN LICENSE BLOCK *****
00004  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00005  *
00006  * The contents of this file are subject to the Mozilla Public License Version
00007  * 1.1 (the "License"); you may not use this file except in compliance with
00008  * the License. You may obtain a copy of the License at
00009  * http://www.mozilla.org/MPL/
00010  *
00011  * Software distributed under the License is distributed on an "AS IS" basis,
00012  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00013  * for the specific language governing rights and limitations under the
00014  * License.
00015  *
00016  * The Original Code is the Mozilla browser.
00017  *
00018  * The Initial Developer of the Original Code is
00019  * Netscape Communications, Inc.
00020  * Portions created by the Initial Developer are Copyright (C) 1999
00021  * the Initial Developer. All Rights Reserved.
00022  *
00023  * Contributor(s):
00024  *   Conrad Carlen <ccarlen@netscape.com>
00025  *   Simon Fraser  <sfraser@netscape.com>
00026  *   Akkana Peck  <akkana@netscape.com>
00027  *
00028  * Alternatively, the contents of this file may be used under the terms of
00029  * either the GNU General Public License Version 2 or later (the "GPL"), or
00030  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00031  * in which case the provisions of the GPL or the LGPL are applicable instead
00032  * of those above. If you wish to allow use of your version of this file only
00033  * under the terms of either the GPL or the LGPL, and not to allow others to
00034  * use your version of this file under the terms of the MPL, indicate your
00035  * decision by deleting the provisions above and replace them with the notice
00036  * and other provisions required by the GPL or the LGPL. If you do not delete
00037  * the provisions above, a recipient may use your version of this file under
00038  * the terms of any one of the MPL, the GPL or the LGPL.
00039  *
00040  * ***** END LICENSE BLOCK ***** */
00041 
00042 #include "nsWebBrowserFind.h"
00043 
00044 // Only need this for NS_FIND_CONTRACTID,
00045 // else we could use nsIDOMRange.h and nsIFind.h.
00046 #include "nsFind.h"
00047 
00048 #include "nsIComponentManager.h"
00049 #include "nsIScriptGlobalObject.h"
00050 #include "nsIScriptSecurityManager.h"
00051 #include "nsIInterfaceRequestor.h"
00052 #include "nsIInterfaceRequestorUtils.h"
00053 #include "nsIDOMWindow.h"
00054 #include "nsIDOMWindowInternal.h"
00055 #include "nsPIDOMWindow.h"
00056 #include "nsIURI.h"
00057 #include "nsIDocShell.h"
00058 #include "nsIEnumerator.h"
00059 #include "nsIDocShellTreeItem.h"
00060 #include "nsIPresShell.h"
00061 #include "nsPresContext.h"
00062 #include "nsIEventStateManager.h"
00063 #include "nsIDocument.h"
00064 #include "nsIDOMDocument.h"
00065 #include "nsIFocusController.h"
00066 #include "nsISelectionController.h"
00067 #include "nsISelection.h"
00068 #include "nsIFrame.h"
00069 #include "nsITextControlFrame.h"
00070 #include "nsReadableUtils.h"
00071 #include "nsIDOMHTMLElement.h"
00072 #include "nsIDOMHTMLDocument.h"
00073 #include "nsIContent.h"
00074 #include "nsContentCID.h"
00075 #include "nsIServiceManager.h"
00076 #include "nsIObserverService.h"
00077 #include "nsISupportsPrimitives.h"
00078 #include "nsITimelineService.h"
00079 
00080 #if DEBUG
00081 #include "nsIWebNavigation.h"
00082 #include "nsXPIDLString.h"
00083 #endif
00084 
00085 #ifdef XP_MACOSX
00086 #include "nsAutoPtr.h"
00087 #include <Scrap.h>
00088 #endif
00089 
00090 
00091 static NS_DEFINE_IID(kRangeCID, NS_RANGE_CID);
00092 
00093 //*****************************************************************************
00094 // nsWebBrowserFind
00095 //*****************************************************************************
00096 
00097 nsWebBrowserFind::nsWebBrowserFind() :
00098     mFindBackwards(PR_FALSE),
00099     mWrapFind(PR_FALSE),
00100     mEntireWord(PR_FALSE),
00101     mMatchCase(PR_FALSE),
00102     mSearchSubFrames(PR_TRUE),
00103     mSearchParentFrames(PR_TRUE)
00104 {
00105 }
00106 
00107 nsWebBrowserFind::~nsWebBrowserFind()
00108 {
00109 }
00110 
00111 NS_IMPL_ISUPPORTS2(nsWebBrowserFind, nsIWebBrowserFind, nsIWebBrowserFindInFrames)
00112 
00113 
00114 /* boolean findNext (); */
00115 NS_IMETHODIMP nsWebBrowserFind::FindNext(PRBool *outDidFind)
00116 {
00117     NS_ENSURE_ARG_POINTER(outDidFind);
00118     *outDidFind = PR_FALSE;
00119 
00120     NS_ENSURE_TRUE(CanFindNext(), NS_ERROR_NOT_INITIALIZED);
00121 
00122     nsresult rv = NS_OK;
00123     nsCOMPtr<nsIDOMWindow> searchFrame = do_QueryReferent(mCurrentSearchFrame);
00124     NS_ENSURE_TRUE(searchFrame, NS_ERROR_NOT_INITIALIZED);
00125 
00126     nsCOMPtr<nsIDOMWindow> rootFrame = do_QueryReferent(mRootSearchFrame);
00127     NS_ENSURE_TRUE(rootFrame, NS_ERROR_NOT_INITIALIZED);
00128     
00129     // first, if there's a "cmd_findagain" observer around, check to see if it
00130     // wants to perform the find again command . If it performs the find again
00131     // it will return true, in which case we exit ::FindNext() early.
00132     // Otherwise, nsWebBrowserFind needs to perform the find again command itself
00133     // this is used by nsTypeAheadFind, which controls find again when it was
00134     // the last executed find in the current window.
00135     nsCOMPtr<nsIObserverService> observerSvc =
00136       do_GetService("@mozilla.org/observer-service;1");
00137     if (observerSvc) {
00138         nsCOMPtr<nsISupportsInterfacePointer> windowSupportsData = 
00139           do_CreateInstance(NS_SUPPORTS_INTERFACE_POINTER_CONTRACTID, &rv);
00140         NS_ENSURE_SUCCESS(rv, rv);
00141         nsCOMPtr<nsISupports> searchWindowSupports =
00142           do_QueryInterface(rootFrame);
00143         windowSupportsData->SetData(searchWindowSupports);
00144         NS_NAMED_LITERAL_STRING(dnStr, "down");
00145         NS_NAMED_LITERAL_STRING(upStr, "up");
00146         observerSvc->NotifyObservers(windowSupportsData, 
00147                                      "nsWebBrowserFind_FindAgain", 
00148                                      mFindBackwards? upStr.get(): dnStr.get());
00149         windowSupportsData->GetData(getter_AddRefs(searchWindowSupports));
00150         // findnext performed if search window data cleared out
00151         *outDidFind = searchWindowSupports == nsnull;
00152         if (*outDidFind)
00153             return NS_OK;
00154     }
00155 
00156     // next, look in the current frame. If found, return.
00157     rv = SearchInFrame(searchFrame, PR_FALSE, outDidFind);
00158     if (NS_FAILED(rv)) return rv;
00159     if (*outDidFind)
00160         return OnFind(searchFrame);     // we are done
00161 
00162     // if we are not searching other frames, return
00163     if (!mSearchSubFrames && !mSearchParentFrames)
00164         return NS_OK;
00165 
00166     nsIDocShell *rootDocShell = GetDocShellFromWindow(rootFrame);
00167     if (!rootDocShell) return NS_ERROR_FAILURE;
00168     
00169     PRInt32 enumDirection;
00170     if (mFindBackwards)
00171         enumDirection = nsIDocShell::ENUMERATE_BACKWARDS;
00172     else
00173         enumDirection = nsIDocShell::ENUMERATE_FORWARDS;
00174         
00175     nsCOMPtr<nsISimpleEnumerator> docShellEnumerator;
00176     rv = rootDocShell->GetDocShellEnumerator(nsIDocShellTreeItem::typeContent,
00177             enumDirection, getter_AddRefs(docShellEnumerator));    
00178     if (NS_FAILED(rv)) return rv;
00179         
00180     // remember where we started
00181     nsCOMPtr<nsIDocShellTreeItem> startingItem =
00182         do_QueryInterface(GetDocShellFromWindow(searchFrame), &rv);
00183     if (NS_FAILED(rv)) return rv;
00184 
00185     nsCOMPtr<nsIDocShellTreeItem> curItem;
00186 
00187     // XXX We should avoid searching in frameset documents here.
00188     // We also need to honour mSearchSubFrames and mSearchParentFrames.
00189     PRBool hasMore, doFind = PR_FALSE;
00190     while (NS_SUCCEEDED(docShellEnumerator->HasMoreElements(&hasMore)) && hasMore)
00191     {
00192         nsCOMPtr<nsISupports> curSupports;
00193         rv = docShellEnumerator->GetNext(getter_AddRefs(curSupports));
00194         if (NS_FAILED(rv)) break;
00195         curItem = do_QueryInterface(curSupports, &rv);
00196         if (NS_FAILED(rv)) break;
00197 
00198         if (doFind)
00199         {
00200             searchFrame = do_GetInterface(curItem, &rv);
00201             if (NS_FAILED(rv)) break;
00202 
00203             OnStartSearchFrame(searchFrame);
00204 
00205             rv = SearchInFrame(searchFrame, PR_FALSE, outDidFind);
00206             if (NS_FAILED(rv)) return rv;
00207             if (*outDidFind)
00208                 return OnFind(searchFrame);     // we are done
00209 
00210             OnEndSearchFrame(searchFrame);
00211         }
00212 
00213         if (curItem.get() == startingItem.get())
00214             doFind = PR_TRUE;       // start looking in frames after this one
00215     };
00216 
00217     if (!mWrapFind)
00218     {
00219         // remember where we left off
00220         SetCurrentSearchFrame(searchFrame);
00221         return NS_OK;
00222     }
00223 
00224     // From here on, we're wrapping, first through the other frames,
00225     // then finally from the beginning of the starting frame back to
00226     // the starting point.
00227 
00228     // because nsISimpleEnumerator is totally lame and isn't resettable, I
00229     // have to make a new one
00230     docShellEnumerator = nsnull;
00231     rv = rootDocShell->GetDocShellEnumerator(nsIDocShellTreeItem::typeContent,
00232             enumDirection, getter_AddRefs(docShellEnumerator));    
00233     if (NS_FAILED(rv)) return rv;
00234     
00235     while (NS_SUCCEEDED(docShellEnumerator->HasMoreElements(&hasMore)) && hasMore)
00236     {
00237         nsCOMPtr<nsISupports> curSupports;
00238         rv = docShellEnumerator->GetNext(getter_AddRefs(curSupports));
00239         if (NS_FAILED(rv)) break;
00240         curItem = do_QueryInterface(curSupports, &rv);
00241         if (NS_FAILED(rv)) break;
00242 
00243         if (curItem.get() == startingItem.get())
00244         {
00245             rv = SearchInFrame(searchFrame, PR_TRUE, outDidFind);
00246             if (NS_FAILED(rv)) return rv;
00247             if (*outDidFind)
00248                 return OnFind(searchFrame);        // we are done
00249             break;
00250         }
00251 
00252         searchFrame = do_GetInterface(curItem, &rv);
00253         if (NS_FAILED(rv)) break;
00254 
00255         OnStartSearchFrame(searchFrame);
00256 
00257         rv = SearchInFrame(searchFrame, PR_FALSE, outDidFind);
00258         if (NS_FAILED(rv)) return rv;
00259         if (*outDidFind)
00260             return OnFind(searchFrame);        // we are done
00261         
00262         OnEndSearchFrame(searchFrame);
00263     }
00264 
00265     // remember where we left off
00266     SetCurrentSearchFrame(searchFrame);
00267     
00268     NS_ASSERTION(NS_SUCCEEDED(rv), "Something failed");
00269     return rv;
00270 }
00271 
00272 
00273 /* attribute wstring searchString; */
00274 NS_IMETHODIMP nsWebBrowserFind::GetSearchString(PRUnichar * *aSearchString)
00275 {
00276     NS_ENSURE_ARG_POINTER(aSearchString);
00277 #ifdef XP_MACOSX
00278     OSStatus err;
00279     ScrapRef scrap;
00280     err = ::GetScrapByName(kScrapFindScrap, kScrapGetNamedScrap, &scrap);
00281     if (err == noErr) {
00282         Size byteCount;
00283         err = ::GetScrapFlavorSize(scrap, kScrapFlavorTypeUnicode, &byteCount);
00284         if (err == noErr) {
00285             NS_ASSERTION(byteCount%2 == 0, "byteCount not a multiple of 2");
00286             nsAutoArrayPtr<PRUnichar> buffer(new PRUnichar[byteCount/2 + 1]);
00287             NS_ENSURE_TRUE(buffer, NS_ERROR_OUT_OF_MEMORY);
00288             err = ::GetScrapFlavorData(scrap, kScrapFlavorTypeUnicode, &byteCount, buffer.get());
00289             if (err == noErr) {
00290                 buffer[byteCount/2] = PRUnichar('\0');
00291                 mSearchString.Assign(buffer);
00292             }
00293         }
00294     }    
00295 #endif
00296     *aSearchString = ToNewUnicode(mSearchString);
00297     return NS_OK;
00298 }
00299 
00300 NS_IMETHODIMP nsWebBrowserFind::SetSearchString(const PRUnichar * aSearchString)
00301 {
00302     mSearchString.Assign(aSearchString);
00303 #ifdef XP_MACOSX
00304     OSStatus err;
00305     ScrapRef scrap;
00306     err = ::GetScrapByName(kScrapFindScrap, kScrapClearNamedScrap, &scrap);
00307     if (err == noErr) {
00308         ::PutScrapFlavor(scrap, kScrapFlavorTypeUnicode, kScrapFlavorMaskNone,
00309         (mSearchString.Length()*2), aSearchString);
00310     }
00311 #endif
00312     return NS_OK;
00313 }
00314 
00315 /* attribute boolean findBackwards; */
00316 NS_IMETHODIMP nsWebBrowserFind::GetFindBackwards(PRBool *aFindBackwards)
00317 {
00318     NS_ENSURE_ARG_POINTER(aFindBackwards);
00319     *aFindBackwards = mFindBackwards;
00320     return NS_OK;
00321 }
00322 
00323 NS_IMETHODIMP nsWebBrowserFind::SetFindBackwards(PRBool aFindBackwards)
00324 {
00325     mFindBackwards = aFindBackwards;
00326     return NS_OK;
00327 }
00328 
00329 /* attribute boolean wrapFind; */
00330 NS_IMETHODIMP nsWebBrowserFind::GetWrapFind(PRBool *aWrapFind)
00331 {
00332     NS_ENSURE_ARG_POINTER(aWrapFind);
00333     *aWrapFind = mWrapFind;
00334     return NS_OK;
00335 }
00336 NS_IMETHODIMP nsWebBrowserFind::SetWrapFind(PRBool aWrapFind)
00337 {
00338     mWrapFind = aWrapFind;
00339     return NS_OK;
00340 }
00341 
00342 /* attribute boolean entireWord; */
00343 NS_IMETHODIMP nsWebBrowserFind::GetEntireWord(PRBool *aEntireWord)
00344 {
00345     NS_ENSURE_ARG_POINTER(aEntireWord);
00346     *aEntireWord = mEntireWord;
00347     return NS_OK;
00348 }
00349 NS_IMETHODIMP nsWebBrowserFind::SetEntireWord(PRBool aEntireWord)
00350 {
00351     mEntireWord = aEntireWord;
00352     return NS_OK;
00353 }
00354 
00355 /* attribute boolean matchCase; */
00356 NS_IMETHODIMP nsWebBrowserFind::GetMatchCase(PRBool *aMatchCase)
00357 {
00358     NS_ENSURE_ARG_POINTER(aMatchCase);
00359     *aMatchCase = mMatchCase;
00360     return NS_OK;
00361 }
00362 NS_IMETHODIMP nsWebBrowserFind::SetMatchCase(PRBool aMatchCase)
00363 {
00364     mMatchCase = aMatchCase;
00365     return NS_OK;
00366 }
00367 
00368 // Same as the tail-end of nsEventStateManager::FocusElementButNotDocument.
00369 // Used here because nsEventStateManager::MoveFocusToCaret() doesn't
00370 // support text input controls.
00371 static void
00372 FocusElementButNotDocument(nsIDocument* aDocument, nsIContent* aContent)
00373 {
00374   nsIFocusController *focusController = nsnull;
00375   nsCOMPtr<nsPIDOMWindow> ourWindow =
00376     do_QueryInterface(aDocument->GetScriptGlobalObject());
00377   if (ourWindow)
00378     focusController = ourWindow->GetRootFocusController();
00379   if (!focusController)
00380     return;
00381 
00382   // Get previous focus
00383   nsCOMPtr<nsIDOMElement> oldFocusedElement;
00384   focusController->GetFocusedElement(getter_AddRefs(oldFocusedElement));
00385   nsCOMPtr<nsIContent> oldFocusedContent =
00386     do_QueryInterface(oldFocusedElement);
00387 
00388   // Notify focus controller of new focus for this document
00389   nsCOMPtr<nsIDOMElement> newFocusedElement(do_QueryInterface(aContent));
00390   focusController->SetFocusedElement(newFocusedElement);
00391 
00392   nsIPresShell* presShell = aDocument->GetShellAt(0);
00393   nsIEventStateManager* esm = presShell->GetPresContext()->EventStateManager();
00394 
00395   // Temporarily set esm::mCurrentFocus so that esm::GetContentState() tells 
00396   // layout system to show focus on this element. 
00397   esm->SetFocusedContent(aContent);  // Reset back to null at the end.
00398   aDocument->BeginUpdate(UPDATE_CONTENT_STATE);
00399   aDocument->ContentStatesChanged(oldFocusedContent, aContent, 
00400                                   NS_EVENT_STATE_FOCUS);
00401   aDocument->EndUpdate(UPDATE_CONTENT_STATE);
00402 
00403   // Reset esm::mCurrentFocus = nsnull for this doc, so when this document
00404   // does get focus next time via preHandleEvent() NS_GOTFOCUS,
00405   // the old document gets blurred
00406   esm->SetFocusedContent(nsnull);
00407 }
00408 
00409 static PRBool
00410 IsNativeAnonymous(nsIContent* aContent)
00411 {
00412     while (aContent) {
00413         nsIContent* bindingParent = aContent->GetBindingParent();
00414         if (bindingParent == aContent) {
00415             return PR_TRUE;
00416         }
00417 
00418         aContent = bindingParent;
00419     }
00420 
00421     return PR_FALSE;
00422 }
00423 
00424 void nsWebBrowserFind::SetSelectionAndScroll(nsIDOMWindow* aWindow,
00425                                              nsIDOMRange*  aRange)
00426 {
00427   nsCOMPtr<nsIDOMDocument> domDoc;    
00428   aWindow->GetDocument(getter_AddRefs(domDoc));
00429   if (!domDoc) return;
00430 
00431   nsCOMPtr<nsIDocument> doc(do_QueryInterface(domDoc));
00432   nsIPresShell* presShell = doc->GetShellAt(0);
00433   if (!presShell) return;
00434 
00435   // since the match could be an anonymous textnode inside a
00436   // <textarea> or text <input>, we need to get the outer frame
00437   nsIFrame *frame = nsnull;
00438   nsITextControlFrame *tcFrame = nsnull;
00439   nsCOMPtr<nsIDOMNode> node;
00440   aRange->GetStartContainer(getter_AddRefs(node));
00441   nsCOMPtr<nsIContent> content(do_QueryInterface(node));
00442   for ( ; content; content = content->GetParent()) {
00443     if (!IsNativeAnonymous(content)) {
00444       presShell->GetPrimaryFrameFor(content, &frame);
00445       if (!frame)
00446         return;
00447       CallQueryInterface(frame, &tcFrame);
00448 
00449       break;
00450     }
00451   }
00452 
00453   nsCOMPtr<nsISelection> selection;
00454   nsCOMPtr<nsISelectionController> selCon;
00455   if (!tcFrame) {
00456     selCon = do_QueryInterface(presShell);
00457   }
00458   else {
00459     tcFrame->GetSelectionContr(getter_AddRefs(selCon));
00460   }
00461 
00462   selCon->SetDisplaySelection(nsISelectionController::SELECTION_ON);
00463   selCon->GetSelection(nsISelectionController::SELECTION_NORMAL,
00464     getter_AddRefs(selection));
00465   if (selection) {
00466     selection->RemoveAllRanges();
00467     selection->AddRange(aRange);
00468 
00469     if (tcFrame) {
00470       FocusElementButNotDocument(doc, content);
00471     }
00472     else {
00473       nsCOMPtr<nsPresContext> presContext = presShell->GetPresContext();
00474       PRBool isSelectionWithFocus;
00475       presContext->EventStateManager()->
00476         MoveFocusToCaret(PR_TRUE, &isSelectionWithFocus);
00477     }
00478 
00479     // Scroll if necessary to make the selection visible:
00480     // Must be the last thing to do - bug 242056
00481     selCon->ScrollSelectionIntoView
00482       (nsISelectionController::SELECTION_NORMAL,
00483        nsISelectionController::SELECTION_FOCUS_REGION, PR_TRUE);
00484   }
00485 }
00486 
00487 // Adapted from nsTextServicesDocument::GetDocumentContentRootNode
00488 nsresult nsWebBrowserFind::GetRootNode(nsIDOMDocument* aDomDoc,
00489                                        nsIDOMNode **aNode)
00490 {
00491   nsresult rv;
00492 
00493   NS_ENSURE_ARG_POINTER(aNode);
00494   *aNode = 0;
00495 
00496   nsCOMPtr<nsIDOMHTMLDocument> htmlDoc = do_QueryInterface(aDomDoc);
00497   if (htmlDoc)
00498   {
00499     // For HTML documents, the content root node is the body.
00500     nsCOMPtr<nsIDOMHTMLElement> bodyElement;
00501     rv = htmlDoc->GetBody(getter_AddRefs(bodyElement));
00502     NS_ENSURE_SUCCESS(rv, rv);
00503     NS_ENSURE_ARG_POINTER(bodyElement);
00504     return bodyElement->QueryInterface(NS_GET_IID(nsIDOMNode),
00505                                        (void **)aNode);
00506   }
00507 
00508   // For non-HTML documents, the content root node will be the doc element.
00509   nsCOMPtr<nsIDOMElement> docElement;
00510   rv = aDomDoc->GetDocumentElement(getter_AddRefs(docElement));
00511   NS_ENSURE_SUCCESS(rv, rv);
00512   NS_ENSURE_ARG_POINTER(docElement);
00513   return docElement->QueryInterface(NS_GET_IID(nsIDOMNode), (void **)aNode);
00514 }
00515 
00516 nsresult nsWebBrowserFind::SetRangeAroundDocument(nsIDOMRange* aSearchRange,
00517                                                   nsIDOMRange* aStartPt,
00518                                                   nsIDOMRange* aEndPt,
00519                                                   nsIDOMDocument* aDoc)
00520 {
00521     nsCOMPtr<nsIDOMNode> bodyNode;
00522     nsresult rv = GetRootNode(aDoc, getter_AddRefs(bodyNode));
00523     nsCOMPtr<nsIContent> bodyContent (do_QueryInterface(bodyNode));
00524     NS_ENSURE_SUCCESS(rv, rv);
00525     NS_ENSURE_ARG_POINTER(bodyContent);
00526 
00527     PRUint32 childCount = bodyContent->GetChildCount();
00528 
00529     aSearchRange->SetStart(bodyNode, 0);
00530     aSearchRange->SetEnd(bodyNode, childCount);
00531 
00532     if (mFindBackwards)
00533     {
00534         aStartPt->SetStart(bodyNode, childCount);
00535         aStartPt->SetEnd(bodyNode, childCount);
00536         aEndPt->SetStart(bodyNode, 0);
00537         aEndPt->SetEnd(bodyNode, 0);
00538     }
00539     else
00540     {
00541         aStartPt->SetStart(bodyNode, 0);
00542         aStartPt->SetEnd(bodyNode, 0);
00543         aEndPt->SetStart(bodyNode, childCount);
00544         aEndPt->SetEnd(bodyNode, childCount);
00545     }
00546 
00547     return NS_OK;
00548 }
00549 
00550 // Set the range to go from the end of the current selection to the
00551 // end of the document (forward), or beginning to beginning (reverse).
00552 // or around the whole document if there's no selection.
00553 nsresult
00554 nsWebBrowserFind::GetSearchLimits(nsIDOMRange* aSearchRange,
00555                                   nsIDOMRange* aStartPt,
00556                                   nsIDOMRange* aEndPt,
00557                                   nsIDOMDocument* aDoc,
00558                                   nsISelection* aSel,
00559                                   PRBool aWrap)
00560 {
00561     NS_ENSURE_ARG_POINTER(aSel);
00562 
00563     // There is a selection.
00564     PRInt32 count = -1;
00565     nsresult rv = aSel->GetRangeCount(&count);
00566     if (count < 1)
00567         return SetRangeAroundDocument(aSearchRange, aStartPt, aEndPt, aDoc);
00568 
00569     // Need bodyNode, for the start/end of the document
00570     nsCOMPtr<nsIDOMNode> bodyNode;
00571     rv = GetRootNode(aDoc, getter_AddRefs(bodyNode));
00572     nsCOMPtr<nsIContent> bodyContent (do_QueryInterface(bodyNode));
00573     NS_ENSURE_ARG_POINTER(bodyContent);
00574 
00575     PRUint32 childCount = bodyContent->GetChildCount();
00576 
00577     // There are four possible range endpoints we might use:
00578     // DocumentStart, SelectionStart, SelectionEnd, DocumentEnd.
00579 
00580     nsCOMPtr<nsIDOMRange> range;
00581     nsCOMPtr<nsIDOMNode> node;
00582     PRInt32 offset;
00583 
00584     // Forward, not wrapping: SelEnd to DocEnd
00585     if (!mFindBackwards && !aWrap)
00586     {
00587         // This isn't quite right, since the selection's ranges aren't
00588         // necessarily in order; but they usually will be.
00589         aSel->GetRangeAt(count-1, getter_AddRefs(range));
00590         if (!range) return NS_ERROR_UNEXPECTED;
00591         range->GetEndContainer(getter_AddRefs(node));
00592         if (!node) return NS_ERROR_UNEXPECTED;
00593         range->GetEndOffset(&offset);
00594 
00595         aSearchRange->SetStart(node, offset);
00596         aSearchRange->SetEnd(bodyNode, childCount);
00597         aStartPt->SetStart(node, offset);
00598         aStartPt->SetEnd(node, offset);
00599         aEndPt->SetStart(bodyNode, childCount);
00600         aEndPt->SetEnd(bodyNode, childCount);
00601     }
00602     // Backward, not wrapping: DocStart to SelStart
00603     else if (mFindBackwards && !aWrap)
00604     {
00605         aSel->GetRangeAt(0, getter_AddRefs(range));
00606         if (!range) return NS_ERROR_UNEXPECTED;
00607         range->GetStartContainer(getter_AddRefs(node));
00608         if (!node) return NS_ERROR_UNEXPECTED;
00609         range->GetStartOffset(&offset);
00610 
00611         aSearchRange->SetStart(bodyNode, 0);
00612         aSearchRange->SetEnd(bodyNode, childCount);
00613         aStartPt->SetStart(node, offset);
00614         aStartPt->SetEnd(node, offset);
00615         aEndPt->SetStart(bodyNode, 0);
00616         aEndPt->SetEnd(bodyNode, 0);
00617     }
00618     // Forward, wrapping: DocStart to SelEnd
00619     else if (!mFindBackwards && aWrap)
00620     {
00621         aSel->GetRangeAt(count-1, getter_AddRefs(range));
00622         if (!range) return NS_ERROR_UNEXPECTED;
00623         range->GetEndContainer(getter_AddRefs(node));
00624         if (!node) return NS_ERROR_UNEXPECTED;
00625         range->GetEndOffset(&offset);
00626 
00627         aSearchRange->SetStart(bodyNode, 0);
00628         aSearchRange->SetEnd(bodyNode, childCount);
00629         aStartPt->SetStart(bodyNode, 0);
00630         aStartPt->SetEnd(bodyNode, 0);
00631         aEndPt->SetStart(node, offset);
00632         aEndPt->SetEnd(node, offset);
00633     }
00634     // Backward, wrapping: SelStart to DocEnd
00635     else if (mFindBackwards && aWrap)
00636     {
00637         aSel->GetRangeAt(0, getter_AddRefs(range));
00638         if (!range) return NS_ERROR_UNEXPECTED;
00639         range->GetStartContainer(getter_AddRefs(node));
00640         if (!node) return NS_ERROR_UNEXPECTED;
00641         range->GetStartOffset(&offset);
00642 
00643         aSearchRange->SetStart(bodyNode, 0);
00644         aSearchRange->SetEnd(bodyNode, childCount);
00645         aStartPt->SetStart(bodyNode, childCount);
00646         aStartPt->SetEnd(bodyNode, childCount);
00647         aEndPt->SetStart(node, offset);
00648         aEndPt->SetEnd(node, offset);
00649     }
00650     return NS_OK;
00651 }
00652 
00653 /* attribute boolean searchFrames; */
00654 NS_IMETHODIMP nsWebBrowserFind::GetSearchFrames(PRBool *aSearchFrames)
00655 {
00656     NS_ENSURE_ARG_POINTER(aSearchFrames);
00657     // this only returns true if we are searching both sub and parent
00658     // frames. There is ambiguity if the caller has previously set
00659     // one, but not both of these.
00660     *aSearchFrames = mSearchSubFrames && mSearchParentFrames;
00661     return NS_OK;
00662 }
00663 
00664 NS_IMETHODIMP nsWebBrowserFind::SetSearchFrames(PRBool aSearchFrames)
00665 {
00666     mSearchSubFrames = aSearchFrames;
00667     mSearchParentFrames = aSearchFrames;
00668     return NS_OK;
00669 }
00670 
00671 /* attribute nsIDOMWindow currentSearchFrame; */
00672 NS_IMETHODIMP nsWebBrowserFind::GetCurrentSearchFrame(nsIDOMWindow * *aCurrentSearchFrame)
00673 {
00674     NS_ENSURE_ARG_POINTER(aCurrentSearchFrame);
00675     nsCOMPtr<nsIDOMWindow> searchFrame = do_QueryReferent(mCurrentSearchFrame);
00676     NS_IF_ADDREF(*aCurrentSearchFrame = searchFrame);
00677     return (*aCurrentSearchFrame) ? NS_OK : NS_ERROR_NOT_INITIALIZED;
00678 }
00679 
00680 NS_IMETHODIMP nsWebBrowserFind::SetCurrentSearchFrame(nsIDOMWindow * aCurrentSearchFrame)
00681 {
00682     // is it ever valid to set this to null?
00683     NS_ENSURE_ARG(aCurrentSearchFrame);
00684     mCurrentSearchFrame = do_GetWeakReference(aCurrentSearchFrame);
00685     return NS_OK;
00686 }
00687 
00688 /* attribute nsIDOMWindow rootSearchFrame; */
00689 NS_IMETHODIMP nsWebBrowserFind::GetRootSearchFrame(nsIDOMWindow * *aRootSearchFrame)
00690 {
00691     NS_ENSURE_ARG_POINTER(aRootSearchFrame);
00692     nsCOMPtr<nsIDOMWindow> searchFrame = do_QueryReferent(mRootSearchFrame);
00693     NS_IF_ADDREF(*aRootSearchFrame = searchFrame);
00694     return (*aRootSearchFrame) ? NS_OK : NS_ERROR_NOT_INITIALIZED;
00695 }
00696 
00697 NS_IMETHODIMP nsWebBrowserFind::SetRootSearchFrame(nsIDOMWindow * aRootSearchFrame)
00698 {
00699     // is it ever valid to set this to null?
00700     NS_ENSURE_ARG(aRootSearchFrame);
00701     mRootSearchFrame = do_GetWeakReference(aRootSearchFrame);
00702     return NS_OK;
00703 }
00704 
00705 /* attribute boolean searchSubframes; */
00706 NS_IMETHODIMP nsWebBrowserFind::GetSearchSubframes(PRBool *aSearchSubframes)
00707 {
00708     NS_ENSURE_ARG_POINTER(aSearchSubframes);
00709     *aSearchSubframes = mSearchSubFrames;
00710     return NS_OK;
00711 }
00712 
00713 NS_IMETHODIMP nsWebBrowserFind::SetSearchSubframes(PRBool aSearchSubframes)
00714 {
00715     mSearchSubFrames = aSearchSubframes;
00716     return NS_OK;
00717 }
00718 
00719 /* attribute boolean searchParentFrames; */
00720 NS_IMETHODIMP nsWebBrowserFind::GetSearchParentFrames(PRBool *aSearchParentFrames)
00721 {
00722     NS_ENSURE_ARG_POINTER(aSearchParentFrames);
00723     *aSearchParentFrames = mSearchParentFrames;
00724     return NS_OK;
00725 }
00726 
00727 NS_IMETHODIMP nsWebBrowserFind::SetSearchParentFrames(PRBool aSearchParentFrames)
00728 {
00729     mSearchParentFrames = aSearchParentFrames;
00730     return NS_OK;
00731 }
00732 
00733 /*
00734     This method handles finding in a single window (aka frame).
00735 
00736 */
00737 nsresult nsWebBrowserFind::SearchInFrame(nsIDOMWindow* aWindow,
00738                                          PRBool aWrapping,
00739                                          PRBool* aDidFind)
00740 {
00741     NS_ENSURE_ARG(aWindow);
00742     NS_ENSURE_ARG_POINTER(aDidFind);
00743 
00744     *aDidFind = PR_FALSE;
00745 
00746     nsCOMPtr<nsIDOMDocument> domDoc;    
00747     nsresult rv = aWindow->GetDocument(getter_AddRefs(domDoc));
00748     NS_ENSURE_SUCCESS(rv, rv);
00749     if (!domDoc) return NS_ERROR_FAILURE;
00750 
00751     // Do security check, to ensure that the frame we're searching
00752     // is from the same origin as the frame from which the Find is
00753     // being run.
00754 
00755     // get a uri for the window
00756     nsCOMPtr<nsIDocument> theDoc = do_QueryInterface(domDoc);
00757     if (!theDoc) return NS_ERROR_FAILURE;
00758 
00759     nsIURI *docURI = theDoc->GetDocumentURI();
00760     NS_ENSURE_TRUE(docURI, NS_ERROR_FAILURE);
00761 
00762     // Get the security manager and do the same-origin check
00763     nsCOMPtr<nsIScriptSecurityManager> secMan = do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv);
00764     NS_ENSURE_SUCCESS(rv, rv);
00765     rv = secMan->CheckSameOrigin(nsnull, docURI);
00766     if (NS_FAILED(rv)) return rv;
00767 
00768     if (!mFind) {
00769         mFind = do_CreateInstance(NS_FIND_CONTRACTID, &rv);
00770         NS_ENSURE_SUCCESS(rv, rv);
00771     }
00772 
00773     (void) mFind->SetCaseSensitive(mMatchCase);
00774     (void) mFind->SetFindBackwards(mFindBackwards);
00775 
00776     // XXX Make and set a line breaker here, once that's implemented.
00777     (void) mFind->SetWordBreaker(0);
00778 
00779     // Now make sure the content (for actual finding) and frame (for
00780     // selection) models are up to date.
00781     theDoc->FlushPendingNotifications(Flush_Frames);
00782 
00783     nsCOMPtr<nsISelection> sel;
00784     GetFrameSelection(aWindow, getter_AddRefs(sel));
00785     NS_ENSURE_ARG_POINTER(sel);
00786 
00787     nsCOMPtr<nsIDOMRange> searchRange (do_CreateInstance(kRangeCID));
00788     NS_ENSURE_ARG_POINTER(searchRange);
00789     nsCOMPtr<nsIDOMRange> startPt (do_CreateInstance(kRangeCID));
00790     NS_ENSURE_ARG_POINTER(startPt);
00791     nsCOMPtr<nsIDOMRange> endPt (do_CreateInstance(kRangeCID));
00792     NS_ENSURE_ARG_POINTER(endPt);
00793 
00794     nsCOMPtr<nsIDOMRange> foundRange;
00795 
00796     // If !aWrapping, search from selection to end
00797     if (!aWrapping)
00798         rv = GetSearchLimits(searchRange, startPt, endPt, domDoc, sel,
00799                              PR_FALSE);
00800 
00801     // If aWrapping, search the part of the starting frame
00802     // up to the point where we left off.
00803     else
00804         rv = GetSearchLimits(searchRange, startPt, endPt, domDoc, sel,
00805                              PR_TRUE);
00806 
00807     NS_ENSURE_SUCCESS(rv, rv);
00808 
00809     rv =  mFind->Find(mSearchString.get(), searchRange, startPt, endPt,
00810                       getter_AddRefs(foundRange));
00811 
00812     if (NS_SUCCEEDED(rv) && foundRange)
00813     {
00814         *aDidFind = PR_TRUE;
00815         sel->RemoveAllRanges();
00816         SetSelectionAndScroll(aWindow, foundRange);
00817     }
00818 
00819     return rv;
00820 }
00821 
00822 
00823 // called when we start searching a frame that is not the initial
00824 // focussed frame. Prepare the frame to be searched.
00825 // we clear the selection, so that the search starts from the top
00826 // of the frame.
00827 nsresult nsWebBrowserFind::OnStartSearchFrame(nsIDOMWindow *aWindow)
00828 {
00829     return ClearFrameSelection(aWindow);
00830 }
00831 
00832 // called when we are done searching a frame and didn't find anything,
00833 // and about about to start searching the next frame.
00834 nsresult nsWebBrowserFind::OnEndSearchFrame(nsIDOMWindow *aWindow)
00835 {
00836     return NS_OK;
00837 }
00838 
00839 void
00840 nsWebBrowserFind::GetFrameSelection(nsIDOMWindow* aWindow, 
00841                                     nsISelection** aSel)
00842 {
00843   *aSel = nsnull;
00844 
00845   nsCOMPtr<nsIDOMDocument> domDoc;    
00846   aWindow->GetDocument(getter_AddRefs(domDoc));
00847   if (!domDoc) return;
00848 
00849   nsCOMPtr<nsIDocument> doc(do_QueryInterface(domDoc));
00850   nsIPresShell* presShell = doc->GetShellAt(0);
00851   if (!presShell) return;
00852 
00853   // text input controls have their independent selection controllers
00854   // that we must use when they have focus.
00855   nsPresContext *presContext = presShell->GetPresContext();
00856 
00857   nsIFrame *frame = nsnull;
00858   presContext->EventStateManager()->GetFocusedFrame(&frame);
00859   if (!frame) {
00860     nsCOMPtr<nsPIDOMWindow> ourWindow = 
00861       do_QueryInterface(doc->GetScriptGlobalObject());
00862     if (ourWindow) {
00863       nsIFocusController *focusController =
00864           ourWindow->GetRootFocusController();
00865       if (focusController) {
00866         nsCOMPtr<nsIDOMElement> focusedElement;
00867         focusController->GetFocusedElement(getter_AddRefs(focusedElement));
00868         if (focusedElement) {
00869             nsCOMPtr<nsIContent> content(do_QueryInterface(focusedElement));
00870             presShell->GetPrimaryFrameFor(content, &frame);
00871         }
00872       }
00873     }
00874   }
00875 
00876   nsCOMPtr<nsISelectionController> selCon;
00877   if (frame) {
00878     frame->GetSelectionController(presContext, getter_AddRefs(selCon));
00879     selCon->GetSelection(nsISelectionController::SELECTION_NORMAL, aSel);
00880     if (*aSel) {
00881       PRInt32 count = -1;
00882       (*aSel)->GetRangeCount(&count);
00883       if (count > 0) {
00884         return;
00885       }
00886       NS_RELEASE(*aSel);
00887     }
00888   }
00889 
00890   selCon = do_QueryInterface(presShell);
00891   selCon->GetSelection(nsISelectionController::SELECTION_NORMAL, aSel);
00892 }
00893 
00894 nsresult nsWebBrowserFind::ClearFrameSelection(nsIDOMWindow *aWindow)
00895 {
00896     NS_ENSURE_ARG(aWindow);
00897     nsCOMPtr<nsISelection> selection;
00898     GetFrameSelection(aWindow, getter_AddRefs(selection));
00899     if (selection)
00900         selection->RemoveAllRanges();
00901     
00902     return NS_OK;
00903 }
00904 
00905 nsresult nsWebBrowserFind::OnFind(nsIDOMWindow *aFoundWindow)
00906 {
00907     SetCurrentSearchFrame(aFoundWindow);
00908 
00909     // We don't want a selection to appear in two frames simultaneously
00910     nsCOMPtr<nsIDOMWindow> lastFocusedWindow = do_QueryReferent(mLastFocusedWindow);
00911     if (lastFocusedWindow && lastFocusedWindow != aFoundWindow)
00912         ClearFrameSelection(lastFocusedWindow);
00913 
00914     // focus the frame we found in
00915     nsCOMPtr<nsPIDOMWindow> ourWindow = do_QueryInterface(aFoundWindow);
00916     nsIFocusController *focusController = nsnull;
00917     if (ourWindow)
00918         focusController = ourWindow->GetRootFocusController();
00919     if (focusController)
00920     {
00921         nsCOMPtr<nsIDOMWindowInternal> windowInt = do_QueryInterface(aFoundWindow);
00922         focusController->SetFocusedWindow(windowInt);
00923         mLastFocusedWindow = do_GetWeakReference(aFoundWindow);
00924     }
00925 
00926     return NS_OK;
00927 }
00928 
00929 /*---------------------------------------------------------------------------
00930 
00931   GetDocShellFromWindow
00932 
00933   Utility method. This will always return nsnull if no docShell
00934   is returned. Oh why isn't there a better way to do this?
00935 ----------------------------------------------------------------------------*/
00936 nsIDocShell *
00937 nsWebBrowserFind::GetDocShellFromWindow(nsIDOMWindow *inWindow)
00938 {
00939   nsCOMPtr<nsIScriptGlobalObject> scriptGO(do_QueryInterface(inWindow));
00940   if (!scriptGO) return nsnull;
00941 
00942   return scriptGO->GetDocShell();
00943 }
00944