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
authorMasayuki Nakano <masayuki@d-toybox.com>
Fri, 05 Aug 2016 16:51:49 +0900
changeset 400385 29f03b7552fc69493af9aee3cf2e8c5026093927
parent 400384 dfd910cbec2f3b98cb2b7a649b69d24abaf5c130
child 400386 cbcfb7b9a51354fc6bbf51d0ff8274197071c49f
push id26138
push usergszorc@mozilla.com
push dateSat, 13 Aug 2016 03:04:36 +0000
reviewerssmaug
bugs1286464
milestone51.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
@@ -1579,30 +1579,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()) {
@@ -1618,16 +1622,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
@@ -1637,17 +1644,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) {
             // Right of the last character.
             brRect.y = brRect.YMost() + 1;
             brRect.height = 1;
           } else {
             // Under the last character.
             brRect.x = brRect.XMost() + 1;
             brRect.width = 1;
           }
@@ -1741,16 +1748,64 @@ ContentEventHandler::OnQueryTextRectArra
       // The appended rect was for "\r" of "\r\n".  Therefore, we need to
       // append same rect for "\n" too because querying rect of "\r" and "\n"
       // should return same rect.  E.g., IME may query previous character's
       // rect of first character of a line.
       aEvent->mReply.mRectArray.AppendElement(rect);
       offset++;
     }
   }
+
+  // If the query range is longer than actual content length, we should append
+  // caret rect at the end of the content as the last character rect because
+  // native IME may want to query character rect at the end of contents for
+  // deciding the position of a popup window (e.g., suggest window for next
+  // word).  Note that when this method hasn't appended character rects, it
+  // means that the offset is too large or the query range is collapsed.
+  if (offset < kEndOffset || aEvent->mReply.mRectArray.IsEmpty()) {
+    // 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);