Bug 1939220 - part 5: Make TextFragmentData::Get(EndOf|First)CollapsibleASCIIWhiteSpaces possible to scan non-editable text too r=m_kato

Differential Revision: https://phabricator.services.mozilla.com/D232936
This commit is contained in:
Masayuki Nakano
2025-01-20 08:03:59 +00:00
parent 693817ee11
commit 0e10d664fc
4 changed files with 171 additions and 66 deletions

View File

@@ -459,22 +459,26 @@ WSRunScanner::GetRangeInTextNodesToBackspaceFrom(const EditorDOMPoint& aPoint,
}
}
// If previous char is an collapsible white-spaces, delete all adjcent
// If previous char is an collapsible white-spaces, delete all adjacent
// white-spaces which are collapsed together.
EditorDOMRangeInTexts rangeToDelete;
if (atPreviousChar.IsCharCollapsibleASCIISpace() ||
atPreviousChar.IsCharPreformattedNewLineCollapsedWithWhiteSpaces()) {
const EditorDOMPointInText startToDelete =
textFragmentDataAtCaret.GetFirstASCIIWhiteSpacePointCollapsedTo(
atPreviousChar, nsIEditor::ePrevious);
const auto startToDelete =
textFragmentDataAtCaret
.GetFirstASCIIWhiteSpacePointCollapsedTo<EditorDOMPointInText>(
atPreviousChar, nsIEditor::ePrevious,
IgnoreNonEditableNodes::Yes);
if (!startToDelete.IsSet()) {
NS_WARNING(
"WSRunScanner::GetFirstASCIIWhiteSpacePointCollapsedTo() failed");
return Err(NS_ERROR_FAILURE);
}
const EditorDOMPointInText endToDelete =
textFragmentDataAtCaret.GetEndOfCollapsibleASCIIWhiteSpaces(
atPreviousChar, nsIEditor::ePrevious);
const auto endToDelete =
textFragmentDataAtCaret
.GetEndOfCollapsibleASCIIWhiteSpaces<EditorDOMPointInText>(
atPreviousChar, nsIEditor::ePrevious,
IgnoreNonEditableNodes::Yes);
if (!endToDelete.IsSet()) {
NS_WARNING("WSRunScanner::GetEndOfCollapsibleASCIIWhiteSpaces() failed");
return Err(NS_ERROR_FAILURE);
@@ -554,22 +558,24 @@ WSRunScanner::GetRangeInTextNodesToForwardDeleteFrom(
atNextChar = atNextChar.NextPoint();
}
// If next char is a collapsible white-space, delete all adjcent white-spaces
// If next char is a collapsible white-space, delete all adjacent white-spaces
// which are collapsed together.
EditorDOMRangeInTexts rangeToDelete;
if (atCaret.IsCharCollapsibleASCIISpace() ||
atCaret.IsCharPreformattedNewLineCollapsedWithWhiteSpaces()) {
const EditorDOMPointInText startToDelete =
textFragmentDataAtCaret.GetFirstASCIIWhiteSpacePointCollapsedTo(
atCaret, nsIEditor::eNext);
const auto startToDelete =
textFragmentDataAtCaret
.GetFirstASCIIWhiteSpacePointCollapsedTo<EditorDOMPointInText>(
atCaret, nsIEditor::eNext, IgnoreNonEditableNodes::Yes);
if (!startToDelete.IsSet()) {
NS_WARNING(
"WSRunScanner::GetFirstASCIIWhiteSpacePointCollapsedTo() failed");
return Err(NS_ERROR_FAILURE);
}
const EditorDOMPointInText endToDelete =
textFragmentDataAtCaret.GetEndOfCollapsibleASCIIWhiteSpaces(
atCaret, nsIEditor::eNext);
textFragmentDataAtCaret
.GetEndOfCollapsibleASCIIWhiteSpaces<EditorDOMPointInText>(
atCaret, nsIEditor::eNext, IgnoreNonEditableNodes::Yes);
if (!endToDelete.IsSet()) {
NS_WARNING("WSRunScanner::GetEndOfCollapsibleASCIIWhiteSpaces() failed");
return Err(NS_ERROR_FAILURE);

View File

@@ -1203,14 +1203,56 @@ class MOZ_STACK_CLASS WSRunScanner final {
GetStartReasonContent());
}
template <typename EditorDOMPointType = EditorDOMPointInText>
EditorDOMPointType GetEndOfCollapsibleASCIIWhiteSpaces(
/**
* Return end of current collapsible ASCII white-spaces.
*
* @param aPointAtASCIIWhiteSpace Must be in a sequence of collapsible
* ASCII white-spaces.
* @param aDirectionToDelete The direction to delete.
*/
template <typename EditorDOMPointType>
[[nodiscard]] static EditorDOMPointType GetEndOfCollapsibleASCIIWhiteSpaces(
const EditorDOMPointInText& aPointAtASCIIWhiteSpace,
nsIEditor::EDirection aDirectionToDelete) const;
template <typename EditorDOMPointType = EditorDOMPointInText>
EditorDOMPointType GetFirstASCIIWhiteSpacePointCollapsedTo(
nsIEditor::EDirection aDirectionToDelete,
BlockInlineCheck aBlockInlineCheck,
IgnoreNonEditableNodes aIgnoreNonEditableNodes,
const nsIContent* aFollowingLimiterContent = nullptr);
template <typename EditorDOMPointType>
[[nodiscard]] EditorDOMPointType GetEndOfCollapsibleASCIIWhiteSpaces(
const EditorDOMPointInText& aPointAtASCIIWhiteSpace,
nsIEditor::EDirection aDirectionToDelete) const;
nsIEditor::EDirection aDirectionToDelete,
IgnoreNonEditableNodes aIgnoreNonEditableNodes) const {
return GetEndOfCollapsibleASCIIWhiteSpaces<EditorDOMPointType>(
aPointAtASCIIWhiteSpace, aDirectionToDelete, mBlockInlineCheck,
aIgnoreNonEditableNodes, GetEndReasonContent());
}
/**
* Return start of current collapsible ASCII white-spaces.
*
* @param aPointAtASCIIWhiteSpace Must be in a sequence of collapsible
* ASCII white-spaces.
* @param aDirectionToDelete The direction to delete.
*/
template <typename EditorDOMPointType>
[[nodiscard]] static EditorDOMPointType
GetFirstASCIIWhiteSpacePointCollapsedTo(
const EditorDOMPointInText& aPointAtASCIIWhiteSpace,
nsIEditor::EDirection aDirectionToDelete,
BlockInlineCheck aBlockInlineCheck,
IgnoreNonEditableNodes aIgnoreNonEditableNodes,
const nsIContent* aPrecedingLimiterContent = nullptr);
template <typename EditorDOMPointType>
[[nodiscard]] EditorDOMPointType GetFirstASCIIWhiteSpacePointCollapsedTo(
const EditorDOMPointInText& aPointAtASCIIWhiteSpace,
nsIEditor::EDirection aDirectionToDelete,
IgnoreNonEditableNodes aIgnoreNonEditableNodes) const {
return GetFirstASCIIWhiteSpacePointCollapsedTo<EditorDOMPointType>(
aPointAtASCIIWhiteSpace, aDirectionToDelete, mBlockInlineCheck,
aIgnoreNonEditableNodes, GetStartReasonContent());
}
/**
* GetNonCollapsedRangeInTexts() returns non-empty range in texts which

View File

@@ -82,15 +82,21 @@ NS_INSTANTIATE_METHOD_RETURNING_ANY_EDITOR_DOM_POINT(
IgnoreNonEditableNodes aIgnoreNonEditableNodes,
const nsIContent* aPrecedingLimiterContent);
NS_INSTANTIATE_CONST_METHOD_RETURNING_ANY_EDITOR_DOM_POINT(
NS_INSTANTIATE_METHOD_RETURNING_ANY_EDITOR_DOM_POINT(
WSRunScanner::TextFragmentData::GetEndOfCollapsibleASCIIWhiteSpaces,
const EditorDOMPointInText& aPointAtASCIIWhiteSpace,
nsIEditor::EDirection aDirectionToDelete);
nsIEditor::EDirection aDirectionToDelete,
BlockInlineCheck aBlockInlineCheck,
IgnoreNonEditableNodes aIgnoreNonEditableNodes,
const nsIContent* aFollowingLimiterContent);
NS_INSTANTIATE_CONST_METHOD_RETURNING_ANY_EDITOR_DOM_POINT(
NS_INSTANTIATE_METHOD_RETURNING_ANY_EDITOR_DOM_POINT(
WSRunScanner::TextFragmentData::GetFirstASCIIWhiteSpacePointCollapsedTo,
const EditorDOMPointInText& aPointAtASCIIWhiteSpace,
nsIEditor::EDirection aDirectionToDelete);
nsIEditor::EDirection aDirectionToDelete,
BlockInlineCheck aBlockInlineCheck,
IgnoreNonEditableNodes aIgnoreNonEditableNodes,
const nsIContent* aPrecedingLimiterContent);
constexpr static const AncestorTypes kScanAnyRootAncestorTypes = {
// If the point is in a block, we need to scan only in the block
@@ -714,13 +720,17 @@ WSRunScanner::TextFragmentData::GetReplaceRangeDataAtEndOfDeletionRange(
}
if (nextCharOfStartOfEnd.IsStartOfContainer() ||
nextCharOfStartOfEnd.IsPreviousCharCollapsibleASCIISpace()) {
nextCharOfStartOfEnd = aTextFragmentDataAtStartToDelete
.GetFirstASCIIWhiteSpacePointCollapsedTo(
nextCharOfStartOfEnd, nsIEditor::eNone);
nextCharOfStartOfEnd =
aTextFragmentDataAtStartToDelete
.GetFirstASCIIWhiteSpacePointCollapsedTo<EditorDOMPointInText>(
nextCharOfStartOfEnd, nsIEditor::eNone,
IgnoreNonEditableNodes::Yes);
}
const EditorDOMPointInText endOfCollapsibleASCIIWhiteSpaces =
aTextFragmentDataAtStartToDelete.GetEndOfCollapsibleASCIIWhiteSpaces(
nextCharOfStartOfEnd, nsIEditor::eNone);
const auto endOfCollapsibleASCIIWhiteSpaces =
aTextFragmentDataAtStartToDelete
.GetEndOfCollapsibleASCIIWhiteSpaces<EditorDOMPointInText>(
nextCharOfStartOfEnd, nsIEditor::eNone,
IgnoreNonEditableNodes::Yes);
return ReplaceRangeData(nextCharOfStartOfEnd,
endOfCollapsibleASCIIWhiteSpaces,
nsDependentSubstring(&HTMLEditUtils::kNBSP, 1));
@@ -789,12 +799,14 @@ WSRunScanner::TextFragmentData::GetReplaceRangeDataAtStartOfDeletionRange(
}
if (atPreviousCharOfStart.IsStartOfContainer() ||
atPreviousCharOfStart.IsPreviousCharASCIISpace()) {
atPreviousCharOfStart = GetFirstASCIIWhiteSpacePointCollapsedTo(
atPreviousCharOfStart, nsIEditor::eNone);
atPreviousCharOfStart =
GetFirstASCIIWhiteSpacePointCollapsedTo<EditorDOMPointInText>(
atPreviousCharOfStart, nsIEditor::eNone,
IgnoreNonEditableNodes::Yes);
}
const EditorDOMPointInText endOfCollapsibleASCIIWhiteSpaces =
GetEndOfCollapsibleASCIIWhiteSpaces(atPreviousCharOfStart,
nsIEditor::eNone);
const auto endOfCollapsibleASCIIWhiteSpaces =
GetEndOfCollapsibleASCIIWhiteSpaces<EditorDOMPointInText>(
atPreviousCharOfStart, nsIEditor::eNone, IgnoreNonEditableNodes::Yes);
return ReplaceRangeData(atPreviousCharOfStart,
endOfCollapsibleASCIIWhiteSpaces,
nsDependentSubstring(&HTMLEditUtils::kNBSP, 1));
@@ -991,11 +1003,15 @@ EditorDOMPointType WSRunScanner::TextFragmentData::GetPreviousCharPoint(
return EditorDOMPointType();
}
// static
template <typename EditorDOMPointType>
EditorDOMPointType
WSRunScanner::TextFragmentData::GetEndOfCollapsibleASCIIWhiteSpaces(
const EditorDOMPointInText& aPointAtASCIIWhiteSpace,
nsIEditor::EDirection aDirectionToDelete) const {
nsIEditor::EDirection aDirectionToDelete,
BlockInlineCheck aBlockInlineCheck,
IgnoreNonEditableNodes aIgnoreNonEditableNodes,
const nsIContent* aFollowingLimiterContent /* = nullptr */) {
MOZ_ASSERT(aDirectionToDelete == nsIEditor::eNone ||
aDirectionToDelete == nsIEditor::eNext ||
aDirectionToDelete == nsIEditor::ePrevious);
@@ -1065,8 +1081,9 @@ WSRunScanner::TextFragmentData::GetEndOfCollapsibleASCIIWhiteSpaces(
*aPointAtASCIIWhiteSpace.ContainerAs<Text>());
for (EditorDOMPointInText atEndOfPreviousTextNode = afterLastWhiteSpace;;) {
const auto atStartOfNextTextNode =
GetInclusiveNextCharPoint<EditorDOMPointInText>(
atEndOfPreviousTextNode, IgnoreNonEditableNodes::Yes);
TextFragmentData::GetInclusiveNextCharPoint<EditorDOMPointInText>(
atEndOfPreviousTextNode, aBlockInlineCheck, aIgnoreNonEditableNodes,
aFollowingLimiterContent);
if (!atStartOfNextTextNode.IsSet()) {
// There is no more text nodes. Return end of the previous text node.
return afterLastWhiteSpace.To<EditorDOMPointType>();
@@ -1102,11 +1119,15 @@ WSRunScanner::TextFragmentData::GetEndOfCollapsibleASCIIWhiteSpaces(
}
}
// static
template <typename EditorDOMPointType>
EditorDOMPointType
WSRunScanner::TextFragmentData::GetFirstASCIIWhiteSpacePointCollapsedTo(
const EditorDOMPointInText& aPointAtASCIIWhiteSpace,
nsIEditor::EDirection aDirectionToDelete) const {
nsIEditor::EDirection aDirectionToDelete,
BlockInlineCheck aBlockInlineCheck,
IgnoreNonEditableNodes aIgnoreNonEditableNodes,
const nsIContent* aPrecedingLimiterContent) {
MOZ_ASSERT(aDirectionToDelete == nsIEditor::eNone ||
aDirectionToDelete == nsIEditor::eNext ||
aDirectionToDelete == nsIEditor::ePrevious);
@@ -1178,8 +1199,9 @@ WSRunScanner::TextFragmentData::GetFirstASCIIWhiteSpacePointCollapsedTo(
EditorDOMPointInText(aPointAtASCIIWhiteSpace.ContainerAs<Text>(), 0u);
for (EditorDOMPointInText atStartOfPreviousTextNode = atLastWhiteSpace;;) {
const auto atLastCharOfPreviousTextNode =
GetPreviousCharPoint<EditorDOMPointInText>(atStartOfPreviousTextNode,
IgnoreNonEditableNodes::Yes);
TextFragmentData::GetPreviousCharPoint<EditorDOMPointInText>(
atStartOfPreviousTextNode, aBlockInlineCheck,
aIgnoreNonEditableNodes, aPrecedingLimiterContent);
if (!atLastCharOfPreviousTextNode.IsSet()) {
// There is no more text nodes. Return end of last text node.
return atLastWhiteSpace.To<EditorDOMPointType>();

View File

@@ -931,11 +931,14 @@ WhiteSpaceVisibilityKeeper::InsertLineBreak(
AutoTrackDOMRange trackLeadingWhiteSpaceRange(
aHTMLEditor.RangeUpdaterRef(),
&invisibleLeadingWhiteSpaceRangeOfNewLine);
// We are at start of non-nbsps. Convert to a single nbsp.
const EditorDOMPointInText endOfCollapsibleASCIIWhiteSpaces =
// We are at start of non-NBSPs. Convert to a single NBSP.
const auto endOfCollapsibleASCIIWhiteSpaces =
textFragmentDataAtInsertionPoint
.GetEndOfCollapsibleASCIIWhiteSpaces(
atNextCharOfInsertionPoint, nsIEditor::eNone);
.GetEndOfCollapsibleASCIIWhiteSpaces<EditorDOMPointInText>(
atNextCharOfInsertionPoint, nsIEditor::eNone,
// XXX Shouldn't be "No"? Skipping non-editable nodes may
// have visible content.
IgnoreNonEditableNodes::Yes);
nsresult rv =
WhiteSpaceVisibilityKeeper::ReplaceTextAndRemoveEmptyTextNodes(
aHTMLEditor,
@@ -1452,10 +1455,16 @@ WhiteSpaceVisibilityKeeper::DeletePreviousWhiteSpace(
auto startToDelete =
textFragmentDataAtDeletion
.GetFirstASCIIWhiteSpacePointCollapsedTo<EditorDOMPoint>(
atPreviousCharOfStart, nsIEditor::ePrevious);
atPreviousCharOfStart, nsIEditor::ePrevious,
// XXX Shouldn't be "No"? Skipping non-editable nodes may have
// visible content.
IgnoreNonEditableNodes::Yes);
auto endToDelete = textFragmentDataAtDeletion
.GetEndOfCollapsibleASCIIWhiteSpaces<EditorDOMPoint>(
atPreviousCharOfStart, nsIEditor::ePrevious);
atPreviousCharOfStart, nsIEditor::ePrevious,
// XXX Shouldn't be "No"? Skipping non-editable
// nodes may have visible content.
IgnoreNonEditableNodes::Yes);
EditorDOMPoint pointToPutCaret;
{
Result<CaretPoint, nsresult> caretPointOrError =
@@ -1565,10 +1574,12 @@ WhiteSpaceVisibilityKeeper::DeleteInclusiveNextWhiteSpace(
auto startToDelete =
textFragmentDataAtDeletion
.GetFirstASCIIWhiteSpacePointCollapsedTo<EditorDOMPoint>(
atNextCharOfStart, nsIEditor::eNext);
atNextCharOfStart, nsIEditor::eNext,
IgnoreNonEditableNodes::Yes);
auto endToDelete = textFragmentDataAtDeletion
.GetEndOfCollapsibleASCIIWhiteSpaces<EditorDOMPoint>(
atNextCharOfStart, nsIEditor::eNext);
atNextCharOfStart, nsIEditor::eNext,
IgnoreNonEditableNodes::Yes);
EditorDOMPoint pointToPutCaret;
{
Result<CaretPoint, nsresult> caretPointOrError =
@@ -1970,13 +1981,21 @@ WhiteSpaceVisibilityKeeper::MakeSureToKeepVisibleWhiteSpacesVisibleAfterSplit(
&pointToSplit);
if (atNextCharOfStart.IsStartOfContainer() ||
atNextCharOfStart.IsPreviousCharASCIISpace()) {
atNextCharOfStart = textFragmentDataAtSplitPoint
.GetFirstASCIIWhiteSpacePointCollapsedTo(
atNextCharOfStart, nsIEditor::eNone);
atNextCharOfStart =
textFragmentDataAtSplitPoint
.GetFirstASCIIWhiteSpacePointCollapsedTo<EditorDOMPointInText>(
atNextCharOfStart, nsIEditor::eNone,
// XXX Shouldn't be "No"? Skipping non-editable nodes may
// have visible content.
IgnoreNonEditableNodes::Yes);
}
const EditorDOMPointInText endOfCollapsibleASCIIWhiteSpaces =
textFragmentDataAtSplitPoint.GetEndOfCollapsibleASCIIWhiteSpaces(
atNextCharOfStart, nsIEditor::eNone);
const auto endOfCollapsibleASCIIWhiteSpaces =
textFragmentDataAtSplitPoint
.GetEndOfCollapsibleASCIIWhiteSpaces<EditorDOMPointInText>(
atNextCharOfStart, nsIEditor::eNone,
// XXX Shouldn't be "No"? Skipping non-editable nodes may
// have visible content.
IgnoreNonEditableNodes::Yes);
nsresult rv =
WhiteSpaceVisibilityKeeper::ReplaceTextAndRemoveEmptyTextNodes(
aHTMLEditor,
@@ -2007,12 +2026,19 @@ WhiteSpaceVisibilityKeeper::MakeSureToKeepVisibleWhiteSpacesVisibleAfterSplit(
atPreviousCharOfStart.IsPreviousCharASCIISpace()) {
atPreviousCharOfStart =
textFragmentDataAtSplitPoint
.GetFirstASCIIWhiteSpacePointCollapsedTo(atPreviousCharOfStart,
nsIEditor::eNone);
.GetFirstASCIIWhiteSpacePointCollapsedTo<EditorDOMPointInText>(
atPreviousCharOfStart, nsIEditor::eNone,
// XXX Shouldn't be "No"? Skipping non-editable nodes may
// have visible content.
IgnoreNonEditableNodes::Yes);
}
const EditorDOMPointInText endOfCollapsibleASCIIWhiteSpaces =
textFragmentDataAtSplitPoint.GetEndOfCollapsibleASCIIWhiteSpaces(
atPreviousCharOfStart, nsIEditor::eNone);
const auto endOfCollapsibleASCIIWhiteSpaces =
textFragmentDataAtSplitPoint
.GetEndOfCollapsibleASCIIWhiteSpaces<EditorDOMPointInText>(
atPreviousCharOfStart, nsIEditor::eNone,
// XXX Shouldn't be "No"? Skipping non-editable nodes may
// have visible content.
IgnoreNonEditableNodes::Yes);
nsresult rv =
WhiteSpaceVisibilityKeeper::ReplaceTextAndRemoveEmptyTextNodes(
aHTMLEditor,
@@ -2303,10 +2329,14 @@ nsresult WhiteSpaceVisibilityKeeper::NormalizeVisibleWhiteSpacesAt(
// replace them with `"&nbsp; "` for avoiding collapsing white-spaces.
MOZ_ASSERT(!atPreviousCharOfPreviousCharOfEndOfVisibleWhiteSpaces
.IsEndOfContainer());
const EditorDOMPointInText atFirstASCIIWhiteSpace =
textFragmentData.GetFirstASCIIWhiteSpacePointCollapsedTo(
atPreviousCharOfPreviousCharOfEndOfVisibleWhiteSpaces,
nsIEditor::eNone);
const auto atFirstASCIIWhiteSpace =
textFragmentData
.GetFirstASCIIWhiteSpacePointCollapsedTo<EditorDOMPointInText>(
atPreviousCharOfPreviousCharOfEndOfVisibleWhiteSpaces,
nsIEditor::eNone,
// XXX Shouldn't be "No"? Skipping non-editable nodes may have
// visible content.
IgnoreNonEditableNodes::Yes);
uint32_t numberOfASCIIWhiteSpacesInStartNode =
atFirstASCIIWhiteSpace.ContainerAs<Text>() ==
atPreviousCharOfEndOfVisibleWhiteSpaces.ContainerAs<Text>()
@@ -2401,9 +2431,14 @@ nsresult WhiteSpaceVisibilityKeeper::NormalizeVisibleWhiteSpacesAt(
atPreviousCharOfPreviousCharOfEndOfVisibleWhiteSpaces.IsSet() &&
atPreviousCharOfPreviousCharOfEndOfVisibleWhiteSpaces
.IsCharCollapsibleASCIISpace()) {
startToDelete = textFragmentData.GetFirstASCIIWhiteSpacePointCollapsedTo(
atPreviousCharOfPreviousCharOfEndOfVisibleWhiteSpaces,
nsIEditor::eNone);
startToDelete =
textFragmentData
.GetFirstASCIIWhiteSpacePointCollapsedTo<EditorDOMPointInText>(
atPreviousCharOfPreviousCharOfEndOfVisibleWhiteSpaces,
nsIEditor::eNone,
// XXX Shouldn't be "No"? Skipping non-editable nodes may have
// visible content.
IgnoreNonEditableNodes::Yes);
endToDelete = atPreviousCharOfPreviousCharOfEndOfVisibleWhiteSpaces;
}
// Otherwise, we don't need to remove any white-spaces, but we may need