Bug 1286464 part.3 Make static methods, AdjustTextRectNode() and GetFirstFrameInRange(), members of ContentEventHandler r=smaug
authorMasayuki Nakano <masayuki@d-toybox.com>
Fri, 05 Aug 2016 12:43:40 +0900
changeset 400374 79cf1cd77358f6b21619483d40d4ac631f72a3af
parent 400373 ebf06d2b6c322b747177fd47f2e3a21b080f79e4
child 400375 49ce9d732d5f160d7214bc6e881944ea0c174864
push id26138
push usergszorc@mozilla.com
push dateSat, 13 Aug 2016 03:04:36 +0000
reviewerssmaug
bugs1286464
milestone51.0a1
Bug 1286464 part.3 Make static methods, AdjustTextRectNode() and GetFirstFrameInRange(), members of ContentEventHandler r=smaug This patch makes the static methods members of ContentEventHandler because ContentEventHandler::NodePosition is useful for them. * nsINode* AdjustTextRectNode(nsINode*, int32_t&) -> NodePosition GetNodeHavingFlatText(nsINode* int32_t) * nsIFrame* GetFirstFrameInRange(nsRange*, int32_t&) -> FrameAndNodeOffset GetFirstFrameHavingFlatTextInRange(nsRange*) So, this patch avoids unclear in/out arguments of them. MozReview-Commit-ID: 7yWeIkRdGj
dom/events/ContentEventHandler.cpp
dom/events/ContentEventHandler.h
--- a/dom/events/ContentEventHandler.cpp
+++ b/dom/events/ContentEventHandler.cpp
@@ -1370,53 +1370,80 @@ ContentEventHandler::OnQueryTextContent(
                "Font ranges doesn't match the string");
   }
 
   aEvent->mSucceeded = true;
 
   return NS_OK;
 }
 
-// Adjust to use a child node if possible
-// to make the returned rect more accurate
-static nsINode* AdjustTextRectNode(nsINode* aNode,
-                                   int32_t& aNodeOffset)
+ContentEventHandler::NodePosition
+ContentEventHandler::GetNodePositionHavingFlatText(
+                       const NodePosition& aNodePosition)
 {
-  int32_t childCount = int32_t(aNode->GetChildCount());
-  nsINode* node = aNode;
-  if (childCount) {
-    if (aNodeOffset < childCount) {
-      node = aNode->GetChildAt(aNodeOffset);
-      aNodeOffset = 0;
-    } else if (aNodeOffset == childCount) {
-      node = aNode->GetChildAt(childCount - 1);
-      aNodeOffset = node->IsNodeOfType(nsINode::eTEXT) ?
-        static_cast<int32_t>(static_cast<nsIContent*>(node)->TextLength()) : 1;
-    }
-  }
-  return node;
+  return GetNodePositionHavingFlatText(aNodePosition.mNode,
+                                       aNodePosition.mOffset);
 }
 
