Bug 1286464 part.5 Create ContentEventHandler::EnsureNonEmptyRect() for query various rect event handlers r?smaug draft
authorMasayuki Nakano <masayuki@d-toybox.com>
Fri, 29 Jul 2016 23:19:46 +0900
changeset 394867 0a83d01eb94490306b159317a62982a716df4508
parent 394866 4cca84aea68c45ac21cc79588ad22defbeb80c71
child 394868 3595636c873652ea1a253ae39196cf217ba58109
push id24655
push usermasayuki@d-toybox.com
push dateMon, 01 Aug 2016 08:00:00 +0000
reviewerssmaug
bugs1286464
milestone50.0a1
Bug 1286464 part.5 Create ContentEventHandler::EnsureNonEmptyRect() for query various rect event handlers r?smaug This can kill the duplicated code in a lot of places in ContentEventHandler. MozReview-Commit-ID: BRpBkbXyeBs
dom/events/ContentEventHandler.cpp
dom/events/ContentEventHandler.h
--- a/dom/events/ContentEventHandler.cpp
+++ b/dom/events/ContentEventHandler.cpp
@@ -390,16 +390,17 @@ ContentEventHandler::QueryContentRect(ns
     nsRect frameRect(nsPoint(0, 0), frame->GetRect().Size());
     rv = ConvertToRootRelativeOffset(frame, frameRect);
     NS_ENSURE_SUCCESS(rv, rv);
     resultRect.UnionRect(resultRect, frameRect);
   }
 
   aEvent->mReply.mRect = LayoutDeviceIntRect::FromUnknownRect(
       resultRect.ToOutsidePixels(mPresContext->AppUnitsPerDevPixel()));
+  EnsureNonEmptyRect(aEvent->mReply.mRect);
   aEvent->mSucceeded = true;
 
   return NS_OK;
 }
 
 // Editor places a bogus BR node under its root content if the editor doesn't
 // have any text. This happens even for single line editors.
 // When we get text content and when we change the selection,
@@ -1382,16 +1383,32 @@ ContentEventHandler::OnQueryTextContent(
                "Font ranges doesn't match the string");
   }
 
   aEvent->mSucceeded = true;
 
   return NS_OK;
 }
 
+void
+ContentEventHandler::EnsureNonEmptyRect(nsRect& aRect) const
+{
+  // See the comment in ContentEventHandler.h why this doesn't set them to
+  // one device pixel.
+  aRect.height = std::max(1, aRect.height);
+  aRect.width = std::max(1, aRect.width);
+}
+
+void
+ContentEventHandler::EnsureNonEmptyRect(LayoutDeviceIntRect& aRect) const
+{
+  aRect.height = std::max(1, aRect.height);
+  aRect.width = std::max(1, aRect.width);
+}
+
 ContentEventHandler::NodePosition
 ContentEventHandler::GetNodeHavingFlatText(const NodePosition& aNodePosition)
 {
   return GetNodeHavingFlatText(aNodePosition.mNode, aNodePosition.mOffset);
 }
 
 ContentEventHandler::NodePosition
 ContentEventHandler::GetNodeHavingFlatText(nsINode* aNode,
@@ -1474,20 +1491,17 @@ ContentEventHandler::OnQueryTextRectArra
 
     for (size_t i = 0; i < charRects.Length(); i++) {
       nsRect charRect = charRects[i];
       charRect.x += frameRect.x;
       charRect.y += frameRect.y;
 
       rect = LayoutDeviceIntRect::FromUnknownRect(
                charRect.ToOutsidePixels(mPresContext->AppUnitsPerDevPixel()));
-
-      // Ensure at least 1px width and height for avoiding empty rect.
-      rect.height = std::max(1, rect.height);
-      rect.width = std::max(1, rect.width);
+      EnsureNonEmptyRect(rect);
 
       aEvent->mReply.mRectArray.AppendElement(rect);
       offset++;
     }
   }
   aEvent->mSucceeded = true;
   return NS_OK;
 }
