Bug 1286464 part.16 Rename ContentEventHandler::Get*FrameHavingFlatTextInRange() to ContentEventHandler::Get*FrameInRangeForTextRect() and make them treat a moz-<br> element as a normal <br> element because it doesn't cause text but needs to compute text rect in the last empty line r=smaug draft
authorMasayuki Nakano <masayuki@d-toybox.com>
Tue, 02 Aug 2016 14:00:32 +0900
changeset 397255 38b19a4575945910f03437a1cc04196618cbc996
parent 397254 ac35c4e72a9d5382bd62bca12cb3b03a21e7d489
child 397256 6e40f378b4fe7432ebcbcf7be008a55955b5e812
push id25246
push usermasayuki@d-toybox.com
push dateFri, 05 Aug 2016 15:41:11 +0000
reviewerssmaug
bugs1286464
milestone51.0a1
Bug 1286464 part.16 Rename ContentEventHandler::Get*FrameHavingFlatTextInRange() to ContentEventHandler::Get*FrameInRangeForTextRect() and make them treat a moz-<br> element as a normal <br> element because it doesn't cause text but needs to compute text rect in the last empty line r=smaug In plain text editor, moz-<br> element is appended for a placeholder of empty line when the text ends with a line breaker. Therefore, when we compute text rects, we need to handle it same as a normal <br> element even though it doesn't cause any text. MozReview-Commit-ID: 4IXLafU6o0W
dom/events/ContentEventHandler.cpp
dom/events/ContentEventHandler.h
--- a/dom/events/ContentEventHandler.cpp
+++ b/dom/events/ContentEventHandler.cpp
@@ -415,16 +415,21 @@ static bool IsContentBR(nsIContent* aCon
                                 nsGkAtoms::moz,
                                 eIgnoreCase) &&
          !aContent->AttrValueIs(kNameSpaceID_None,
                                 nsGkAtoms::mozeditorbogusnode,
                                 nsGkAtoms::_true,
                                 eIgnoreCase);
 }
 
+static bool IsMozBR(nsIContent* aContent)
+{
+  return aContent->IsHTMLElement(nsGkAtoms::br) && !IsContentBR(aContent);
+}
+
 static void ConvertToNativeNewlines(nsAFlatString& aString)
 {
 #if defined(XP_WIN)
   aString.ReplaceSubstring(NS_LITERAL_STRING("\n"), NS_LITERAL_STRING("\r\n"));
 #endif
 }
 
 static void AppendString(nsAString& aString, nsIContent* aContent)
@@ -1445,17 +1450,17 @@ ContentEventHandler::GetNodePositionHavi
       static_cast<int32_t>(result.mNode->AsContent()->TextLength()) : 1;
   }
 
   NS_WARNING("aNodeOffset is invalid value");
   return NodePosition();
 }
 
 ContentEventHandler::FrameAndNodeOffset
-ContentEventHandler::GetFirstFrameHavingFlatTextInRange(nsRange* aRange)
+ContentEventHandler::GetFirstFrameInRangeForTextRect(nsRange* aRange)
 {
   NodePosition nodePosition;
   nsCOMPtr<nsIContentIterator> iter = NS_NewPreContentIterator();
   for (iter->Init(aRange); !iter->IsDone(); iter->Next()) {
     nsINode* node = iter->GetCurrentNode();
     if (NS_WARN_IF(!node)) {
       break;
     }
@@ -1474,34 +1479,35 @@ ContentEventHandler::GetFirstFrameHaving
         nodePosition.mOffset = offsetInNode;
         break;
       }
       continue;
     }
 
     // If the element node causes a line break before it, it's the first
     // node causing text.
-    if (ShouldBreakLineBefore(node->AsContent(), mRootContent)) {
+    if (ShouldBreakLineBefore(node->AsContent(), mRootContent) ||
+        IsMozBR(node->AsContent())) {
       nodePosition.mNode = node;
       nodePosition.mOffset = 0;
     }
   }
 
   if (!nodePosition.IsValid()) {
     return FrameAndNodeOffset();
   }
 
   nsIFrame* firstFrame = nullptr;
   GetFrameForTextRect(nodePosition.mNode, nodePosition.mOffset,
                       true, &firstFrame);
   return FrameAndNodeOffset(firstFrame, nodePosition.mOffset);
 }
 
 ContentEventHandler::FrameAndNodeOffset
