Bug 998188 part.2 ContentEventHandler should support 2 modes, native line break mode and XP line break mode r=smaug
authorMasayuki Nakano <masayuki@d-toybox.com>
Sat, 26 Apr 2014 08:52:12 +0900
changeset 199916 394ef98ad57487dc03babef3b389ea625a1f1ee4
parent 199915 7f9e0440e61948be00da4aea40bbeb635243feab
child 199917 ac566b771d7b77652c910e45cb210386c56b303b
push id486
push userasasaki@mozilla.com
push dateMon, 14 Jul 2014 18:39:42 +0000
treeherdermozilla-release@d33428174ff1 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssmaug
bugs998188
milestone31.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 998188 part.2 ContentEventHandler should support 2 modes, native line break mode and XP line break mode r=smaug
dom/events/ContentEventHandler.cpp
dom/events/ContentEventHandler.h
dom/events/IMEContentObserver.cpp
widget/TextEvents.h
widget/nsGUIEventIPC.h
--- a/dom/events/ContentEventHandler.cpp
+++ b/dom/events/ContentEventHandler.cpp
@@ -199,16 +199,17 @@ static bool IsContentBR(nsIContent* aCon
                                 nsGkAtoms::mozeditorbogusnode,
                                 nsGkAtoms::_true,
                                 eIgnoreCase);
 }
 
 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"));
 #endif
 }
 
 static void AppendString(nsAString& aString, nsIContent* aContent)
 {
@@ -285,43 +286,52 @@ static uint32_t CountNewlinesInNativeLen
   return newlines;
 }
 #endif
 
 /* 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 =
 #if defined(XP_MACOSX)
       // On Mac, the length of a native newline ("\r") is equal to the length of
       // the XP newline ("\n"), so the native length is the same as the XP
       // length.
       0;
 #elif defined(XP_WIN)
       // 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;
 #endif
 
     const nsTextFragment* text = aContent->GetText();
     if (!text) {
       return 0;
     }
     uint32_t length = std::min(text->GetLength(), aMaxLength);
     return length + textLengthDifference;
   } 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
   }
   return 0;
 }
 
 static uint32_t ConvertToXPOffset(nsIContent* aContent, uint32_t aNativeOffset)
@@ -337,17 +347,18 @@ static uint32_t ConvertToXPOffset(nsICon
   return aNativeOffset - CountNewlinesInNativeLength(aContent, aNativeOffset);
 #else
   // On other platforms, the native and XP newlines are the same.
   return aNativeOffset;
 #endif
 }
 
 static nsresult GenerateFlatTextContent(nsRange* aRange,
-                                        nsAFlatString& aString)
+                                        nsAFlatString& aString,
+                                        LineBreakType aLineBreakType)
 {
   nsCOMPtr<nsIContentIterator> iter = NS_NewContentIterator();
   iter->Init(aRange);
 
   NS_ASSERTION(aString.IsEmpty(), "aString must be empty string");
 
   nsINode* startNode = aRange->GetStartParent();
   NS_ENSURE_TRUE(startNode, NS_ERROR_FAILURE);
@@ -381,17 +392,19 @@ static nsresult GenerateFlatTextContent(
         AppendSubString(aString, content, 0, aRange->EndOffset());
       } else {
         AppendString(aString, content);
       }
     } else if (IsContentBR(content)) {
       aString.Append(char16_t('\n'));
     }
   }
-  ConvertToNativeNewlines(aString);
+  if (aLineBreakType == LINE_BREAK_TYPE_NATIVE) {
+    ConvertToNativeNewlines(aString);
+  }
   return NS_OK;
 }
 
 nsresult
 ContentEventHandler::ExpandToClusterBoundary(nsIContent* aContent,
                                              bool aForward,
                                              uint32_t* aXPOffset)
 {
@@ -438,16 +451,17 @@ ContentEventHandler::ExpandToClusterBoun
   *aXPOffset = startOffset + newOffsetInFrame;
   return NS_OK;
 }
 
 nsresult
 ContentEventHandler::SetRangeFromFlatTextOffset(nsRange* aRange,
                                                 uint32_t aNativeOffset,
                                                 uint32_t aNativeLength,
+                                                LineBreakType aLineBreakType,
                                                 bool aExpandToClusterBoundaries,
                                                 uint32_t* aNewNativeOffset)
 {
   if (aNewNativeOffset) {
     *aNewNativeOffset = aNativeOffset;
   }
 
   nsCOMPtr<nsIContentIterator> iter = NS_NewPreContentIterator();
@@ -462,30 +476,35 @@ ContentEventHandler::SetRangeFromFlatTex
     if (!node) {
       break;
     }
     if (!node->IsNodeOfType(nsINode::eCONTENT)) {
       continue;
     }
     nsIContent* content = static_cast<nsIContent*>(node);
 
-    uint32_t nativeTextLength;
-    nativeTextLength = GetNativeTextLength(content);
+    uint32_t nativeTextLength = GetTextLength(content, aLineBreakType);
     if (nativeTextLength == 0) {
       continue;
     }
 
     if (nativeOffset <= aNativeOffset &&
         aNativeOffset < nativeOffset + nativeTextLength) {
       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;
         rv = ExpandToClusterBoundary(content, false, &xpOffset);
         NS_ENSURE_SUCCESS(rv, rv);
         if (aNewNativeOffset) {
           *aNewNativeOffset -= (oldXPOffset - xpOffset);
         }
@@ -502,17 +521,20 @@ ContentEventHandler::SetRangeFromFlatTex
       }
     }
     if (nativeEndOffset <= nativeOffset + nativeTextLength) {
       nsCOMPtr<nsIDOMNode> domNode(do_QueryInterface(content));
       NS_ASSERTION(domNode, "aContent doesn't have nsIDOMNode!");
 
       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);
         }
       } else {
         // Use first position of next node, because the end node is ignored
         // by ContentIterator when the offset is zero.
         xpOffset = 0;
@@ -545,29 +567,49 @@ ContentEventHandler::SetRangeFromFlatTex
       *aNewNativeOffset = nativeOffset;
     }
   }
   rv = aRange->SetEnd(domNode, int32_t(mRootContent->GetChildCount()));
   NS_ASSERTION(NS_SUCCEEDED(rv), "nsIDOMRange::SetEnd failed");
   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)
 {
   nsresult rv = Init(aEvent);
   if (NS_FAILED(rv)) {
     return rv;
   }
 
   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;
   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);
 
@@ -581,17 +623,18 @@ ContentEventHandler::OnQuerySelectedText
   nsCOMPtr<nsINode> focusNode(do_QueryInterface(focusDomNode));
   NS_ENSURE_TRUE(anchorNode && focusNode, NS_ERROR_UNEXPECTED);
 
   int16_t compare = nsContentUtils::ComparePoints(anchorNode, anchorOffset,
                                                   focusNode, focusOffset);
   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);
   }
 
   aEvent->mSucceeded = true;
   return NS_OK;
 }
 
 nsresult
@@ -600,23 +643,25 @@ ContentEventHandler::OnQueryTextContent(
   nsresult rv = Init(aEvent);
   if (NS_FAILED(rv)) {
     return rv;
   }
 
   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;
 
   return NS_OK;
 }
 
 // Adjust to use a child node if possible
@@ -659,22 +704,23 @@ static nsresult GetFrameForTextRect(nsIN
 nsresult
 ContentEventHandler::OnQueryTextRect(WidgetQueryContentEvent* aEvent)
 {
   nsresult rv = Init(aEvent);
   if (NS_FAILED(rv)) {
     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
   nsCOMPtr<nsIContentIterator> iter = NS_NewContentIterator();
   iter->Init(range);
 
   // get the starting frame
   int32_t offset = range->StartOffset();
@@ -767,36 +813,32 @@ ContentEventHandler::OnQueryEditorRect(W
 nsresult
 ContentEventHandler::OnQueryCaretRect(WidgetQueryContentEvent* aEvent)
 {
   nsresult rv = Init(aEvent);
   if (NS_FAILED(rv)) {
     return rv;
   }
 
+  LineBreakType lineBreakType = GetLineBreakType(aEvent);
+
   nsRefPtr<nsCaret> caret = mPresShell->GetCaret();
   NS_ASSERTION(caret, "GetCaret returned null");
 
   // When the selection is collapsed and the queried offset is current caret
   // position, we should return the "real" caret rect.
   bool selectionIsCollapsed;
   rv = mSelection->GetIsCollapsed(&selectionIsCollapsed);
   NS_ENSURE_SUCCESS(rv, rv);
 
   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);
       if (!caretFrame) {
         return NS_ERROR_FAILURE;
       }
       rv = ConvertToRootViewRelativeOffset(caretFrame, rect);
       NS_ENSURE_SUCCESS(rv, rv);
@@ -805,17 +847,18 @@ ContentEventHandler::OnQueryCaretRect(Wi
       aEvent->mReply.mOffset = aEvent->mInput.mOffset;
       aEvent->mSucceeded = true;
       return NS_OK;
     }
   }
 
   // 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);
 
   int32_t offsetInFrame;
   nsIFrame* frame;
   rv = GetStartFrameAndOffset(range, &frame, &offsetInFrame);
   NS_ENSURE_SUCCESS(rv, rv);
 
@@ -897,16 +940,17 @@ ContentEventHandler::OnQueryCharacterAtP
     rootFrame = view->GetFrame();
     NS_ENSURE_TRUE(rootFrame, NS_ERROR_FAILURE);
     rootWidget = rootFrame->GetNearestWidget();
     NS_ENSURE_TRUE(rootWidget, NS_ERROR_FAILURE);
   }
 
   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(
       aEvent->widget->WidgetToScreenOffset() -
         rootWidget->WidgetToScreenOffset());
   }
   nsPoint ptInRoot =
     nsLayoutUtils::GetEventCoordinatesRelativeTo(&eventOnRoot, rootFrame);
@@ -927,21 +971,21 @@ ContentEventHandler::OnQueryCharacterAtP
   ptInTarget = ptInTarget.ConvertAppUnits(rootAPD, targetAPD);
 
   nsTextFrame* textframe = static_cast<nsTextFrame*>(targetFrame);
   nsIFrame::ContentOffsets offsets =
     textframe->GetCharacterOffsetAtFramePoint(ptInTarget);
   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);
 
   // currently, we don't need to get the actual text.
   aEvent->mReply.mOffset = nativeOffset;
   aEvent->mReply.mRect = textRect.mReply.mRect;
   aEvent->mSucceeded = true;
@@ -987,21 +1031,22 @@ ContentEventHandler::OnQueryDOMWidgetHit
       aEvent->mReply.mWidgetIsHit = true;
     }
   }
 
   aEvent->mSucceeded = true;
   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");
 
   nsRefPtr<nsRange> prev = new nsRange(aRootContent);
   nsCOMPtr<nsIDOMNode> rootDOMNode(do_QueryInterface(aRootContent));
   prev->SetStart(rootDOMNode, 0);
 
@@ -1035,43 +1080,44 @@ ContentEventHandler::GetFlatTextOffsetOf
     if (!node->IsNodeOfType(nsINode::eCONTENT)) {
       continue;
     }
     nsIContent* content = static_cast<nsIContent*>(node);
 
     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;
 #endif
     }
   }
   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
 ContentEventHandler::GetStartFrameAndOffset(nsRange* aRange,
                                             nsIFrame** aFrame,
                                             int32_t* aOffsetInFrame)
 {
   NS_ASSERTION(aRange && aFrame && aOffsetInFrame, "params are invalid");
@@ -1158,16 +1204,17 @@ ContentEventHandler::OnSelectionEvent(Wi
   } else {
     rv = Init(aEvent);
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   // 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);
 
   nsINode* startNode = range->GetStartParent();
   nsINode* endNode = range->GetEndParent();
   int32_t startOffset = range->StartOffset();
   int32_t endOffset = range->EndOffset();
   AdjustRangeForSelection(mRootContent, &startNode, &startOffset);
--- a/dom/events/ContentEventHandler.h
+++ b/dom/events/ContentEventHandler.h
@@ -12,16 +12,22 @@
 #include "nsRange.h"
 
 class nsPresContext;
 
 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.
  *   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.
  */
 
