Bug 1251519 Part 5 - Use union rect of child frames for clamping. r?mats draft
authorTing-Yu Lin <tlin@mozilla.com>
Thu, 10 Mar 2016 17:38:33 +0800
changeset 338987 2bf27e10aa0413f2a9fbe9a0a6bd7cb2ee1c208c
parent 338986 c50c5c6723f10252b3d71e37de6d62fd81a275f1
child 515897 97622b88dcc6b0f3b3c4c9f8fadda63db041e946
push id12625
push usertlin@mozilla.com
push dateThu, 10 Mar 2016 09:49:24 +0000
reviewersmats
bugs1251519
milestone48.0a1
Bug 1251519 Part 5 - Use union rect of child frames for clamping. r?mats This patch use the union of all child scrollable overflow frame rects to clamp the caret dragging point. MozReview-Commit-ID: GEF9BpQkQNd
layout/base/AccessibleCaretManager.cpp
layout/base/AccessibleCaretManager.h
--- a/layout/base/AccessibleCaretManager.cpp
+++ b/layout/base/AccessibleCaretManager.cpp
@@ -258,17 +258,16 @@ AccessibleCaretManager::IsCaretDisplayab
 
 bool
 AccessibleCaretManager::HasNonEmptyTextContent(nsINode* aNode) const
 {
   return nsContentUtils::HasNonEmptyTextContent(
            aNode, nsContentUtils::eRecurseIntoChildren);
 }
 
