michael@0: /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- michael@0: * michael@0: * This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "nsWebBrowserFind.h" michael@0: michael@0: // Only need this for NS_FIND_CONTRACTID, michael@0: // else we could use nsIDOMRange.h and nsIFind.h. michael@0: #include "nsFind.h" michael@0: michael@0: #include "nsIComponentManager.h" michael@0: #include "nsIScriptSecurityManager.h" michael@0: #include "nsIInterfaceRequestor.h" michael@0: #include "nsIInterfaceRequestorUtils.h" michael@0: #include "nsPIDOMWindow.h" michael@0: #include "nsIURI.h" michael@0: #include "nsIDocShell.h" michael@0: #include "nsIPresShell.h" michael@0: #include "nsPresContext.h" michael@0: #include "nsIDocument.h" michael@0: #include "nsIDOMDocument.h" michael@0: #include "nsISelectionController.h" michael@0: #include "nsISelection.h" michael@0: #include "nsIFrame.h" michael@0: #include "nsITextControlFrame.h" michael@0: #include "nsReadableUtils.h" michael@0: #include "nsIDOMHTMLElement.h" michael@0: #include "nsIDOMHTMLDocument.h" michael@0: #include "nsIContent.h" michael@0: #include "nsContentCID.h" michael@0: #include "nsIServiceManager.h" michael@0: #include "nsIObserverService.h" michael@0: #include "nsISupportsPrimitives.h" michael@0: #include "nsFind.h" michael@0: #include "nsError.h" michael@0: #include "nsFocusManager.h" michael@0: #include "mozilla/Services.h" michael@0: #include "mozilla/dom/Element.h" michael@0: #include "nsISimpleEnumerator.h" michael@0: michael@0: #if DEBUG michael@0: #include "nsIWebNavigation.h" michael@0: #include "nsXPIDLString.h" michael@0: #endif michael@0: michael@0: //***************************************************************************** michael@0: // nsWebBrowserFind michael@0: //***************************************************************************** michael@0: michael@0: nsWebBrowserFind::nsWebBrowserFind() : michael@0: mFindBackwards(false), michael@0: mWrapFind(false), michael@0: mEntireWord(false), michael@0: mMatchCase(false), michael@0: mSearchSubFrames(true), michael@0: mSearchParentFrames(true) michael@0: { michael@0: } michael@0: michael@0: nsWebBrowserFind::~nsWebBrowserFind() michael@0: { michael@0: } michael@0: michael@0: NS_IMPL_ISUPPORTS(nsWebBrowserFind, nsIWebBrowserFind, nsIWebBrowserFindInFrames) michael@0: michael@0: michael@0: /* boolean findNext (); */ michael@0: NS_IMETHODIMP nsWebBrowserFind::FindNext(bool *outDidFind) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(outDidFind); michael@0: *outDidFind = false; michael@0: michael@0: NS_ENSURE_TRUE(CanFindNext(), NS_ERROR_NOT_INITIALIZED); michael@0: michael@0: nsresult rv = NS_OK; michael@0: nsCOMPtr searchFrame = do_QueryReferent(mCurrentSearchFrame); michael@0: NS_ENSURE_TRUE(searchFrame, NS_ERROR_NOT_INITIALIZED); michael@0: michael@0: nsCOMPtr rootFrame = do_QueryReferent(mRootSearchFrame); michael@0: NS_ENSURE_TRUE(rootFrame, NS_ERROR_NOT_INITIALIZED); michael@0: michael@0: // first, if there's a "cmd_findagain" observer around, check to see if it michael@0: // wants to perform the find again command . If it performs the find again michael@0: // it will return true, in which case we exit ::FindNext() early. michael@0: // Otherwise, nsWebBrowserFind needs to perform the find again command itself michael@0: // this is used by nsTypeAheadFind, which controls find again when it was michael@0: // the last executed find in the current window. michael@0: nsCOMPtr observerSvc = michael@0: mozilla::services::GetObserverService(); michael@0: if (observerSvc) { michael@0: nsCOMPtr windowSupportsData = michael@0: do_CreateInstance(NS_SUPPORTS_INTERFACE_POINTER_CONTRACTID, &rv); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: nsCOMPtr searchWindowSupports = michael@0: do_QueryInterface(rootFrame); michael@0: windowSupportsData->SetData(searchWindowSupports); michael@0: NS_NAMED_LITERAL_STRING(dnStr, "down"); michael@0: NS_NAMED_LITERAL_STRING(upStr, "up"); michael@0: observerSvc->NotifyObservers(windowSupportsData, michael@0: "nsWebBrowserFind_FindAgain", michael@0: mFindBackwards? upStr.get(): dnStr.get()); michael@0: windowSupportsData->GetData(getter_AddRefs(searchWindowSupports)); michael@0: // findnext performed if search window data cleared out michael@0: *outDidFind = searchWindowSupports == nullptr; michael@0: if (*outDidFind) michael@0: return NS_OK; michael@0: } michael@0: michael@0: // next, look in the current frame. If found, return. michael@0: michael@0: // Beware! This may flush notifications via synchronous michael@0: // ScrollSelectionIntoView. michael@0: rv = SearchInFrame(searchFrame, false, outDidFind); michael@0: if (NS_FAILED(rv)) return rv; michael@0: if (*outDidFind) michael@0: return OnFind(searchFrame); // we are done michael@0: michael@0: // if we are not searching other frames, return michael@0: if (!mSearchSubFrames && !mSearchParentFrames) michael@0: return NS_OK; michael@0: michael@0: nsIDocShell *rootDocShell = GetDocShellFromWindow(rootFrame); michael@0: if (!rootDocShell) return NS_ERROR_FAILURE; michael@0: michael@0: int32_t enumDirection; michael@0: if (mFindBackwards) michael@0: enumDirection = nsIDocShell::ENUMERATE_BACKWARDS; michael@0: else michael@0: enumDirection = nsIDocShell::ENUMERATE_FORWARDS; michael@0: michael@0: nsCOMPtr docShellEnumerator; michael@0: rv = rootDocShell->GetDocShellEnumerator(nsIDocShellTreeItem::typeAll, michael@0: enumDirection, getter_AddRefs(docShellEnumerator)); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: // remember where we started michael@0: nsCOMPtr startingItem = michael@0: do_QueryInterface(GetDocShellFromWindow(searchFrame), &rv); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: nsCOMPtr curItem; michael@0: michael@0: // XXX We should avoid searching in frameset documents here. michael@0: // We also need to honour mSearchSubFrames and mSearchParentFrames. michael@0: bool hasMore, doFind = false; michael@0: while (NS_SUCCEEDED(docShellEnumerator->HasMoreElements(&hasMore)) && hasMore) michael@0: { michael@0: nsCOMPtr curSupports; michael@0: rv = docShellEnumerator->GetNext(getter_AddRefs(curSupports)); michael@0: if (NS_FAILED(rv)) break; michael@0: curItem = do_QueryInterface(curSupports, &rv); michael@0: if (NS_FAILED(rv)) break; michael@0: michael@0: if (doFind) michael@0: { michael@0: searchFrame = do_GetInterface(curItem, &rv); michael@0: if (NS_FAILED(rv)) break; michael@0: michael@0: OnStartSearchFrame(searchFrame); michael@0: michael@0: // Beware! This may flush notifications via synchronous michael@0: // ScrollSelectionIntoView. michael@0: rv = SearchInFrame(searchFrame, false, outDidFind); michael@0: if (NS_FAILED(rv)) return rv; michael@0: if (*outDidFind) michael@0: return OnFind(searchFrame); // we are done michael@0: michael@0: OnEndSearchFrame(searchFrame); michael@0: } michael@0: michael@0: if (curItem.get() == startingItem.get()) michael@0: doFind = true; // start looking in frames after this one michael@0: }; michael@0: michael@0: if (!mWrapFind) michael@0: { michael@0: // remember where we left off michael@0: SetCurrentSearchFrame(searchFrame); michael@0: return NS_OK; michael@0: } michael@0: michael@0: // From here on, we're wrapping, first through the other frames, michael@0: // then finally from the beginning of the starting frame back to michael@0: // the starting point. michael@0: michael@0: // because nsISimpleEnumerator is totally lame and isn't resettable, I michael@0: // have to make a new one michael@0: docShellEnumerator = nullptr; michael@0: rv = rootDocShell->GetDocShellEnumerator(nsIDocShellTreeItem::typeAll, michael@0: enumDirection, getter_AddRefs(docShellEnumerator)); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: while (NS_SUCCEEDED(docShellEnumerator->HasMoreElements(&hasMore)) && hasMore) michael@0: { michael@0: nsCOMPtr curSupports; michael@0: rv = docShellEnumerator->GetNext(getter_AddRefs(curSupports)); michael@0: if (NS_FAILED(rv)) break; michael@0: curItem = do_QueryInterface(curSupports, &rv); michael@0: if (NS_FAILED(rv)) break; michael@0: michael@0: searchFrame = do_GetInterface(curItem, &rv); michael@0: if (NS_FAILED(rv)) break; michael@0: michael@0: if (curItem.get() == startingItem.get()) michael@0: { michael@0: // Beware! This may flush notifications via synchronous michael@0: // ScrollSelectionIntoView. michael@0: rv = SearchInFrame(searchFrame, true, outDidFind); michael@0: if (NS_FAILED(rv)) return rv; michael@0: if (*outDidFind) michael@0: return OnFind(searchFrame); // we are done michael@0: break; michael@0: } michael@0: michael@0: OnStartSearchFrame(searchFrame); michael@0: michael@0: // Beware! This may flush notifications via synchronous michael@0: // ScrollSelectionIntoView. michael@0: rv = SearchInFrame(searchFrame, false, outDidFind); michael@0: if (NS_FAILED(rv)) return rv; michael@0: if (*outDidFind) michael@0: return OnFind(searchFrame); // we are done michael@0: michael@0: OnEndSearchFrame(searchFrame); michael@0: } michael@0: michael@0: // remember where we left off michael@0: SetCurrentSearchFrame(searchFrame); michael@0: michael@0: NS_ASSERTION(NS_SUCCEEDED(rv), "Something failed"); michael@0: return rv; michael@0: } michael@0: michael@0: michael@0: /* attribute wstring searchString; */ michael@0: NS_IMETHODIMP nsWebBrowserFind::GetSearchString(char16_t * *aSearchString) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(aSearchString); michael@0: *aSearchString = ToNewUnicode(mSearchString); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP nsWebBrowserFind::SetSearchString(const char16_t * aSearchString) michael@0: { michael@0: mSearchString.Assign(aSearchString); michael@0: return NS_OK; michael@0: } michael@0: michael@0: /* attribute boolean findBackwards; */ michael@0: NS_IMETHODIMP nsWebBrowserFind::GetFindBackwards(bool *aFindBackwards) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(aFindBackwards); michael@0: *aFindBackwards = mFindBackwards; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP nsWebBrowserFind::SetFindBackwards(bool aFindBackwards) michael@0: { michael@0: mFindBackwards = aFindBackwards; michael@0: return NS_OK; michael@0: } michael@0: michael@0: /* attribute boolean wrapFind; */ michael@0: NS_IMETHODIMP nsWebBrowserFind::GetWrapFind(bool *aWrapFind) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(aWrapFind); michael@0: *aWrapFind = mWrapFind; michael@0: return NS_OK; michael@0: } michael@0: NS_IMETHODIMP nsWebBrowserFind::SetWrapFind(bool aWrapFind) michael@0: { michael@0: mWrapFind = aWrapFind; michael@0: return NS_OK; michael@0: } michael@0: michael@0: /* attribute boolean entireWord; */ michael@0: NS_IMETHODIMP nsWebBrowserFind::GetEntireWord(bool *aEntireWord) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(aEntireWord); michael@0: *aEntireWord = mEntireWord; michael@0: return NS_OK; michael@0: } michael@0: NS_IMETHODIMP nsWebBrowserFind::SetEntireWord(bool aEntireWord) michael@0: { michael@0: mEntireWord = aEntireWord; michael@0: return NS_OK; michael@0: } michael@0: michael@0: /* attribute boolean matchCase; */ michael@0: NS_IMETHODIMP nsWebBrowserFind::GetMatchCase(bool *aMatchCase) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(aMatchCase); michael@0: *aMatchCase = mMatchCase; michael@0: return NS_OK; michael@0: } michael@0: NS_IMETHODIMP nsWebBrowserFind::SetMatchCase(bool aMatchCase) michael@0: { michael@0: mMatchCase = aMatchCase; michael@0: return NS_OK; michael@0: } michael@0: michael@0: static bool michael@0: IsInNativeAnonymousSubtree(nsIContent* aContent) michael@0: { michael@0: while (aContent) { michael@0: nsIContent* bindingParent = aContent->GetBindingParent(); michael@0: if (bindingParent == aContent) { michael@0: return true; michael@0: } michael@0: michael@0: aContent = bindingParent; michael@0: } michael@0: michael@0: return false; michael@0: } michael@0: michael@0: void nsWebBrowserFind::SetSelectionAndScroll(nsIDOMWindow* aWindow, michael@0: nsIDOMRange* aRange) michael@0: { michael@0: nsCOMPtr domDoc; michael@0: aWindow->GetDocument(getter_AddRefs(domDoc)); michael@0: if (!domDoc) return; michael@0: michael@0: nsCOMPtr doc(do_QueryInterface(domDoc)); michael@0: nsIPresShell* presShell = doc->GetShell(); michael@0: if (!presShell) return; michael@0: michael@0: nsCOMPtr node; michael@0: aRange->GetStartContainer(getter_AddRefs(node)); michael@0: nsCOMPtr content(do_QueryInterface(node)); michael@0: nsIFrame* frame = content->GetPrimaryFrame(); michael@0: if (!frame) michael@0: return; michael@0: nsCOMPtr selCon; michael@0: frame->GetSelectionController(presShell->GetPresContext(), michael@0: getter_AddRefs(selCon)); michael@0: michael@0: // since the match could be an anonymous textnode inside a michael@0: //