Bug 1286464 part.18 ContentEventHandler::OnQueryTextRectArray() should compute line breaker's rect with the last character's node when the start of queried range is a line breaker and it's caused by a block frame r?smaug draft
authorMasayuki Nakano <masayuki@d-toybox.com>
Fri, 29 Jul 2016 20:50:56 +0900
changeset 394880 c7ef92d5169557ff2fdb2edb897ee82729fe1b52
parent 394879 43eb79cff14baebb79ef8bc02df57b544d876534
child 394881 e6b39b2641222604da89eac110fd1acf2b935cfd
push id24655
push usermasayuki@d-toybox.com
push dateMon, 01 Aug 2016 08:00:00 +0000
reviewerssmaug
bugs1286464
milestone50.0a1
Bug 1286464 part.18 ContentEventHandler::OnQueryTextRectArray() should compute line breaker's rect with the last character's node when the start of queried range is a line breaker and it's caused by a block frame r?smaug Similar to OnQueryTextRect(), OnQueryTextRectArray() should compute a line breaker's rect with the last character when it's the first character in queried range and not caused by <br> frame. MozReview-Commit-ID: CGDHbcrc6zB
dom/events/ContentEventHandler.cpp
dom/events/ContentEventHandler.h
--- a/dom/events/ContentEventHandler.cpp
+++ b/dom/events/ContentEventHandler.cpp
@@ -1636,16 +1636,45 @@ ContentEventHandler::GetLineBreakerRectB
       // left of top-left corner of aFrame.
       result.mRect.x = -mPresContext->AppUnitsPerDevPixel();
       result.mRect.y = 0;
     }
   }
   return result;
 }
 