@@ -68,37 +74,46 @@ protected:
 public:
   // FlatText means the text that is generated from DOM tree. The BR elements
   // are replaced to native linefeeds. Other elements are ignored.
 
   // Get the offset in FlatText of the range. (also used by IMEContentObserver)
   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.
   bool IsPlugin(nsIContent* aContent);
   // QueryContentRect() sets the rect of aContent's frame(s) to aEvent.
   nsresult QueryContentRect(nsIContent* aContent,
                             WidgetQueryContentEvent* aEvent);
   // 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.
   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
   // the frame.
   nsresult GetStartFrameAndOffset(nsRange* aRange,
                                   nsIFrame** aFrame,
                                   int32_t* aOffsetInFrame);
   // Convert the frame relative offset to the root view relative offset.
--- a/dom/events/IMEContentObserver.cpp
+++ b/dom/events/IMEContentObserver.cpp
@@ -392,17 +392,18 @@ IMEContentObserver::CharacterDataChanged
     return;
   }
 
   uint32_t offset = 0;
   // get offsets of change and fire notification
   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;
   uint32_t newEnd = offset + aInfo->mReplaceLength;
 
   nsContentUtils::AddScriptRunner(
     new TextChangeEvent(this, offset, oldEnd, newEnd, causedByComposition));
 }