@@ -1526,24 +1540,24 @@ ContentEventHandler::OnQueryTextRect(Wid
 
   // get the starting frame rect
   nsRect rect(nsPoint(0, 0), firstFrame->GetRect().Size());
   rv = ConvertToRootRelativeOffset(firstFrame, rect);
   NS_ENSURE_SUCCESS(rv, rv);
   nsRect frameRect = rect;
   nsPoint ptOffset;
   firstFrame->GetPointFromOffset(startNodePosition.mOffset, &ptOffset);
-  // minus 1 to avoid creating an empty rect
   if (firstFrame->GetWritingMode().IsVertical()) {
-    rect.y += ptOffset.y - 1;
-    rect.height -= ptOffset.y - 1;
+    rect.y += ptOffset.y;
+    rect.height -= ptOffset.y;
   } else {
-    rect.x += ptOffset.x - 1;
-    rect.width -= ptOffset.x - 1;
+    rect.x += ptOffset.x;
+    rect.width -= ptOffset.x;
   }
+  EnsureNonEmptyRect(rect);
 
   // get the ending frame
   NodePosition endNodePosition =
     GetNodeHavingFlatText(range->GetEndParent(), range->EndOffset());
   nsIFrame* lastFrame = nullptr;
   rv = GetFrameForTextRect(endNodePosition.mNode, endNodePosition.mOffset,
                            range->Collapsed(), &lastFrame);
   NS_ENSURE_SUCCESS(rv, rv);
@@ -1566,38 +1580,40 @@ ContentEventHandler::OnQueryTextRect(Wid
       if (!frame) {
         // this can happen when the end offset of the range is 0.
         frame = lastFrame;
       }
     }
     frameRect.SetRect(nsPoint(0, 0), frame->GetRect().Size());
     rv = ConvertToRootRelativeOffset(frame, frameRect);
     NS_ENSURE_SUCCESS(rv, rv);
+    EnsureNonEmptyRect(frameRect);
     if (frame != lastFrame) {
       // not last frame, so just add rect to previous result
       rect.UnionRect(rect, frameRect);
     }
   }
 
   // get the ending frame rect
   lastFrame->GetPointFromOffset(endNodePosition.mOffset, &ptOffset);
-  // minus 1 to avoid creating an empty rect
   if (lastFrame->GetWritingMode().IsVertical()) {
-    frameRect.height -= lastFrame->GetRect().height - ptOffset.y - 1;
+    frameRect.height -= lastFrame->GetRect().height - ptOffset.y;
   } else {
-    frameRect.width -= lastFrame->GetRect().width - ptOffset.x - 1;
+    frameRect.width -= lastFrame->GetRect().width - ptOffset.x;
   }
+  EnsureNonEmptyRect(frameRect);
 
   if (firstFrame == lastFrame) {
     rect.IntersectRect(rect, frameRect);
   } else {
     rect.UnionRect(rect, frameRect);
   }
   aEvent->mReply.mRect = LayoutDeviceIntRect::FromUnknownRect(
       rect.ToOutsidePixels(mPresContext->AppUnitsPerDevPixel()));
+  EnsureNonEmptyRect(aEvent->mReply.mRect);
   aEvent->mReply.mWritingMode = lastFrame->GetWritingMode();
   aEvent->mSucceeded = true;
   return NS_OK;
 }
 
 nsresult
 ContentEventHandler::OnQueryEditorRect(WidgetQueryContentEvent* aEvent)
 {
@@ -1636,16 +1652,17 @@ ContentEventHandler::OnQueryCaretRect(Wi
       NS_ENSURE_SUCCESS(rv, rv);
       if (offset == aEvent->mInput.mOffset) {
         rv = ConvertToRootRelativeOffset(caretFrame, caretRect);
         NS_ENSURE_SUCCESS(rv, rv);
         nscoord appUnitsPerDevPixel =
           caretFrame->PresContext()->AppUnitsPerDevPixel();
         aEvent->mReply.mRect = LayoutDeviceIntRect::FromUnknownRect(
           caretRect.ToOutsidePixels(appUnitsPerDevPixel));
+        EnsureNonEmptyRect(aEvent->mReply.mRect);
         aEvent->mReply.mWritingMode = caretFrame->GetWritingMode();
         aEvent->mReply.mOffset = aEvent->mInput.mOffset;
         aEvent->mSucceeded = true;
         return NS_OK;
       }
     }
   }
 
@@ -1687,23 +1704,17 @@ ContentEventHandler::OnQueryCaretRect(Wi
     rect.height = fontMetrics->MaxHeight();
   }
 
   rv = ConvertToRootRelativeOffset(frame, rect);
   NS_ENSURE_SUCCESS(rv, rv);
 
   aEvent->mReply.mRect = LayoutDeviceIntRect::FromUnknownRect(
       rect.ToOutsidePixels(mPresContext->AppUnitsPerDevPixel()));
-  // If the caret rect is empty, let's make it non-empty rect.
-  if (!aEvent->mReply.mRect.width) {
-    aEvent->mReply.mRect.width = 1;
-  }
-  if (!aEvent->mReply.mRect.height) {
-    aEvent->mReply.mRect.height = 1;
-  }
+  EnsureNonEmptyRect(aEvent->mReply.mRect);
   aEvent->mSucceeded = true;
   return NS_OK;
 }
 
 nsresult
 ContentEventHandler::OnQueryContentState(WidgetQueryContentEvent* aEvent)
 {
   nsresult rv = Init(aEvent);
--- a/dom/events/ContentEventHandler.h
+++ b/dom/events/ContentEventHandler.h
@@ -331,13 +331,20 @@ 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 && mStartOffsetInNode >= 0; }
   };
   // Get first frame in the given range for computing text rect.
   FrameAndNodeOffset GetFirstFrameHavingFlatTextInRange(nsRange* aRange);
+
+  // 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;
 };
 
 } // namespace mozilla
 
 #endif // mozilla_ContentEventHandler_h_