зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1671197 - Make `HTMLEditor::SelectAllInternal()` select all children of `<body>` element if computed selection root is an ancestor of the `<body>` element r=m_kato
In strictly speaking, we should shrink selection ranges at very first time of edit action handling. However, we support multiple selection ranges and it makes the check cost really expensive, and the code would be really complicated since ranges cannot be overlapped. I.e., changing one range could affect some of the others. Therefore, this patch changes `HTMLEditor::SelectAllInternal()` instead. If computed selection root is an ancestor of `<body>` element in HTML document, it use the `<body>` element instead. Note that, in HTML document, there should be only one `<body>` element and only its content should be editable at least for now. (Note that in XHTML document, no `<body>` is allowed, multiple `<body>` elements allowed.) Differential Revision: https://phabricator.services.mozilla.com/D93712
This commit is contained in:
Родитель
6ecb24d4d2
Коммит
049c973fbf
|
@ -3851,13 +3851,28 @@ nsresult HTMLEditor::SelectAllInternal() {
|
|||
if (anchorContent->HasIndependentSelection()) {
|
||||
SelectionRefPtr()->SetAncestorLimiter(nullptr);
|
||||
rootContent = mRootElement;
|
||||
if (NS_WARN_IF(!rootContent)) {
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
} else {
|
||||
RefPtr<PresShell> presShell = GetPresShell();
|
||||
rootContent = anchorContent->GetSelectionRootContent(presShell);
|
||||
}
|
||||
|
||||
if (NS_WARN_IF(!rootContent)) {
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
if (NS_WARN_IF(!rootContent)) {
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
// If the document is HTML document (not XHTML document), we should
|
||||
// select all children of the `<body>` element instead of `<html>`
|
||||
// element.
|
||||
if (Document* document = GetDocument()) {
|
||||
if (document->IsHTMLDocument()) {
|
||||
if (HTMLBodyElement* bodyElement = document->GetBodyElement()) {
|
||||
if (nsContentUtils::ContentIsFlattenedTreeDescendantOf(bodyElement,
|
||||
rootContent)) {
|
||||
rootContent = bodyElement;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Maybe<Selection::AutoUserInitiated> userSelection;
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
[select-all-and-delete-in-html-element-having-contenteditable.html]
|
||||
[Select All, then, Backspace]
|
||||
expected:
|
||||
if (os == "linux"): FAIL
|
||||
PASS
|
||||
|
||||
[Select All, then, Delete]
|
||||
expected:
|
||||
if (os == "linux"): FAIL
|
||||
PASS
|
||||
|
||||
[Select All, then, execCommand("forwarddelete")]
|
||||
expected:
|
||||
if (os == "linux"): FAIL
|
||||
PASS
|
||||
|
||||
[Select All, then, execCommand("delete")]
|
||||
expected:
|
||||
if (os == "linux"): FAIL
|
||||
PASS
|
||||
|
||||
[getSelection().selectAllChildren(document.documentElement), then, Backspace]
|
||||
expected: FAIL
|
||||
|
||||
[getSelection().selectAllChildren(document.documentElement), then, Delete]
|
||||
expected: FAIL
|
||||
|
||||
[getSelection().selectAllChildren(document.documentElement), then, execCommand("forwarddelete")]
|
||||
expected: FAIL
|
||||
|
||||
[getSelection().selectAllChildren(document.documentElement), then, execCommand("delete")]
|
||||
expected: FAIL
|
||||
|
|
@ -0,0 +1,151 @@
|
|||
<!doctype html>
|
||||
<html contenteditable>
|
||||
<head>
|
||||
<meta charset=utf-8>
|
||||
<title>Test "Select all" and deletion work with <html contenteditable></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>
|
||||
</head>
|
||||
<body>
|
||||
<script>
|
||||
"use strict";
|
||||
|
||||
const kBackspaceKey = "\uE003";
|
||||
const kDeleteKey = "\uE017";
|
||||
const kMeta = "\uE03d";
|
||||
const kControl = "\uE009";
|
||||
|
||||
async function selectAllWithKey(elementToSelectAll) {
|
||||
if (elementToSelectAll.length === 0) {
|
||||
throw "element to select all must not be empty";
|
||||
}
|
||||
getSelection().collapse(elementToSelectAll, 0);
|
||||
try {
|
||||
await new test_driver.Actions()
|
||||
.keyDown(kControl)
|
||||
.keyDown("a")
|
||||
.keyUp("a")
|
||||
.keyUp(kControl)
|
||||
.send();
|
||||
if (!getSelection().isCollapsed) {
|
||||
return;
|
||||
}
|
||||
await new test_driver.Actions()
|
||||
.keyDown(kMeta)
|
||||
.keyDown("a")
|
||||
.keyUp("a")
|
||||
.keyUp(kMeta)
|
||||
.send();
|
||||
if (!getSelection().isCollapsed) {
|
||||
return;
|
||||
}
|
||||
} catch (ex) {
|
||||
throw ex;
|
||||
}
|
||||
throw "Neither Control-A nor Meta-A does not select all contents";
|
||||
}
|
||||
|
||||
function deleteWithBackspaceKey() {
|
||||
return new test_driver.Actions()
|
||||
.keyDown(kBackspaceKey)
|
||||
.keyUp(kBackspaceKey)
|
||||
.send();
|
||||
}
|
||||
|
||||
function deleteWithDeleteKey() {
|
||||
return new test_driver.Actions()
|
||||
.keyDown(kDeleteKey)
|
||||
.keyUp(kDeleteKey)
|
||||
.send();
|
||||
}
|
||||
|
||||
promise_test(async () => {
|
||||
document.body.innerHTML = "abc";
|
||||
await selectAllWithKey(document.body);
|
||||
await deleteWithBackspaceKey();
|
||||
assert_in_array(document.body.innerHTML, ["", "<br>"]);
|
||||
}, "Select All, then, Backspace");
|
||||
|
||||
promise_test(async () => {
|
||||
document.body.innerHTML = "abc";
|
||||
await selectAllWithKey(document.body);
|
||||
await deleteWithDeleteKey();
|
||||
assert_in_array(document.body.innerHTML, ["", "<br>"]);
|
||||
}, "Select All, then, Delete");
|
||||
|
||||
promise_test(async () => {
|
||||
document.body.innerHTML = "abc";
|
||||
document.execCommand("selectall");
|
||||
await deleteWithBackspaceKey();
|
||||
assert_in_array(document.body.innerHTML, ["", "<br>"]);
|
||||
}, 'execCommand("selectall"), then, Backspace');
|
||||
|
||||
promise_test(async () => {
|
||||
document.body.innerHTML = "abc";
|
||||
document.execCommand("selectall");
|
||||
await deleteWithDeleteKey();
|
||||
assert_in_array(document.body.innerHTML, ["", "<br>"]);
|
||||
}, 'execCommand("selectall"), then, Delete');
|
||||
|
||||
promise_test(async () => {
|
||||
document.body.innerHTML = "abc";
|
||||
await selectAllWithKey(document.body);
|
||||
document.execCommand("forwarddelete", false, false);
|
||||
assert_in_array(document.body.innerHTML, ["", "<br>"]);
|
||||
}, 'Select All, then, execCommand("forwarddelete")');
|
||||
|
||||
promise_test(async () => {
|
||||
document.body.innerHTML = "abc";
|
||||
await selectAllWithKey(document.body);
|
||||
document.execCommand("delete", false, false);
|
||||
assert_in_array(document.body.innerHTML, ["", "<br>"]);
|
||||
}, 'Select All, then, execCommand("delete")');
|
||||
|
||||
test(() => {
|
||||
document.body.innerHTML = "abc";
|
||||
document.execCommand("selectall");
|
||||
document.execCommand("forwarddelete", false, false);
|
||||
assert_in_array(document.body.innerHTML, ["", "<br>"]);
|
||||
}, 'execCommand("selectall"), then, execCommand("forwarddelete")');
|
||||
|
||||
test(() => {
|
||||
document.body.innerHTML = "abc";
|
||||
document.execCommand("selectall");
|
||||
document.execCommand("delete", false, false);
|
||||
assert_in_array(document.body.innerHTML, ["", "<br>"]);
|
||||
}, 'execCommand("selectall"), then, execCommand("delete")');
|
||||
|
||||
promise_test(async () => {
|
||||
document.body.innerHTML = "abc";
|
||||
getSelection().selectAllChildren(document.documentElement);
|
||||
await deleteWithBackspaceKey();
|
||||
assert_in_array(document.body.innerHTML, ["", "<br>"]);
|
||||
}, 'getSelection().selectAllChildren(document.documentElement), then, Backspace');
|
||||
|
||||
promise_test(async () => {
|
||||
document.body.innerHTML = "abc";
|
||||
getSelection().selectAllChildren(document.documentElement);
|
||||
await deleteWithDeleteKey();
|
||||
assert_in_array(document.body.innerHTML, ["", "<br>"]);
|
||||
}, 'getSelection().selectAllChildren(document.documentElement), then, Delete');
|
||||
|
||||
test(() => {
|
||||
document.body.innerHTML = "abc";
|
||||
getSelection().selectAllChildren(document.documentElement);
|
||||
document.execCommand("forwarddelete", false, false);
|
||||
assert_in_array(document.body.innerHTML, ["", "<br>"]);
|
||||
}, 'getSelection().selectAllChildren(document.documentElement), then, execCommand("forwarddelete")');
|
||||
|
||||
test(() => {
|
||||
document.body.innerHTML = "abc";
|
||||
getSelection().selectAllChildren(document.documentElement);
|
||||
document.execCommand("delete", false, false);
|
||||
assert_in_array(document.body.innerHTML, ["", "<br>"]);
|
||||
}, 'getSelection().selectAllChildren(document.documentElement), then, execCommand("delete")');
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
Загрузка…
Ссылка в новой задаче