-
 void
 AccessibleCaretManager::UpdateCaretsForCursorMode(UpdateCaretsHint aHint)
 {
   AC_LOG("%s, selection: %p", __FUNCTION__, GetSelection());
 
   int32_t offset = 0;
   nsIFrame* frame = nullptr;
   if (!IsCaretDisplayableInCursorMode(&frame, &offset)) {
@@ -1079,56 +1078,78 @@ AccessibleCaretManager::DragCaretInterna
   nsIFrame* capturingFrame = saf->GetScrolledFrame();
   nsPoint ptInScrolled = point;
   nsLayoutUtils::TransformPoint(rootFrame, capturingFrame, ptInScrolled);
   fs->StartAutoScrollTimer(capturingFrame, ptInScrolled, kAutoScrollTimerDelay);
   return NS_OK;
 }
 
 nsRect
-AccessibleCaretManager::GetContentBoundaryForFrame(nsIFrame* aFrame) const
+AccessibleCaretManager::GetAllChildFrameRectsUnion(nsIFrame* aFrame) const
 {
-  nsRect resultRect;
-  nsIFrame* rootFrame = mPresShell->GetRootFrame();
+  nsRect unionRect;
+
+  // Drill through scroll frames, we don't want to include scrollbar child
+  // frames below.
+  for (nsIFrame* frame = aFrame->GetContentInsertionFrame();
+       frame;
+       frame = frame->GetNextContinuation()) {
+    nsRect frameRect;
 
-  for (; aFrame; aFrame = aFrame->GetNextContinuation()) {
-    nsRect rect = aFrame->GetContentRectRelativeToSelf();
-    nsLayoutUtils::TransformRect(aFrame, rootFrame, rect);
-    resultRect = resultRect.Union(rect);
+    for (nsIFrame::ChildListIterator lists(frame); !lists.IsDone(); lists.Next()) {
+      // Loop all children to union their scrollable overflow rect.
+      for (nsIFrame* child : lists.CurrentList()) {
+        nsRect childRect = child->GetScrollableOverflowRectRelativeToSelf();
+        nsLayoutUtils::TransformRect(child, frame, childRect);
 
-    nsIFrame::ChildListIterator lists(aFrame);
-    for (; !lists.IsDone(); lists.Next()) {
-      // Loop over all children to take the overflow rect into consideration.
-      for (nsIFrame* child : lists.CurrentList()) {
-        nsRect overflowRect = child->GetScrollableOverflowRect();
-        nsLayoutUtils::TransformRect(child, rootFrame, overflowRect);
-        resultRect = resultRect.Union(overflowRect);
+        // A TextFrame containing only '\n' has positive height and width 0, or
+        // positive width and height 0 if it's vertical. Need to use UnionEdges
+        // to add its rect. BRFrame rect should be non-empty.
+        if (childRect.IsEmpty()) {
+          frameRect = frameRect.UnionEdges(childRect);
+        } else {
+          frameRect = frameRect.Union(childRect);
+        }
       }
     }
+
+    MOZ_ASSERT(!frameRect.IsEmpty(),
+               "Editable frames should have at least one BRFrame child to make "
+               "frameRect non-empty!");
+    if (frame != aFrame) {
+      nsLayoutUtils::TransformRect(frame, aFrame, frameRect);
+    }
+    unionRect = unionRect.Union(frameRect);
   }
 
-  // Shrink rect to make sure we never hit the boundary.
-  resultRect.Deflate(kBoundaryAppUnits);
-  return resultRect;
+  return unionRect;
 }
 
 nsPoint
 AccessibleCaretManager::AdjustDragBoundary(const nsPoint& aPoint) const
 {
   nsPoint adjustedPoint = aPoint;
 
   int32_t focusOffset = 0;
   nsIFrame* focusFrame =
     nsCaret::GetFrameAndOffset(GetSelection(), nullptr, 0, &focusOffset);
   Element* editingHost = GetEditingHostForFrame(focusFrame);
 
   if (editingHost) {
-    nsRect boundary =
-      GetContentBoundaryForFrame(editingHost->GetPrimaryFrame());
-    adjustedPoint = boundary.ClampPoint(adjustedPoint);
+    nsIFrame* editingHostFrame = editingHost->GetPrimaryFrame();
+    if (editingHostFrame) {
+      nsRect boundary = GetAllChildFrameRectsUnion(editingHostFrame);
+      nsLayoutUtils::TransformRect(editingHostFrame, mPresShell->GetRootFrame(),
+                                   boundary);
+
+      // Shrink the rect to make sure we never hit the boundary.
+      boundary.Deflate(kBoundaryAppUnits);
+
+      adjustedPoint = boundary.ClampPoint(adjustedPoint);
+    }
   }
 
   if (GetCaretMode() == CaretMode::Selection) {
     // Bug 1068474: Adjust the Y-coordinate so that the carets won't be in tilt
     // mode when a caret is being dragged surpass the other caret.
     //
     // For example, when dragging the second caret, the horizontal boundary (lower
     // bound) of its Y-coordinate is the logical position of the first caret.
--- a/layout/base/AccessibleCaretManager.h
+++ b/layout/base/AccessibleCaretManager.h
@@ -169,19 +169,20 @@ protected:
   // Caller is responsible to use IsTerminated() to check whether PresShell is
   // still valid.
   void FlushLayout() const;
 
   dom::Element* GetEditingHostForFrame(nsIFrame* aFrame) const;
   dom::Selection* GetSelection() const;
   already_AddRefed<nsFrameSelection> GetFrameSelection() const;
 
-  // Get the bounding rectangle for aFrame where the caret under cursor mode can
-  // be positioned. The rectangle is relative to the root frame.
-  nsRect GetContentBoundaryForFrame(nsIFrame* aFrame) const;
+  // Get the union of all the child frame scrollable overflow rects for aFrame,
+  // which is used as a helper function to restrict the area where the caret can
+  // be dragged. Returns the rect relative to aFrame.
+  nsRect GetAllChildFrameRectsUnion(nsIFrame* aFrame) const;
 
   // If we're dragging the first caret, we do not want to drag it over the
   // previous character of the second caret. Same as the second caret. So we
   // check if content offset exceeds the previous/next character of second/first
   // caret base the active caret.
   bool CompareRangeWithContentOffset(nsIFrame::ContentOffsets& aOffsets);
 
   // Timeout in milliseconds to hide the AccessibleCaret under cursor mode while