From e8e8d245bc2c64879abc1153cfba03f6da204c56 Mon Sep 17 00:00:00 2001 From: Masayuki Nakano Date: Sat, 8 Mar 2025 22:31:55 +0000 Subject: [PATCH] Bug 1940377 - part 3: Make "insert paragraph" handlers use the new normalizer if it's enabled r=m_kato Unfortunately, this change does not make `white-spaces-after-execCommand-insertparagraph.tentative.html` pass because the insert paragraph handler inserts a temporary `
` element and that causes normalizing the following white-spaces at the split point. However, Chrome does not normalize the following white-spaces if it begins with an NBSP. Differential Revision: https://phabricator.services.mozilla.com/D239465 --- editor/libeditor/HTMLEditSubActionHandler.cpp | 31 ++++++++- editor/libeditor/HTMLEditor.cpp | 8 ++- .../libeditor/WhiteSpaceVisibilityKeeper.cpp | 63 ++++++++++++++----- editor/libeditor/WhiteSpaceVisibilityKeeper.h | 14 ++++- 4 files changed, 93 insertions(+), 23 deletions(-) diff --git a/editor/libeditor/HTMLEditSubActionHandler.cpp b/editor/libeditor/HTMLEditSubActionHandler.cpp index 2cb79eed4b32..c537444a1a43 100644 --- a/editor/libeditor/HTMLEditSubActionHandler.cpp +++ b/editor/libeditor/HTMLEditSubActionHandler.cpp @@ -560,9 +560,9 @@ nsresult HTMLEditor::OnEndHandlingTopLevelEditSubActionInternal() { case EditSubAction::eInsertText: case EditSubAction::eInsertTextComingFromIME: case EditSubAction::eInsertLineBreak: + case EditSubAction::eInsertParagraphSeparator: return !StaticPrefs:: editor_white_space_normalization_blink_compatible(); - case EditSubAction::eInsertParagraphSeparator: case EditSubAction::ePasteHTMLContent: case EditSubAction::eInsertHTMLSource: return true; @@ -2305,7 +2305,7 @@ HTMLEditor::InsertParagraphSeparatorAsSubAction(const Element& aEditingHost) { Result HTMLEditor::HandleInsertBRElement( const EditorDOMPoint& aPointToBreak, const Element& aEditingHost) { - MOZ_ASSERT(aPointToBreak.IsSet()); + MOZ_ASSERT(aPointToBreak.IsInContentNode()); MOZ_ASSERT(IsEditActionDataAvailable()); const bool editingHostIsEmpty = HTMLEditUtils::IsEmptyNode( @@ -2365,6 +2365,8 @@ Result HTMLEditor::HandleInsertBRElement( RefPtr linkNode = HTMLEditor::GetLinkElement(pointToBreak.GetContainer()); if (linkNode) { + // FIXME: Normalize surrounding white-spaces before splitting the + // insertion point here. Result splitLinkNodeResult = SplitNodeDeepWithTransaction( *linkNode, pointToBreak, @@ -2716,6 +2718,26 @@ Result HTMLEditor::HandleInsertParagraphInMailCiteElement( return Err(NS_ERROR_FAILURE); } + if (StaticPrefs::editor_white_space_normalization_blink_compatible()) { + Result pointToSplitOrError = + WhiteSpaceVisibilityKeeper::NormalizeWhiteSpacesToSplitAt( + *this, pointToSplit, + {WhiteSpaceVisibilityKeeper::NormalizeOption:: + StopIfPrecedingWhiteSpacesEndsWithNBP, + WhiteSpaceVisibilityKeeper::NormalizeOption:: + StopIfFollowingWhiteSpacesStartsWithNBSP}); + if (MOZ_UNLIKELY(pointToSplitOrError.isErr())) { + NS_WARNING( + "WhiteSpaceVisibilityKeeper::NormalizeWhiteSpacesToSplitAt() " + "failed"); + return pointToSplitOrError.propagateErr(); + } + pointToSplit = pointToSplitOrError.unwrap(); + if (NS_WARN_IF(!pointToSplit.IsInContentNode())) { + return Err(NS_ERROR_EDITOR_UNEXPECTED_DOM_TREE); + } + } + Result splitResult = SplitNodeDeepWithTransaction(aMailCiteElement, pointToSplit, SplitAtEdges::eDoNotCreateEmptyContainer); @@ -9005,6 +9027,7 @@ Result HTMLEditor::HandleInsertParagraphInParagraph( if (pointToInsertBR.IsInContentNode() && HTMLEditUtils::CanNodeContain( *pointToInsertBR.ContainerAs(), *nsGkAtoms::br)) { + AutoTrackDOMPoint trackPointToSplit(RangeUpdaterRef(), &pointToSplit); Result insertBRElementResultOrError = InsertLineBreak(WithTransaction::Yes, LineBreakType::BRElement, pointToInsertBR); @@ -9020,6 +9043,10 @@ Result HTMLEditor::HandleInsertParagraphInParagraph( // SplitParagraphWithTransaction. insertBRElementResult.IgnoreCaretPointSuggestion(); brElement = &insertBRElementResult->BRElementRef(); + trackPointToSplit.FlushAndStopTracking(); + if (NS_WARN_IF(!pointToSplit.IsInContentNodeAndValidInComposedDoc())) { + return Err(NS_ERROR_EDITOR_UNEXPECTED_DOM_TREE); + } } } } else { diff --git a/editor/libeditor/HTMLEditor.cpp b/editor/libeditor/HTMLEditor.cpp index 0048d789d08d..cb4a55f00bab 100644 --- a/editor/libeditor/HTMLEditor.cpp +++ b/editor/libeditor/HTMLEditor.cpp @@ -4414,7 +4414,9 @@ Result HTMLEditor::PrepareToInsertLineBreak( } Result pointToInsertOrError = WhiteSpaceVisibilityKeeper::NormalizeWhiteSpacesToSplitAt( - *this, aPointToInsert); + *this, aPointToInsert, + {WhiteSpaceVisibilityKeeper::NormalizeOption:: + StopIfPrecedingWhiteSpacesEndsWithNBP}); if (NS_WARN_IF(pointToInsertOrError.isErr())) { return pointToInsertOrError.propagateErr(); } @@ -4433,7 +4435,9 @@ Result HTMLEditor::PrepareToInsertLineBreak( Result pointToInsertOrError = StaticPrefs::editor_white_space_normalization_blink_compatible() ? WhiteSpaceVisibilityKeeper::NormalizeWhiteSpacesToSplitAt( - *this, aPointToInsert) + *this, aPointToInsert, + {WhiteSpaceVisibilityKeeper::NormalizeOption:: + StopIfPrecedingWhiteSpacesEndsWithNBP}) : aPointToInsert; if (NS_WARN_IF(pointToInsertOrError.isErr())) { return pointToInsertOrError.propagateErr(); diff --git a/editor/libeditor/WhiteSpaceVisibilityKeeper.cpp b/editor/libeditor/WhiteSpaceVisibilityKeeper.cpp index 050bfe14acf9..caa97cc8ba80 100644 --- a/editor/libeditor/WhiteSpaceVisibilityKeeper.cpp +++ b/editor/libeditor/WhiteSpaceVisibilityKeeper.cpp @@ -47,7 +47,7 @@ Result WhiteSpaceVisibilityKeeper::PrepareToSplitBlockElement( HTMLEditor& aHTMLEditor, const EditorDOMPoint& aPointToSplit, const Element& aSplittingBlockElement) { - if (NS_WARN_IF(!aPointToSplit.IsInContentNode()) || + if (NS_WARN_IF(!aPointToSplit.IsInContentNodeAndValidInComposedDoc()) || NS_WARN_IF(!HTMLEditUtils::IsSplittableNode(aSplittingBlockElement)) || NS_WARN_IF(!EditorUtils::IsEditableContent( *aPointToSplit.ContainerAs(), EditorType::HTML))) { @@ -69,7 +69,8 @@ WhiteSpaceVisibilityKeeper::PrepareToSplitBlockElement( pointToSplit.Set(content); } - { + // TODO: Delete this block once we ship the new normalizer. + if (!StaticPrefs::editor_white_space_normalization_blink_compatible()) { AutoTrackDOMPoint tracker(aHTMLEditor.RangeUpdaterRef(), &pointToSplit); nsresult rv = WhiteSpaceVisibilityKeeper:: @@ -84,6 +85,21 @@ WhiteSpaceVisibilityKeeper::PrepareToSplitBlockElement( "MakeSureToKeepVisibleWhiteSpacesVisibleAfterSplit() failed"); return Err(rv); } + } else { + // NOTE: Chrome does not normalize white-spaces at splitting `Text` when + // inserting a paragraph at least when the surrounding white-spaces being or + // end with an NBSP. + Result pointToSplitOrError = + WhiteSpaceVisibilityKeeper::NormalizeWhiteSpacesToSplitAt( + aHTMLEditor, pointToSplit, + {NormalizeOption::StopIfFollowingWhiteSpacesStartsWithNBSP, + NormalizeOption::StopIfPrecedingWhiteSpacesEndsWithNBP}); + if (MOZ_UNLIKELY(pointToSplitOrError.isErr())) { + NS_WARNING( + "WhiteSpaceVisibilityKeeper::NormalizeWhiteSpacesToSplitAt() failed"); + return pointToSplitOrError.propagateErr(); + } + pointToSplit = pointToSplitOrError.unwrap(); } if (NS_WARN_IF(!pointToSplit.IsInContentNode()) || @@ -821,7 +837,8 @@ Result WhiteSpaceVisibilityKeeper:: // static Result WhiteSpaceVisibilityKeeper::NormalizeWhiteSpacesToSplitTextNodeAt( - HTMLEditor& aHTMLEditor, const EditorDOMPointInText& aPointToSplit) { + HTMLEditor& aHTMLEditor, const EditorDOMPointInText& aPointToSplit, + NormalizeOptions aOptions) { MOZ_ASSERT(aPointToSplit.IsSetAndValid()); MOZ_ASSERT(StaticPrefs::editor_white_space_normalization_blink_compatible()); @@ -855,13 +872,18 @@ WhiteSpaceVisibilityKeeper::NormalizeWhiteSpacesToSplitTextNodeAt( const HTMLEditor::ReplaceWhiteSpacesData replacePrecedingWhiteSpacesData = aPointToSplit.IsStartOfContainer() || - // Chrome does not normalize the left `Text` at least when it ends - // with an NBSP. - aPointToSplit.IsPreviousCharNBSP() + (aOptions.contains( + NormalizeOption::StopIfPrecedingWhiteSpacesEndsWithNBP) && + aPointToSplit.IsPreviousCharNBSP()) ? HTMLEditor::ReplaceWhiteSpacesData() : aHTMLEditor.GetPrecedingNormalizedStringToSplitAt(aPointToSplit); const HTMLEditor::ReplaceWhiteSpacesData replaceFollowingWhiteSpaceData = - aHTMLEditor.GetFollowingNormalizedStringToSplitAt(aPointToSplit); + aPointToSplit.IsEndOfContainer() || + (aOptions.contains( + NormalizeOption::StopIfFollowingWhiteSpacesStartsWithNBSP) && + aPointToSplit.IsCharNBSP()) + ? HTMLEditor::ReplaceWhiteSpacesData() + : aHTMLEditor.GetFollowingNormalizedStringToSplitAt(aPointToSplit); const HTMLEditor::ReplaceWhiteSpacesData replaceWhiteSpacesData = (replacePrecedingWhiteSpacesData + replaceFollowingWhiteSpaceData) .GetMinimizedData(*textNode); @@ -904,7 +926,8 @@ WhiteSpaceVisibilityKeeper::NormalizeWhiteSpacesToSplitTextNodeAt( // static Result WhiteSpaceVisibilityKeeper::NormalizeWhiteSpacesToSplitAt( - HTMLEditor& aHTMLEditor, const EditorDOMPoint& aPointToSplit) { + HTMLEditor& aHTMLEditor, const EditorDOMPoint& aPointToSplit, + NormalizeOptions aOptions) { MOZ_ASSERT(aPointToSplit.IsSet()); MOZ_ASSERT(StaticPrefs::editor_white_space_normalization_blink_compatible()); @@ -936,7 +959,7 @@ WhiteSpaceVisibilityKeeper::NormalizeWhiteSpacesToSplitAt( if (pointToSplit.IsInTextNode()) { Result pointToSplitOrError = WhiteSpaceVisibilityKeeper::NormalizeWhiteSpacesToSplitTextNodeAt( - aHTMLEditor, pointToSplit.AsInText()); + aHTMLEditor, pointToSplit.AsInText(), aOptions); if (MOZ_UNLIKELY(pointToSplitOrError.isErr())) { NS_WARNING( "WhiteSpaceVisibilityKeeper::NormalizeWhiteSpacesToSplitTextNodeAt() " @@ -981,11 +1004,9 @@ WhiteSpaceVisibilityKeeper::NormalizeWhiteSpacesToSplitAt( textNode->TextDataLength()) { break; } - // Chrome does not normalize preceding `Text` at least when it ends with - // an NBSP. - if (textNode->TextDataLength() && - textNode->TextFragment().CharAt(textNode->TextLength() - 1u) == - HTMLEditUtils::kNBSP) { + if (aOptions.contains( + NormalizeOption::StopIfPrecedingWhiteSpacesEndsWithNBP) && + textNode->TextFragment().SafeLastChar() == HTMLEditUtils::kNBSP) { break; } precedingTextNodes.AppendElement(*textNode); @@ -1021,6 +1042,11 @@ WhiteSpaceVisibilityKeeper::NormalizeWhiteSpacesToSplitAt( textNode->TextDataLength()) { break; } + if (aOptions.contains( + NormalizeOption::StopIfFollowingWhiteSpacesStartsWithNBSP) && + textNode->TextFragment().SafeFirstChar() == HTMLEditUtils::kNBSP) { + break; + } followingTextNodes.AppendElement(*textNode); if (textNode->TextIsOnlyWhitespace() && EditorUtils::IsWhiteSpacePreformatted(*textNode)) { @@ -1045,7 +1071,7 @@ WhiteSpaceVisibilityKeeper::NormalizeWhiteSpacesToSplitAt( for (const auto& textNode : precedingTextNodes) { Result normalizeWhiteSpacesResultOrError = WhiteSpaceVisibilityKeeper::NormalizeWhiteSpacesToSplitTextNodeAt( - aHTMLEditor, EditorDOMPointInText::AtEndOf(textNode)); + aHTMLEditor, EditorDOMPointInText::AtEndOf(textNode), aOptions); if (MOZ_UNLIKELY(normalizeWhiteSpacesResultOrError.isErr())) { NS_WARNING( "WhiteSpaceVisibilityKeeper::NormalizeWhiteSpacesToSplitTextNodeAt() " @@ -1062,7 +1088,7 @@ WhiteSpaceVisibilityKeeper::NormalizeWhiteSpacesToSplitAt( for (const auto& textNode : followingTextNodes) { Result normalizeWhiteSpacesResultOrError = WhiteSpaceVisibilityKeeper::NormalizeWhiteSpacesToSplitTextNodeAt( - aHTMLEditor, EditorDOMPointInText(textNode, 0u)); + aHTMLEditor, EditorDOMPointInText(textNode, 0u), aOptions); if (MOZ_UNLIKELY(normalizeWhiteSpacesResultOrError.isErr())) { NS_WARNING( "WhiteSpaceVisibilityKeeper::NormalizeWhiteSpacesToSplitTextNodeAt() " @@ -1295,10 +1321,13 @@ WhiteSpaceVisibilityKeeper::InsertLineBreak( } } } else { + // Chrome does not normalize preceding white-spaces at least when it ends + // with an NBSP. Result normalizeSurroundingWhiteSpacesResultOrError = WhiteSpaceVisibilityKeeper::NormalizeWhiteSpacesToSplitAt( - aHTMLEditor, aPointToInsert); + aHTMLEditor, aPointToInsert, + {NormalizeOption::StopIfPrecedingWhiteSpacesEndsWithNBP}); if (MOZ_UNLIKELY(normalizeSurroundingWhiteSpacesResultOrError.isErr())) { NS_WARNING( "WhiteSpaceVisibilityKeeper::NormalizeWhiteSpacesToSplitAt() failed"); diff --git a/editor/libeditor/WhiteSpaceVisibilityKeeper.h b/editor/libeditor/WhiteSpaceVisibilityKeeper.h index ebb3035e8c2f..76b2f66ab435 100644 --- a/editor/libeditor/WhiteSpaceVisibilityKeeper.h +++ b/editor/libeditor/WhiteSpaceVisibilityKeeper.h @@ -130,6 +130,14 @@ class WhiteSpaceVisibilityKeeper final { const EditorDOMPoint& aPointToSplit, const Element& aSplittingBlockElement); + enum class NormalizeOption { + // If set, don't normalize following white-spaces if starts with an NBSP. + StopIfFollowingWhiteSpacesStartsWithNBSP, + // If set, don't normalize preceding white-spaces if ends with an NBSP. + StopIfPrecedingWhiteSpacesEndsWithNBP, + }; + using NormalizeOptions = EnumSet; + /** * Normalize surrounding white-spaces of aPointToSplit. This may normalize * 2 `Text` nodes if the point is surrounded by them. @@ -137,7 +145,8 @@ class WhiteSpaceVisibilityKeeper final { */ [[nodiscard]] MOZ_CAN_RUN_SCRIPT static Result NormalizeWhiteSpacesToSplitAt(HTMLEditor& aHTMLEditor, - const EditorDOMPoint& aPointToSplit); + const EditorDOMPoint& aPointToSplit, + NormalizeOptions aOptions); /** * MergeFirstLineOfRightBlockElementIntoDescendantLeftBlockElement() merges @@ -365,7 +374,8 @@ class WhiteSpaceVisibilityKeeper final { */ [[nodiscard]] MOZ_CAN_RUN_SCRIPT static Result NormalizeWhiteSpacesToSplitTextNodeAt( - HTMLEditor& aHTMLEditor, const EditorDOMPointInText& aPointToSplit); + HTMLEditor& aHTMLEditor, const EditorDOMPointInText& aPointToSplit, + NormalizeOptions aOptions); /** * Delete leading or trailing invisible white-spaces around block boundaries