The current mozilla::ScrollAxis struct in PresShellForwards.h isn't a simple axis enum. It carries information about where to scroll to along a specific axis, and under what conditions that should happen. So 'ScrollAxis' isn't the best name. Now we're introducing WebIDL 'ScrollAxis' in bug 1350457: https://drafts.csswg.org/scroll-animations-1/#scrolltimeline-interface The extensive use of PresShellForwards.h's ScrollAxis in DOM code makes it conflict with this new DOM enum. Rather than try to fix this through lots of explitic namespacing, this patch renames PresShellForwards.h's ScrollAxis. Differential Revision: https://phabricator.services.mozilla.com/D293485
737 lines
22 KiB
C++
737 lines
22 KiB
C++
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
#include "nsWebBrowserFind.h"
|
|
|
|
// Only need this for NS_FIND_CONTRACTID,
|
|
// else we could use nsRange.h and nsIFind.h.
|
|
#include "nsFind.h"
|
|
|
|
#include "mozilla/dom/ScriptSettings.h"
|
|
#include "nsIInterfaceRequestor.h"
|
|
#include "nsIInterfaceRequestorUtils.h"
|
|
#include "nsPIDOMWindow.h"
|
|
#include "nsIDocShell.h"
|
|
#include "nsPresContext.h"
|
|
#include "mozilla/dom/Document.h"
|
|
#include "nsISelectionController.h"
|
|
#include "nsIFrame.h"
|
|
#include "nsReadableUtils.h"
|
|
#include "nsIContentInlines.h"
|
|
#include "nsIObserverService.h"
|
|
#include "nsISupportsPrimitives.h"
|
|
#include "nsFind.h"
|
|
#include "nsError.h"
|
|
#include "nsFocusManager.h"
|
|
#include "nsRange.h"
|
|
#include "mozilla/PresShell.h"
|
|
#include "mozilla/Services.h"
|
|
#include "mozilla/dom/Element.h"
|
|
#include "mozilla/dom/Selection.h"
|
|
#include "nsComponentManagerUtils.h"
|
|
#include "nsContentUtils.h"
|
|
#include "nsGenericHTMLElement.h"
|
|
|
|
#if DEBUG
|
|
# include "nsIWebNavigation.h"
|
|
# include "nsString.h"
|
|
#endif
|
|
|
|
using namespace mozilla;
|
|
using mozilla::dom::Document;
|
|
using mozilla::dom::Element;
|
|
using mozilla::dom::Selection;
|
|
|
|
nsWebBrowserFind::nsWebBrowserFind()
|
|
: mFindBackwards(false),
|
|
mWrapFind(false),
|
|
mEntireWord(false),
|
|
mMatchCase(false),
|
|
mMatchDiacritics(false),
|
|
mSearchSubFrames(true),
|
|
mSearchParentFrames(true) {}
|
|
|
|
nsWebBrowserFind::~nsWebBrowserFind() = default;
|
|
|
|
NS_IMPL_ISUPPORTS(nsWebBrowserFind, nsIWebBrowserFind,
|
|
nsIWebBrowserFindInFrames)
|
|
|
|
NS_IMETHODIMP
|
|
nsWebBrowserFind::FindNext(bool* aResult) {
|
|
NS_ENSURE_ARG_POINTER(aResult);
|
|
*aResult = false;
|
|
|
|
NS_ENSURE_TRUE(CanFindNext(), NS_ERROR_NOT_INITIALIZED);
|
|
|
|
nsresult rv = NS_OK;
|
|
nsCOMPtr<nsPIDOMWindowOuter> searchFrame =
|
|
do_QueryReferent(mCurrentSearchFrame);
|
|
NS_ENSURE_TRUE(searchFrame, NS_ERROR_NOT_INITIALIZED);
|
|
|
|
nsCOMPtr<nsPIDOMWindowOuter> rootFrame = do_QueryReferent(mRootSearchFrame);
|
|
NS_ENSURE_TRUE(rootFrame, NS_ERROR_NOT_INITIALIZED);
|
|
|
|
// first, if there's a "cmd_findagain" observer around, check to see if it
|
|
// wants to perform the find again command . If it performs the find again
|
|
// it will return true, in which case we exit ::FindNext() early.
|
|
// Otherwise, nsWebBrowserFind needs to perform the find again command itself
|
|
// this is used by nsTypeAheadFind, which controls find again when it was
|
|
// the last executed find in the current window.
|
|
nsCOMPtr<nsIObserverService> observerSvc =
|
|
mozilla::services::GetObserverService();
|
|
if (observerSvc) {
|
|
nsCOMPtr<nsISupportsInterfacePointer> windowSupportsData =
|
|
do_CreateInstance(NS_SUPPORTS_INTERFACE_POINTER_CONTRACTID, &rv);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
nsCOMPtr<nsISupports> searchWindowSupports = do_QueryInterface(rootFrame);
|
|
windowSupportsData->SetData(searchWindowSupports);
|
|
observerSvc->NotifyObservers(windowSupportsData,
|
|
"nsWebBrowserFind_FindAgain",
|
|
mFindBackwards ? u"up" : u"down");
|
|
windowSupportsData->GetData(getter_AddRefs(searchWindowSupports));
|
|
// findnext performed if search window data cleared out
|
|
*aResult = searchWindowSupports == nullptr;
|
|
if (*aResult) {
|
|
return NS_OK;
|
|
}
|
|
}
|
|
|
|
// next, look in the current frame. If found, return.
|
|
|
|
// Beware! This may flush notifications via synchronous
|
|
// ScrollSelectionIntoView.
|
|
rv = SearchInFrame(searchFrame, false, aResult);
|
|
if (NS_FAILED(rv)) {
|
|
return rv;
|
|
}
|
|
if (*aResult) {
|
|
return OnFind(searchFrame); // we are done
|
|
}
|
|
|
|
// if we are not searching other frames, return
|
|
if (!mSearchSubFrames && !mSearchParentFrames) {
|
|
return NS_OK;
|
|
}
|
|
|
|
nsIDocShell* rootDocShell = rootFrame->GetDocShell();
|
|
if (!rootDocShell) {
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
auto enumDirection = mFindBackwards ? nsIDocShell::ENUMERATE_BACKWARDS
|
|
: nsIDocShell::ENUMERATE_FORWARDS;
|
|
|
|
nsTArray<RefPtr<nsIDocShell>> docShells;
|
|
rv = rootDocShell->GetAllDocShellsInSubtree(nsIDocShellTreeItem::typeAll,
|
|
enumDirection, docShells);
|
|
if (NS_FAILED(rv)) {
|
|
return rv;
|
|
}
|
|
|
|
// remember where we started
|
|
nsCOMPtr<nsIDocShellTreeItem> startingItem = searchFrame->GetDocShell();
|
|
|
|
// XXX We should avoid searching in frameset documents here.
|
|
// We also need to honour mSearchSubFrames and mSearchParentFrames.
|
|
bool doFind = false;
|
|
for (const auto& curItem : docShells) {
|
|
if (doFind) {
|
|
searchFrame = curItem->GetWindow();
|
|
if (!searchFrame) {
|
|
break;
|
|
}
|
|
|
|
OnStartSearchFrame(searchFrame);
|
|
|
|
// Beware! This may flush notifications via synchronous
|
|
// ScrollSelectionIntoView.
|
|
rv = SearchInFrame(searchFrame, false, aResult);
|
|
if (NS_FAILED(rv)) {
|
|
return rv;
|
|
}
|
|
if (*aResult) {
|
|
return OnFind(searchFrame); // we are done
|
|
}
|
|
|
|
OnEndSearchFrame(searchFrame);
|
|
}
|
|
|
|
if (curItem.get() == startingItem.get()) {
|
|
doFind = true; // start looking in frames after this one
|
|
}
|
|
}
|
|
|
|
if (!mWrapFind) {
|
|
// remember where we left off
|
|
SetCurrentSearchFrame(searchFrame);
|
|
return NS_OK;
|
|
}
|
|
|
|
// From here on, we're wrapping, first through the other frames, then finally
|
|
// from the beginning of the starting frame back to the starting point.
|
|
|
|
// because nsISimpleEnumerator is bad and isn't resettable, I have to
|
|
// make a new one
|
|
rv = rootDocShell->GetAllDocShellsInSubtree(nsIDocShellTreeItem::typeAll,
|
|
enumDirection, docShells);
|
|
if (NS_FAILED(rv)) {
|
|
return rv;
|
|
}
|
|
|
|
for (const auto& curItem : docShells) {
|
|
searchFrame = curItem->GetWindow();
|
|
if (!searchFrame) {
|
|
rv = NS_ERROR_FAILURE;
|
|
break;
|
|
}
|
|
|
|
if (curItem.get() == startingItem.get()) {
|
|
// Beware! This may flush notifications via synchronous
|
|
// ScrollSelectionIntoView.
|
|
rv = SearchInFrame(searchFrame, true, aResult);
|
|
if (NS_FAILED(rv)) {
|
|
return rv;
|
|
}
|
|
if (*aResult) {
|
|
return OnFind(searchFrame); // we are done
|
|
}
|
|
break;
|
|
}
|
|
|
|
OnStartSearchFrame(searchFrame);
|
|
|
|
// Beware! This may flush notifications via synchronous
|
|
// ScrollSelectionIntoView.
|
|
rv = SearchInFrame(searchFrame, false, aResult);
|
|
if (NS_FAILED(rv)) {
|
|
return rv;
|
|
}
|
|
if (*aResult) {
|
|
return OnFind(searchFrame); // we are done
|
|
}
|
|
|
|
OnEndSearchFrame(searchFrame);
|
|
}
|
|
|
|
// remember where we left off
|
|
SetCurrentSearchFrame(searchFrame);
|
|
|
|
NS_ASSERTION(NS_SUCCEEDED(rv), "Something failed");
|
|
return rv;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsWebBrowserFind::GetSearchString(nsAString& aSearchString) {
|
|
aSearchString = mSearchString;
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsWebBrowserFind::SetSearchString(const nsAString& aSearchString) {
|
|
mSearchString = aSearchString;
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsWebBrowserFind::GetFindBackwards(bool* aFindBackwards) {
|
|
NS_ENSURE_ARG_POINTER(aFindBackwards);
|
|
*aFindBackwards = mFindBackwards;
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsWebBrowserFind::SetFindBackwards(bool aFindBackwards) {
|
|
mFindBackwards = aFindBackwards;
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsWebBrowserFind::GetWrapFind(bool* aWrapFind) {
|
|
NS_ENSURE_ARG_POINTER(aWrapFind);
|
|
*aWrapFind = mWrapFind;
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsWebBrowserFind::SetWrapFind(bool aWrapFind) {
|
|
mWrapFind = aWrapFind;
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsWebBrowserFind::GetEntireWord(bool* aEntireWord) {
|
|
NS_ENSURE_ARG_POINTER(aEntireWord);
|
|
*aEntireWord = mEntireWord;
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsWebBrowserFind::SetEntireWord(bool aEntireWord) {
|
|
mEntireWord = aEntireWord;
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsWebBrowserFind::GetMatchCase(bool* aMatchCase) {
|
|
NS_ENSURE_ARG_POINTER(aMatchCase);
|
|
*aMatchCase = mMatchCase;
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsWebBrowserFind::SetMatchCase(bool aMatchCase) {
|
|
mMatchCase = aMatchCase;
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsWebBrowserFind::GetMatchDiacritics(bool* aMatchDiacritics) {
|
|
NS_ENSURE_ARG_POINTER(aMatchDiacritics);
|
|
*aMatchDiacritics = mMatchDiacritics;
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsWebBrowserFind::SetMatchDiacritics(bool aMatchDiacritics) {
|
|
mMatchDiacritics = aMatchDiacritics;
|
|
return NS_OK;
|
|
}
|
|
|
|
already_AddRefed<Selection> nsWebBrowserFind::UpdateSelection(
|
|
nsPIDOMWindowOuter* aWindow, nsRange* aRange) {
|
|
RefPtr<Document> doc = aWindow->GetDoc();
|
|
if (!doc) {
|
|
return nullptr;
|
|
}
|
|
|
|
PresShell* presShell = doc->GetPresShell();
|
|
if (!presShell) {
|
|
return nullptr;
|
|
}
|
|
|
|
nsCOMPtr<nsIContent> content =
|
|
nsIContent::FromNodeOrNull(aRange->GetStartContainer());
|
|
nsIFrame* const frameForStartContainer = content->GetPrimaryFrame();
|
|
if (!frameForStartContainer) {
|
|
return nullptr;
|
|
}
|
|
|
|
// since the match could be an anonymous textnode inside a
|
|
// <textarea> or text <input>, we need to get the outer frame
|
|
nsIFrame* tcFrame = nullptr;
|
|
for (; content; content = content->GetFlattenedTreeParent()) {
|
|
if (!content->IsInNativeAnonymousSubtree()) {
|
|
nsIFrame* f = content->GetPrimaryFrame();
|
|
if (!f) {
|
|
return nullptr;
|
|
}
|
|
if (f->IsTextInputFrame()) {
|
|
tcFrame = f;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
const nsCOMPtr<nsISelectionController> selCon =
|
|
frameForStartContainer->GetSelectionController();
|
|
selCon->SetDisplaySelection(nsISelectionController::SELECTION_ON);
|
|
RefPtr<Selection> selection =
|
|
selCon->GetSelection(nsISelectionController::SELECTION_NORMAL);
|
|
if (!selection) {
|
|
return nullptr;
|
|
}
|
|
selection->RemoveAllRanges(IgnoreErrors());
|
|
selection->AddRangeAndSelectFramesAndNotifyListeners(*aRange, IgnoreErrors());
|
|
|
|
if (RefPtr<nsFocusManager> fm = nsFocusManager::GetFocusManager()) {
|
|
if (tcFrame) {
|
|
RefPtr<Element> newFocusedElement = Element::FromNode(content);
|
|
fm->SetFocus(newFocusedElement, nsIFocusManager::FLAG_NOSCROLL);
|
|
} else {
|
|
RefPtr<Element> result;
|
|
fm->MoveFocus(aWindow, nullptr, nsIFocusManager::MOVEFOCUS_CARET,
|
|
nsIFocusManager::FLAG_NOSCROLL, getter_AddRefs(result));
|
|
}
|
|
}
|
|
return selection.forget();
|
|
}
|
|
|
|
nsresult nsWebBrowserFind::SetRangeAroundDocument(nsRange* aSearchRange,
|
|
nsRange* aStartPt,
|
|
nsRange* aEndPt,
|
|
Document* aDoc) {
|
|
NS_ENSURE_ARG_POINTER(aDoc);
|
|
uint32_t childCount = aDoc->GetChildCount();
|
|
|
|
aSearchRange->SetStart(*aDoc, 0, IgnoreErrors());
|
|
aSearchRange->SetEnd(*aDoc, childCount, IgnoreErrors());
|
|
|
|
aStartPt->SetStart(*aDoc, 0, IgnoreErrors());
|
|
aStartPt->SetEnd(*aDoc, 0, IgnoreErrors());
|
|
aEndPt->SetStart(*aDoc, childCount, IgnoreErrors());
|
|
aEndPt->SetEnd(*aDoc, childCount, IgnoreErrors());
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
// Set the range to go from the end of the current selection to the end of the
|
|
// document (forward), or beginning to beginning (reverse). or around the whole
|
|
// document if there's no selection.
|
|
nsresult nsWebBrowserFind::GetSearchLimits(nsRange* aSearchRange,
|
|
nsRange* aStartPt, nsRange* aEndPt,
|
|
Document* aDoc, Selection* aSel,
|
|
bool aWrap) {
|
|
NS_ENSURE_ARG_POINTER(aSel);
|
|
|
|
// There is a selection.
|
|
const uint32_t rangeCount = aSel->RangeCount();
|
|
if (rangeCount < 1) {
|
|
return SetRangeAroundDocument(aSearchRange, aStartPt, aEndPt, aDoc);
|
|
}
|
|
|
|
NS_ENSURE_ARG_POINTER(aDoc);
|
|
|
|
uint32_t childCount = aDoc->GetChildCount();
|
|
|
|
// There are four possible range endpoints we might use:
|
|
// DocumentStart, SelectionStart, SelectionEnd, DocumentEnd.
|
|
|
|
RefPtr<const nsRange> range;
|
|
nsCOMPtr<nsINode> node;
|
|
uint32_t offset;
|
|
|
|
// Prevent the security checks in nsRange from getting into effect for the
|
|
// purposes of determining the search range. These ranges will never be
|
|
// exposed to content.
|
|
mozilla::dom::AutoNoJSAPI nojsapi;
|
|
|
|
// Forward, not wrapping: SelEnd to DocEnd
|
|
if (!mFindBackwards && !aWrap) {
|
|
// This isn't quite right, since the selection's ranges aren't
|
|
// necessarily in order; but they usually will be.
|
|
range = aSel->GetRangeAt(rangeCount - 1);
|
|
if (!range) {
|
|
return NS_ERROR_UNEXPECTED;
|
|
}
|
|
node = range->GetEndContainer();
|
|
if (!node) {
|
|
return NS_ERROR_UNEXPECTED;
|
|
}
|
|
offset = range->EndOffset();
|
|
|
|
aSearchRange->SetStart(*node, offset, IgnoreErrors());
|
|
aSearchRange->SetEnd(*aDoc, childCount, IgnoreErrors());
|
|
aStartPt->SetStart(*node, offset, IgnoreErrors());
|
|
aStartPt->SetEnd(*node, offset, IgnoreErrors());
|
|
aEndPt->SetStart(*aDoc, childCount, IgnoreErrors());
|
|
aEndPt->SetEnd(*aDoc, childCount, IgnoreErrors());
|
|
}
|
|
// Backward, not wrapping: DocStart to SelStart
|
|
else if (mFindBackwards && !aWrap) {
|
|
range = aSel->GetRangeAt(0);
|
|
if (!range) {
|
|
return NS_ERROR_UNEXPECTED;
|
|
}
|
|
node = range->GetStartContainer();
|
|
if (!node) {
|
|
return NS_ERROR_UNEXPECTED;
|
|
}
|
|
offset = range->StartOffset();
|
|
|
|
aSearchRange->SetStart(*aDoc, 0, IgnoreErrors());
|
|
aSearchRange->SetEnd(*aDoc, childCount, IgnoreErrors());
|
|
aStartPt->SetStart(*aDoc, 0, IgnoreErrors());
|
|
aStartPt->SetEnd(*aDoc, 0, IgnoreErrors());
|
|
aEndPt->SetStart(*node, offset, IgnoreErrors());
|
|
aEndPt->SetEnd(*node, offset, IgnoreErrors());
|
|
}
|
|
// Forward, wrapping: DocStart to SelEnd
|
|
else if (!mFindBackwards && aWrap) {
|
|
range = aSel->GetRangeAt(rangeCount - 1);
|
|
if (!range) {
|
|
return NS_ERROR_UNEXPECTED;
|
|
}
|
|
node = range->GetEndContainer();
|
|
if (!node) {
|
|
return NS_ERROR_UNEXPECTED;
|
|
}
|
|
offset = range->EndOffset();
|
|
|
|
aSearchRange->SetStart(*aDoc, 0, IgnoreErrors());
|
|
aSearchRange->SetEnd(*aDoc, childCount, IgnoreErrors());
|
|
aStartPt->SetStart(*aDoc, 0, IgnoreErrors());
|
|
aStartPt->SetEnd(*aDoc, 0, IgnoreErrors());
|
|
aEndPt->SetStart(*node, offset, IgnoreErrors());
|
|
aEndPt->SetEnd(*node, offset, IgnoreErrors());
|
|
}
|
|
// Backward, wrapping: SelStart to DocEnd
|
|
else if (mFindBackwards && aWrap) {
|
|
range = aSel->GetRangeAt(0);
|
|
if (!range) {
|
|
return NS_ERROR_UNEXPECTED;
|
|
}
|
|
node = range->GetStartContainer();
|
|
if (!node) {
|
|
return NS_ERROR_UNEXPECTED;
|
|
}
|
|
offset = range->StartOffset();
|
|
|
|
aSearchRange->SetStart(*aDoc, 0, IgnoreErrors());
|
|
aSearchRange->SetEnd(*aDoc, childCount, IgnoreErrors());
|
|
aStartPt->SetStart(*node, offset, IgnoreErrors());
|
|
aStartPt->SetEnd(*node, offset, IgnoreErrors());
|
|
aEndPt->SetStart(*aDoc, childCount, IgnoreErrors());
|
|
aEndPt->SetEnd(*aDoc, childCount, IgnoreErrors());
|
|
}
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsWebBrowserFind::GetSearchFrames(bool* aSearchFrames) {
|
|
NS_ENSURE_ARG_POINTER(aSearchFrames);
|
|
// this only returns true if we are searching both sub and parent frames.
|
|
// There is ambiguity if the caller has previously set one, but not both of
|
|
// these.
|
|
*aSearchFrames = mSearchSubFrames && mSearchParentFrames;
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsWebBrowserFind::SetSearchFrames(bool aSearchFrames) {
|
|
mSearchSubFrames = aSearchFrames;
|
|
mSearchParentFrames = aSearchFrames;
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsWebBrowserFind::GetCurrentSearchFrame(
|
|
mozIDOMWindowProxy** aCurrentSearchFrame) {
|
|
NS_ENSURE_ARG_POINTER(aCurrentSearchFrame);
|
|
nsCOMPtr<mozIDOMWindowProxy> searchFrame =
|
|
do_QueryReferent(mCurrentSearchFrame);
|
|
searchFrame.forget(aCurrentSearchFrame);
|
|
return (*aCurrentSearchFrame) ? NS_OK : NS_ERROR_NOT_INITIALIZED;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsWebBrowserFind::SetCurrentSearchFrame(
|
|
mozIDOMWindowProxy* aCurrentSearchFrame) {
|
|
// is it ever valid to set this to null?
|
|
NS_ENSURE_ARG(aCurrentSearchFrame);
|
|
mCurrentSearchFrame = do_GetWeakReference(aCurrentSearchFrame);
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsWebBrowserFind::GetRootSearchFrame(mozIDOMWindowProxy** aRootSearchFrame) {
|
|
NS_ENSURE_ARG_POINTER(aRootSearchFrame);
|
|
nsCOMPtr<mozIDOMWindowProxy> searchFrame = do_QueryReferent(mRootSearchFrame);
|
|
searchFrame.forget(aRootSearchFrame);
|
|
return (*aRootSearchFrame) ? NS_OK : NS_ERROR_NOT_INITIALIZED;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsWebBrowserFind::SetRootSearchFrame(mozIDOMWindowProxy* aRootSearchFrame) {
|
|
// is it ever valid to set this to null?
|
|
NS_ENSURE_ARG(aRootSearchFrame);
|
|
mRootSearchFrame = do_GetWeakReference(aRootSearchFrame);
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsWebBrowserFind::GetSearchSubframes(bool* aSearchSubframes) {
|
|
NS_ENSURE_ARG_POINTER(aSearchSubframes);
|
|
*aSearchSubframes = mSearchSubFrames;
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsWebBrowserFind::SetSearchSubframes(bool aSearchSubframes) {
|
|
mSearchSubFrames = aSearchSubframes;
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsWebBrowserFind::GetSearchParentFrames(bool* aSearchParentFrames) {
|
|
NS_ENSURE_ARG_POINTER(aSearchParentFrames);
|
|
*aSearchParentFrames = mSearchParentFrames;
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsWebBrowserFind::SetSearchParentFrames(bool aSearchParentFrames) {
|
|
mSearchParentFrames = aSearchParentFrames;
|
|
return NS_OK;
|
|
}
|
|
|
|
/*
|
|
This method handles finding in a single window (aka frame).
|
|
|
|
*/
|
|
nsresult nsWebBrowserFind::SearchInFrame(nsPIDOMWindowOuter* aWindow,
|
|
bool aWrapping, bool* aDidFind) {
|
|
NS_ENSURE_ARG(aWindow);
|
|
NS_ENSURE_ARG_POINTER(aDidFind);
|
|
|
|
*aDidFind = false;
|
|
|
|
// Do security check, to ensure that the frame we're searching is
|
|
// accessible from the frame where the Find is being run.
|
|
|
|
// get a uri for the window
|
|
RefPtr<Document> theDoc = aWindow->GetDoc();
|
|
if (!theDoc) {
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
if (!nsContentUtils::SubjectPrincipal()->Subsumes(theDoc->NodePrincipal())) {
|
|
return NS_ERROR_DOM_PROP_ACCESS_DENIED;
|
|
}
|
|
|
|
nsresult rv;
|
|
nsCOMPtr<nsIFind> find = do_CreateInstance(NS_FIND_CONTRACTID, &rv);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
(void)find->SetCaseSensitive(mMatchCase);
|
|
(void)find->SetMatchDiacritics(mMatchDiacritics);
|
|
(void)find->SetFindBackwards(mFindBackwards);
|
|
|
|
(void)find->SetEntireWord(mEntireWord);
|
|
|
|
// Now make sure the content (for actual finding) and frame (for
|
|
// selection) models are up to date.
|
|
theDoc->FlushPendingNotifications(FlushType::Frames);
|
|
|
|
RefPtr<Selection> sel = GetFrameSelection(aWindow);
|
|
NS_ENSURE_ARG_POINTER(sel);
|
|
|
|
RefPtr<nsRange> searchRange = nsRange::Create(theDoc);
|
|
RefPtr<nsRange> startPt = nsRange::Create(theDoc);
|
|
RefPtr<nsRange> endPt = nsRange::Create(theDoc);
|
|
|
|
RefPtr<nsRange> foundRange;
|
|
|
|
rv = GetSearchLimits(searchRange, startPt, endPt, theDoc, sel, aWrapping);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
rv = find->Find(mSearchString, searchRange, startPt, endPt,
|
|
getter_AddRefs(foundRange));
|
|
|
|
if (NS_SUCCEEDED(rv) && foundRange) {
|
|
*aDidFind = true;
|
|
sel->RemoveAllRanges(IgnoreErrors());
|
|
RefPtr<Selection> scrollSelection = UpdateSelection(aWindow, foundRange);
|
|
|
|
NS_DispatchToMainThread(NS_NewRunnableFunction(
|
|
"nsWebBrowserFind::RevealAndScroll",
|
|
[foundRange = RefPtr{foundRange},
|
|
scrollSelection = RefPtr{scrollSelection}]()
|
|
MOZ_CAN_RUN_SCRIPT_BOUNDARY_LAMBDA {
|
|
// Reveal hidden-until-found and closed details elements.
|
|
// https://html.spec.whatwg.org/#interaction-with-details-and-hidden=until-found
|
|
if (RefPtr startNode = foundRange->GetStartContainer()) {
|
|
startNode->AncestorRevealingAlgorithm(IgnoreErrors());
|
|
}
|
|
|
|
// Scroll to make the selection visible
|
|
if (scrollSelection) {
|
|
scrollSelection->ScrollIntoView(
|
|
nsISelectionController::SELECTION_WHOLE_SELECTION,
|
|
AxisScrollParams(WhereToScroll::Center), AxisScrollParams(),
|
|
ScrollFlags::None, SelectionScrollMode::SyncFlush);
|
|
}
|
|
}));
|
|
}
|
|
|
|
return rv;
|
|
}
|
|
|
|
// called when we start searching a frame that is not the initial focussed
|
|
// frame. Prepare the frame to be searched. we clear the selection, so that the
|
|
// search starts from the top of the frame.
|
|
nsresult nsWebBrowserFind::OnStartSearchFrame(nsPIDOMWindowOuter* aWindow) {
|
|
return ClearFrameSelection(aWindow);
|
|
}
|
|
|
|
// called when we are done searching a frame and didn't find anything, and about
|
|
// about to start searching the next frame.
|
|
nsresult nsWebBrowserFind::OnEndSearchFrame(nsPIDOMWindowOuter* aWindow) {
|
|
return NS_OK;
|
|
}
|
|
|
|
already_AddRefed<Selection> nsWebBrowserFind::GetFrameSelection(
|
|
nsPIDOMWindowOuter* aWindow) const {
|
|
MOZ_ASSERT(aWindow);
|
|
|
|
Document* const doc = aWindow->GetDoc();
|
|
if (MOZ_UNLIKELY(!doc)) {
|
|
return nullptr;
|
|
}
|
|
|
|
PresShell* const presShell = doc->GetPresShell();
|
|
if (MOZ_UNLIKELY(!presShell)) {
|
|
return nullptr;
|
|
}
|
|
|
|
nsCOMPtr<nsPIDOMWindowOuter> focusedWindow;
|
|
if (const nsCOMPtr<nsIContent> focusedContent =
|
|
nsFocusManager::GetFocusedDescendant(
|
|
aWindow, nsFocusManager::eOnlyCurrentWindow,
|
|
getter_AddRefs(focusedWindow))) {
|
|
nsIFrame* const focusedFrame = focusedContent->GetPrimaryFrame();
|
|
if (focusedFrame && focusedFrame->PresShell() == presShell) {
|
|
// While a text control has focus, we should use it instead of the
|
|
// selection for the document. nsIFrame::GetSelectionController()
|
|
// returns the independent selection controller if and only if it's a
|
|
// TextControlFrame.
|
|
if (nsISelectionController* const selCon =
|
|
focusedFrame->GetSelectionController()) {
|
|
Selection* const sel =
|
|
selCon->GetSelection(nsISelectionController::SELECTION_NORMAL);
|
|
if (sel && sel->RangeCount() > 0) {
|
|
return do_AddRef(sel);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
RefPtr<Selection> sel =
|
|
presShell->GetSelection(nsISelectionController::SELECTION_NORMAL);
|
|
return sel.forget();
|
|
}
|
|
|
|
nsresult nsWebBrowserFind::ClearFrameSelection(nsPIDOMWindowOuter* aWindow) {
|
|
NS_ENSURE_ARG(aWindow);
|
|
RefPtr<Selection> selection = GetFrameSelection(aWindow);
|
|
if (selection) {
|
|
selection->RemoveAllRanges(IgnoreErrors());
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult nsWebBrowserFind::OnFind(nsPIDOMWindowOuter* aFoundWindow) {
|
|
SetCurrentSearchFrame(aFoundWindow);
|
|
|
|
// We don't want a selection to appear in two frames simultaneously
|
|
nsCOMPtr<nsPIDOMWindowOuter> lastFocusedWindow =
|
|
do_QueryReferent(mLastFocusedWindow);
|
|
if (lastFocusedWindow && lastFocusedWindow != aFoundWindow) {
|
|
ClearFrameSelection(lastFocusedWindow);
|
|
}
|
|
|
|
if (RefPtr<nsFocusManager> fm = nsFocusManager::GetFocusManager()) {
|
|
// get the containing frame and focus it. For top-level windows, the right
|
|
// window should already be focused.
|
|
if (RefPtr<Element> frameElement =
|
|
aFoundWindow->GetFrameElementInternal()) {
|
|
fm->SetFocus(frameElement, 0);
|
|
}
|
|
|
|
mLastFocusedWindow = do_GetWeakReference(aFoundWindow);
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|