-ContentEventHandler::GetLastFrameHavingFlatTextInRange(nsRange* aRange)
+ContentEventHandler::GetLastFrameInRangeForTextRect(nsRange* aRange)
 {
   NodePosition nodePosition;
   nsCOMPtr<nsIContentIterator> iter = NS_NewPreContentIterator();
   iter->Init(aRange);
 
   nsINode* endNode = aRange->GetEndParent();
   uint32_t endOffset = static_cast<uint32_t>(aRange->EndOffset());
   // If the end point is start of a text node or specified by its parent and
@@ -1542,17 +1548,18 @@ ContentEventHandler::GetLastFrameHavingF
       if (node == aRange->GetEndParent()) {
         nodePosition.mOffset = aRange->EndOffset();
       } else {
         nodePosition.mOffset = node->Length();
       }
       break;
     }
 
-    if (ShouldBreakLineBefore(node->AsContent(), mRootContent)) {
+    if (ShouldBreakLineBefore(node->AsContent(), mRootContent) ||
+        IsMozBR(node->AsContent())) {
       nodePosition.mNode = node;
       nodePosition.mOffset = 0;
       break;
     }
   }
 
   if (!nodePosition.IsValid()) {
     return FrameAndNodeOffset();
@@ -1591,18 +1598,20 @@ ContentEventHandler::GetLastFrameHavingF
 
   return FrameAndNodeOffset(lastFrame, nodePosition.mOffset);
 }
 
 ContentEventHandler::FrameRelativeRect
 ContentEventHandler::GetLineBreakerRectBefore(nsIFrame* aFrame)
 {
   // Note that this method should be called only with an element's frame whose
-  // open tag causes a line break.
-  MOZ_ASSERT(ShouldBreakLineBefore(aFrame->GetContent(), mRootContent));
+  // open tag causes a line break or moz-<br> for computing empty last line's
+  // rect.
+  MOZ_ASSERT(ShouldBreakLineBefore(aFrame->GetContent(), mRootContent) ||
+             IsMozBR(aFrame->GetContent()));
 
   nsIFrame* frameForFontMetrics = aFrame;
 
   // If it's not a <br> frame, this method computes the line breaker's rect
   // outside the frame.  Therefore, we need to compute with parent frame's
   // font metrics in such case.
   if (aFrame->GetType() != nsGkAtoms::brFrame && aFrame->GetParent()) {
     frameForFontMetrics = aFrame->GetParent();
@@ -1696,19 +1705,19 @@ ContentEventHandler::OnQueryTextRectArra
 
     // If the range is collapsed, offset has already reached the end of the
     // contents.
     if (range->Collapsed()) {
       break;
     }
 
     // Get the first frame which causes some text after the offset.
-    FrameAndNodeOffset firstFrame = GetFirstFrameHavingFlatTextInRange(range);
+    FrameAndNodeOffset firstFrame = GetFirstFrameInRangeForTextRect(range);
 
-    // If GetFirstFrameHavingFlatTextInRange() does not return valid frame,
+    // If GetFirstFrameInRangeForTextRect() does not return valid frame,
     // that means that there is no remaining content which causes text.
     // So, in such case, we must have reached the end of the contents.
     if (!firstFrame.IsValid()) {
       break;
     }
 
     nsIContent* firstContent = firstFrame.mFrame->GetContent();
     if (NS_WARN_IF(!firstContent)) {
@@ -1724,17 +1733,18 @@ ContentEventHandler::OnQueryTextRectArra
 
     bool startsBetweenLineBreaker = false;
     nsAutoString chars;
     // XXX not bidi-aware this class...
     isVertical = firstFrame->GetWritingMode().IsVertical();
 
     AutoTArray<nsRect, 16> charRects;
 
-    if (ShouldBreakLineBefore(firstContent, mRootContent)) {
+    if (ShouldBreakLineBefore(firstContent, mRootContent) ||
+        IsMozBR(firstContent)) {
       nsRect brRect;
       // If the frame is <br> frame, we can always trust
       // GetLineBreakerRectBefore().  Otherwise, "after" the last character
       // rect is better than its result.
       // TODO: We need to look for the last character rect if non-br frame
       //       causes a line break at first time of this loop.
       if (firstFrame->GetType() == nsGkAtoms::brFrame ||
           aEvent->mInput.mOffset == offset) {
@@ -1765,17 +1775,17 @@ ContentEventHandler::OnQueryTextRectArra
         // the start offset is between the first line breaker (i.e., the range
         // starts between "\r" and "\n").
         rv = SetRangeFromFlatTextOffset(range, aEvent->mInput.mOffset - 1, 1,
                                         lineBreakType, true, nullptr);
         if (NS_WARN_IF(NS_FAILED(rv))) {
           return NS_ERROR_UNEXPECTED;
         }
         FrameAndNodeOffset frameForPrevious =
-          GetFirstFrameHavingFlatTextInRange(range);
+          GetFirstFrameInRangeForTextRect(range);
         startsBetweenLineBreaker = frameForPrevious.mFrame == firstFrame.mFrame;
       }
     } else {
       rv = firstFrame->GetCharacterRectsInRange(firstFrame.mOffsetInNode,
                                                 kEndOffset - offset, charRects);
       if (NS_WARN_IF(NS_FAILED(rv)) || NS_WARN_IF(charRects.IsEmpty())) {
         return rv;
       }
@@ -1925,19 +1935,19 @@ ContentEventHandler::OnQueryTextRect(Wid
   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 first frame which causes some text after the offset.
-  FrameAndNodeOffset firstFrame = GetFirstFrameHavingFlatTextInRange(range);
+  FrameAndNodeOffset firstFrame = GetFirstFrameInRangeForTextRect(range);
 
-  // If GetFirstFrameHavingFlatTextInRange() does not return valid frame,
+  // If GetFirstFrameInRangeForTextRect() does not return valid frame,
   // that means that there is no remaining content which causes text.
   // So, in such case, we must have reached the end of the contents.
   if (!firstFrame.IsValid()) {
     // TODO: Handle this case later.
     return NS_ERROR_FAILURE;
   }
 
   // For GetLineBreakerRectBefore() doesn't work well with block frame, so,
@@ -1978,17 +1988,17 @@ ContentEventHandler::OnQueryTextRect(Wid
     }
     frameRect = rect;
   }
   // UnionRect() requires non-empty rect.  So, let's make sure to get non-emtpy
   // rect from the first frame.
   EnsureNonEmptyRect(rect);
 
   // Get the last frame which causes some text in the range.
-  FrameAndNodeOffset lastFrame = GetLastFrameHavingFlatTextInRange(range);
+  FrameAndNodeOffset lastFrame = GetLastFrameInRangeForTextRect(range);
   if (NS_WARN_IF(!lastFrame.IsValid())) {
     return NS_ERROR_FAILURE;
   }
 
   // iterate over all covered frames
   for (nsIFrame* frame = firstFrame; frame != lastFrame;) {
     frame = frame->GetNextContinuation();
     if (!frame) {
@@ -2069,16 +2079,17 @@ ContentEventHandler::OnQueryTextRect(Wid
     EnsureNonEmptyRect(frameRect);
 
     if (firstFrame.mFrame == lastFrame.mFrame) {
       rect.IntersectRect(rect, frameRect);
     } else {
       rect.UnionRect(rect, frameRect);
     }
   }
+
   aEvent->mReply.mRect = LayoutDeviceIntRect::FromUnknownRect(
       rect.ToOutsidePixels(mPresContext->AppUnitsPerDevPixel()));
   // Returning empty rect may cause native IME confused, let's make sure to
   // return non-empty rect.
   EnsureNonEmptyRect(aEvent->mReply.mRect);
   aEvent->mReply.mWritingMode = lastFrame->GetWritingMode();
   aEvent->mSucceeded = true;
   return NS_OK;
--- a/dom/events/ContentEventHandler.h
+++ b/dom/events/ContentEventHandler.h
@@ -336,23 +336,25 @@ protected:
     nsIFrame* operator->() { return mFrame; }
     const nsIFrame* operator->() const { return mFrame; }
     operator nsIFrame*() { return mFrame; }
     operator const nsIFrame*() const { return mFrame; }
     bool IsValid() const { return mFrame && mOffsetInNode >= 0; }
   };
   // Get first frame after the start of the given range for computing text rect.
   // This returns invalid FrameAndNodeOffset if there is no content which
-  // causes text in the range.  mOffsetInNode is start offset in the frame.
-  FrameAndNodeOffset GetFirstFrameHavingFlatTextInRange(nsRange* aRange);
+  // should affect to computing text rect in the range.  mOffsetInNode is start
+  // offset in the frame.
+  FrameAndNodeOffset GetFirstFrameInRangeForTextRect(nsRange* aRange);
 
   // Get last frame before the end of the given range for computing text rect.
   // This returns invalid FrameAndNodeOffset if there is no content which
-  // causes text in the range.  mOffsetInOde is end offset in the frame.
-  FrameAndNodeOffset GetLastFrameHavingFlatTextInRange(nsRange* aRange);
+  // should affect to computing text rect in the range.  mOffsetInOde is end
+  // offset in the frame.
+  FrameAndNodeOffset GetLastFrameInRangeForTextRect(nsRange* aRange);
 
   struct MOZ_STACK_CLASS FrameRelativeRect final
   {
     // mRect is relative to the mBaseFrame's position.
     nsRect mRect;
     nsIFrame* mBaseFrame;
 
     FrameRelativeRect()