Bug 88831 Support new IME API "Text Services Framework" from Office XP and Windows XP r=masayuki+peterv, sr=roc

--HG--
rename : content/events/src/nsQueryContentEventHandler.cpp => content/events/src/nsContentEventHandler.cpp
rename : content/events/src/nsQueryContentEventHandler.h => content/events/src/nsContentEventHandler.h
This commit is contained in:
Jim Chen 2009-02-07 18:52:59 +09:00
parent 06f9f3850c
commit c997843b00
22 changed files with 4305 additions and 159 deletions

View File

@ -118,7 +118,7 @@
#include "nsIDOMNSFeatureFactory.h"
#include "nsIDOMDocumentType.h"
#include "nsIDOMUserDataHandler.h"
#include "nsIDOMNSEditableElement.h"
#include "nsGenericHTMLElement.h"
#include "nsIEditor.h"
#include "nsIEditorDocShell.h"
#include "nsEventDispatcher.h"
@ -343,13 +343,15 @@ nsINode::GetTextEditorRootContent(nsIEditor** aEditor)
if (aEditor)
*aEditor = nsnull;
for (nsINode* node = this; node; node = node->GetNodeParent()) {
nsCOMPtr<nsIDOMNSEditableElement> editableElement(do_QueryInterface(node));
if (!editableElement)
if (!node->IsNodeOfType(eHTML))
continue;
nsCOMPtr<nsIEditor> editor;
editableElement->GetEditor(getter_AddRefs(editor));
NS_ENSURE_TRUE(editor, nsnull);
static_cast<nsGenericHTMLElement*>(node)->
GetEditorInternal(getter_AddRefs(editor));
if (!editor)
continue;
nsIContent* rootContent = GetEditorRootContent(editor);
if (aEditor)
editor.swap(*aEditor);

View File

@ -62,7 +62,9 @@
//---------------------------------------------------------------------------
GK_ATOM(_empty, "")
GK_ATOM(moz, "_moz")
GK_ATOM(mozdirty, "_moz_dirty")
GK_ATOM(mozeditorbogusnode, "_moz_editor_bogus_node")
GK_ATOM(mozgeneratedcontentbefore, "_moz_generated_content_before")
GK_ATOM(mozgeneratedcontentafter, "_moz_generated_content_after")
GK_ATOM(mozgeneratedcontentimage, "_moz_generated_content_image")

View File

@ -91,7 +91,7 @@ CPPSRCS = \
nsPLDOMEvent.cpp \
nsEventDispatcher.cpp \
nsIMEStateManager.cpp \
nsQueryContentEventHandler.cpp \
nsContentEventHandler.cpp \
nsDOMProgressEvent.cpp \
nsDOMDataTransfer.cpp \
nsDOMNotifyPaintEvent.cpp \

View File

@ -22,6 +22,7 @@
*
* Contributor(s):
* Masayuki Nakano <masayuki@d-toybox.com>
* Ningjie Chen <chenn@email.uc.edu>
*
* Alternatively, the contents of this file may be used under the terms of
* either of the GNU General Public License Version 2 or later (the "GPL"),
@ -37,7 +38,7 @@
*
* ***** END LICENSE BLOCK ***** */
#include "nsQueryContentEventHandler.h"
#include "nsContentEventHandler.h"
#include "nsCOMPtr.h"
#include "nsPresContext.h"
#include "nsIPresShell.h"
@ -53,14 +54,19 @@
#include "nsIContentIterator.h"
#include "nsTextFragment.h"
#include "nsTextFrame.h"
#include "nsISelectionController.h"
#include "nsISelectionPrivate.h"
#include "nsContentUtils.h"
#include "nsISelection2.h"
#include "nsIMEStateManager.h"
nsresult NS_NewContentIterator(nsIContentIterator** aInstancePtrResult);
/******************************************************************/
/* nsQueryContentEventHandler */
/* nsContentEventHandler */
/******************************************************************/
nsQueryContentEventHandler::nsQueryContentEventHandler(
nsContentEventHandler::nsContentEventHandler(
nsPresContext* aPresContext) :
mPresContext(aPresContext),
mPresShell(aPresContext->GetPresShell()), mSelection(nsnull),
@ -69,7 +75,7 @@ nsQueryContentEventHandler::nsQueryContentEventHandler(
}
nsresult
nsQueryContentEventHandler::Init(nsQueryContentEvent* aEvent)
nsContentEventHandler::Init(nsQueryContentEvent* aEvent)
{
NS_ASSERTION(aEvent, "aEvent must not be null");
@ -117,6 +123,24 @@ nsQueryContentEventHandler::Init(nsQueryContentEvent* aEvent)
return NS_OK;
}
// Editor places a bogus BR node under its root content if the editor doesn't
// have any text. This happens even for single line editors.
// When we get text content and when we change the selection,
// we don't want to include the bogus BRs at the end.
static PRBool IsContentBR(nsIContent* aContent)
{
return aContent->IsNodeOfType(nsINode::eHTML) &&
aContent->Tag() == nsGkAtoms::br &&
!aContent->AttrValueIs(kNameSpaceID_None,
nsGkAtoms::type,
nsGkAtoms::moz,
eIgnoreCase) &&
!aContent->AttrValueIs(kNameSpaceID_None,
nsGkAtoms::mozeditorbogusnode,
nsGkAtoms::_true,
eIgnoreCase);
}
static void ConvertToNativeNewlines(nsAFlatString& aString)
{
#if defined(XP_MACOSX)
@ -161,8 +185,7 @@ static PRUint32 GetNativeTextLength(nsIContent* aContent)
nsAutoString str;
if (aContent->IsNodeOfType(nsINode::eTEXT))
AppendString(str, aContent);
else if (aContent->IsNodeOfType(nsINode::eHTML) &&
aContent->Tag() == nsGkAtoms::br)
else if (IsContentBR(aContent))
str.Assign(PRUnichar('\n'));
ConvertToNativeNewlines(str);
return str.Length();
@ -181,9 +204,8 @@ static PRUint32 ConvertToXPOffset(nsIContent* aContent, PRUint32 aNativeOffset)
return str.Length();
}
nsresult
nsQueryContentEventHandler::GenerateFlatTextContent(nsIRange* aRange,
nsAFlatString& aString)
static nsresult GenerateFlatTextContent(nsIRange* aRange,
nsAFlatString& aString)
{
nsCOMPtr<nsIContentIterator> iter;
nsresult rv = NS_NewContentIterator(getter_AddRefs(iter));
@ -221,8 +243,7 @@ nsQueryContentEventHandler::GenerateFlatTextContent(nsIRange* aRange,
AppendSubString(aString, content, 0, aRange->EndOffset());
else
AppendString(aString, content);
} else if (content->IsNodeOfType(nsINode::eHTML) &&
content->Tag() == nsGkAtoms::br)
} else if (IsContentBR(content))
aString.Append(PRUnichar('\n'));
}
ConvertToNativeNewlines(aString);
@ -230,18 +251,19 @@ nsQueryContentEventHandler::GenerateFlatTextContent(nsIRange* aRange,
}
nsresult
nsQueryContentEventHandler::ExpandToClusterBoundary(nsIContent* aContent,
nsContentEventHandler::ExpandToClusterBoundary(nsIContent* aContent,
PRBool aForward,
PRUint32* aXPOffset)
{
NS_ASSERTION(*aXPOffset >= 0 && *aXPOffset <= aContent->TextLength(),
"offset is out of range.");
// XXX This method assumes that the frame boundaries must be cluster
// boundaries. It's false, but no problem now, maybe.
if (!aContent->IsNodeOfType(nsINode::eTEXT) ||
*aXPOffset == 0 || *aXPOffset == aContent->TextLength())
return NS_OK;
NS_ASSERTION(*aXPOffset >= 0 && *aXPOffset <= aContent->TextLength(),
"offset is out of range.");
nsCOMPtr<nsFrameSelection> fs = mPresShell->FrameSelection();
PRInt32 offsetInFrame;
nsFrameSelection::HINT hint =
@ -265,7 +287,7 @@ nsQueryContentEventHandler::ExpandToClusterBoundary(nsIContent* aContent,
if (frame->GetType() != nsGkAtoms::textFrame)
return NS_ERROR_FAILURE;
nsTextFrame* textFrame = static_cast<nsTextFrame*>(frame);
PRInt32 newOffsetInFrame = offsetInFrame;
PRInt32 newOffsetInFrame = *aXPOffset - startOffset;
newOffsetInFrame += aForward ? -1 : 1;
textFrame->PeekOffsetCharacter(aForward, &newOffsetInFrame);
*aXPOffset = startOffset + newOffsetInFrame;
@ -273,7 +295,7 @@ nsQueryContentEventHandler::ExpandToClusterBoundary(nsIContent* aContent,
}
nsresult
nsQueryContentEventHandler::SetRangeFromFlatTextOffset(
nsContentEventHandler::SetRangeFromFlatTextOffset(
nsIRange* aRange,
PRUint32 aNativeOffset,
PRUint32 aNativeLength,
@ -369,7 +391,7 @@ nsQueryContentEventHandler::SetRangeFromFlatTextOffset(
}
nsresult
nsQueryContentEventHandler::OnQuerySelectedText(nsQueryContentEvent* aEvent)
nsContentEventHandler::OnQuerySelectedText(nsQueryContentEvent* aEvent)
{
nsresult rv = Init(aEvent);
if (NS_FAILED(rv))
@ -378,21 +400,32 @@ nsQueryContentEventHandler::OnQuerySelectedText(nsQueryContentEvent* aEvent)
NS_ASSERTION(aEvent->mReply.mString.IsEmpty(),
"The reply string must be empty");
rv = GetFlatTextOffsetOfRange(mFirstSelectedRange, &aEvent->mReply.mOffset);
rv = GetFlatTextOffsetOfRange(mRootContent,
mFirstSelectedRange, &aEvent->mReply.mOffset);
NS_ENSURE_SUCCESS(rv, rv);
PRBool isCollapsed;
rv = mSelection->GetIsCollapsed(&isCollapsed);
nsCOMPtr<nsIDOMNode> anchorDomNode, focusDomNode;
rv = mSelection->GetAnchorNode(getter_AddRefs(anchorDomNode));
NS_ENSURE_TRUE(anchorDomNode, NS_ERROR_FAILURE);
rv = mSelection->GetFocusNode(getter_AddRefs(focusDomNode));
NS_ENSURE_TRUE(focusDomNode, NS_ERROR_FAILURE);
PRInt32 anchorOffset, focusOffset;
rv = mSelection->GetAnchorOffset(&anchorOffset);
NS_ENSURE_SUCCESS(rv, rv);
rv = mSelection->GetFocusOffset(&focusOffset);
NS_ENSURE_SUCCESS(rv, rv);
if (!isCollapsed) {
nsCOMPtr<nsIDOMRange> domRange;
rv = mSelection->GetRangeAt(0, getter_AddRefs(domRange));
NS_ENSURE_SUCCESS(rv, rv);
NS_ASSERTION(domRange, "GetRangeAt succeeded, but the result is null");
nsCOMPtr<nsINode> anchorNode(do_QueryInterface(anchorDomNode));
nsCOMPtr<nsINode> focusNode(do_QueryInterface(focusDomNode));
NS_ENSURE_TRUE(anchorNode && focusNode, NS_ERROR_UNEXPECTED);
nsCOMPtr<nsIRange> range(do_QueryInterface(domRange));
NS_ENSURE_TRUE(range, NS_ERROR_FAILURE);
PRInt16 compare = nsContentUtils::ComparePoints(anchorNode, anchorOffset,
focusNode, focusOffset);
aEvent->mReply.mReversed = compare > 0;
if (compare) {
nsCOMPtr<nsIRange> range = mFirstSelectedRange;
rv = GenerateFlatTextContent(range, aEvent->mReply.mString);
NS_ENSURE_SUCCESS(rv, rv);
}
@ -402,7 +435,7 @@ nsQueryContentEventHandler::OnQuerySelectedText(nsQueryContentEvent* aEvent)
}
nsresult
nsQueryContentEventHandler::OnQueryTextContent(nsQueryContentEvent* aEvent)
nsContentEventHandler::OnQueryTextContent(nsQueryContentEvent* aEvent)
{
nsresult rv = Init(aEvent);
if (NS_FAILED(rv))
@ -425,65 +458,168 @@ nsQueryContentEventHandler::OnQueryTextContent(nsQueryContentEvent* aEvent)
return NS_OK;
}
nsresult
nsQueryContentEventHandler::QueryRectFor(nsQueryContentEvent* aEvent,
nsIRange* aRange,
nsCaret* aCaret)
// Adjust to use a child node if possible
// to make the returned rect more accurate
static nsINode* AdjustTextRectNode(nsINode* aNode,
PRInt32& aOffset)
{
PRInt32 offsetInFrame;
nsIFrame* frame;
nsresult rv = GetStartFrameAndOffset(aRange, &frame, &offsetInFrame);
NS_ENSURE_SUCCESS(rv, rv);
nsPoint posInFrame;
rv = frame->GetPointFromOffset(aRange->StartOffset(), &posInFrame);
NS_ENSURE_SUCCESS(rv, rv);
nsRect rect;
rect.y = posInFrame.y;
rect.height = frame->GetSize().height;
if (aEvent->message == NS_QUERY_CHARACTER_RECT) {
nsPoint nextPos;
rv = frame->GetPointFromOffset(aRange->EndOffset(), &nextPos);
NS_ENSURE_SUCCESS(rv, rv);
rect.x = PR_MIN(posInFrame.x, nextPos.x);
rect.width = PR_ABS(posInFrame.x - nextPos.x);
} else {
rect.x = posInFrame.x;
rect.width = aCaret->GetCaretRect().width;
PRInt32 childCount = PRInt32(aNode->GetChildCount());
nsINode* node = aNode;
if (childCount) {
if (aOffset < childCount) {
node = aNode->GetChildAt(aOffset);
aOffset = 0;
} else if (aOffset == childCount) {
node = aNode->GetChildAt(childCount - 1);
aOffset = node->IsNodeOfType(nsINode::eTEXT) ?
static_cast<nsIContent*>(node)->TextLength() : 1;
}
}
return node;
}
rv = ConvertToRootViewRelativeOffset(frame, rect);
NS_ENSURE_SUCCESS(rv, rv);
aEvent->mReply.mRect = nsRect::ToOutsidePixels(rect, mPresContext->AppUnitsPerDevPixel());
aEvent->mSucceeded = PR_TRUE;
return NS_OK;
// Similar to nsFrameSelection::GetFrameForNodeOffset,
// but this is more flexible for OnQueryTextRect to use
static nsresult GetFrameForTextRect(nsIPresShell* aPresShell,
nsINode* aNode,
PRInt32 aOffset,
PRBool aHint,
nsIFrame** aReturnFrame)
{
NS_ENSURE_TRUE(aNode && aNode->IsNodeOfType(nsINode::eCONTENT),
NS_ERROR_UNEXPECTED);
nsIContent* content = static_cast<nsIContent*>(aNode);
nsIFrame* frame = aPresShell->GetPrimaryFrameFor(content);
NS_ENSURE_TRUE(frame, NS_ERROR_FAILURE);
PRInt32 childOffset = 0;
return frame->GetChildFrameContainingOffset(aOffset, aHint, &childOffset,
aReturnFrame);
}
nsresult
nsQueryContentEventHandler::OnQueryCharacterRect(nsQueryContentEvent* aEvent)
nsContentEventHandler::OnQueryTextRect(nsQueryContentEvent* aEvent)
{
nsresult rv = Init(aEvent);
if (NS_FAILED(rv))
return rv;
nsCOMPtr<nsIRange> range = new nsRange();
NS_ENSURE_TRUE(range, NS_ERROR_OUT_OF_MEMORY);
rv = SetRangeFromFlatTextOffset(range, aEvent->mInput.mOffset, 1, PR_TRUE);
nsRefPtr<nsRange> range = new nsRange();
if (!range) {
return NS_ERROR_OUT_OF_MEMORY;
}
rv = SetRangeFromFlatTextOffset(range, aEvent->mInput.mOffset,
aEvent->mInput.mLength, PR_TRUE);
NS_ENSURE_SUCCESS(rv, rv);
if (range->Collapsed()) {
// There is no character at the offset.
return NS_OK;
// used to iterate over all contents and their frames
nsCOMPtr<nsIContentIterator> iter;
rv = NS_NewContentIterator(getter_AddRefs(iter));
NS_ENSURE_SUCCESS(rv, rv);
iter->Init(range);
NS_ENSURE_SUCCESS(rv, rv);
// get the starting frame
PRInt32 offset = range->StartOffset();
nsINode* node = iter->GetCurrentNode();
if (!node) {
node = AdjustTextRectNode(range->GetStartParent(), offset);
}
nsIFrame* firstFrame = nsnull;
rv = GetFrameForTextRect(mPresShell, node, offset,
PR_TRUE, &firstFrame);
NS_ENSURE_SUCCESS(rv, rv);
// get the starting frame rect
nsRect rect(nsPoint(0, 0), firstFrame->GetRect().Size());
rv = ConvertToRootViewRelativeOffset(firstFrame, rect);
NS_ENSURE_SUCCESS(rv, rv);
nsRect frameRect = rect;
nsPoint ptOffset;
firstFrame->GetPointFromOffset(offset, &ptOffset);
// minus 1 to avoid creating an empty rect
rect.x += ptOffset.x - 1;
rect.width -= ptOffset.x - 1;
// get the ending frame
offset = range->EndOffset();
node = AdjustTextRectNode(range->GetEndParent(), offset);
nsIFrame* lastFrame = nsnull;
rv = GetFrameForTextRect(mPresShell, node, offset,
range->Collapsed(), &lastFrame);
NS_ENSURE_SUCCESS(rv, rv);
// iterate over all covered frames
for (nsIFrame* frame = firstFrame; frame != lastFrame;) {
frame = frame->GetNextContinuation();
if (!frame) {
do {
iter->Next();
node = iter->GetCurrentNode();
if (!node || !node->IsNodeOfType(nsINode::eCONTENT))
continue;
frame = mPresShell->GetPrimaryFrameFor(static_cast<nsIContent*>(node));
} while (!frame && !iter->IsDone());
if (!frame) {
// this can happen when the end offset of the range is 0.
frame = lastFrame;
}
}
frameRect.SetRect(nsPoint(0, 0), frame->GetRect().Size());
rv = ConvertToRootViewRelativeOffset(frame, frameRect);
NS_ENSURE_SUCCESS(rv, rv);
if (frame != lastFrame) {
// not last frame, so just add rect to previous result
rect.UnionRect(rect, frameRect);
}
}
return QueryRectFor(aEvent, range, nsnull);
// get the ending frame rect
lastFrame->GetPointFromOffset(offset, &ptOffset);
// minus 1 to avoid creating an empty rect
frameRect.width -= lastFrame->GetRect().width - ptOffset.x - 1;
if (firstFrame == lastFrame) {
rect.IntersectRect(rect, frameRect);
} else {
rect.UnionRect(rect, frameRect);
}
aEvent->mReply.mRect =
nsRect::ToOutsidePixels(rect, mPresContext->AppUnitsPerDevPixel());
aEvent->mSucceeded = PR_TRUE;
return NS_OK;
}
nsresult
nsQueryContentEventHandler::OnQueryCaretRect(nsQueryContentEvent* aEvent)
nsContentEventHandler::OnQueryEditorRect(nsQueryContentEvent* aEvent)
{
nsresult rv = Init(aEvent);
if (NS_FAILED(rv))
return rv;
nsIFrame* frame = mPresShell->GetPrimaryFrameFor(mRootContent);
NS_ENSURE_TRUE(frame, NS_ERROR_FAILURE);
// get rect for first frame
nsRect resultRect(nsPoint(0, 0), frame->GetRect().Size());
rv = ConvertToRootViewRelativeOffset(frame, resultRect);
NS_ENSURE_SUCCESS(rv, rv);
// account for any additional frames
while ((frame = frame->GetNextContinuation()) != nsnull) {
nsRect frameRect(nsPoint(0, 0), frame->GetRect().Size());
rv = ConvertToRootViewRelativeOffset(frame, frameRect);
NS_ENSURE_SUCCESS(rv, rv);
resultRect.UnionRect(resultRect, frameRect);
}
aEvent->mReply.mRect =
nsRect::ToOutsidePixels(resultRect, mPresContext->AppUnitsPerDevPixel());
aEvent->mSucceeded = PR_TRUE;
return NS_OK;
}
nsresult
nsContentEventHandler::OnQueryCaretRect(nsQueryContentEvent* aEvent)
{
nsresult rv = Init(aEvent);
if (NS_FAILED(rv))
@ -502,7 +638,7 @@ nsQueryContentEventHandler::OnQueryCaretRect(nsQueryContentEvent* aEvent)
if (selectionIsCollapsed) {
PRUint32 offset;
rv = GetFlatTextOffsetOfRange(mFirstSelectedRange, &offset);
rv = GetFlatTextOffsetOfRange(mRootContent, mFirstSelectedRange, &offset);
NS_ENSURE_SUCCESS(rv, rv);
if (offset == aEvent->mInput.mOffset) {
PRBool isCollapsed;
@ -510,7 +646,8 @@ nsQueryContentEventHandler::OnQueryCaretRect(nsQueryContentEvent* aEvent)
rv = caret->GetCaretCoordinates(nsCaret::eTopLevelWindowCoordinates,
mSelection, &rect,
&isCollapsed, nsnull);
aEvent->mReply.mRect = nsRect::ToOutsidePixels(rect, mPresContext->AppUnitsPerDevPixel());
aEvent->mReply.mRect =
nsRect::ToOutsidePixels(rect, mPresContext->AppUnitsPerDevPixel());
NS_ENSURE_SUCCESS(rv, rv);
aEvent->mSucceeded = PR_TRUE;
return NS_OK;
@ -523,12 +660,36 @@ nsQueryContentEventHandler::OnQueryCaretRect(nsQueryContentEvent* aEvent)
rv = SetRangeFromFlatTextOffset(range, aEvent->mInput.mOffset, 0, PR_TRUE);
NS_ENSURE_SUCCESS(rv, rv);
return QueryRectFor(aEvent, range, caret);
PRInt32 offsetInFrame;
nsIFrame* frame;
rv = GetStartFrameAndOffset(range, &frame, &offsetInFrame);
NS_ENSURE_SUCCESS(rv, rv);
nsPoint posInFrame;
rv = frame->GetPointFromOffset(range->StartOffset(), &posInFrame);
NS_ENSURE_SUCCESS(rv, rv);
nsRect rect;
rect.x = posInFrame.x;
rect.y = posInFrame.y;
rect.width = caret->GetCaretRect().width;
rect.height = frame->GetSize().height;
rv = ConvertToRootViewRelativeOffset(frame, rect);
NS_ENSURE_SUCCESS(rv, rv);
aEvent->mReply.mRect =
nsRect::ToOutsidePixels(rect, mPresContext->AppUnitsPerDevPixel());
aEvent->mSucceeded = PR_TRUE;
return NS_OK;
}
nsresult
nsQueryContentEventHandler::GetFlatTextOffsetOfRange(nsIRange* aRange,
PRUint32* aNativeOffset)
nsContentEventHandler::GetFlatTextOffsetOfRange(nsIContent* aRootContent,
nsINode* aNode,
PRInt32 aNodeOffset,
PRUint32* aNativeOffset)
{
NS_ASSERTION(aNativeOffset, "param is invalid");
@ -536,16 +697,12 @@ nsQueryContentEventHandler::GetFlatTextOffsetOfRange(nsIRange* aRange,
NS_ENSURE_TRUE(prev, NS_ERROR_OUT_OF_MEMORY);
nsCOMPtr<nsIDOMRange> domPrev(do_QueryInterface(prev));
NS_ASSERTION(domPrev, "nsRange doesn't have nsIDOMRange??");
nsCOMPtr<nsIDOMNode> rootDOMNode(do_QueryInterface(mRootContent));
nsCOMPtr<nsIDOMNode> rootDOMNode(do_QueryInterface(aRootContent));
domPrev->SetStart(rootDOMNode, 0);
nsINode* startNode = aRange->GetStartParent();
NS_ENSURE_TRUE(startNode, NS_ERROR_FAILURE);
PRInt32 startOffset = aRange->StartOffset();
nsCOMPtr<nsIDOMNode> startDOMNode(do_QueryInterface(startNode));
nsCOMPtr<nsIDOMNode> startDOMNode(do_QueryInterface(aNode));
NS_ASSERTION(startDOMNode, "startNode doesn't have nsIDOMNode");
domPrev->SetEnd(startDOMNode, startOffset);
domPrev->SetEnd(startDOMNode, aNodeOffset);
nsAutoString prevStr;
nsresult rv = GenerateFlatTextContent(prev, prevStr);
@ -555,9 +712,21 @@ nsQueryContentEventHandler::GetFlatTextOffsetOfRange(nsIRange* aRange,
}
nsresult
nsQueryContentEventHandler::GetStartFrameAndOffset(nsIRange* aRange,
nsIFrame** aFrame,
PRInt32* aOffsetInFrame)
nsContentEventHandler::GetFlatTextOffsetOfRange(nsIContent* aRootContent,
nsIRange* aRange,
PRUint32* aNativeOffset)
{
nsINode* startNode = aRange->GetStartParent();
NS_ENSURE_TRUE(startNode, NS_ERROR_FAILURE);
PRInt32 startOffset = aRange->StartOffset();
return GetFlatTextOffsetOfRange(aRootContent, startNode, startOffset,
aNativeOffset);
}
nsresult
nsContentEventHandler::GetStartFrameAndOffset(nsIRange* aRange,
nsIFrame** aFrame,
PRInt32* aOffsetInFrame)
{
NS_ASSERTION(aRange && aFrame && aOffsetInFrame, "params are invalid");
@ -577,8 +746,8 @@ nsQueryContentEventHandler::GetStartFrameAndOffset(nsIRange* aRange,
}
nsresult
nsQueryContentEventHandler::ConvertToRootViewRelativeOffset(nsIFrame* aFrame,
nsRect& aRect)
nsContentEventHandler::ConvertToRootViewRelativeOffset(nsIFrame* aFrame,
nsRect& aRect)
{
NS_ASSERTION(aFrame, "aFrame must not be null");
@ -590,3 +759,86 @@ nsQueryContentEventHandler::ConvertToRootViewRelativeOffset(nsIFrame* aFrame,
aRect += posInView + view->GetOffsetTo(nsnull);
return NS_OK;
}
static void AdjustRangeForSelection(nsIContent* aRoot,
nsINode** aNode,
PRInt32* aOffset)
{
nsINode* node = *aNode;
PRInt32 offset = *aOffset;
if (aRoot != node && node->GetParent() &&
!node->IsNodeOfType(nsINode::eTEXT)) {
node = node->GetParent();
offset = node->IndexOf(*aNode) + (offset ? 1 : 0);
}
nsINode* brNode = node->GetChildAt(offset - 1);
while (brNode && brNode->IsNodeOfType(nsINode::eHTML)) {
nsIContent* brContent = static_cast<nsIContent*>(brNode);
if (brContent->Tag() != nsGkAtoms::br || IsContentBR(brContent))
break;
brNode = node->GetChildAt(--offset - 1);
}
*aNode = node;
*aOffset = PR_MAX(offset, 0);
}
nsresult
nsContentEventHandler::OnSelectionEvent(nsSelectionEvent* aEvent)
{
aEvent->mSucceeded = PR_FALSE;
// Get selection to manipulate
nsCOMPtr<nsISelection> sel;
nsresult rv = nsIMEStateManager::
GetFocusSelectionAndRoot(getter_AddRefs(sel),
getter_AddRefs(mRootContent));
NS_ENSURE_SUCCESS(rv, rv);
// Get range from offset and length
nsRefPtr<nsRange> range = new nsRange();
NS_ENSURE_TRUE(range, NS_ERROR_OUT_OF_MEMORY);
rv = SetRangeFromFlatTextOffset(range, aEvent->mOffset,
aEvent->mLength, PR_TRUE);
NS_ENSURE_SUCCESS(rv, rv);
nsINode* startNode = range->GetStartParent();
nsINode* endNode = range->GetEndParent();
PRInt32 startOffset = range->StartOffset();
PRInt32 endOffset = range->EndOffset();
AdjustRangeForSelection(mRootContent, &startNode, &startOffset);
AdjustRangeForSelection(mRootContent, &endNode, &endOffset);
nsCOMPtr<nsIDOMNode> startDomNode(do_QueryInterface(startNode));
nsCOMPtr<nsIDOMNode> endDomNode(do_QueryInterface(endNode));
NS_ENSURE_TRUE(startDomNode && endDomNode, NS_ERROR_UNEXPECTED);
nsCOMPtr<nsISelectionPrivate> selPrivate = do_QueryInterface(sel);
NS_ENSURE_TRUE(selPrivate, NS_ERROR_UNEXPECTED);
selPrivate->StartBatchChanges();
// Clear selection first before setting
rv = sel->RemoveAllRanges();
// Need to call EndBatchChanges at the end even if call failed
if (NS_SUCCEEDED(rv)) {
if (aEvent->mReversed) {
rv = sel->Collapse(endDomNode, endOffset);
} else {
rv = sel->Collapse(startDomNode, startOffset);
}
if (NS_SUCCEEDED(rv) &&
(startDomNode != endDomNode || startOffset != endOffset)) {
if (aEvent->mReversed) {
rv = sel->Extend(startDomNode, startOffset);
} else {
rv = sel->Extend(endDomNode, endOffset);
}
}
}
selPrivate->EndBatchChanges();
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsISelection2>(do_QueryInterface(sel))->ScrollIntoView(
nsISelectionController::SELECTION_FOCUS_REGION, PR_FALSE, -1, -1);
aEvent->mSucceeded = PR_TRUE;
return NS_OK;
}

View File

@ -21,6 +21,7 @@
*
* Contributor(s):
* Masayuki Nakano <masayuki@d-toybox.com>
* Ningjie Chen <chenn@email.uc.edu>
*
* Alternatively, the contents of this file may be used under the terms of
* either of the GNU General Public License Version 2 or later (the "GPL"),
@ -36,8 +37,8 @@
*
* ***** END LICENSE BLOCK ***** */
#ifndef nsQueryContentEventHandler_h__
#define nsQueryContentEventHandler_h__
#ifndef nsContentEventHandler_h__
#define nsContentEventHandler_h__
#include "nscore.h"
#include "nsCOMPtr.h"
@ -50,29 +51,36 @@
class nsPresContext;
class nsIPresShell;
class nsQueryContentEvent;
class nsSelectionEvent;
class nsCaret;
struct nsRect;
/*
* Query Content Event Handler
* nsQueryContentEventHandler is a helper class for nsEventStateManager.
* nsContentEventHandler is a helper class for nsEventStateManager.
* The platforms request some content informations, e.g., the selected text,
* the offset of the selected text and the text for specified range.
* This class answers to NS_QUERY_* events from actual contents.
*/
class NS_STACK_CLASS nsQueryContentEventHandler {
class NS_STACK_CLASS nsContentEventHandler {
public:
nsQueryContentEventHandler(nsPresContext *aPresContext);
nsContentEventHandler(nsPresContext *aPresContext);
// NS_QUERY_SELECTED_TEXT event handler
nsresult OnQuerySelectedText(nsQueryContentEvent* aEvent);
// NS_QUERY_TEXT_CONTENT event handler
nsresult OnQueryTextContent(nsQueryContentEvent* aEvent);
// NS_QUERY_CHARACTER_RECT event handler
nsresult OnQueryCharacterRect(nsQueryContentEvent* aEvent);
// NS_QUERY_CARET_RECT event handler
nsresult OnQueryCaretRect(nsQueryContentEvent* aEvent);
// NS_QUERY_TEXT_RECT event handler
nsresult OnQueryTextRect(nsQueryContentEvent* aEvent);
// NS_QUERY_EDITOR_RECT event handler
nsresult OnQueryEditorRect(nsQueryContentEvent* aEvent);
// NS_SELECTION_* event
nsresult OnSelectionEvent(nsSelectionEvent* aEvent);
protected:
nsPresContext* mPresContext;
nsIPresShell* mPresShell;
@ -82,11 +90,19 @@ protected:
nsresult Init(nsQueryContentEvent* aEvent);
public:
// FlatText means the text that is generated from DOM tree. The BR elements
// are replaced to native linefeeds. Other elements are ignored.
// Generate the FlatText from DOM range.
nsresult GenerateFlatTextContent(nsIRange* aRange, nsAFlatString& aString);
// Get the offset in FlatText of the range. (also used by nsIMEStateManager)
static nsresult GetFlatTextOffsetOfRange(nsIContent* aRootContent,
nsINode* aNode,
PRInt32 aNodeOffset,
PRUint32* aOffset);
static nsresult GetFlatTextOffsetOfRange(nsIContent* aRootContent,
nsIRange* aRange,
PRUint32* aOffset);
protected:
// Make the DOM range from the offset of FlatText and the text length.
// If aExpandToClusterBoundaries is true, the start offset and the end one are
// expanded to nearest cluster boundaries.
@ -94,22 +110,18 @@ protected:
PRUint32 aNativeOffset,
PRUint32 aNativeLength,
PRBool aExpandToClusterBoundaries);
// Get the offset in FlatText of the range.
nsresult GetFlatTextOffsetOfRange(nsIRange* aRange, PRUint32* aOffset);
// Find the first textframe for the range, and get the start offset in
// the frame.
nsresult GetStartFrameAndOffset(nsIRange* aRange,
nsIFrame** aFrame, PRInt32* aOffsetInFrame);
nsIFrame** aFrame,
PRInt32* aOffsetInFrame);
// Convert the frame relative offset to the root view relative offset.
nsresult ConvertToRootViewRelativeOffset(nsIFrame* aFrame, nsRect& aRect);
// The helper for OnQueryCharacterRect/OnQueryCaretRect.
// Don't call for another event.
nsresult QueryRectFor(nsQueryContentEvent* aEvent, nsIRange* aRange,
nsCaret* aCaret);
nsresult ConvertToRootViewRelativeOffset(nsIFrame* aFrame,
nsRect& aRect);
// Expand aXPOffset to the nearest offset in cluster boundary. aForward is
// true, it is expanded to forward.
nsresult ExpandToClusterBoundary(nsIContent* aContent, PRBool aForward,
PRUint32* aXPOffset);
};
#endif // nsQueryContentEventHandler_h__
#endif // nsContentEventHandler_h__

View File

@ -28,6 +28,7 @@
* Ginn Chen <ginn.chen@sun.com>
* Simon Bünzli <zeniko@gmail.com>
* Ehsan Akhgari <ehsan.akhgari@gmail.com>
* Ningjie Chen <chenn@email.uc.edu>
*
* Alternatively, the contents of this file may be used under the terms of
* either of the GNU General Public License Version 2 or later (the "GPL"),
@ -47,7 +48,7 @@
#include "nsEventStateManager.h"
#include "nsEventListenerManager.h"
#include "nsIMEStateManager.h"
#include "nsQueryContentEventHandler.h"
#include "nsContentEventHandler.h"
#include "nsIContent.h"
#include "nsINodeInfo.h"
#include "nsIDocument.h"
@ -959,6 +960,8 @@ nsEventStateManager::PreHandleEvent(nsPresContext* aPresContext,
break;
if (mDocument) {
nsIMEStateManager::OnTextStateBlur(mPresContext, mCurrentFocus);
if (gLastFocusedDocument && gLastFocusedPresContextWeak) {
nsCOMPtr<nsPIDOMWindow> ourWindow =
gLastFocusedDocument->GetWindow();
@ -1076,6 +1079,8 @@ nsEventStateManager::PreHandleEvent(nsPresContext* aPresContext,
NS_IF_RELEASE(gLastFocusedContent);
gLastFocusedContent = mCurrentFocus;
NS_IF_ADDREF(gLastFocusedContent);
nsIMEStateManager::OnTextStateFocus(mPresContext, mCurrentFocus);
}
// Try to keep the focus controllers and the globals in synch
@ -1147,6 +1152,8 @@ nsEventStateManager::PreHandleEvent(nsPresContext* aPresContext,
NS_RELEASE(gLastFocusedContent);
}
nsIMEStateManager::OnTextStateBlur(nsnull, nsnull);
// Now fire blurs. We fire a blur on the focused document, element,
// and window.
@ -1320,6 +1327,8 @@ nsEventStateManager::PreHandleEvent(nsPresContext* aPresContext,
if (focusController)
focusController->SetSuppressFocus(PR_TRUE, "Deactivate Suppression");
nsIMEStateManager::OnTextStateBlur(nsnull, nsnull);
// Now fire blurs. Blur the content, then the document, then the window.
if (gLastFocusedDocument && gLastFocusedDocument == mDocument &&
@ -1494,28 +1503,40 @@ nsEventStateManager::PreHandleEvent(nsPresContext* aPresContext,
break;
case NS_QUERY_SELECTED_TEXT:
{
nsQueryContentEventHandler handler(mPresContext);
nsContentEventHandler handler(mPresContext);
handler.OnQuerySelectedText((nsQueryContentEvent*)aEvent);
}
break;
case NS_QUERY_TEXT_CONTENT:
{
nsQueryContentEventHandler handler(mPresContext);
nsContentEventHandler handler(mPresContext);
handler.OnQueryTextContent((nsQueryContentEvent*)aEvent);
}
break;
case NS_QUERY_CHARACTER_RECT:
{
nsQueryContentEventHandler handler(mPresContext);
handler.OnQueryCharacterRect((nsQueryContentEvent*)aEvent);
}
break;
case NS_QUERY_CARET_RECT:
{
nsQueryContentEventHandler handler(mPresContext);
nsContentEventHandler handler(mPresContext);
handler.OnQueryCaretRect((nsQueryContentEvent*)aEvent);
}
break;
case NS_QUERY_TEXT_RECT:
{
nsContentEventHandler handler(mPresContext);
handler.OnQueryTextRect((nsQueryContentEvent*)aEvent);
}
break;
case NS_QUERY_EDITOR_RECT:
{
nsContentEventHandler handler(mPresContext);
handler.OnQueryEditorRect((nsQueryContentEvent*)aEvent);
}
break;
case NS_SELECTION_SET:
{
nsContentEventHandler handler(mPresContext);
handler.OnSelectionEvent((nsSelectionEvent*)aEvent);
}
break;
}
return NS_OK;
}
@ -5031,6 +5052,8 @@ nsEventStateManager::SendFocusBlur(nsPresContext* aPresContext,
// Track the old focus controller if any focus suppressions is used on it.
nsFocusSuppressor oldFocusSuppressor;
nsIMEStateManager::OnTextStateBlur(aPresContext, aContent);
if (nsnull != gLastFocusedPresContextWeak) {
nsCOMPtr<nsIContent> focusAfterBlur;
@ -5261,6 +5284,8 @@ nsEventStateManager::SendFocusBlur(nsPresContext* aPresContext,
if (clearFirstFocusEvent) {
mFirstFocusEvent = nsnull;
}
nsIMEStateManager::OnTextStateFocus(mPresContext, mCurrentFocus);
} else if (!aContent) {
//fire focus on document even if the content isn't focusable (ie. text)
//see bugzilla bug 93521

View File

@ -22,6 +22,7 @@
*
* Contributor(s):
* Masayuki Nakano <masayuki@d-toybox.com>
* Ningjie Chen <chenn@email.uc.edu>
*
* Alternatively, the contents of this file may be used under the terms of
* either of the GNU General Public License Version 2 or later (the "GPL"),
@ -53,6 +54,16 @@
#include "nsIFocusController.h"
#include "nsIDOMWindow.h"
#include "nsContentUtils.h"
#include "nsINode.h"
#include "nsIFrame.h"
#include "nsRange.h"
#include "nsIDOMRange.h"
#include "nsISelection.h"
#include "nsISelectionPrivate.h"
#include "nsISelectionListener.h"
#include "nsISelectionController.h"
#include "nsIMutationObserver.h"
#include "nsContentEventHandler.h"
/******************************************************************/
/* nsIMEStateManager */
@ -63,6 +74,8 @@ nsPresContext* nsIMEStateManager::sPresContext = nsnull;
nsPIDOMWindow* nsIMEStateManager::sActiveWindow = nsnull;
PRBool nsIMEStateManager::sInstalledMenuKeyboardListener = PR_FALSE;
nsTextStateManager* nsIMEStateManager::sTextStateObserver = nsnull;
nsresult
nsIMEStateManager::OnDestroyPresContext(nsPresContext* aPresContext)
{
@ -71,6 +84,7 @@ nsIMEStateManager::OnDestroyPresContext(nsPresContext* aPresContext)
return NS_OK;
sContent = nsnull;
sPresContext = nsnull;
OnTextStateBlur(nsnull, nsnull);
return NS_OK;
}
@ -269,3 +283,301 @@ nsIMEStateManager::GetWidget(nsPresContext* aPresContext)
return widget;
}
// nsTextStateManager notifies widget of any text and selection changes
// in the currently focused editor
// sTextStateObserver points to the currently active nsTextStateManager
// sTextStateObserver is null if there is no focused editor
class nsTextStateManager : public nsISelectionListener,
public nsStubMutationObserver
{
public:
nsTextStateManager();
NS_DECL_ISUPPORTS
NS_DECL_NSISELECTIONLISTENER
NS_DECL_NSIMUTATIONOBSERVER_CHARACTERDATACHANGED
NS_DECL_NSIMUTATIONOBSERVER_CONTENTAPPENDED
NS_DECL_NSIMUTATIONOBSERVER_CONTENTINSERTED
NS_DECL_NSIMUTATIONOBSERVER_CONTENTREMOVED
nsresult Init(nsIWidget* aWidget,
nsPresContext* aPresContext,
nsINode* aNode);
void Destroy(void);
nsCOMPtr<nsIWidget> mWidget;
nsCOMPtr<nsISelection> mSel;
nsCOMPtr<nsIContent> mRootContent;
nsCOMPtr<nsINode> mEditableNode;
PRBool mDestroying;
private:
void NotifyContentAdded(nsINode* aContainer, PRInt32 aStart, PRInt32 aEnd);
};
nsTextStateManager::nsTextStateManager()
{
mDestroying = PR_FALSE;
}
nsresult
nsTextStateManager::Init(nsIWidget* aWidget,
nsPresContext* aPresContext,
nsINode* aNode)
{
mWidget = aWidget;
nsIPresShell* presShell = aPresContext->PresShell();
// get selection and root content
nsCOMPtr<nsISelectionController> selCon;
if (aNode->IsNodeOfType(nsINode::eCONTENT)) {
nsIFrame* frame = presShell->GetPrimaryFrameFor(
static_cast<nsIContent*>(aNode));
NS_ENSURE_TRUE(frame, NS_ERROR_UNEXPECTED);
frame->GetSelectionController(aPresContext,
getter_AddRefs(selCon));
} else {
// aNode is a document
selCon = do_QueryInterface(presShell);
}
NS_ENSURE_TRUE(selCon, NS_ERROR_FAILURE);
nsCOMPtr<nsISelection> sel;
nsresult rv = selCon->GetSelection(nsISelectionController::SELECTION_NORMAL,
getter_AddRefs(sel));
NS_ENSURE_TRUE(sel, NS_ERROR_UNEXPECTED);
nsCOMPtr<nsIDOMRange> selDomRange;
rv = sel->GetRangeAt(0, getter_AddRefs(selDomRange));
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIRange> selRange(do_QueryInterface(selDomRange));
NS_ENSURE_TRUE(selRange && selRange->GetStartParent(), NS_ERROR_UNEXPECTED);
mRootContent = selRange->GetStartParent()->
GetSelectionRootContent(presShell);
// add text change observer
mRootContent->AddMutationObserver(this);
// add selection change listener
nsCOMPtr<nsISelectionPrivate> selPrivate(do_QueryInterface(sel));
NS_ENSURE_TRUE(selPrivate, NS_ERROR_UNEXPECTED);
rv = selPrivate->AddSelectionListener(this);
NS_ENSURE_SUCCESS(rv, rv);
mSel = sel;
mEditableNode = aNode;
return NS_OK;
}
void
nsTextStateManager::Destroy(void)
{
if (mSel) {
nsCOMPtr<nsISelectionPrivate> selPrivate(do_QueryInterface(mSel));
if (selPrivate)
selPrivate->RemoveSelectionListener(this);
mSel = nsnull;
}
if (mRootContent) {
mRootContent->RemoveMutationObserver(this);
mRootContent = nsnull;
}
mEditableNode = nsnull;
mWidget = nsnull;
}
NS_IMPL_ISUPPORTS2(nsTextStateManager,
nsIMutationObserver,
nsISelectionListener)
nsresult
nsTextStateManager::NotifySelectionChanged(nsIDOMDocument* aDoc,
nsISelection* aSel,
PRInt16 aReason)
{
PRInt32 count = 0;
nsresult rv = aSel->GetRangeCount(&count);
NS_ENSURE_SUCCESS(rv, rv);
if (count > 0) {
mWidget->OnIMESelectionChange();
}
return NS_OK;
}
void
nsTextStateManager::CharacterDataChanged(nsIDocument* aDocument,
nsIContent* aContent,
CharacterDataChangeInfo* aInfo)
{
NS_ASSERTION(aContent->IsNodeOfType(nsINode::eTEXT),
"character data changed for non-text node");
PRUint32 offset = 0;
// get offsets of change and fire notification
if (NS_FAILED(nsContentEventHandler::GetFlatTextOffsetOfRange(
mRootContent, aContent, aInfo->mChangeStart, &offset)))
return;
PRUint32 oldEnd = offset + aInfo->mChangeEnd - aInfo->mChangeStart;
PRUint32 newEnd = offset + aInfo->mReplaceLength;
mWidget->OnIMETextChange(offset, oldEnd, newEnd);
}
void
nsTextStateManager::NotifyContentAdded(nsINode* aContainer,
PRInt32 aStartIndex,
PRInt32 aEndIndex)
{
PRUint32 offset = 0, newOffset = 0;
if (NS_FAILED(nsContentEventHandler::GetFlatTextOffsetOfRange(
mRootContent, aContainer, aStartIndex, &offset)))
return;
// get offset at the end of the last added node
if (NS_FAILED(nsContentEventHandler::GetFlatTextOffsetOfRange(
aContainer->GetChildAt(aStartIndex),
aContainer, aEndIndex, &newOffset)))
return;
// fire notification
if (newOffset)
mWidget->OnIMETextChange(offset, offset, offset + newOffset);
}
void
nsTextStateManager::ContentAppended(nsIDocument* aDocument,
nsIContent* aContainer,
PRInt32 aNewIndexInContainer)
{
NotifyContentAdded(aContainer, aNewIndexInContainer,
aContainer->GetChildCount());
}
void
nsTextStateManager::ContentInserted(nsIDocument* aDocument,
nsIContent* aContainer,
nsIContent* aChild,
PRInt32 aIndexInContainer)
{
NotifyContentAdded(NODE_FROM(aContainer, aDocument),
aIndexInContainer, aIndexInContainer + 1);
}
void
nsTextStateManager::ContentRemoved(nsIDocument* aDocument,
nsIContent* aContainer,
nsIContent* aChild,
PRInt32 aIndexInContainer)
{
PRUint32 offset = 0, childOffset = 1;
if (NS_FAILED(nsContentEventHandler::GetFlatTextOffsetOfRange(
mRootContent, NODE_FROM(aContainer, aDocument),
aIndexInContainer, &offset)))
return;
// get offset at the end of the deleted node
if (aChild->IsNodeOfType(nsINode::eTEXT))
childOffset = aChild->TextLength();
else if (0 < aChild->GetChildCount())
childOffset = aChild->GetChildCount();
if (NS_FAILED(nsContentEventHandler::GetFlatTextOffsetOfRange(
aChild, aChild, childOffset, &childOffset)))
return;
// fire notification
if (childOffset)
mWidget->OnIMETextChange(offset, offset + childOffset, offset);
}
static nsINode* GetRootEditableNode(nsPresContext* aPresContext,
nsIContent* aContent)
{
if (aContent) {
nsINode* root = nsnull;
nsINode* node = aContent;
while (node && node->IsEditable()) {
root = node;
node = node->GetParent();
}
return root;
}
if (aPresContext) {
nsIDocument* document = aPresContext->Document();
if (document && document->IsEditable())
return document;
}
return nsnull;
}
nsresult
nsIMEStateManager::OnTextStateBlur(nsPresContext* aPresContext,
nsIContent* aContent)
{
if (!sTextStateObserver || sTextStateObserver->mDestroying ||
sTextStateObserver->mEditableNode ==
GetRootEditableNode(aPresContext, aContent))
return NS_OK;
sTextStateObserver->mDestroying = PR_TRUE;
sTextStateObserver->mWidget->OnIMEFocusChange(PR_FALSE);
sTextStateObserver->Destroy();
NS_RELEASE(sTextStateObserver);
return NS_OK;
}
nsresult
nsIMEStateManager::OnTextStateFocus(nsPresContext* aPresContext,
nsIContent* aContent)
{
if (sTextStateObserver) return NS_OK;
nsINode *editableNode = GetRootEditableNode(aPresContext, aContent);
if (!editableNode) return NS_OK;
nsIViewManager* vm = aPresContext->GetViewManager();
NS_ENSURE_TRUE(vm, NS_ERROR_NOT_AVAILABLE);
nsCOMPtr<nsIWidget> widget;
nsresult rv = vm->GetWidget(getter_AddRefs(widget));
NS_ENSURE_SUCCESS(rv, NS_ERROR_NOT_AVAILABLE);
rv = widget->OnIMEFocusChange(PR_TRUE);
NS_ENSURE_SUCCESS(rv, NS_OK);
// OnIMEFocusChange may cause focus and sTextStateObserver to change
// In that case return and keep the current sTextStateObserver
NS_ENSURE_TRUE(!sTextStateObserver, NS_OK);
sTextStateObserver = new nsTextStateManager();
NS_ENSURE_TRUE(sTextStateObserver, NS_ERROR_OUT_OF_MEMORY);
NS_ADDREF(sTextStateObserver);
rv = sTextStateObserver->Init(widget, aPresContext, editableNode);
if (NS_FAILED(rv)) {
sTextStateObserver->mDestroying = PR_TRUE;
sTextStateObserver->Destroy();
NS_RELEASE(sTextStateObserver);
widget->OnIMEFocusChange(PR_FALSE);
return rv;
}
return NS_OK;
}
nsresult
nsIMEStateManager::GetFocusSelectionAndRoot(nsISelection** aSel,
nsIContent** aRoot)
{
if (!sTextStateObserver || !sTextStateObserver->mEditableNode)
return NS_ERROR_NOT_AVAILABLE;
NS_ASSERTION(sTextStateObserver->mSel && sTextStateObserver->mRootContent,
"uninitialized text state observer");
NS_ADDREF(*aSel = sTextStateObserver->mSel);
NS_ADDREF(*aRoot = sTextStateObserver->mRootContent);
return NS_OK;
}

View File

@ -40,12 +40,15 @@
#define nsIMEStateManager_h__
#include "nscore.h"
#include "nsGUIEvent.h"
class nsIContent;
class nsPIDOMWindow;
class nsPresContext;
class nsIWidget;
class nsIFocusController;
class nsTextStateManager;
class nsISelection;
/*
* IME state manager
@ -62,6 +65,25 @@ public:
static nsresult OnActivate(nsPresContext* aPresContext);
static nsresult OnDeactivate(nsPresContext* aPresContext);
static void OnInstalledMenuKeyboardListener(PRBool aInstalling);
// These two methods manage focus and selection/text observers.
// They are separate from OnChangeFocus above because this offers finer
// control compared to having the two methods incorporated into OnChangeFocus
// OnTextStateBlur should be called *before* NS_BLUR_CONTENT fires
// aPresContext is the nsPresContext receiving focus (not lost focus)
// aContent is the nsIContent receiving focus (not lost focus)
// aPresContext and/or aContent may be null
static nsresult OnTextStateBlur(nsPresContext* aPresContext,
nsIContent* aContent);
// OnTextStateFocus should be called *after* NS_FOCUS_CONTENT fires
// aPresContext is the nsPresContext receiving focus
// aContent is the nsIContent receiving focus
static nsresult OnTextStateFocus(nsPresContext* aPresContext,
nsIContent* aContent);
// Get the focused editor's selection and root
static nsresult GetFocusSelectionAndRoot(nsISelection** aSel,
nsIContent** aRoot);
protected:
static void SetIMEState(nsPresContext* aPresContext,
PRUint32 aState,
@ -78,6 +100,8 @@ protected:
static nsPresContext* sPresContext;
static nsPIDOMWindow* sActiveWindow;
static PRBool sInstalledMenuKeyboardListener;
static nsTextStateManager* sTextStateObserver;
};
#endif // nsIMEStateManager_h__

View File

@ -569,6 +569,13 @@ public:
static nsresult GetHashFromHrefString(const nsAString &aHref,
nsAString& aHash);
/**
* Locate an nsIEditor rooted at this content node, if there is one.
*/
NS_HIDDEN_(nsresult) GetEditor(nsIEditor** aEditor);
NS_HIDDEN_(nsresult) GetEditorInternal(nsIEditor** aEditor);
protected:
/**
* Focus or blur the element. This is what you should call if you want to
@ -728,12 +735,6 @@ protected:
*/
NS_HIDDEN_(nsresult) GetURIListAttr(nsIAtom* aAttr, nsAString& aResult);
/**
* Locate an nsIEditor rooted at this content node, if there is one.
*/
NS_HIDDEN_(nsresult) GetEditor(nsIEditor** aEditor);
NS_HIDDEN_(nsresult) GetEditorInternal(nsIEditor** aEditor);
/**
* Locates the nsIEditor associated with this node. In general this is
* equivalent to GetEditorInternal(), but for designmode or contenteditable,

View File

@ -1249,6 +1249,7 @@ NS_IMETHODIMP nsViewManager::DispatchEvent(nsGUIEvent *aEvent, nsEventStatus *aS
if (!NS_IS_KEY_EVENT(aEvent) && !NS_IS_IME_EVENT(aEvent) &&
!NS_IS_CONTEXT_MENU_KEY(aEvent) && !NS_IS_FOCUS_EVENT(aEvent) &&
!NS_IS_QUERY_CONTENT_EVENT(aEvent) && !NS_IS_PLUGIN_EVENT(aEvent) &&
!NS_IS_SELECTION_EVENT(aEvent) &&
aEvent->eventStructType != NS_ACCESSIBLE_EVENT) {
// will dispatch using coordinates. Pretty bogus but it's consistent
// with what presshell does.

View File

@ -45,7 +45,7 @@ include $(DEPTH)/config/autoconf.mk
DIRS = public src
ifdef ENABLE_TESTS
DIRS += tests
TOOL_DIRS += tests
endif
include $(topsrcdir)/config/rules.mk

View File

@ -105,6 +105,7 @@ class nsHashKey;
#define NS_DRAG_EVENT 35
#define NS_NOTIFYPAINT_EVENT 36
#define NS_SIMPLE_GESTURE_EVENT 37
#define NS_SELECTION_EVENT 38
// These flags are sort of a mess. They're sort of shared between event
// listener flags and event flags, but only some of them. You've been
@ -344,14 +345,18 @@ class nsHashKey;
#define NS_QUERY_SELECTED_TEXT (NS_QUERY_CONTENT_EVENT_START)
// Query for the text content of specified range, it returns actual lengh (if
// the specified range is too long) and the text of the specified range.
// Returns the entire text if requested length > actual length.
#define NS_QUERY_TEXT_CONTENT (NS_QUERY_CONTENT_EVENT_START + 1)
// Query for the character rect of nth character. If there is no character at
// the offset, the query will be failed. The offset of the result is relative
// position from the top level widget.
#define NS_QUERY_CHARACTER_RECT (NS_QUERY_CONTENT_EVENT_START + 2)
// Query for the caret rect of nth insertion point. The offset of the result is
// relative position from the top level widget.
#define NS_QUERY_CARET_RECT (NS_QUERY_CONTENT_EVENT_START + 3)
// Query for the bounding rect of a range of characters. This works on any
// valid character range given offset and length. Result is relative to top
// level widget coordinates
#define NS_QUERY_TEXT_RECT (NS_QUERY_CONTENT_EVENT_START + 4)
// Query for the bounding rect of the current focused frame. Result is relative
// to top level widget coordinates
#define NS_QUERY_EDITOR_RECT (NS_QUERY_CONTENT_EVENT_START + 5)
// Video events
#ifdef MOZ_MEDIA
@ -397,6 +402,11 @@ class nsHashKey;
#define NS_PLUGIN_EVENT_START 3600
#define NS_PLUGIN_EVENT (NS_PLUGIN_EVENT_START)
// Events to manipulate selection (nsSelectionEvent)
#define NS_SELECTION_EVENT_START 3700
// Clear any previous selection and set the given range as the selection
#define NS_SELECTION_SET (NS_SELECTION_EVENT_START)
/**
* Return status for event processors, nsEventStatus, is defined in
* nsEvent.h.
@ -858,11 +868,11 @@ class nsTextEvent : public nsInputEvent
public:
nsTextEvent(PRBool isTrusted, PRUint32 msg, nsIWidget *w)
: nsInputEvent(isTrusted, msg, w, NS_TEXT_EVENT),
theText(nsnull), rangeCount(0), rangeArray(nsnull), isChar(PR_FALSE)
rangeCount(0), rangeArray(nsnull), isChar(PR_FALSE)
{
}
const PRUnichar* theText;
nsString theText;
nsTextEventReply theReply;
PRUint32 rangeCount;
// Note that the range array may not specify a caret position; in that
@ -965,13 +975,6 @@ public:
mInput.mLength = aLength;
}
void InitForQueryCharacterRect(PRUint32 aOffset)
{
NS_ASSERTION(message == NS_QUERY_CHARACTER_RECT,
"wrong initializer is called");
mInput.mOffset = aOffset;
}
void InitForQueryCaretRect(PRUint32 aOffset)
{
NS_ASSERTION(message == NS_QUERY_CARET_RECT,
@ -979,6 +982,14 @@ public:
mInput.mOffset = aOffset;
}
void InitForQueryTextRect(PRUint32 aOffset, PRUint32 aLength)
{
NS_ASSERTION(message == NS_QUERY_TEXT_RECT,
"wrong initializer is called");
mInput.mOffset = aOffset;
mInput.mLength = aLength;
}
PRBool mSucceeded;
struct {
PRUint32 mOffset;
@ -991,9 +1002,25 @@ public:
nsIntRect mRect; // Finally, the coordinates is system coordinates.
// The return widget has the caret. This is set at all query events.
nsIWidget* mFocusedWidget;
PRPackedBool mReversed; // true if selection is reversed (end < start)
} mReply;
};
class nsSelectionEvent : public nsGUIEvent
{
public:
nsSelectionEvent(PRBool aIsTrusted, PRUint32 aMsg, nsIWidget *aWidget) :
nsGUIEvent(aIsTrusted, aMsg, aWidget, NS_SELECTION_EVENT),
mSucceeded(PR_FALSE)
{
}
PRUint32 mOffset; // start offset of selection
PRUint32 mLength; // length of selection
PRPackedBool mReversed; // selection "anchor" should be in front
PRPackedBool mSucceeded;
};
/**
* MenuItem event
*
@ -1232,8 +1259,12 @@ enum nsDragDropEventStatus {
#define NS_IS_QUERY_CONTENT_EVENT(evnt) \
(((evnt)->message == NS_QUERY_SELECTED_TEXT) || \
((evnt)->message == NS_QUERY_TEXT_CONTENT) || \
((evnt)->message == NS_QUERY_CHARACTER_RECT) || \
((evnt)->message == NS_QUERY_CARET_RECT))
((evnt)->message == NS_QUERY_CARET_RECT) || \
((evnt)->message == NS_QUERY_TEXT_RECT) || \
((evnt)->message == NS_QUERY_EDITOR_RECT))
#define NS_IS_SELECTION_EVENT(evnt) \
(((evnt)->message == NS_SELECTION_SET))
#define NS_IS_PLUGIN_EVENT(evnt) \
(((evnt)->message == NS_PLUGIN_EVENT))

View File

@ -93,11 +93,14 @@ typedef nsEventStatus (* EVENT_CALLBACK)(nsGUIEvent *event);
#define NS_NATIVE_PLUGIN_PORT_QD 100
#define NS_NATIVE_PLUGIN_PORT_CG 101
#endif
#ifdef XP_WIN
#define NS_NATIVE_TSF_POINTER 100
#endif
// a85944af-7fce-4e45-bf04-ac12c823394b
// {51C24E3B-229F-4c4f-ADA5-BE891FD9EFE9}
#define NS_IWIDGET_IID \
{ 0xa85944af, 0x7fce, 0x4e45, \
{ 0xbf, 0x04, 0xac, 0x12, 0xc8, 0x23, 0x39, 0x4b } }
{ 0x51c24e3b, 0x229f, 0x4c4f, \
{ 0xad, 0xa5, 0xbe, 0x89, 0x1f, 0xd9, 0xef, 0xe9 } }
// Hide the native window systems real window type so as to avoid
// including native window system types and APIs. This is necessary
@ -1242,6 +1245,29 @@ class nsIWidget : public nsISupports {
*/
NS_IMETHOD GetToggledKeyState(PRUint32 aKeyCode, PRBool* aLEDState) = 0;
/*
* An editable node (i.e. input/textarea/design mode document)
* is receiving or giving up focus
* aFocus is true if node is receiving focus
* aFocus is false if node is giving up focus (blur)
*/
NS_IMETHOD OnIMEFocusChange(PRBool aFocus) = 0;
/*
* Text content of the focused node has changed
* aStart is the starting offset of the change
* aOldEnd is the ending offset of the change
* aNewEnd is the caret offset after the change
*/
NS_IMETHOD OnIMETextChange(PRUint32 aStart,
PRUint32 aOldEnd,
PRUint32 aNewEnd) = 0;
/*
* Selection has changed in the focused node
*/
NS_IMETHOD OnIMESelectionChange(void) = 0;
protected:
// keep the list of children. We also keep track of our siblings.
// The ownership model is as follows: parent holds a strong ref to

View File

@ -5490,8 +5490,8 @@ GetUSLayoutCharFromKeyTranslate(UInt32 aKeyCode, UInt32 aModifiers)
nsIntRect r;
PRBool useCaretRect = theRange.length == 0;
if (!useCaretRect) {
nsQueryContentEvent charRect(PR_TRUE, NS_QUERY_CHARACTER_RECT, mGeckoChild);
charRect.InitForQueryCharacterRect(theRange.location);
nsQueryContentEvent charRect(PR_TRUE, NS_QUERY_TEXT_RECT, mGeckoChild);
charRect.InitForQueryTextRect(theRange.location, 1);
mGeckoChild->DispatchWindowEvent(charRect);
if (charRect.mSucceeded)
r = charRect.mReply.mRect;

View File

@ -107,6 +107,7 @@ CPPSRCS += \
nsBidiKeyboard.cpp \
nsSound.cpp \
nsIdleServiceWin.cpp \
nsTextStore.cpp \
$(NULL)
endif

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,232 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is mozilla.org code.
*
* The Initial Developer of the Original Code is
* Netscape Communications Corporation.
* Portions created by the Initial Developer are Copyright (C) 1998
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Ningjie Chen <chenn@email.uc.edu>
*
* 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
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#ifndef NSTEXTSTORE_H_
#define NSTEXTSTORE_H_
#include "nsAutoPtr.h"
#include "nsString.h"
#include <msctf.h>
#include <textstor.h>
struct ITfThreadMgr;
struct ITfDocumentMgr;
class nsWindow;
// It doesn't work well when we notify TSF of text change
// during a mutation observer call because things get broken.
// So we post a message and notify TSF when we get it later.
#define WM_USER_TSF_TEXTCHANGE (WM_USER + 0x100)
/*
* Text Services Framework text store
*/
class nsTextStore : public ITextStoreACP,
public ITfContextOwnerCompositionSink
{
public: /*IUnknown*/
STDMETHODIMP_(ULONG) AddRef(void);
STDMETHODIMP QueryInterface(REFIID, void**);
STDMETHODIMP_(ULONG) Release(void);
public: /*ITextStoreACP*/
STDMETHODIMP AdviseSink(REFIID, IUnknown*, DWORD);
STDMETHODIMP UnadviseSink(IUnknown*);
STDMETHODIMP RequestLock(DWORD, HRESULT*);
STDMETHODIMP GetStatus(TS_STATUS*);
STDMETHODIMP QueryInsert(LONG, LONG, ULONG, LONG*, LONG*);
STDMETHODIMP GetSelection(ULONG, ULONG, TS_SELECTION_ACP*, ULONG*);
STDMETHODIMP SetSelection(ULONG, const TS_SELECTION_ACP*);
STDMETHODIMP GetText(LONG, LONG, WCHAR*, ULONG, ULONG*, TS_RUNINFO*, ULONG,
ULONG*, LONG*);
STDMETHODIMP SetText(DWORD, LONG, LONG, const WCHAR*, ULONG, TS_TEXTCHANGE*);
STDMETHODIMP GetFormattedText(LONG, LONG, IDataObject**);
STDMETHODIMP GetEmbedded(LONG, REFGUID, REFIID, IUnknown**);
STDMETHODIMP QueryInsertEmbedded(const GUID*, const FORMATETC*, BOOL*);
STDMETHODIMP InsertEmbedded(DWORD, LONG, LONG, IDataObject*, TS_TEXTCHANGE*);
STDMETHODIMP RequestSupportedAttrs(DWORD, ULONG, const TS_ATTRID*);
STDMETHODIMP RequestAttrsAtPosition(LONG, ULONG, const TS_ATTRID*, DWORD);
STDMETHODIMP RequestAttrsTransitioningAtPosition(LONG, ULONG,
const TS_ATTRID*, DWORD);
STDMETHODIMP FindNextAttrTransition(LONG, LONG, ULONG, const TS_ATTRID*,
DWORD, LONG*, BOOL*, LONG*);
STDMETHODIMP RetrieveRequestedAttrs(ULONG, TS_ATTRVAL*, ULONG*);
STDMETHODIMP GetEndACP(LONG*);
STDMETHODIMP GetActiveView(TsViewCookie*);
STDMETHODIMP GetACPFromPoint(TsViewCookie, const POINT*, DWORD, LONG*);
STDMETHODIMP GetTextExt(TsViewCookie, LONG, LONG, RECT*, BOOL*);
STDMETHODIMP GetScreenExt(TsViewCookie, RECT*);
STDMETHODIMP GetWnd(TsViewCookie, HWND*);
STDMETHODIMP InsertTextAtSelection(DWORD, const WCHAR*, ULONG, LONG*, LONG*,
TS_TEXTCHANGE*);
STDMETHODIMP InsertEmbeddedAtSelection(DWORD, IDataObject*, LONG*, LONG*,
TS_TEXTCHANGE*);
public: /*ITfContextOwnerCompositionSink*/
STDMETHODIMP OnStartComposition(ITfCompositionView*, BOOL*);
STDMETHODIMP OnUpdateComposition(ITfCompositionView*, ITfRange*);
STDMETHODIMP OnEndComposition(ITfCompositionView*);
public:
static void Initialize(void);
static void Terminate(void);
static void SetIMEOpenState(PRBool);
static PRBool GetIMEOpenState(void);
static void CommitComposition(PRBool aDiscard)
{
if (!sTsfTextStore) return;
sTsfTextStore->CommitCompositionInternal(aDiscard);
}
static void SetIMEEnabled(PRUint32 aState)
{
if (!sTsfTextStore) return;
sTsfTextStore->SetIMEEnabledInternal(aState);
}
static nsresult OnFocusChange(PRBool, nsWindow*, PRUint32);
static nsresult OnTextChange(PRUint32 aStart,
PRUint32 aOldEnd,
PRUint32 aNewEnd)
{
if (!sTsfTextStore) return NS_OK;
return sTsfTextStore->OnTextChangeInternal(aStart, aOldEnd, aNewEnd);
}
static void OnTextChangeMsg(void)
{
if (!sTsfTextStore) return;
// Notify TSF text change
// (see comments on WM_USER_TSF_TEXTCHANGE in nsTextStore.h)
sTsfTextStore->OnTextChangeMsgInternal();
}
static nsresult OnSelectionChange(void)
{
if (!sTsfTextStore) return NS_OK;
return sTsfTextStore->OnSelectionChangeInternal();
}
static void* GetNativeData(void)
{
// Returns the address of the pointer so that the TSF automatic test can
// replace the system object with a custom implementation for testing.
return (void*) & sTsfThreadMgr;
}
protected:
nsTextStore();
~nsTextStore();
PRBool Create(nsWindow*, PRUint32);
PRBool Destroy(void);
PRBool Focus(void);
PRBool Blur(void);
HRESULT LoadManagers(void);
HRESULT SetSelectionInternal(const TS_SELECTION_ACP*);
HRESULT OnStartCompositionInternal(ITfCompositionView*, ITfRange*, PRBool);
void CommitCompositionInternal(PRBool);
void SetIMEEnabledInternal(PRUint32 aState);
nsresult OnTextChangeInternal(PRUint32, PRUint32, PRUint32);
void OnTextChangeMsgInternal(void);
nsresult OnSelectionChangeInternal(void);
// TSF display attribute manager, loaded by LoadManagers
nsRefPtr<ITfDisplayAttributeMgr> mDAMgr;
// TSF category manager, loaded by LoadManagers
nsRefPtr<ITfCategoryMgr> mCatMgr;
// Document manager for the currently focused editor
nsRefPtr<ITfDocumentMgr> mDocumentMgr;
// Edit cookie associated with the current editing context
DWORD mEditCookie;
// Editing context at the bottom of mDocumentMgr's context stack
nsRefPtr<ITfContext> mContext;
// Currently installed notification sink
nsRefPtr<ITextStoreACPSink> mSink;
// TS_AS_* mask of what events to notify
DWORD mSinkMask;
// Window containing the focused editor
nsWindow* mWindow;
// 0 if not locked, otherwise TS_LF_* indicating the current lock
DWORD mLock;
// 0 if no lock is queued, otherwise TS_LF_* indicating the queue lock
DWORD mLockQueued;
// Cumulative text change offsets since the last notification
TS_TEXTCHANGE mTextChange;
// NULL if no composition is active, otherwise the current composition
nsRefPtr<ITfCompositionView> mCompositionView;
// Current copy of the active composition string. Only mCompositionString is
// changed during a InsertTextAtSelection call if we have a composition.
// mCompositionString acts as a buffer until OnUpdateComposition is called
// and mCompositionString is flushed to editor through NS_TEXT_TEXT. This
// way all changes are updated in batches to avoid inconsistencies/artifacts.
nsString mCompositionString;
// "Current selection" during a composition, in ACP offsets.
// We use a fake selection during a composition because editor code doesn't
// like us accessing the actual selection during a composition. So we leave
// the actual selection alone and get/set mCompositionSelection instead
// during GetSelection/SetSelection calls.
TS_SELECTION_ACP mCompositionSelection;
// The start and length of the current active composition, in ACP offsets
LONG mCompositionStart;
LONG mCompositionLength;
// TSF thread manager object for the current application
static ITfThreadMgr* sTsfThreadMgr;
// TSF client ID for the current application
static DWORD sTsfClientId;
// Current text store. Currently only ONE nsTextStore instance is ever used,
// although Create is called when an editor is focused and Destroy called
// when the focused editor is blurred.
static nsTextStore* sTsfTextStore;
// Message the Tablet Input Panel uses to flush text during blurring.
// See comments in Destroy
static UINT sFlushTIPInputMessage;
private:
ULONG mRefCnt;
};
#endif /*NSTEXTSTORE_H_*/

View File

@ -34,6 +34,7 @@
* Dainis Jonitis <Dainis_Jonitis@swh-t.lv>
* Christian Biesinger <cbiesinger@web.de>
* Mats Palmgren <mats.palmgren@bredband.net>
* Ningjie Chen <chenn@email.uc.edu>
*
* 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
@ -149,7 +150,9 @@
#include "prprf.h"
#include "prmem.h"
#ifdef NS_ENABLE_TSF
#include "nsTextStore.h"
#endif //NS_ENABLE_TSF
#ifdef WINCE
@ -746,14 +749,18 @@ nsWindow::nsWindow() : nsBaseWidget()
mIsTopWidgetWindow = PR_FALSE;
mLastKeyboardLayout = 0;
#ifdef NS_ENABLE_TSF
if (!sInstanceCount)
nsTextStore::Initialize();
#endif //NS_ENABLE_TSF
#ifndef WINCE
if (!sInstanceCount && SUCCEEDED(::OleInitialize(NULL))) {
sIsOleInitialized = TRUE;
}
NS_ASSERTION(sIsOleInitialized, "***** OLE is not initialized!\n");
sInstanceCount++;
#endif
sInstanceCount++;
}
//-------------------------------------------------------------------------
@ -788,11 +795,17 @@ nsWindow::~nsWindow()
SetCursor(eCursor_standard);
}
sInstanceCount--;
#ifdef NS_ENABLE_TSF
if (!sInstanceCount)
nsTextStore::Terminate();
#endif //NS_ENABLE_TSF
#ifndef WINCE
//
// delete any of the IME structures that we allocated
//
sInstanceCount--;
if (sInstanceCount == 0) {
if (sIMECompUnicode)
delete sIMECompUnicode;
@ -2816,6 +2829,12 @@ void* nsWindow::GetNativeData(PRUint32 aDataType)
#else
return (void*)::GetDC(mWnd);
#endif
#ifdef NS_ENABLE_TSF
case NS_NATIVE_TSF_POINTER:
return nsTextStore::GetNativeData();
#endif //NS_ENABLE_TSF
case NS_NATIVE_COLORMAP:
default:
break;
@ -5309,6 +5328,12 @@ PRBool nsWindow::ProcessMessage(UINT msg, WPARAM wParam, LPARAM lParam, LRESULT
}
#endif // WINCE
#ifdef NS_ENABLE_TSF
else if (msg == WM_USER_TSF_TEXTCHANGE) {
nsTextStore::OnTextChangeMsg();
}
#endif //NS_ENABLE_TSF
}
break;
#ifndef WINCE
@ -7433,8 +7458,8 @@ PRBool nsWindow::OnIMEQueryCharPosition(LPARAM aData, LRESULT *oResult)
nsIntRect r;
if (!useCaretRect) {
nsQueryContentEvent charRect(PR_TRUE, NS_QUERY_CHARACTER_RECT, this);
charRect.InitForQueryCharacterRect(offset);
nsQueryContentEvent charRect(PR_TRUE, NS_QUERY_TEXT_RECT, this);
charRect.InitForQueryTextRect(offset, 1);
InitEvent(charRect, &point);
DispatchWindowEvent(&charRect);
if (charRect.mSucceeded)
@ -7542,6 +7567,11 @@ NS_IMETHODIMP nsWindow::ResetInputState()
#ifdef DEBUG_KBSTATE
printf("ResetInputState\n");
#endif
#ifdef NS_ENABLE_TSF
nsTextStore::CommitComposition(PR_FALSE);
#endif //NS_ENABLE_TSF
HIMC hIMC = ::ImmGetContext(mWnd);
if (hIMC) {
BOOL ret = FALSE;
@ -7559,6 +7589,11 @@ NS_IMETHODIMP nsWindow::SetIMEOpenState(PRBool aState)
#ifdef DEBUG_KBSTATE
printf("SetIMEOpenState %s\n", (aState ? "Open" : "Close"));
#endif
#ifdef NS_ENABLE_TSF
nsTextStore::SetIMEOpenState(aState);
#endif //NS_ENABLE_TSF
HIMC hIMC = ::ImmGetContext(mWnd);
if (hIMC) {
::ImmSetOpenStatus(hIMC, aState ? TRUE : FALSE);
@ -7577,12 +7612,21 @@ NS_IMETHODIMP nsWindow::GetIMEOpenState(PRBool* aState)
::ImmReleaseContext(mWnd, hIMC);
} else
*aState = PR_FALSE;
#ifdef NS_ENABLE_TSF
*aState |= nsTextStore::GetIMEOpenState();
#endif //NS_ENABLE_TSF
return NS_OK;
}
//==========================================================================
NS_IMETHODIMP nsWindow::SetIMEEnabled(PRUint32 aState)
{
#ifdef NS_ENABLE_TSF
nsTextStore::SetIMEEnabled(aState);
#endif //NS_ENABLE_TSF
if (sIMEIsComposing)
ResetInputState();
mIMEEnabled = aState;
@ -7609,6 +7653,11 @@ NS_IMETHODIMP nsWindow::CancelIMEComposition()
#ifdef DEBUG_KBSTATE
printf("CancelIMEComposition\n");
#endif
#ifdef NS_ENABLE_TSF
nsTextStore::CommitComposition(PR_TRUE);
#endif //NS_ENABLE_TSF
HIMC hIMC = ::ImmGetContext(mWnd);
if (hIMC) {
BOOL ret = FALSE;
@ -7814,6 +7863,30 @@ static VOID CALLBACK nsGetAttentionTimerFunc(HWND hwnd, UINT uMsg, UINT idEvent,
gAttentionTimerMonitor->KillTimer(hwnd);
}
#ifdef NS_ENABLE_TSF
NS_IMETHODIMP
nsWindow::OnIMEFocusChange(PRBool aFocus)
{
return nsTextStore::OnFocusChange(aFocus, this, mIMEEnabled);
}
NS_IMETHODIMP
nsWindow::OnIMETextChange(PRUint32 aStart,
PRUint32 aOldEnd,
PRUint32 aNewEnd)
{
return nsTextStore::OnTextChange(aStart, aOldEnd, aNewEnd);
}
NS_IMETHODIMP
nsWindow::OnIMESelectionChange(void)
{
return nsTextStore::OnSelectionChange();
}
#endif //NS_ENABLE_TSF
// Draw user's attention to this window until it comes to foreground.
NS_IMETHODIMP
nsWindow::GetAttention(PRInt32 aCycleCount)

View File

@ -25,6 +25,7 @@
* Makoto Kato <m_kato@ga2.so-net.ne.jp>
* Dainis Jonitis <Dainis_Jonitis@swh-t.lv>
* Masayuki Nakano <masayuki@d-toybox.com>
* Ningjie Chen <chenn@email.uc.edu>
*
* 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
@ -73,6 +74,11 @@ struct nsFakeCharMessage;
#include "gfxWindowsSurface.h"
// Text Services Framework support
#ifndef WINCE
#define NS_ENABLE_TSF
#endif //WINCE
#define IME_MAX_CHAR_POS 64
#define NSRGB_2_COLOREF(color) \
@ -235,6 +241,12 @@ public:
NS_IMETHOD CancelIMEComposition();
NS_IMETHOD GetToggledKeyState(PRUint32 aKeyCode, PRBool* aLEDState);
#ifdef NS_ENABLE_TSF
NS_IMETHOD OnIMEFocusChange(PRBool aFocus);
NS_IMETHOD OnIMETextChange(PRUint32 aStart, PRUint32 aOldEnd, PRUint32 aNewEnd);
NS_IMETHOD OnIMESelectionChange(void);
#endif //NS_ENABLE_TSF
PRBool IMEMouseHandling(PRInt32 aAction, LPARAM lParam);
PRBool IMECompositionHitTest(POINT * ptPos);
PRBool HandleMouseActionOfIME(PRInt32 aAction, POINT* ptPos);
@ -250,6 +262,8 @@ public:
LPARAM lParam,
PRBool aIsContextMenuKey = PR_FALSE,
PRInt16 aButton = nsMouseEvent::eLeftButton);
virtual PRBool DispatchWindowEvent(nsGUIEvent* event);
virtual PRBool DispatchWindowEvent(nsGUIEvent*event, nsEventStatus &aStatus);
#ifdef ACCESSIBILITY
virtual PRBool DispatchAccessibleEvent(PRUint32 aEventType, nsIAccessible** aAccessible, nsIntPoint* aPoint = nsnull);
already_AddRefed<nsIAccessible> GetRootAccessible();
@ -303,8 +317,6 @@ protected:
LRESULT ProcessKeyDownMessage(const MSG &aMsg,
PRBool *aEventDispatched);
virtual PRBool DispatchWindowEvent(nsGUIEvent* event);
virtual PRBool DispatchWindowEvent(nsGUIEvent*event, nsEventStatus &aStatus);
// Allow Derived classes to modify the height that is passed
// when the window is created or resized.

View File

@ -139,13 +139,16 @@ public:
NS_IMETHOD BeginResizeDrag(nsGUIEvent* aEvent, PRInt32 aHorizontal, PRInt32 aVertical);
virtual nsresult ActivateNativeMenuItemAt(const nsAString& indexString) { return NS_ERROR_NOT_IMPLEMENTED; }
virtual nsresult ForceUpdateNativeMenuAt(const nsAString& indexString) { return NS_ERROR_NOT_IMPLEMENTED; }
NS_IMETHOD ResetInputState() { return NS_ERROR_NOT_IMPLEMENTED; }
NS_IMETHOD ResetInputState() { return NS_OK; }
NS_IMETHOD SetIMEOpenState(PRBool aState) { return NS_ERROR_NOT_IMPLEMENTED; }
NS_IMETHOD GetIMEOpenState(PRBool* aState) { return NS_ERROR_NOT_IMPLEMENTED; }
NS_IMETHOD SetIMEEnabled(PRUint32 aState) { return NS_ERROR_NOT_IMPLEMENTED; }
NS_IMETHOD GetIMEEnabled(PRUint32* aState) { return NS_ERROR_NOT_IMPLEMENTED; }
NS_IMETHOD CancelIMEComposition() { return NS_ERROR_NOT_IMPLEMENTED; }
NS_IMETHOD CancelIMEComposition() { return NS_OK; }
NS_IMETHOD GetToggledKeyState(PRUint32 aKeyCode, PRBool* aLEDState) { return NS_ERROR_NOT_IMPLEMENTED; }
NS_IMETHOD OnIMEFocusChange(PRBool aFocus) { return NS_ERROR_NOT_IMPLEMENTED; }
NS_IMETHOD OnIMETextChange(PRUint32 aStart, PRUint32 aOldEnd, PRUint32 aNewEnd) { return NS_ERROR_NOT_IMPLEMENTED; }
NS_IMETHOD OnIMESelectionChange(void) { return NS_ERROR_NOT_IMPLEMENTED; }
protected:

View File

@ -39,9 +39,40 @@ DEPTH = ../..
topsrcdir = @top_srcdir@
srcdir = @srcdir@
VPATH = @srcdir@
relativesrcdir = widget/test
relativesrcdir = widget/tests
include $(DEPTH)/config/autoconf.mk
ifeq ($(MOZ_WIDGET_TOOLKIT),windows)
ifneq ($(OS_ARCH), WINCE)
ifndef MOZ_ENABLE_LIBXUL
MOZILLA_INTERNAL_API = 1
else
LIBS += $(DIST)/lib/$(LIB_PREFIX)xpcomglue_s.$(LIB_SUFFIX) \
$(NULL)
endif
CPP_UNIT_TESTS += TestWinTSF.cpp \
$(NULL)
LOCAL_INCLUDES += -I$(topsrcdir)/xpcom/tests
CPPSRCS += $(CPP_UNIT_TESTS)
SIMPLE_PROGRAMS += $(CPP_UNIT_TESTS:.cpp=$(BIN_SUFFIX))
REQUIRES += appshell content docshell \
dom embed_base gfx layout locale \
necko string thebes uriloader view \
webbrwsr widget xpcom \
$(NULL)
LIBS += $(XPCOM_LIBS) \
$(NSPR_LIBS) \
$(NULL)
endif
endif
include $(topsrcdir)/config/rules.mk
_TEST_FILES = test_bug343416.xul \
@ -60,3 +91,13 @@ endif
libs:: $(_TEST_FILES)
$(INSTALL) $^ $(DEPTH)/_tests/testing/mochitest/chrome/$(relativesrcdir)
ifeq ($(MOZ_WIDGET_TOOLKIT),windows)
ifneq ($(OS_ARCH), WINCE)
check::
@$(EXIT_ON_ERROR) \
for f in $(subst .cpp,,$(CPP_UNIT_TESTS)); do \
XPCOM_DEBUG_BREAK=stack-and-abort $(RUN_TEST_PROGRAM) $(DIST)/bin/$$f; \
done
endif
endif

1822
widget/tests/TestWinTSF.cpp Normal file

File diff suppressed because it is too large Load Diff