Bug 1939220 - part 3: Make TextFragmentData::BoundaryData not depend on the editing host r=m_kato

Its role is scanning the DOM.  Therefore, it can know which node is an editing
host during a scan.  Therefore, it does not require the editing host parameter.
To fix that, some utility methods of `HTMLEditUtils` needs to work with
non-editable nodes.  Therefore, this patch also updates `HTMLEditUtils`.

Finally, this gets rid of the `NS_ASSERTION`s in the constructor of
`TextFragmentData` because it'll be available with non-editable nodes too.
Although current all callers assume that it works as in an editing host.

Differential Revision: https://phabricator.services.mozilla.com/D232934
This commit is contained in:
Masayuki Nakano
2025-01-19 23:35:35 +00:00
parent bdebf6b629
commit 5a37bc8a95
21 changed files with 213 additions and 260 deletions

View File

@@ -879,6 +879,10 @@ class EditorDOMPointBase final {
return true; return true;
} }
[[nodiscard]] bool IsInContentNodeAndValid() const {
return IsInContentNode() && IsSetAndValid();
}
[[nodiscard]] bool IsInComposedDoc() const { [[nodiscard]] bool IsInComposedDoc() const {
return IsSet() && mParent->IsInComposedDoc(); return IsSet() && mParent->IsInComposedDoc();
} }
@@ -887,6 +891,10 @@ class EditorDOMPointBase final {
return IsInComposedDoc() && IsSetAndValid(); return IsInComposedDoc() && IsSetAndValid();
} }
[[nodiscard]] bool IsInContentNodeAndValidInComposedDoc() const {
return IsInContentNode() && IsSetAndValidInComposedDoc();
}
bool IsStartOfContainer() const { bool IsStartOfContainer() const {
// If we're referring the first point in the container: // If we're referring the first point in the container:
// If mParent is not a container like a text node, mOffset is 0. // If mParent is not a container like a text node, mOffset is 0.

View File

@@ -3362,13 +3362,12 @@ HTMLEditor::DeleteTextAndNormalizeSurroundingWhiteSpaces(
*newCaretPosition.ContainerAs<nsIContent>(), *newCaretPosition.ContainerAs<nsIContent>(),
HTMLEditUtils::ClosestEditableBlockElementOrInlineEditingHost, HTMLEditUtils::ClosestEditableBlockElementOrInlineEditingHost,
BlockInlineCheck::UseComputedDisplayStyle)) { BlockInlineCheck::UseComputedDisplayStyle)) {
Element* editingHost = ComputeEditingHost();
// Try to put caret next to immediately after previous editable leaf. // Try to put caret next to immediately after previous editable leaf.
nsIContent* previousContent = nsIContent* previousContent =
HTMLEditUtils::GetPreviousLeafContentOrPreviousBlockElement( HTMLEditUtils::GetPreviousLeafContentOrPreviousBlockElement(
newCaretPosition, *editableBlockElementOrInlineEditingHost, newCaretPosition, {LeafNodeType::LeafNodeOrNonEditableNode},
{LeafNodeType::LeafNodeOrNonEditableNode}, BlockInlineCheck::UseComputedDisplayStyle,
BlockInlineCheck::UseComputedDisplayStyle, editingHost); editableBlockElementOrInlineEditingHost);
if (previousContent && if (previousContent &&
!HTMLEditUtils::IsBlockElement( !HTMLEditUtils::IsBlockElement(
*previousContent, BlockInlineCheck::UseComputedDisplayStyle)) { *previousContent, BlockInlineCheck::UseComputedDisplayStyle)) {
@@ -3383,10 +3382,9 @@ HTMLEditor::DeleteTextAndNormalizeSurroundingWhiteSpaces(
else if (nsIContent* nextContent = else if (nsIContent* nextContent =
HTMLEditUtils::GetNextLeafContentOrNextBlockElement( HTMLEditUtils::GetNextLeafContentOrNextBlockElement(
newCaretPosition, newCaretPosition,
*editableBlockElementOrInlineEditingHost,
{LeafNodeType::LeafNodeOrNonEditableNode}, {LeafNodeType::LeafNodeOrNonEditableNode},
BlockInlineCheck::UseComputedDisplayStyle, BlockInlineCheck::UseComputedDisplayStyle,
editingHost)) { editableBlockElementOrInlineEditingHost)) {
newCaretPosition = nextContent->IsText() || newCaretPosition = nextContent->IsText() ||
HTMLEditUtils::IsContainerNode(*nextContent) HTMLEditUtils::IsContainerNode(*nextContent)
? EditorDOMPoint(nextContent, 0) ? EditorDOMPoint(nextContent, 0)

View File

@@ -1043,8 +1043,9 @@ Maybe<EditorLineBreakType> HTMLEditUtils::GetUnnecessaryLineBreak(
content = content =
aScanLineBreak == ScanLineBreak::AtEndOfBlock aScanLineBreak == ScanLineBreak::AtEndOfBlock
? HTMLEditUtils::GetPreviousLeafContentOrPreviousBlockElement( ? HTMLEditUtils::GetPreviousLeafContentOrPreviousBlockElement(
*content, aBlockElement, leafNodeOrNonEditableNode, *content, leafNodeOrNonEditableNode,
BlockInlineCheck::UseComputedDisplayStyle) BlockInlineCheck::UseComputedDisplayStyle,
&aBlockElement)
: HTMLEditUtils::GetPreviousContent( : HTMLEditUtils::GetPreviousContent(
*content, onlyPrecedingLine, *content, onlyPrecedingLine,
BlockInlineCheck::UseComputedDisplayStyle, BlockInlineCheck::UseComputedDisplayStyle,
@@ -1118,13 +1119,12 @@ Maybe<EditorLineBreakType> HTMLEditUtils::GetUnnecessaryLineBreak(
BlockInlineCheck::UseComputedDisplayStyle); BlockInlineCheck::UseComputedDisplayStyle);
for (nsIContent* content = for (nsIContent* content =
HTMLEditUtils::GetPreviousLeafContentOrPreviousBlockElement( HTMLEditUtils::GetPreviousLeafContentOrPreviousBlockElement(
*lastLineBreakContent, *blockElement, *lastLineBreakContent, leafNodeOrNonEditableNodeOrChildBlock,
leafNodeOrNonEditableNodeOrChildBlock, BlockInlineCheck::UseComputedDisplayStyle, blockElement);
BlockInlineCheck::UseComputedDisplayStyle);
content; content;
content = HTMLEditUtils::GetPreviousLeafContentOrPreviousBlockElement( content = HTMLEditUtils::GetPreviousLeafContentOrPreviousBlockElement(
*content, *blockElement, leafNodeOrNonEditableNodeOrChildBlock, *content, leafNodeOrNonEditableNodeOrChildBlock,
BlockInlineCheck::UseComputedDisplayStyle)) { BlockInlineCheck::UseComputedDisplayStyle, blockElement)) {
if (HTMLEditUtils::IsBlockElement( if (HTMLEditUtils::IsBlockElement(
*content, BlockInlineCheck::UseComputedDisplayStyle) || *content, BlockInlineCheck::UseComputedDisplayStyle) ||
(content->IsElement() && !content->IsHTMLElement())) { (content->IsElement() && !content->IsHTMLElement())) {
@@ -2223,7 +2223,8 @@ Element* HTMLEditUtils::GetAncestorElement(
MOZ_ASSERT( MOZ_ASSERT(
aAncestorTypes.contains(AncestorType::ClosestBlockElement) || aAncestorTypes.contains(AncestorType::ClosestBlockElement) ||
aAncestorTypes.contains(AncestorType::MostDistantInlineElementInBlock) || aAncestorTypes.contains(AncestorType::MostDistantInlineElementInBlock) ||
aAncestorTypes.contains(AncestorType::ButtonElement)); aAncestorTypes.contains(AncestorType::ButtonElement) ||
aAncestorTypes.contains(AncestorType::AllowRootOrAncestorLimiterElement));
if (&aContent == aAncestorLimiter) { if (&aContent == aAncestorLimiter) {
return nullptr; return nullptr;
@@ -2242,7 +2243,12 @@ Element* HTMLEditUtils::GetAncestorElement(
aAncestorTypes.contains(AncestorType::IgnoreHRElement); aAncestorTypes.contains(AncestorType::IgnoreHRElement);
const bool lookingForButtonElement = const bool lookingForButtonElement =
aAncestorTypes.contains(AncestorType::ButtonElement); aAncestorTypes.contains(AncestorType::ButtonElement);
const bool lookingForAnyElement =
aAncestorTypes.contains(AncestorType::AllowRootOrAncestorLimiterElement);
auto IsSearchingElementType = [&](const nsIContent& aContent) -> bool { auto IsSearchingElementType = [&](const nsIContent& aContent) -> bool {
if (lookingForAnyElement) {
return aContent.IsElement();
}
if (!aContent.IsElement() || if (!aContent.IsElement() ||
(ignoreHRElement && aContent.IsHTMLElement(nsGkAtoms::hr))) { (ignoreHRElement && aContent.IsHTMLElement(nsGkAtoms::hr))) {
return false; return false;
@@ -2267,6 +2273,9 @@ Element* HTMLEditUtils::GetAncestorElement(
} }
if (ignoreHRElement && element->IsHTMLElement(nsGkAtoms::hr)) { if (ignoreHRElement && element->IsHTMLElement(nsGkAtoms::hr)) {
if (element == aAncestorLimiter) { if (element == aAncestorLimiter) {
if (lookingForAnyElement) {
lastAncestorElement = element;
}
break; break;
} }
continue; continue;
@@ -2285,6 +2294,9 @@ Element* HTMLEditUtils::GetAncestorElement(
} }
if (element == aAncestorLimiter || element == theBodyElement || if (element == aAncestorLimiter || element == theBodyElement ||
element == theDocumentElement) { element == theDocumentElement) {
if (lookingForAnyElement) {
lastAncestorElement = element;
}
break; break;
} }
lastAncestorElement = element; lastAncestorElement = element;
@@ -2302,7 +2314,8 @@ Element* HTMLEditUtils::GetInclusiveAncestorElement(
MOZ_ASSERT( MOZ_ASSERT(
aAncestorTypes.contains(AncestorType::ClosestBlockElement) || aAncestorTypes.contains(AncestorType::ClosestBlockElement) ||
aAncestorTypes.contains(AncestorType::MostDistantInlineElementInBlock) || aAncestorTypes.contains(AncestorType::MostDistantInlineElementInBlock) ||
aAncestorTypes.contains(AncestorType::ButtonElement)); aAncestorTypes.contains(AncestorType::ButtonElement) ||
aAncestorTypes.contains(AncestorType::AllowRootOrAncestorLimiterElement));
const Element* theBodyElement = aContent.OwnerDoc()->GetBody(); const Element* theBodyElement = aContent.OwnerDoc()->GetBody();
const Element* theDocumentElement = aContent.OwnerDoc()->GetDocumentElement(); const Element* theDocumentElement = aContent.OwnerDoc()->GetDocumentElement();
@@ -2316,7 +2329,12 @@ Element* HTMLEditUtils::GetInclusiveAncestorElement(
aAncestorTypes.contains(AncestorType::ButtonElement); aAncestorTypes.contains(AncestorType::ButtonElement);
const bool ignoreHRElement = const bool ignoreHRElement =
aAncestorTypes.contains(AncestorType::IgnoreHRElement); aAncestorTypes.contains(AncestorType::IgnoreHRElement);
const bool lookingForAnyElement =
aAncestorTypes.contains(AncestorType::AllowRootOrAncestorLimiterElement);
auto IsSearchingElementType = [&](const nsIContent& aContent) -> bool { auto IsSearchingElementType = [&](const nsIContent& aContent) -> bool {
if (lookingForAnyElement) {
return aContent.IsElement();
}
if (!aContent.IsElement() || if (!aContent.IsElement() ||
(ignoreHRElement && aContent.IsHTMLElement(nsGkAtoms::hr))) { (ignoreHRElement && aContent.IsHTMLElement(nsGkAtoms::hr))) {
return false; return false;
@@ -2374,7 +2392,10 @@ Element* HTMLEditUtils::GetInclusiveAncestorElement(
} }
if (&aContent == aAncestorLimiter) { if (&aContent == aAncestorLimiter) {
return nullptr; return aAncestorTypes.contains(
AncestorType::AllowRootOrAncestorLimiterElement)
? Element::FromNode(const_cast<nsIContent&>(aContent))
: nullptr;
} }
return HTMLEditUtils::GetAncestorElement(aContent, aAncestorTypes, return HTMLEditUtils::GetAncestorElement(aContent, aAncestorTypes,

View File

@@ -1294,21 +1294,15 @@ class HTMLEditUtils final {
/** /**
* GetNextLeafContentOrNextBlockElement() returns next leaf content or * GetNextLeafContentOrNextBlockElement() returns next leaf content or
* next block element of aStartContent inside aAncestorLimiter. * next block element of aStartContent inside aAncestorLimiter.
* Note that the result may be a contet outside aCurrentBlock if
* aStartContent equals aCurrentBlock.
* *
* @param aStartContent The start content to scan next content. * @param aStartContent The start content to scan next content.
* @param aCurrentBlock Must be ancestor of aStartContent. Dispite
* the name, inline content is allowed if
* aStartContent is in an inline editing host.
* @param aLeafNodeTypes See LeafNodeType. * @param aLeafNodeTypes See LeafNodeType.
* @param aAncestorLimiter Optional, setting this guarantees the * @param aAncestorLimiter Optional, if you set this, it must be an
* result is in aAncestorLimiter unless * inclusive ancestor of aStartContent.
* aStartContent is not a descendant of this.
*/ */
static nsIContent* GetNextLeafContentOrNextBlockElement( static nsIContent* GetNextLeafContentOrNextBlockElement(
const nsIContent& aStartContent, const nsIContent& aCurrentBlock, const nsIContent& aStartContent, const LeafNodeTypes& aLeafNodeTypes,
const LeafNodeTypes& aLeafNodeTypes, BlockInlineCheck aBlockInlineCheck, BlockInlineCheck aBlockInlineCheck,
const Element* aAncestorLimiter = nullptr) { const Element* aAncestorLimiter = nullptr) {
MOZ_ASSERT_IF( MOZ_ASSERT_IF(
aLeafNodeTypes.contains(LeafNodeType::OnlyEditableLeafNode), aLeafNodeTypes.contains(LeafNodeType::OnlyEditableLeafNode),
@@ -1325,11 +1319,8 @@ class HTMLEditUtils final {
return nullptr; return nullptr;
} }
for (Element* parentElement : aStartContent.AncestorsOfType<Element>()) { for (Element* parentElement : aStartContent.AncestorsOfType<Element>()) {
if (parentElement == &aCurrentBlock) { if (parentElement == aAncestorLimiter ||
return nullptr; HTMLEditUtils::IsBlockElement(*parentElement, aBlockInlineCheck)) {
}
if (parentElement == aAncestorLimiter) {
NS_WARNING("Reached editing host while climbing up the DOM tree");
return nullptr; return nullptr;
} }
nextContent = parentElement->GetNextSibling(); nextContent = parentElement->GetNextSibling();
@@ -1371,8 +1362,7 @@ class HTMLEditUtils final {
template <typename PT, typename CT> template <typename PT, typename CT>
static nsIContent* GetNextLeafContentOrNextBlockElement( static nsIContent* GetNextLeafContentOrNextBlockElement(
const EditorDOMPointBase<PT, CT>& aStartPoint, const EditorDOMPointBase<PT, CT>& aStartPoint,
const nsIContent& aCurrentBlock, const LeafNodeTypes& aLeafNodeTypes, const LeafNodeTypes& aLeafNodeTypes, BlockInlineCheck aBlockInlineCheck,
BlockInlineCheck aBlockInlineCheck,
const Element* aAncestorLimiter = nullptr) { const Element* aAncestorLimiter = nullptr) {
MOZ_ASSERT(aStartPoint.IsSet()); MOZ_ASSERT(aStartPoint.IsSet());
MOZ_ASSERT_IF( MOZ_ASSERT_IF(
@@ -1386,28 +1376,30 @@ class HTMLEditUtils final {
} }
if (aStartPoint.IsInTextNode()) { if (aStartPoint.IsInTextNode()) {
return HTMLEditUtils::GetNextLeafContentOrNextBlockElement( return HTMLEditUtils::GetNextLeafContentOrNextBlockElement(
*aStartPoint.template ContainerAs<Text>(), aCurrentBlock, *aStartPoint.template ContainerAs<Text>(), aLeafNodeTypes,
aLeafNodeTypes, aBlockInlineCheck, aAncestorLimiter); aBlockInlineCheck, aAncestorLimiter);
} }
if (!HTMLEditUtils::IsContainerNode( if (!HTMLEditUtils::IsContainerNode(
*aStartPoint.template ContainerAs<nsIContent>())) { *aStartPoint.template ContainerAs<nsIContent>())) {
return HTMLEditUtils::GetNextLeafContentOrNextBlockElement( return HTMLEditUtils::GetNextLeafContentOrNextBlockElement(
*aStartPoint.template ContainerAs<nsIContent>(), aCurrentBlock, *aStartPoint.template ContainerAs<nsIContent>(), aLeafNodeTypes,
aLeafNodeTypes, aBlockInlineCheck, aAncestorLimiter); aBlockInlineCheck, aAncestorLimiter);
} }
nsCOMPtr<nsIContent> nextContent = aStartPoint.GetChild(); nsCOMPtr<nsIContent> nextContent = aStartPoint.GetChild();
if (!nextContent) { if (!nextContent) {
if (aStartPoint.GetContainer() == &aCurrentBlock) { if (aStartPoint.GetContainer() == aAncestorLimiter ||
HTMLEditUtils::IsBlockElement(
*aStartPoint.template ContainerAs<nsIContent>(),
aBlockInlineCheck)) {
// We are at end of the block. // We are at end of the block.
return nullptr; return nullptr;
} }
// We are at end of non-block container // We are at end of non-block container
return HTMLEditUtils::GetNextLeafContentOrNextBlockElement( return HTMLEditUtils::GetNextLeafContentOrNextBlockElement(
*aStartPoint.template ContainerAs<nsIContent>(), aCurrentBlock, *aStartPoint.template ContainerAs<nsIContent>(), aLeafNodeTypes,
aLeafNodeTypes, IgnoreInsideBlockBoundary(aBlockInlineCheck), IgnoreInsideBlockBoundary(aBlockInlineCheck), aAncestorLimiter);
aAncestorLimiter);
} }
// We have a next node. If it's a block, return it. // We have a next node. If it's a block, return it.
@@ -1435,21 +1427,15 @@ class HTMLEditUtils final {
* GetPreviousLeafContentOrPreviousBlockElement() returns previous leaf * GetPreviousLeafContentOrPreviousBlockElement() returns previous leaf
* content or previous block element of aStartContent inside * content or previous block element of aStartContent inside
* aAncestorLimiter. * aAncestorLimiter.
* Note that the result may be a content outside aCurrentBlock if
* aStartContent equals aCurrentBlock.
* *
* @param aStartContent The start content to scan previous content. * @param aStartContent The start content to scan previous content.
* @param aCurrentBlock Must be ancestor of aStartContent. Despite
* the name, inline content is allowed if
* aStartContent is in an inline editing host.
* @param aLeafNodeTypes See LeafNodeType. * @param aLeafNodeTypes See LeafNodeType.
* @param aAncestorLimiter Optional, setting this guarantees the * @param aAncestorLimiter Optional, if you set this, it must be an
* result is in aAncestorLimiter unless * inclusive ancestor of aStartContent.
* aStartContent is not a descendant of this.
*/ */
static nsIContent* GetPreviousLeafContentOrPreviousBlockElement( static nsIContent* GetPreviousLeafContentOrPreviousBlockElement(
const nsIContent& aStartContent, const nsIContent& aCurrentBlock, const nsIContent& aStartContent, const LeafNodeTypes& aLeafNodeTypes,
const LeafNodeTypes& aLeafNodeTypes, BlockInlineCheck aBlockInlineCheck, BlockInlineCheck aBlockInlineCheck,
const Element* aAncestorLimiter = nullptr) { const Element* aAncestorLimiter = nullptr) {
MOZ_ASSERT_IF( MOZ_ASSERT_IF(
aLeafNodeTypes.contains(LeafNodeType::OnlyEditableLeafNode), aLeafNodeTypes.contains(LeafNodeType::OnlyEditableLeafNode),
@@ -1468,11 +1454,8 @@ class HTMLEditUtils final {
return nullptr; return nullptr;
} }
for (Element* parentElement : aStartContent.AncestorsOfType<Element>()) { for (Element* parentElement : aStartContent.AncestorsOfType<Element>()) {
if (parentElement == &aCurrentBlock) { if (parentElement == aAncestorLimiter ||
return nullptr; HTMLEditUtils::IsBlockElement(*parentElement, aBlockInlineCheck)) {
}
if (parentElement == aAncestorLimiter) {
NS_WARNING("Reached editing host while climbing up the DOM tree");
return nullptr; return nullptr;
} }
previousContent = parentElement->GetPreviousSibling(); previousContent = parentElement->GetPreviousSibling();
@@ -1514,8 +1497,7 @@ class HTMLEditUtils final {
template <typename PT, typename CT> template <typename PT, typename CT>
static nsIContent* GetPreviousLeafContentOrPreviousBlockElement( static nsIContent* GetPreviousLeafContentOrPreviousBlockElement(
const EditorDOMPointBase<PT, CT>& aStartPoint, const EditorDOMPointBase<PT, CT>& aStartPoint,
const nsIContent& aCurrentBlock, const LeafNodeTypes& aLeafNodeTypes, const LeafNodeTypes& aLeafNodeTypes, BlockInlineCheck aBlockInlineCheck,
BlockInlineCheck aBlockInlineCheck,
const Element* aAncestorLimiter = nullptr) { const Element* aAncestorLimiter = nullptr) {
MOZ_ASSERT(aStartPoint.IsSet()); MOZ_ASSERT(aStartPoint.IsSet());
MOZ_ASSERT_IF( MOZ_ASSERT_IF(
@@ -1529,27 +1511,29 @@ class HTMLEditUtils final {
} }
if (aStartPoint.IsInTextNode()) { if (aStartPoint.IsInTextNode()) {
return HTMLEditUtils::GetPreviousLeafContentOrPreviousBlockElement( return HTMLEditUtils::GetPreviousLeafContentOrPreviousBlockElement(
*aStartPoint.template ContainerAs<Text>(), aCurrentBlock, *aStartPoint.template ContainerAs<Text>(), aLeafNodeTypes,
aLeafNodeTypes, aBlockInlineCheck, aAncestorLimiter); aBlockInlineCheck, aAncestorLimiter);
} }
if (!HTMLEditUtils::IsContainerNode( if (!HTMLEditUtils::IsContainerNode(
*aStartPoint.template ContainerAs<nsIContent>())) { *aStartPoint.template ContainerAs<nsIContent>())) {
return HTMLEditUtils::GetPreviousLeafContentOrPreviousBlockElement( return HTMLEditUtils::GetPreviousLeafContentOrPreviousBlockElement(
*aStartPoint.template ContainerAs<nsIContent>(), aCurrentBlock, *aStartPoint.template ContainerAs<nsIContent>(), aLeafNodeTypes,
aLeafNodeTypes, aBlockInlineCheck, aAncestorLimiter); aBlockInlineCheck, aAncestorLimiter);
} }
if (aStartPoint.IsStartOfContainer()) { if (aStartPoint.IsStartOfContainer()) {
if (aStartPoint.GetContainer() == &aCurrentBlock) { if (aStartPoint.GetContainer() == aAncestorLimiter ||
HTMLEditUtils::IsBlockElement(
*aStartPoint.template ContainerAs<nsIContent>(),
aBlockInlineCheck)) {
// We are at start of the block. // We are at start of the block.
return nullptr; return nullptr;
} }
// We are at start of non-block container // We are at start of non-block container
return HTMLEditUtils::GetPreviousLeafContentOrPreviousBlockElement( return HTMLEditUtils::GetPreviousLeafContentOrPreviousBlockElement(
*aStartPoint.template ContainerAs<nsIContent>(), aCurrentBlock, *aStartPoint.template ContainerAs<nsIContent>(), aLeafNodeTypes,
aLeafNodeTypes, IgnoreInsideBlockBoundary(aBlockInlineCheck), IgnoreInsideBlockBoundary(aBlockInlineCheck), aAncestorLimiter);
aAncestorLimiter);
} }
nsCOMPtr<nsIContent> previousContent = nsCOMPtr<nsIContent> previousContent =
@@ -1620,11 +1604,23 @@ class HTMLEditUtils final {
* aAncestorTypes. * aAncestorTypes.
*/ */
enum class AncestorType { enum class AncestorType {
// If there is an ancestor block, it's a limiter of the scan.
ClosestBlockElement, ClosestBlockElement,
// If there is no ancestor block in the range, the topmost inline element is
// a limiter of the scan.
MostDistantInlineElementInBlock, MostDistantInlineElementInBlock,
EditableElement, // Ignore ancestor <hr> elements to check whether a block.
IgnoreHRElement, // Ignore ancestor <hr> element since invalid structure IgnoreHRElement,
// If there is an ancestor <button> element, it's also a limiter of the
// scan.
ButtonElement, ButtonElement,
// The root element of the scan start node or the ancestor limiter may be
// return if there is no proper element.
AllowRootOrAncestorLimiterElement,
// Limit to editable elements. If it reaches an non-editable element,
// return its child element.
EditableElement,
}; };
using AncestorTypes = EnumSet<AncestorType>; using AncestorTypes = EnumSet<AncestorType>;
constexpr static AncestorTypes constexpr static AncestorTypes

View File

@@ -1181,7 +1181,7 @@ nsresult HTMLEditor::MaybeCollapseSelectionAtFirstEditableNode(
// Chromium collapses selection to start of the editing host when this // Chromium collapses selection to start of the editing host when this
// is the last leaf content. So, we don't need special handling here. // is the last leaf content. So, we don't need special handling here.
leafContent = HTMLEditUtils::GetNextLeafContentOrNextBlockElement( leafContent = HTMLEditUtils::GetNextLeafContentOrNextBlockElement(
*leafElement, *editingHost, *leafElement,
{LeafNodeType::LeafNodeOrNonEditableNode, {LeafNodeType::LeafNodeOrNonEditableNode,
LeafNodeType::LeafNodeOrChildBlock}, LeafNodeType::LeafNodeOrChildBlock},
BlockInlineCheck::UseComputedDisplayStyle, editingHost); BlockInlineCheck::UseComputedDisplayStyle, editingHost);
@@ -1207,7 +1207,7 @@ nsresult HTMLEditor::MaybeCollapseSelectionAtFirstEditableNode(
} }
// If it's an invisible text node, keep scanning next leaf. // If it's an invisible text node, keep scanning next leaf.
leafContent = HTMLEditUtils::GetNextLeafContentOrNextBlockElement( leafContent = HTMLEditUtils::GetNextLeafContentOrNextBlockElement(
*leafContent, *editingHost, *leafContent,
{LeafNodeType::LeafNodeOrNonEditableNode, {LeafNodeType::LeafNodeOrNonEditableNode,
LeafNodeType::LeafNodeOrChildBlock}, LeafNodeType::LeafNodeOrChildBlock},
BlockInlineCheck::UseComputedDisplayStyle, editingHost); BlockInlineCheck::UseComputedDisplayStyle, editingHost);
@@ -1254,7 +1254,7 @@ nsresult HTMLEditor::MaybeCollapseSelectionAtFirstEditableNode(
// Otherwise, we must meet an empty block element or a data node like // Otherwise, we must meet an empty block element or a data node like
// comment node. Let's ignore it. // comment node. Let's ignore it.
leafContent = HTMLEditUtils::GetNextLeafContentOrNextBlockElement( leafContent = HTMLEditUtils::GetNextLeafContentOrNextBlockElement(
*leafContent, *editingHost, *leafContent,
{LeafNodeType::LeafNodeOrNonEditableNode, {LeafNodeType::LeafNodeOrNonEditableNode,
LeafNodeType::LeafNodeOrChildBlock}, LeafNodeType::LeafNodeOrChildBlock},
BlockInlineCheck::UseComputedDisplayStyle, editingHost); BlockInlineCheck::UseComputedDisplayStyle, editingHost);

View File

@@ -927,20 +927,13 @@ class MOZ_STACK_CLASS WSRunScanner final {
* this returns the data at aPoint. * this returns the data at aPoint.
* *
* @param aPoint Scan start point. * @param aPoint Scan start point.
* @param aEditableBlockParentOrTopmostEditableInlineElement
* Nearest editable block parent element of
* aPoint if there is. Otherwise, inline editing
* host.
* @param aEditingHost Active editing host.
* @param aNBSPData Optional. If set, this recodes first and last * @param aNBSPData Optional. If set, this recodes first and last
* NBSP positions. * NBSP positions.
*/ */
template <typename EditorDOMPointType> template <typename EditorDOMPointType>
static BoundaryData ScanCollapsibleWhiteSpaceStartFrom( static BoundaryData ScanCollapsibleWhiteSpaceStartFrom(
const EditorDOMPointType& aPoint, const EditorDOMPointType& aPoint, NoBreakingSpaceData* aNBSPData,
const Element& aEditableBlockParentOrTopmostEditableInlineElement, BlockInlineCheck aBlockInlineCheck, const Element& aAncestorLimiter);
const Element* aEditingHost, NoBreakingSpaceData* aNBSPData,
BlockInlineCheck aBlockInlineCheck);
/** /**
* ScanCollapsibleWhiteSpaceEndFrom() returns end boundary data of * ScanCollapsibleWhiteSpaceEndFrom() returns end boundary data of
@@ -949,20 +942,13 @@ class MOZ_STACK_CLASS WSRunScanner final {
* this returns the data at aPoint. * this returns the data at aPoint.
* *
* @param aPoint Scan start point. * @param aPoint Scan start point.
* @param aEditableBlockParentOrTopmostEditableInlineElement
* Nearest editable block parent element of
* aPoint if there is. Otherwise, inline editing
* host.
* @param aEditingHost Active editing host.
* @param aNBSPData Optional. If set, this recodes first and last * @param aNBSPData Optional. If set, this recodes first and last
* NBSP positions. * NBSP positions.
*/ */
template <typename EditorDOMPointType> template <typename EditorDOMPointType>
static BoundaryData ScanCollapsibleWhiteSpaceEndFrom( static BoundaryData ScanCollapsibleWhiteSpaceEndFrom(
const EditorDOMPointType& aPoint, const EditorDOMPointType& aPoint, NoBreakingSpaceData* aNBSPData,
const Element& aEditableBlockParentOrTopmostEditableInlineElement, BlockInlineCheck aBlockInlineCheck, const Element& aAncestorLimiter);
const Element* aEditingHost, NoBreakingSpaceData* aNBSPData,
BlockInlineCheck aBlockInlineCheck);
BoundaryData() = default; BoundaryData() = default;
template <typename EditorDOMPointType> template <typename EditorDOMPointType>

View File

@@ -10,6 +10,7 @@
#include "HTMLEditUtils.h" #include "HTMLEditUtils.h"
#include "mozilla/Assertions.h" #include "mozilla/Assertions.h"
#include "mozilla/dom/AncestorIterator.h"
#include "nsCRT.h" #include "nsCRT.h"
#include "nsDebug.h" #include "nsDebug.h"
@@ -21,6 +22,8 @@ namespace mozilla {
using namespace dom; using namespace dom;
using AncestorType = HTMLEditUtils::AncestorType;
using AncestorTypes = HTMLEditUtils::AncestorTypes;
using LeafNodeType = HTMLEditUtils::LeafNodeType; using LeafNodeType = HTMLEditUtils::LeafNodeType;
template WSRunScanner::TextFragmentData::TextFragmentData( template WSRunScanner::TextFragmentData::TextFragmentData(
@@ -72,58 +75,60 @@ NS_INSTANTIATE_CONST_METHOD_RETURNING_ANY_EDITOR_DOM_POINT(
const EditorDOMPointInText& aPointAtASCIIWhiteSpace, const EditorDOMPointInText& aPointAtASCIIWhiteSpace,
nsIEditor::EDirection aDirectionToDelete); nsIEditor::EDirection aDirectionToDelete);
constexpr static const AncestorTypes kScanAnyRootAncestorTypes = {
// If the point is in a block, we need to scan only in the block
AncestorType::ClosestBlockElement,
// So, we want a root element of the (shadow) tree root element of the
// point
// if there is no parent block
AncestorType::AllowRootOrAncestorLimiterElement,
// Basically, given point shouldn't be a void element, so, ignore
// ancestor
// void elements
AncestorType::IgnoreHRElement};
constexpr static const AncestorTypes kScanEditableRootAncestorTypes = {
// Only editable elements
AncestorType::EditableElement,
// And the others are same as kScanAnyRootAncestorTypes
AncestorType::ClosestBlockElement,
AncestorType::AllowRootOrAncestorLimiterElement,
AncestorType::IgnoreHRElement};
template <typename EditorDOMPointType> template <typename EditorDOMPointType>
WSRunScanner::TextFragmentData::TextFragmentData( WSRunScanner::TextFragmentData::TextFragmentData(
const EditorDOMPointType& aPoint, const Element* aEditingHost, const EditorDOMPointType& aPoint, const Element* aEditingHost,
BlockInlineCheck aBlockInlineCheck) BlockInlineCheck aBlockInlineCheck)
: mEditingHost(aEditingHost), mBlockInlineCheck(aBlockInlineCheck) { : mEditingHost(aEditingHost), mBlockInlineCheck(aBlockInlineCheck) {
if (!aPoint.IsSetAndValid()) { if (NS_WARN_IF(!aPoint.IsInContentNodeAndValidInComposedDoc()) ||
NS_WARNING("aPoint was invalid"); NS_WARN_IF(!aPoint.GetContainerOrContainerParentElement())) {
return; // We don't need to support composing in uncomposed tree.
}
if (!aPoint.IsInContentNode()) {
NS_WARNING("aPoint was in Document or DocumentFragment");
// I.e., we're try to modify outside of root element. We don't need to
// support such odd case because web apps cannot append text nodes as
// direct child of Document node.
return; return;
} }
mScanStartPoint = aPoint.template To<EditorDOMPoint>(); mScanStartPoint = aPoint.template To<EditorDOMPoint>();
NS_ASSERTION( const Element* const
EditorUtils::IsEditableContent(*mScanStartPoint.ContainerAs<nsIContent>(), editableBlockElementOrInlineEditingHostOrNonEditableRootElement =
EditorType::HTML),
"Given content is not editable");
NS_ASSERTION(
mScanStartPoint.ContainerAs<nsIContent>()->GetAsElementOrParentElement(),
"Given content is not an element and an orphan node");
if (NS_WARN_IF(!EditorUtils::IsEditableContent(
*mScanStartPoint.ContainerAs<nsIContent>(), EditorType::HTML))) {
return;
}
const Element* editableBlockElementOrInlineEditingHost =
HTMLEditUtils::GetInclusiveAncestorElement( HTMLEditUtils::GetInclusiveAncestorElement(
*mScanStartPoint.ContainerAs<nsIContent>(), *mScanStartPoint.ContainerAs<nsIContent>(),
HTMLEditUtils::ClosestEditableBlockElementOrInlineEditingHost, HTMLEditUtils::IsSimplyEditableNode(
*mScanStartPoint.ContainerAs<nsIContent>())
? kScanEditableRootAncestorTypes
: kScanAnyRootAncestorTypes,
aBlockInlineCheck); aBlockInlineCheck);
if (!editableBlockElementOrInlineEditingHost) { if (NS_WARN_IF(
NS_WARNING( !editableBlockElementOrInlineEditingHostOrNonEditableRootElement)) {
"HTMLEditUtils::GetInclusiveAncestorElement(HTMLEditUtils::"
"ClosestEditableBlockElementOrInlineEditingHost) couldn't find "
"editing host");
return; return;
} }
mStart = BoundaryData::ScanCollapsibleWhiteSpaceStartFrom( mStart = BoundaryData::ScanCollapsibleWhiteSpaceStartFrom(
mScanStartPoint, *editableBlockElementOrInlineEditingHost, mEditingHost, mScanStartPoint, &mNBSPData, aBlockInlineCheck,
&mNBSPData, aBlockInlineCheck); *editableBlockElementOrInlineEditingHostOrNonEditableRootElement);
MOZ_ASSERT_IF(mStart.IsNonCollapsibleCharacters(), MOZ_ASSERT_IF(mStart.IsNonCollapsibleCharacters(),
!mStart.PointRef().IsPreviousCharPreformattedNewLine()); !mStart.PointRef().IsPreviousCharPreformattedNewLine());
MOZ_ASSERT_IF(mStart.IsPreformattedLineBreak(), MOZ_ASSERT_IF(mStart.IsPreformattedLineBreak(),
mStart.PointRef().IsPreviousCharPreformattedNewLine()); mStart.PointRef().IsPreviousCharPreformattedNewLine());
mEnd = BoundaryData::ScanCollapsibleWhiteSpaceEndFrom( mEnd = BoundaryData::ScanCollapsibleWhiteSpaceEndFrom(
mScanStartPoint, *editableBlockElementOrInlineEditingHost, mEditingHost, mScanStartPoint, &mNBSPData, aBlockInlineCheck,
&mNBSPData, aBlockInlineCheck); *editableBlockElementOrInlineEditingHostOrNonEditableRootElement);
MOZ_ASSERT_IF(mEnd.IsNonCollapsibleCharacters(), MOZ_ASSERT_IF(mEnd.IsNonCollapsibleCharacters(),
!mEnd.PointRef().IsCharPreformattedNewLine()); !mEnd.PointRef().IsCharPreformattedNewLine());
MOZ_ASSERT_IF(mEnd.IsPreformattedLineBreak(), MOZ_ASSERT_IF(mEnd.IsPreformattedLineBreak(),
@@ -196,12 +201,9 @@ Maybe<WSRunScanner::TextFragmentData::BoundaryData> WSRunScanner::
template <typename EditorDOMPointType> template <typename EditorDOMPointType>
WSRunScanner::TextFragmentData::BoundaryData WSRunScanner::TextFragmentData:: WSRunScanner::TextFragmentData::BoundaryData WSRunScanner::TextFragmentData::
BoundaryData::ScanCollapsibleWhiteSpaceStartFrom( BoundaryData::ScanCollapsibleWhiteSpaceStartFrom(
const EditorDOMPointType& aPoint, const EditorDOMPointType& aPoint, NoBreakingSpaceData* aNBSPData,
const Element& aEditableBlockParentOrTopmostEditableInlineElement, BlockInlineCheck aBlockInlineCheck, const Element& aAncestorLimiter) {
const Element* aEditingHost, NoBreakingSpaceData* aNBSPData,
BlockInlineCheck aBlockInlineCheck) {
MOZ_ASSERT(aPoint.IsSetAndValid()); MOZ_ASSERT(aPoint.IsSetAndValid());
MOZ_ASSERT(aEditableBlockParentOrTopmostEditableInlineElement.IsEditable());
if (aPoint.IsInTextNode() && !aPoint.IsStartOfContainer()) { if (aPoint.IsInTextNode() && !aPoint.IsStartOfContainer()) {
Maybe<BoundaryData> startInTextNode = Maybe<BoundaryData> startInTextNode =
@@ -213,26 +215,20 @@ WSRunScanner::TextFragmentData::BoundaryData WSRunScanner::TextFragmentData::
// The text node does not have visible character, let's keep scanning // The text node does not have visible character, let's keep scanning
// preceding nodes. // preceding nodes.
return BoundaryData::ScanCollapsibleWhiteSpaceStartFrom( return BoundaryData::ScanCollapsibleWhiteSpaceStartFrom(
EditorDOMPoint(aPoint.template ContainerAs<Text>(), 0), EditorDOMPoint(aPoint.template ContainerAs<Text>(), 0), aNBSPData,
aEditableBlockParentOrTopmostEditableInlineElement, aEditingHost, aBlockInlineCheck, aAncestorLimiter);
aNBSPData, aBlockInlineCheck);
} }
// Then, we need to check previous leaf node. // Then, we need to check previous leaf node.
nsIContent* previousLeafContentOrBlock = nsIContent* previousLeafContentOrBlock =
HTMLEditUtils::GetPreviousLeafContentOrPreviousBlockElement( HTMLEditUtils::GetPreviousLeafContentOrPreviousBlockElement(
aPoint, aEditableBlockParentOrTopmostEditableInlineElement, aPoint, {LeafNodeType::LeafNodeOrNonEditableNode}, aBlockInlineCheck,
{LeafNodeType::LeafNodeOrNonEditableNode}, aBlockInlineCheck, &aAncestorLimiter);
aEditingHost);
if (!previousLeafContentOrBlock) { if (!previousLeafContentOrBlock) {
// No previous content means that we reached // No previous content means that we reached the aAncestorLimiter boundary.
// aEditableBlockParentOrTopmostEditableInlineElement boundary. return BoundaryData(
return BoundaryData(aPoint, aPoint, const_cast<Element&>(aAncestorLimiter),
const_cast<Element&>( HTMLEditUtils::IsBlockElement(aAncestorLimiter, aBlockInlineCheck)
aEditableBlockParentOrTopmostEditableInlineElement),
HTMLEditUtils::IsBlockElement(
aEditableBlockParentOrTopmostEditableInlineElement,
aBlockInlineCheck)
? WSType::CurrentBlockBoundary ? WSType::CurrentBlockBoundary
: WSType::InlineEditingHostBoundary); : WSType::InlineEditingHostBoundary);
} }
@@ -259,8 +255,7 @@ WSRunScanner::TextFragmentData::BoundaryData WSRunScanner::TextFragmentData::
// looking for the previous one. // looking for the previous one.
return BoundaryData::ScanCollapsibleWhiteSpaceStartFrom( return BoundaryData::ScanCollapsibleWhiteSpaceStartFrom(
EditorDOMPointInText(previousLeafContentOrBlock->AsText(), 0), EditorDOMPointInText(previousLeafContentOrBlock->AsText(), 0),
aEditableBlockParentOrTopmostEditableInlineElement, aEditingHost, aNBSPData, aBlockInlineCheck, aAncestorLimiter);
aNBSPData, aBlockInlineCheck);
} }
Maybe<BoundaryData> startInTextNode = Maybe<BoundaryData> startInTextNode =
@@ -274,9 +269,8 @@ WSRunScanner::TextFragmentData::BoundaryData WSRunScanner::TextFragmentData::
// The text node does not have visible character, let's keep scanning // The text node does not have visible character, let's keep scanning
// preceding nodes. // preceding nodes.
return BoundaryData::ScanCollapsibleWhiteSpaceStartFrom( return BoundaryData::ScanCollapsibleWhiteSpaceStartFrom(
EditorDOMPointInText(previousLeafContentOrBlock->AsText(), 0), EditorDOMPointInText(previousLeafContentOrBlock->AsText(), 0), aNBSPData,
aEditableBlockParentOrTopmostEditableInlineElement, aEditingHost, aBlockInlineCheck, aAncestorLimiter);
aNBSPData, aBlockInlineCheck);
} }
// static // static
@@ -343,12 +337,9 @@ Maybe<WSRunScanner::TextFragmentData::BoundaryData> WSRunScanner::
template <typename EditorDOMPointType> template <typename EditorDOMPointType>
WSRunScanner::TextFragmentData::BoundaryData WSRunScanner::TextFragmentData::BoundaryData
WSRunScanner::TextFragmentData::BoundaryData::ScanCollapsibleWhiteSpaceEndFrom( WSRunScanner::TextFragmentData::BoundaryData::ScanCollapsibleWhiteSpaceEndFrom(
const EditorDOMPointType& aPoint, const EditorDOMPointType& aPoint, NoBreakingSpaceData* aNBSPData,
const Element& aEditableBlockParentOrTopmostEditableInlineElement, BlockInlineCheck aBlockInlineCheck, const Element& aAncestorLimiter) {
const Element* aEditingHost, NoBreakingSpaceData* aNBSPData,
BlockInlineCheck aBlockInlineCheck) {
MOZ_ASSERT(aPoint.IsSetAndValid()); MOZ_ASSERT(aPoint.IsSetAndValid());
MOZ_ASSERT(aEditableBlockParentOrTopmostEditableInlineElement.IsEditable());
if (aPoint.IsInTextNode() && !aPoint.IsEndOfContainer()) { if (aPoint.IsInTextNode() && !aPoint.IsEndOfContainer()) {
Maybe<BoundaryData> endInTextNode = Maybe<BoundaryData> endInTextNode =
@@ -361,25 +352,20 @@ WSRunScanner::TextFragmentData::BoundaryData::ScanCollapsibleWhiteSpaceEndFrom(
// following nodes. // following nodes.
return BoundaryData::ScanCollapsibleWhiteSpaceEndFrom( return BoundaryData::ScanCollapsibleWhiteSpaceEndFrom(
EditorDOMPointInText::AtEndOf(*aPoint.template ContainerAs<Text>()), EditorDOMPointInText::AtEndOf(*aPoint.template ContainerAs<Text>()),
aEditableBlockParentOrTopmostEditableInlineElement, aEditingHost, aNBSPData, aBlockInlineCheck, aAncestorLimiter);
aNBSPData, aBlockInlineCheck);
} }
// Then, we need to check next leaf node. // Then, we need to check next leaf node.
nsIContent* nextLeafContentOrBlock = nsIContent* nextLeafContentOrBlock =
HTMLEditUtils::GetNextLeafContentOrNextBlockElement( HTMLEditUtils::GetNextLeafContentOrNextBlockElement(
aPoint, aEditableBlockParentOrTopmostEditableInlineElement, aPoint, {LeafNodeType::LeafNodeOrNonEditableNode}, aBlockInlineCheck,
{LeafNodeType::LeafNodeOrNonEditableNode}, aBlockInlineCheck, &aAncestorLimiter);
aEditingHost);
if (!nextLeafContentOrBlock) { if (!nextLeafContentOrBlock) {
// No next content means that we reached // No next content means that we reached aAncestorLimiter boundary.
// aEditableBlockParentOrTopmostEditableInlineElement boundary. return BoundaryData(
return BoundaryData(aPoint.template To<EditorDOMPoint>(), aPoint.template To<EditorDOMPoint>(),
const_cast<Element&>( const_cast<Element&>(aAncestorLimiter),
aEditableBlockParentOrTopmostEditableInlineElement), HTMLEditUtils::IsBlockElement(aAncestorLimiter, aBlockInlineCheck)
HTMLEditUtils::IsBlockElement(
aEditableBlockParentOrTopmostEditableInlineElement,
aBlockInlineCheck)
? WSType::CurrentBlockBoundary ? WSType::CurrentBlockBoundary
: WSType::InlineEditingHostBoundary); : WSType::InlineEditingHostBoundary);
} }
@@ -407,9 +393,8 @@ WSRunScanner::TextFragmentData::BoundaryData::ScanCollapsibleWhiteSpaceEndFrom(
// Note that even if the empty text node is preformatted, we should keep // Note that even if the empty text node is preformatted, we should keep
// looking for the next one. // looking for the next one.
return BoundaryData::ScanCollapsibleWhiteSpaceEndFrom( return BoundaryData::ScanCollapsibleWhiteSpaceEndFrom(
EditorDOMPointInText(nextLeafContentOrBlock->AsText(), 0), EditorDOMPointInText(nextLeafContentOrBlock->AsText(), 0), aNBSPData,
aEditableBlockParentOrTopmostEditableInlineElement, aEditingHost, aBlockInlineCheck, aAncestorLimiter);
aNBSPData, aBlockInlineCheck);
} }
Maybe<BoundaryData> endInTextNode = Maybe<BoundaryData> endInTextNode =
@@ -424,8 +409,7 @@ WSRunScanner::TextFragmentData::BoundaryData::ScanCollapsibleWhiteSpaceEndFrom(
// following nodes. // following nodes.
return BoundaryData::ScanCollapsibleWhiteSpaceEndFrom( return BoundaryData::ScanCollapsibleWhiteSpaceEndFrom(
EditorDOMPointInText::AtEndOf(*nextLeafContentOrBlock->AsText()), EditorDOMPointInText::AtEndOf(*nextLeafContentOrBlock->AsText()),
aEditableBlockParentOrTopmostEditableInlineElement, aEditingHost, aNBSPData, aBlockInlineCheck, aAncestorLimiter);
aNBSPData, aBlockInlineCheck);
} }
const EditorDOMRange& const EditorDOMRange&
@@ -837,39 +821,30 @@ WSRunScanner::TextFragmentData::GetInclusiveNextEditableCharPoint(
return EditorDOMPointType(); return EditorDOMPointType();
} }
NS_ASSERTION( const Element* const
EditorUtils::IsEditableContent(*mScanStartPoint.ContainerAs<nsIContent>(), editableBlockElementOrInlineEditingHostOrNonEditableRootElement =
EditorType::HTML), HTMLEditUtils::GetInclusiveAncestorElement(
"Given content is not editable");
NS_ASSERTION(
mScanStartPoint.ContainerAs<nsIContent>()->GetAsElementOrParentElement(),
"Given content is not an element and an orphan node");
nsIContent* editableBlockElementOrInlineEditingHost =
mScanStartPoint.ContainerAs<nsIContent>() &&
EditorUtils::IsEditableContent(
*mScanStartPoint.ContainerAs<nsIContent>(), EditorType::HTML)
? HTMLEditUtils::GetInclusiveAncestorElement(
*mScanStartPoint.ContainerAs<nsIContent>(), *mScanStartPoint.ContainerAs<nsIContent>(),
HTMLEditUtils::ClosestEditableBlockElementOrInlineEditingHost, HTMLEditUtils::IsSimplyEditableNode(
mBlockInlineCheck) *mScanStartPoint.ContainerAs<nsIContent>())
: nullptr; ? kScanEditableRootAncestorTypes
if (NS_WARN_IF(!editableBlockElementOrInlineEditingHost)) { : kScanAnyRootAncestorTypes,
// Meaning that the container of `mScanStartPoint` is not editable. mBlockInlineCheck);
editableBlockElementOrInlineEditingHost = if (NS_WARN_IF(
mScanStartPoint.ContainerAs<nsIContent>(); !editableBlockElementOrInlineEditingHostOrNonEditableRootElement)) {
return EditorDOMPointType();
} }
for (nsIContent* nextContent = for (nsIContent* nextContent =
HTMLEditUtils::GetNextLeafContentOrNextBlockElement( HTMLEditUtils::GetNextLeafContentOrNextBlockElement(
*point.ContainerAs<nsIContent>(), *point.ContainerAs<nsIContent>(),
*editableBlockElementOrInlineEditingHost,
{LeafNodeType::LeafNodeOrNonEditableNode}, mBlockInlineCheck, {LeafNodeType::LeafNodeOrNonEditableNode}, mBlockInlineCheck,
mEditingHost); editableBlockElementOrInlineEditingHostOrNonEditableRootElement);
nextContent; nextContent;
nextContent = HTMLEditUtils::GetNextLeafContentOrNextBlockElement( nextContent = HTMLEditUtils::GetNextLeafContentOrNextBlockElement(
*nextContent, *editableBlockElementOrInlineEditingHost, *nextContent, {LeafNodeType::LeafNodeOrNonEditableNode},
{LeafNodeType::LeafNodeOrNonEditableNode}, mBlockInlineCheck, mBlockInlineCheck,
mEditingHost)) { editableBlockElementOrInlineEditingHostOrNonEditableRootElement)) {
if (!nextContent->IsText() || !nextContent->IsEditable()) { if (!nextContent->IsText() || !nextContent->IsEditable()) {
if (nextContent == GetEndReasonContent()) { if (nextContent == GetEndReasonContent()) {
break; // Reached end of current runs. break; // Reached end of current runs.
@@ -921,40 +896,32 @@ EditorDOMPointType WSRunScanner::TextFragmentData::GetPreviousEditableCharPoint(
return EditorDOMPointType(); return EditorDOMPointType();
} }
NS_ASSERTION( const Element* const
EditorUtils::IsEditableContent(*mScanStartPoint.ContainerAs<nsIContent>(), editableBlockElementOrInlineEditingHostOrNonEditableRootElement =
EditorType::HTML), HTMLEditUtils::GetInclusiveAncestorElement(
"Given content is not editable");
NS_ASSERTION(
mScanStartPoint.ContainerAs<nsIContent>()->GetAsElementOrParentElement(),
"Given content is not an element and an orphan node");
nsIContent* editableBlockElementOrInlineEditingHost =
mScanStartPoint.ContainerAs<nsIContent>() &&
EditorUtils::IsEditableContent(
*mScanStartPoint.ContainerAs<nsIContent>(), EditorType::HTML)
? HTMLEditUtils::GetInclusiveAncestorElement(
*mScanStartPoint.ContainerAs<nsIContent>(), *mScanStartPoint.ContainerAs<nsIContent>(),
HTMLEditUtils::ClosestEditableBlockElementOrInlineEditingHost, HTMLEditUtils::IsSimplyEditableNode(
mBlockInlineCheck) *mScanStartPoint.ContainerAs<nsIContent>())
: nullptr; ? kScanEditableRootAncestorTypes
if (NS_WARN_IF(!editableBlockElementOrInlineEditingHost)) { : kScanAnyRootAncestorTypes,
// Meaning that the container of `mScanStartPoint` is not editable. mBlockInlineCheck);
editableBlockElementOrInlineEditingHost = if (NS_WARN_IF(
mScanStartPoint.ContainerAs<nsIContent>(); !editableBlockElementOrInlineEditingHostOrNonEditableRootElement)) {
return EditorDOMPointType();
} }
for (nsIContent* previousContent = for (
nsIContent* previousContent =
HTMLEditUtils::GetPreviousLeafContentOrPreviousBlockElement( HTMLEditUtils::GetPreviousLeafContentOrPreviousBlockElement(
*point.ContainerAs<nsIContent>(), *point.ContainerAs<nsIContent>(),
*editableBlockElementOrInlineEditingHost,
{LeafNodeType::LeafNodeOrNonEditableNode}, mBlockInlineCheck, {LeafNodeType::LeafNodeOrNonEditableNode}, mBlockInlineCheck,
mEditingHost); editableBlockElementOrInlineEditingHostOrNonEditableRootElement);
previousContent; previousContent;
previousContent = previousContent =
HTMLEditUtils::GetPreviousLeafContentOrPreviousBlockElement( HTMLEditUtils::GetPreviousLeafContentOrPreviousBlockElement(
*previousContent, *editableBlockElementOrInlineEditingHost, *previousContent, {LeafNodeType::LeafNodeOrNonEditableNode},
{LeafNodeType::LeafNodeOrNonEditableNode}, mBlockInlineCheck, mBlockInlineCheck,
mEditingHost)) { editableBlockElementOrInlineEditingHostOrNonEditableRootElement)) {
if (!previousContent->IsText() || !previousContent->IsEditable()) { if (!previousContent->IsText() || !previousContent->IsEditable()) {
if (previousContent == GetStartReasonContent()) { if (previousContent == GetStartReasonContent()) {
break; // Reached start of current runs. break; // Reached start of current runs.

View File

@@ -1,5 +1,4 @@
[insertparagraph-in-non-splittable-element.html] [insertparagraph-in-non-splittable-element.html]
max-asserts: 2
[insertParagraph in iframe of <div><iframe srcdoc="abc"></iframe></div>] [insertParagraph in iframe of <div><iframe srcdoc="abc"></iframe></div>]
expected: FAIL expected: FAIL

View File

@@ -1,6 +1,5 @@
[white-spaces-after-execCommand-delete.tentative.html] [white-spaces-after-execCommand-delete.tentative.html]
prefs: [editor.white_space_normalization.blink_compatible:true] prefs: [editor.white_space_normalization.blink_compatible:true]
max-asserts: 4
[execCommand("delete", false, ""): "a&nbsp;&nbsp;&nbsp;&nbsp;[\]||&nbsp;&nbsp;&nbsp;&nbsp;c"] [execCommand("delete", false, ""): "a&nbsp;&nbsp;&nbsp;&nbsp;[\]||&nbsp;&nbsp;&nbsp;&nbsp;c"]
expected: FAIL expected: FAIL

View File

@@ -1,6 +1,5 @@
[white-spaces-after-execCommand-forwarddelete.tentative.html] [white-spaces-after-execCommand-forwarddelete.tentative.html]
prefs: [editor.white_space_normalization.blink_compatible:true] prefs: [editor.white_space_normalization.blink_compatible:true]
max-asserts: 3
[execCommand("forwarddelete", false, ""): "&nbsp; [\]&nbsp; &nbsp; &nbsp; &nbsp; b" (length of whitespace sequence: 10)] [execCommand("forwarddelete", false, ""): "&nbsp; [\]&nbsp; &nbsp; &nbsp; &nbsp; b" (length of whitespace sequence: 10)]
expected: FAIL expected: FAIL

View File

@@ -156,7 +156,6 @@
[delete.html?2001-3000] [delete.html?2001-3000]
expected: expected:
if (os == "android") and fission: [OK, TIMEOUT] if (os == "android") and fission: [OK, TIMEOUT]
max-asserts: 3
[[["delete",""\]\] "foo<blockquote>[\]bar</blockquote>" compare innerHTML] [[["delete",""\]\] "foo<blockquote>[\]bar</blockquote>" compare innerHTML]
expected: FAIL expected: FAIL

View File

@@ -1,5 +1,5 @@
[inserthorizontalrule.html] [inserthorizontalrule.html]
max-asserts: 8 max-asserts: 8 # in nsTextFrame.cpp
bug: 1909069 bug: 1909069
[[["inserthorizontalrule",""\]\] "<span>foo[</span><span>\]bar</span>" compare innerHTML] [[["inserthorizontalrule",""\]\] "<span>foo[</span><span>\]bar</span>" compare innerHTML]
expected: FAIL expected: FAIL

View File

@@ -1,8 +1,6 @@
[inserthtml.html] [inserthtml.html]
expected: expected:
if (os == "android") and fission: [OK, TIMEOUT] if (os == "android") and fission: [OK, TIMEOUT]
max-asserts: 14
min-asserts: 5
[[["defaultparagraphseparator","div"\],["inserthtml","<li>abc"\]\] "<p>foo[bar\]baz" compare innerHTML] [[["defaultparagraphseparator","div"\],["inserthtml","<li>abc"\]\] "<p>foo[bar\]baz" compare innerHTML]
expected: FAIL expected: FAIL

View File

@@ -1,7 +1,6 @@
[justifycenter.html?1-1000] [justifycenter.html?1-1000]
expected: expected:
if (os == "android") and fission: [OK, TIMEOUT] if (os == "android") and fission: [OK, TIMEOUT]
max-asserts: 8
[[["stylewithcss","false"\],["defaultparagraphseparator","div"\],["justifycenter",""\]\] "foo[\]bar<p>extra" compare innerHTML] [[["stylewithcss","false"\],["defaultparagraphseparator","div"\],["justifycenter",""\]\] "foo[\]bar<p>extra" compare innerHTML]
expected: FAIL expected: FAIL
@@ -114,7 +113,6 @@
[justifycenter.html?1001-2000] [justifycenter.html?1001-2000]
expected: expected:
if (os == "android") and fission: [OK, TIMEOUT] if (os == "android") and fission: [OK, TIMEOUT]
max-asserts: 8
[[["stylewithcss","false"\],["defaultparagraphseparator","p"\],["justifycenter",""\]\] "<center><p>[foo<p>bar\]</center><p>extra" compare innerHTML] [[["stylewithcss","false"\],["defaultparagraphseparator","p"\],["justifycenter",""\]\] "<center><p>[foo<p>bar\]</center><p>extra" compare innerHTML]
expected: FAIL expected: FAIL
@@ -341,7 +339,6 @@
[justifycenter.html?2001-3000] [justifycenter.html?2001-3000]
expected: expected:
if (os == "android") and fission: [OK, TIMEOUT] if (os == "android") and fission: [OK, TIMEOUT]
max-asserts: 8
[[["stylewithcss","true"\],["defaultparagraphseparator","p"\],["justifycenter",""\]\] "<table align=center data-start=0 data-end=1><tbody><tr><td>foo<td>bar<td>baz</table><p>extra" queryCommandValue("defaultparagraphseparator") after] [[["stylewithcss","true"\],["defaultparagraphseparator","p"\],["justifycenter",""\]\] "<table align=center data-start=0 data-end=1><tbody><tr><td>foo<td>bar<td>baz</table><p>extra" queryCommandValue("defaultparagraphseparator") after]
expected: FAIL expected: FAIL
@@ -751,7 +748,6 @@
[justifycenter.html?3001-4000] [justifycenter.html?3001-4000]
expected: expected:
if (os == "android") and fission: [OK, TIMEOUT] if (os == "android") and fission: [OK, TIMEOUT]
max-asserts: 8
[[["stylewithcss","false"\],["defaultparagraphseparator","p"\],["justifycenter",""\]\] "<div style=text-align:justify><p>[foo\]<p>bar</div><p>extra" queryCommandState("justifycenter") after] [[["stylewithcss","false"\],["defaultparagraphseparator","p"\],["justifycenter",""\]\] "<div style=text-align:justify><p>[foo\]<p>bar</div><p>extra" queryCommandState("justifycenter") after]
expected: FAIL expected: FAIL
@@ -900,7 +896,6 @@
[justifycenter.html?4001-5000] [justifycenter.html?4001-5000]
expected: expected:
if (os == "android") and fission: [OK, TIMEOUT] if (os == "android") and fission: [OK, TIMEOUT]
max-asserts: 8
[[["stylewithcss","false"\],["defaultparagraphseparator","div"\],["justifycenter",""\]\] "<div style=text-align:center><p>foo</div><p>[bar\]<div style=text-align:center><p>baz</div><p>extra" queryCommandState("justifycenter") after] [[["stylewithcss","false"\],["defaultparagraphseparator","div"\],["justifycenter",""\]\] "<div style=text-align:center><p>foo</div><p>[bar\]<div style=text-align:center><p>baz</div><p>extra" queryCommandState("justifycenter") after]
expected: FAIL expected: FAIL
@@ -1160,7 +1155,6 @@
[justifycenter.html?5001-6000] [justifycenter.html?5001-6000]
expected: expected:
if (os == "android") and fission: [OK, TIMEOUT] if (os == "android") and fission: [OK, TIMEOUT]
max-asserts: 8
[[["stylewithcss","true"\],["defaultparagraphseparator","div"\],["justifycenter",""\]\] "<div align=left>{<div align=center>foo</div>}</div>" queryCommandState("justifycenter") before] [[["stylewithcss","true"\],["defaultparagraphseparator","div"\],["justifycenter",""\]\] "<div align=left>{<div align=center>foo</div>}</div>" queryCommandState("justifycenter") before]
expected: FAIL expected: FAIL
@@ -1519,7 +1513,6 @@
[justifycenter.html?6001-last] [justifycenter.html?6001-last]
expected: expected:
if (os == "android") and fission: [OK, TIMEOUT] if (os == "android") and fission: [OK, TIMEOUT]
max-asserts: 8
[[["stylewithcss","false"\],["defaultparagraphseparator","p"\],["justifycenter",""\]\] "<div style=text-align:center><p>foo</div> <p>[bar\]</p> <div style=text-align:center><p>baz</div>" compare innerHTML] [[["stylewithcss","false"\],["defaultparagraphseparator","p"\],["justifycenter",""\]\] "<div style=text-align:center><p>foo</div> <p>[bar\]</p> <div style=text-align:center><p>baz</div>" compare innerHTML]
expected: FAIL expected: FAIL

View File

@@ -1,5 +1,4 @@
[justifyfull.html?1-1000] [justifyfull.html?1-1000]
max-asserts: 9
expected: expected:
if (os == "android") and fission: [OK, TIMEOUT] if (os == "android") and fission: [OK, TIMEOUT]
[[["stylewithcss","false"\],["defaultparagraphseparator","div"\],["justifyfull",""\]\] "foo[\]bar<p>extra" compare innerHTML] [[["stylewithcss","false"\],["defaultparagraphseparator","div"\],["justifyfull",""\]\] "foo[\]bar<p>extra" compare innerHTML]

View File

@@ -403,7 +403,6 @@
[justifyleft.html?2001-last] [justifyleft.html?2001-last]
max-asserts: 9
expected: expected:
if (os == "android") and fission: [OK, TIMEOUT] if (os == "android") and fission: [OK, TIMEOUT]
[[["stylewithcss","false"\],["defaultparagraphseparator","p"\],["justifyleft",""\]\] "<div style=text-align:left>[foo<div style=text-align:left contenteditable=false>bar</div>baz\]</div><p>extra" queryCommandValue("defaultparagraphseparator") before] [[["stylewithcss","false"\],["defaultparagraphseparator","p"\],["justifyleft",""\]\] "<div style=text-align:left>[foo<div style=text-align:left contenteditable=false>bar</div>baz\]</div><p>extra" queryCommandValue("defaultparagraphseparator") before]

View File

@@ -1,6 +1,4 @@
[input-events-get-target-ranges-backspace.tentative.html] [input-events-get-target-ranges-backspace.tentative.html]
max-asserts: 2 # An assertion in the constructor of TextFragmentData
min-asserts: 0 # But sometimes not counted correctly
expected: expected:
if (os == "android") and fission: [OK, TIMEOUT] if (os == "android") and fission: [OK, TIMEOUT]
[Alt + Backspace at "<p>abc def[\] ghi</p>"] [Alt + Backspace at "<p>abc def[\] ghi</p>"]

View File

@@ -1,6 +1,4 @@
[input-events-get-target-ranges-forwarddelete.tentative.html] [input-events-get-target-ranges-forwarddelete.tentative.html]
max-asserts: 5
min-asserts: 0
expected: expected:
if (os == "android") and fission: [OK, TIMEOUT] if (os == "android") and fission: [OK, TIMEOUT]
[Alt + Delete at "<p>abc [\]def ghi</p>" - comparing innerHTML] [Alt + Delete at "<p>abc [\]def ghi</p>" - comparing innerHTML]

View File

@@ -1,7 +1,7 @@
[input-events-get-target-ranges-joining-dl-element-and-another-list.tentative.html?Delete] [input-events-get-target-ranges-joining-dl-element-and-another-list.tentative.html?Delete]
expected: expected:
if (os == "android") and fission: [OK, TIMEOUT] if (os == "android") and fission: [OK, TIMEOUT]
max-asserts: 17 max-asserts: 8 # in WhiteSpaceVisibilityKeeper::MergeFirstLineOfRightBlockElementIntoLeftBlockElement
[Delete at "<ul><li>[list-item1</li></ul><dl><dt>}list-item2</dt><dt>list-item3</dt></dl>" - comparing innerHTML] [Delete at "<ul><li>[list-item1</li></ul><dl><dt>}list-item2</dt><dt>list-item3</dt></dl>" - comparing innerHTML]
expected: FAIL expected: FAIL
@@ -280,7 +280,7 @@
[input-events-get-target-ranges-joining-dl-element-and-another-list.tentative.html?Backspace] [input-events-get-target-ranges-joining-dl-element-and-another-list.tentative.html?Backspace]
max-asserts: 17 max-asserts: 8 # in WhiteSpaceVisibilityKeeper::MergeFirstLineOfRightBlockElementIntoLeftBlockElement
expected: expected:
if (os == "android") and fission: [OK, TIMEOUT] if (os == "android") and fission: [OK, TIMEOUT]
[Backspace at "<ul><li>[list-item1</li></ul><dl><dt>list-item2\]</dt><dt>list-item3</dt></dl>"] [Backspace at "<ul><li>[list-item1</li></ul><dl><dt>list-item2\]</dt><dt>list-item3</dt></dl>"]

View File

@@ -1,5 +1,5 @@
[input-events-get-target-ranges-joining-dl-elements.tentative.html?Delete] [input-events-get-target-ranges-joining-dl-elements.tentative.html?Delete]
max-asserts: 41 max-asserts: 24 # in WhiteSpaceVisibilityKeeper::MergeFirstLineOfRightBlockElementIntoLeftBlockElement
[Delete at "<dl><dt>list-item1</dt><dt>[list-item2</dt></dl><dl><dd>}list-item3</dd></dl>"] [Delete at "<dl><dt>list-item1</dt><dt>[list-item2</dt></dl><dl><dd>}list-item3</dd></dl>"]
expected: FAIL expected: FAIL
@@ -830,7 +830,7 @@
[input-events-get-target-ranges-joining-dl-elements.tentative.html?Backspace] [input-events-get-target-ranges-joining-dl-elements.tentative.html?Backspace]
max-asserts: 41 max-asserts: 24 # in WhiteSpaceVisibilityKeeper::MergeFirstLineOfRightBlockElementIntoLeftBlockElement
[Backspace at "<dl><dd>list-item1</dd><dt>list-item2</dt></dl><dl><dd>[\]list-item3</dd><dt>list-item4</dt></dl>"] [Backspace at "<dl><dd>list-item1</dd><dt>list-item2</dt></dl><dl><dd>[\]list-item3</dd><dt>list-item4</dt></dl>"]
expected: FAIL expected: FAIL

View File

@@ -1,5 +1,4 @@
[input-events-get-target-ranges-non-collapsed-selection.tentative.html?Backspace] [input-events-get-target-ranges-non-collapsed-selection.tentative.html?Backspace]
max-asserts: 11
expected: expected:
if (os == "android") and fission: [OK, TIMEOUT] if (os == "android") and fission: [OK, TIMEOUT]
[Backspace at "<p>{abc}<br></p>"] [Backspace at "<p>{abc}<br></p>"]
@@ -13,8 +12,6 @@
[input-events-get-target-ranges-non-collapsed-selection.tentative.html?Delete] [input-events-get-target-ranges-non-collapsed-selection.tentative.html?Delete]
max-asserts: 11
min-asserts: 0
expected: expected:
if (os == "android") and fission: [OK, TIMEOUT] if (os == "android") and fission: [OK, TIMEOUT]
[Delete at "<p>{abc}<br></p>"] [Delete at "<p>{abc}<br></p>"]
@@ -31,7 +28,6 @@
[input-events-get-target-ranges-non-collapsed-selection.tentative.html?TypingA] [input-events-get-target-ranges-non-collapsed-selection.tentative.html?TypingA]
max-asserts: 11
expected: expected:
if (os == "android") and fission: [OK, TIMEOUT] if (os == "android") and fission: [OK, TIMEOUT]
[TypingA at "<p>{abc}<br></p>"] [TypingA at "<p>{abc}<br></p>"]