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