@@ -416,24 +417,26 @@ IMEContentObserver::NotifyContentAdded(n
   if (causedByComposition &&
       !mUpdatePreference.WantChangesCausedByComposition()) {
     return;
   }
 
   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) {
     return;
   }
 
   nsContentUtils::AddScriptRunner(
     new TextChangeEvent(this, offset, offset, offset + addingLength,
@@ -473,28 +476,30 @@ IMEContentObserver::ContentRemoved(nsIDo
     return;
   }
 
   uint32_t offset = 0;
   nsresult rv =
     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
   int32_t nodeLength =
     aChild->IsNodeOfType(nsINode::eTEXT) ?
       static_cast<int32_t>(aChild->TextLength()) :
       std::max(static_cast<int32_t>(aChild->GetChildCount()), 1);
   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) {
     return;
   }
 
   nsContentUtils::AddScriptRunner(
     new TextChangeEvent(this, offset, offset + textLength, offset,
@@ -544,17 +549,18 @@ IMEContentObserver::AttributeChanged(nsI
   uint32_t postAttrChangeLength =
     ContentEventHandler::GetNativeTextLength(content);
   if (postAttrChangeLength == mPreAttrChangeLength) {
     return;
   }
   uint32_t start;
   nsresult rv =
     ContentEventHandler::GetFlatTextOffsetOfRange(mRootContent, content,
-                                                  0, &start);
+                                                  0, &start,
+                                                  LINE_BREAK_TYPE_NATIVE);
   NS_ENSURE_SUCCESS_VOID(rv);
 
   nsContentUtils::AddScriptRunner(
     new TextChangeEvent(this, start, start + mPreAttrChangeLength,
                         start + postAttrChangeLength, causedByComposition));
 }
 
 } // namespace mozilla
--- a/widget/TextEvents.h
+++ b/widget/TextEvents.h
@@ -332,52 +332,60 @@ private:
 
 public:
   virtual WidgetQueryContentEvent* AsQueryContentEvent() MOZ_OVERRIDE
   {
     return this;
   }
 
   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)
   {
   }
 
   virtual WidgetEvent* Duplicate() const MOZ_OVERRIDE
   {
     // This event isn't an internal event of any DOM event.
     NS_ASSERTION(!IsAllowedToDispatchDOMEvent(),
       "WidgetQueryContentEvent needs to support Duplicate()");
     MOZ_CRASH("WidgetQueryContentEvent doesn't support Duplicate()");
     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)
   {
     NS_ASSERTION(message == NS_QUERY_DOM_WIDGET_HITTEST,
                  "wrong initializer is called");
     refPoint = aPoint;
   }
