forked from mirrors/gecko-dev
Bug 1881989 - Make AutoDeleteRangesHandler::ExtendOrShrinkRangeToDelete handle it with the closest editable ancestor block or inline editing host r=m_kato
It's currently handling its job with the closest ancestor block which may be non-editable and editing host which is either inline or block. However, the closest block is required for check whether the range won't be extended outside the closest block of the common ancestor of the range and the range is guaranteed that they are in an editing host. Therefore, it's not required if it's outside the editing host. So, comparisons which check whether a node is either/neither editing host or/nor ancestor block can get same result with comparing with the closest one of the editing host or the closest editable block. Differential Revision: https://phabricator.services.mozilla.com/D202697
This commit is contained in:
parent
20495a80d2
commit
b4501d1622
2 changed files with 92 additions and 24 deletions
|
|
@ -6666,23 +6666,31 @@ HTMLEditor::AutoDeleteRangesHandler::ExtendOrShrinkRangeToDelete(
|
|||
return Err(NS_ERROR_FAILURE);
|
||||
}
|
||||
|
||||
// Look for the common ancestor's block element. It's fine that we get
|
||||
// non-editable block element which is ancestor of inline editing host
|
||||
// because the following code checks editing host too.
|
||||
const Element* const maybeNonEditableBlockElement =
|
||||
HTMLEditUtils::GetInclusiveAncestorElement(
|
||||
*commonAncestor, HTMLEditUtils::ClosestBlockElement,
|
||||
BlockInlineCheck::UseComputedDisplayOutsideStyle);
|
||||
if (NS_WARN_IF(!maybeNonEditableBlockElement)) {
|
||||
// Editing host may be nested and outer one could have focus. Let's use
|
||||
// the closest editing host instead.
|
||||
const RefPtr<Element> closestEditingHost =
|
||||
aHTMLEditor.ComputeEditingHost(*commonAncestor, LimitInBodyElement::No);
|
||||
if (NS_WARN_IF(!closestEditingHost)) {
|
||||
return Err(NS_ERROR_FAILURE);
|
||||
}
|
||||
|
||||
// Look for the common ancestor's block element in the editing host. It's
|
||||
// fine that we get non-editable block element which is ancestor of inline
|
||||
// editing host because the following code checks editing host too.
|
||||
const RefPtr<Element> closestBlockAncestorOrInlineEditingHost = [&]() {
|
||||
// Note that if non-closest editing host has focus, found block may be
|
||||
// non-editable.
|
||||
if (Element* const maybeEditableBlockElement =
|
||||
HTMLEditUtils::GetInclusiveAncestorElement(
|
||||
*commonAncestor, HTMLEditUtils::ClosestBlockElement,
|
||||
BlockInlineCheck::UseComputedDisplayOutsideStyle,
|
||||
closestEditingHost)) {
|
||||
return maybeEditableBlockElement;
|
||||
}
|
||||
return closestEditingHost.get();
|
||||
}();
|
||||
|
||||
// Set up for loops and cache our root element
|
||||
RefPtr<Element> editingHost = aHTMLEditor.ComputeEditingHost();
|
||||
if (NS_WARN_IF(!editingHost)) {
|
||||
return Err(NS_ERROR_FAILURE);
|
||||
}
|
||||
|
||||
// If only one list element is selected, and if the list element is empty,
|
||||
// we should delete only the list element. Or if the list element is not
|
||||
// empty, we should make the list has only one empty list item element.
|
||||
|
|
@ -6712,18 +6720,18 @@ HTMLEditor::AutoDeleteRangesHandler::ExtendOrShrinkRangeToDelete(
|
|||
|
||||
// Find previous visible things before start of selection
|
||||
EditorRawDOMRange rangeToDelete(aRangeToDelete);
|
||||
if (rangeToDelete.StartRef().GetContainer() != maybeNonEditableBlockElement &&
|
||||
rangeToDelete.StartRef().GetContainer() != editingHost) {
|
||||
if (rangeToDelete.StartRef().GetContainer() !=
|
||||
closestBlockAncestorOrInlineEditingHost) {
|
||||
for (;;) {
|
||||
WSScanResult backwardScanFromStartResult =
|
||||
WSRunScanner::ScanPreviousVisibleNodeOrBlockBoundary(
|
||||
editingHost, rangeToDelete.StartRef(),
|
||||
closestEditingHost, rangeToDelete.StartRef(),
|
||||
BlockInlineCheck::UseComputedDisplayOutsideStyle);
|
||||
if (!backwardScanFromStartResult.ReachedCurrentBlockBoundary()) {
|
||||
break;
|
||||
}
|
||||
MOZ_ASSERT(backwardScanFromStartResult.GetContent() ==
|
||||
WSRunScanner(editingHost, rangeToDelete.StartRef(),
|
||||
WSRunScanner(closestEditingHost, rangeToDelete.StartRef(),
|
||||
BlockInlineCheck::UseComputedDisplayOutsideStyle)
|
||||
.GetStartReasonContent());
|
||||
// We want to keep looking up. But stop if we are crossing table
|
||||
|
|
@ -6731,8 +6739,8 @@ HTMLEditor::AutoDeleteRangesHandler::ExtendOrShrinkRangeToDelete(
|
|||
if (HTMLEditUtils::IsAnyTableElement(
|
||||
backwardScanFromStartResult.GetContent()) ||
|
||||
backwardScanFromStartResult.GetContent() ==
|
||||
maybeNonEditableBlockElement ||
|
||||
backwardScanFromStartResult.GetContent() == editingHost) {
|
||||
closestBlockAncestorOrInlineEditingHost ||
|
||||
backwardScanFromStartResult.GetContent() == closestEditingHost) {
|
||||
break;
|
||||
}
|
||||
// Don't cross list element boundary because we don't want to delete list
|
||||
|
|
@ -6767,11 +6775,11 @@ HTMLEditor::AutoDeleteRangesHandler::ExtendOrShrinkRangeToDelete(
|
|||
|
||||
// Find next visible things after end of selection
|
||||
EditorDOMPoint atFirstInvisibleBRElement;
|
||||
if (rangeToDelete.EndRef().GetContainer() != maybeNonEditableBlockElement &&
|
||||
rangeToDelete.EndRef().GetContainer() != editingHost) {
|
||||
if (rangeToDelete.EndRef().GetContainer() !=
|
||||
closestBlockAncestorOrInlineEditingHost) {
|
||||
for (;;) {
|
||||
WSRunScanner wsScannerAtEnd(
|
||||
editingHost, rangeToDelete.EndRef(),
|
||||
closestEditingHost, rangeToDelete.EndRef(),
|
||||
BlockInlineCheck::UseComputedDisplayOutsideStyle);
|
||||
WSScanResult forwardScanFromEndResult =
|
||||
wsScannerAtEnd.ScanNextVisibleNodeOrBlockBoundaryFrom(
|
||||
|
|
@ -6806,8 +6814,7 @@ HTMLEditor::AutoDeleteRangesHandler::ExtendOrShrinkRangeToDelete(
|
|||
if (HTMLEditUtils::IsAnyTableElement(
|
||||
forwardScanFromEndResult.GetContent()) ||
|
||||
forwardScanFromEndResult.GetContent() ==
|
||||
maybeNonEditableBlockElement ||
|
||||
forwardScanFromEndResult.GetContent() == editingHost) {
|
||||
closestBlockAncestorOrInlineEditingHost) {
|
||||
break;
|
||||
}
|
||||
// Don't cross flex-item/grid-item boundary to make new content inserted
|
||||
|
|
|
|||
|
|
@ -0,0 +1,61 @@
|
|||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Backspace/Delete in inline editing host which is a shadow root</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.firstChild.attachShadow({mode: "open"});
|
||||
const editingHost = document.createElement("span");
|
||||
editingHost.setAttribute("contenteditable", "");
|
||||
shadowRoot.appendChild(editingHost);
|
||||
const utils = new EditorTestUtils(editingHost);
|
||||
|
||||
promise_test(async t => {
|
||||
utils.setupEditingHost("ab[]c");
|
||||
await utils.sendBackspaceKey();
|
||||
assert_equals(
|
||||
editingHost.textContent,
|
||||
"ac"
|
||||
);
|
||||
}, "Backspace at <span contenteditable>ab[]c</span>");
|
||||
|
||||
promise_test(async t => {
|
||||
utils.setupEditingHost("a[]bc");
|
||||
await utils.sendDeleteKey();
|
||||
assert_equals(
|
||||
editingHost.textContent,
|
||||
"ac"
|
||||
);
|
||||
}, "Delete at <span contenteditable>a[]bc</span>");
|
||||
|
||||
promise_test(async t => {
|
||||
utils.setupEditingHost("a[b]c");
|
||||
await utils.sendBackspaceKey();
|
||||
assert_equals(
|
||||
editingHost.textContent,
|
||||
"ac"
|
||||
);
|
||||
}, "Backspace at <span contenteditable>a[b]c</span>");
|
||||
|
||||
promise_test(async t => {
|
||||
utils.setupEditingHost("a[b]c");
|
||||
await utils.sendDeleteKey();
|
||||
assert_equals(
|
||||
editingHost.textContent,
|
||||
"ac"
|
||||
);
|
||||
}, "Delete at <span contenteditable>a[b]c</span>");
|
||||
}, {once: true});
|
||||
</script>
|
||||
</head>
|
||||
<body><div></div></body>
|
||||
</html>
|
||||
Loading…
Reference in a new issue