Bug 1632726 - part 1: Create utility methods to compute delete range from collapsed selection in `nsFrameSelection` r=smaug

Currently, `EditorBase::ExtendSelectionForDelete()` depends on some
`nsISelectionController` methods to compute extended range for deletion
from collapsed selection.  They are implemented by
`nsFrameSelection::MoveCaret()` and `nsFrameSelection::TakeFocus()`.
Ideally, we should split these methods for computation part and performing
part.  However, they change selection with updating other selection state,
for example, table selection state and bidi information.  Therefore, it's
impossible to split them with simple code.  However, I need to change
`EditorBase::ExtendSelectionForDelete()` just return extended range.

Therefore, this patch creates `nsFrameSelection::PeekOffsetForCaretMove()`
which has the main path in `MoveCaret()` for the `EditorBase` method.

Then, `MoveCaret()` and new `nsFrameSelection::CreateRangeExtendedToSomewhere()`
share the computation code of expanding normal selection.

Finally, this patch wraps `nsFrameSelection::CreateRangeExtendedToSomewhere()`
with new public inline methods for `EditorBase`.

The following patch will remove no-user methods of `nsISelectionController`.

Differential Revision: https://phabricator.services.mozilla.com/D72295
This commit is contained in:
Masayuki Nakano 2020-04-30 10:05:35 +00:00
Родитель bdfeb282f5
Коммит adf7a304f3
5 изменённых файлов: 362 добавлений и 116 удалений

Просмотреть файл

