Bug 1926031 - Make Selection::ScrollIntoView take ScrollFlags. r=jjaschke,masayuki
Differential Revision: https://phabricator.services.mozilla.com/D226389
This commit is contained in:
@@ -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());
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user