Bug 1644313 - part 1: Create new path to handle backspace or (forward) delete with the new normalizer r=m_kato

`InputEvent.getTargetRange()` should have actual delete range.  It means that
the range should contain the invisible leading/trailing white-spaces which
need to be removed for keep them invisible, but should not contain the range
of normalizing white-space sequence because they are not target of edit action
indicated by `InputEvent.inputType`.

So, we can use new path which uses the new white-space normalizer for
computing the value of `InputEvent.getTargetRanges()` because difference of
white-space normalizer shouldn't affect the deleting ranges (although, some
existing path calls `DeleteNodeIfInvisibleAndEditableTextNode()` later so that
the new method, `ComputeRangeInTextNodesContainingInvisibleWhiteSpaces()`, does
not exactly same thing, but the result shouldn't become different in usual
cases).  This new path can test with some WPTs under `editing/other`.

This patch creates new backspace/delete key handler when caret is at next
to a white-space as `HTMLEditor::HandleDeleteTextAroundCollapsedSelection()`
and creates helper methods of `WSRunScanner` to treat invisible leading and
trailing white-spaces.

Note that new failures are caused by the difference whether adjacent white-space
sequence at deletion is normalized or not in edge cases.  They will be fixed
by the part.5.

Depends on D84943

Differential Revision: https://phabricator.services.mozilla.com/D84944
This commit is contained in:
Masayuki Nakano 2020-07-29 00:04:00 +00:00
parent 33b61e1ce5
commit a269bbe497
7 changed files with 424 additions and 273 deletions

View File

