зеркало из https://github.com/mozilla/gecko-dev.git
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:
Родитель
41123bf49f
Коммит
759d9ab5d3
|
@ -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)
|
||||
{
|
||||
|
|
Загрузка…
Ссылка в новой задаче