diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-06-12 05:35:37 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-06-12 05:35:37 +0000 |
commit | a90a5cba08fdf6c0ceb95101c275108a152a3aed (patch) | |
tree | 532507288f3defd7f4dcf1af49698bcb76034855 /editor/libeditor/WSRunObject.cpp | |
parent | Adding debian version 126.0.1-1. (diff) | |
download | firefox-a90a5cba08fdf6c0ceb95101c275108a152a3aed.tar.xz firefox-a90a5cba08fdf6c0ceb95101c275108a152a3aed.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.cpp | 181 |
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>())); |