Bug 1286464 part.12 ContentEventHandler::GetFirstFrameHavingFlatTextInRange() should return only frames whose content causes text r?smaug draft
authorMasayuki Nakano <masayuki@d-toybox.com>
Thu, 21 Jul 2016 17:45:17 +0900
changeset 394874 b124e0a1046d81af949f4842d4228dad6185ae24
parent 394873 0358bd96836b4297105dbe867127efbb753157c7
child 394875 1c01cbbe22dc2efda137d50ea1f49d012d0d7d05
push id24655
push usermasayuki@d-toybox.com
push dateMon, 01 Aug 2016 08:00:00 +0000
reviewerssmaug
bugs1286464
milestone50.0a1
Bug 1286464 part.12 ContentEventHandler::GetFirstFrameHavingFlatTextInRange() should return only frames whose content causes text r?smaug If it returns frames which don't cause text, the caller needs complicated loop. So, it should return only frames which causes some text. MozReview-Commit-ID: 9gny0w1PUMa
dom/events/ContentEventHandler.cpp
dom/events/ContentEventHandler.h
--- a/dom/events/ContentEventHandler.cpp
+++ b/dom/events/ContentEventHandler.cpp
@@ -1427,26 +1427,53 @@ ContentEventHandler::GetNodeHavingFlatTe
     }
   }
   return result;
 }
 
 ContentEventHandler::FrameAndNodeOffset
 ContentEventHandler::GetFirstFrameHavingFlatTextInRange(nsRange* aRange)
 {
-  // used to iterate over all contents and their frames
-  nsCOMPtr<nsIContentIterator> iter = NS_NewContentIterator();
-  iter->Init(aRange);
+  NodePosition nodePosition;
+  nsCOMPtr<nsIContentIterator> iter = NS_NewPreContentIterator();
+  for (iter->Init(aRange); !iter->IsDone(); iter->Next()) {
+    nsINode* node = iter->GetCurrentNode();
+    if (NS_WARN_IF(!node)) {
+      break;
+    }
+
+    if (!node->IsContent()) {
+      continue;
+    }
 
-  // get the starting frame
-  NodePosition nodePosition(iter->GetCurrentNode(), aRange->StartOffset());
-  if (!nodePosition.mNode) {
-    nodePosition =
-      GetNodeHavingFlatText(aRange->GetStartParent(), nodePosition.mOffset);
+    if (node->IsNodeOfType(nsINode::eTEXT)) {
+      // If the range starts at the end of a text node, we need to find
+      // next node which causes text.
+      int32_t offsetInNode =
+        node == aRange->GetStartParent() ? aRange->StartOffset() : 0;
+      if (static_cast<uint32_t>(offsetInNode) < node->Length()) {
+        nodePosition.mNode = node;
+        nodePosition.mOffset = offsetInNode;
+        break;
+      }
+      continue;
+    }
+
+    // If the element node causes a line break before it, it's the first
+    // node causing text.
+    if (ShouldBreakLineBefore(node->AsContent(), mRootContent)) {
+      nodePosition.mNode = node;
+      nodePosition.mOffset = 0;
+    }
   }
+
+  if (!nodePosition.IsValid()) {
+    return FrameAndNodeOffset();
+  }
+
   nsIFrame* firstFrame = nullptr;
   GetFrameForTextRect(nodePosition.mNode, nodePosition.mOffset,
                       true, &firstFrame);
   return FrameAndNodeOffset(firstFrame, nodePosition.mOffset);
 }
 
 ContentEventHandler::FrameRelativeRect
 ContentEventHandler::GetLineBreakerRectBefore(nsIFrame* aFrame)
@@ -1532,20 +1559,24 @@ ContentEventHandler::OnQueryTextRectArra
                                     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.
 
-    // get the starting frame
+    // Get the first frame which causes some text after the offset.
     FrameAndNodeOffset firstFrame = GetFirstFrameHavingFlatTextInRange(range);
-    if (NS_WARN_IF(!firstFrame.IsValid())) {
-      return NS_ERROR_FAILURE;
+
+    // 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()) {
+      break;
     }
 
     nsIContent* firstContent = firstFrame.mFrame->GetContent();
     if (NS_WARN_IF(!firstContent)) {
       return NS_ERROR_FAILURE;
     }
 
     // get the starting frame rect
@@ -1578,18 +1609,16 @@ ContentEventHandler::OnQueryTextRectArra
         FrameAndNodeOffset frameForPrevious =
           GetFirstFrameHavingFlatTextInRange(range);
         startsBetweenLineBreaker = frameForPrevious.mFrame == firstFrame.mFrame;
       }
     } else {
       rv = firstFrame->GetCharacterRectsInRange(firstFrame.mStartOffsetInNode,
                                                 kEndOffset - offset, charRects);
       if (NS_WARN_IF(NS_FAILED(rv)) || NS_WARN_IF(charRects.IsEmpty())) {
-        // XXX: If the node isn't a text node and does not cause a line break,
-        //      we need to recompute with new range, but how?
         return rv;
       }
       AppendSubString(chars, firstContent, firstFrame.mStartOffsetInNode,
                       charRects.Length());
       if (NS_WARN_IF(chars.Length() != charRects.Length())) {
         return NS_ERROR_UNEXPECTED;
       }
       if (kBRLength > 1 && chars[0] == '\n' &&
--- a/dom/events/ContentEventHandler.h
+++ b/dom/events/ContentEventHandler.h
@@ -329,17 +329,19 @@ 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.
+  // Get first frame after the start of the given range for computing text rect.
+  // This returns invalid FrameAndNodeOffset if there is no content which
+  // causes text after the start of the range.
   FrameAndNodeOffset GetFirstFrameHavingFlatTextInRange(nsRange* aRange);
 
   struct FrameRelativeRect final
   {
     // mRect is relative to the mBaseFrame's position.
     nsRect mRect;
     nsIFrame* mBaseFrame;