summaryrefslogtreecommitdiffstats
path: root/editor/libeditor/WSRunObject.cpp
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-06-12 05:43:14 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-06-12 05:43:14 +0000
commit8dd16259287f58f9273002717ec4d27e97127719 (patch)
tree3863e62a53829a84037444beab3abd4ed9dfc7d0 /editor/libeditor/WSRunObject.cpp
parentReleasing progress-linux version 126.0.1-1~progress7.99u1. (diff)
downloadfirefox-8dd16259287f58f9273002717ec4d27e97127719.tar.xz
firefox-8dd16259287f58f9273002717ec4d27e97127719.zip
Merging upstream version 127.0.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'editor/libeditor/WSRunObject.cpp')
-rw-r--r--editor/libeditor/WSRunObject.cpp181
1 files changed, 147 insertions, 34 deletions
diff --git a/editor/libeditor/WSRunObject.cpp b/editor/libeditor/WSRunObject.cpp
index 7149578be1..8acd1f60e7 100644
--- a/editor/libeditor/WSRunObject.cpp
+++ b/editor/libeditor/WSRunObject.cpp
@@ -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);
@@ -1292,7 +1294,9 @@ Result<InsertTextResult, nsresult> WhiteSpaceVisibilityKeeper::ReplaceText(
// If the insertion point is (was) before the start of text and it's
// immediately after a hard line break, the first ASCII white-space should
// be replaced with an NBSP for making it visible.
- else if (textFragmentDataAtStart.StartsFromHardLineBreak() &&
+ else if ((textFragmentDataAtStart.StartsFromHardLineBreak() ||
+ textFragmentDataAtStart
+ .StartsFromInlineEditingHostBoundary()) &&
isInsertionPointEqualsOrIsBeforeStartOfText) {
theString.SetCharAt(HTMLEditUtils::kNBSP, 0);
}
@@ -1325,7 +1329,8 @@ Result<InsertTextResult, nsresult> WhiteSpaceVisibilityKeeper::ReplaceText(
// If the end of replacing range is (was) after the end of text and it's
// immediately before block boundary, the last ASCII white-space should
// be replaced with an NBSP for making it visible.
- else if (textFragmentDataAtEnd.EndsByBlockBoundary() &&
+ else if ((textFragmentDataAtEnd.EndsByBlockBoundary() ||
+ textFragmentDataAtEnd.EndsByInlineEditingHostBoundary()) &&
isInsertionPointEqualsOrAfterEndOfText) {
theString.SetCharAt(HTMLEditUtils::kNBSP, lastCharIndex);
}
@@ -1721,9 +1726,24 @@ template <typename PT, typename CT>
WSScanResult WSRunScanner::ScanPreviousVisibleNodeOrBlockBoundaryFrom(
const EditorDOMPointBase<PT, CT>& aPoint) const {
MOZ_ASSERT(aPoint.IsSet());
+ MOZ_ASSERT(aPoint.IsInComposedDoc());
+
+ if (MOZ_UNLIKELY(!aPoint.IsSet())) {
+ return WSScanResult::Error();
+ }
+
+ // We may not be able to check editable state in uncomposed tree as expected.
+ // For example, only some descendants in an editing host is temporarily
+ // removed from the tree, they are not editable unless nested contenteditable
+ // attribute is set to "true".
+ if (MOZ_UNLIKELY(!aPoint.IsInComposedDoc())) {
+ return WSScanResult(WSScanResult::ScanDirection::Backward,
+ *aPoint.template ContainerAs<nsIContent>(),
+ WSType::InUncomposedDoc, mBlockInlineCheck);
+ }
if (!TextFragmentDataAtStartRef().IsInitialized()) {
- return WSScanResult(nullptr, WSType::UnexpectedError, mBlockInlineCheck);
+ return WSScanResult::Error();
}
// If the range has visible text and start of the visible text is before
@@ -1736,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 =
@@ -1744,35 +1765,82 @@ 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);
}
}
+ if (NS_WARN_IF(TextFragmentDataAtStartRef().StartRawReason() ==
+ WSType::UnexpectedError)) {
+ 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()) {
+ if (NS_WARN_IF(!TextFragmentDataAtStartRef().GetStartReasonContent())) {
+ return WSScanResult::Error();
+ }
// In this case, TextFragmentDataAtStartRef().StartRef().Offset() is not
// meaningful.
- return WSScanResult(TextFragmentDataAtStartRef().GetStartReasonContent(),
+ return WSScanResult(WSScanResult::ScanDirection::Backward,
+ *TextFragmentDataAtStartRef().GetStartReasonContent(),
TextFragmentDataAtStartRef().StartRawReason(),
mBlockInlineCheck);
}
- return WSScanResult(TextFragmentDataAtStartRef().StartRef(),
+ if (NS_WARN_IF(!TextFragmentDataAtStartRef().StartRef().IsSet())) {
+ return WSScanResult::Error();
+ }
+ 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());
+
+ if (MOZ_UNLIKELY(!aPoint.IsSet())) {
+ return WSScanResult::Error();
+ }
+
+ // We may not be able to check editable state in uncomposed tree as expected.
+ // For example, only some descendants in an editing host is temporarily
+ // removed from the tree, they are not editable unless nested contenteditable
+ // attribute is set to "true".
+ if (MOZ_UNLIKELY(!aPoint.IsInComposedDoc())) {
+ return WSScanResult(WSScanResult::ScanDirection::Forward,
+ *aPoint.template ContainerAs<nsIContent>(),
+ WSType::InUncomposedDoc, mBlockInlineCheck);
+ }
if (!TextFragmentDataAtStartRef().IsInitialized()) {
- return WSScanResult(nullptr, WSType::UnexpectedError, mBlockInlineCheck);
+ return WSScanResult::Error();
}
// If the range has visible text and aPoint equals or is before the end of the
@@ -1785,32 +1853,66 @@ 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);
}
}
+ if (NS_WARN_IF(TextFragmentDataAtStartRef().EndRawReason() ==
+ WSType::UnexpectedError)) {
+ 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()) {
+ if (NS_WARN_IF(!TextFragmentDataAtStartRef().GetEndReasonContent())) {
+ return WSScanResult::Error();
+ }
// In this case, TextFragmentDataAtStartRef().EndRef().Offset() is not
// meaningful.
- return WSScanResult(TextFragmentDataAtStartRef().GetEndReasonContent(),
+ return WSScanResult(WSScanResult::ScanDirection::Forward,
+ *TextFragmentDataAtStartRef().GetEndReasonContent(),
TextFragmentDataAtStartRef().EndRawReason(),
mBlockInlineCheck);
}
- return WSScanResult(TextFragmentDataAtStartRef().EndRef(),
+ if (NS_WARN_IF(!TextFragmentDataAtStartRef().EndRef().IsSet())) {
+ return WSScanResult::Error();
+ }
+ return WSScanResult(WSScanResult::ScanDirection::Forward,
+ TextFragmentDataAtStartRef().EndRef(),
TextFragmentDataAtStartRef().EndRawReason(),
mBlockInlineCheck);
}
@@ -1944,6 +2046,7 @@ WSRunScanner::TextFragmentData::BoundaryData WSRunScanner::TextFragmentData::
const Element* aEditingHost, NoBreakingSpaceData* aNBSPData,
BlockInlineCheck aBlockInlineCheck) {
MOZ_ASSERT(aPoint.IsSetAndValid());
+ MOZ_ASSERT(aEditableBlockParentOrTopmostEditableInlineElement.IsEditable());
if (aPoint.IsInTextNode() && !aPoint.IsStartOfContainer()) {
Maybe<BoundaryData> startInTextNode =
@@ -1967,14 +2070,16 @@ WSRunScanner::TextFragmentData::BoundaryData WSRunScanner::TextFragmentData::
{LeafNodeType::LeafNodeOrNonEditableNode}, aBlockInlineCheck,
aEditingHost);
if (!previousLeafContentOrBlock) {
- // no prior node means we exhausted
- // aEditableBlockParentOrTopmostEditableInlineElement
- // mReasonContent can be either a block element or any non-editable
- // content in this case.
+ // No previous content means that we reached
+ // aEditableBlockParentOrTopmostEditableInlineElement boundary.
return BoundaryData(aPoint,
const_cast<Element&>(
aEditableBlockParentOrTopmostEditableInlineElement),
- WSType::CurrentBlockBoundary);
+ HTMLEditUtils::IsBlockElement(
+ aEditableBlockParentOrTopmostEditableInlineElement,
+ aBlockInlineCheck)
+ ? WSType::CurrentBlockBoundary
+ : WSType::InlineEditingHostBoundary);
}
if (HTMLEditUtils::IsBlockElement(*previousLeafContentOrBlock,
@@ -2088,6 +2193,7 @@ WSRunScanner::TextFragmentData::BoundaryData::ScanCollapsibleWhiteSpaceEndFrom(
const Element* aEditingHost, NoBreakingSpaceData* aNBSPData,
BlockInlineCheck aBlockInlineCheck) {
MOZ_ASSERT(aPoint.IsSetAndValid());
+ MOZ_ASSERT(aEditableBlockParentOrTopmostEditableInlineElement.IsEditable());
if (aPoint.IsInTextNode() && !aPoint.IsEndOfContainer()) {
Maybe<BoundaryData> endInTextNode =
@@ -2111,14 +2217,16 @@ WSRunScanner::TextFragmentData::BoundaryData::ScanCollapsibleWhiteSpaceEndFrom(
{LeafNodeType::LeafNodeOrNonEditableNode}, aBlockInlineCheck,
aEditingHost);
if (!nextLeafContentOrBlock) {
- // no next node means we exhausted
- // aEditableBlockParentOrTopmostEditableInlineElement
- // mReasonContent can be either a block element or any non-editable
- // content in this case.
+ // No next content means that we reached
+ // aEditableBlockParentOrTopmostEditableInlineElement boundary.
return BoundaryData(aPoint.template To<EditorDOMPoint>(),
const_cast<Element&>(
aEditableBlockParentOrTopmostEditableInlineElement),
- WSType::CurrentBlockBoundary);
+ HTMLEditUtils::IsBlockElement(
+ aEditableBlockParentOrTopmostEditableInlineElement,
+ aBlockInlineCheck)
+ ? WSType::CurrentBlockBoundary
+ : WSType::InlineEditingHostBoundary);
}
if (HTMLEditUtils::IsBlockElement(*nextLeafContentOrBlock,
@@ -2172,7 +2280,7 @@ WSRunScanner::TextFragmentData::InvisibleLeadingWhiteSpaceRangeRef() const {
}
// If it's start of line, there is no invisible leading white-spaces.
- if (!StartsFromHardLineBreak()) {
+ if (!StartsFromHardLineBreak() && !StartsFromInlineEditingHostBoundary()) {
mLeadingWhiteSpaceRange.emplace();
return mLeadingWhiteSpaceRange.ref();
}
@@ -2202,7 +2310,8 @@ WSRunScanner::TextFragmentData::InvisibleTrailingWhiteSpaceRangeRef() const {
// If it's not immediately before a block boundary nor an invisible
// preformatted linefeed, there is no invisible trailing white-spaces. Note
// that collapsible white-spaces before a `<br>` element is visible.
- if (!EndsByBlockBoundary() && !EndsByInvisiblePreformattedLineBreak()) {
+ if (!EndsByBlockBoundary() && !EndsByInlineEditingHostBoundary() &&
+ !EndsByInvisiblePreformattedLineBreak()) {
mTrailingWhiteSpaceRange.emplace();
return mTrailingWhiteSpaceRange.ref();
}
@@ -2332,7 +2441,7 @@ WSRunScanner::TextFragmentData::VisibleWhiteSpacesDataRef() const {
return mVisibleWhiteSpacesData.ref();
}
- if (!StartsFromHardLineBreak()) {
+ if (!StartsFromHardLineBreak() && !StartsFromInlineEditingHostBoundary()) {
VisibleWhiteSpacesData visibleWhiteSpaces;
if (mStart.PointRef().IsSet()) {
visibleWhiteSpaces.SetStartPoint(mStart.PointRef());
@@ -2352,7 +2461,8 @@ WSRunScanner::TextFragmentData::VisibleWhiteSpacesDataRef() const {
return mVisibleWhiteSpacesData.ref();
}
- MOZ_ASSERT(StartsFromHardLineBreak());
+ MOZ_ASSERT(StartsFromHardLineBreak() ||
+ StartsFromInlineEditingHostBoundary());
MOZ_ASSERT(maybeHaveLeadingWhiteSpaces);
VisibleWhiteSpacesData visibleWhiteSpaces;
@@ -2360,7 +2470,7 @@ WSRunScanner::TextFragmentData::VisibleWhiteSpacesDataRef() const {
visibleWhiteSpaces.SetStartPoint(leadingWhiteSpaceRange.EndRef());
}
visibleWhiteSpaces.SetStartFromLeadingWhiteSpaces();
- if (!EndsByBlockBoundary()) {
+ if (!EndsByBlockBoundary() && !EndsByInlineEditingHostBoundary()) {
// then no trailing ws. this normal run ends the overall ws run.
if (mEnd.PointRef().IsSet()) {
visibleWhiteSpaces.SetEndPoint(mEnd.PointRef());
@@ -2370,7 +2480,7 @@ WSRunScanner::TextFragmentData::VisibleWhiteSpacesDataRef() const {
return mVisibleWhiteSpacesData.ref();
}
- MOZ_ASSERT(EndsByBlockBoundary());
+ MOZ_ASSERT(EndsByBlockBoundary() || EndsByInlineEditingHostBoundary());
if (!maybeHaveTrailingWhiteSpaces) {
// normal ws runs right up to adjacent block (nbsp next to block)
@@ -3401,7 +3511,8 @@ nsresult WhiteSpaceVisibilityKeeper::NormalizeVisibleWhiteSpacesAt(
isPreviousCharCollapsibleASCIIWhiteSpace) {
// First, try to insert <br> element if NBSP is at end of a block.
// XXX We should stop this if there is a visible content.
- if (visibleWhiteSpaces.EndsByBlockBoundary() &&
+ if ((visibleWhiteSpaces.EndsByBlockBoundary() ||
+ visibleWhiteSpaces.EndsByInlineEditingHostBoundary()) &&
aPoint.IsInContentNode()) {
bool insertBRElement = HTMLEditUtils::IsBlockElement(
*aPoint.template ContainerAs<nsIContent>(),
@@ -4320,7 +4431,8 @@ WSRunScanner::GetRangeContainingInvisibleWhiteSpacesAtRangeBoundaries(
// If there is no invisible white-space and the line starts with a
// text node, shrink the range to start of the text node.
else if (!aRange.StartRef().IsInTextNode() &&
- textFragmentDataAtStart.StartsFromBlockBoundary() &&
+ (textFragmentDataAtStart.StartsFromBlockBoundary() ||
+ textFragmentDataAtStart.StartsFromInlineEditingHostBoundary()) &&
textFragmentDataAtStart.EndRef().IsInTextNode()) {
result.SetStart(textFragmentDataAtStart.EndRef());
}
@@ -4353,7 +4465,8 @@ WSRunScanner::GetRangeContainingInvisibleWhiteSpacesAtRangeBoundaries(
// If there is no invisible white-space and the line ends with a text
// node, shrink the range to end of the text node.
else if (!aRange.EndRef().IsInTextNode() &&
- textFragmentDataAtEnd.EndsByBlockBoundary() &&
+ (textFragmentDataAtEnd.EndsByBlockBoundary() ||
+ textFragmentDataAtEnd.EndsByInlineEditingHostBoundary()) &&
textFragmentDataAtEnd.StartRef().IsInTextNode()) {
result.SetEnd(EditorDOMPoint::AtEndOf(
*textFragmentDataAtEnd.StartRef().ContainerAs<Text>()));