@ -699,6 +699,10 @@ class EditorBase : public nsIEditor,
// to restore it.
bool mRestoreContentEditableCount;
// If we explicitly normalized whitespaces around the changed range,
// set to true.
bool mDidNormalizeWhitespaces;
/**
* The following methods modifies some data of this struct and
* `EditSubActionData` struct. Currently, these are required only
@ -747,6 +751,7 @@ class EditorBase : public nsIEditor,
mDidDeleteNonCollapsedRange = false;
mDidDeleteEmptyParentBlocks = false;
mRestoreContentEditableCount = false;
mDidNormalizeWhitespaces = false;
}
/**

View File

@ -1065,7 +1065,8 @@ class EditorDOMRangeBase final {
}
template <typename OtherRangeType>
bool operator==(const OtherRangeType& aOther) const {
return mStart == aOther.mStart && mEnd == aOther.mEnd;
return (!IsPositioned() && !aOther.IsPositioned()) ||
(mStart == aOther.mStart && mEnd == aOther.mEnd);
}
template <typename OtherRangeType>
bool operator!=(const OtherRangeType& aOther) const {

View File

@ -23,6 +23,7 @@
#include "mozilla/OwningNonNull.h"
#include "mozilla/Preferences.h"
#include "mozilla/RangeUtils.h"
#include "mozilla/StaticPrefs_editor.h" // for StaticPrefs::editor_*
#include "mozilla/TextComposition.h"
#include "mozilla/UniquePtr.h"
#include "mozilla/Unused.h"
@ -523,9 +524,13 @@ nsresult HTMLEditor::OnEndHandlingTopLevelEditSubActionInternal() {
// attempt to transform any unneeded nbsp's into spaces after doing various
// operations
switch (GetTopLevelEditSubAction()) {
case EditSubAction::eDeleteSelectedContent:
if (TopLevelEditSubActionDataRef().mDidNormalizeWhitespaces) {
break;
}
[[fallthrough]];
case EditSubAction::eInsertText:
case EditSubAction::eInsertTextComingFromIME:
case EditSubAction::eDeleteSelectedContent:
case EditSubAction::eInsertLineBreak:
case EditSubAction::eInsertParagraphSeparator:
case EditSubAction::ePasteHTMLContent:
@ -2617,11 +2622,94 @@ EditActionResult HTMLEditor::HandleDeleteAroundCollapsedSelection(
return EditActionIgnored();
}
EditActionResult HTMLEditor::HandleDeleteTextAroundCollapsedSelection(
nsIEditor::EDirection aDirectionAndAmount,
const EditorDOMPoint& aCaretPosition) {
MOZ_ASSERT(IsEditActionDataAvailable());
MOZ_ASSERT(aDirectionAndAmount == nsIEditor::eNext ||
aDirectionAndAmount == nsIEditor::ePrevious);
EditorDOMRangeInTexts rangeToDelete;
EditorDOMPointInText newCaretPosition;
if (aDirectionAndAmount == nsIEditor::eNext) {
Result<EditorDOMRangeInTexts, nsresult> result =
WSRunScanner::GetRangeInTextNodesToForwardDeleteFrom(*this,
aCaretPosition);
if (result.isErr()) {
NS_WARNING(
"WSRunScanner::GetRangeInTextNodesToForwardDeleteFrom() failed");
return EditActionHandled(result.unwrapErr());
}
rangeToDelete = result.unwrap();
if (!rangeToDelete.IsPositioned()) {
return EditActionHandled(); // no range to delete
}
newCaretPosition = rangeToDelete.StartRef();
} else {
Result<EditorDOMRangeInTexts, nsresult> result =
WSRunScanner::GetRangeInTextNodesToBackspaceFrom(*this, aCaretPosition);
if (result.isErr()) {
NS_WARNING("WSRunScanner::GetRangeInTextNodesToBackspaceFrom() failed");
return EditActionHandled(result.unwrapErr());
}
rangeToDelete = result.unwrap();
if (!rangeToDelete.IsPositioned()) {
return EditActionHandled(); // no range to delete
}
if (rangeToDelete.InSameContainer()) {
newCaretPosition = rangeToDelete.StartRef();
} else {
newCaretPosition =
EditorDOMPointInText(rangeToDelete.EndRef().ContainerAsText(), 0);
}
}
AutoTransactionsConserveSelection dontChangeMySelection(*this);
nsresult rv = DeleteTextAndNormalizeSurroundingWhiteSpaces(
rangeToDelete.StartRef(), rangeToDelete.EndRef());
TopLevelEditSubActionDataRef().mDidNormalizeWhitespaces = true;
if (NS_FAILED(rv)) {
NS_WARNING(
"HTMLEditor::DeleteTextAndNormalizeSurroundingWhiteSpaces() failed");
return EditActionHandled(rv);
}
if (!newCaretPosition.IsSetAndValid() ||
!newCaretPosition.ContainerAsText()->IsInComposedDoc()) {
return EditActionHandled(NS_ERROR_EDITOR_UNEXPECTED_DOM_TREE);
}
// For compatibility with Blink, we should move caret to end of previous
// text node if it's direct previous sibling of the first text node in the
// range.
if (newCaretPosition.IsStartOfContainer() &&
newCaretPosition.GetContainer()->GetPreviousSibling() &&
newCaretPosition.GetContainer()->GetPreviousSibling()->IsText()) {
newCaretPosition.SetToEndOf(
newCaretPosition.GetContainer()->GetPreviousSibling()->AsText());
}
DebugOnly<nsresult> rvIgnored =
SelectionRefPtr()->Collapse(newCaretPosition.ToRawRangeBoundary());
NS_WARNING_ASSERTION(NS_SUCCEEDED(rvIgnored),
"Selection::Collapse() failed, but ignored");
return EditActionHandled();
}
EditActionResult HTMLEditor::HandleDeleteCollapsedSelectionAtWhiteSpaces(
nsIEditor::EDirection aDirectionAndAmount,
const EditorDOMPoint& aPointToDelete) {
MOZ_ASSERT(IsEditActionDataAvailable());
if (StaticPrefs::editor_white_space_normalization_blink_compatible()) {
EditActionResult result = HandleDeleteTextAroundCollapsedSelection(
aDirectionAndAmount, aPointToDelete);
NS_WARNING_ASSERTION(
result.Succeeded(),
"HTMLEditor::HandleDeleteTextAroundCollapsedSelection() failed");
return result;
}
if (aDirectionAndAmount == nsIEditor::eNext) {
nsresult rv = WhiteSpaceVisibilityKeeper::DeleteInclusiveNextWhiteSpace(
*this, aPointToDelete);
@ -3913,9 +4001,9 @@ nsresult HTMLEditor::DeleteTextAndNormalizeSurroundingWhiteSpaces(
startToDelete, endToDelete, normalizedWhiteSpacesInFirstNode,
normalizedWhiteSpacesInLastNode);
// If given range is collapsed, i.e., the caller just wants to normalize
// white-space sequence, but there is no white-spaces which need to be
// replaced, we need to do nothing here.
// If extended range is still collapsed, i.e., the caller just wants to
// normalize white-space sequence, but there is no white-spaces which need to
// be replaced, we need to do nothing here.
if (startToDelete == endToDelete) {
return NS_OK;
}

View File

@ -2739,6 +2739,19 @@ class HTMLEditor final : public TextEditor,
nsIEditor::EDirection aDirectionAndAmount,
nsIEditor::EStripWrappers aStripWrappers);
/**
* HandleDeleteTextAroundCollapsedSelection() handles deletion of
* collapsed selection in a text node.
*
* @param aDirectionAndAmount Must be eNext or ePrevious.
* @param aCaretPoisition The position where caret is. This container
* must be a text node.
*/
[[nodiscard]] MOZ_CAN_RUN_SCRIPT EditActionResult
HandleDeleteTextAroundCollapsedSelection(
nsIEditor::EDirection aDirectionAndAmount,
const EditorDOMPoint& aCaretPosition);
/**
* HandleDeleteNonCollapsedSelection() handles deletion with non-collapsed
* `Selection`. Callers must guarantee that this is called only when

View File

@ -1416,7 +1416,7 @@ nsresult WhiteSpaceVisibilityKeeper::
ReplaceRangeData
WSRunScanner::TextFragmentData::GetReplaceRangeDataAtEndOfDeletionRange(
const TextFragmentData& aTextFragmentDataAtStartToDelete) {
const TextFragmentData& aTextFragmentDataAtStartToDelete) const {
const EditorDOMPoint& startToDelete =
aTextFragmentDataAtStartToDelete.ScanStartRef();
const EditorDOMPoint& endToDelete = mScanStartPoint;
@ -1495,7 +1495,7 @@ WSRunScanner::TextFragmentData::GetReplaceRangeDataAtEndOfDeletionRange(
ReplaceRangeData
WSRunScanner::TextFragmentData::GetReplaceRangeDataAtStartOfDeletionRange(
const TextFragmentData& aTextFragmentDataAtEndToDelete) {
const TextFragmentData& aTextFragmentDataAtEndToDelete) const {
const EditorDOMPoint& startToDelete = mScanStartPoint;
const EditorDOMPoint& endToDelete =
aTextFragmentDataAtEndToDelete.ScanStartRef();
@ -2437,4 +2437,276 @@ nsresult WhiteSpaceVisibilityKeeper::DeleteInvisibleASCIIWhiteSpaces(
return NS_OK;
}
/*****************************************************************************
* Implementation for new white-space normalizer
*****************************************************************************/
// static
EditorDOMRangeInTexts
WSRunScanner::ComputeRangeInTextNodesContainingInvisibleWhiteSpaces(
const TextFragmentData& aStart, const TextFragmentData& aEnd) {
// Corresponding to handling invisible white-spaces part of
// `TextFragmentData::GetReplaceRangeDataAtEndOfDeletionRange()` and
// `TextFragmentData::GetReplaceRangeDataAtStartOfDeletionRange()`
MOZ_ASSERT(aStart.ScanStartRef().IsSetAndValid());
MOZ_ASSERT(aEnd.ScanStartRef().IsSetAndValid());
MOZ_ASSERT(aStart.ScanStartRef().EqualsOrIsBefore(aEnd.ScanStartRef()));
MOZ_ASSERT(aStart.ScanStartRef().IsInTextNode());
MOZ_ASSERT(aEnd.ScanStartRef().IsInTextNode());
// XXX `GetReplaceRangeDataAtEndOfDeletionRange()` and
// `GetReplaceRangeDataAtStartOfDeletionRange()` use
// `GetNewInvisibleLeadingWhiteSpaceRangeIfSplittingAt()` and
// `GetNewInvisibleTrailingWhiteSpaceRangeIfSplittingAt()`.
// However, they are really odd as mentioned with "XXX" comments
// in them. For the new white-space normalizer, we need to treat
// invisible white-spaces stricter because the legacy path handles
// white-spaces multiple times (e.g., calling `HTMLEditor::
// DeleteNodeIfInvisibleAndEditableTextNode()` later) and that hides
// the bug, but in the new path, we should stop doing same things
// multiple times for both performance and footprint. Therefore,
// even though the result might be different in some edge cases,
// we should use clean path for now. Perhaps, we should fix the odd
// cases before shipping `beforeinput` event in release channel.
const EditorDOMRange& invisibleLeadingWhiteSpaceRange =
aStart.InvisibleLeadingWhiteSpaceRangeRef();
const EditorDOMRange& invisibleTrailingWhiteSpaceRange =
aEnd.InvisibleTrailingWhiteSpaceRangeRef();
const bool hasInvisibleLeadingWhiteSpaces =
invisibleLeadingWhiteSpaceRange.IsPositioned() &&
!invisibleLeadingWhiteSpaceRange.Collapsed();
const bool hasInvisibleTrailingWhiteSpaces =
invisibleLeadingWhiteSpaceRange != invisibleTrailingWhiteSpaceRange &&
invisibleTrailingWhiteSpaceRange.IsPositioned() &&
!invisibleTrailingWhiteSpaceRange.Collapsed();
EditorDOMRangeInTexts result(aStart.ScanStartRef().AsInText(),
aEnd.ScanStartRef().AsInText());
MOZ_ASSERT(result.IsPositionedAndValid());
if (!hasInvisibleLeadingWhiteSpaces && !hasInvisibleTrailingWhiteSpaces) {
return result;
}
MOZ_ASSERT_IF(
hasInvisibleLeadingWhiteSpaces && hasInvisibleTrailingWhiteSpaces,
invisibleLeadingWhiteSpaceRange.StartRef().IsBefore(
invisibleTrailingWhiteSpaceRange.StartRef()));
const EditorDOMPoint& aroundFirstInvisibleWhiteSpace =
hasInvisibleLeadingWhiteSpaces
? invisibleLeadingWhiteSpaceRange.StartRef()
: invisibleTrailingWhiteSpaceRange.StartRef();
if (aroundFirstInvisibleWhiteSpace.IsBefore(result.StartRef())) {
if (aroundFirstInvisibleWhiteSpace.IsInTextNode()) {
result.SetStart(aroundFirstInvisibleWhiteSpace.AsInText());
MOZ_ASSERT(result.IsPositionedAndValid());
} else {
const EditorDOMPointInText atFirstInvisibleWhiteSpace =
hasInvisibleLeadingWhiteSpaces
? aStart.GetInclusiveNextEditableCharPoint(
aroundFirstInvisibleWhiteSpace)
: aEnd.GetInclusiveNextEditableCharPoint(
aroundFirstInvisibleWhiteSpace);
MOZ_ASSERT(atFirstInvisibleWhiteSpace.IsSet());
MOZ_ASSERT(
atFirstInvisibleWhiteSpace.EqualsOrIsBefore(result.StartRef()));
result.SetStart(atFirstInvisibleWhiteSpace);
MOZ_ASSERT(result.IsPositionedAndValid());
}
}
MOZ_ASSERT_IF(
hasInvisibleLeadingWhiteSpaces && hasInvisibleTrailingWhiteSpaces,
invisibleLeadingWhiteSpaceRange.EndRef().IsBefore(
invisibleTrailingWhiteSpaceRange.EndRef()));
const EditorDOMPoint& afterLastInvisibleWhiteSpace =
hasInvisibleTrailingWhiteSpaces
? invisibleTrailingWhiteSpaceRange.EndRef()
: invisibleLeadingWhiteSpaceRange.EndRef();
if (afterLastInvisibleWhiteSpace.EqualsOrIsBefore(result.EndRef())) {
MOZ_ASSERT(result.IsPositionedAndValid());
return result;
}
if (afterLastInvisibleWhiteSpace.IsInTextNode()) {
result.SetEnd(afterLastInvisibleWhiteSpace.AsInText());
MOZ_ASSERT(result.IsPositionedAndValid());
return result;
}
const EditorDOMPointInText atLastInvisibleWhiteSpace =
hasInvisibleTrailingWhiteSpaces
? aEnd.GetPreviousEditableCharPoint(afterLastInvisibleWhiteSpace)
: aStart.GetPreviousEditableCharPoint(afterLastInvisibleWhiteSpace);
MOZ_ASSERT(atLastInvisibleWhiteSpace.IsSet());
MOZ_ASSERT(atLastInvisibleWhiteSpace.IsContainerEmpty() ||
atLastInvisibleWhiteSpace.IsAtLastContent());
MOZ_ASSERT(result.EndRef().EqualsOrIsBefore(atLastInvisibleWhiteSpace));
result.SetEnd(atLastInvisibleWhiteSpace.IsEndOfContainer()
? atLastInvisibleWhiteSpace
: atLastInvisibleWhiteSpace.NextPoint());
MOZ_ASSERT(result.IsPositionedAndValid());
return result;
}
// static
Result<EditorDOMRangeInTexts, nsresult>
WSRunScanner::GetRangeInTextNodesToBackspaceFrom(const HTMLEditor& aHTMLEditor,
const EditorDOMPoint& aPoint) {
// Corresponding to computing delete range part of
// `WhiteSpaceVisibilityKeeper::DeletePreviousWhiteSpace()`
MOZ_ASSERT(aPoint.IsSetAndValid());
Element* editingHost = aHTMLEditor.GetActiveEditingHost();
TextFragmentData textFragmentDataAtCaret(aPoint, editingHost);
EditorDOMPointInText atPreviousChar =
textFragmentDataAtCaret.GetPreviousEditableCharPoint(aPoint);
if (!atPreviousChar.IsSet()) {
return EditorDOMRangeInTexts(); // There is no content in the block.
}
// For now, we handle white-space deletion.
if (!atPreviousChar.IsCharASCIISpaceOrNBSP()) {
return EditorDOMRangeInTexts();
}
// XXX When previous char point is in an empty text node, we do nothing,
// but this must look odd from point of user view. We should delete
// something before aPoint.
if (atPreviousChar.IsEndOfContainer()) {
return EditorDOMRangeInTexts();
}
// If the text node is preformatted, just remove the previous character.
if (textFragmentDataAtCaret.IsPreformatted()) {
return EditorDOMRangeInTexts(atPreviousChar, atPreviousChar.NextPoint());
}
// If previous char is an ASCII white-spaces, delete all adjcent ASCII
// whitespaces.
EditorDOMRangeInTexts rangeToDelete;
if (atPreviousChar.IsCharASCIISpace()) {
EditorDOMPointInText startToDelete =
textFragmentDataAtCaret.GetFirstASCIIWhiteSpacePointCollapsedTo(
atPreviousChar);
if (!startToDelete.IsSet()) {
NS_WARNING(
"WSRunScanner::GetFirstASCIIWhiteSpacePointCollapsedTo() failed");
return Err(NS_ERROR_FAILURE);
}
EditorDOMPointInText endToDelete =
textFragmentDataAtCaret.GetEndOfCollapsibleASCIIWhiteSpaces(
atPreviousChar);
if (!endToDelete.IsSet()) {
NS_WARNING("WSRunScanner::GetEndOfCollapsibleASCIIWhiteSpaces() failed");
return Err(NS_ERROR_FAILURE);
}
rangeToDelete = EditorDOMRangeInTexts(startToDelete, endToDelete);
}
// if previous char is an NBSP, remove it.
else {
MOZ_ASSERT(atPreviousChar.IsCharNBSP());
rangeToDelete =
EditorDOMRangeInTexts(atPreviousChar, atPreviousChar.NextPoint());
}
// If there is no removable and visible content, we should do nothing.
if (rangeToDelete.Collapsed()) {
return EditorDOMRangeInTexts();
}
// And also delete invisible white-spaces if they become visible.
TextFragmentData textFragmentDataAtStart =
rangeToDelete.StartRef() != aPoint
? TextFragmentData(rangeToDelete.StartRef(), editingHost)
: textFragmentDataAtCaret;
TextFragmentData textFragmentDataAtEnd =
rangeToDelete.EndRef() != aPoint
? TextFragmentData(rangeToDelete.EndRef(), editingHost)
: textFragmentDataAtCaret;
EditorDOMRangeInTexts extendedRangeToDelete =
WSRunScanner::ComputeRangeInTextNodesContainingInvisibleWhiteSpaces(
textFragmentDataAtStart, textFragmentDataAtEnd);
MOZ_ASSERT(extendedRangeToDelete.IsPositionedAndValid());
return extendedRangeToDelete.IsPositioned() ? extendedRangeToDelete
: rangeToDelete;
}
// static
Result<EditorDOMRangeInTexts, nsresult>
WSRunScanner::GetRangeInTextNodesToForwardDeleteFrom(
const HTMLEditor& aHTMLEditor, const EditorDOMPoint& aPoint) {
// Corresponding to computing delete range part of
// `WhiteSpaceVisibilityKeeper::DeleteInclusiveNextWhiteSpace()`
MOZ_ASSERT(aPoint.IsSetAndValid());
Element* editingHost = aHTMLEditor.GetActiveEditingHost();
TextFragmentData textFragmentDataAtCaret(aPoint, editingHost);
EditorDOMPointInText atCaret =
textFragmentDataAtCaret.GetInclusiveNextEditableCharPoint(aPoint);
if (!atCaret.IsSet()) {
return EditorDOMRangeInTexts(); // There is no content in the block.
}
// For now, we handle whitespace deletion.
if (!atCaret.IsCharASCIISpaceOrNBSP()) {
return EditorDOMRangeInTexts();
}
// XXX When next char point is in an empty text node, we do nothing,
// but this must look odd from point of user view. We should delete
// something after aPoint.
if (atCaret.IsEndOfContainer()) {
return EditorDOMRangeInTexts();
}
// If the text node is preformatted, just remove the previous character.
if (textFragmentDataAtCaret.IsPreformatted()) {
return EditorDOMRangeInTexts(atCaret, atCaret.NextPoint());
}
// If next char is an ASCII whitespaces, delete all adjcent ASCII
// whitespaces.
EditorDOMRangeInTexts rangeToDelete;
if (atCaret.IsCharASCIISpace()) {
EditorDOMPointInText startToDelete =
textFragmentDataAtCaret.GetFirstASCIIWhiteSpacePointCollapsedTo(
atCaret);
if (!startToDelete.IsSet()) {
NS_WARNING(
"WSRunScanner::GetFirstASCIIWhiteSpacePointCollapsedTo() failed");
return Err(NS_ERROR_FAILURE);
}
EditorDOMPointInText endToDelete =
textFragmentDataAtCaret.GetEndOfCollapsibleASCIIWhiteSpaces(atCaret);
if (!endToDelete.IsSet()) {
NS_WARNING("WSRunScanner::GetEndOfCollapsibleASCIIWhiteSpaces() failed");
return Err(NS_ERROR_FAILURE);
}
rangeToDelete = EditorDOMRangeInTexts(startToDelete, endToDelete);
}
// if next char is an NBSP, remove it.
else {
MOZ_ASSERT(atCaret.IsCharNBSP());
rangeToDelete = EditorDOMRangeInTexts(atCaret, atCaret.NextPoint());
}
// If there is no removable and visible content, we should do nothing.
if (rangeToDelete.Collapsed()) {
return EditorDOMRangeInTexts();
}
// And also delete invisible white-spaces if they become visible.
TextFragmentData textFragmentDataAtStart =
rangeToDelete.StartRef() != aPoint
? TextFragmentData(rangeToDelete.StartRef(), editingHost)
: textFragmentDataAtCaret;
TextFragmentData textFragmentDataAtEnd =
rangeToDelete.EndRef() != aPoint
? TextFragmentData(rangeToDelete.EndRef(), editingHost)
: textFragmentDataAtCaret;
EditorDOMRangeInTexts extendedRangeToDelete =
WSRunScanner::ComputeRangeInTextNodesContainingInvisibleWhiteSpaces(
textFragmentDataAtStart, textFragmentDataAtEnd);
MOZ_ASSERT(extendedRangeToDelete.IsPositionedAndValid());
return extendedRangeToDelete.IsPositioned() ? extendedRangeToDelete
: rangeToDelete;
}
} // namespace mozilla