@@ -393,16 +401,17 @@ public:
   {
     NS_ASSERTION(message == NS_QUERY_SELECTED_TEXT,
                  "not querying selection");
     return mReply.mOffset + (mReply.mReversed ? 0 : mReply.mString.Length());
   }
 
   bool mSucceeded;
   bool mWasAsync;
+  bool mUseNativeLineBreak;
   struct
   {
     uint32_t mOffset;
     uint32_t mLength;
   } mInput;
   struct
   {
     void* mContentsRoot;
@@ -468,16 +477,17 @@ public:
   WidgetSelectionEvent(bool aIsTrusted, uint32_t aMessage, nsIWidget* aWidget)
     : WidgetGUIEvent(aIsTrusted, aMessage, aWidget, NS_SELECTION_EVENT)
     , mSeqno(kLatestSeqno)
     , mOffset(0)
     , mLength(0)
     , mReversed(false)
     , mExpandToClusterBoundary(true)
     , mSucceeded(false)
+    , mUseNativeLineBreak(true)
   {
   }
 
   virtual WidgetEvent* Duplicate() const MOZ_OVERRIDE
   {
     // This event isn't an internal event of any DOM event.
     NS_ASSERTION(!IsAllowedToDispatchDOMEvent(),
       "WidgetSelectionEvent needs to support Duplicate()");
@@ -490,16 +500,18 @@ public:
   // Length of selection
   uint32_t mLength;
   // Selection "anchor" should be in front
   bool mReversed;
   // Cluster-based or character-based
   bool mExpandToClusterBoundary;
   // true if setting selection succeeded.
   bool mSucceeded;
+  // true if native line breaks are used for mOffset and mLength
+  bool mUseNativeLineBreak;
 };
 
 /******************************************************************************
  * mozilla::InternalEditorInputEvent
  ******************************************************************************/
 
 class InternalEditorInputEvent : public InternalUIEvent
 {
--- a/widget/nsGUIEventIPC.h
+++ b/widget/nsGUIEventIPC.h
@@ -479,32 +479,34 @@ template<>
 struct ParamTraits<mozilla::WidgetQueryContentEvent>
 {
   typedef mozilla::WidgetQueryContentEvent paramType;
 
   static void Write(Message* aMsg, const paramType& aParam)
   {
     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);
     WriteParam(aMsg, aParam.mReply.mString);
     WriteParam(aMsg, aParam.mReply.mRect);
     WriteParam(aMsg, aParam.mReply.mReversed);
     WriteParam(aMsg, aParam.mReply.mHasSelection);
     WriteParam(aMsg, aParam.mReply.mWidgetIsHit);
   }
 
   static bool Read(const Message* aMsg, void** aIter, paramType* aResult)
   {
     aResult->mWasAsync = true;
     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) &&
            ReadParam(aMsg, aIter, &aResult->mReply.mString) &&
            ReadParam(aMsg, aIter, &aResult->mReply.mRect) &&
            ReadParam(aMsg, aIter, &aResult->mReply.mReversed) &&
            ReadParam(aMsg, aIter, &aResult->mReply.mHasSelection) &&
            ReadParam(aMsg, aIter, &aResult->mReply.mWidgetIsHit);
@@ -520,28 +522,30 @@ struct ParamTraits<mozilla::WidgetSelect
   {
     WriteParam(aMsg, static_cast<mozilla::WidgetGUIEvent>(aParam));
     WriteParam(aMsg, aParam.mSeqno);
     WriteParam(aMsg, aParam.mOffset);
     WriteParam(aMsg, aParam.mLength);
     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)
   {
     return ReadParam(aMsg, aIter,
                      static_cast<mozilla::WidgetGUIEvent*>(aResult)) &&
            ReadParam(aMsg, aIter, &aResult->mSeqno) &&
            ReadParam(aMsg, aIter, &aResult->mOffset) &&
            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);
   }
 };
 
 template<>
 struct ParamTraits<nsIMEUpdatePreference>
 {
   typedef nsIMEUpdatePreference paramType;