Bug 1286464 part.14 When ContentEventHandler::OnQueryTextRectArray() reaches the end of query range, it should append caret rect at the end of the content r?smaug draft
authorMasayuki Nakano <masayuki@d-toybox.com>
Fri, 29 Jul 2016 01:30:16 +0900
changeset 394876 54298e0cd697aaaf4af7adf5d2b0d6f36c6b1f1b
parent 394875 1c01cbbe22dc2efda137d50ea1f49d012d0d7d05
child 394877 7a94199b991caddd5ecc7fc34a080402404ac189
push id24655
push usermasayuki@d-toybox.com
push dateMon, 01 Aug 2016 08:00:00 +0000
reviewerssmaug
bugs1286464
milestone50.0a1
Bug 1286464 part.14 When ContentEventHandler::OnQueryTextRectArray() reaches the end of query range, it should append caret rect at the end of the content r?smaug When the loop in ContentEventHandler::OnQueryTextRectArray() reaches at the end of contents, it should append a caret rect at the end of the contents. That helps deciding popup position of IME when caret is at the end of the contents. This patch guesses the caret rect with eQueryTextRect event (for avoiding the dependency of native caret position of eQueryCaretRect event handler). MozReview-Commit-ID: 8cGeHpkzX9g
dom/events/ContentEventHandler.cpp
--- a/dom/events/ContentEventHandler.cpp
+++ b/dom/events/ContentEventHandler.cpp
@@ -1546,30 +1546,34 @@ ContentEventHandler::OnQueryTextRectArra
     return rv;
   }
 
   LineBreakType lineBreakType = GetLineBreakType(aEvent);
   const uint32_t kBRLength = GetBRLength(lineBreakType);
 
   RefPtr<nsRange> range = new nsRange(mRootContent);
 
+  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) {
     rv = SetRangeFromFlatTextOffset(range, offset, 1, lineBreakType, true,
                                     nullptr);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
 
-    // TODO: If the range is collapsed, that means offset reaches to the end
-    //       of the contents.  We need to do something here.
+    // 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);
 
     // If GetFirstFrameHavingFlatTextInRange() 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()) {
@@ -1585,16 +1589,19 @@ ContentEventHandler::OnQueryTextRectArra
     nsRect frameRect(nsPoint(0, 0), firstFrame->GetRect().Size());
     rv = ConvertToRootRelativeOffset(firstFrame, frameRect);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
 
     bool startsBetweenLineBreaker = false;
     nsAutoString chars;
+    // XXX not bidi-aware this class...
+    isVertical = firstFrame->GetWritingMode().IsVertical();
+
     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
@@ -1604,17 +1611,17 @@ ContentEventHandler::OnQueryTextRectArra
         FrameRelativeRect relativeBRRect = GetLineBreakerRectBefore(firstFrame);
         brRect = relativeBRRect.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 (firstFrame->GetWritingMode().IsVertical()) {
+          if (isVertical) {
             brRect.y = brRect.YMost() + 1;
             brRect.height = 0;
           } else {
             brRect.x = brRect.XMost() + 1;
             brRect.width = 0;
           }
         }
       }
@@ -1697,16 +1704,60 @@ ContentEventHandler::OnQueryTextRectArra
         continue;
       }
 
       // Let's append same rect for following character in a line breaker.
       aEvent->mReply.mRectArray.AppendElement(rect);
       offset++;
     }
   }
+
+  // If the query range is longer than actual content length, we should append
+  // caret rect after the last character.
+  if (offset < kEndOffset) {
+    // If we've already retrieved some character rects before current offset,
+    // we can guess the last rect from the last character's rect unless it's a
+    // line breaker.  (If it's a line breaker, the caret rect is in next line.)
+    if (!aEvent->mReply.mRectArray.IsEmpty() && !wasLineBreaker) {
+      rect = aEvent->mReply.mRectArray.LastElement();
+      if (isVertical) {
+        rect.y = rect.YMost() + 1;
+        rect.height = 1;
+        MOZ_ASSERT(rect.width);
+      } else {
+        rect.x = rect.XMost() + 1;
+        rect.width = 1;
+        MOZ_ASSERT(rect.height);
+      }
+      aEvent->mReply.mRectArray.AppendElement(rect);
+    } else {
+      // Note that don't use eQueryCaretRect here because if caret is at the
+      // end of the content, it returns actual caret rect instead of computing
+      // the rect itself.  It means that the result depends on caret position.
+      // So, we shouldn't use it for consistency result in automated tests.
+      WidgetQueryContentEvent queryTextRect(eQueryTextRect, *aEvent);
+      WidgetQueryContentEvent::Options options(*aEvent);
+      queryTextRect.InitForQueryTextRect(offset, 1, options);
+      rv = OnQueryTextRect(&queryTextRect);
+      if (NS_WARN_IF(NS_FAILED(rv))) {
+        return rv;
+      }
+      if (NS_WARN_IF(!queryTextRect.mSucceeded)) {
+        return NS_ERROR_FAILURE;
+      }
+      MOZ_ASSERT(!queryTextRect.mReply.mRect.IsEmpty());
+      if (queryTextRect.mReply.mWritingMode.IsVertical()) {
+        queryTextRect.mReply.mRect.height = 1;
+      } else {
+        queryTextRect.mReply.mRect.width = 1;
+      }
+      aEvent->mReply.mRectArray.AppendElement(queryTextRect.mReply.mRect);
+    }
+  }
+
   aEvent->mSucceeded = true;
   return NS_OK;
 }
 
 nsresult
 ContentEventHandler::OnQueryTextRect(WidgetQueryContentEvent* aEvent)
 {
   nsresult rv = Init(aEvent);