View File

@ -329,6 +329,22 @@ class MOZ_STACK_CLASS WSRunScanner final {
return scanner.GetPreviousEditableCharPoint(aPoint);
}
/**
* GetRangeInTextNodesToForwardDeleteFrom() returns the range to remove
* text when caret is at aPoint.
*/
static Result<EditorDOMRangeInTexts, nsresult>
GetRangeInTextNodesToForwardDeleteFrom(const HTMLEditor& aHTMLEditor,
const EditorDOMPoint& aPoint);
/**
* GetRangeInTextNodesToBackspaceFrom() returns the range to remove text
* when caret is at aPoint.
*/
static Result<EditorDOMRangeInTexts, nsresult>
GetRangeInTextNodesToBackspaceFrom(const HTMLEditor& aHTMLEditor,
const EditorDOMPoint& aPoint);
/**
* GetStartReasonContent() and GetEndReasonContent() return a node which
* was found by scanning from mScanStartPoint backward or forward. If there
@ -1035,9 +1051,9 @@ class MOZ_STACK_CLASS WSRunScanner final {
* with an NBSP.
*/
ReplaceRangeData GetReplaceRangeDataAtEndOfDeletionRange(
const TextFragmentData& aTextFragmentDataAtStartToDelete);
const TextFragmentData& aTextFragmentDataAtStartToDelete) const;
ReplaceRangeData GetReplaceRangeDataAtStartOfDeletionRange(
const TextFragmentData& aTextFragmentDataAtEndToDelete);
const TextFragmentData& aTextFragmentDataAtEndToDelete) const;
/**
* VisibleWhiteSpacesDataRef() returns reference to visible white-spaces
@ -1092,6 +1108,20 @@ class MOZ_STACK_CLASS WSRunScanner final {
const HTMLEditor* mHTMLEditor;
private:
/**
* ComputeRangeInTextNodesContainingInvisibleWhiteSpaces() returns range
* containing invisible white-spaces if deleting between aStart and aEnd
* causes them become visible.
*
* @param aStart TextFragmentData at start of deleting range.
* This must be initialized with DOM point in a text node.
* @param aEnd TextFragmentData at end of deleting range.
* This must be initialized with DOM point in a text node.
*/
static EditorDOMRangeInTexts
ComputeRangeInTextNodesContainingInvisibleWhiteSpaces(
const TextFragmentData& aStart, const TextFragmentData& aEnd);
TextFragmentData mTextFragmentDataAtStart;
friend class WhiteSpaceVisibilityKeeper;

