Bug 1926031 - Make Selection::ScrollIntoView take ScrollFlags. r=jjaschke,masayuki

Differential Revision: https://phabricator.services.mozilla.com/D226389
This commit is contained in:
Emilio Cobos Álvarez
2024-10-22 09:58:02 +00:00
parent 228997534a
commit 1123d2e75f
7 changed files with 62 additions and 62 deletions

View File

@@ -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());

View File

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

View File

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

View File

@@ -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;
};
/**

View File

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

View File

@@ -769,8 +769,7 @@ nsresult IMEContentObserver::MaybeHandleSelectionEvent(
mSelectionData.StartOffset() == aEvent->mOffset &&
mSelectionData.Length() == aEvent->mLength) {
if (RefPtr<Selection> selection = mSelection) {
selection->ScrollIntoView(nsISelectionController::SELECTION_FOCUS_REGION,
ScrollAxis(), ScrollAxis(), 0);
selection->ScrollIntoView(nsISelectionController::SELECTION_FOCUS_REGION);
}
aEvent->mSucceeded = true;
return NS_OK;

View File

@@ -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<Selection> 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) {