Bug 1675779 - HandleDeleteAtomicContent should handle non-editable text node. r=masayuki

Since `WSScanResult` can return non-editable/non-removable text node,
`HandleDeleteAtomicContent` is called with non editable text node, then it cannot
remove atomic content.

This fix doesn't follow other block case such as added newer test.

Differential Revision: https://phabricator.services.mozilla.com/D96298
This commit is contained in:
Makoto Kato 2021-02-22 10:18:48 +00:00
Родитель 93b37bfebb
Коммит 6d173e8282
7 изменённых файлов: 128 добавлений и 9 удалений

Просмотреть файл

@ -257,6 +257,23 @@ class MOZ_STACK_CLASS HTMLEditor::AutoDeleteRangesHandler final {
const HTMLEditor& aHTMLEditor, const nsIContent& aAtomicContent, const HTMLEditor& aHTMLEditor, const nsIContent& aAtomicContent,
AutoRangeArray& aRangesToDelete) const; AutoRangeArray& aRangesToDelete) const;
/**
* GetAtomicContnetToDelete() returns better content that is deletion of
* atomic element. If aScanFromCaretPointResult is special, since this
* point may not be editable, we look for better point to remove atomic
* content.
*
* @param aDirectionAndAmount Direction of the deletion.
* @param aWSRunScannerAtCaret WSRunScanner instance which was
* initialized with the caret point.
* @param aScanFromCaretPointResult Scan result of aWSRunScannerAtCaret
* toward aDirectionAndAmount.
*/
static nsIContent* GetAtomicContentToDelete(
nsIEditor::EDirection aDirectionAndAmount,
const WSRunScanner& aWSRunScannerAtCaret,
const WSScanResult& aScanFromCaretPointResult) MOZ_NONNULL_RETURN;
/** /**
* HandleDeleteHRElement() handles deletion around `<hr>` element. If * HandleDeleteHRElement() handles deletion around `<hr>` element. If
* aDirectionAndAmount is nsIEditor::ePrevious, aHTElement is removed only * aDirectionAndAmount is nsIEditor::ePrevious, aHTElement is removed only
@ -1637,8 +1654,16 @@ HTMLEditor::AutoDeleteRangesHandler::ComputeRangesToDeleteAroundCollapsedRanges(
aWSRunScannerAtCaret.GetEditingHost()) { aWSRunScannerAtCaret.GetEditingHost()) {
return NS_OK; return NS_OK;
} }
nsIContent* atomicContent = GetAtomicContentToDelete(
aDirectionAndAmount, aWSRunScannerAtCaret, aScanFromCaretPointResult);
if (!HTMLEditUtils::IsRemovableNode(*atomicContent)) {
NS_WARNING(
"AutoDeleteRangesHandler::GetAtomicContentToDelete() cannot find "
"removable atomic content");
return NS_ERROR_FAILURE;
}
nsresult rv = ComputeRangesToDeleteAtomicContent( nsresult rv = ComputeRangesToDeleteAtomicContent(
aHTMLEditor, *aScanFromCaretPointResult.GetContent(), aRangesToDelete); aHTMLEditor, *atomicContent, aRangesToDelete);
NS_WARNING_ASSERTION( NS_WARNING_ASSERTION(
NS_SUCCEEDED(rv), NS_SUCCEEDED(rv),
"AutoDeleteRangesHandler::ComputeRangesToDeleteAtomicContent() failed"); "AutoDeleteRangesHandler::ComputeRangesToDeleteAtomicContent() failed");
@ -1665,6 +1690,10 @@ HTMLEditor::AutoDeleteRangesHandler::ComputeRangesToDeleteAroundCollapsedRanges(
if (NS_WARN_IF(!aScanFromCaretPointResult.GetContent()->IsElement())) { if (NS_WARN_IF(!aScanFromCaretPointResult.GetContent()->IsElement())) {
return NS_ERROR_FAILURE; return NS_ERROR_FAILURE;
} }
// TODO(m_kato):
// When aScanFromCaretPointResult.GetContent isn't editable and is
// removable, PrepareToDeleteAtOtherBlockBoundary will return false.
// But this should be removed.
AutoBlockElementsJoiner joiner(*this); AutoBlockElementsJoiner joiner(*this);
if (!joiner.PrepareToDeleteAtOtherBlockBoundary( if (!joiner.PrepareToDeleteAtOtherBlockBoundary(
aHTMLEditor, aDirectionAndAmount, aHTMLEditor, aDirectionAndAmount,
@ -1764,9 +1793,17 @@ HTMLEditor::AutoDeleteRangesHandler::HandleDeleteAroundCollapsedRanges(
aWSRunScannerAtCaret.GetEditingHost()) { aWSRunScannerAtCaret.GetEditingHost()) {
return EditActionHandled(); return EditActionHandled();
} }
nsCOMPtr<nsIContent> atomicContent = GetAtomicContentToDelete(
aDirectionAndAmount, aWSRunScannerAtCaret, aScanFromCaretPointResult);
if (!HTMLEditUtils::IsRemovableNode(*atomicContent)) {
NS_WARNING(
"AutoDeleteRangesHandler::GetAtomicContentToDelete() cannot find "
"removable atomic content");
return EditActionResult(NS_ERROR_FAILURE);
}
EditActionResult result = HandleDeleteAtomicContent( EditActionResult result = HandleDeleteAtomicContent(
aHTMLEditor, MOZ_KnownLive(*aScanFromCaretPointResult.GetContent()), aHTMLEditor, *atomicContent, aWSRunScannerAtCaret.ScanStartRef(),
aWSRunScannerAtCaret.ScanStartRef(), aWSRunScannerAtCaret); aWSRunScannerAtCaret);
NS_WARNING_ASSERTION( NS_WARNING_ASSERTION(
result.Succeeded(), result.Succeeded(),
"AutoDeleteRangesHandler::HandleDeleteAtomicContent() failed"); "AutoDeleteRangesHandler::HandleDeleteAtomicContent() failed");
@ -2264,6 +2301,38 @@ EditActionResult HTMLEditor::AutoDeleteRangesHandler::HandleDeleteHRElement(
return EditActionHandled(rv); return EditActionHandled(rv);
} }
// static
nsIContent* HTMLEditor::AutoDeleteRangesHandler::GetAtomicContentToDelete(
nsIEditor::EDirection aDirectionAndAmount,
const WSRunScanner& aWSRunScannerAtCaret,
const WSScanResult& aScanFromCaretPointResult) {
MOZ_ASSERT(aScanFromCaretPointResult.GetContent());
if (!aScanFromCaretPointResult.ReachedSpecialContent()) {
return aScanFromCaretPointResult.GetContent();
}
if (!aScanFromCaretPointResult.GetContent()->IsText() ||
HTMLEditUtils::IsRemovableNode(*aScanFromCaretPointResult.GetContent())) {
return aScanFromCaretPointResult.GetContent();
}
// aScanFromCaretPointResult is non-removable text node.
// Since we try removing atomic content, we look for removable node from
// scanned point that is non-removable text.
nsIContent* removableRoot = aScanFromCaretPointResult.GetContent();
while (removableRoot && !HTMLEditUtils::IsRemovableNode(*removableRoot)) {
removableRoot = removableRoot->GetParent();
}
if (removableRoot) {
return removableRoot;
}
// Not found better content. This content may not be removable.
return aScanFromCaretPointResult.GetContent();
}
nsresult nsresult
HTMLEditor::AutoDeleteRangesHandler::ComputeRangesToDeleteAtomicContent( HTMLEditor::AutoDeleteRangesHandler::ComputeRangesToDeleteAtomicContent(
const HTMLEditor& aHTMLEditor, const nsIContent& aAtomicContent, const HTMLEditor& aHTMLEditor, const nsIContent& aAtomicContent,

Просмотреть файл

@ -697,3 +697,6 @@
[[["delete",""\]\] "<div> a[\]bc</div>" compare innerHTML] [[["delete",""\]\] "<div> a[\]bc</div>" compare innerHTML]
expected: FAIL expected: FAIL
[[["delete",""\]\] "foo<div contenteditable=false>bar</div>[\]baz" compare innerHTML]
expected: FAIL

Просмотреть файл

@ -506,8 +506,8 @@
[forwarddelete.html?6001-last] [forwarddelete.html?6001-last]
max-asserts: 3 max-asserts: 6
min-asserts: 3 min-asserts: 6
[[["forwarddelete",""\]\] "<ol><li>foo</ol><p>{}<br></p><ol><li>bar</ol>" compare innerHTML] [[["forwarddelete",""\]\] "<ol><li>foo</ol><p>{}<br></p><ol><li>bar</ol>" compare innerHTML]
expected: FAIL expected: FAIL

Просмотреть файл

@ -300,7 +300,7 @@
[insertparagraph.html?5001-6000] [insertparagraph.html?5001-6000]
min-asserts: 14 # Retrieving meaningless raw DOM point from WSScannerResult min-asserts: 10 # Retrieving meaningless raw DOM point from WSScannerResult
max-asserts: 16 # Only on W-fis on Linux1804-64-qr and on Android, there are 2 additional assertions. max-asserts: 16 # Only on W-fis on Linux1804-64-qr and on Android, there are 2 additional assertions.
[[["defaultparagraphseparator","p"\],["insertparagraph",""\]\] "<ul><li><div>foo[\]</ul>" compare innerHTML] [[["defaultparagraphseparator","p"\],["insertparagraph",""\]\] "<ul><li><div>foo[\]</ul>" compare innerHTML]
expected: FAIL expected: FAIL

Просмотреть файл

@ -52,6 +52,3 @@
[Backspace at "<div>abc <ul><li>[\] def </li></ul> ghi</div>" - comparing innerHTML] [Backspace at "<div>abc <ul><li>[\] def </li></ul> ghi</div>" - comparing innerHTML]
expected: FAIL expected: FAIL
[Backspace at "<p>abc<span contenteditable=\"false\">def</span>[\]ghi</p>"]
expected: FAIL

Просмотреть файл

@ -2560,4 +2560,29 @@ var browserTests = [
"foo{}bar", "foo{}bar",
[true], [true],
{"delete":[false,false,"",false,false,""]}], {"delete":[false,false,"",false,false,""]}],
["foo<span contenteditable=false>bar</span>[]baz",
[["delete",""]],
"foo{}baz",
[true],
{"delete":[false,false,"",false,false,""]}],
["foo<span contenteditable=false><span>b</span><span>a</span><span>r</span></span>[]baz",
[["delete",""]],
"foo{}baz",
[true],
{"delete":[false,false,"",false,false,""]}],
["foo<div contenteditable=false>bar</div>[]baz",
[["delete",""]],
"foo{}baz",
[true],
{"delete":[false,false,"",false,false,""]}],
["foo<span contenteditable=false><b>bar</b></span>[]baz",
[["delete",""]],
"foo{}baz",
[true],
{"delete":[false,false,"",false,false,""]}],
["foo<span>bar<span contenteditable=false>baz</span></span>[]qux",
[["delete",""]],
"foo<span>bar{}</span>qux",
[true],
{"delete":[false,false,"",false,false,""]}],
] ]

Просмотреть файл

@ -2445,4 +2445,29 @@ var browserTests = [
"foo{}bar", "foo{}bar",
[true], [true],
{"forwarddelete":[false,false,"",false,false,""]}], {"forwarddelete":[false,false,"",false,false,""]}],
["foo[]<span contenteditable=false>bar</span>baz",
[["forwarddelete",""]],
"foo{}baz",
[true],
{"forwarddelete":[false,false,"",false,false,""]}],
["foo[]<span contenteditable=false><span>b</span><span>a</span><span>r</span></span>baz",
[["forwarddelete",""]],
"foo{}baz",
[true],
{"forwarddelete":[false,false,"",false,false,""]}],
["foo[]<div contenteditable=false>bar</div>baz",
[["forwarddelete",""]],
"foo{}baz",
[true],
{"forwarddelete":[false,false,"",false,false,""]}],
["foo[]<span contenteditable=false><b>bar</b></span>baz",
[["forwarddelete",""]],
"foo{}baz",
[true],
{"forwarddelete":[false,false,"",false,false,""]}],
["foo<span>bar[]<span contenteditable=false>baz</span></span>qux",
[["forwarddelete",""]],
"foo<span>bar{}</span>qux",
[true],
{"forwarddelete":[false,false,"",false,false,""]}],
] ]