View File

@ -1,366 +1,108 @@
[white-spaces-after-execCommand-delete.tentative.html]
prefs: [editor.white_space_normalization.blink_compatible:true]
max-asserts: 3
max-asserts: 4
[execCommand("delete", false, ""): "a&nbsp;&nbsp;&nbsp;&nbsp;[\]<span style=white-space:pre;> </span>"]
expected: FAIL
[execCommand("delete", false, ""): "a&nbsp;&nbsp; &nbsp;[\]b" (length of whiteSpace sequence: 4)]
expected: FAIL
[execCommand("delete", false, ""): "<span><span>abc &nbsp;</span>[\]&nbsp;def</span>"]
expected: FAIL
[execCommand("delete", false, ""): "a&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;[\] b" (length of whiteSpace sequence: 10)]
expected: FAIL
[execCommand("delete", false, ""): "&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; [\]&nbsp; b" (length of whiteSpace sequence: 11)]
expected: FAIL
[execCommand("delete", false, ""): "&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;[\] b" (length of whiteSpace sequence: 11)]
expected: FAIL
[execCommand("delete", false, ""): "<span>abc &nbsp;[\]</span><span>&nbsp;def</span>"]
expected: FAIL
[execCommand("delete", false, ""): "a&nbsp; &nbsp; &nbsp;[\] &nbsp; &nbsp; b" (length of whiteSpace sequence: 10)]
expected: FAIL
[execCommand("delete", false, ""): "a&nbsp;&nbsp;&nbsp;&nbsp;[\] b" (length of whiteSpace sequence: 5)]
expected: FAIL
[execCommand("delete", false, ""): "<span><span>abc&nbsp; </span>[\]&nbsp;def</span>"]
expected: FAIL
[execCommand("delete", false, ""): "a&nbsp; [\]&nbsp; &nbsp; &nbsp; &nbsp; b" (length of whiteSpace sequence: 10)]
expected: FAIL
[execCommand("delete", false, ""): "<span>abc&nbsp;</span><span>&nbsp;[\]def</span>"]
expected: FAIL
[execCommand("delete", false, ""): "a&nbsp; &nbsp;[\] &nbsp; &nbsp; &nbsp; b" (length of whiteSpace sequence: 10)]
expected: FAIL
[execCommand("delete", false, ""): "&nbsp;[\] &nbsp; b" (length of whiteSpace sequence: 4)]
expected: FAIL
[execCommand("delete", false, ""): "<span>abc&nbsp;<span>[\]&nbsp;def</span></span>"]
expected: FAIL
[execCommand("delete", false, ""): "&nbsp;&nbsp; &nbsp; [\]&nbsp; &nbsp; &nbsp; b" (length of whiteSpace sequence: 11)]
expected: FAIL
[execCommand("delete", false, ""): "<span>abc&nbsp; [\]</span><span>&nbsp;def</span>"]
expected: FAIL
[execCommand("delete", false, ""): "a &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;[\] b" (length of whiteSpace sequence: 11)]
expected: FAIL
[execCommand("delete", false, ""): "a&nbsp;&nbsp;&nbsp;&nbsp;[\]||&nbsp;&nbsp;&nbsp;&nbsp;c"]
expected: FAIL
[execCommand("delete", false, ""): "&nbsp;&nbsp; &nbsp;[\]b" (length of whiteSpace sequence: 4)]
expected: FAIL
[execCommand("delete", false, ""): "a &nbsp; &nbsp; &nbsp;[\] &nbsp; &nbsp; b" (length of whiteSpace sequence: 11)]
expected: FAIL
[execCommand("delete", false, ""): "a&nbsp;&nbsp;b[\]&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;&nbsp;c"]
expected: FAIL
[execCommand("delete", false, ""): "a&nbsp;&nbsp;&nbsp;[\]&nbsp;| |&nbsp;&nbsp;&nbsp;c"]
expected: FAIL
[execCommand("delete", false, ""): "<span><span>abc &nbsp;</span>&nbsp;[\]def</span>"]
expected: FAIL
[execCommand("delete", false, ""): "a&nbsp;b[\]&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;&nbsp;c"]
expected: FAIL
[execCommand("delete", false, ""): "a&nbsp;[\] &nbsp; &nbsp; &nbsp; &nbsp; b" (length of whiteSpace sequence: 10)]
expected: FAIL
[execCommand("delete", false, ""): "&nbsp; &nbsp; &nbsp; [\]&nbsp; &nbsp; b" (length of whiteSpace sequence: 10)]
expected: FAIL
[execCommand("delete", false, ""): "<span>abc&nbsp; </span><span>[\]&nbsp;def</span>"]
expected: FAIL
[execCommand("delete", false, ""): "a&nbsp; &nbsp; &nbsp; [\]&nbsp; &nbsp; b" (length of whiteSpace sequence: 10)]
expected: FAIL
[execCommand("delete", false, ""): "<span>abc [\]<span>&nbsp;def</span></span>"]
expected: FAIL
[execCommand("delete", false, ""): "<span>abc &nbsp;<span>[\]&nbsp;def</span></span>"]
expected: FAIL
[execCommand("delete", false, ""): "a&nbsp; &nbsp; &nbsp; &nbsp;[\] &nbsp; b" (length of whiteSpace sequence: 10)]
expected: FAIL
[execCommand("delete", false, ""): "a&nbsp;&nbsp;&nbsp;|b[\]&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;c"]
expected: FAIL
[execCommand("delete", false, ""): "a&nbsp;&nbsp;&nbsp;&nbsp;|[\]|&nbsp;&nbsp;&nbsp;&nbsp;c"]
expected: FAIL
[execCommand("delete", false, ""): "a&nbsp;&nbsp;&nbsp;[\]&nbsp;&nbsp;|&nbsp;|&nbsp;&nbsp;&nbsp;c"]
expected: FAIL
[execCommand("delete", false, ""): "a&nbsp;&nbsp;&nbsp;|&nbsp;|[\]&nbsp;&nbsp;&nbsp;&nbsp;c"]
expected: FAIL
[execCommand("delete", false, ""): "<span>abc &nbsp;[\]<span>&nbsp;def</span></span>"]
expected: FAIL
[execCommand("delete", false, ""): "&nbsp;&nbsp; &nbsp;[\] &nbsp; &nbsp; &nbsp; b" (length of whiteSpace sequence: 11)]
expected: FAIL
[execCommand("delete", false, ""): "a&nbsp; &nbsp; [\]&nbsp; &nbsp; &nbsp; b" (length of whiteSpace sequence: 10)]
expected: FAIL
[execCommand("delete", false, ""): "&nbsp;&nbsp; [\]&nbsp; &nbsp; &nbsp; &nbsp; b" (length of whiteSpace sequence: 11)]
expected: FAIL
[execCommand("delete", false, ""): "<span>abc&nbsp;[\]<span>&nbsp;def</span></span>"]
expected: FAIL
[execCommand("delete", false, ""): "&nbsp; &nbsp;[\] &nbsp; &nbsp; &nbsp; b" (length of whiteSpace sequence: 10)]
expected: FAIL
[execCommand("delete", false, ""): "a&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;[\]&nbsp;&nbsp;&nbsp;b"]
expected: FAIL
[execCommand("delete", false, ""): "&nbsp;&nbsp;&nbsp;[\] b" (length of whiteSpace sequence: 4)]
expected: FAIL
[execCommand("delete", false, ""): "<span><span>abc&nbsp; [\]</span>&nbsp;def</span>"]
expected: FAIL
[execCommand("delete", false, ""): "a &nbsp; &nbsp;[\] &nbsp; &nbsp; &nbsp; b" (length of whiteSpace sequence: 11)]
expected: FAIL
[execCommand("delete", false, ""): "<span>abc <span>[\]&nbsp;def</span></span>"]
expected: FAIL
[execCommand("delete", false, ""): "a&nbsp; &nbsp;| [\]&nbsp; b"]
expected: FAIL
[execCommand("delete", false, ""): "<span>abc&nbsp; <span>[\]&nbsp;def</span></span>"]
expected: FAIL
[execCommand("delete", false, ""): "&nbsp;&nbsp;&nbsp; &nbsp;[\]b" (length of whiteSpace sequence: 5)]
expected: FAIL
[execCommand("delete", false, ""): "a&nbsp;[\] &nbsp; b" (length of whiteSpace sequence: 4)]
expected: FAIL
[execCommand("delete", false, ""): "a&nbsp; &nbsp; &nbsp; &nbsp; [\]&nbsp; b" (length of whiteSpace sequence: 10)]
expected: FAIL
[execCommand("delete", false, ""): "a&nbsp;&nbsp;&nbsp;&nbsp;||&nbsp;[\]&nbsp;&nbsp;&nbsp;c"]
expected: FAIL
[execCommand("delete", false, ""): "<span>abc &nbsp;</span><span>[\]&nbsp;def</span>"]
expected: FAIL
[execCommand("delete", false, ""): "a&nbsp;&nbsp;&nbsp;&nbsp;||[\]&nbsp;&nbsp;&nbsp;&nbsp;c"]
expected: FAIL
[execCommand("delete", false, ""): "<span>abc [\]</span><span>&nbsp;def</span>"]
expected: FAIL
[execCommand("delete", false, ""): "a &nbsp; &nbsp; &nbsp; [\]&nbsp; &nbsp; b" (length of whiteSpace sequence: 11)]
expected: FAIL
[execCommand("delete", false, ""): "<span>abc&nbsp;[\]</span><span> &nbsp; def</span>"]
expected: FAIL
[execCommand("delete", false, ""): "&nbsp; &nbsp;[\] b" (length of whiteSpace sequence: 4)]
expected: FAIL
[execCommand("delete", false, ""): "a &nbsp; | [\]&nbsp; b"]
expected: FAIL
[execCommand("delete", false, ""): "a&nbsp; [\]&nbsp; b" (length of whiteSpace sequence: 4)]
expected: FAIL
[execCommand("delete", false, ""): "a&nbsp;&nbsp;&nbsp;| [\]|&nbsp;&nbsp;&nbsp;&nbsp;c"]
expected: FAIL
[execCommand("delete", false, ""): "<span>abc&nbsp; [\]<span>&nbsp;def</span></span>"]
expected: FAIL
[execCommand("delete", false, ""): "a&nbsp; &nbsp;[\] b" (length of whiteSpace sequence: 4)]
expected: FAIL
[execCommand("delete", false, ""): "&nbsp; &nbsp; &nbsp;[\] &nbsp; &nbsp; b" (length of whiteSpace sequence: 10)]
expected: FAIL
[execCommand("delete", false, ""): "a &nbsp; [\]&nbsp; &nbsp; &nbsp; &nbsp; b" (length of whiteSpace sequence: 11)]
expected: FAIL
[execCommand("delete", false, ""): "a &nbsp; &nbsp; &nbsp; &nbsp; [\]&nbsp; b" (length of whiteSpace sequence: 11)]
expected: FAIL
[execCommand("delete", false, ""): "a &nbsp; &nbsp;[\] b" (length of whiteSpace sequence: 5)]
expected: FAIL
[execCommand("delete", false, ""): "<span><span>abc </span>[\]&nbsp;def</span>"]
expected: FAIL
[execCommand("delete", false, ""): "<span><span>abc &nbsp;</span></span><span>[\]&nbsp;def</span>"]
expected: FAIL
[execCommand("delete", false, ""): "a&nbsp; &nbsp;[\]|&nbsp; &nbsp;b"]
expected: FAIL
[execCommand("delete", false, ""): "a&nbsp; &nbsp;[\]| &nbsp; b"]
expected: FAIL
[execCommand("delete", false, ""): "<span><span>abc&nbsp;[\]</span>&nbsp;def</span>"]
expected: FAIL
[execCommand("delete", false, ""): "&nbsp; &nbsp; &nbsp; &nbsp;[\] &nbsp; b" (length of whiteSpace sequence: 10)]
expected: FAIL
[execCommand("delete", false, ""): "&nbsp;&nbsp; &nbsp; &nbsp; [\]&nbsp; &nbsp; b" (length of whiteSpace sequence: 11)]
expected: FAIL
[execCommand("delete", false, ""): "a&nbsp;&nbsp;&nbsp;&nbsp;<span style=white-space:pre;>[\] </span>"]
expected: FAIL
[execCommand("delete", false, ""): "<span><span>abc &nbsp;[\]</span>&nbsp;def</span>"]
expected: FAIL
[execCommand("delete", false, ""): "&nbsp; [\]&nbsp; b" (length of whiteSpace sequence: 4)]
expected: FAIL
[execCommand("delete", false, ""): "a&nbsp;&nbsp;&nbsp;[\]&nbsp;||&nbsp;&nbsp;&nbsp;&nbsp;c"]
expected: FAIL
[execCommand("delete", false, ""): "a&nbsp;&nbsp;&nbsp;&nbsp;[\]| |&nbsp;&nbsp;&nbsp;c"]
expected: FAIL
[execCommand("delete", false, ""): "<span>abc&nbsp;</span><span>&nbsp; [\]def</span>"]
expected: FAIL
[execCommand("delete", false, ""): "<span>abc&nbsp;</span><span>[\]&nbsp;def</span>"]
expected: FAIL
[execCommand("delete", false, ""): "a&nbsp;[\]&nbsp;&nbsp;&nbsp; b" (length of whiteSpace sequence: 5)]
expected: FAIL
[execCommand("delete", false, ""): "<span>abc &nbsp;[\]</span><span> &nbsp; def</span>"]
expected: FAIL
[execCommand("delete", false, ""): "a&nbsp;&nbsp;[\]&nbsp;&nbsp; b" (length of whiteSpace sequence: 5)]
expected: FAIL
[execCommand("delete", false, ""): "<span><span>abc [\]</span>&nbsp;def</span>"]
expected: FAIL
[execCommand("delete", false, ""): "a<span style=white-space:pre;> </span>[\]b"]
expected: FAIL
[execCommand("delete", false, ""): "<span>abc &nbsp;<span>&nbsp;[\]def</span></span>"]
expected: FAIL
[execCommand("delete", false, ""): "a&nbsp;&nbsp;&nbsp;| |[\]&nbsp;&nbsp;&nbsp;&nbsp;c"]
expected: FAIL
[execCommand("delete", false, ""): "&nbsp;&nbsp;[\]&nbsp; b" (length of whiteSpace sequence: 4)]
expected: FAIL
[execCommand("delete", false, ""): "a&nbsp; &nbsp;|[\] &nbsp; b"]
expected: FAIL
[execCommand("delete", false, ""): "&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;[\] b" (length of whiteSpace sequence: 10)]
expected: FAIL
[execCommand("delete", false, ""): "<span>abc </span><span>[\]&nbsp;def</span>"]
expected: FAIL
[execCommand("delete", false, ""): "a &nbsp; &nbsp; [\]&nbsp; &nbsp; &nbsp; b" (length of whiteSpace sequence: 11)]
expected: FAIL
[execCommand("delete", false, ""): "a &nbsp; [\]&nbsp; b" (length of whiteSpace sequence: 5)]
expected: FAIL
[execCommand("delete", false, ""): "&nbsp;[\]&nbsp;&nbsp; b" (length of whiteSpace sequence: 4)]
expected: FAIL
[execCommand("delete", false, ""): "<span>abc&nbsp;&nbsp;[\]</span><span> &nbsp; def</span>"]
expected: FAIL
[execCommand("delete", false, ""): "<span>abc&nbsp;<span>&nbsp;[\]def</span></span>"]
expected: FAIL
[execCommand("delete", false, ""): "a&nbsp;&nbsp;&nbsp;[\]&nbsp;&nbsp;| |&nbsp;&nbsp;&nbsp;c"]
expected: FAIL
[execCommand("delete", false, ""): "&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;[\]b" (length of whiteSpace sequence: 10)]
expected: FAIL
[execCommand("delete", false, ""): "&nbsp; [\]&nbsp; &nbsp; &nbsp; &nbsp; b" (length of whiteSpace sequence: 10)]
expected: FAIL
[execCommand("delete", false, ""): "<span><span>abc &nbsp;[\]</span></span><span>&nbsp;def</span>"]
expected: FAIL
[execCommand("delete", false, ""): "a&nbsp; &nbsp;|&nbsp;[\] &nbsp;b"]
expected: FAIL
[execCommand("delete", false, ""): "a &nbsp; &nbsp;[\]b" (length of whiteSpace sequence: 4)]
expected: FAIL
[execCommand("delete", false, ""): "<span>abc&nbsp;</span><span>[\] &nbsp; def</span>"]
expected: FAIL
[execCommand("delete", false, ""): "<span>abc&nbsp;[\]</span><span>&nbsp;def</span>"]
expected: FAIL
[execCommand("delete", false, ""): "<span><span>abc &nbsp;</span></span><span>&nbsp;[\]def</span>"]
expected: FAIL
[execCommand("delete", false, ""): "<span><span>abc&nbsp;</span>&nbsp;[\]def</span>"]
expected: FAIL
[execCommand("delete", false, ""): "a &nbsp; |&nbsp;[\] &nbsp;b"]
expected: FAIL
[execCommand("delete", false, ""): "&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;[\] &nbsp; b" (length of whiteSpace sequence: 11)]
expected: FAIL
[execCommand("delete", false, ""): "<span>abc &nbsp;</span><span>&nbsp;[\]def</span>"]
expected: FAIL
[execCommand("delete", false, ""): "a&nbsp; &nbsp;|[\]&nbsp; &nbsp;b"]
expected: FAIL
[execCommand("delete", false, ""): "a&nbsp;&nbsp;&nbsp;[\]&nbsp; b" (length of whiteSpace sequence: 5)]
[execCommand("delete", false, ""): "a&nbsp;&nbsp;&nbsp;&nbsp;|[\]&nbsp;&nbsp;&nbsp;&nbsp;b"]
expected: FAIL
[execCommand("delete", false, ""): "a&nbsp;&nbsp;&nbsp; &nbsp;[\]b" (length of whiteSpace sequence: 5)]
[execCommand("delete", false, ""): "a &nbsp; [\]|&nbsp; &nbsp;b"]
expected: FAIL
[execCommand("delete", false, ""): "&nbsp; &nbsp; [\]&nbsp; &nbsp; &nbsp; b" (length of whiteSpace sequence: 10)]
[execCommand("delete", false, ""): "a &nbsp; |[\]&nbsp; &nbsp;b"]
expected: FAIL
[execCommand("delete", false, ""): "a &nbsp; &nbsp; &nbsp; &nbsp;[\] &nbsp; b" (length of whiteSpace sequence: 11)]
[execCommand("delete", false, ""): "<span>abc&nbsp;&nbsp;[\]</span><span>&nbsp; &nbsp;def</span>"]
expected: FAIL
[execCommand("delete", false, ""): "a &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;[\]b" (length of whiteSpace sequence: 10)]
expected: FAIL
[execCommand("delete", false, ""): "<span><span>abc&nbsp;</span>[\]&nbsp;def</span>"]
expected: FAIL
[execCommand("delete", false, ""): "&nbsp; &nbsp; &nbsp; &nbsp; [\]&nbsp; b" (length of whiteSpace sequence: 10)]
expected: FAIL
[execCommand("delete", false, ""): "&nbsp;[\] &nbsp; &nbsp; &nbsp; &nbsp; b" (length of whiteSpace sequence: 10)]
expected: FAIL
[execCommand("delete", false, ""): "&nbsp;&nbsp; &nbsp; &nbsp;[\] &nbsp; &nbsp; b" (length of whiteSpace sequence: 11)]
[execCommand("delete", false, ""): "<span style=white-space:pre;> </span>[\]&nbsp;&nbsp;&nbsp;a"]
expected: FAIL