-static
-nsIFrame*
-GetFirstFrameInRange(nsRange* aRange, int32_t& aNodeOffset)
+ContentEventHandler::NodePosition
+ContentEventHandler::GetNodePositionHavingFlatText(nsINode* aNode,
+                                                   int32_t aNodeOffset)
+{
+  if (aNode->IsNodeOfType(nsINode::eTEXT)) {
+    return NodePosition(aNode, aNodeOffset);
+  }
+
+  int32_t childCount = static_cast<int32_t>(aNode->GetChildCount());
+
+  // If it's a empty element node, returns itself.
+  if (!childCount) {
+    MOZ_ASSERT(!aNodeOffset || aNodeOffset == 1);
+    return NodePosition(aNode, aNodeOffset);
+  }
+
+  // If there is a node at given position, return the start of it.
+  if (aNodeOffset < childCount) {
+    return NodePosition(aNode->GetChildAt(aNodeOffset), 0);
+  }
+
+  // If the offset represents "after" the node, we need to return the last
+  // child of it.  For example, if a range is |<p>[<br>]</p>|, then, the
+  // end point is {<p>, 1}.  In such case, callers need the <br> node.
+  if (aNodeOffset == childCount) {
+    NodePosition result;
+    result.mNode = aNode->GetChildAt(childCount - 1);
+    result.mOffset = result.mNode->IsNodeOfType(nsINode::eTEXT) ?
+      static_cast<int32_t>(result.mNode->AsContent()->TextLength()) : 1;
+  }
+
+  NS_WARNING("aNodeOffset is invalid value");
+  return NodePosition();
+}
+
+ContentEventHandler::FrameAndNodeOffset
+ContentEventHandler::GetFirstFrameHavingFlatTextInRange(nsRange* aRange)
 {
   // used to iterate over all contents and their frames
   nsCOMPtr<nsIContentIterator> iter = NS_NewContentIterator();
   iter->Init(aRange);
 
   // get the starting frame
-  aNodeOffset = aRange->StartOffset();
-  nsINode* node = iter->GetCurrentNode();
-  if (!node) {
-    node = AdjustTextRectNode(aRange->GetStartParent(), aNodeOffset);
+  NodePosition nodePosition(iter->GetCurrentNode(), aRange->StartOffset());
+  if (!nodePosition.mNode) {
+    nodePosition =
+      GetNodePositionHavingFlatText(aRange->GetStartParent(),
+                                    nodePosition.mOffset);
+    if (NS_WARN_IF(!nodePosition.IsValid())) {
+      return FrameAndNodeOffset();
+    }
   }
   nsIFrame* firstFrame = nullptr;
-  GetFrameForTextRect(node, aNodeOffset, true, &firstFrame);
-  return firstFrame;
+  GetFrameForTextRect(nodePosition.mNode, nodePosition.mOffset,
+                      true, &firstFrame);
+  return FrameAndNodeOffset(firstFrame, nodePosition.mOffset);
 }
 
 nsresult
 ContentEventHandler::OnQueryTextRectArray(WidgetQueryContentEvent* aEvent)
 {
   nsresult rv = Init(aEvent);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
@@ -1431,32 +1458,31 @@ ContentEventHandler::OnQueryTextRectArra
   while (offset < kEndOffset) {
     rv = SetRangeFromFlatTextOffset(range, offset, 1, lineBreakType, true,
                                     nullptr);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
 
     // get the starting frame
-    int32_t nodeOffset = -1;
-    nsIFrame* firstFrame = GetFirstFrameInRange(range, nodeOffset);
-    if (NS_WARN_IF(!firstFrame)) {
+    FrameAndNodeOffset firstFrame = GetFirstFrameHavingFlatTextInRange(range);
+    if (NS_WARN_IF(!firstFrame.IsValid())) {
       return NS_ERROR_FAILURE;
     }
 
     // get the starting frame rect
     nsRect frameRect(nsPoint(0, 0), firstFrame->GetRect().Size());
     rv = ConvertToRootRelativeOffset(firstFrame, frameRect);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
 
     AutoTArray<nsRect, 16> charRects;
-    rv = firstFrame->GetCharacterRectsInRange(nodeOffset, kEndOffset - offset,
-                                              charRects);
+    rv = firstFrame->GetCharacterRectsInRange(firstFrame.mStartOffsetInNode,
+                                              kEndOffset - offset, charRects);
     if (NS_WARN_IF(NS_FAILED(rv)) || NS_WARN_IF(charRects.IsEmpty())) {
       return rv;
     }
 
     for (size_t i = 0; i < charRects.Length(); i++) {
       nsRect charRect = charRects[i];
       charRect.x += frameRect.x;
       charRect.y += frameRect.y;
@@ -1493,79 +1519,88 @@ 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 starting frame
-  int32_t nodeOffset = range->StartOffset();
-  nsINode* node = iter->GetCurrentNode();
-  if (!node) {
-    node = AdjustTextRectNode(range->GetStartParent(), nodeOffset);
+  NodePosition startNodePosition(iter->GetCurrentNode(), range->StartOffset());
+  if (!startNodePosition.mNode) {
+    startNodePosition =
+      GetNodePositionHavingFlatText(range->GetStartParent(),
+                                    startNodePosition.mOffset);
+    if (NS_WARN_IF(!startNodePosition.IsValid())) {
+      return NS_ERROR_FAILURE;
+    }
   }
   nsIFrame* firstFrame = nullptr;
-  rv = GetFrameForTextRect(node, nodeOffset, true, &firstFrame);
+  rv = GetFrameForTextRect(startNodePosition.mNode, startNodePosition.mOffset,
+                           true, &firstFrame);
   NS_ENSURE_SUCCESS(rv, rv);
 
   // 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(nodeOffset, &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;
   } else {
     rect.x += ptOffset.x - 1;
     rect.width -= ptOffset.x - 1;
   }
 
   // get the ending frame
-  nodeOffset = range->EndOffset();
-  node = AdjustTextRectNode(range->GetEndParent(), nodeOffset);
+  NodePosition endNodePosition =
+    GetNodePositionHavingFlatText(range->GetEndParent(), range->EndOffset());
+  if (NS_WARN_IF(!endNodePosition.IsValid())) {
+    return NS_ERROR_FAILURE;
+  }
   nsIFrame* lastFrame = nullptr;
-  rv = GetFrameForTextRect(node, nodeOffset, range->Collapsed(), &lastFrame);
+  rv = GetFrameForTextRect(endNodePosition.mNode, endNodePosition.mOffset,
+                           range->Collapsed(), &lastFrame);
   NS_ENSURE_SUCCESS(rv, rv);
 
   // iterate over all covered frames
   for (nsIFrame* frame = firstFrame; frame != lastFrame;) {
     frame = frame->GetNextContinuation();
     if (!frame) {
       do {
         iter->Next();
-        node = iter->GetCurrentNode();
+        nsINode* node = iter->GetCurrentNode();
         if (!node) {
           break;
         }
         if (!node->IsNodeOfType(nsINode::eCONTENT)) {
           continue;
         }
-        frame = static_cast<nsIContent*>(node)->GetPrimaryFrame();
+        frame = node->AsContent()->GetPrimaryFrame();
       } while (!frame && !iter->IsDone());
       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);
     if (frame != lastFrame) {
       // not last frame, so just add rect to previous result
       rect.UnionRect(rect, frameRect);
     }
   }
 
   // get the ending frame rect
-  lastFrame->GetPointFromOffset(nodeOffset, &ptOffset);
+  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;
   } else {
     frameRect.width -= lastFrame->GetRect().width - ptOffset.x - 1;
   }
 
   if (firstFrame == lastFrame) {
--- a/dom/events/ContentEventHandler.h
+++ b/dom/events/ContentEventHandler.h
@@ -299,13 +299,50 @@ protected:
                                LineBreakType aLineBreakType);
   nsresult GenerateFlatFontRanges(nsRange* aRange,
                                   FontRangeArray& aFontRanges,
                                   uint32_t& aLength,
                                   LineBreakType aLineBreakType);
   nsresult QueryTextRectByRange(nsRange* aRange,
                                 LayoutDeviceIntRect& aRect,
                                 WritingMode& aWritingMode);
+
+  // Returns a node and position in the node for computing text rect.
+  NodePosition GetNodePositionHavingFlatText(const NodePosition& aNodePosition);
+  NodePosition GetNodePositionHavingFlatText(nsINode* aNode,
+                                             int32_t aNodeOffset);
+
+  struct MOZ_STACK_CLASS FrameAndNodeOffset final
+  {
+    // mFrame is safe since this can live in only stack class and
+    // ContentEventHandler doesn't modify layout after
+    // ContentEventHandler::Init() flushes pending layout.  In other words,
+    // this struct shouldn't be used before calling
+    // ContentEventHandler::Init().
+    nsIFrame* mFrame;
+    // Start offset in the node of mFrame
+    int32_t mStartOffsetInNode;
+
+    FrameAndNodeOffset()
+      : mFrame(nullptr)
+      , mStartOffsetInNode(-1)
+    {
+    }
+
+    FrameAndNodeOffset(nsIFrame* aFrame, int32_t aStartOffsetInNode)
+      : mFrame(aFrame)
+      , mStartOffsetInNode(aStartOffsetInNode)
+    {
+    }
+
+    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);
 };
 
 } // namespace mozilla
 
 #endif // mozilla_ContentEventHandler_h_