Bug 1404854 Part 3 - Fix page cannot be scrolled automatically when dragging caret. draft
authorTing-Yu Lin <tlin@mozilla.com>
Fri, 06 Oct 2017 15:54:23 +0800
changeset 676580 47a4d919843c2960902529d17a8e3c4386bc92a4
parent 676579 d02fb91e9835b11bfda7034aaad26772a95a122e
child 734975 270480be1cb42ebe1d7abbb1b1e5dc4df0845f01
push id83535
push userbmo:tlin@mozilla.com
push dateMon, 09 Oct 2017 02:19:24 +0000
bugs1404854
milestone58.0a1
Bug 1404854 Part 3 - Fix page cannot be scrolled automatically when dragging caret. The issue is: when we hit some failure conditions in DragCaretInternal() such as the frame is not selectable, or no frame is under the point, etc., we'll early return so that the auto scroll code is not being executed. The logic in StartSelectionAutoScrollTimer() is similar to how nsFrame::HandleDrag() handles the auto scrolling. MozReview-Commit-ID: FtXZ8BWp3BX
layout/base/AccessibleCaretManager.cpp
layout/base/AccessibleCaretManager.h
--- a/layout/base/AccessibleCaretManager.cpp
+++ b/layout/base/AccessibleCaretManager.cpp
@@ -505,17 +505,21 @@ AccessibleCaretManager::DragCaret(const 
 {
   MOZ_ASSERT(mActiveCaret);
   MOZ_ASSERT(GetCaretMode() != CaretMode::None);
 
   if (!mPresShell || !mPresShell->GetRootFrame() || !GetSelection()) {
     return NS_ERROR_NULL_POINTER;
   }
 
+  StopSelectionAutoScrollTimer();
   DragCaretInternal(aPoint);
+
+  // We want to scroll the page even if we failed to drag the caret.
+  StartSelectionAutoScrollTimer(aPoint);
   UpdateCarets();
   return NS_OK;
 }
 
 nsresult
 AccessibleCaretManager::ReleaseCaret()
 {
   MOZ_ASSERT(mActiveCaret);
@@ -1248,45 +1252,26 @@ AccessibleCaretManager::DragCaretInterna
   }
 
   nsIFrame::ContentOffsets offsets =
     newFrame->GetContentOffsetsFromPoint(newPoint);
   if (offsets.IsNull()) {
     return NS_ERROR_FAILURE;
   }
 
-  Selection* selection = GetSelection();
-  MOZ_ASSERT(selection);
-
   if (GetCaretMode() == CaretMode::Selection &&
       !RestrictCaretDraggingOffsets(offsets)) {
     return NS_ERROR_FAILURE;
   }
 
   ClearMaintainedSelection();
 
-  nsIFrame* anchorFrame = nullptr;
-  selection->GetPrimaryFrameForAnchorNode(&anchorFrame);
-
-  nsIFrame* scrollable =
-    nsLayoutUtils::GetClosestFrameOfType(anchorFrame, LayoutFrameType::Scroll);
-  AutoWeakFrame weakScrollable = scrollable;
   fs->HandleClick(offsets.content, offsets.StartOffset(), offsets.EndOffset(),
                   GetCaretMode() == CaretMode::Selection, false,
                   offsets.associate);
-  if (!weakScrollable.IsAlive()) {
-    return NS_OK;
-  }
-
-  // Scroll scrolled frame.
-  nsIScrollableFrame* saf = do_QueryFrame(scrollable);
-  nsIFrame* capturingFrame = saf->GetScrolledFrame();
-  nsPoint ptInScrolled = point;
-  nsLayoutUtils::TransformPoint(rootFrame, capturingFrame, ptInScrolled);
-  fs->StartAutoScrollTimer(capturingFrame, ptInScrolled, kAutoScrollTimerDelay);
   return NS_OK;
 }
 
 nsRect
 AccessibleCaretManager::GetAllChildFrameRectsUnion(nsIFrame* aFrame) const
 {
   nsRect unionRect;
 
@@ -1371,16 +1356,61 @@ AccessibleCaretManager::AdjustDragBounda
       }
     }
   }
 
   return adjustedPoint;
 }
 
 void
+AccessibleCaretManager::StartSelectionAutoScrollTimer(
+  const nsPoint& aPoint) const
+{
+  Selection* selection = GetSelection();
+  MOZ_ASSERT(selection);
+
+  nsIFrame* anchorFrame = nullptr;
+  selection->GetPrimaryFrameForAnchorNode(&anchorFrame);
+  if (!anchorFrame) {
+    return;
+  }
+
+  nsIScrollableFrame* scrollFrame =
+    nsLayoutUtils::GetNearestScrollableFrame(
+      anchorFrame,
+      nsLayoutUtils::SCROLLABLE_SAME_DOC |
+      nsLayoutUtils::SCROLLABLE_INCLUDE_HIDDEN);
+  if (!scrollFrame) {
+    return;
+  }
+
+  nsIFrame* capturingFrame = scrollFrame->GetScrolledFrame();
+  if (!capturingFrame) {
+    return;
+  }
+
+  nsIFrame* rootFrame = mPresShell->GetRootFrame();
+  MOZ_ASSERT(rootFrame);
+  nsPoint ptInScrolled = aPoint;
+  nsLayoutUtils::TransformPoint(rootFrame, capturingFrame, ptInScrolled);
+
+  RefPtr<nsFrameSelection> fs = GetFrameSelection();
+  MOZ_ASSERT(fs);
+  fs->StartAutoScrollTimer(capturingFrame, ptInScrolled, kAutoScrollTimerDelay);
+}
+
+void
+AccessibleCaretManager::StopSelectionAutoScrollTimer() const
+{
+  RefPtr<nsFrameSelection> fs = GetFrameSelection();
+  MOZ_ASSERT(fs);
+  fs->StopAutoScrollTimer();
+}
+
+void
 AccessibleCaretManager::DispatchCaretStateChangedEvent(CaretChangedReason aReason) const
 {
   if (!mPresShell) {
     return;
   }
 
   FlushLayout();
   if (IsTerminated()) {
--- a/layout/base/AccessibleCaretManager.h
+++ b/layout/base/AccessibleCaretManager.h
@@ -186,16 +186,22 @@ protected:
   nsIFrame* GetFrameForFirstRangeStartOrLastRangeEnd(
     nsDirection aDirection,
     int32_t* aOutOffset,
     nsIContent** aOutContent = nullptr,
     int32_t* aOutContentOffset = nullptr) const;
 
   nsresult DragCaretInternal(const nsPoint& aPoint);
   nsPoint AdjustDragBoundary(const nsPoint& aPoint) const;
+
+  // Start the selection scroll timer if the caret is being dragged out of
+  // the scroll port.
+  void StartSelectionAutoScrollTimer(const nsPoint& aPoint) const;
+  void StopSelectionAutoScrollTimer() const;
+
   void ClearMaintainedSelection() const;
 
   // 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;