Bug 1891408 - part 2: Make WSScanResult::PointAtContent() return a point in a text node r=m_kato

When it's called, it just returns at the reached content node.  However, this
does not make sense when it reached a character in the text node.

Depends on D207685

Differential Revision: https://phabricator.services.mozilla.com/D207686
This commit is contained in:
Masayuki Nakano 2024-04-24 06:12:09 +00:00
parent 49d46d78d6
commit 31cdc09f52
7 changed files with 179 additions and 81 deletions

View file

@ -1616,7 +1616,7 @@ nsresult HTMLEditor::InsertLineBreakAsSubAction() {
}
const WSScanResult forwardScanFromAfterBRElementResult =
WSRunScanner::ScanNextVisibleNodeOrBlockBoundary(
WSRunScanner::ScanInclusiveNextVisibleNodeOrBlockBoundary(
editingHost, pointToPutCaret,
BlockInlineCheck::UseComputedDisplayStyle);
if (MOZ_UNLIKELY(forwardScanFromAfterBRElementResult.Failed())) {
@ -1663,8 +1663,8 @@ nsresult HTMLEditor::InsertLineBreakAsSubAction() {
} else if (forwardScanFromAfterBRElementResult.ReachedSpecialContent()) {
// Next inserting text should be inserted into styled inline elements if
// they have first visible thing in the new line.
pointToPutCaret =
forwardScanFromAfterBRElementResult.PointAtContent<EditorDOMPoint>();
pointToPutCaret = forwardScanFromAfterBRElementResult
.PointAtReachedContent<EditorDOMPoint>();
}
nsresult rv = CollapseSelectionTo(pointToPutCaret);
@ -2281,7 +2281,8 @@ Result<CreateElementResult, nsresult> HTMLEditor::HandleInsertBRElement(
// now.
backwardScanResult.ReachedInlineEditingHostBoundary();
const WSScanResult forwardScanResult =
wsRunScanner.ScanNextVisibleNodeOrBlockBoundaryFrom(aPointToBreak);
wsRunScanner.ScanInclusiveNextVisibleNodeOrBlockBoundaryFrom(
aPointToBreak);
if (MOZ_UNLIKELY(forwardScanResult.Failed())) {
NS_WARNING("WSRunScanner::ScanNextVisibleNodeOrBlockBoundaryFrom() failed");
return Err(NS_ERROR_FAILURE);
@ -2405,7 +2406,7 @@ Result<CreateElementResult, nsresult> HTMLEditor::HandleInsertBRElement(
}
const WSScanResult forwardScanFromAfterBRElementResult =
WSRunScanner::ScanNextVisibleNodeOrBlockBoundary(
WSRunScanner::ScanInclusiveNextVisibleNodeOrBlockBoundary(
&aEditingHost, afterBRElement,
BlockInlineCheck::UseComputedDisplayStyle);
if (MOZ_UNLIKELY(forwardScanFromAfterBRElementResult.Failed())) {
@ -2642,7 +2643,7 @@ HTMLEditor::HandleInsertParagraphInMailCiteElement(
// user if they click there and start typing, because being in the
// mailquote may affect wrapping behavior, or font color, etc.
const WSScanResult forwardScanFromPointToSplitResult =
WSRunScanner::ScanNextVisibleNodeOrBlockBoundary(
WSRunScanner::ScanInclusiveNextVisibleNodeOrBlockBoundary(
&aEditingHost, pointToSplit, BlockInlineCheck::UseHTMLDefaultStyle);
if (forwardScanFromPointToSplitResult.Failed()) {
return Err(NS_ERROR_FAILURE);
@ -2653,8 +2654,8 @@ HTMLEditor::HandleInsertParagraphInMailCiteElement(
forwardScanFromPointToSplitResult.BRElementPtr() != &aMailCiteElement &&
aMailCiteElement.Contains(
forwardScanFromPointToSplitResult.BRElementPtr())) {
pointToSplit =
forwardScanFromPointToSplitResult.PointAfterContent<EditorDOMPoint>();
pointToSplit = forwardScanFromPointToSplitResult
.PointAfterReachedContent<EditorDOMPoint>();
}
if (NS_WARN_IF(!pointToSplit.IsInContentNode())) {
@ -2773,7 +2774,7 @@ HTMLEditor::HandleInsertParagraphInMailCiteElement(
return NS_SUCCESS_DOM_NO_OPERATION;
}
const WSScanResult forwardScanFromPointAfterNewBRElementResult =
WSRunScanner::ScanNextVisibleNodeOrBlockBoundary(
WSRunScanner::ScanInclusiveNextVisibleNodeOrBlockBoundary(
&aEditingHost,
EditorRawDOMPoint::After(pointToCreateNewBRElement),
BlockInlineCheck::UseComputedDisplayStyle);
@ -7725,7 +7726,8 @@ HTMLEditor::GetRangeExtendedToHardLineEdgesForBlockEditAction(
WSRunScanner wsScannerAtStart(&aEditingHost, startPoint,
BlockInlineCheck::UseHTMLDefaultStyle);
const WSScanResult scanResultAtStart =
wsScannerAtStart.ScanNextVisibleNodeOrBlockBoundaryFrom(startPoint);
wsScannerAtStart.ScanInclusiveNextVisibleNodeOrBlockBoundaryFrom(
startPoint);
if (scanResultAtStart.Failed()) {
NS_WARNING("WSRunScanner::ScanNextVisibleNodeOrBlockBoundaryFrom() failed");
return Err(NS_ERROR_FAILURE);
@ -8978,7 +8980,7 @@ HTMLEditor::HandleInsertParagraphInListItemElement(
// put caret in it. If it has non-container inline elements, <br> or <hr>, at
// the element is proper position.
const WSScanResult forwardScanFromStartOfListItemResult =
WSRunScanner::ScanNextVisibleNodeOrBlockBoundary(
WSRunScanner::ScanInclusiveNextVisibleNodeOrBlockBoundary(
&aEditingHost, EditorRawDOMPoint(&rightListItemElement, 0u),
BlockInlineCheck::UseComputedDisplayStyle);
if (MOZ_UNLIKELY(forwardScanFromStartOfListItemResult.Failed())) {
@ -8988,8 +8990,8 @@ HTMLEditor::HandleInsertParagraphInListItemElement(
if (forwardScanFromStartOfListItemResult.ReachedSpecialContent() ||
forwardScanFromStartOfListItemResult.ReachedBRElement() ||
forwardScanFromStartOfListItemResult.ReachedHRElement()) {
auto atFoundElement =
forwardScanFromStartOfListItemResult.PointAtContent<EditorDOMPoint>();
auto atFoundElement = forwardScanFromStartOfListItemResult
.PointAtReachedContent<EditorDOMPoint>();
if (NS_WARN_IF(!atFoundElement.IsSetAndValid())) {
return Err(NS_ERROR_FAILURE);
}

View file

@ -2204,7 +2204,7 @@ nsIContent* HTMLEditUtils::GetContentToPreserveInlineStyles(
}
for (auto point = aPoint.template To<EditorRawDOMPoint>(); point.IsSet();) {
const WSScanResult nextVisibleThing =
WSRunScanner::ScanNextVisibleNodeOrBlockBoundary(
WSRunScanner::ScanInclusiveNextVisibleNodeOrBlockBoundary(
&aEditingHost, point,
BlockInlineCheck::UseComputedDisplayOutsideStyle);
if (nextVisibleThing.InVisibleOrCollapsibleCharacters()) {
@ -2261,7 +2261,7 @@ EditorDOMPointType HTMLEditUtils::GetBetterInsertionPointFor(
// i.e., the insertion position is just before a visible line break <br>,
// we want to skip to the position just after the line break (see bug 68767).
const WSScanResult forwardScanFromPointToInsertResult =
wsScannerForPointToInsert.ScanNextVisibleNodeOrBlockBoundaryFrom(
wsScannerForPointToInsert.ScanInclusiveNextVisibleNodeOrBlockBoundaryFrom(
pointToInsert);
// So, if the next visible node isn't a <br> element, we can insert the block
// level element to the point.
@ -2289,7 +2289,7 @@ EditorDOMPointType HTMLEditUtils::GetBetterInsertionPointFor(
}
return forwardScanFromPointToInsertResult
.template PointAfterContent<EditorDOMPointType>();
.template PointAfterReachedContent<EditorDOMPointType>();
}
// static

View file

@ -1151,7 +1151,7 @@ nsresult HTMLEditor::MaybeCollapseSelectionAtFirstEditableNode(
// If there is editable and visible text node, move caret at first of
// the visible character.
const WSScanResult scanResultInTextNode =
WSRunScanner::ScanNextVisibleNodeOrBlockBoundary(
WSRunScanner::ScanInclusiveNextVisibleNodeOrBlockBoundary(
editingHost, EditorRawDOMPoint(text, 0),
BlockInlineCheck::UseComputedDisplayStyle);
if ((scanResultInTextNode.InVisibleOrCollapsibleCharacters() ||

View file

@ -1373,8 +1373,8 @@ nsresult HTMLEditor::AutoDeleteRangesHandler::ComputeRangesToDelete(
BlockInlineCheck::UseComputedDisplayOutsideStyle);
const WSScanResult scanFromCaretPointResult =
aDirectionAndAmount == nsIEditor::eNext
? wsRunScannerAtCaret.ScanNextVisibleNodeOrBlockBoundaryFrom(
caretPoint)
? wsRunScannerAtCaret
.ScanInclusiveNextVisibleNodeOrBlockBoundaryFrom(caretPoint)
: wsRunScannerAtCaret.ScanPreviousVisibleNodeOrBlockBoundaryFrom(
caretPoint);
if (scanFromCaretPointResult.Failed()) {
@ -1396,8 +1396,10 @@ nsresult HTMLEditor::AutoDeleteRangesHandler::ComputeRangesToDelete(
if (scanFromCaretPointResult.ReachedInvisibleBRElement()) {
EditorDOMPoint newCaretPosition =
aDirectionAndAmount == nsIEditor::eNext
? scanFromCaretPointResult.PointAfterContent<EditorDOMPoint>()
: scanFromCaretPointResult.PointAtContent<EditorDOMPoint>();
? scanFromCaretPointResult
.PointAfterReachedContent<EditorDOMPoint>()
: scanFromCaretPointResult
.PointAtReachedContent<EditorDOMPoint>();
if (NS_WARN_IF(!newCaretPosition.IsSet())) {
return NS_ERROR_FAILURE;
}
@ -1445,7 +1447,8 @@ nsresult HTMLEditor::AutoDeleteRangesHandler::ComputeRangesToDelete(
// Otherwise, extend the range to contain the invisible `<br>`
// element.
if (scanFromCaretPointResult.PointAtContent<EditorRawDOMPoint>()
if (scanFromCaretPointResult
.PointAtReachedContent<EditorRawDOMPoint>()
.IsBefore(
aRangesToDelete
.GetFirstRangeStartPoint<EditorRawDOMPoint>())) {
@ -1458,11 +1461,13 @@ nsresult HTMLEditor::AutoDeleteRangesHandler::ComputeRangesToDelete(
return rv;
}
if (aRangesToDelete.GetFirstRangeEndPoint<EditorRawDOMPoint>()
.IsBefore(scanFromCaretPointResult
.PointAfterContent<EditorRawDOMPoint>())) {
.IsBefore(
scanFromCaretPointResult
.PointAfterReachedContent<EditorRawDOMPoint>())) {
nsresult rv = aRangesToDelete.FirstRangeRef()->SetStartAndEnd(
aRangesToDelete.FirstRangeRef()->StartRef(),
scanFromCaretPointResult.PointAfterContent<EditorRawDOMPoint>()
scanFromCaretPointResult
.PointAfterReachedContent<EditorRawDOMPoint>()
.ToRawRangeBoundary());
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
"nsRange::SetStartAndEnd() failed");
@ -1666,8 +1671,9 @@ Result<EditActionResult, nsresult> HTMLEditor::AutoDeleteRangesHandler::Run(
BlockInlineCheck::UseComputedDisplayOutsideStyle);
const WSScanResult scanFromCaretPointResult =
aDirectionAndAmount == nsIEditor::eNext
? wsRunScannerAtCaret.ScanNextVisibleNodeOrBlockBoundaryFrom(
caretPoint.ref())
? wsRunScannerAtCaret
.ScanInclusiveNextVisibleNodeOrBlockBoundaryFrom(
caretPoint.ref())
: wsRunScannerAtCaret.ScanPreviousVisibleNodeOrBlockBoundaryFrom(
caretPoint.ref());
if (MOZ_UNLIKELY(scanFromCaretPointResult.Failed())) {
@ -1722,7 +1728,7 @@ Result<EditActionResult, nsresult> HTMLEditor::AutoDeleteRangesHandler::Run(
const WSScanResult scanFromCaretPointResult =
aDirectionAndAmount == nsIEditor::eNext
? wsRunScannerAtCaret
.ScanNextVisibleNodeOrBlockBoundaryFrom(
.ScanInclusiveNextVisibleNodeOrBlockBoundaryFrom(
caretPoint.ref())
: wsRunScannerAtCaret
.ScanPreviousVisibleNodeOrBlockBoundaryFrom(
@ -2521,8 +2527,8 @@ bool HTMLEditor::AutoDeleteRangesHandler::AutoBlockElementsJoiner::
aDirectionAndAmount == nsIEditor::eNext
? aWSRunScannerAtCaret.ScanPreviousVisibleNodeOrBlockBoundaryFrom(
aCaretPoint)
: aWSRunScannerAtCaret.ScanNextVisibleNodeOrBlockBoundaryFrom(
aCaretPoint);
: aWSRunScannerAtCaret
.ScanInclusiveNextVisibleNodeOrBlockBoundaryFrom(aCaretPoint);
// If we found a `<br>` element, we need to delete it instead of joining the
// contents.
if (scanFromCaretResult.ReachedBRElement()) {
@ -2572,7 +2578,7 @@ HTMLEditor::AutoDeleteRangesHandler::AutoBlockElementsJoiner::DeleteBRElement(
return maybePreviousText.Point_Deprecated<EditorDOMPoint>();
}
const WSScanResult maybeNextText =
scanner.ScanNextVisibleNodeOrBlockBoundaryFrom(
scanner.ScanInclusiveNextVisibleNodeOrBlockBoundaryFrom(
EditorRawDOMPoint::After(*mBRElement));
if (maybeNextText.IsContentEditable() &&
maybeNextText.InVisibleOrCollapsibleCharacters()) {
@ -4323,7 +4329,7 @@ HTMLEditor::AutoDeleteRangesHandler::DeleteParentBlocksWithTransactionIfEmpty(
// Next, check there is visible contents after the point in current block.
const WSScanResult forwardScanFromPointResult =
wsScannerForPoint.ScanNextVisibleNodeOrBlockBoundaryFrom(aPoint);
wsScannerForPoint.ScanInclusiveNextVisibleNodeOrBlockBoundaryFrom(aPoint);
if (forwardScanFromPointResult.Failed()) {
NS_WARNING("WSRunScanner::ScanNextVisibleNodeOrBlockBoundaryFrom() failed");
return NS_ERROR_FAILURE;
@ -4344,7 +4350,7 @@ HTMLEditor::AutoDeleteRangesHandler::DeleteParentBlocksWithTransactionIfEmpty(
}
if (wsScannerForPoint.GetEndReasonContent()->GetNextSibling()) {
const WSScanResult scanResult =
WSRunScanner::ScanNextVisibleNodeOrBlockBoundary(
WSRunScanner::ScanInclusiveNextVisibleNodeOrBlockBoundary(
editingHost,
EditorRawDOMPoint::After(
*wsScannerForPoint.GetEndReasonContent()),
@ -6841,8 +6847,8 @@ HTMLEditor::AutoDeleteRangesHandler::ExtendOrShrinkRangeToDelete(
*backwardScanFromStartResult.ElementPtr())) {
break;
}
rangeToDelete.SetStart(
backwardScanFromStartResult.PointAtContent<EditorRawDOMPoint>());
rangeToDelete.SetStart(backwardScanFromStartResult
.PointAtReachedContent<EditorRawDOMPoint>());
}
if (aFrameSelection && !aFrameSelection->IsValidSelectionPoint(
rangeToDelete.StartRef().GetContainer())) {
@ -6864,7 +6870,7 @@ HTMLEditor::AutoDeleteRangesHandler::ExtendOrShrinkRangeToDelete(
closestEditingHost, rangeToDelete.EndRef(),
BlockInlineCheck::UseComputedDisplayOutsideStyle);
const WSScanResult forwardScanFromEndResult =
wsScannerAtEnd.ScanNextVisibleNodeOrBlockBoundaryFrom(
wsScannerAtEnd.ScanInclusiveNextVisibleNodeOrBlockBoundaryFrom(
rangeToDelete.EndRef());
if (forwardScanFromEndResult.ReachedBRElement()) {
// XXX In my understanding, this is odd. The end reason may not be
@ -6909,7 +6915,8 @@ HTMLEditor::AutoDeleteRangesHandler::ExtendOrShrinkRangeToDelete(
break;
}
rangeToDelete.SetEnd(
forwardScanFromEndResult.PointAfterContent<EditorRawDOMPoint>());
forwardScanFromEndResult
.PointAfterReachedContent<EditorRawDOMPoint>());
continue;
}

View file

@ -1915,7 +1915,7 @@ HTMLEditor::AutoInlineStyleSetter::ExtendOrShrinkRangeToApplyTheStyle(
EditorDOMRange range(aRange);
if (range.EndRef().IsInContentNode()) {
const WSScanResult nextContentData =
WSRunScanner::ScanNextVisibleNodeOrBlockBoundary(
WSRunScanner::ScanInclusiveNextVisibleNodeOrBlockBoundary(
&aEditingHost, range.EndRef(),
BlockInlineCheck::UseComputedDisplayOutsideStyle);
if (nextContentData.ReachedInvisibleBRElement() &&

View file

@ -49,9 +49,11 @@ template WSScanResult WSRunScanner::ScanPreviousVisibleNodeOrBlockBoundaryFrom(
const EditorDOMPoint& aPoint) const;
template WSScanResult WSRunScanner::ScanPreviousVisibleNodeOrBlockBoundaryFrom(
const EditorRawDOMPoint& aPoint) const;
template WSScanResult WSRunScanner::ScanNextVisibleNodeOrBlockBoundaryFrom(
template WSScanResult
WSRunScanner::ScanInclusiveNextVisibleNodeOrBlockBoundaryFrom(
const EditorDOMPoint& aPoint) const;
template WSScanResult WSRunScanner::ScanNextVisibleNodeOrBlockBoundaryFrom(
template WSScanResult
WSRunScanner::ScanInclusiveNextVisibleNodeOrBlockBoundaryFrom(
const EditorRawDOMPoint& aPoint) const;
template EditorDOMPoint WSRunScanner::GetAfterLastVisiblePoint(
Text& aTextNode, const Element* aAncestorLimiter);
@ -1735,7 +1737,8 @@ WSScanResult WSRunScanner::ScanPreviousVisibleNodeOrBlockBoundaryFrom(
// removed from the tree, they are not editable unless nested contenteditable
// attribute is set to "true".
if (MOZ_UNLIKELY(!aPoint.IsInComposedDoc())) {
return WSScanResult(*aPoint.template ContainerAs<nsIContent>(),
return WSScanResult(WSScanResult::ScanDirection::Backward,
*aPoint.template ContainerAs<nsIContent>(),
WSType::InUncomposedDoc, mBlockInlineCheck);
}
@ -1753,7 +1756,8 @@ WSScanResult WSRunScanner::ScanPreviousVisibleNodeOrBlockBoundaryFrom(
// things now. Whether keep scanning editable things or not should be
// considered by the caller.
if (aPoint.GetChild() && !aPoint.GetChild()->IsEditable()) {
return WSScanResult(*aPoint.GetChild(), WSType::SpecialContent,
return WSScanResult(WSScanResult::ScanDirection::Backward,
*aPoint.GetChild(), WSType::SpecialContent,
mBlockInlineCheck);
}
const auto atPreviousChar =
@ -1761,9 +1765,12 @@ WSScanResult WSRunScanner::ScanPreviousVisibleNodeOrBlockBoundaryFrom(
// When it's a non-empty text node, return it.
if (atPreviousChar.IsSet() && !atPreviousChar.IsContainerEmpty()) {
MOZ_ASSERT(!atPreviousChar.IsEndOfContainer());
return WSScanResult(atPreviousChar.template NextPoint<EditorDOMPoint>(),
return WSScanResult(WSScanResult::ScanDirection::Backward,
atPreviousChar.template NextPoint<EditorDOMPoint>(),
atPreviousChar.IsCharCollapsibleASCIISpaceOrNBSP()
? WSType::CollapsibleWhiteSpaces
: atPreviousChar.IsCharPreformattedNewLine()
? WSType::PreformattedLineBreak
: WSType::NonCollapsibleCharacters,
mBlockInlineCheck);
}
@ -1774,6 +1781,22 @@ WSScanResult WSRunScanner::ScanPreviousVisibleNodeOrBlockBoundaryFrom(
return WSScanResult::Error();
}
switch (TextFragmentDataAtStartRef().StartRawReason()) {
case WSType::CollapsibleWhiteSpaces:
case WSType::NonCollapsibleCharacters:
case WSType::PreformattedLineBreak:
MOZ_ASSERT(TextFragmentDataAtStartRef().StartRef().IsSet());
// XXX: If we find the character at last of a text node and we started
// scanning from following text node of it, some callers may work with the
// point in the following text node instead of end of the found text node.
return WSScanResult(WSScanResult::ScanDirection::Backward,
TextFragmentDataAtStartRef().StartRef(),
TextFragmentDataAtStartRef().StartRawReason(),
mBlockInlineCheck);
default:
break;
}
// Otherwise, return the start of the range.
if (TextFragmentDataAtStartRef().GetStartReasonContent() !=
TextFragmentDataAtStartRef().StartRef().GetContainer()) {
@ -1782,20 +1805,22 @@ WSScanResult WSRunScanner::ScanPreviousVisibleNodeOrBlockBoundaryFrom(
}
// In this case, TextFragmentDataAtStartRef().StartRef().Offset() is not
// meaningful.
return WSScanResult(*TextFragmentDataAtStartRef().GetStartReasonContent(),
return WSScanResult(WSScanResult::ScanDirection::Backward,
*TextFragmentDataAtStartRef().GetStartReasonContent(),
TextFragmentDataAtStartRef().StartRawReason(),
mBlockInlineCheck);
}
if (NS_WARN_IF(!TextFragmentDataAtStartRef().StartRef().IsSet())) {
return WSScanResult::Error();
}
return WSScanResult(TextFragmentDataAtStartRef().StartRef(),
return WSScanResult(WSScanResult::ScanDirection::Backward,
TextFragmentDataAtStartRef().StartRef(),
TextFragmentDataAtStartRef().StartRawReason(),
mBlockInlineCheck);
}
template <typename PT, typename CT>
WSScanResult WSRunScanner::ScanNextVisibleNodeOrBlockBoundaryFrom(
WSScanResult WSRunScanner::ScanInclusiveNextVisibleNodeOrBlockBoundaryFrom(
const EditorDOMPointBase<PT, CT>& aPoint) const {
MOZ_ASSERT(aPoint.IsSet());
MOZ_ASSERT(aPoint.IsInComposedDoc());
@ -1809,7 +1834,8 @@ WSScanResult WSRunScanner::ScanNextVisibleNodeOrBlockBoundaryFrom(
// removed from the tree, they are not editable unless nested contenteditable
// attribute is set to "true".
if (MOZ_UNLIKELY(!aPoint.IsInComposedDoc())) {
return WSScanResult(*aPoint.template ContainerAs<nsIContent>(),
return WSScanResult(WSScanResult::ScanDirection::Forward,
*aPoint.template ContainerAs<nsIContent>(),
WSType::InUncomposedDoc, mBlockInlineCheck);
}
@ -1827,17 +1853,21 @@ WSScanResult WSRunScanner::ScanNextVisibleNodeOrBlockBoundaryFrom(
// things now. Whether keep scanning editable things or not should be
// considered by the caller.
if (aPoint.GetChild() && !aPoint.GetChild()->IsEditable()) {
return WSScanResult(*aPoint.GetChild(), WSType::SpecialContent,
return WSScanResult(WSScanResult::ScanDirection::Forward,
*aPoint.GetChild(), WSType::SpecialContent,
mBlockInlineCheck);
}
const auto atNextChar =
GetInclusiveNextEditableCharPoint<EditorDOMPoint>(aPoint);
// When it's a non-empty text node, return it.
if (atNextChar.IsSet() && !atNextChar.IsContainerEmpty()) {
return WSScanResult(atNextChar,
return WSScanResult(WSScanResult::ScanDirection::Forward, atNextChar,
!atNextChar.IsEndOfContainer() &&
atNextChar.IsCharCollapsibleASCIISpaceOrNBSP()
? WSType::CollapsibleWhiteSpaces
: !atNextChar.IsEndOfContainer() &&
atNextChar.IsCharPreformattedNewLine()
? WSType::PreformattedLineBreak
: WSType::NonCollapsibleCharacters,
mBlockInlineCheck);
}
@ -1848,6 +1878,23 @@ WSScanResult WSRunScanner::ScanNextVisibleNodeOrBlockBoundaryFrom(
return WSScanResult::Error();
}
switch (TextFragmentDataAtStartRef().EndRawReason()) {
case WSType::CollapsibleWhiteSpaces:
case WSType::NonCollapsibleCharacters:
case WSType::PreformattedLineBreak:
MOZ_ASSERT(TextFragmentDataAtStartRef().StartRef().IsSet());
// XXX: If we find the character at start of a text node and we
// started scanning from preceding text node of it, some callers may want
// to work with the point at end of the preceding text node instead of
// start of the found text node.
return WSScanResult(WSScanResult::ScanDirection::Forward,
TextFragmentDataAtStartRef().EndRef(),
TextFragmentDataAtStartRef().EndRawReason(),
mBlockInlineCheck);
default:
break;
}
// Otherwise, return the end of the range.
if (TextFragmentDataAtStartRef().GetEndReasonContent() !=
TextFragmentDataAtStartRef().EndRef().GetContainer()) {
@ -1856,14 +1903,16 @@ WSScanResult WSRunScanner::ScanNextVisibleNodeOrBlockBoundaryFrom(
}
// In this case, TextFragmentDataAtStartRef().EndRef().Offset() is not
// meaningful.
return WSScanResult(*TextFragmentDataAtStartRef().GetEndReasonContent(),
return WSScanResult(WSScanResult::ScanDirection::Forward,
*TextFragmentDataAtStartRef().GetEndReasonContent(),
TextFragmentDataAtStartRef().EndRawReason(),
mBlockInlineCheck);
}
if (NS_WARN_IF(!TextFragmentDataAtStartRef().EndRef().IsSet())) {
return WSScanResult::Error();
}
return WSScanResult(TextFragmentDataAtStartRef().EndRef(),
return WSScanResult(WSScanResult::ScanDirection::Forward,
TextFragmentDataAtStartRef().EndRef(),
TextFragmentDataAtStartRef().EndRawReason(),
mBlockInlineCheck);
}

View file

@ -106,17 +106,24 @@ class MOZ_STACK_CLASS WSScanResult final {
public:
WSScanResult() = delete;
MOZ_NEVER_INLINE_DEBUG WSScanResult(nsIContent& aContent, WSType aReason,
enum class ScanDirection : bool { Backward, Forward };
MOZ_NEVER_INLINE_DEBUG WSScanResult(ScanDirection aScanDirection,
nsIContent& aContent, WSType aReason,
BlockInlineCheck aBlockInlineCheck)
: mContent(&aContent), mReason(aReason) {
: mContent(&aContent), mReason(aReason), mDirection(aScanDirection) {
MOZ_ASSERT(aReason != WSType::CollapsibleWhiteSpaces &&
aReason != WSType::NonCollapsibleCharacters &&
aReason != WSType::PreformattedLineBreak);
AssertIfInvalidData(aBlockInlineCheck);
}
MOZ_NEVER_INLINE_DEBUG WSScanResult(const EditorDOMPoint& aPoint,
MOZ_NEVER_INLINE_DEBUG WSScanResult(ScanDirection aScanDirection,
const EditorDOMPoint& aPoint,
WSType aReason,
BlockInlineCheck aBlockInlineCheck)
: mContent(aPoint.GetContainerAs<nsIContent>()),
mOffset(Some(aPoint.Offset())),
mReason(aReason) {
mReason(aReason),
mDirection(aScanDirection) {
AssertIfInvalidData(aBlockInlineCheck);
}
@ -142,13 +149,31 @@ class MOZ_STACK_CLASS WSScanResult final {
MOZ_ASSERT_IF(mContent && !mContent->IsInComposedDoc(),
mReason == WSType::InUncomposedDoc);
MOZ_ASSERT_IF(mReason == WSType::NonCollapsibleCharacters ||
mReason == WSType::CollapsibleWhiteSpaces,
mReason == WSType::CollapsibleWhiteSpaces ||
mReason == WSType::PreformattedLineBreak,
mContent->IsText());
MOZ_ASSERT_IF(mReason == WSType::NonCollapsibleCharacters ||
mReason == WSType::CollapsibleWhiteSpaces ||
mReason == WSType::PreformattedLineBreak,
mOffset.isSome());
MOZ_ASSERT_IF(mReason == WSType::NonCollapsibleCharacters ||
mReason == WSType::CollapsibleWhiteSpaces ||
mReason == WSType::PreformattedLineBreak,
mContent->AsText()->TextDataLength() > 0);
MOZ_ASSERT_IF(mDirection == ScanDirection::Backward &&
(mReason == WSType::NonCollapsibleCharacters ||
mReason == WSType::CollapsibleWhiteSpaces ||
mReason == WSType::PreformattedLineBreak),
*mOffset > 0);
MOZ_ASSERT_IF(mDirection == ScanDirection::Forward &&
(mReason == WSType::NonCollapsibleCharacters ||
mReason == WSType::CollapsibleWhiteSpaces ||
mReason == WSType::PreformattedLineBreak),
*mOffset < mContent->AsText()->TextDataLength());
MOZ_ASSERT_IF(mReason == WSType::BRElement,
mContent->IsHTMLElement(nsGkAtoms::br));
MOZ_ASSERT_IF(
mReason == WSType::PreformattedLineBreak,
mContent->IsText() && EditorUtils::IsNewLinePreformatted(*mContent));
MOZ_ASSERT_IF(mReason == WSType::PreformattedLineBreak,
EditorUtils::IsNewLinePreformatted(*mContent));
MOZ_ASSERT_IF(
mReason == WSType::SpecialContent,
(mContent->IsText() && !mContent->IsEditable()) ||
@ -241,24 +266,34 @@ class MOZ_STACK_CLASS WSScanResult final {
}
/**
* PointAtContent() returns the position of found visible content or reached
* block element.
*/
template <typename EditorDOMPointType>
EditorDOMPointType PointAtContent() const {
MOZ_ASSERT(mContent);
return EditorDOMPointType(mContent);
}
/**
* PointAfterContent() returns the position after found visible content or
* PointAtReachedContent() returns the position of found visible content or
* reached block element.
*/
template <typename EditorDOMPointType>
EditorDOMPointType PointAfterContent() const {
EditorDOMPointType PointAtReachedContent() const {
MOZ_ASSERT(mContent);
return mContent ? EditorDOMPointType::After(mContent)
: EditorDOMPointType();
switch (mReason) {
case WSType::CollapsibleWhiteSpaces:
case WSType::NonCollapsibleCharacters:
case WSType::PreformattedLineBreak:
MOZ_DIAGNOSTIC_ASSERT(mOffset.isSome());
return mDirection == ScanDirection::Forward
? EditorDOMPointType(mContent, mOffset.valueOr(0))
: EditorDOMPointType(mContent,
std::max(mOffset.valueOr(1), 1u) - 1);
default:
return EditorDOMPointType(mContent);
}
}
/**
* PointAfterReachedContent() returns the position after found visible content
* or reached block element.
*/
template <typename EditorDOMPointType>
EditorDOMPointType PointAfterReachedContent() const {
MOZ_ASSERT(mContent);
return PointAtReachedContent<EditorDOMPointType>().template NextPoint();
}
/**
@ -362,6 +397,7 @@ class MOZ_STACK_CLASS WSScanResult final {
nsCOMPtr<nsIContent> mContent;
Maybe<uint32_t> mOffset;
WSType mReason;
ScanDirection mDirection = ScanDirection::Backward;
};
class MOZ_STACK_CLASS WSRunScanner final {
@ -378,25 +414,29 @@ class MOZ_STACK_CLASS WSRunScanner final {
aBlockInlineCheck),
mBlockInlineCheck(aBlockInlineCheck) {}
// ScanNextVisibleNodeOrBlockBoundaryForwardFrom() returns the first visible
// node after aPoint. If there is no visible nodes after aPoint, returns
// topmost editable inline ancestor at end of current block. See comments
// around WSScanResult for the detail.
// ScanInclusiveNextVisibleNodeOrBlockBoundaryFrom() returns the first visible
// node at or after aPoint. If there is no visible nodes after aPoint,
// returns topmost editable inline ancestor at end of current block. See
// comments around WSScanResult for the detail. When you reach a character,
// this returns WSScanResult both whose Point_Deprecated() and
// PointAtReachedContent() return the found character position.
template <typename PT, typename CT>
WSScanResult ScanNextVisibleNodeOrBlockBoundaryFrom(
WSScanResult ScanInclusiveNextVisibleNodeOrBlockBoundaryFrom(
const EditorDOMPointBase<PT, CT>& aPoint) const;
template <typename PT, typename CT>
static WSScanResult ScanNextVisibleNodeOrBlockBoundary(
static WSScanResult ScanInclusiveNextVisibleNodeOrBlockBoundary(
const Element* aEditingHost, const EditorDOMPointBase<PT, CT>& aPoint,
BlockInlineCheck aBlockInlineCheck) {
return WSRunScanner(aEditingHost, aPoint, aBlockInlineCheck)
.ScanNextVisibleNodeOrBlockBoundaryFrom(aPoint);
.ScanInclusiveNextVisibleNodeOrBlockBoundaryFrom(aPoint);
}
// ScanPreviousVisibleNodeOrBlockBoundaryFrom() returns the first visible node
// before aPoint. If there is no visible nodes before aPoint, returns topmost
// editable inline ancestor at start of current block. See comments around
// WSScanResult for the detail.
// WSScanResult for the detail. When you reach a character, this returns
// WSScanResult whose Point_Deprecated() returns next point of the found
// character and PointAtReachedContent() returns the point at found character.
template <typename PT, typename CT>
WSScanResult ScanPreviousVisibleNodeOrBlockBoundaryFrom(
const EditorDOMPointBase<PT, CT>& aPoint) const;