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
--- 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