Add the much requested support for find in <textarea> and text <input> elements. Also fix a long-standing problem with the mutually-exclusion: selecting something in a text field doesn't clear an earlier selection outside. b=58305, r=akkana, sr=jst

This commit is contained in:
rbs%maths.uq.edu.au 2004-04-15 19:45:46 +00:00
Родитель 41123bf49f
Коммит 759d9ab5d3
6 изменённых файлов: 620 добавлений и 106 удалений

Просмотреть файл

@ -32,6 +32,7 @@ REQUIRES = string \
docshell \
gfx \
layout \
editor \
locale \
content \
widget \

Просмотреть файл

@ -21,6 +21,7 @@
*
* Contributor(s):
* Akkana Peck <akkana@netscape.com>
* Roger B. Sidje <rbs@maths.uq.edu.au> (added find in <textarea> & text <input>)
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
@ -50,6 +51,10 @@
#include "nsISelectionController.h"
#include "nsIPresShell.h"
#include "nsIFrame.h"
#include "nsITextControlFrame.h"
#include "nsIFormControl.h"
#include "nsIEditor.h"
#include "nsIPlaintextEditor.h"
#include "nsIDocument.h"
#include "nsTextFragment.h"
#include "nsString.h"
@ -68,6 +73,379 @@ static NS_DEFINE_CID(kCPreContentIteratorCID, NS_PRECONTENTITERATOR_CID);
static NS_DEFINE_CID(kParserServiceCID, NS_PARSERSERVICE_CID);
static NS_DEFINE_IID(kRangeCID, NS_RANGE_CID);
// -----------------------------------------------------------------------
// nsFindContentIterator is a special iterator that also goes through
// any existing <textarea>'s or text <input>'s editor to lookup the
// anonymous DOM content there.
//
// Details:
// 1) We use two iterators: The "outer-iterator" goes through the
// normal DOM. The "inner-iterator" goes through the anonymous DOM
// inside the editor.
//
// 2) [MaybeSetupInnerIterator] As soon as the outer-iterator's current
// node is changed, a check is made to see if the node is a <textarea> or
// a text <input> node. If so, an inner-iterator is created to lookup the
// anynomous contents of the editor underneath the text control.
//
// 3) When the inner-iterator is created, we position the outer-iterator
// 'after' (or 'before' in backward search) the text control to avoid
// revisiting that control.
//
// 4) As a consequence of searching through text controls, we can be
// called via FindNext with the current selection inside a <textarea>
// or a text <input>. This means that we can be given an initial search
// range that stretches across the anonymous DOM and the normal DOM. To
// cater for this situation, we split the anonymous part into the
// inner-iterator and then reposition the outer-iterator outside.
//
// 5) The implementation assumes that First() and Next() are only called
// in find-forward mode, while Last() and Prev() are used in find-backward.
class nsFindContentIterator : public nsIContentIterator
{
public:
nsFindContentIterator(PRBool aFindBackward)
: mOuterIterator(nsnull)
, mInnerIterator(nsnull)
, mRange(nsnull)
, mStartOuterNode(nsnull)
, mEndOuterNode(nsnull)
, mFindBackward(aFindBackward)
{
}
virtual ~nsFindContentIterator()
{
}
// nsISupports
NS_DECL_ISUPPORTS
// nsIContentIterator
virtual nsresult Init(nsIContent* aRoot)
{
NS_NOTREACHED("internal error");
return NS_ERROR_NOT_IMPLEMENTED;
}
virtual nsresult Init(nsIDOMRange* aRange);
virtual void First();
virtual void Last();
virtual void Next();
virtual void Prev();
virtual nsIContent* GetCurrentNode();
virtual PRBool IsDone();
virtual nsresult PositionAt(nsIContent* aCurNode);
private:
nsCOMPtr<nsIContentIterator> mOuterIterator;
nsCOMPtr<nsIContentIterator> mInnerIterator;
nsCOMPtr<nsIDOMRange> mRange;
nsCOMPtr<nsIDOMNode> mStartOuterNode;
nsCOMPtr<nsIDOMNode> mEndOuterNode;
PRBool mFindBackward;
void Reset();
void MaybeSetupInnerIterator();
void SetupInnerIterator(nsIContent* aContent);
};
NS_IMPL_ISUPPORTS1(nsFindContentIterator, nsIContentIterator)
nsresult
nsFindContentIterator::Init(nsIDOMRange* aRange)
{
if (mFindBackward) {
// Use post-order in the reverse case, so we get parents
// before children in case we want to prevent descending
// into a node.
mOuterIterator = do_CreateInstance(kCContentIteratorCID);
}
else {
// Use pre-order in the forward case, so we get parents
// before children in case we want to prevent descending
// into a node.
mOuterIterator = do_CreateInstance(kCPreContentIteratorCID);
}
NS_ENSURE_ARG_POINTER(mOuterIterator);
// mRange is the search range that we will examine
return aRange->CloneRange(getter_AddRefs(mRange));
}
void
nsFindContentIterator::First()
{
Reset();
mOuterIterator->First();
if (mInnerIterator) {
mInnerIterator->First();
}
}
void
nsFindContentIterator::Last()
{
Reset();
mOuterIterator->Last();
if (mInnerIterator) {
mInnerIterator->Last();
}
}
void
nsFindContentIterator::Next()
{
if (mInnerIterator) {
mInnerIterator->Next();
if (!mInnerIterator->IsDone())
return;
// by construction, mOuterIterator is already on the next node
}
else {
mOuterIterator->Next();
}
MaybeSetupInnerIterator();
if (mInnerIterator) {
mInnerIterator->First();
// finish setup: position mOuterIterator on the actual "next"
// node (this completes its re-init, @see SetupInnerIterator)
mOuterIterator->First();
}
}
void
nsFindContentIterator::Prev()
{
if (mInnerIterator) {
mInnerIterator->Prev();
if (!mInnerIterator->IsDone())
return;
// by construction, mOuterIterator is already on the previous node
}
else {
mOuterIterator->Prev();
}
MaybeSetupInnerIterator();
if (mInnerIterator) {
mInnerIterator->Last();
// finish setup: position mOuterIterator on the actual "previous"
// node (this completes its re-init, @see SetupInnerIterator)
mOuterIterator->Last();
}
}
nsIContent*
nsFindContentIterator::GetCurrentNode()
{
if (mInnerIterator && !mInnerIterator->IsDone()) {
return mInnerIterator->GetCurrentNode();
}
return mOuterIterator->GetCurrentNode();
}
PRBool
nsFindContentIterator::IsDone() {
if (mInnerIterator && !mInnerIterator->IsDone()) {
return PR_FALSE;
}
return mOuterIterator->IsDone();
}
nsresult
nsFindContentIterator::PositionAt(nsIContent* aCurNode)
{
nsresult rv = mOuterIterator->PositionAt(aCurNode);
if (NS_SUCCEEDED(rv)) {
MaybeSetupInnerIterator();
if (mInnerIterator) {
// same rationale as in Next() and Prev() above
if (!mFindForward) {
mInnerIterator->First();
mOuterIterator->First();
}
else {
mInnerIterator->Last();
mOuterIterator->Last();
}
}
}
else if (mInnerIterator) {
rv = mInnerIterator->PositionAt(aCurNode);
}
return rv;
}
void
nsFindContentIterator::Reset()
{
mInnerIterator = nsnull;
mStartOuterNode = nsnull;
mEndOuterNode = nsnull;
// As a consequence of searching through text controls, we may have been
// initialized with a selection inside a <textarea> or a text <input>.
// see if the start node is an anonymous text node inside a text control
nsCOMPtr<nsIDOMNode> startNode;
mRange->GetStartContainer(getter_AddRefs(startNode));
nsCOMPtr<nsIContent> startContent(do_QueryInterface(startNode));
for ( ; startContent; startContent = startContent->GetParent()) {
if (!startContent->IsNativeAnonymous()) {
mStartOuterNode = do_QueryInterface(startContent);
break;
}
}
// see if the end node is an anonymous text node inside a text control
nsCOMPtr<nsIDOMNode> endNode;
mRange->GetEndContainer(getter_AddRefs(endNode));
nsCOMPtr<nsIContent> endContent(do_QueryInterface(endNode));
for ( ; endContent; endContent = endContent->GetParent()) {
if (!endContent->IsNativeAnonymous()) {
mEndOuterNode = do_QueryInterface(endContent);
break;
}
}
mOuterIterator->Init(mRange);
if (!mFindBackward) {
if (mStartOuterNode != startNode) {
// the start node was an anonymous text node
SetupInnerIterator(startContent);
}
}
else if (mEndOuterNode != endNode) {
// the end node was an anonymous text node
SetupInnerIterator(endContent);
}
}
void
nsFindContentIterator::MaybeSetupInnerIterator()
{
mInnerIterator = nsnull;
nsIContent* content = mOuterIterator->GetCurrentNode();
if (!content || !content->IsContentOfType(nsIContent::eHTML_FORM_CONTROL))
return;
nsCOMPtr<nsIFormControl> formControl(do_QueryInterface(content));
PRInt32 controlType = formControl->GetType();
if (controlType != NS_FORM_TEXTAREA &&
controlType != NS_FORM_INPUT_TEXT)
return;
SetupInnerIterator(content);
}
void
nsFindContentIterator::SetupInnerIterator(nsIContent* aContent)
{
NS_ASSERTION(aContent && !aContent->IsNativeAnonymous(), "invalid call");
nsIDocument* doc = aContent->GetDocument();
nsIPresShell* shell = doc ? doc->GetShellAt(0) : nsnull;
if (!shell)
return;
nsIFrame* frame = nsnull;
shell->GetPrimaryFrameFor(aContent, &frame);
if (!frame)
return;
nsITextControlFrame* tcFrame = nsnull;
CallQueryInterface(frame, &tcFrame);
if (!tcFrame)
return;
nsCOMPtr<nsIEditor> editor;
tcFrame->GetEditor(getter_AddRefs(editor));
if (!editor)
return;
// don't mess with disabled input fields
PRUint32 editorFlags = 0;
editor->GetFlags(&editorFlags);
if (editorFlags & nsIPlaintextEditor::eEditorDisabledMask)
return;
nsCOMPtr<nsIDOMElement> rootElement;
editor->GetRootElement(getter_AddRefs(rootElement));
nsCOMPtr<nsIContent> rootContent(do_QueryInterface(rootElement));
// now create the inner-iterator
mInnerIterator = do_CreateInstance(kCPreContentIteratorCID);
if (mInnerIterator) {
nsCOMPtr<nsIDOMNode> node(do_QueryInterface(rootContent));
nsCOMPtr<nsIDOMRange> range(do_CreateInstance(kRangeCID));
range->SelectNodeContents(node);
// fix up the inner bounds, we may have to only lookup a portion
// of the text control if the current node is a boundary point
PRInt32 offset;
nsCOMPtr<nsIDOMNode> outerNode(do_QueryInterface(aContent));
if (outerNode == mStartOuterNode) {
mRange->GetStartOffset(&offset);
mRange->GetStartContainer(getter_AddRefs(node));
range->SetStart(node, offset);
}
if (outerNode == mEndOuterNode) {
mRange->GetEndOffset(&offset);
mRange->GetEndContainer(getter_AddRefs(node));
range->SetEnd(node, offset);
}
// Note: we just init here. We do First() or Last() later.
mInnerIterator->Init(range);
// make sure to place the outer-iterator outside
// the text control so that we don't go there again.
nsresult res;
mRange->CloneRange(getter_AddRefs(range));
if (!mFindBackward) { // find forward
// cut the outer-iterator after the current node
res = range->SetStartAfter(outerNode);
}
else { // find backward
// cut the outer-iterator before the current node
res = range->SetEndBefore(outerNode);
}
if (NS_FAILED(res)) {
// we are done with the outer-iterator, the
// inner-iterator will traverse what we want
range->Collapse(PR_TRUE);
}
// Note: we just re-init here, using the segment of mRange that is
// yet to be visited. Thus when we later do mOuterIterator->First()
// [or mOuterIterator->Last()], we will effectively be on the next
// node [or the previous node] _with respect to_ mRange.
mOuterIterator->Init(range);
}
}
nsresult
NS_NewFindContentIterator(PRBool aFindBackward,
nsIContentIterator** aResult)
{
NS_ENSURE_ARG_POINTER(aResult);
if (!aResult) {
return NS_ERROR_NULL_POINTER;
}
nsFindContentIterator* it = new nsFindContentIterator(aFindBackward);
if (!it) {
return NS_ERROR_OUT_OF_MEMORY;
}
return it->QueryInterface(NS_GET_IID(nsIContentIterator), (void **)aResult);
}
// --------------------------------------------------------------------
// Sure would be nice if we could just get these from somewhere else!
PRInt32 nsFind::sInstanceCount = 0;
nsIAtom* nsFind::sTextAtom = nsnull;
@ -166,27 +544,7 @@ nsFind::InitIterator(nsIDOMRange* aSearchRange)
nsresult rv;
if (!mIterator)
{
if (mFindBackward) {
// Use post-order in the reverse case, so we get parents
// before children in case we want to prevent descending
// into a node.
rv = nsComponentManager::CreateInstance(kCContentIteratorCID,
nsnull,
NS_GET_IID(nsIContentIterator),
getter_AddRefs(mIterator));
}
else {
// Use pre-order in the forward case, so we get parents
// before children in case we want to prevent descending
// into a node.
rv = nsComponentManager::CreateInstance(kCPreContentIteratorCID,
nsnull,
NS_GET_IID(nsIContentIterator),
getter_AddRefs(mIterator));
}
rv = NS_NewFindContentIterator(mFindBackward, getter_AddRefs(mIterator));
NS_ENSURE_SUCCESS(rv, rv);
NS_ENSURE_ARG_POINTER(mIterator);
}
@ -197,7 +555,8 @@ nsFind::InitIterator(nsIDOMRange* aSearchRange)
printf("InitIterator search range:\n"); DumpRange(aSearchRange);
#endif
mIterator->Init(aSearchRange);
rv = mIterator->Init(aSearchRange);
NS_ENSURE_SUCCESS(rv, rv);
if (mFindBackward) {
mIterator->Last();
}
@ -503,8 +862,7 @@ PRBool nsFind::SkipNode(nsIContent* aContent)
(atom == sScriptAtom ||
atom == sCommentAtom ||
atom == sNoframesAtom ||
atom == sSelectAtom ||
atom == sTextareaAtom)));
atom == sSelectAtom)));
#else /* HAVE_BIDI_ITERATOR */
// Temporary: eventually we will have an iterator to do this,
@ -521,11 +879,11 @@ PRBool nsFind::SkipNode(nsIContent* aContent)
(content->IsContentOfType(nsIContent::eHTML) &&
(atom == sScriptAtom ||
atom == sNoframesAtom ||
atom == sSelectAtom ||
atom == sTextareaAtom)))
atom == sSelectAtom)))
{
#ifdef DEBUG_FIND
printf("Skipping node: ");
nsCOMPtr<nsIDOMNode> node (do_QueryInterface(content));
DumpNode(node);
#endif

Просмотреть файл

@ -48,6 +48,8 @@
#include "nsIFocusController.h"
#include "nsISelectionController.h"
#include "nsISelection.h"
#include "nsIFrame.h"
#include "nsITextControlFrame.h"
#include "nsReadableUtils.h"
#include "nsIDOMHTMLElement.h"
#include "nsIDOMHTMLDocument.h"
@ -346,22 +348,109 @@ NS_IMETHODIMP nsWebBrowserFind::SetMatchCase(PRBool aMatchCase)
return NS_OK;
}
void nsWebBrowserFind::SetSelectionAndScroll(nsIDOMRange* aRange,
nsISelectionController *aSelCon)
// Same as the tail-end of nsEventStateManager::FocusElementButNotDocument.
// Used here because nsEventStateManager::MoveFocusToCaret() doesn't
// support text input controls.
static void
FocusElementButNotDocument(nsIDocument* aDocument, nsIContent* aContent)
{
nsCOMPtr<nsIFocusController> focusController;
nsCOMPtr<nsPIDOMWindow> ourWindow =
do_QueryInterface(aDocument->GetScriptGlobalObject());
if (ourWindow)
ourWindow->GetRootFocusController(getter_AddRefs(focusController));
if (!focusController)
return;
// Get previous focus
nsCOMPtr<nsIDOMElement> oldFocusedElement;
focusController->GetFocusedElement(getter_AddRefs(oldFocusedElement));
nsCOMPtr<nsIContent> oldFocusedContent =
do_QueryInterface(oldFocusedElement);
// Notify focus controller of new focus for this document
nsCOMPtr<nsIDOMElement> newFocusedElement(do_QueryInterface(aContent));
focusController->SetFocusedElement(newFocusedElement);
nsIPresShell* presShell = aDocument->GetShellAt(0);
nsCOMPtr<nsIPresContext> presContext;
presShell->GetPresContext(getter_AddRefs(presContext));
nsIEventStateManager* esm = presContext->EventStateManager();
// Temporarily set esm::mCurrentFocus so that esm::GetContentState() tells
// layout system to show focus on this element.
esm->SetFocusedContent(aContent); // Reset back to null at the end.
aDocument->BeginUpdate(UPDATE_CONTENT_STATE);
aDocument->ContentStatesChanged(oldFocusedContent, aContent,
NS_EVENT_STATE_FOCUS);
aDocument->EndUpdate(UPDATE_CONTENT_STATE);
// Reset esm::mCurrentFocus = nsnull for this doc, so when this document
// does get focus next time via preHandleEvent() NS_GOTFOCUS,
// the old document gets blurred
esm->SetFocusedContent(nsnull);
}
void nsWebBrowserFind::SetSelectionAndScroll(nsIDOMWindow* aWindow,
nsIDOMRange* aRange)
{
nsCOMPtr<nsIDOMDocument> domDoc;
aWindow->GetDocument(getter_AddRefs(domDoc));
if (!domDoc) return;
nsCOMPtr<nsIDocument> doc(do_QueryInterface(domDoc));
nsIPresShell* presShell = doc->GetShellAt(0);
if (!presShell) return;
// since the match could be an anonymous textnode inside a
// <textarea> or text <input>, we need to get the outer frame
nsIFrame *frame = nsnull;
nsITextControlFrame *tcFrame = nsnull;
nsCOMPtr<nsIDOMNode> node;
aRange->GetStartContainer(getter_AddRefs(node));
nsCOMPtr<nsIContent> content(do_QueryInterface(node));
for ( ; content; content = content->GetParent()) {
if (!content->IsNativeAnonymous()) {
presShell->GetPrimaryFrameFor(content, &frame);
if (!frame)
return;
CallQueryInterface(frame, &tcFrame);
break;
}
}
nsCOMPtr<nsISelection> selection;
if (!aSelCon) return;
nsCOMPtr<nsISelectionController> selCon;
if (!tcFrame) {
selCon = do_QueryInterface(presShell);
}
else {
tcFrame->GetSelectionContr(getter_AddRefs(selCon));
}
aSelCon->GetSelection(nsISelectionController::SELECTION_NORMAL,
getter_AddRefs(selection));
if (!selection) return;
selection->RemoveAllRanges();
selection->AddRange(aRange);
selCon->SetDisplaySelection(nsISelectionController::SELECTION_ON);
selCon->GetSelection(nsISelectionController::SELECTION_NORMAL,
getter_AddRefs(selection));
if (selection) {
selection->RemoveAllRanges();
selection->AddRange(aRange);
// Scroll if necessary to make the selection visible:
selCon->ScrollSelectionIntoView
(nsISelectionController::SELECTION_NORMAL,
nsISelectionController::SELECTION_FOCUS_REGION, PR_TRUE);
// Scroll if necessary to make the selection visible:
aSelCon->ScrollSelectionIntoView
(nsISelectionController::SELECTION_NORMAL,
nsISelectionController::SELECTION_FOCUS_REGION, PR_TRUE);
if (tcFrame) {
FocusElementButNotDocument(doc, content);
}
else {
nsCOMPtr<nsIPresContext> presContext;
presShell->GetPresContext(getter_AddRefs(presContext));
PRBool isSelectionWithFocus;
presContext->EventStateManager()->
MoveFocusToCaret(PR_TRUE, &isSelectionWithFocus);
}
}
}
// Adapted from nsTextServicesDocument::GetDocumentContentRootNode
@ -435,18 +524,14 @@ nsWebBrowserFind::GetSearchLimits(nsIDOMRange* aSearchRange,
nsIDOMRange* aStartPt,
nsIDOMRange* aEndPt,
nsIDOMDocument* aDoc,
nsISelectionController* aSelCon,
nsISelection* aSel,
PRBool aWrap)
{
NS_ENSURE_ARG_POINTER(aSelCon);
nsCOMPtr<nsISelection> selection;
aSelCon->GetSelection(nsISelectionController::SELECTION_NORMAL,
getter_AddRefs(selection));
if (!selection) return NS_ERROR_BASE;
NS_ENSURE_ARG_POINTER(aSel);
// There is a selection.
PRInt32 count = -1;
nsresult rv = selection->GetRangeCount(&count);
nsresult rv = aSel->GetRangeCount(&count);
if (count < 1)
return SetRangeAroundDocument(aSearchRange, aStartPt, aEndPt, aDoc);
@ -470,7 +555,7 @@ nsWebBrowserFind::GetSearchLimits(nsIDOMRange* aSearchRange,
{
// This isn't quite right, since the selection's ranges aren't
// necessarily in order; but they usually will be.
selection->GetRangeAt(count-1, getter_AddRefs(range));
aSel->GetRangeAt(count-1, getter_AddRefs(range));
if (!range) return NS_ERROR_UNEXPECTED;
range->GetEndContainer(getter_AddRefs(node));
if (!node) return NS_ERROR_UNEXPECTED;
@ -486,7 +571,7 @@ nsWebBrowserFind::GetSearchLimits(nsIDOMRange* aSearchRange,
// Backward, not wrapping: DocStart to SelStart
else if (mFindBackwards && !aWrap)
{
selection->GetRangeAt(0, getter_AddRefs(range));
aSel->GetRangeAt(0, getter_AddRefs(range));
if (!range) return NS_ERROR_UNEXPECTED;
range->GetStartContainer(getter_AddRefs(node));
if (!node) return NS_ERROR_UNEXPECTED;
@ -502,7 +587,7 @@ nsWebBrowserFind::GetSearchLimits(nsIDOMRange* aSearchRange,
// Forward, wrapping: DocStart to SelEnd
else if (!mFindBackwards && aWrap)
{
selection->GetRangeAt(count-1, getter_AddRefs(range));
aSel->GetRangeAt(count-1, getter_AddRefs(range));
if (!range) return NS_ERROR_UNEXPECTED;
range->GetEndContainer(getter_AddRefs(node));
if (!node) return NS_ERROR_UNEXPECTED;
@ -518,7 +603,7 @@ nsWebBrowserFind::GetSearchLimits(nsIDOMRange* aSearchRange,
// Backward, wrapping: SelStart to DocEnd
else if (mFindBackwards && aWrap)
{
selection->GetRangeAt(0, getter_AddRefs(range));
aSel->GetRangeAt(0, getter_AddRefs(range));
if (!range) return NS_ERROR_UNEXPECTED;
range->GetStartContainer(getter_AddRefs(node));
if (!node) return NS_ERROR_UNEXPECTED;
@ -614,26 +699,6 @@ NS_IMETHODIMP nsWebBrowserFind::SetSearchParentFrames(PRBool aSearchParentFrames
return NS_OK;
}
void nsWebBrowserFind::MoveFocusToCaret(nsIDOMWindow *aWindow)
{
nsIDocShell *docShell = GetDocShellFromWindow(aWindow);
if (!docShell)
return;
nsCOMPtr<nsIPresShell> presShell;
docShell->GetPresShell(getter_AddRefs(presShell));
if (!presShell)
return;
nsCOMPtr<nsIPresContext> presContext;
presShell->GetPresContext(getter_AddRefs(presContext));
if (!presContext)
return;
PRBool isSelectionWithFocus;
presContext->EventStateManager()->MoveFocusToCaret(PR_TRUE,
&isSelectionWithFocus);
}
/*
This method handles finding in a single window (aka frame).
@ -691,8 +756,9 @@ nsresult nsWebBrowserFind::SearchInFrame(nsIDOMWindow* aWindow,
// XXX Make and set a line breaker here, once that's implemented.
(void) mFind->SetWordBreaker(0);
nsCOMPtr<nsISelectionController> selCon (do_QueryInterface(presShell));
NS_ENSURE_ARG_POINTER(selCon);
nsCOMPtr<nsISelection> sel;
GetFrameSelection(aWindow, getter_AddRefs(sel));
NS_ENSURE_ARG_POINTER(sel);
nsCOMPtr<nsIDOMRange> searchRange (do_CreateInstance(kRangeCID));
NS_ENSURE_ARG_POINTER(searchRange);
@ -705,13 +771,13 @@ nsresult nsWebBrowserFind::SearchInFrame(nsIDOMWindow* aWindow,
// If !aWrapping, search from selection to end
if (!aWrapping)
rv = GetSearchLimits(searchRange, startPt, endPt, domDoc, selCon,
rv = GetSearchLimits(searchRange, startPt, endPt, domDoc, sel,
PR_FALSE);
// If aWrapping, search the part of the starting frame
// up to the point where we left off.
else
rv = GetSearchLimits(searchRange, startPt, endPt, domDoc, selCon,
rv = GetSearchLimits(searchRange, startPt, endPt, domDoc, sel,
PR_TRUE);
NS_ENSURE_SUCCESS(rv, rv);
@ -722,12 +788,10 @@ nsresult nsWebBrowserFind::SearchInFrame(nsIDOMWindow* aWindow,
if (NS_SUCCEEDED(rv) && foundRange)
{
*aDidFind = PR_TRUE;
SetSelectionAndScroll(foundRange, selCon);
sel->RemoveAllRanges();
SetSelectionAndScroll(aWindow, foundRange);
}
if (*aDidFind)
MoveFocusToCaret(aWindow);
return rv;
}
@ -748,11 +812,58 @@ nsresult nsWebBrowserFind::OnEndSearchFrame(nsIDOMWindow *aWindow)
return NS_OK;
}
void
nsWebBrowserFind::GetFrameSelection(nsIDOMWindow* aWindow,
nsISelection** aSel)
{
*aSel = nsnull;
nsCOMPtr<nsIDOMDocument> domDoc;
aWindow->GetDocument(getter_AddRefs(domDoc));
if (!domDoc) return;
nsCOMPtr<nsIDocument> doc(do_QueryInterface(domDoc));
nsIPresShell* presShell = doc->GetShellAt(0);
if (!presShell) return;
// text input controls have their independent selection controllers
// that we must use when they have focus.
nsCOMPtr<nsIPresContext> presContext;
presShell->GetPresContext(getter_AddRefs(presContext));
nsIFrame *frame = nsnull;
presContext->EventStateManager()->GetFocusedFrame(&frame);
if (!frame) {
nsCOMPtr<nsPIDOMWindow> ourWindow =
do_QueryInterface(doc->GetScriptGlobalObject());
if (ourWindow) {
nsCOMPtr<nsIFocusController> focusController;
ourWindow->GetRootFocusController(getter_AddRefs(focusController));
if (focusController) {
nsCOMPtr<nsIDOMElement> focusedElement;
focusController->GetFocusedElement(getter_AddRefs(focusedElement));
nsCOMPtr<nsIContent> content(do_QueryInterface(focusedElement));
presShell->GetPrimaryFrameFor(content, &frame);
}
}
}
nsCOMPtr<nsISelectionController> selCon;
if (!frame) {
selCon = do_QueryInterface(presShell);
}
else {
frame->GetSelectionController(presContext, getter_AddRefs(selCon));
}
selCon->GetSelection(nsISelectionController::SELECTION_NORMAL, aSel);
}
nsresult nsWebBrowserFind::ClearFrameSelection(nsIDOMWindow *aWindow)
{
NS_ENSURE_ARG(aWindow);
nsCOMPtr<nsISelection> selection;
aWindow->GetSelection(getter_AddRefs(selection));
GetFrameSelection(aWindow, getter_AddRefs(selection));
if (selection)
selection->RemoveAllRanges();

Просмотреть файл

@ -39,7 +39,7 @@
#define NS_WEB_BROWSER_FIND_CID \
{0x57cf9383, 0x3405, 0x11d5, {0xbe, 0x5b, 0xaa, 0x20, 0xfa, 0x2c, 0xf3, 0x7c}}
class nsISelectionController;
class nsISelection;
class nsIDOMWindow;
class nsIDocShell;
@ -70,28 +70,28 @@ protected:
PRBool CanFindNext()
{ return mSearchString.Length() != 0; }
void MoveFocusToCaret(nsIDOMWindow *aWindow);
nsresult SearchInFrame(nsIDOMWindow* aWindow, PRBool aWrapping,
PRBool* didFind);
nsresult OnStartSearchFrame(nsIDOMWindow *aWindow);
nsresult OnEndSearchFrame(nsIDOMWindow *aWindow);
void GetFrameSelection(nsIDOMWindow* aWindow, nsISelection** aSel);
nsresult ClearFrameSelection(nsIDOMWindow *aWindow);
nsresult OnFind(nsIDOMWindow *aFoundWindow);
nsIDocShell *GetDocShellFromWindow(nsIDOMWindow *inWindow);
void SetSelectionAndScroll(nsIDOMRange* aRange,
nsISelectionController* aSelCon);
void SetSelectionAndScroll(nsIDOMWindow* aWindow,
nsIDOMRange* aRange);
nsresult GetRootNode(nsIDOMDocument* aDomDoc, nsIDOMNode** aNode);
nsresult GetSearchLimits(nsIDOMRange* aRange,
nsIDOMRange* aStartPt,
nsIDOMRange* aEndPt,
nsIDOMDocument* aDoc,
nsISelectionController* aSelCon,
nsISelection* aSel,
PRBool aWrap);
nsresult SetRangeAroundDocument(nsIDOMRange* aSearchRange,
nsIDOMRange* aStartPoint,

Просмотреть файл

@ -585,16 +585,6 @@ nsTextInputSelectionImpl::SetCaretEnabled(PRBool enabled)
nsCOMPtr<nsIPresShell> shell = do_QueryReferent(mPresShellWeak);
if (!shell) return NS_ERROR_FAILURE;
// first, tell the caret which selection to use
nsCOMPtr<nsISelection> domSel;
GetSelection(nsISelectionController::SELECTION_NORMAL, getter_AddRefs(domSel));
if (!domSel) return NS_ERROR_FAILURE;
nsCOMPtr<nsICaret> caret;
shell->GetCaret(getter_AddRefs(caret));
if (!caret) return NS_OK;
caret->SetCaretDOMSelection(domSel);
// tell the pres shell to enable the caret, rather than settings its visibility directly.
// this way the presShell's idea of caret visibility is maintained.
@ -2086,7 +2076,39 @@ nsTextControlFrame::GetFormControlType() const
return nsFormControlHelper::GetType(mContent);
}
void nsTextControlFrame::SetFocus(PRBool aOn , PRBool aRepaint){}
void nsTextControlFrame::SetFocus(PRBool aOn, PRBool aRepaint)
{
if (!aOn || !mSelCon)
return;
// tell the caret to use our selection
nsCOMPtr<nsISelection> ourSel;
mSelCon->GetSelection(nsISelectionController::SELECTION_NORMAL,
getter_AddRefs(ourSel));
if (!ourSel) return;
nsIPresShell* presShell = GetPresContext()->GetPresShell();
nsCOMPtr<nsICaret> caret;
presShell->GetCaret(getter_AddRefs(caret));
if (!caret) return;
caret->SetCaretDOMSelection(ourSel);
// mutual-exclusion: the selection is either controlled by the
// document or by the text input/area. Clear any selection in the
// document since the focus is now on our independent selection.
nsCOMPtr<nsISelectionController> selCon(do_QueryInterface(presShell));
nsCOMPtr<nsISelection> docSel;
selCon->GetSelection(nsISelectionController::SELECTION_NORMAL,
getter_AddRefs(docSel));
if (!docSel) return;
PRBool isCollapsed = PR_FALSE;
docSel->GetIsCollapsed(&isCollapsed);
if (!isCollapsed)
docSel->RemoveAllRanges();
}
void nsTextControlFrame::ScrollIntoView(nsIPresContext* aPresContext)
{

Просмотреть файл

@ -585,16 +585,6 @@ nsTextInputSelectionImpl::SetCaretEnabled(PRBool enabled)
nsCOMPtr<nsIPresShell> shell = do_QueryReferent(mPresShellWeak);
if (!shell) return NS_ERROR_FAILURE;
// first, tell the caret which selection to use
nsCOMPtr<nsISelection> domSel;
GetSelection(nsISelectionController::SELECTION_NORMAL, getter_AddRefs(domSel));
if (!domSel) return NS_ERROR_FAILURE;
nsCOMPtr<nsICaret> caret;
shell->GetCaret(getter_AddRefs(caret));
if (!caret) return NS_OK;
caret->SetCaretDOMSelection(domSel);
// tell the pres shell to enable the caret, rather than settings its visibility directly.
// this way the presShell's idea of caret visibility is maintained.
@ -2086,7 +2076,39 @@ nsTextControlFrame::GetFormControlType() const
return nsFormControlHelper::GetType(mContent);
}
void nsTextControlFrame::SetFocus(PRBool aOn , PRBool aRepaint){}
void nsTextControlFrame::SetFocus(PRBool aOn, PRBool aRepaint)
{
if (!aOn || !mSelCon)
return;
// tell the caret to use our selection
nsCOMPtr<nsISelection> ourSel;
mSelCon->GetSelection(nsISelectionController::SELECTION_NORMAL,
getter_AddRefs(ourSel));
if (!ourSel) return;
nsIPresShell* presShell = GetPresContext()->GetPresShell();
nsCOMPtr<nsICaret> caret;
presShell->GetCaret(getter_AddRefs(caret));
if (!caret) return;
caret->SetCaretDOMSelection(ourSel);
// mutual-exclusion: the selection is either controlled by the
// document or by the text input/area. Clear any selection in the
// document since the focus is now on our independent selection.
nsCOMPtr<nsISelectionController> selCon(do_QueryInterface(presShell));
nsCOMPtr<nsISelection> docSel;
selCon->GetSelection(nsISelectionController::SELECTION_NORMAL,
getter_AddRefs(docSel));
if (!docSel) return;
PRBool isCollapsed = PR_FALSE;
docSel->GetIsCollapsed(&isCollapsed);
if (!isCollapsed)
docSel->RemoveAllRanges();
}
void nsTextControlFrame::ScrollIntoView(nsIPresContext* aPresContext)
{