mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-19 16:25:38 +00:00
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:
parent
33b61e1ce5
commit
a269bbe497
@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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 {
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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 [\]<span style=white-space:pre;> </span>"]
|
||||
expected: FAIL
|
||||
|
||||
[execCommand("delete", false, ""): "a [\]b" (length of whiteSpace sequence: 4)]
|
||||
expected: FAIL
|
||||
|
||||
[execCommand("delete", false, ""): "<span><span>abc </span>[\] def</span>"]
|
||||
expected: FAIL
|
||||
|
||||
[execCommand("delete", false, ""): "a [\] b" (length of whiteSpace sequence: 10)]
|
||||
expected: FAIL
|
||||
|
||||
[execCommand("delete", false, ""): " [\] b" (length of whiteSpace sequence: 11)]
|
||||
expected: FAIL
|
||||
|
||||
[execCommand("delete", false, ""): " [\] b" (length of whiteSpace sequence: 11)]
|
||||
expected: FAIL
|
||||
|
||||
[execCommand("delete", false, ""): "<span>abc [\]</span><span> def</span>"]
|
||||
expected: FAIL
|
||||
|
||||
[execCommand("delete", false, ""): "a [\] b" (length of whiteSpace sequence: 10)]
|
||||
expected: FAIL
|
||||
|
||||
[execCommand("delete", false, ""): "a [\] b" (length of whiteSpace sequence: 5)]
|
||||
expected: FAIL
|
||||
|
||||
[execCommand("delete", false, ""): "<span><span>abc </span>[\] def</span>"]
|
||||
expected: FAIL
|
||||
|
||||
[execCommand("delete", false, ""): "a [\] b" (length of whiteSpace sequence: 10)]
|
||||
expected: FAIL
|
||||
|
||||
[execCommand("delete", false, ""): "<span>abc </span><span> [\]def</span>"]
|
||||
expected: FAIL
|
||||
|
||||
[execCommand("delete", false, ""): "a [\] b" (length of whiteSpace sequence: 10)]
|
||||
expected: FAIL
|
||||
|
||||
[execCommand("delete", false, ""): " [\] b" (length of whiteSpace sequence: 4)]
|
||||
expected: FAIL
|
||||
|
||||
[execCommand("delete", false, ""): "<span>abc <span>[\] def</span></span>"]
|
||||
expected: FAIL
|
||||
|
||||
[execCommand("delete", false, ""): " [\] b" (length of whiteSpace sequence: 11)]
|
||||
expected: FAIL
|
||||
|
||||
[execCommand("delete", false, ""): "<span>abc [\]</span><span> def</span>"]
|
||||
expected: FAIL
|
||||
|
||||
[execCommand("delete", false, ""): "a [\] b" (length of whiteSpace sequence: 11)]
|
||||
expected: FAIL
|
||||
|
||||
[execCommand("delete", false, ""): "a [\]|| c"]
|
||||
expected: FAIL
|
||||
|
||||
[execCommand("delete", false, ""): " [\]b" (length of whiteSpace sequence: 4)]
|
||||
expected: FAIL
|
||||
|
||||
[execCommand("delete", false, ""): "a [\] b" (length of whiteSpace sequence: 11)]
|
||||
expected: FAIL
|
||||
|
||||
[execCommand("delete", false, ""): "a b[\] | c"]
|
||||
expected: FAIL
|
||||
|
||||
[execCommand("delete", false, ""): "a [\] | | c"]
|
||||
expected: FAIL
|
||||
|
||||
[execCommand("delete", false, ""): "<span><span>abc </span> [\]def</span>"]
|
||||
expected: FAIL
|
||||
|
||||
[execCommand("delete", false, ""): "a b[\] | c"]
|
||||
expected: FAIL
|
||||
|
||||
[execCommand("delete", false, ""): "a [\] b" (length of whiteSpace sequence: 10)]
|
||||
expected: FAIL
|
||||
|
||||
[execCommand("delete", false, ""): " [\] b" (length of whiteSpace sequence: 10)]
|
||||
expected: FAIL
|
||||
|
||||
[execCommand("delete", false, ""): "<span>abc </span><span>[\] def</span>"]
|
||||
expected: FAIL
|
||||
|
||||
[execCommand("delete", false, ""): "a [\] b" (length of whiteSpace sequence: 10)]
|
||||
expected: FAIL
|
||||
|
||||
[execCommand("delete", false, ""): "<span>abc [\]<span> def</span></span>"]
|
||||
expected: FAIL
|
||||
|
||||
[execCommand("delete", false, ""): "<span>abc <span>[\] def</span></span>"]
|
||||
expected: FAIL
|
||||
|
||||
[execCommand("delete", false, ""): "a [\] b" (length of whiteSpace sequence: 10)]
|
||||
expected: FAIL
|
||||
|
||||
[execCommand("delete", false, ""): "a |b[\] c"]
|
||||
expected: FAIL
|
||||
|
||||
[execCommand("delete", false, ""): "a |[\]| c"]
|
||||
expected: FAIL
|
||||
|
||||
[execCommand("delete", false, ""): "a [\] | | c"]
|
||||
expected: FAIL
|
||||
|
||||
[execCommand("delete", false, ""): "a | |[\] c"]
|
||||
expected: FAIL
|
||||
|
||||
[execCommand("delete", false, ""): "<span>abc [\]<span> def</span></span>"]
|
||||
expected: FAIL
|
||||
|
||||
[execCommand("delete", false, ""): " [\] b" (length of whiteSpace sequence: 11)]
|
||||
expected: FAIL
|
||||
|
||||
[execCommand("delete", false, ""): "a [\] b" (length of whiteSpace sequence: 10)]
|
||||
expected: FAIL
|
||||
|
||||
[execCommand("delete", false, ""): " [\] b" (length of whiteSpace sequence: 11)]
|
||||
expected: FAIL
|
||||
|
||||
[execCommand("delete", false, ""): "<span>abc [\]<span> def</span></span>"]
|
||||
expected: FAIL
|
||||
|
||||
[execCommand("delete", false, ""): " [\] b" (length of whiteSpace sequence: 10)]
|
||||
expected: FAIL
|
||||
|
||||
[execCommand("delete", false, ""): "a | [\] b"]
|
||||
expected: FAIL
|
||||
|
||||
[execCommand("delete", false, ""): " [\] b" (length of whiteSpace sequence: 4)]
|
||||
expected: FAIL
|
||||
|
||||
[execCommand("delete", false, ""): "<span><span>abc [\]</span> def</span>"]
|
||||
expected: FAIL
|
||||
|
||||
[execCommand("delete", false, ""): "a [\] b" (length of whiteSpace sequence: 11)]
|
||||
expected: FAIL
|
||||
|
||||
[execCommand("delete", false, ""): "<span>abc <span>[\] def</span></span>"]
|
||||
expected: FAIL
|
||||
|
||||
[execCommand("delete", false, ""): "a | [\] b"]
|
||||
expected: FAIL
|
||||
|
||||
[execCommand("delete", false, ""): "<span>abc <span>[\] def</span></span>"]
|
||||
expected: FAIL
|
||||
|
||||
[execCommand("delete", false, ""): " [\]b" (length of whiteSpace sequence: 5)]
|
||||
expected: FAIL
|
||||
|
||||
[execCommand("delete", false, ""): "a [\] b" (length of whiteSpace sequence: 4)]
|
||||
expected: FAIL
|
||||
|
||||
[execCommand("delete", false, ""): "a [\] b" (length of whiteSpace sequence: 10)]
|
||||
expected: FAIL
|
||||
|
||||
[execCommand("delete", false, ""): "a || [\] c"]
|
||||
expected: FAIL
|
||||
|
||||
[execCommand("delete", false, ""): "<span>abc </span><span>[\] def</span>"]
|
||||
expected: FAIL
|
||||
|
||||
[execCommand("delete", false, ""): "a ||[\] c"]
|
||||
expected: FAIL
|
||||
|
||||
[execCommand("delete", false, ""): "<span>abc [\]</span><span> def</span>"]
|
||||
expected: FAIL
|
||||
|
||||
[execCommand("delete", false, ""): "a [\] b" (length of whiteSpace sequence: 11)]
|
||||
expected: FAIL
|
||||
|
||||
[execCommand("delete", false, ""): "<span>abc [\]</span><span> def</span>"]
|
||||
expected: FAIL
|
||||
|
||||
[execCommand("delete", false, ""): " [\] b" (length of whiteSpace sequence: 4)]
|
||||
expected: FAIL
|
||||
|
||||
[execCommand("delete", false, ""): "a | [\] b"]
|
||||
expected: FAIL
|
||||
|
||||
[execCommand("delete", false, ""): "a [\] b" (length of whiteSpace sequence: 4)]
|
||||
expected: FAIL
|
||||
|
||||
[execCommand("delete", false, ""): "a | [\]| c"]
|
||||
expected: FAIL
|
||||
|
||||
[execCommand("delete", false, ""): "<span>abc [\]<span> def</span></span>"]
|
||||
expected: FAIL
|
||||
|
||||
[execCommand("delete", false, ""): "a [\] b" (length of whiteSpace sequence: 4)]
|
||||
expected: FAIL
|
||||
|
||||
[execCommand("delete", false, ""): " [\] b" (length of whiteSpace sequence: 10)]
|
||||
expected: FAIL
|
||||
|
||||
[execCommand("delete", false, ""): "a [\] b" (length of whiteSpace sequence: 11)]
|
||||
expected: FAIL
|
||||
|
||||
[execCommand("delete", false, ""): "a [\] b" (length of whiteSpace sequence: 11)]
|
||||
expected: FAIL
|
||||
|
||||
[execCommand("delete", false, ""): "a [\] b" (length of whiteSpace sequence: 5)]
|
||||
expected: FAIL
|
||||
|
||||
[execCommand("delete", false, ""): "<span><span>abc </span>[\] def</span>"]
|
||||
expected: FAIL
|
||||
|
||||
[execCommand("delete", false, ""): "<span><span>abc </span></span><span>[\] def</span>"]
|
||||
expected: FAIL
|
||||
|
||||
[execCommand("delete", false, ""): "a [\]| b"]
|
||||
expected: FAIL
|
||||
|
||||
[execCommand("delete", false, ""): "a [\]| b"]
|
||||
expected: FAIL
|
||||
|
||||
[execCommand("delete", false, ""): "<span><span>abc [\]</span> def</span>"]
|
||||
expected: FAIL
|
||||
|
||||
[execCommand("delete", false, ""): " [\] b" (length of whiteSpace sequence: 10)]
|
||||
expected: FAIL
|
||||
|
||||
[execCommand("delete", false, ""): " [\] b" (length of whiteSpace sequence: 11)]
|
||||
expected: FAIL
|
||||
|
||||
[execCommand("delete", false, ""): "a <span style=white-space:pre;>[\] </span>"]
|
||||
expected: FAIL
|
||||
|
||||
[execCommand("delete", false, ""): "<span><span>abc [\]</span> def</span>"]
|
||||
expected: FAIL
|
||||
|
||||
[execCommand("delete", false, ""): " [\] b" (length of whiteSpace sequence: 4)]
|
||||
expected: FAIL
|
||||
|
||||
[execCommand("delete", false, ""): "a [\] || c"]
|
||||
expected: FAIL
|
||||
|
||||
[execCommand("delete", false, ""): "a [\]| | c"]
|
||||
expected: FAIL
|
||||
|
||||
[execCommand("delete", false, ""): "<span>abc </span><span> [\]def</span>"]
|
||||
expected: FAIL
|
||||
|
||||
[execCommand("delete", false, ""): "<span>abc </span><span>[\] def</span>"]
|
||||
expected: FAIL
|
||||
|
||||
[execCommand("delete", false, ""): "a [\] b" (length of whiteSpace sequence: 5)]
|
||||
expected: FAIL
|
||||
|
||||
[execCommand("delete", false, ""): "<span>abc [\]</span><span> def</span>"]
|
||||
expected: FAIL
|
||||
|
||||
[execCommand("delete", false, ""): "a [\] b" (length of whiteSpace sequence: 5)]
|
||||
expected: FAIL
|
||||
|
||||
[execCommand("delete", false, ""): "<span><span>abc [\]</span> def</span>"]
|
||||
expected: FAIL
|
||||
|
||||
[execCommand("delete", false, ""): "a<span style=white-space:pre;> </span>[\]b"]
|
||||
expected: FAIL
|
||||
|
||||
[execCommand("delete", false, ""): "<span>abc <span> [\]def</span></span>"]
|
||||
expected: FAIL
|
||||
|
||||
[execCommand("delete", false, ""): "a | |[\] c"]
|
||||
expected: FAIL
|
||||
|
||||
[execCommand("delete", false, ""): " [\] b" (length of whiteSpace sequence: 4)]
|
||||
expected: FAIL
|
||||
|
||||
[execCommand("delete", false, ""): "a |[\] b"]
|
||||
expected: FAIL
|
||||
|
||||
[execCommand("delete", false, ""): " [\] b" (length of whiteSpace sequence: 10)]
|
||||
expected: FAIL
|
||||
|
||||
[execCommand("delete", false, ""): "<span>abc </span><span>[\] def</span>"]
|
||||
expected: FAIL
|
||||
|
||||
[execCommand("delete", false, ""): "a [\] b" (length of whiteSpace sequence: 11)]
|
||||
expected: FAIL
|
||||
|
||||
[execCommand("delete", false, ""): "a [\] b" (length of whiteSpace sequence: 5)]
|
||||
expected: FAIL
|
||||
|
||||
[execCommand("delete", false, ""): " [\] b" (length of whiteSpace sequence: 4)]
|
||||
expected: FAIL
|
||||
|
||||
[execCommand("delete", false, ""): "<span>abc [\]</span><span> def</span>"]
|
||||
expected: FAIL
|
||||
|
||||
[execCommand("delete", false, ""): "<span>abc <span> [\]def</span></span>"]
|
||||
expected: FAIL
|
||||
|
||||
[execCommand("delete", false, ""): "a [\] | | c"]
|
||||
expected: FAIL
|
||||
|
||||
[execCommand("delete", false, ""): " [\]b" (length of whiteSpace sequence: 10)]
|
||||
expected: FAIL
|
||||
|
||||
[execCommand("delete", false, ""): " [\] b" (length of whiteSpace sequence: 10)]
|
||||
expected: FAIL
|
||||
|
||||
[execCommand("delete", false, ""): "<span><span>abc [\]</span></span><span> def</span>"]
|
||||
expected: FAIL
|
||||
|
||||
[execCommand("delete", false, ""): "a | [\] b"]
|
||||
expected: FAIL
|
||||
|
||||
[execCommand("delete", false, ""): "a [\]b" (length of whiteSpace sequence: 4)]
|
||||
expected: FAIL
|
||||
|
||||
[execCommand("delete", false, ""): "<span>abc </span><span>[\] def</span>"]
|
||||
expected: FAIL
|
||||
|
||||
[execCommand("delete", false, ""): "<span>abc [\]</span><span> def</span>"]
|
||||
expected: FAIL
|
||||
|
||||
[execCommand("delete", false, ""): "<span><span>abc </span></span><span> [\]def</span>"]
|
||||
expected: FAIL
|
||||
|
||||
[execCommand("delete", false, ""): "<span><span>abc </span> [\]def</span>"]
|
||||
expected: FAIL
|
||||
|
||||
[execCommand("delete", false, ""): "a | [\] b"]
|
||||
expected: FAIL
|
||||
|
||||
[execCommand("delete", false, ""): " [\] b" (length of whiteSpace sequence: 11)]
|
||||
expected: FAIL
|
||||
|
||||
[execCommand("delete", false, ""): "<span>abc </span><span> [\]def</span>"]
|
||||
expected: FAIL
|
||||
|
||||
[execCommand("delete", false, ""): "a |[\] b"]
|
||||
expected: FAIL
|
||||
|
||||
[execCommand("delete", false, ""): "a [\] b" (length of whiteSpace sequence: 5)]
|
||||
[execCommand("delete", false, ""): "a |[\] b"]
|
||||
expected: FAIL
|
||||
|
||||
[execCommand("delete", false, ""): "a [\]b" (length of whiteSpace sequence: 5)]
|
||||
[execCommand("delete", false, ""): "a [\]| b"]
|
||||
expected: FAIL
|
||||
|
||||
[execCommand("delete", false, ""): " [\] b" (length of whiteSpace sequence: 10)]
|
||||
[execCommand("delete", false, ""): "a |[\] b"]
|
||||
expected: FAIL
|
||||
|
||||
[execCommand("delete", false, ""): "a [\] b" (length of whiteSpace sequence: 11)]
|
||||
[execCommand("delete", false, ""): "<span>abc [\]</span><span> def</span>"]
|
||||
expected: FAIL
|
||||
|
||||
[execCommand("delete", false, ""): "a [\]b" (length of whiteSpace sequence: 10)]
|
||||
expected: FAIL
|
||||
|
||||
[execCommand("delete", false, ""): "<span><span>abc </span>[\] def</span>"]
|
||||
expected: FAIL
|
||||
|
||||
[execCommand("delete", false, ""): " [\] b" (length of whiteSpace sequence: 10)]
|
||||
expected: FAIL
|
||||
|
||||
[execCommand("delete", false, ""): " [\] b" (length of whiteSpace sequence: 10)]
|
||||
expected: FAIL
|
||||
|
||||
[execCommand("delete", false, ""): " [\] b" (length of whiteSpace sequence: 11)]
|
||||
[execCommand("delete", false, ""): "<span style=white-space:pre;> </span>[\] a"]
|
||||
expected: FAIL
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user