Bug 998188 part.2 ContentEventHandler should support 2 modes, native line break mode and XP line break mode r=smaug

This commit is contained in:
Masayuki Nakano 2014-04-26 08:52:12 +09:00
parent 6de1b7019c
commit 2e098a58a9
5 changed files with 135 additions and 51 deletions

View File

@ -204,6 +204,7 @@ static bool IsContentBR(nsIContent* aContent)
static void ConvertToNativeNewlines(nsAFlatString& aString)
{
#if defined(XP_MACOSX)
// XXX Mac OS X doesn't use "\r".
aString.ReplaceSubstring(NS_LITERAL_STRING("\n"), NS_LITERAL_STRING("\r"));
#elif defined(XP_WIN)
aString.ReplaceSubstring(NS_LITERAL_STRING("\n"), NS_LITERAL_STRING("\r\n"));
@ -289,6 +290,14 @@ static uint32_t CountNewlinesInNativeLength(nsIContent* aContent,
/* static */ uint32_t
ContentEventHandler::GetNativeTextLength(nsIContent* aContent,
uint32_t aMaxLength)
{
return GetTextLength(aContent, LINE_BREAK_TYPE_NATIVE, aMaxLength);
}
/* static */ uint32_t
ContentEventHandler::GetTextLength(nsIContent* aContent,
LineBreakType aLineBreakType,
uint32_t aMaxLength)
{
if (aContent->IsNodeOfType(nsINode::eTEXT)) {
uint32_t textLengthDifference =
@ -301,7 +310,8 @@ ContentEventHandler::GetNativeTextLength(nsIContent* aContent,
// On Windows, the length of a native newline ("\r\n") is twice the length
// of the XP newline ("\n"), so XP length is equal to the length of the
// native offset plus the number of newlines encountered in the string.
CountNewlinesInXPLength(aContent, aMaxLength);
(aLineBreakType == LINE_BREAK_TYPE_NATIVE) ?
CountNewlinesInXPLength(aContent, aMaxLength) : 0;
#else
// On other platforms, the native and XP newlines are the same.
0;
@ -316,7 +326,7 @@ ContentEventHandler::GetNativeTextLength(nsIContent* aContent,
} else if (IsContentBR(aContent)) {
#if defined(XP_WIN)
// Length of \r\n
return 2;
return (aLineBreakType == LINE_BREAK_TYPE_NATIVE) ? 2 : 1;
#else
return 1;
#endif
@ -342,7 +352,8 @@ static uint32_t ConvertToXPOffset(nsIContent* aContent, uint32_t aNativeOffset)
}
static nsresult GenerateFlatTextContent(nsRange* aRange,
nsAFlatString& aString)
nsAFlatString& aString,
LineBreakType aLineBreakType)
{
nsCOMPtr<nsIContentIterator> iter = NS_NewContentIterator();
iter->Init(aRange);
@ -386,7 +397,9 @@ static nsresult GenerateFlatTextContent(nsRange* aRange,
aString.Append(char16_t('\n'));
}
}
ConvertToNativeNewlines(aString);
if (aLineBreakType == LINE_BREAK_TYPE_NATIVE) {
ConvertToNativeNewlines(aString);
}
return NS_OK;
}
@ -443,6 +456,7 @@ nsresult
ContentEventHandler::SetRangeFromFlatTextOffset(nsRange* aRange,
uint32_t aNativeOffset,
uint32_t aNativeLength,
LineBreakType aLineBreakType,
bool aExpandToClusterBoundaries,
uint32_t* aNewNativeOffset)
{
@ -467,8 +481,7 @@ ContentEventHandler::SetRangeFromFlatTextOffset(nsRange* aRange,
}
nsIContent* content = static_cast<nsIContent*>(node);
uint32_t nativeTextLength;
nativeTextLength = GetNativeTextLength(content);
uint32_t nativeTextLength = GetTextLength(content, aLineBreakType);
if (nativeTextLength == 0) {
continue;
}
@ -478,9 +491,15 @@ ContentEventHandler::SetRangeFromFlatTextOffset(nsRange* aRange,
nsCOMPtr<nsIDOMNode> domNode(do_QueryInterface(content));
NS_ASSERTION(domNode, "aContent doesn't have nsIDOMNode!");
uint32_t xpOffset =
content->IsNodeOfType(nsINode::eTEXT) ?
ConvertToXPOffset(content, aNativeOffset - nativeOffset) : 0;
uint32_t xpOffset;
if (!content->IsNodeOfType(nsINode::eTEXT)) {
xpOffset = 0;
} else {
xpOffset = aNativeOffset - nativeOffset;
if (aLineBreakType == LINE_BREAK_TYPE_NATIVE) {
xpOffset = ConvertToXPOffset(content, xpOffset);
}
}
if (aExpandToClusterBoundaries) {
uint32_t oldXPOffset = xpOffset;
@ -507,7 +526,10 @@ ContentEventHandler::SetRangeFromFlatTextOffset(nsRange* aRange,
uint32_t xpOffset;
if (content->IsNodeOfType(nsINode::eTEXT)) {
xpOffset = ConvertToXPOffset(content, nativeEndOffset - nativeOffset);
xpOffset = nativeEndOffset - nativeOffset;
if (aLineBreakType == LINE_BREAK_TYPE_NATIVE) {
xpOffset = ConvertToXPOffset(content, xpOffset);
}
if (aExpandToClusterBoundaries) {
rv = ExpandToClusterBoundary(content, true, &xpOffset);
NS_ENSURE_SUCCESS(rv, rv);
@ -550,6 +572,25 @@ ContentEventHandler::SetRangeFromFlatTextOffset(nsRange* aRange,
return rv;
}
/* static */ LineBreakType
ContentEventHandler::GetLineBreakType(WidgetQueryContentEvent* aEvent)
{
return GetLineBreakType(aEvent->mUseNativeLineBreak);
}
/* static */ LineBreakType
ContentEventHandler::GetLineBreakType(WidgetSelectionEvent* aEvent)
{
return GetLineBreakType(aEvent->mUseNativeLineBreak);
}
/* static */ LineBreakType
ContentEventHandler::GetLineBreakType(bool aUseNativeLineBreak)
{
return aUseNativeLineBreak ?
LINE_BREAK_TYPE_NATIVE : LINE_BREAK_TYPE_XP;
}
nsresult
ContentEventHandler::OnQuerySelectedText(WidgetQueryContentEvent* aEvent)
{
@ -561,8 +602,9 @@ ContentEventHandler::OnQuerySelectedText(WidgetQueryContentEvent* aEvent)
NS_ASSERTION(aEvent->mReply.mString.IsEmpty(),
"The reply string must be empty");
rv = GetFlatTextOffsetOfRange(mRootContent,
mFirstSelectedRange, &aEvent->mReply.mOffset);
LineBreakType lineBreakType = GetLineBreakType(aEvent);
rv = GetFlatTextOffsetOfRange(mRootContent, mFirstSelectedRange,
&aEvent->mReply.mOffset, lineBreakType);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIDOMNode> anchorDomNode, focusDomNode;
@ -586,7 +628,8 @@ ContentEventHandler::OnQuerySelectedText(WidgetQueryContentEvent* aEvent)
aEvent->mReply.mReversed = compare > 0;
if (compare) {
rv = GenerateFlatTextContent(mFirstSelectedRange, aEvent->mReply.mString);
rv = GenerateFlatTextContent(mFirstSelectedRange, aEvent->mReply.mString,
lineBreakType);
NS_ENSURE_SUCCESS(rv, rv);
}
@ -605,13 +648,15 @@ ContentEventHandler::OnQueryTextContent(WidgetQueryContentEvent* aEvent)
NS_ASSERTION(aEvent->mReply.mString.IsEmpty(),
"The reply string must be empty");
LineBreakType lineBreakType = GetLineBreakType(aEvent);
nsRefPtr<nsRange> range = new nsRange(mRootContent);
rv = SetRangeFromFlatTextOffset(range, aEvent->mInput.mOffset,
aEvent->mInput.mLength, false,
aEvent->mInput.mLength, lineBreakType, false,
&aEvent->mReply.mOffset);
NS_ENSURE_SUCCESS(rv, rv);
rv = GenerateFlatTextContent(range, aEvent->mReply.mString);
rv = GenerateFlatTextContent(range, aEvent->mReply.mString, lineBreakType);
NS_ENSURE_SUCCESS(rv, rv);
aEvent->mSucceeded = true;
@ -664,12 +709,13 @@ ContentEventHandler::OnQueryTextRect(WidgetQueryContentEvent* aEvent)
return rv;
}
LineBreakType lineBreakType = GetLineBreakType(aEvent);
nsRefPtr<nsRange> range = new nsRange(mRootContent);
rv = SetRangeFromFlatTextOffset(range, aEvent->mInput.mOffset,
aEvent->mInput.mLength, true,
aEvent->mInput.mLength, lineBreakType, true,
&aEvent->mReply.mOffset);
NS_ENSURE_SUCCESS(rv, rv);
rv = GenerateFlatTextContent(range, aEvent->mReply.mString);
rv = GenerateFlatTextContent(range, aEvent->mReply.mString, lineBreakType);
NS_ENSURE_SUCCESS(rv, rv);
// used to iterate over all contents and their frames
@ -772,6 +818,8 @@ ContentEventHandler::OnQueryCaretRect(WidgetQueryContentEvent* aEvent)
return rv;
}
LineBreakType lineBreakType = GetLineBreakType(aEvent);
nsRefPtr<nsCaret> caret = mPresShell->GetCaret();
NS_ASSERTION(caret, "GetCaret returned null");
@ -783,15 +831,9 @@ ContentEventHandler::OnQueryCaretRect(WidgetQueryContentEvent* aEvent)
if (selectionIsCollapsed) {
uint32_t offset;
rv = GetFlatTextOffsetOfRange(mRootContent, mFirstSelectedRange, &offset);
rv = GetFlatTextOffsetOfRange(mRootContent, mFirstSelectedRange, &offset,
lineBreakType);
NS_ENSURE_SUCCESS(rv, rv);
// strip out native new lines, we want the non-native offset. The offsets
// handed in here are from selection, caretPositionFromPoint, and editable
// element offset properties. We need to match those or things break.
nsINode* startNode = mFirstSelectedRange->GetStartParent();
if (startNode && startNode->IsNodeOfType(nsINode::eTEXT)) {
offset = ConvertToXPOffset(static_cast<nsIContent*>(startNode), offset);
}
if (offset == aEvent->mInput.mOffset) {
nsRect rect;
nsIFrame* caretFrame = caret->GetGeometry(mSelection, &rect);
@ -810,7 +852,8 @@ ContentEventHandler::OnQueryCaretRect(WidgetQueryContentEvent* aEvent)
// Otherwise, we should set the guessed caret rect.
nsRefPtr<nsRange> range = new nsRange(mRootContent);
rv = SetRangeFromFlatTextOffset(range, aEvent->mInput.mOffset, 0, true,
rv = SetRangeFromFlatTextOffset(range, aEvent->mInput.mOffset, 0,
lineBreakType, true,
&aEvent->mReply.mOffset);
NS_ENSURE_SUCCESS(rv, rv);
@ -902,6 +945,7 @@ ContentEventHandler::OnQueryCharacterAtPoint(WidgetQueryContentEvent* aEvent)
WidgetQueryContentEvent eventOnRoot(true, NS_QUERY_CHARACTER_AT_POINT,
rootWidget);
eventOnRoot.mUseNativeLineBreak = aEvent->mUseNativeLineBreak;
eventOnRoot.refPoint = aEvent->refPoint;
if (rootWidget != aEvent->widget) {
eventOnRoot.refPoint += LayoutDeviceIntPoint::FromUntyped(
@ -932,11 +976,11 @@ ContentEventHandler::OnQueryCharacterAtPoint(WidgetQueryContentEvent* aEvent)
NS_ENSURE_TRUE(offsets.content, NS_ERROR_FAILURE);
uint32_t nativeOffset;
rv = GetFlatTextOffsetOfRange(mRootContent, offsets.content, offsets.offset,
&nativeOffset);
&nativeOffset, GetLineBreakType(aEvent));
NS_ENSURE_SUCCESS(rv, rv);
WidgetQueryContentEvent textRect(true, NS_QUERY_TEXT_RECT, aEvent->widget);
textRect.InitForQueryTextRect(nativeOffset, 1);
textRect.InitForQueryTextRect(nativeOffset, 1, aEvent->mUseNativeLineBreak);
rv = OnQueryTextRect(&textRect);
NS_ENSURE_SUCCESS(rv, rv);
NS_ENSURE_TRUE(textRect.mSucceeded, NS_ERROR_FAILURE);
@ -992,11 +1036,12 @@ ContentEventHandler::OnQueryDOMWidgetHittest(WidgetQueryContentEvent* aEvent)
return NS_OK;
}
nsresult
/* static */ nsresult
ContentEventHandler::GetFlatTextOffsetOfRange(nsIContent* aRootContent,
nsINode* aNode,
int32_t aNodeOffset,
uint32_t* aNativeOffset)
uint32_t* aNativeOffset,
LineBreakType aLineBreakType)
{
NS_ENSURE_STATE(aRootContent);
NS_ASSERTION(aNativeOffset, "param is invalid");
@ -1040,14 +1085,14 @@ ContentEventHandler::GetFlatTextOffsetOfRange(nsIContent* aRootContent,
if (node->IsNodeOfType(nsINode::eTEXT)) {
// Note: our range always starts from offset 0
if (node == endNode) {
*aNativeOffset += GetNativeTextLength(content, aNodeOffset);
*aNativeOffset += GetTextLength(content, aLineBreakType, aNodeOffset);
} else {
*aNativeOffset += GetNativeTextLength(content);
*aNativeOffset += GetTextLength(content, aLineBreakType);
}
} else if (IsContentBR(content)) {
#if defined(XP_WIN)
// On Windows, the length of the newline is 2.
*aNativeOffset += 2;
*aNativeOffset += (aLineBreakType == LINE_BREAK_TYPE_NATIVE) ? 2 : 1;
#else
// On other platforms, the length of the newline is 1.
*aNativeOffset += 1;
@ -1057,16 +1102,17 @@ ContentEventHandler::GetFlatTextOffsetOfRange(nsIContent* aRootContent,
return NS_OK;
}
nsresult
/* static */ nsresult
ContentEventHandler::GetFlatTextOffsetOfRange(nsIContent* aRootContent,
nsRange* aRange,
uint32_t* aNativeOffset)
uint32_t* aNativeOffset,
LineBreakType aLineBreakType)
{
nsINode* startNode = aRange->GetStartParent();
NS_ENSURE_TRUE(startNode, NS_ERROR_FAILURE);
int32_t startOffset = aRange->StartOffset();
return GetFlatTextOffsetOfRange(aRootContent, startNode, startOffset,
aNativeOffset);
aNativeOffset, aLineBreakType);
}
nsresult
@ -1163,6 +1209,7 @@ ContentEventHandler::OnSelectionEvent(WidgetSelectionEvent* aEvent)
// Get range from offset and length
nsRefPtr<nsRange> range = new nsRange(mRootContent);
rv = SetRangeFromFlatTextOffset(range, aEvent->mOffset, aEvent->mLength,
GetLineBreakType(aEvent),
aEvent->mExpandToClusterBoundary);
NS_ENSURE_SUCCESS(rv, rv);

View File

@ -17,6 +17,12 @@ struct nsRect;
namespace mozilla {
enum LineBreakType
{
LINE_BREAK_TYPE_NATIVE,
LINE_BREAK_TYPE_XP
};
/*
* Query Content Event Handler
* ContentEventHandler is a helper class for EventStateManager.
@ -73,14 +79,22 @@ public:
static nsresult GetFlatTextOffsetOfRange(nsIContent* aRootContent,
nsINode* aNode,
int32_t aNodeOffset,
uint32_t* aOffset);
uint32_t* aOffset,
LineBreakType aLineBreakType);
static nsresult GetFlatTextOffsetOfRange(nsIContent* aRootContent,
nsRange* aRange,
uint32_t* aOffset);
uint32_t* aOffset,
LineBreakType aLineBreakType);
// Get the native text length of a content node excluding any children
static uint32_t GetNativeTextLength(nsIContent* aContent,
uint32_t aMaxLength = UINT32_MAX);
protected:
static uint32_t GetTextLength(nsIContent* aContent,
LineBreakType aLineBreakType,
uint32_t aMaxLength = UINT32_MAX);
static LineBreakType GetLineBreakType(WidgetQueryContentEvent* aEvent);
static LineBreakType GetLineBreakType(WidgetSelectionEvent* aEvent);
static LineBreakType GetLineBreakType(bool aUseNativeLineBreak);
// Returns focused content (including its descendant documents).
nsIContent* GetFocusedContent();
// Returns true if the content is a plugin host.
@ -94,6 +108,7 @@ protected:
nsresult SetRangeFromFlatTextOffset(nsRange* aRange,
uint32_t aNativeOffset,
uint32_t aNativeLength,
LineBreakType aLineBreakType,
bool aExpandToClusterBoundaries,
uint32_t* aNewNativeOffset = nullptr);
// Find the first textframe for the range, and get the start offset in

View File

@ -397,7 +397,8 @@ IMEContentObserver::CharacterDataChanged(nsIDocument* aDocument,
nsresult rv =
ContentEventHandler::GetFlatTextOffsetOfRange(mRootContent, aContent,
aInfo->mChangeStart,
&offset);
&offset,
LINE_BREAK_TYPE_NATIVE);
NS_ENSURE_SUCCESS_VOID(rv);
uint32_t oldEnd = offset + aInfo->mChangeEnd - aInfo->mChangeStart;
@ -421,14 +422,16 @@ IMEContentObserver::NotifyContentAdded(nsINode* aContainer,
uint32_t offset = 0;
nsresult rv =
ContentEventHandler::GetFlatTextOffsetOfRange(mRootContent, aContainer,
aStartIndex, &offset);
aStartIndex, &offset,
LINE_BREAK_TYPE_NATIVE);
NS_ENSURE_SUCCESS_VOID(rv);
// get offset at the end of the last added node
nsIContent* childAtStart = aContainer->GetChildAt(aStartIndex);
uint32_t addingLength = 0;
rv = ContentEventHandler::GetFlatTextOffsetOfRange(childAtStart, aContainer,
aEndIndex, &addingLength);
aEndIndex, &addingLength,
LINE_BREAK_TYPE_NATIVE);
NS_ENSURE_SUCCESS_VOID(rv);
if (!addingLength) {
@ -478,7 +481,8 @@ IMEContentObserver::ContentRemoved(nsIDocument* aDocument,
ContentEventHandler::GetFlatTextOffsetOfRange(mRootContent,
NODE_FROM(aContainer,
aDocument),
aIndexInContainer, &offset);
aIndexInContainer, &offset,
LINE_BREAK_TYPE_NATIVE);
NS_ENSURE_SUCCESS_VOID(rv);
// get offset at the end of the deleted node
@ -489,7 +493,8 @@ IMEContentObserver::ContentRemoved(nsIDocument* aDocument,
MOZ_ASSERT(nodeLength >= 0, "The node length is out of range");
uint32_t textLength = 0;
rv = ContentEventHandler::GetFlatTextOffsetOfRange(aChild, aChild,
nodeLength, &textLength);
nodeLength, &textLength,
LINE_BREAK_TYPE_NATIVE);
NS_ENSURE_SUCCESS_VOID(rv);
if (!textLength) {
@ -549,7 +554,8 @@ IMEContentObserver::AttributeChanged(nsIDocument* aDocument,
uint32_t start;
nsresult rv =
ContentEventHandler::GetFlatTextOffsetOfRange(mRootContent, content,
0, &start);
0, &start,
LINE_BREAK_TYPE_NATIVE);
NS_ENSURE_SUCCESS_VOID(rv);
nsContentUtils::AddScriptRunner(

View File

@ -337,9 +337,11 @@ public:
}
WidgetQueryContentEvent(bool aIsTrusted, uint32_t aMessage,
nsIWidget* aWidget) :
WidgetGUIEvent(aIsTrusted, aMessage, aWidget, NS_QUERY_CONTENT_EVENT),
mSucceeded(false), mWasAsync(false)
nsIWidget* aWidget)
: WidgetGUIEvent(aIsTrusted, aMessage, aWidget, NS_QUERY_CONTENT_EVENT)
, mSucceeded(false)
, mWasAsync(false)
, mUseNativeLineBreak(true)
{
}
@ -352,27 +354,33 @@ public:
return nullptr;
}
void InitForQueryTextContent(uint32_t aOffset, uint32_t aLength)
void InitForQueryTextContent(uint32_t aOffset, uint32_t aLength,
bool aUseNativeLineBreak = true)
{
NS_ASSERTION(message == NS_QUERY_TEXT_CONTENT,
"wrong initializer is called");
mInput.mOffset = aOffset;
mInput.mLength = aLength;
mUseNativeLineBreak = aUseNativeLineBreak;
}
void InitForQueryCaretRect(uint32_t aOffset)
void InitForQueryCaretRect(uint32_t aOffset,
bool aUseNativeLineBreak = true)
{
NS_ASSERTION(message == NS_QUERY_CARET_RECT,
"wrong initializer is called");
mInput.mOffset = aOffset;
mUseNativeLineBreak = aUseNativeLineBreak;
}
void InitForQueryTextRect(uint32_t aOffset, uint32_t aLength)
void InitForQueryTextRect(uint32_t aOffset, uint32_t aLength,
bool aUseNativeLineBreak = true)
{
NS_ASSERTION(message == NS_QUERY_TEXT_RECT,
"wrong initializer is called");
mInput.mOffset = aOffset;
mInput.mLength = aLength;
mUseNativeLineBreak = aUseNativeLineBreak;
}
void InitForQueryDOMWidgetHittest(const mozilla::LayoutDeviceIntPoint& aPoint)
@ -398,6 +406,7 @@ public:
bool mSucceeded;
bool mWasAsync;
bool mUseNativeLineBreak;
struct
{
uint32_t mOffset;
@ -473,6 +482,7 @@ public:
, mReversed(false)
, mExpandToClusterBoundary(true)
, mSucceeded(false)
, mUseNativeLineBreak(true)
{
}
@ -495,6 +505,8 @@ public:
bool mExpandToClusterBoundary;
// true if setting selection succeeded.
bool mSucceeded;
// true if native line breaks are used for mOffset and mLength
bool mUseNativeLineBreak;
};
/******************************************************************************

View File

@ -484,6 +484,7 @@ struct ParamTraits<mozilla::WidgetQueryContentEvent>
{
WriteParam(aMsg, static_cast<mozilla::WidgetGUIEvent>(aParam));
WriteParam(aMsg, aParam.mSucceeded);
WriteParam(aMsg, aParam.mUseNativeLineBreak);
WriteParam(aMsg, aParam.mInput.mOffset);
WriteParam(aMsg, aParam.mInput.mLength);
WriteParam(aMsg, aParam.mReply.mOffset);
@ -500,6 +501,7 @@ struct ParamTraits<mozilla::WidgetQueryContentEvent>
return ReadParam(aMsg, aIter,
static_cast<mozilla::WidgetGUIEvent*>(aResult)) &&
ReadParam(aMsg, aIter, &aResult->mSucceeded) &&
ReadParam(aMsg, aIter, &aResult->mUseNativeLineBreak) &&
ReadParam(aMsg, aIter, &aResult->mInput.mOffset) &&
ReadParam(aMsg, aIter, &aResult->mInput.mLength) &&
ReadParam(aMsg, aIter, &aResult->mReply.mOffset) &&
@ -525,6 +527,7 @@ struct ParamTraits<mozilla::WidgetSelectionEvent>
WriteParam(aMsg, aParam.mReversed);
WriteParam(aMsg, aParam.mExpandToClusterBoundary);
WriteParam(aMsg, aParam.mSucceeded);
WriteParam(aMsg, aParam.mUseNativeLineBreak);
}
static bool Read(const Message* aMsg, void** aIter, paramType* aResult)
@ -536,7 +539,8 @@ struct ParamTraits<mozilla::WidgetSelectionEvent>
ReadParam(aMsg, aIter, &aResult->mLength) &&
ReadParam(aMsg, aIter, &aResult->mReversed) &&
ReadParam(aMsg, aIter, &aResult->mExpandToClusterBoundary) &&
ReadParam(aMsg, aIter, &aResult->mSucceeded);
ReadParam(aMsg, aIter, &aResult->mSucceeded) &&
ReadParam(aMsg, aIter, &aResult->mUseNativeLineBreak);
}
};