зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1727008
- `HTMLEditor` shouldn't strip `<html>` element nor `<body>` elements r=m_kato
The new editor utility method does not stop scanning editable elements even if it reaches the document root nor the (primary) `<body>` element. Of course, they should stop there if scanning editable block. And `ScanEmptyBlockInclusiveAncestor()` shouldn't store the removable empty block element to them. Differential Revision: https://phabricator.services.mozilla.com/D123316
This commit is contained in:
Родитель
5b454de3d5
Коммит
984b60a1b6
|
@ -1405,6 +1405,8 @@ Element* HTMLEditUtils::GetAncestorElement(
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
const Element* theBodyElement = aContent.OwnerDoc()->GetBody();
|
||||
const Element* theDocumentElement = aContent.OwnerDoc()->GetDocumentElement();
|
||||
Element* lastAncestorElement = nullptr;
|
||||
const bool editableElementOnly =
|
||||
aAncestorTypes.contains(AncestorType::EditableElement);
|
||||
|
@ -1449,7 +1451,8 @@ Element* HTMLEditUtils::GetAncestorElement(
|
|||
HTMLEditUtils::IsInlineElement(*lastAncestorElement));
|
||||
return lastAncestorElement; // the last inline element which we found
|
||||
}
|
||||
if (element == aAncestorLimiter) {
|
||||
if (element == aAncestorLimiter || element == theBodyElement ||
|
||||
element == theDocumentElement) {
|
||||
break;
|
||||
}
|
||||
lastAncestorElement = element;
|
||||
|
@ -1467,6 +1470,8 @@ Element* HTMLEditUtils::GetInclusiveAncestorElement(
|
|||
aAncestorTypes.contains(AncestorType::ClosestBlockElement) ||
|
||||
aAncestorTypes.contains(AncestorType::MostDistantInlineElementInBlock));
|
||||
|
||||
const Element* theBodyElement = aContent.OwnerDoc()->GetBody();
|
||||
const Element* theDocumentElement = aContent.OwnerDoc()->GetDocumentElement();
|
||||
const bool editableElementOnly =
|
||||
aAncestorTypes.contains(AncestorType::EditableElement);
|
||||
const bool lookingForClosesetBlockElement =
|
||||
|
@ -1490,6 +1495,15 @@ Element* HTMLEditUtils::GetInclusiveAncestorElement(
|
|||
HTMLEditUtils::IsInlineElement(aContent));
|
||||
};
|
||||
|
||||
// If aContent is the body element or the document element, we shouldn't climb
|
||||
// up to its parent.
|
||||
if (editableElementOnly &&
|
||||
(&aContent == theBodyElement || &aContent == theDocumentElement)) {
|
||||
return isSerachingElementType(aContent)
|
||||
? const_cast<Element*>(aContent.AsElement())
|
||||
: nullptr;
|
||||
}
|
||||
|
||||
// If aContent is a block element, we don't need to climb up the tree.
|
||||
// Consider the result right now.
|
||||
if (HTMLEditUtils::IsBlockElement(aContent) &&
|
||||
|
|
|
@ -82,7 +82,9 @@ class HTMLEditUtils final {
|
|||
* if aContent isn't editable.
|
||||
*/
|
||||
static bool IsRemovableNode(const nsIContent& aContent) {
|
||||
return aContent.GetParentNode() && aContent.GetParentNode()->IsEditable();
|
||||
return aContent.GetParentNode() && aContent.GetParentNode()->IsEditable() &&
|
||||
&aContent != aContent.OwnerDoc()->GetBody() &&
|
||||
&aContent != aContent.OwnerDoc()->GetDocumentElement();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -91,7 +93,9 @@ class HTMLEditUtils final {
|
|||
*/
|
||||
static bool IsRemovableFromParentNode(const nsIContent& aContent) {
|
||||
return aContent.IsEditable() && aContent.GetParentNode() &&
|
||||
aContent.GetParentNode()->IsEditable();
|
||||
aContent.GetParentNode()->IsEditable() &&
|
||||
&aContent != aContent.OwnerDoc()->GetBody() &&
|
||||
&aContent != aContent.OwnerDoc()->GetDocumentElement();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -5029,6 +5029,7 @@ Element* HTMLEditor::AutoDeleteRangesHandler::AutoEmptyBlockAncestorDeleter::
|
|||
ScanEmptyBlockInclusiveAncestor(const HTMLEditor& aHTMLEditor,
|
||||
nsIContent& aStartContent) {
|
||||
MOZ_ASSERT(aHTMLEditor.IsEditActionDataAvailable());
|
||||
MOZ_ASSERT(!mEmptyInclusiveAncestorBlockElement);
|
||||
|
||||
// If we are inside an empty block, delete it.
|
||||
// Note: do NOT delete table elements this way.
|
||||
|
|
|
@ -0,0 +1,51 @@
|
|||
<html><head>
|
||||
<script src=/resources/testharness.js></script>
|
||||
<script src=/resources/testharnessreport.js></script>
|
||||
</head><body><script>
|
||||
"use strict"
|
||||
|
||||
document.designMode = "on";
|
||||
|
||||
test(() => {
|
||||
document.querySelector("script")?.remove();
|
||||
document.head?.remove();
|
||||
document.body.firstChild?.remove();
|
||||
document.body.appendChild(document.createElement("p"));
|
||||
getSelection().collapse(document.querySelector("p"), 0);
|
||||
document.querySelector("p").firstChild?.remove();
|
||||
document.execCommand("delete");
|
||||
assert_in_array(
|
||||
document.documentElement?.outerHTML.replace(/\n/g, ""),
|
||||
[
|
||||
"<html><body></body></html>",
|
||||
"<html><body><br></body></html>",
|
||||
"<html><body><p></p></body></html>",
|
||||
"<html><body><p><br></p></body></html>"
|
||||
],
|
||||
"Body element shouldn't be deleted even if it becomes empty"
|
||||
);
|
||||
}, "Delete in empty paragraph shouldn't delete parent body and html elements even if they become empty by Backspace");
|
||||
|
||||
test(() => {
|
||||
document.querySelector("script")?.remove();
|
||||
document.head?.remove();
|
||||
document.body.firstChild?.remove();
|
||||
document.body.appendChild(document.createElement("p"));
|
||||
getSelection().collapse(document.querySelector("p"), 0);
|
||||
document.querySelector("p").firstChild?.remove();
|
||||
document.execCommand("delete");
|
||||
assert_in_array(
|
||||
document.documentElement?.outerHTML.replace(/\n/g, ""),
|
||||
[
|
||||
"<html><body></body></html>",
|
||||
"<html><body><br></body></html>",
|
||||
"<html><body><p></p></body></html>",
|
||||
"<html><body><p><br></p></body></html>"
|
||||
],
|
||||
"Body element shouldn't be deleted even if it becomes empty"
|
||||
);
|
||||
|
||||
document.designMode = "off";
|
||||
}, "Delete in empty paragraph shouldn't delete parent body and html elements even if they become empty by Delete");
|
||||
|
||||
</script></body></html>
|
Загрузка…
Ссылка в новой задаче