+ContentEventHandler::FrameRelativeRect
+ContentEventHandler::GetLineBreakerRectAfter(nsIContent* aTextContent)
+{
+  MOZ_ASSERT(aTextContent->IsNodeOfType(nsINode::eTEXT));
+
+  FrameRelativeRect result;
+  int32_t length = static_cast<int32_t>(aTextContent->Length());
+  if (NS_WARN_IF(length < 0)) {
+    return result;
+  }
+  nsIFrame* lastTextFrame = nullptr;
+  nsresult rv = GetFrameForTextRect(aTextContent, length, true, &lastTextFrame);
+  if (NS_WARN_IF(NS_FAILED(rv)) || NS_WARN_IF(!lastTextFrame)) {
+    return result;
+  }
+  const nsRect kLastTextFrameRect = lastTextFrame->GetRect();
+  if (lastTextFrame->GetWritingMode().IsVertical()) {
+    // Below of the last text frame.
+    result.mRect.SetRect(0, kLastTextFrameRect.height,
+                         kLastTextFrameRect.width, 0);
+  } else {
+    // Right of the last text frame (not bidi-aware).
+    result.mRect.SetRect(kLastTextFrameRect.width, 0,
+                         0, kLastTextFrameRect.height);
+  }
+  result.mBaseFrame = lastTextFrame;
+  return result;
+}
+
 nsresult
 ContentEventHandler::OnQueryTextRectArray(WidgetQueryContentEvent* aEvent)
 {
   nsresult rv = Init(aEvent);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
@@ -1656,18 +1685,19 @@ ContentEventHandler::OnQueryTextRectArra
 
   bool isVertical = false;
   LayoutDeviceIntRect rect;
   uint32_t offset = aEvent->mInput.mOffset;
   const uint32_t kEndOffset = offset + aEvent->mInput.mLength;
   bool wasLineBreaker = false;
   nsRect lastCharRect;
   while (offset < kEndOffset) {
+    nsCOMPtr<nsIContent> lastTextContent;
     rv = SetRangeFromFlatTextOffset(range, offset, 1, lineBreakType, true,
-                                    nullptr);
+                                    nullptr, getter_AddRefs(lastTextContent));
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
 
     // If the range is collapsed, offset has already reached the end of the
     // contents.
     if (range->Collapsed()) {
       break;
@@ -1702,22 +1732,26 @@ ContentEventHandler::OnQueryTextRectArra
 
     AutoTArray<nsRect, 16> charRects;
 
     if (ShouldBreakLineBefore(firstContent, mRootContent)) {
       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) {
+      if (firstFrame->GetType() == nsGkAtoms::brFrame || !lastTextContent) {
         FrameRelativeRect relativeBRRect = GetLineBreakerRectBefore(firstFrame);
         brRect = relativeBRRect.RectRelativeTo(firstFrame);
+      } else if (aEvent->mInput.mOffset == offset) {
+        FrameRelativeRect brRectRelativeToLastTextFrame =
+          GetLineBreakerRectAfter(lastTextContent);
+        if (NS_WARN_IF(!brRectRelativeToLastTextFrame.IsValid())) {
+          return NS_ERROR_FAILURE;
+        }
+        brRect = brRectRelativeToLastTextFrame.RectRelativeTo(firstFrame);
       } else {
         // The frame position in the root widget will be added in the
         // following for loop but we need the rect in the previous frame.
         // So, we need to avoid using current frame position.
         brRect = lastCharRect - frameRect.TopLeft();
         if (!wasLineBreaker) {
           if (isVertical) {
             brRect.y = brRect.YMost() + 1;
@@ -1916,36 +1950,23 @@ ContentEventHandler::OnQueryTextRect(Wid
       rect.height -= ptOffset.y;
     } else {
       rect.x += ptOffset.x;
       rect.width -= ptOffset.x;
     }
   } else if (firstFrame->GetType() != nsGkAtoms::brFrame && lastTextContent) {
     // If the node isn't <br> frame but there is a text node, we should
     // compute the line break rect with the last character.
-    int32_t length = static_cast<int32_t>(lastTextContent->Length());
-    if (NS_WARN_IF(length < 0)) {
-      return NS_ERROR_FAILURE;
-    }
-    nsIFrame* lastTextFrame = nullptr;
-    rv = GetFrameForTextRect(lastTextContent, length, true, &lastTextFrame);
-    if (NS_WARN_IF(NS_FAILED(rv)) || NS_WARN_IF(!lastTextFrame)) {
+    FrameRelativeRect brRectAfterLastChar =
+      GetLineBreakerRectAfter(lastTextContent);
+    if (NS_WARN_IF(!brRectAfterLastChar.IsValid())) {
       return NS_ERROR_FAILURE;
     }
-    const nsRect kLastTextFrameRect = lastTextFrame->GetRect();
-    if (lastTextFrame->GetWritingMode().IsVertical()) {
-      // Below of the last text frame.
-      rect.SetRect(0, kLastTextFrameRect.height,
-                   kLastTextFrameRect.width, 0);
-    } else {
-      // Right of the last text frame (not bidi-aware).
-      rect.SetRect(kLastTextFrameRect.width, 0,
-                   0, kLastTextFrameRect.height);
-    }
-    rv = ConvertToRootRelativeOffset(lastTextFrame, rect);
+    rect = brRectAfterLastChar.mRect;
+    rv = ConvertToRootRelativeOffset(brRectAfterLastChar.mBaseFrame, rect);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
     frameRect = rect;
   } else {
     // If the frame is a <br> frame or there are no text nodes, we should use
     // the result of GetLineBreakerRectBefore().
     FrameRelativeRect relativeRect = GetLineBreakerRectBefore(firstFrame);
--- a/dom/events/ContentEventHandler.h
+++ b/dom/events/ContentEventHandler.h
@@ -375,16 +375,20 @@ protected:
     nsRect RectRelativeTo(nsIFrame* aBaseFrame) const;
   };
 
   // Returns a rect for line breaker before the node of aFrame.
   // Note that this doesn't check if aFrame should cause line break in
   // non-debug build.
   FrameRelativeRect GetLineBreakerRectBefore(nsIFrame* aFrame);
 
+  // Returns a line breaker rect after aTextContent.  The rect is relative to
+  // the last text frame for aTextContent.
+  FrameRelativeRect GetLineBreakerRectAfter(nsIContent* aTextContent);
+
   // Make aRect non-empty.  If width and/or height is 0, these methods set them
   // to 1.  Note that it doesn't set nsRect's width nor height to one device
   // pixel because using nsRect::ToOutsidePixels() makes actual width or height
   // to 2 pixels because x and y may not be aligned to device pixels.
   void EnsureNonEmptyRect(nsRect& aRect) const;
   void EnsureNonEmptyRect(LayoutDeviceIntRect& aRect) const;
 };