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 `<br>` 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
This commit is contained in:
Masayuki Nakano
2025-03-08 00:23:15 +00:00
parent cb5f775b5c
commit e14afee1c1
4 changed files with 93 additions and 23 deletions

View File

@@ -560,9 +560,9 @@ nsresult HTMLEditor::OnEndHandlingTopLevelEditSubActionInternal() {
case EditSubAction::eInsertText: case EditSubAction::eInsertText:
case EditSubAction::eInsertTextComingFromIME: case EditSubAction::eInsertTextComingFromIME:
case EditSubAction::eInsertLineBreak: case EditSubAction::eInsertLineBreak:
case EditSubAction::eInsertParagraphSeparator:
return !StaticPrefs:: return !StaticPrefs::
editor_white_space_normalization_blink_compatible(); editor_white_space_normalization_blink_compatible();
case EditSubAction::eInsertParagraphSeparator:
case EditSubAction::ePasteHTMLContent: case EditSubAction::ePasteHTMLContent:
case EditSubAction::eInsertHTMLSource: case EditSubAction::eInsertHTMLSource:
return true; return true;
@@ -2305,7 +2305,7 @@ HTMLEditor::InsertParagraphSeparatorAsSubAction(const Element& aEditingHost) {
Result<CreateElementResult, nsresult> HTMLEditor::HandleInsertBRElement( Result<CreateElementResult, nsresult> HTMLEditor::HandleInsertBRElement(
const EditorDOMPoint& aPointToBreak, const Element& aEditingHost) { const EditorDOMPoint& aPointToBreak, const Element& aEditingHost) {
MOZ_ASSERT(aPointToBreak.IsSet()); MOZ_ASSERT(aPointToBreak.IsInContentNode());
MOZ_ASSERT(IsEditActionDataAvailable()); MOZ_ASSERT(IsEditActionDataAvailable());
const bool editingHostIsEmpty = HTMLEditUtils::IsEmptyNode( const bool editingHostIsEmpty = HTMLEditUtils::IsEmptyNode(
@@ -2365,6 +2365,8 @@ Result<CreateElementResult, nsresult> HTMLEditor::HandleInsertBRElement(
RefPtr<Element> linkNode = RefPtr<Element> linkNode =
HTMLEditor::GetLinkElement(pointToBreak.GetContainer()); HTMLEditor::GetLinkElement(pointToBreak.GetContainer());
if (linkNode) { if (linkNode) {
// FIXME: Normalize surrounding white-spaces before splitting the
// insertion point here.
Result<SplitNodeResult, nsresult> splitLinkNodeResult = Result<SplitNodeResult, nsresult> splitLinkNodeResult =
SplitNodeDeepWithTransaction( SplitNodeDeepWithTransaction(
*linkNode, pointToBreak, *linkNode, pointToBreak,
@@ -2716,6 +2718,26 @@ Result<CaretPoint, nsresult> HTMLEditor::HandleInsertParagraphInMailCiteElement(
return Err(NS_ERROR_FAILURE); return Err(NS_ERROR_FAILURE);
} }
if (StaticPrefs::editor_white_space_normalization_blink_compatible()) {
Result<EditorDOMPoint, nsresult> 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<SplitNodeResult, nsresult> splitResult = Result<SplitNodeResult, nsresult> splitResult =
SplitNodeDeepWithTransaction(aMailCiteElement, pointToSplit, SplitNodeDeepWithTransaction(aMailCiteElement, pointToSplit,
SplitAtEdges::eDoNotCreateEmptyContainer); SplitAtEdges::eDoNotCreateEmptyContainer);
@@ -9005,6 +9027,7 @@ Result<SplitNodeResult, nsresult> HTMLEditor::HandleInsertParagraphInParagraph(
if (pointToInsertBR.IsInContentNode() && if (pointToInsertBR.IsInContentNode() &&
HTMLEditUtils::CanNodeContain( HTMLEditUtils::CanNodeContain(
*pointToInsertBR.ContainerAs<nsIContent>(), *nsGkAtoms::br)) { *pointToInsertBR.ContainerAs<nsIContent>(), *nsGkAtoms::br)) {
AutoTrackDOMPoint trackPointToSplit(RangeUpdaterRef(), &pointToSplit);
Result<CreateLineBreakResult, nsresult> insertBRElementResultOrError = Result<CreateLineBreakResult, nsresult> insertBRElementResultOrError =
InsertLineBreak(WithTransaction::Yes, LineBreakType::BRElement, InsertLineBreak(WithTransaction::Yes, LineBreakType::BRElement,
pointToInsertBR); pointToInsertBR);
@@ -9020,6 +9043,10 @@ Result<SplitNodeResult, nsresult> HTMLEditor::HandleInsertParagraphInParagraph(
// SplitParagraphWithTransaction. // SplitParagraphWithTransaction.
insertBRElementResult.IgnoreCaretPointSuggestion(); insertBRElementResult.IgnoreCaretPointSuggestion();
brElement = &insertBRElementResult->BRElementRef(); brElement = &insertBRElementResult->BRElementRef();
trackPointToSplit.FlushAndStopTracking();
if (NS_WARN_IF(!pointToSplit.IsInContentNodeAndValidInComposedDoc())) {
return Err(NS_ERROR_EDITOR_UNEXPECTED_DOM_TREE);
}
} }
} }
} else { } else {

View File

@@ -4394,7 +4394,9 @@ Result<EditorDOMPoint, nsresult> HTMLEditor::PrepareToInsertLineBreak(
} }
Result<EditorDOMPoint, nsresult> pointToInsertOrError = Result<EditorDOMPoint, nsresult> pointToInsertOrError =
WhiteSpaceVisibilityKeeper::NormalizeWhiteSpacesToSplitAt( WhiteSpaceVisibilityKeeper::NormalizeWhiteSpacesToSplitAt(
*this, aPointToInsert); *this, aPointToInsert,
{WhiteSpaceVisibilityKeeper::NormalizeOption::
StopIfPrecedingWhiteSpacesEndsWithNBP});
if (NS_WARN_IF(pointToInsertOrError.isErr())) { if (NS_WARN_IF(pointToInsertOrError.isErr())) {
return pointToInsertOrError.propagateErr(); return pointToInsertOrError.propagateErr();
} }
@@ -4413,7 +4415,9 @@ Result<EditorDOMPoint, nsresult> HTMLEditor::PrepareToInsertLineBreak(
Result<EditorDOMPoint, nsresult> pointToInsertOrError = Result<EditorDOMPoint, nsresult> pointToInsertOrError =
StaticPrefs::editor_white_space_normalization_blink_compatible() StaticPrefs::editor_white_space_normalization_blink_compatible()
? WhiteSpaceVisibilityKeeper::NormalizeWhiteSpacesToSplitAt( ? WhiteSpaceVisibilityKeeper::NormalizeWhiteSpacesToSplitAt(
*this, aPointToInsert) *this, aPointToInsert,
{WhiteSpaceVisibilityKeeper::NormalizeOption::
StopIfPrecedingWhiteSpacesEndsWithNBP})
: aPointToInsert; : aPointToInsert;
if (NS_WARN_IF(pointToInsertOrError.isErr())) { if (NS_WARN_IF(pointToInsertOrError.isErr())) {
return pointToInsertOrError.propagateErr(); return pointToInsertOrError.propagateErr();

View File

@@ -47,7 +47,7 @@ Result<EditorDOMPoint, nsresult>
WhiteSpaceVisibilityKeeper::PrepareToSplitBlockElement( WhiteSpaceVisibilityKeeper::PrepareToSplitBlockElement(
HTMLEditor& aHTMLEditor, const EditorDOMPoint& aPointToSplit, HTMLEditor& aHTMLEditor, const EditorDOMPoint& aPointToSplit,
const Element& aSplittingBlockElement) { const Element& aSplittingBlockElement) {
if (NS_WARN_IF(!aPointToSplit.IsInContentNode()) || if (NS_WARN_IF(!aPointToSplit.IsInContentNodeAndValidInComposedDoc()) ||
NS_WARN_IF(!HTMLEditUtils::IsSplittableNode(aSplittingBlockElement)) || NS_WARN_IF(!HTMLEditUtils::IsSplittableNode(aSplittingBlockElement)) ||
NS_WARN_IF(!EditorUtils::IsEditableContent( NS_WARN_IF(!EditorUtils::IsEditableContent(
*aPointToSplit.ContainerAs<nsIContent>(), EditorType::HTML))) { *aPointToSplit.ContainerAs<nsIContent>(), EditorType::HTML))) {
@@ -69,7 +69,8 @@ WhiteSpaceVisibilityKeeper::PrepareToSplitBlockElement(
pointToSplit.Set(content); 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); AutoTrackDOMPoint tracker(aHTMLEditor.RangeUpdaterRef(), &pointToSplit);
nsresult rv = WhiteSpaceVisibilityKeeper:: nsresult rv = WhiteSpaceVisibilityKeeper::
@@ -84,6 +85,21 @@ WhiteSpaceVisibilityKeeper::PrepareToSplitBlockElement(
"MakeSureToKeepVisibleWhiteSpacesVisibleAfterSplit() failed"); "MakeSureToKeepVisibleWhiteSpacesVisibleAfterSplit() failed");
return Err(rv); 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<EditorDOMPoint, nsresult> 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()) || if (NS_WARN_IF(!pointToSplit.IsInContentNode()) ||
@@ -821,7 +837,8 @@ Result<MoveNodeResult, nsresult> WhiteSpaceVisibilityKeeper::
// static // static
Result<EditorDOMPoint, nsresult> Result<EditorDOMPoint, nsresult>
WhiteSpaceVisibilityKeeper::NormalizeWhiteSpacesToSplitTextNodeAt( WhiteSpaceVisibilityKeeper::NormalizeWhiteSpacesToSplitTextNodeAt(
HTMLEditor& aHTMLEditor, const EditorDOMPointInText& aPointToSplit) { HTMLEditor& aHTMLEditor, const EditorDOMPointInText& aPointToSplit,
NormalizeOptions aOptions) {
MOZ_ASSERT(aPointToSplit.IsSetAndValid()); MOZ_ASSERT(aPointToSplit.IsSetAndValid());
MOZ_ASSERT(StaticPrefs::editor_white_space_normalization_blink_compatible()); MOZ_ASSERT(StaticPrefs::editor_white_space_normalization_blink_compatible());
@@ -855,13 +872,18 @@ WhiteSpaceVisibilityKeeper::NormalizeWhiteSpacesToSplitTextNodeAt(
const HTMLEditor::ReplaceWhiteSpacesData replacePrecedingWhiteSpacesData = const HTMLEditor::ReplaceWhiteSpacesData replacePrecedingWhiteSpacesData =
aPointToSplit.IsStartOfContainer() || aPointToSplit.IsStartOfContainer() ||
// Chrome does not normalize the left `Text` at least when it ends (aOptions.contains(
// with an NBSP. NormalizeOption::StopIfPrecedingWhiteSpacesEndsWithNBP) &&
aPointToSplit.IsPreviousCharNBSP() aPointToSplit.IsPreviousCharNBSP())
? HTMLEditor::ReplaceWhiteSpacesData() ? HTMLEditor::ReplaceWhiteSpacesData()
: aHTMLEditor.GetPrecedingNormalizedStringToSplitAt(aPointToSplit); : aHTMLEditor.GetPrecedingNormalizedStringToSplitAt(aPointToSplit);
const HTMLEditor::ReplaceWhiteSpacesData replaceFollowingWhiteSpaceData = const HTMLEditor::ReplaceWhiteSpacesData replaceFollowingWhiteSpaceData =
aHTMLEditor.GetFollowingNormalizedStringToSplitAt(aPointToSplit); aPointToSplit.IsEndOfContainer() ||
(aOptions.contains(
NormalizeOption::StopIfFollowingWhiteSpacesStartsWithNBSP) &&
aPointToSplit.IsCharNBSP())
? HTMLEditor::ReplaceWhiteSpacesData()
: aHTMLEditor.GetFollowingNormalizedStringToSplitAt(aPointToSplit);
const HTMLEditor::ReplaceWhiteSpacesData replaceWhiteSpacesData = const HTMLEditor::ReplaceWhiteSpacesData replaceWhiteSpacesData =
(replacePrecedingWhiteSpacesData + replaceFollowingWhiteSpaceData) (replacePrecedingWhiteSpacesData + replaceFollowingWhiteSpaceData)
.GetMinimizedData(*textNode); .GetMinimizedData(*textNode);
@@ -904,7 +926,8 @@ WhiteSpaceVisibilityKeeper::NormalizeWhiteSpacesToSplitTextNodeAt(
// static // static
Result<EditorDOMPoint, nsresult> Result<EditorDOMPoint, nsresult>
WhiteSpaceVisibilityKeeper::NormalizeWhiteSpacesToSplitAt( WhiteSpaceVisibilityKeeper::NormalizeWhiteSpacesToSplitAt(
HTMLEditor& aHTMLEditor, const EditorDOMPoint& aPointToSplit) { HTMLEditor& aHTMLEditor, const EditorDOMPoint& aPointToSplit,
NormalizeOptions aOptions) {
MOZ_ASSERT(aPointToSplit.IsSet()); MOZ_ASSERT(aPointToSplit.IsSet());
MOZ_ASSERT(StaticPrefs::editor_white_space_normalization_blink_compatible()); MOZ_ASSERT(StaticPrefs::editor_white_space_normalization_blink_compatible());
@@ -936,7 +959,7 @@ WhiteSpaceVisibilityKeeper::NormalizeWhiteSpacesToSplitAt(
if (pointToSplit.IsInTextNode()) { if (pointToSplit.IsInTextNode()) {
Result<EditorDOMPoint, nsresult> pointToSplitOrError = Result<EditorDOMPoint, nsresult> pointToSplitOrError =
WhiteSpaceVisibilityKeeper::NormalizeWhiteSpacesToSplitTextNodeAt( WhiteSpaceVisibilityKeeper::NormalizeWhiteSpacesToSplitTextNodeAt(
aHTMLEditor, pointToSplit.AsInText()); aHTMLEditor, pointToSplit.AsInText(), aOptions);
if (MOZ_UNLIKELY(pointToSplitOrError.isErr())) { if (MOZ_UNLIKELY(pointToSplitOrError.isErr())) {
NS_WARNING( NS_WARNING(
"WhiteSpaceVisibilityKeeper::NormalizeWhiteSpacesToSplitTextNodeAt() " "WhiteSpaceVisibilityKeeper::NormalizeWhiteSpacesToSplitTextNodeAt() "
@@ -981,11 +1004,9 @@ WhiteSpaceVisibilityKeeper::NormalizeWhiteSpacesToSplitAt(
textNode->TextDataLength()) { textNode->TextDataLength()) {
break; break;
} }
// Chrome does not normalize preceding `Text` at least when it ends with if (aOptions.contains(
// an NBSP. NormalizeOption::StopIfPrecedingWhiteSpacesEndsWithNBP) &&
if (textNode->TextDataLength() && textNode->TextFragment().SafeLastChar() == HTMLEditUtils::kNBSP) {
textNode->TextFragment().CharAt(textNode->TextLength() - 1u) ==
HTMLEditUtils::kNBSP) {
break; break;
} }
precedingTextNodes.AppendElement(*textNode); precedingTextNodes.AppendElement(*textNode);
@@ -1021,6 +1042,11 @@ WhiteSpaceVisibilityKeeper::NormalizeWhiteSpacesToSplitAt(
textNode->TextDataLength()) { textNode->TextDataLength()) {
break; break;
} }
if (aOptions.contains(
NormalizeOption::StopIfFollowingWhiteSpacesStartsWithNBSP) &&
textNode->TextFragment().SafeFirstChar() == HTMLEditUtils::kNBSP) {
break;
}
followingTextNodes.AppendElement(*textNode); followingTextNodes.AppendElement(*textNode);
if (textNode->TextIsOnlyWhitespace() && if (textNode->TextIsOnlyWhitespace() &&
EditorUtils::IsWhiteSpacePreformatted(*textNode)) { EditorUtils::IsWhiteSpacePreformatted(*textNode)) {
@@ -1045,7 +1071,7 @@ WhiteSpaceVisibilityKeeper::NormalizeWhiteSpacesToSplitAt(
for (const auto& textNode : precedingTextNodes) { for (const auto& textNode : precedingTextNodes) {
Result<EditorDOMPoint, nsresult> normalizeWhiteSpacesResultOrError = Result<EditorDOMPoint, nsresult> normalizeWhiteSpacesResultOrError =
WhiteSpaceVisibilityKeeper::NormalizeWhiteSpacesToSplitTextNodeAt( WhiteSpaceVisibilityKeeper::NormalizeWhiteSpacesToSplitTextNodeAt(
aHTMLEditor, EditorDOMPointInText::AtEndOf(textNode)); aHTMLEditor, EditorDOMPointInText::AtEndOf(textNode), aOptions);
if (MOZ_UNLIKELY(normalizeWhiteSpacesResultOrError.isErr())) { if (MOZ_UNLIKELY(normalizeWhiteSpacesResultOrError.isErr())) {
NS_WARNING( NS_WARNING(
"WhiteSpaceVisibilityKeeper::NormalizeWhiteSpacesToSplitTextNodeAt() " "WhiteSpaceVisibilityKeeper::NormalizeWhiteSpacesToSplitTextNodeAt() "
@@ -1062,7 +1088,7 @@ WhiteSpaceVisibilityKeeper::NormalizeWhiteSpacesToSplitAt(
for (const auto& textNode : followingTextNodes) { for (const auto& textNode : followingTextNodes) {
Result<EditorDOMPoint, nsresult> normalizeWhiteSpacesResultOrError = Result<EditorDOMPoint, nsresult> normalizeWhiteSpacesResultOrError =
WhiteSpaceVisibilityKeeper::NormalizeWhiteSpacesToSplitTextNodeAt( WhiteSpaceVisibilityKeeper::NormalizeWhiteSpacesToSplitTextNodeAt(
aHTMLEditor, EditorDOMPointInText(textNode, 0u)); aHTMLEditor, EditorDOMPointInText(textNode, 0u), aOptions);
if (MOZ_UNLIKELY(normalizeWhiteSpacesResultOrError.isErr())) { if (MOZ_UNLIKELY(normalizeWhiteSpacesResultOrError.isErr())) {
NS_WARNING( NS_WARNING(
"WhiteSpaceVisibilityKeeper::NormalizeWhiteSpacesToSplitTextNodeAt() " "WhiteSpaceVisibilityKeeper::NormalizeWhiteSpacesToSplitTextNodeAt() "
@@ -1295,10 +1321,13 @@ WhiteSpaceVisibilityKeeper::InsertLineBreak(
} }
} }
} else { } else {
// Chrome does not normalize preceding white-spaces at least when it ends
// with an NBSP.
Result<EditorDOMPoint, nsresult> Result<EditorDOMPoint, nsresult>
normalizeSurroundingWhiteSpacesResultOrError = normalizeSurroundingWhiteSpacesResultOrError =
WhiteSpaceVisibilityKeeper::NormalizeWhiteSpacesToSplitAt( WhiteSpaceVisibilityKeeper::NormalizeWhiteSpacesToSplitAt(
aHTMLEditor, aPointToInsert); aHTMLEditor, aPointToInsert,
{NormalizeOption::StopIfPrecedingWhiteSpacesEndsWithNBP});
if (MOZ_UNLIKELY(normalizeSurroundingWhiteSpacesResultOrError.isErr())) { if (MOZ_UNLIKELY(normalizeSurroundingWhiteSpacesResultOrError.isErr())) {
NS_WARNING( NS_WARNING(
"WhiteSpaceVisibilityKeeper::NormalizeWhiteSpacesToSplitAt() failed"); "WhiteSpaceVisibilityKeeper::NormalizeWhiteSpacesToSplitAt() failed");

View File

@@ -130,6 +130,14 @@ class WhiteSpaceVisibilityKeeper final {
const EditorDOMPoint& aPointToSplit, const EditorDOMPoint& aPointToSplit,
const Element& aSplittingBlockElement); 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<NormalizeOption>;
/** /**
* Normalize surrounding white-spaces of aPointToSplit. This may normalize * Normalize surrounding white-spaces of aPointToSplit. This may normalize
* 2 `Text` nodes if the point is surrounded by them. * 2 `Text` nodes if the point is surrounded by them.
@@ -137,7 +145,8 @@ class WhiteSpaceVisibilityKeeper final {
*/ */
[[nodiscard]] MOZ_CAN_RUN_SCRIPT static Result<EditorDOMPoint, nsresult> [[nodiscard]] MOZ_CAN_RUN_SCRIPT static Result<EditorDOMPoint, nsresult>
NormalizeWhiteSpacesToSplitAt(HTMLEditor& aHTMLEditor, NormalizeWhiteSpacesToSplitAt(HTMLEditor& aHTMLEditor,
const EditorDOMPoint& aPointToSplit); const EditorDOMPoint& aPointToSplit,
NormalizeOptions aOptions);
/** /**
* MergeFirstLineOfRightBlockElementIntoDescendantLeftBlockElement() merges * MergeFirstLineOfRightBlockElementIntoDescendantLeftBlockElement() merges
@@ -365,7 +374,8 @@ class WhiteSpaceVisibilityKeeper final {
*/ */
[[nodiscard]] MOZ_CAN_RUN_SCRIPT static Result<EditorDOMPoint, nsresult> [[nodiscard]] MOZ_CAN_RUN_SCRIPT static Result<EditorDOMPoint, nsresult>
NormalizeWhiteSpacesToSplitTextNodeAt( NormalizeWhiteSpacesToSplitTextNodeAt(
HTMLEditor& aHTMLEditor, const EditorDOMPointInText& aPointToSplit); HTMLEditor& aHTMLEditor, const EditorDOMPointInText& aPointToSplit,
NormalizeOptions aOptions);
/** /**
* Delete leading or trailing invisible white-spaces around block boundaries * Delete leading or trailing invisible white-spaces around block boundaries