@ -4671,54 +4671,63 @@ nsresult EditorBase::ExtendSelectionForDelete(
return NS_OK;
}
nsCOMPtr<nsISelectionController> selectionController =
GetSelectionController();
if (NS_WARN_IF(!selectionController)) {
RefPtr<nsFrameSelection> frameSelection =
SelectionRefPtr()->GetFrameSelection();
if (NS_WARN_IF(!frameSelection)) {
return NS_ERROR_NOT_INITIALIZED;
}
Result<RefPtr<StaticRange>, nsresult> result(NS_ERROR_UNEXPECTED);
nsIEditor::EDirection directionAndAmountResult = *aDirectionAndAmount;
switch (*aDirectionAndAmount) {
case eNextWord: {
nsresult rv = selectionController->WordExtendForDelete(true);
case eNextWord:
result =
frameSelection->CreateRangeExtendedToNextWordBoundary<StaticRange>();
if (NS_WARN_IF(Destroyed())) {
return NS_ERROR_EDITOR_DESTROYED;
}
NS_WARNING_ASSERTION(
NS_SUCCEEDED(rv),
"nsISelectionController::WordExtendForDelete(true) failed");
result.isOk(),
"nsFrameSelection::CreateRangeExtendedToNextWordBoundary() failed");
// DeleteSelectionWithTransaction() doesn't handle these actions
// because it's inside batching, so don't confuse it:
*aDirectionAndAmount = eNone;
return rv;
}
case ePreviousWord: {
nsresult rv = selectionController->WordExtendForDelete(false);
directionAndAmountResult = eNone;
break;
case ePreviousWord:
result = frameSelection
->CreateRangeExtendedToPreviousWordBoundary<StaticRange>();
if (NS_WARN_IF(Destroyed())) {
return NS_ERROR_EDITOR_DESTROYED;
}
NS_WARNING_ASSERTION(
NS_SUCCEEDED(rv),
"nsISelectionController::WordExtendForDelete(false) failed");
*aDirectionAndAmount = eNone;
return rv;
}
case eNext: {
nsresult rv = selectionController->CharacterExtendForDelete();
result.isOk(),
"nsFrameSelection::CreateRangeExtendedToPreviousWordBoundary() "
"failed");
// DeleteSelectionWithTransaction() doesn't handle these actions
// because it's inside batching, so don't confuse it:
directionAndAmountResult = eNone;
break;
case eNext:
result =
frameSelection
->CreateRangeExtendedToNextGraphemeClusterBoundary<StaticRange>();
if (NS_WARN_IF(Destroyed())) {
return NS_ERROR_EDITOR_DESTROYED;
}
NS_WARNING_ASSERTION(
NS_SUCCEEDED(rv),
"nsISelectionController::CharacterExtendForDelete() failed");
NS_WARNING_ASSERTION(result.isOk(),
"nsFrameSelection::"
"CreateRangeExtendedToNextGraphemeClusterBoundary() "
"failed");
// Don't set aDirectionAndAmount to eNone (see Bug 502259)
return rv;
}
break;
case ePrevious: {
// Only extend the selection where the selection is after a UTF-16
// surrogate pair or a variation selector.
// For other cases we don't want to do that, in order
// to make sure that pressing backspace will only delete the last
// typed character.
// XXX This is odd if the previous one is a sequence for a grapheme
// cluster.
EditorRawDOMPoint atStartOfSelection =
EditorBase::GetStartPoint(*SelectionRefPtr());
if (NS_WARN_IF(!atStartOfSelection.IsSet())) {
@ -4734,52 +4743,80 @@ nsresult EditorBase::ExtendSelectionForDelete(
return NS_OK;
}
if (insertionPoint.IsInTextNode()) {
const nsTextFragment* data =
&insertionPoint.GetContainerAsText()->TextFragment();
uint32_t offset = insertionPoint.Offset();
if ((offset > 1 &&
data->IsLowSurrogateFollowingHighSurrogateAt(offset - 1)) ||
(offset > 0 &&
gfxFontUtils::IsVarSelector(data->CharAt(offset - 1)))) {
nsresult rv = selectionController->CharacterExtendForBackspace();
if (NS_WARN_IF(Destroyed())) {
return NS_ERROR_EDITOR_DESTROYED;
}
NS_WARNING_ASSERTION(
NS_SUCCEEDED(rv),
"nsISelectionController::CharacterExtendForBackspace() failed");
return rv;
}
if (!insertionPoint.IsInTextNode()) {
return NS_OK;
}
return NS_OK;
}
case eToBeginningOfLine: {
// Select to beginning
nsresult rv = selectionController->IntraLineMove(false, true);
const nsTextFragment* data =
&insertionPoint.GetContainerAsText()->TextFragment();
uint32_t offset = insertionPoint.Offset();
if (!(offset > 1 &&
data->IsLowSurrogateFollowingHighSurrogateAt(offset - 1)) &&
!(offset > 0 &&
gfxFontUtils::IsVarSelector(data->CharAt(offset - 1)))) {
return NS_OK;
}
// Different from the `eNext` case, we look for character boundary.
// I'm not sure whether this inconsistency between "Delete" and
// "Backspace" is intentional or not.
result =
frameSelection
->CreateRangeExtendedToPreviousCharacterBoundary<StaticRange>();
if (NS_WARN_IF(Destroyed())) {
return NS_ERROR_EDITOR_DESTROYED;
}
NS_WARNING_ASSERTION(
NS_SUCCEEDED(rv),
"nsISelectionController::IntraLineMove(false, true) failed");
*aDirectionAndAmount = eNone;
return rv;
result.isOk(),
"nsFrameSelection::"
"CreateRangeExtendedToPreviousGraphemeClusterBoundary() failed");
break;
}
case eToEndOfLine: {
nsresult rv = selectionController->IntraLineMove(true, true);
case eToBeginningOfLine:
result = frameSelection
->CreateRangeExtendedToPreviousHardLineBreak<StaticRange>();
if (NS_WARN_IF(Destroyed())) {
return NS_ERROR_EDITOR_DESTROYED;
}
NS_WARNING_ASSERTION(
NS_SUCCEEDED(rv),
"nsISelectionController::IntraLineMove(true, true) failed");
*aDirectionAndAmount = eNext;
return rv;
}
result.isOk(),
"nsFrameSelection::CreateRangeExtendedToPreviousHardLineBreak() "
"failed");
directionAndAmountResult = eNone;
break;
case eToEndOfLine:
result =
frameSelection->CreateRangeExtendedToNextHardLineBreak<StaticRange>();
if (NS_WARN_IF(Destroyed())) {
return NS_ERROR_EDITOR_DESTROYED;
}
NS_WARNING_ASSERTION(
result.isOk(),
"nsFrameSelection::CreateRangeExtendedToNextHardLineBreak() failed");
directionAndAmountResult = eNext;
break;
default:
return NS_OK;
}
if (result.isErr()) {
return result.unwrapErr();
}
*aDirectionAndAmount = directionAndAmountResult;
StaticRange* range = result.inspect().get();
if (!range || NS_WARN_IF(!range->IsPositioned())) {
return NS_OK;
}
ErrorResult error;
MOZ_KnownLive(SelectionRefPtr())
->SetStartAndEndInLimiter(range->StartRef().AsRaw(),
range->EndRef().AsRaw(), error);
if (NS_WARN_IF(Destroyed())) {
error.SuppressException();
return NS_ERROR_EDITOR_DESTROYED;
}
NS_WARNING_ASSERTION(!error.Failed(),
"Selection::SetBaseAndExtentInLimiter() failed");
return error.StealNSResult();
}
nsresult EditorBase::DeleteSelectionWithTransaction(

Просмотреть файл

@ -3140,10 +3140,10 @@ EditActionResult HTMLEditor::HandleDeleteNonCollapsedSelection(
if (SelectionRefPtr()->RangeCount() == 1) {
if (nsRange* firstRange = SelectionRefPtr()->GetRangeAt(0)) {
RefPtr<StaticRange> extendedRange =
GetExtendedRangeToIncludeInvisibleNodes(*firstRange);
GetRangeExtendedToIncludeInvisibleNodes(*firstRange);
if (!extendedRange) {
NS_WARNING(
"HTMLEditor::GetExtendedRangeToIncludeInvisibleNodes() failed");
"HTMLEditor::GetRangeExtendedToIncludeInvisibleNodes() failed");
return EditActionResult(NS_ERROR_FAILURE);
}
ErrorResult error;
@ -7473,7 +7473,7 @@ size_t HTMLEditor::CollectChildren(
}
already_AddRefed<StaticRange>
HTMLEditor::GetExtendedRangeToIncludeInvisibleNodes(
HTMLEditor::GetRangeExtendedToIncludeInvisibleNodes(
const AbstractRange& aAbstractRange) {
MOZ_ASSERT(IsEditActionDataAvailable());
MOZ_ASSERT(!aAbstractRange.Collapsed());

Просмотреть файл

@ -2279,7 +2279,7 @@ class HTMLEditor final : public TextEditor,
nsIEditor::EDirection aDirectionAndAmount);
/**
* GetExtendedRangeToIncludeInvisibleNodes() returns extended range.
* GetRangeExtendedToIncludeInvisibleNodes() returns extended range.
* If there are some invisible nodes around aAbstractRange, they may
* be included.
*
@ -2287,7 +2287,7 @@ class HTMLEditor final : public TextEditor,
* and must be positioned.
* @return Extended range.
*/
already_AddRefed<dom::StaticRange> GetExtendedRangeToIncludeInvisibleNodes(
already_AddRefed<dom::StaticRange> GetRangeExtendedToIncludeInvisibleNodes(
const dom::AbstractRange& aAbstractRange);
/**

Просмотреть файл

@ -70,6 +70,7 @@ static NS_DEFINE_CID(kFrameTraversalCID, NS_FRAMETRAVERSAL_CID);
#include "mozilla/dom/Element.h"
#include "mozilla/dom/Selection.h"
#include "mozilla/dom/ShadowRoot.h"
#include "mozilla/dom/StaticRange.h"
#include "mozilla/dom/Text.h"
#include "mozilla/ErrorResult.h"
#include "mozilla/dom/SelectionBinding.h"
@ -305,6 +306,15 @@ struct MOZ_RAII AutoPrepareFocusRange {
////////////BEGIN nsFrameSelection methods
template Result<RefPtr<nsRange>, nsresult>
nsFrameSelection::CreateRangeExtendedToSomewhere(
nsDirection aDirection, const nsSelectionAmount aAmount,
CaretMovementStyle aMovementStyle);
template Result<RefPtr<StaticRange>, nsresult>
nsFrameSelection::CreateRangeExtendedToSomewhere(
nsDirection aDirection, const nsSelectionAmount aAmount,
CaretMovementStyle aMovementStyle);
nsFrameSelection::nsFrameSelection(PresShell* aPresShell, nsIContent* aLimiter,
const bool aAccessibleCaretEnabled) {
for (size_t i = 0; i < ArrayLength(mDomSelections); i++) {
@ -640,11 +650,6 @@ nsresult nsFrameSelection::MoveCaret(nsDirection aDirection,
bool aContinueSelection,
const nsSelectionAmount aAmount,
CaretMovementStyle aMovementStyle) {
bool visualMovement = aMovementStyle == eVisual ||
(aMovementStyle == eUsePrefStyle &&
(mCaret.mMovementStyle == 1 ||
(mCaret.mMovementStyle == 2 && !aContinueSelection)));
NS_ENSURE_STATE(mPresShell);
// Flush out layout, since we need it to be up to date to do caret
// positioning.
@ -668,8 +673,7 @@ nsresult nsFrameSelection::MoveCaret(nsDirection aDirection,
}
int32_t scrollFlags = Selection::SCROLL_FOR_CARET_MOVE;
const bool isEditorSelection = sel->IsEditorSelection();
if (isEditorSelection) {
if (sel->IsEditorSelection()) {
// If caret moves in editor, it should cause scrolling even if it's in
// overflow: hidden;.
scrollFlags |= Selection::SCROLL_OVERFLOW_HIDDEN;
@ -728,55 +732,33 @@ nsresult nsFrameSelection::MoveCaret(nsDirection aDirection,
return NS_OK;
}
bool visualMovement =
mCaret.IsVisualMovement(aContinueSelection, aMovementStyle);
nsIFrame* frame;
int32_t offsetused = 0;
nsresult result =
nsresult rv =
sel->GetPrimaryFrameForFocusNode(&frame, &offsetused, visualMovement);
if (NS_FAILED(result) || !frame)
return NS_FAILED(result) ? result : NS_ERROR_FAILURE;
const auto forceEditableRegion =
isEditorSelection ? nsPeekOffsetStruct::ForceEditableRegion::Yes
: nsPeekOffsetStruct::ForceEditableRegion::No;
const nsBidiDirection paraDir = nsBidiPresUtils::ParagraphDirection(frame);
if (NS_FAILED(rv) || !frame) {
return NS_FAILED(rv) ? rv : NS_ERROR_FAILURE;
}
CaretAssociateHint tHint(mCaret.mHint); // temporary variable so we dont set
// mCaret.mHint until it is necessary
nsDirection direction{eDirPrevious};
switch (aAmount) {
case eSelectCharacter:
case eSelectCluster:
case eSelectWord:
case eSelectWordNoSpace:
InvalidateDesiredPos();
direction = (visualMovement && paraDir == NSBIDI_RTL)
? nsDirection(1 - aDirection)
: aDirection;
break;
case eSelectLine:
direction = aDirection;
break;
case eSelectBeginLine:
case eSelectEndLine:
InvalidateDesiredPos();
direction = (visualMovement && paraDir == NSBIDI_RTL)
? nsDirection(1 - aDirection)
: aDirection;
break;
default:
return NS_ERROR_FAILURE;
Result<bool, nsresult> isIntraLineCaretMove = IsIntraLineCaretMove(aAmount);
if (isIntraLineCaretMove.isErr()) {
return NS_ERROR_FAILURE;
}
if (isIntraLineCaretMove.inspect()) {
// Forget old caret position for moving caret to different line since
// caret position may be changed.
InvalidateDesiredPos();
}
// set data using mLimiters.mLimiter to stop on scroll views. If we have a
// limiter then we stop peeking when we hit scrollable views. If no limiter
// then just let it go ahead
nsPeekOffsetStruct pos(aAmount, direction, offsetused, desiredPos, true,
mLimiters.mLimiter != nullptr, true, visualMovement,
aContinueSelection, forceEditableRegion);
if (NS_SUCCEEDED(result = frame->PeekOffset(&pos)) && pos.mResultContent) {
Result<nsPeekOffsetStruct, nsresult> result = PeekOffsetForCaretMove(
aDirection, aContinueSelection, aAmount, aMovementStyle, desiredPos);
if (result.isOk() && result.inspect().mResultContent) {
const nsPeekOffsetStruct& pos = result.inspect();
nsIFrame* theFrame;
int32_t currentOffset, frameStart, frameEnd;
@ -837,8 +819,8 @@ nsresult nsFrameSelection::MoveCaret(nsDirection aDirection,
const FocusMode focusMode = aContinueSelection
? FocusMode::kExtendSelection
: FocusMode::kCollapseToNewPoint;
result = TakeFocus(MOZ_KnownLive(pos.mResultContent), pos.mContentOffset,
pos.mContentOffset, tHint, focusMode);
rv = TakeFocus(MOZ_KnownLive(pos.mResultContent), pos.mContentOffset,
pos.mContentOffset, tHint, focusMode);
} else if (aAmount <= eSelectWordNoSpace && aDirection == eDirNext &&
!aContinueSelection) {
// Collapse selection if PeekOffset failed, we either
@ -851,14 +833,69 @@ nsresult nsFrameSelection::MoveCaret(nsDirection aDirection,
mCaret.mHint = CARET_ASSOCIATE_BEFORE; // We're now at the end of the
// frame to the left.
}
result = NS_OK;
rv = NS_OK;
} else {
rv = result.isErr() ? result.unwrapErr() : NS_OK;
}
if (NS_SUCCEEDED(result)) {
result = sel->ScrollIntoView(nsISelectionController::SELECTION_FOCUS_REGION,
ScrollAxis(), ScrollAxis(), scrollFlags);
if (NS_SUCCEEDED(rv)) {
rv = sel->ScrollIntoView(nsISelectionController::SELECTION_FOCUS_REGION,
ScrollAxis(), ScrollAxis(), scrollFlags);
}
return result;
return rv;
}
Result<nsPeekOffsetStruct, nsresult> nsFrameSelection::PeekOffsetForCaretMove(
nsDirection aDirection, bool aContinueSelection,
const nsSelectionAmount aAmount, CaretMovementStyle aMovementStyle,
const nsPoint& aDesiredPos) const {
Selection* selection =
mDomSelections[GetIndexFromSelectionType(SelectionType::eNormal)];
if (!selection) {
return Err(NS_ERROR_NULL_POINTER);
}
const bool visualMovement =
mCaret.IsVisualMovement(aContinueSelection, aMovementStyle);
nsIFrame* frame = nullptr;
int32_t offsetused = 0;
nsresult rv = selection->GetPrimaryFrameForFocusNode(&frame, &offsetused,
visualMovement);
if (NS_FAILED(rv) || !frame) {
return Err(NS_FAILED(rv) ? rv : NS_ERROR_FAILURE);
}
const auto kForceEditableRegion =
selection->IsEditorSelection()
? nsPeekOffsetStruct::ForceEditableRegion::Yes
: nsPeekOffsetStruct::ForceEditableRegion::No;
const nsBidiDirection kParagraphDirection =
nsBidiPresUtils::ParagraphDirection(frame);
nsDirection direction{aDirection};
Result<bool, nsresult> isIntraLineCareMove = IsIntraLineCaretMove(aAmount);
if (isIntraLineCareMove.isErr()) {
return Err(NS_ERROR_FAILURE);
}
if (isIntraLineCareMove.inspect()) {
// If caret is moving in same line and user expects visual movement,
// we revert the direction if direction of current paragraph is RTL.
direction = (visualMovement && kParagraphDirection == NSBIDI_RTL)
? nsDirection(1 - aDirection)
: aDirection;
}
// set data using mLimiters.mLimiter to stop on scroll views. If we have a
// limiter then we stop peeking when we hit scrollable views. If no limiter
// then just let it go ahead
nsPeekOffsetStruct pos(aAmount, direction, offsetused, aDesiredPos, true,
!!mLimiters.mLimiter, true, visualMovement,
aContinueSelection, kForceEditableRegion);
if (NS_FAILED(rv = frame->PeekOffset(&pos))) {
return Err(rv);
}
return pos;
}
nsPrevNextBidiLevels nsFrameSelection::GetPrevNextBidiLevels(
@ -1999,6 +2036,58 @@ nsresult nsFrameSelection::IntraLineMove(bool aForward, bool aExtend) {
}
}
template <typename RangeType>
Result<RefPtr<RangeType>, nsresult>
nsFrameSelection::CreateRangeExtendedToSomewhere(
nsDirection aDirection, const nsSelectionAmount aAmount,
CaretMovementStyle aMovementStyle) {
MOZ_ASSERT(aDirection == eDirNext || aDirection == eDirPrevious);
MOZ_ASSERT(aAmount == eSelectCharacter || aAmount == eSelectCluster ||
aAmount == eSelectWord || aAmount == eSelectBeginLine ||
aAmount == eSelectEndLine);
MOZ_ASSERT(aMovementStyle == eLogical || aMovementStyle == eVisual ||
aMovementStyle == eUsePrefStyle);
if (!mPresShell) {
return Err(NS_ERROR_UNEXPECTED);
}
OwningNonNull<PresShell> presShell(*mPresShell);
presShell->FlushPendingNotifications(FlushType::Layout);
if (!mPresShell) {
return Err(NS_ERROR_FAILURE);
}
Selection* selection =
mDomSelections[GetIndexFromSelectionType(SelectionType::eNormal)];
if (!selection || selection->RangeCount() != 1) {
return Err(NS_ERROR_FAILURE);
}
RefPtr<nsRange> firstRange = selection->GetRangeAt(0);
if (!firstRange || !firstRange->IsPositioned()) {
return Err(NS_ERROR_FAILURE);
}
Result<nsPeekOffsetStruct, nsresult> result = PeekOffsetForCaretMove(
aDirection, true, aAmount, aMovementStyle, nsPoint(0, 0));
if (result.isErr()) {
return Err(NS_ERROR_FAILURE);
}
const nsPeekOffsetStruct& pos = result.inspect();
RefPtr<RangeType> range;
if (NS_WARN_IF(!pos.mResultContent)) {
return range;
}
if (aDirection == eDirPrevious) {
range = RangeType::Create(
RawRangeBoundary(pos.mResultContent, pos.mContentOffset),
firstRange->EndRef(), IgnoreErrors());
} else {
range = RangeType::Create(
firstRange->StartRef(),
RawRangeBoundary(pos.mResultContent, pos.mContentOffset),
IgnoreErrors());
}
return range;
}
nsresult nsFrameSelection::SelectAll() {
nsCOMPtr<nsIContent> rootContent;
if (mLimiters.mLimiter) {

Просмотреть файл

@ -11,6 +11,7 @@
#include "mozilla/Attributes.h"
#include "mozilla/EventForwards.h"
#include "mozilla/dom/Selection.h"
#include "mozilla/Result.h"
#include "mozilla/TextRange.h"
#include "mozilla/UniquePtr.h"
#include "nsIFrame.h"
@ -566,6 +567,74 @@ class nsFrameSelection final {
MOZ_CAN_RUN_SCRIPT_BOUNDARY nsresult IntraLineMove(bool aForward,
bool aExtend);
/**
* CreateRangeExtendedToNextGraphemeClusterBoundary() returns range which is
* extended from normal selection range to start of next grapheme cluster
* boundary.
*/
template <typename RangeType>
MOZ_CAN_RUN_SCRIPT mozilla::Result<RefPtr<RangeType>, nsresult>
CreateRangeExtendedToNextGraphemeClusterBoundary() {
return CreateRangeExtendedToSomewhere<RangeType>(eDirNext, eSelectCluster,
eLogical);
}
/**
* CreateRangeExtendedToPreviousCharacterBoundary() returns range which is
* extended from normal selection range to start of previous character
* boundary.
*/
template <typename RangeType>
MOZ_CAN_RUN_SCRIPT mozilla::Result<RefPtr<RangeType>, nsresult>
CreateRangeExtendedToPreviousCharacterBoundary() {
return CreateRangeExtendedToSomewhere<RangeType>(
eDirPrevious, eSelectCharacter, eLogical);
}
/**
* CreateRangeExtendedToNextWordBoundary() returns range which is
* extended from normal selection range to start of next word boundary.
*/
template <typename RangeType>
MOZ_CAN_RUN_SCRIPT mozilla::Result<RefPtr<RangeType>, nsresult>
CreateRangeExtendedToNextWordBoundary() {
return CreateRangeExtendedToSomewhere<RangeType>(eDirNext, eSelectWord,
eLogical);
}
/**
* CreateRangeExtendedToPreviousWordBoundary() returns range which is
* extended from normal selection range to start of previous word boundary.
*/
template <typename RangeType>
MOZ_CAN_RUN_SCRIPT mozilla::Result<RefPtr<RangeType>, nsresult>
CreateRangeExtendedToPreviousWordBoundary() {
return CreateRangeExtendedToSomewhere<RangeType>(eDirPrevious, eSelectWord,
eLogical);
}
/**
* CreateRangeExtendedToPreviousHardLineBreak() returns range which is
* extended from normal selection range to previous hard line break.
*/
template <typename RangeType>
MOZ_CAN_RUN_SCRIPT mozilla::Result<RefPtr<RangeType>, nsresult>
CreateRangeExtendedToPreviousHardLineBreak() {
return CreateRangeExtendedToSomewhere<RangeType>(
eDirPrevious, eSelectBeginLine, eLogical);
}
/**
* CreateRangeExtendedToNextHardLineBreak() returns range which is extended
* from normal selection range to next hard line break.
*/
template <typename RangeType>
MOZ_CAN_RUN_SCRIPT mozilla::Result<RefPtr<RangeType>, nsresult>
CreateRangeExtendedToNextHardLineBreak() {
return CreateRangeExtendedToSomewhere<RangeType>(eDirNext, eSelectEndLine,
eLogical);
}
/**
* Select All will generally be called from the nsiselectioncontroller
* implementations. it will select the whole doc
@ -770,6 +839,49 @@ class nsFrameSelection final {
nsSelectionAmount aAmount,
CaretMovementStyle aMovementStyle);
/**
* PeekOffsetForCaretMove() only peek offset for caret move. I.e., won't
* change selection ranges nor bidi information.
*/
mozilla::Result<nsPeekOffsetStruct, nsresult> PeekOffsetForCaretMove(
nsDirection aDirection, bool aContinueSelection,
const nsSelectionAmount aAmount, CaretMovementStyle aMovementStyle,
const nsPoint& aDesiredPos) const;
/**
* CreateRangeExtendedToSomewhere() is common method to implement
* CreateRangeExtendedTo*(). This method creates a range extended from
* normal selection range.
*/
template <typename RangeType>
MOZ_CAN_RUN_SCRIPT mozilla::Result<RefPtr<RangeType>, nsresult>
CreateRangeExtendedToSomewhere(nsDirection aDirection,
const nsSelectionAmount aAmount,
CaretMovementStyle aMovementStyle);
/**
* IsIntraLineCaretMove() is a helper method for PeekOffsetForCaretMove()
* and CreateRangeExtendedToSomwhereFromNormalSelection(). This returns
* whether aAmount is intra line move or is crossing hard line break.
* This returns error if aMount is not supported by the methods.
*/
static mozilla::Result<bool, nsresult> IsIntraLineCaretMove(
nsSelectionAmount aAmount) {
switch (aAmount) {
case eSelectCharacter:
case eSelectCluster:
case eSelectWord:
case eSelectWordNoSpace:
case eSelectBeginLine:
case eSelectEndLine:
return true;
case eSelectLine:
return false;
default:
return mozilla::Err(NS_ERROR_FAILURE);
}
}
nsresult FetchDesiredPos(
nsPoint& aDesiredPos); // the position requested by the Key Handling for
// up down
@ -902,6 +1014,14 @@ class nsFrameSelection final {
CaretAssociateHint mHint = mozilla::CARET_ASSOCIATE_BEFORE;
nsBidiLevel mBidiLevel = BIDI_LEVEL_UNDEFINED;
int8_t mMovementStyle = 0;
bool IsVisualMovement(bool aContinueSelection,
CaretMovementStyle aMovementStyle) const {
return aMovementStyle == eVisual ||
(aMovementStyle == eUsePrefStyle &&
(mMovementStyle == 1 ||
(mMovementStyle == 2 && !aContinueSelection)));
}
};
Caret mCaret;