зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1880710 - Make `HTMLEditor::IsEmpty` check whether the editing host is empty r=m_kato
It checks whether the document body itself is empty or not. However, if there is only a shadow DOM hosts, it considers the content is empty. Therefore, `HTMLEditor::HandleDeleteSelection` does nothing. I think that it should check whether current editing host is empty or not. That does not work without selection ranges, but I think it's fine in most cases. Differential Revision: https://phabricator.services.mozilla.com/D202274
This commit is contained in:
Родитель
381a551e0f
Коммит
3719643bde
|
@ -5949,14 +5949,23 @@ bool HTMLEditor::IsEmpty() const {
|
|||
return true;
|
||||
}
|
||||
|
||||
// XXX Oddly, we check body or document element's state instead of
|
||||
// active editing host. Must be a bug.
|
||||
Element* bodyOrDocumentElement = GetRoot();
|
||||
if (!bodyOrDocumentElement) {
|
||||
return true;
|
||||
const Element* activeElement =
|
||||
GetDocument() ? GetDocument()->GetActiveElement() : nullptr;
|
||||
const Element* editingHostOrBodyOrRootElement =
|
||||
activeElement && activeElement->IsEditable()
|
||||
? ComputeEditingHost(*activeElement, LimitInBodyElement::No)
|
||||
: ComputeEditingHost(LimitInBodyElement::No);
|
||||
if (MOZ_UNLIKELY(!editingHostOrBodyOrRootElement)) {
|
||||
// If there is no active element nor no selection range in the document,
|
||||
// let's check entire the document as what we do traditionally.
|
||||
editingHostOrBodyOrRootElement = GetRoot();
|
||||
if (!editingHostOrBodyOrRootElement) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
for (nsIContent* childContent = bodyOrDocumentElement->GetFirstChild();
|
||||
for (nsIContent* childContent =
|
||||
editingHostOrBodyOrRootElement->GetFirstChild();
|
||||
childContent; childContent = childContent->GetNextSibling()) {
|
||||
if (!childContent->IsText() || childContent->Length()) {
|
||||
return false;
|
||||
|
|
|
@ -80,7 +80,7 @@
|
|||
ok(true, "nsIEditor.documentIsEmpty should throw an exception when no editing host has focus");
|
||||
}
|
||||
document.querySelector("div[contenteditable]").focus();
|
||||
todo_is(getHTMLEditor().documentIsEmpty, true,
|
||||
is(getHTMLEditor().documentIsEmpty, true,
|
||||
"nsIEditor.documentIsEmpty should be true when editing host does not have contents");
|
||||
|
||||
document.body.innerHTML = "<div contenteditable><br></div>";
|
||||
|
|
|
@ -32,7 +32,15 @@ class EditorTestUtils {
|
|||
|
||||
sendKey(key, modifier) {
|
||||
if (!modifier) {
|
||||
return this.window.test_driver.send_keys(this.editingHost, key)
|
||||
// send_keys requires element in the light DOM.
|
||||
const elementInLightDOM = (e => {
|
||||
const doc = e.ownerDocument;
|
||||
while (e.getRootNode({composed:false}) !== doc) {
|
||||
e = e.getRootNode({composed:false}).host;
|
||||
}
|
||||
return e;
|
||||
})(this.editingHost);
|
||||
return this.window.test_driver.send_keys(elementInLightDOM, key)
|
||||
.catch(() => {
|
||||
return new this.window.test_driver.Actions()
|
||||
.keyDown(key)
|
||||
|
|
|
@ -0,0 +1,81 @@
|
|||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Delete editor in a shadow</title>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="/resources/testdriver.js"></script>
|
||||
<script src="/resources/testdriver-vendor.js"></script>
|
||||
<script src="/resources/testdriver-actions.js"></script>
|
||||
<script src="../include/editor-test-utils.js"></script>
|
||||
<script>
|
||||
"use strict";
|
||||
|
||||
addEventListener("load", () => {
|
||||
const shadowRoot = document.body.attachShadow({mode: "open"});
|
||||
for (const tag of ["input", "textarea"]) {
|
||||
promise_test(async t => {
|
||||
const textControl = document.createElement(tag);
|
||||
textControl.value = "text";
|
||||
shadowRoot.appendChild(textControl);
|
||||
textControl.focus();
|
||||
textControl.selectionStart = textControl.value.length;
|
||||
const utils = new EditorTestUtils(textControl);
|
||||
await utils.sendBackspaceKey();
|
||||
assert_equals(
|
||||
textControl.value,
|
||||
"tex",
|
||||
`Backspace in ${t.name} should delete character before the caret`
|
||||
);
|
||||
textControl.value = "text";
|
||||
textControl.selectionStart = textControl.selectionEnd = 0;
|
||||
await utils.sendDeleteKey();
|
||||
assert_equals(
|
||||
textControl.value,
|
||||
"ext",
|
||||
`Delete in ${t.name} should delete character after the caret`
|
||||
);
|
||||
textControl.value = "text";
|
||||
textControl.select();
|
||||
await utils.sendBackspaceKey();
|
||||
assert_equals(
|
||||
textControl.value,
|
||||
"",
|
||||
`Backspace after selecting all text in ${t.name} should delete all text`
|
||||
);
|
||||
}, `<${tag}> in shadow of the <body>`);
|
||||
}
|
||||
|
||||
promise_test(async t => {
|
||||
const editingHost = document.createElement("div");
|
||||
editingHost.setAttribute("contenteditable", "");
|
||||
shadowRoot.appendChild(editingHost);
|
||||
const utils = new EditorTestUtils(editingHost);
|
||||
utils.setupEditingHost("text[]");
|
||||
await utils.sendBackspaceKey();
|
||||
assert_equals(
|
||||
editingHost.textContent,
|
||||
"tex",
|
||||
`Backspace in ${t.name} should delete character before the caret`
|
||||
);
|
||||
utils.setupEditingHost("[]text");
|
||||
await utils.sendDeleteKey();
|
||||
assert_equals(
|
||||
editingHost.textContent,
|
||||
"ext",
|
||||
`Delete in ${t.name} should delete character after the caret`
|
||||
);
|
||||
utils.setupEditingHost("[text]");
|
||||
await utils.sendBackspaceKey();
|
||||
assert_equals(
|
||||
editingHost.textContent,
|
||||
"",
|
||||
`Backspace after selecting all text in ${t.name} should delete all text`
|
||||
);
|
||||
}, "<div contenteditable> in shadow of the <body>");
|
||||
}, {once: true});
|
||||
</script>
|
||||
</head>
|
||||
<body></body>
|
||||
</html>
|
Загрузка…
Ссылка в новой задаче