diff --git a/accessible/base/nsCoreUtils.cpp b/accessible/base/nsCoreUtils.cpp index 02e524191c41..de8320f0f459 100644 --- a/accessible/base/nsCoreUtils.cpp +++ b/accessible/base/nsCoreUtils.cpp @@ -251,8 +251,8 @@ nsresult nsCoreUtils::ScrollSubstringTo(nsIFrame* aFrame, nsRange* aRange, selection->AddRangeAndSelectFramesAndNotifyListeners(*aRange, IgnoreErrors()); selection->ScrollIntoView(nsISelectionController::SELECTION_ANCHOR_REGION, - aVertical, aHorizontal, - Selection::SCROLL_SYNCHRONOUS); + aVertical, aHorizontal, ScrollFlags::None, + SelectionScrollMode::SyncNoFlush); selection->CollapseToStart(IgnoreErrors()); diff --git a/accessible/generic/HyperTextAccessible.cpp b/accessible/generic/HyperTextAccessible.cpp index 9e84a305ba7f..5230b79b3273 100644 --- a/accessible/generic/HyperTextAccessible.cpp +++ b/accessible/generic/HyperTextAccessible.cpp @@ -560,7 +560,7 @@ nsresult HyperTextAccessible::SetSelectionRange(int32_t aStartPos, // Make sure it is visible domSel->ScrollIntoView(nsISelectionController::SELECTION_FOCUS_REGION, ScrollAxis(), ScrollAxis(), - dom::Selection::SCROLL_OVERFLOW_HIDDEN); + ScrollFlags::ScrollOverflowHidden); // When selection is done, move the focus to the selection if accessible is // not focusable. That happens when selection is set within hypertext diff --git a/dom/base/Selection.cpp b/dom/base/Selection.cpp index 0ad66b0882b8..028140e1ec94 100644 --- a/dom/base/Selection.cpp +++ b/dom/base/Selection.cpp @@ -3635,19 +3635,20 @@ nsIFrame* Selection::GetSelectionEndPointGeometry(SelectionRegion aRegion, NS_IMETHODIMP Selection::ScrollSelectionIntoViewEvent::Run() { - if (!mSelection) return NS_OK; // event revoked - - int32_t flags = Selection::SCROLL_DO_FLUSH | Selection::SCROLL_SYNCHRONOUS; + if (!mSelection) { + // event revoked + return NS_OK; + } const RefPtr selection{mSelection}; selection->mScrollEvent.Forget(); - selection->ScrollIntoView(mRegion, mVerticalScroll, mHorizontalScroll, - mFlags | flags); + selection->ScrollIntoView(mRegion, mVerticalScroll, mHorizontalScroll, mFlags, + SelectionScrollMode::SyncFlush); return NS_OK; } nsresult Selection::PostScrollSelectionIntoViewEvent(SelectionRegion aRegion, - int32_t aFlags, + ScrollFlags aFlags, ScrollAxis aVertical, ScrollAxis aHorizontal) { // If we've already posted an event, revoke it and place a new one at the @@ -3668,7 +3669,8 @@ nsresult Selection::PostScrollSelectionIntoViewEvent(SelectionRegion aRegion, nsresult Selection::ScrollIntoView(SelectionRegion aRegion, ScrollAxis aVertical, ScrollAxis aHorizontal, - int32_t aFlags) { + ScrollFlags aScrollFlags, + SelectionScrollMode aMode) { if (!mFrameSelection) { return NS_ERROR_NOT_INITIALIZED; } @@ -3682,9 +3684,13 @@ nsresult Selection::ScrollIntoView(SelectionRegion aRegion, return NS_OK; } - if (!(aFlags & Selection::SCROLL_SYNCHRONOUS)) - return PostScrollSelectionIntoViewEvent(aRegion, aFlags, aVertical, + if (aMode == SelectionScrollMode::Async) { + return PostScrollSelectionIntoViewEvent(aRegion, aScrollFlags, aVertical, aHorizontal); + } + + MOZ_ASSERT(aMode == SelectionScrollMode::SyncFlush || + aMode == SelectionScrollMode::SyncNoFlush); // From this point on, the presShell may get destroyed by the calls below, so // hold on to it using a strong reference to ensure the safety of the @@ -3696,7 +3702,7 @@ nsresult Selection::ScrollIntoView(SelectionRegion aRegion, // is that some callers might scroll to the wrong place. Those should // either manually flush if they're in a safe position for it or use the // async version of this method. - if (aFlags & Selection::SCROLL_DO_FLUSH) { + if (aMode == SelectionScrollMode::SyncFlush) { presShell->GetDocument()->FlushPendingNotifications(FlushType::Layout); // Reget the presshell, since it might have been Destroy'ed. @@ -3706,29 +3712,18 @@ nsresult Selection::ScrollIntoView(SelectionRegion aRegion, } } - // - // Scroll the selection region into view. - // - nsRect rect; nsIFrame* frame = GetSelectionAnchorGeometry(aRegion, &rect); - if (!frame) return NS_ERROR_FAILURE; + if (!frame) { + return NS_ERROR_FAILURE; + } // Scroll vertically to get the caret into view, but only if the container // is perceived to be scrollable in that direction (i.e. there is a visible // vertical scrollbar or the scroll range is at least one device pixel) aVertical.mOnlyIfPerceivedScrollableDirection = true; - - auto scrollFlags = ScrollFlags::None; - if (aFlags & Selection::SCROLL_FIRST_ANCESTOR_ONLY) { - scrollFlags |= ScrollFlags::ScrollFirstAncestorOnly; - } - if (aFlags & Selection::SCROLL_OVERFLOW_HIDDEN) { - scrollFlags |= ScrollFlags::ScrollOverflowHidden; - } - presShell->ScrollFrameIntoView(frame, Some(rect), aVertical, aHorizontal, - scrollFlags); + aScrollFlags); return NS_OK; } diff --git a/dom/base/Selection.h b/dom/base/Selection.h index 4926e3fbc362..eaa0917371c4 100644 --- a/dom/base/Selection.h +++ b/dom/base/Selection.h @@ -54,6 +54,18 @@ class DocGroup; namespace mozilla { +enum class SelectionScrollMode : uint8_t { + // Don't scroll synchronously. We'll flush when the scroll event fires so we + // make sure to scroll to the right place. + Async, + // Scroll synchronously, without flushing layout. + SyncNoFlush, + // Scroll synchronously, flushing layout. You MUST hold a strong ref on + // 'this' for the duration of this call. This might destroy arbitrary + // layout objects. + SyncFlush, +}; + namespace dom { /** @@ -220,24 +232,14 @@ class Selection final : public nsSupportsWeakReference, nsRect* aRect); nsresult PostScrollSelectionIntoViewEvent(SelectionRegion aRegion, - int32_t aFlags, + ScrollFlags aFlags, ScrollAxis aVertical, ScrollAxis aHorizontal); - enum { - SCROLL_SYNCHRONOUS = 1 << 1, - SCROLL_FIRST_ANCESTOR_ONLY = 1 << 2, - SCROLL_DO_FLUSH = - 1 << 3, // only matters if SCROLL_SYNCHRONOUS is passed too - SCROLL_OVERFLOW_HIDDEN = 1 << 5, - }; - // If aFlags doesn't contain SCROLL_SYNCHRONOUS, then we'll flush when - // the scroll event fires so we make sure to scroll to the right place. - // Otherwise, if SCROLL_DO_FLUSH is also in aFlags, then this method will - // flush layout and you MUST hold a strong ref on 'this' for the duration - // of this call. This might destroy arbitrary layout objects. - MOZ_CAN_RUN_SCRIPT nsresult - ScrollIntoView(SelectionRegion aRegion, ScrollAxis aVertical = ScrollAxis(), - ScrollAxis aHorizontal = ScrollAxis(), int32_t aFlags = 0); + + MOZ_CAN_RUN_SCRIPT nsresult ScrollIntoView( + SelectionRegion, ScrollAxis aVertical = ScrollAxis(), + ScrollAxis aHorizontal = ScrollAxis(), ScrollFlags = ScrollFlags::None, + SelectionScrollMode = SelectionScrollMode::Async); private: static bool IsUserSelectionCollapsed( @@ -903,7 +905,7 @@ class Selection final : public nsSupportsWeakReference, ScrollSelectionIntoViewEvent(Selection* aSelection, SelectionRegion aRegion, ScrollAxis aVertical, ScrollAxis aHorizontal, - int32_t aFlags) + ScrollFlags aFlags) : Runnable("dom::Selection::ScrollSelectionIntoViewEvent"), mSelection(aSelection), mRegion(aRegion), @@ -919,7 +921,7 @@ class Selection final : public nsSupportsWeakReference, SelectionRegion mRegion; ScrollAxis mVerticalScroll; ScrollAxis mHorizontalScroll; - int32_t mFlags; + ScrollFlags mFlags; }; /** diff --git a/dom/events/ContentEventHandler.cpp b/dom/events/ContentEventHandler.cpp index be32fac4bd04..541216c7d7b0 100644 --- a/dom/events/ContentEventHandler.cpp +++ b/dom/events/ContentEventHandler.cpp @@ -3511,8 +3511,7 @@ nsresult ContentEventHandler::OnSelectionEvent(WidgetSelectionEvent* aEvent) { // `ContentEventHandler` is a `MOZ_STACK_CLASS`, so `mSelection` is known to // be alive. MOZ_KnownLive(mSelection) - ->ScrollIntoView(nsISelectionController::SELECTION_FOCUS_REGION, - ScrollAxis(), ScrollAxis(), 0); + ->ScrollIntoView(nsISelectionController::SELECTION_FOCUS_REGION); aEvent->mSucceeded = true; return NS_OK; } diff --git a/dom/events/IMEContentObserver.cpp b/dom/events/IMEContentObserver.cpp index 9185cef77d6f..db75a6f859b0 100644 --- a/dom/events/IMEContentObserver.cpp +++ b/dom/events/IMEContentObserver.cpp @@ -769,8 +769,7 @@ nsresult IMEContentObserver::MaybeHandleSelectionEvent( mSelectionData.StartOffset() == aEvent->mOffset && mSelectionData.Length() == aEvent->mLength) { if (RefPtr selection = mSelection) { - selection->ScrollIntoView(nsISelectionController::SELECTION_FOCUS_REGION, - ScrollAxis(), ScrollAxis(), 0); + selection->ScrollIntoView(nsISelectionController::SELECTION_FOCUS_REGION); } aEvent->mSucceeded = true; return NS_OK; diff --git a/layout/generic/nsFrameSelection.cpp b/layout/generic/nsFrameSelection.cpp index cbe9f43a38ed..c1d2e1c70408 100644 --- a/layout/generic/nsFrameSelection.cpp +++ b/layout/generic/nsFrameSelection.cpp @@ -720,11 +720,11 @@ nsresult nsFrameSelection::MoveCaret(nsDirection aDirection, return NS_ERROR_NULL_POINTER; } - int32_t scrollFlags = 0; + auto scrollFlags = ScrollFlags::None; if (sel->IsEditorSelection()) { // If caret moves in editor, it should cause scrolling even if it's in // overflow: hidden;. - scrollFlags |= Selection::SCROLL_OVERFLOW_HIDDEN; + scrollFlags |= ScrollFlags::ScrollOverflowHidden; } const bool doCollapse = [&] { @@ -1628,9 +1628,13 @@ nsresult nsFrameSelection::ScrollSelectionIntoView(SelectionType aSelectionType, SelectionRegion aRegion, int16_t aFlags) const { int8_t index = GetIndexFromSelectionType(aSelectionType); - if (index < 0) return NS_ERROR_INVALID_ARG; + if (index < 0) { + return NS_ERROR_INVALID_ARG; + } - if (!mDomSelections[index]) return NS_ERROR_NULL_POINTER; + if (!mDomSelections[index]) { + return NS_ERROR_NULL_POINTER; + } const auto vScroll = [&]() -> WhereToScroll { if (aFlags & nsISelectionController::SCROLL_VERTICAL_START) { @@ -1644,20 +1648,21 @@ nsresult nsFrameSelection::ScrollSelectionIntoView(SelectionType aSelectionType, } return WhereToScroll::Nearest; }(); - int32_t flags = Selection::SCROLL_DO_FLUSH; - if (aFlags & nsISelectionController::SCROLL_SYNCHRONOUS) { - flags |= Selection::SCROLL_SYNCHRONOUS; - } else if (aFlags & nsISelectionController::SCROLL_FIRST_ANCESTOR_ONLY) { - flags |= Selection::SCROLL_FIRST_ANCESTOR_ONLY; - } + + auto mode = aFlags & nsISelectionController::SCROLL_SYNCHRONOUS + ? SelectionScrollMode::SyncFlush + : SelectionScrollMode::Async; + + auto scrollFlags = ScrollFlags::None; if (aFlags & nsISelectionController::SCROLL_OVERFLOW_HIDDEN) { - flags |= Selection::SCROLL_OVERFLOW_HIDDEN; + scrollFlags |= ScrollFlags::ScrollOverflowHidden; } // After ScrollSelectionIntoView(), the pending notifications might be // flushed and PresShell/PresContext/Frames may be dead. See bug 418470. RefPtr sel = mDomSelections[index]; - return sel->ScrollIntoView(aRegion, ScrollAxis(vScroll), ScrollAxis(), flags); + return sel->ScrollIntoView(aRegion, ScrollAxis(vScroll), ScrollAxis(), + scrollFlags, mode); } nsresult nsFrameSelection::RepaintSelection(SelectionType aSelectionType) {