Bug 1922149 - Make HTMLEditor::ComputeEditingHostInternal use common ancestor of all selection ranges r=m_kato

Selection ranges can cross editing host boundaries if no editing host has focus.
Therefore, `Selection.focusNode` may be in an editing host but there may be
no active/focused editing host.

The computation may be expensive if there are a lot of ranges and selecting
in slotted shadow tree.  However, it's rare case, so, I think it's okay for
now.

Differential Revision: https://phabricator.services.mozilla.com/D224282
This commit is contained in:
Masayuki Nakano 2024-10-07 23:06:55 +00:00
parent 033db44f68
commit ee7a27d122
3 changed files with 43 additions and 7 deletions

View File

@ -20,6 +20,7 @@
#include "nsComputedDOMStyle.h"
#include "nsDebug.h"
#include "nsError.h"
#include "nsFocusManager.h"
#include "nsGenericHTMLElement.h"
#include "nsGkAtoms.h"
#include "nsAtom.h"
@ -380,7 +381,8 @@ nsresult HTMLEditor::RefreshEditingUI() {
if (editingHost && editingHost->IsContentEditablePlainTextOnly()) {
return NS_OK;
}
MOZ_ASSERT(editingHost == selectionContainerElement->GetEditingHost());
MOZ_ASSERT_IF(editingHost,
editingHost == selectionContainerElement->GetEditingHost());
// what's its tag?
RefPtr<Element> focusElement = std::move(selectionContainerElement);

View File

@ -7033,12 +7033,28 @@ Element* HTMLEditor::ComputeEditingHostInternal(
if (aContent) {
return aContent;
}
// If the selection has focus node, let's look for its editing host because
// selection ranges may be visible for users.
nsIContent* const selectionFocusNode = nsIContent::FromNodeOrNull(
SelectionRef().GetMayCrossShadowBoundaryFocusNode());
if (selectionFocusNode) {
return selectionFocusNode;
// If there are selection ranges, let's look for their common ancestor's
// editing host because selection ranges may be visible for users.
nsIContent* selectionCommonAncestor = nullptr;
for (uint32_t i : IntegerRange(SelectionRef().RangeCount())) {
nsRange* range = SelectionRef().GetRangeAt(i);
MOZ_ASSERT(range);
nsIContent* commonAncestor =
nsIContent::FromNodeOrNull(range->GetCommonAncestorContainer(
IgnoreErrors(), AllowRangeCrossShadowBoundary::Yes));
if (MOZ_UNLIKELY(!commonAncestor)) {
continue;
}
if (!selectionCommonAncestor) {
selectionCommonAncestor = commonAncestor;
} else {
selectionCommonAncestor =
nsContentUtils::GetCommonFlattenedTreeAncestorForSelection(
commonAncestor, selectionCommonAncestor);
}
}
if (selectionCommonAncestor) {
return selectionCommonAncestor;
}
// Otherwise, let's use the focused element in the window.
nsPIDOMWindowInner* const innerWindow = document->GetInnerWindow();

View File

@ -0,0 +1,18 @@
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<script>
"use strict";
document.addEventListener("DOMContentLoaded", () => {
document.execCommand("enableObjectResizing");
document.execCommand("selectAll");
});
</script>
</head>
<body>
<figcaption contenteditable="true">
<canvas>
</canvas></figcaption></body>
</html>