commit b77ea8700fc2dda9e12eb4ae9b3034d9d2a716e7
parent ee38a798b31b620a1414e4f1d89d11f6db2d1b8d
Author: Sandor Molnar <smolnar@mozilla.com>
Date: Wed, 7 Jan 2026 08:12:44 +0200
Revert "Bug 1998077 - part 7: Reimplement `HTMLEditUtils::GetInclusiveDeepestFirstChildWhichHasOneChild` with `LeafNodeOptions` r=m_kato" for causing build bustages @ WSRunScanner.cpp
This reverts commit 5ccf392dce86930b66101bc463f91fd0530915d1.
Revert "Bug 1998077 - part 6: Reimplement `HTMLEditUtils::Get(First|Last)Child()` and `HTMLEditUtils::Get(Next|Previous)Sibling()` with `LeafNodeOptions` r=m_kato"
This reverts commit 12eb928cb50025afa509f0c2a9ff85e6adc37c36.
Revert "Bug 1998077 - part 5: Replace the remaining `HTMLEditUtils::Get(Next|Previous)Content()` with new ones based on `Get(Next|Previous)LeafNodeOr(Next|Previous)BlockElement()` r=m_kato"
This reverts commit 58b4243d101d51384ac4713b5c9916328af95733.
Revert "Bug 1998077 - part 4: Replace some callers of `HTMLEditUtils::Get(Next|Previous)Content()` with `HTMLEditUtils::Get(Next|Previous)LeafContentOr(Next|Previous)BlockElement()` r=m_kato"
This reverts commit dd46967eb4b7cb89e09a713c664548c7123b3fb4.
Revert "Bug 1998077 - part 3: Add new options to ignore empty/invisible leaf nodes r=m_kato"
This reverts commit ca50a4a628a3ea7e8b7fcd8071a48881e3a0846a.
Revert "Bug 1998077 - part 2: Add `WSType::EmptyInlineContainerElement` r=m_kato"
This reverts commit 4a5d93d6315c2809b13c130cd43802ce6716b904.
Revert "Bug 1998077 - part 1: Make `HTMLEditUtils::IsVisibleElementEvenIfLeafNode` work better if primary frame is available r=m_kato"
This reverts commit b42dc9d35fc6d72432d156a76d4ee694e1577973.
Diffstat:
19 files changed, 1528 insertions(+), 2329 deletions(-)
diff --git a/editor/libeditor/AutoClonedRangeArray.cpp b/editor/libeditor/AutoClonedRangeArray.cpp
@@ -35,7 +35,6 @@ namespace mozilla {
using namespace dom;
using EmptyCheckOption = HTMLEditUtils::EmptyCheckOption;
-using LeafNodeOption = HTMLEditUtils::LeafNodeOption;
using ReplaceOrVoidElementOption = HTMLEditUtils::ReplaceOrVoidElementOption;
/******************************************************************************
@@ -438,22 +437,20 @@ GetPointAtFirstContentOfLineOrParentHTMLBlockIfFirstContentOfBlock(
// Look back through any further inline nodes that aren't across a <br>
// from us, and that are enclosed in the same block.
// I.e., looking for start of current hard line.
- for (nsIContent* previousEditableContent =
- HTMLEditUtils::GetPreviousLeafContentOrPreviousBlockElement(
- point,
- {LeafNodeOption::IgnoreNonEditableNode,
- LeafNodeOption::TreatChildBlockAsLeafNode},
- aBlockInlineCheck, &aAncestorLimiter);
+ constexpr HTMLEditUtils::WalkTreeOptions
+ ignoreNonEditableNodeAndStopAtBlockBoundary{
+ HTMLEditUtils::WalkTreeOption::IgnoreNonEditableNode,
+ HTMLEditUtils::WalkTreeOption::StopAtBlockBoundary};
+ for (nsIContent* previousEditableContent = HTMLEditUtils::GetPreviousContent(
+ point, ignoreNonEditableNodeAndStopAtBlockBoundary,
+ aBlockInlineCheck, &aAncestorLimiter);
previousEditableContent && previousEditableContent->GetParentNode() &&
!HTMLEditUtils::IsVisibleBRElement(*previousEditableContent) &&
!HTMLEditUtils::IsBlockElement(*previousEditableContent,
aBlockInlineCheck);
- previousEditableContent =
- HTMLEditUtils::GetPreviousLeafContentOrPreviousBlockElement(
- point,
- {LeafNodeOption::IgnoreNonEditableNode,
- LeafNodeOption::TreatChildBlockAsLeafNode},
- aBlockInlineCheck, &aAncestorLimiter)) {
+ previousEditableContent = HTMLEditUtils::GetPreviousContent(
+ point, ignoreNonEditableNodeAndStopAtBlockBoundary,
+ aBlockInlineCheck, &aAncestorLimiter)) {
EditorDOMPoint atLastPreformattedNewLine =
HTMLEditUtils::GetPreviousPreformattedNewLineInTextNode<EditorDOMPoint>(
EditorRawDOMPoint::AtEndOf(*previousEditableContent));
@@ -467,20 +464,14 @@ GetPointAtFirstContentOfLineOrParentHTMLBlockIfFirstContentOfBlock(
// <br> element. Look up the tree for as long as we are the first node in
// the container (typically, start of nearest block ancestor), and as long
// as we haven't hit the body node.
- for (nsIContent* nearContent =
- HTMLEditUtils::GetPreviousLeafContentOrPreviousBlockElement(
- point,
- {LeafNodeOption::IgnoreNonEditableNode,
- LeafNodeOption::TreatChildBlockAsLeafNode},
- aBlockInlineCheck, &aAncestorLimiter);
+ for (nsIContent* nearContent = HTMLEditUtils::GetPreviousContent(
+ point, ignoreNonEditableNodeAndStopAtBlockBoundary,
+ aBlockInlineCheck, &aAncestorLimiter);
!nearContent && !point.IsContainerHTMLElement(nsGkAtoms::body) &&
point.GetContainerParent();
- nearContent =
- HTMLEditUtils::GetPreviousLeafContentOrPreviousBlockElement(
- point,
- {LeafNodeOption::IgnoreNonEditableNode,
- LeafNodeOption::TreatChildBlockAsLeafNode},
- aBlockInlineCheck, &aAncestorLimiter)) {
+ nearContent = HTMLEditUtils::GetPreviousContent(
+ point, ignoreNonEditableNodeAndStopAtBlockBoundary,
+ aBlockInlineCheck, &aAncestorLimiter)) {
// Don't keep looking up if we have found a blockquote element to act on
// when we handle outdent.
// XXX Sounds like this is hacky. If possible, it should be check in
@@ -610,22 +601,20 @@ GetPointAfterFollowingLineBreakOrAtFollowingHTMLBlock(
// * <div contenteditable>foo[]<b contenteditable="false">bar</b>baz</div>
// Only in the first case, after the caret position isn't wrapped with
// new <div> element.
- for (nsIContent* nextEditableContent =
- HTMLEditUtils::GetNextLeafContentOrNextBlockElement(
- point,
- {LeafNodeOption::IgnoreNonEditableNode,
- LeafNodeOption::TreatChildBlockAsLeafNode},
- aBlockInlineCheck, &aAncestorLimiter);
+ constexpr HTMLEditUtils::WalkTreeOptions
+ ignoreNonEditableNodeAndStopAtBlockBoundary{
+ HTMLEditUtils::WalkTreeOption::IgnoreNonEditableNode,
+ HTMLEditUtils::WalkTreeOption::StopAtBlockBoundary};
+ for (nsIContent* nextEditableContent = HTMLEditUtils::GetNextContent(
+ point, ignoreNonEditableNodeAndStopAtBlockBoundary,
+ aBlockInlineCheck, &aAncestorLimiter);
nextEditableContent &&
!HTMLEditUtils::IsBlockElement(*nextEditableContent,
aBlockInlineCheck) &&
nextEditableContent->GetParent();
- nextEditableContent =
- HTMLEditUtils::GetNextLeafContentOrNextBlockElement(
- point,
- {LeafNodeOption::IgnoreNonEditableNode,
- LeafNodeOption::TreatChildBlockAsLeafNode},
- aBlockInlineCheck, &aAncestorLimiter)) {
+ nextEditableContent = HTMLEditUtils::GetNextContent(
+ point, ignoreNonEditableNodeAndStopAtBlockBoundary,
+ aBlockInlineCheck, &aAncestorLimiter)) {
EditorDOMPoint atFirstPreformattedNewLine =
HTMLEditUtils::GetInclusiveNextPreformattedNewLineInTextNode<
EditorDOMPoint>(EditorRawDOMPoint(nextEditableContent, 0));
@@ -669,18 +658,13 @@ GetPointAfterFollowingLineBreakOrAtFollowingHTMLBlock(
// element. Look up the tree for as long as we are the last node in the
// container (typically, block node), and as long as we haven't hit the body
// node.
- for (nsIContent* nearContent =
- HTMLEditUtils::GetNextLeafContentOrNextBlockElement(
- point,
- {LeafNodeOption::IgnoreNonEditableNode,
- LeafNodeOption::TreatChildBlockAsLeafNode},
- aBlockInlineCheck, &aAncestorLimiter);
+ for (nsIContent* nearContent = HTMLEditUtils::GetNextContent(
+ point, ignoreNonEditableNodeAndStopAtBlockBoundary,
+ aBlockInlineCheck, &aAncestorLimiter);
!nearContent && !point.IsContainerHTMLElement(nsGkAtoms::body) &&
point.GetContainerParent();
- nearContent = HTMLEditUtils::GetNextLeafContentOrNextBlockElement(
- point,
- {LeafNodeOption::IgnoreNonEditableNode,
- LeafNodeOption::TreatChildBlockAsLeafNode},
+ nearContent = HTMLEditUtils::GetNextContent(
+ point, ignoreNonEditableNodeAndStopAtBlockBoundary,
aBlockInlineCheck, &aAncestorLimiter)) {
// Don't walk past the editable section. Note that we need to check before
// walking up to a parent because we need to return the parent object, so
@@ -1061,10 +1045,11 @@ nsresult AutoClonedRangeArray::CollectEditTargetNodes(
if (aOutArrayOfContents.Length() != 1) {
break;
}
- Element* const deepestDivBlockquoteOrListElement =
+ Element* deepestDivBlockquoteOrListElement =
HTMLEditUtils::GetInclusiveDeepestFirstChildWhichHasOneChild(
- aOutArrayOfContents[0], {LeafNodeOption::IgnoreNonEditableNode},
- BlockInlineCheck::Auto, nsGkAtoms::div, nsGkAtoms::blockquote,
+ aOutArrayOfContents[0],
+ {HTMLEditUtils::WalkTreeOption::IgnoreNonEditableNode},
+ BlockInlineCheck::Unused, nsGkAtoms::div, nsGkAtoms::blockquote,
nsGkAtoms::ul, nsGkAtoms::ol, nsGkAtoms::dl);
if (!deepestDivBlockquoteOrListElement) {
break;
diff --git a/editor/libeditor/EditorBase.cpp b/editor/libeditor/EditorBase.cpp
@@ -144,7 +144,9 @@ using namespace dom;
using namespace widget;
using EmptyCheckOption = HTMLEditUtils::EmptyCheckOption;
-using LeafNodeOption = HTMLEditUtils::LeafNodeOption;
+using LeafNodeType = HTMLEditUtils::LeafNodeType;
+using LeafNodeTypes = HTMLEditUtils::LeafNodeTypes;
+using WalkTreeOption = HTMLEditUtils::WalkTreeOption;
static LazyLogModule gEventLog("EditorEvent");
static LazyLogModule gHTMLEditorEditActionStartLog("HTMLEditorEditActionStart");
@@ -4417,19 +4419,14 @@ EditorBase::CreateTransactionForCollapsedRange(
if (aHowToHandleCollapsedRange == HowToHandleCollapsedRange::ExtendBackward &&
point.IsStartOfContainer()) {
MOZ_ASSERT(IsHTMLEditor());
- if (MOZ_UNLIKELY(!point.IsInContentNode())) {
- NS_WARNING("There was no editable content before the collapsed range");
- return nullptr;
- }
// We're backspacing from the beginning of a node. Delete the last thing
// of previous editable content.
- nsIContent* previousEditableContent = HTMLEditUtils::GetPreviousLeafContent(
- *point.ContainerAs<nsIContent>(),
- {LeafNodeOption::IgnoreNonEditableNode},
+ nsIContent* previousEditableContent = HTMLEditUtils::GetPreviousContent(
+ *point.GetContainer(), {WalkTreeOption::IgnoreNonEditableNode},
IsTextEditor() ? BlockInlineCheck::UseHTMLDefaultStyle
: BlockInlineCheck::UseComputedDisplayOutsideStyle,
anonymousDivOrEditingHost);
- if (MOZ_UNLIKELY(!previousEditableContent)) {
+ if (!previousEditableContent) {
NS_WARNING("There was no editable content before the collapsed range");
return nullptr;
}
@@ -4472,19 +4469,14 @@ EditorBase::CreateTransactionForCollapsedRange(
if (aHowToHandleCollapsedRange == HowToHandleCollapsedRange::ExtendForward &&
point.IsEndOfContainer()) {
MOZ_ASSERT(IsHTMLEditor());
- if (MOZ_UNLIKELY(!point.IsInContentNode())) {
- NS_WARNING("There was no editable content after the collapsed range");
- return nullptr;
- }
// We're deleting from the end of a node. Delete the first thing of
// next editable content.
- nsIContent* nextEditableContent = HTMLEditUtils::GetNextLeafContent(
- *point.ContainerAs<nsIContent>(),
- {LeafNodeOption::IgnoreNonEditableNode},
+ nsIContent* nextEditableContent = HTMLEditUtils::GetNextContent(
+ *point.GetContainer(), {WalkTreeOption::IgnoreNonEditableNode},
IsTextEditor() ? BlockInlineCheck::UseHTMLDefaultStyle
: BlockInlineCheck::UseComputedDisplayOutsideStyle,
anonymousDivOrEditingHost);
- if (MOZ_UNLIKELY(!nextEditableContent)) {
+ if (!nextEditableContent) {
NS_WARNING("There was no editable content after the collapsed range");
return nullptr;
}
@@ -4548,12 +4540,12 @@ EditorBase::CreateTransactionForCollapsedRange(
if (IsHTMLEditor()) {
editableContent =
aHowToHandleCollapsedRange == HowToHandleCollapsedRange::ExtendBackward
- ? HTMLEditUtils::GetPreviousLeafContent(
- point, {LeafNodeOption::IgnoreNonEditableNode},
+ ? HTMLEditUtils::GetPreviousContent(
+ point, {WalkTreeOption::IgnoreNonEditableNode},
BlockInlineCheck::UseComputedDisplayOutsideStyle,
anonymousDivOrEditingHost)
- : HTMLEditUtils::GetNextLeafContent(
- point, {LeafNodeOption::IgnoreNonEditableNode},
+ : HTMLEditUtils::GetNextContent(
+ point, {WalkTreeOption::IgnoreNonEditableNode},
BlockInlineCheck::UseComputedDisplayOutsideStyle,
anonymousDivOrEditingHost);
if (!editableContent) {
@@ -4566,12 +4558,12 @@ EditorBase::CreateTransactionForCollapsedRange(
editableContent =
aHowToHandleCollapsedRange ==
HowToHandleCollapsedRange::ExtendBackward
- ? HTMLEditUtils::GetPreviousLeafContent(
- *editableContent, {LeafNodeOption::IgnoreNonEditableNode},
+ ? HTMLEditUtils::GetPreviousContent(
+ *editableContent, {WalkTreeOption::IgnoreNonEditableNode},
BlockInlineCheck::UseComputedDisplayOutsideStyle,
anonymousDivOrEditingHost)
- : HTMLEditUtils::GetNextLeafContent(
- *editableContent, {LeafNodeOption::IgnoreNonEditableNode},
+ : HTMLEditUtils::GetNextContent(
+ *editableContent, {WalkTreeOption::IgnoreNonEditableNode},
BlockInlineCheck::UseComputedDisplayOutsideStyle,
anonymousDivOrEditingHost);
}
diff --git a/editor/libeditor/HTMLEditSubActionHandler.cpp b/editor/libeditor/HTMLEditSubActionHandler.cpp
@@ -78,10 +78,11 @@ extern LazyLogModule gTextInputLog; // Defined in EditorBase.cpp
using namespace dom;
using EmptyCheckOption = HTMLEditUtils::EmptyCheckOption;
using EmptyCheckOptions = HTMLEditUtils::EmptyCheckOptions;
-using LeafNodeOption = HTMLEditUtils::LeafNodeOption;
-using LeafNodeOptions = HTMLEditUtils::LeafNodeOptions;
+using LeafNodeType = HTMLEditUtils::LeafNodeType;
+using LeafNodeTypes = HTMLEditUtils::LeafNodeTypes;
using WalkTextOption = HTMLEditUtils::WalkTextOption;
using WalkTreeDirection = HTMLEditUtils::WalkTreeDirection;
+using WalkTreeOption = HTMLEditUtils::WalkTreeOption;
/********************************************************
* first some helpful functors we will use
@@ -786,7 +787,7 @@ nsresult HTMLEditor::EnsureCaretNotAfterInvisibleBRElement(
return NS_OK;
}
- nsIContent* previousBRElement = HTMLEditUtils::GetPreviousLeafContent(
+ nsIContent* previousBRElement = HTMLEditUtils::GetPreviousContent(
atSelectionStart, {}, BlockInlineCheck::UseComputedDisplayStyle,
&aEditingHost);
if (!previousBRElement || !previousBRElement->IsHTMLElement(nsGkAtoms::br) ||
@@ -925,8 +926,8 @@ nsresult HTMLEditor::ReflectPaddingBRElementForEmptyEditor() {
// here and at redo, or doing it everywhere else that might care. Since undo
// and redo are relatively rare, it makes sense to take the (small)
// performance hit here.
- nsIContent* firstLeafChild =
- HTMLEditUtils::GetFirstLeafContent(*mRootElement, {});
+ nsIContent* firstLeafChild = HTMLEditUtils::GetFirstLeafContent(
+ *mRootElement, {LeafNodeType::OnlyLeafNode});
if (firstLeafChild &&
EditorUtils::IsPaddingBRElementForEmptyEditor(*firstLeafChild)) {
mPaddingBRElementForEmptyEditor =
@@ -2565,8 +2566,7 @@ HTMLEditor::DeleteTextAndNormalizeSurroundingWhiteSpaces(
// Try to put caret next to immediately after previous editable leaf.
nsIContent* previousContent =
HTMLEditUtils::GetPreviousLeafContentOrPreviousBlockElement(
- newCaretPosition,
- {LeafNodeOption::TreatNonEditableNodeAsLeafNode},
+ newCaretPosition, {LeafNodeType::LeafNodeOrNonEditableNode},
BlockInlineCheck::UseComputedDisplayStyle,
editableBlockElementOrInlineEditingHost);
if (previousContent &&
@@ -2584,7 +2584,7 @@ HTMLEditor::DeleteTextAndNormalizeSurroundingWhiteSpaces(
else if (nsIContent* nextContent =
HTMLEditUtils::GetNextLeafContentOrNextBlockElement(
newCaretPosition,
- {LeafNodeOption::TreatNonEditableNodeAsLeafNode},
+ {LeafNodeType::LeafNodeOrNonEditableNode},
BlockInlineCheck::UseComputedDisplayStyle,
editableBlockElementOrInlineEditingHost)) {
if (HTMLEditUtils::IsSimplyEditableNode(*nextContent) &&
@@ -3212,9 +3212,9 @@ HTMLEditor::AutoListElementCreator::WrapContentNodesIntoNewListElements(
// if there is only one node in the array, and it is a list, div, or
// blockquote, then look inside of it until we find inner list or content.
if (aArrayOfContents.Length() == 1) {
- if (Element* const deepestDivBlockquoteOrListElement =
+ if (Element* deepestDivBlockquoteOrListElement =
HTMLEditUtils::GetInclusiveDeepestFirstChildWhichHasOneChild(
- aArrayOfContents[0], {LeafNodeOption::IgnoreNonEditableNode},
+ aArrayOfContents[0], {WalkTreeOption::IgnoreNonEditableNode},
BlockInlineCheck::UseHTMLDefaultStyle, nsGkAtoms::div,
nsGkAtoms::blockquote, nsGkAtoms::ul, nsGkAtoms::ol,
nsGkAtoms::dl)) {
@@ -4184,8 +4184,8 @@ HTMLEditor::FormatBlockContainerWithTransaction(
// If the first editable node after selection is a br, consume it.
// Otherwise it gets pushed into a following block after the split,
// which is visually bad.
- if (nsCOMPtr<nsIContent> brContent = HTMLEditUtils::GetNextLeafContent(
- pointToInsertBlock, {LeafNodeOption::IgnoreNonEditableNode},
+ if (nsCOMPtr<nsIContent> brContent = HTMLEditUtils::GetNextContent(
+ pointToInsertBlock, {WalkTreeOption::IgnoreNonEditableNode},
BlockInlineCheck::UseComputedDisplayOutsideStyle,
&aEditingHost)) {
if (brContent && brContent->IsHTMLElement(nsGkAtoms::br)) {
@@ -4233,13 +4233,11 @@ HTMLEditor::FormatBlockContainerWithTransaction(
}
// We are making a block. Consume a br, if needed.
- if (nsCOMPtr<nsIContent> maybeBRContent =
- HTMLEditUtils::GetNextLeafContentOrNextBlockElement(
- pointToInsertBlock,
- {LeafNodeOption::IgnoreNonEditableNode,
- LeafNodeOption::TreatChildBlockAsLeafNode},
- BlockInlineCheck::UseComputedDisplayOutsideStyle,
- &aEditingHost)) {
+ if (nsCOMPtr<nsIContent> maybeBRContent = HTMLEditUtils::GetNextContent(
+ pointToInsertBlock,
+ {WalkTreeOption::IgnoreNonEditableNode,
+ WalkTreeOption::StopAtBlockBoundary},
+ BlockInlineCheck::UseComputedDisplayOutsideStyle, &aEditingHost)) {
if (maybeBRContent->IsHTMLElement(nsGkAtoms::br)) {
AutoEditorDOMPointChildInvalidator lockOffset(pointToInsertBlock);
nsresult rv = DeleteNodeWithTransaction(*maybeBRContent);
@@ -4429,10 +4427,8 @@ Result<EditorDOMPoint, nsresult> HTMLEditor::IndentListChildWithTransaction(
// same as the parent list element's tag, we can move it to start of the
// sub-list.
if (nsIContent* const nextEditableSibling = HTMLEditUtils::GetNextSibling(
- aContentMovingToSubList,
- {LeafNodeOption::IgnoreInvisibleText,
- LeafNodeOption::IgnoreNonEditableNode},
- BlockInlineCheck::UseComputedDisplayOutsideStyle)) {
+ aContentMovingToSubList, {WalkTreeOption::IgnoreWhiteSpaceOnlyText,
+ WalkTreeOption::IgnoreNonEditableNode})) {
if (HTMLEditUtils::IsListElement(*nextEditableSibling) &&
aPointInListElement.GetContainer()->NodeInfo()->NameAtom() ==
nextEditableSibling->NodeInfo()->NameAtom() &&
@@ -4454,9 +4450,8 @@ Result<EditorDOMPoint, nsresult> HTMLEditor::IndentListChildWithTransaction(
if (const nsCOMPtr<nsIContent> previousEditableSibling =
HTMLEditUtils::GetPreviousSibling(
aContentMovingToSubList,
- {LeafNodeOption::IgnoreInvisibleText,
- LeafNodeOption::IgnoreNonEditableNode},
- BlockInlineCheck::UseComputedDisplayOutsideStyle)) {
+ {WalkTreeOption::IgnoreWhiteSpaceOnlyText,
+ WalkTreeOption::IgnoreNonEditableNode})) {
if (HTMLEditUtils::IsListElement(*previousEditableSibling) &&
aPointInListElement.GetContainer()->NodeInfo()->NameAtom() ==
previousEditableSibling->NodeInfo()->NameAtom() &&
@@ -4479,9 +4474,8 @@ Result<EditorDOMPoint, nsresult> HTMLEditor::IndentListChildWithTransaction(
nsIContent* previousEditableSibling =
*aSubListElement ? HTMLEditUtils::GetPreviousSibling(
aContentMovingToSubList,
- {LeafNodeOption::IgnoreInvisibleText,
- LeafNodeOption::IgnoreNonEditableNode},
- BlockInlineCheck::UseComputedDisplayOutsideStyle)
+ {WalkTreeOption::IgnoreWhiteSpaceOnlyText,
+ WalkTreeOption::IgnoreNonEditableNode})
: nullptr;
if (!*aSubListElement || (previousEditableSibling &&
previousEditableSibling != *aSubListElement)) {
@@ -5187,11 +5181,10 @@ nsresult HTMLEditor::HandleHTMLIndentAroundRanges(
}
// check to see if subListElement is still appropriate. Which it is if
// content is still right after it in the same list.
- nsIContent* const previousEditableSibling =
+ nsIContent* previousEditableSibling =
subListElement
? HTMLEditUtils::GetPreviousSibling(
- *listItem, {LeafNodeOption::IgnoreNonEditableNode},
- BlockInlineCheck::UseComputedDisplayOutsideStyle)
+ *listItem, {WalkTreeOption::IgnoreNonEditableNode})
: nullptr;
if (!subListElement || (previousEditableSibling &&
previousEditableSibling != subListElement)) {
@@ -7105,9 +7098,8 @@ Result<EditorDOMPoint, nsresult> HTMLEditor::AlignBlockContentsWithDivElement(
// XXX I don't understand why we should NOT align non-editable children
// with modifying EDITABLE `<div>` element.
const nsCOMPtr<nsIContent> firstEditableContent =
- HTMLEditUtils::GetFirstChild(
- aBlockElement, {LeafNodeOption::IgnoreNonEditableNode},
- BlockInlineCheck::UseComputedDisplayOutsideStyle);
+ HTMLEditUtils::GetFirstChild(aBlockElement,
+ {WalkTreeOption::IgnoreNonEditableNode});
if (!firstEditableContent) {
// This block has no editable content, nothing to align.
return EditorDOMPoint();
@@ -7116,8 +7108,7 @@ Result<EditorDOMPoint, nsresult> HTMLEditor::AlignBlockContentsWithDivElement(
// If there is only one editable content and it's a `<div>` element,
// just set `align` attribute of it.
const nsCOMPtr<nsIContent> lastEditableContent = HTMLEditUtils::GetLastChild(
- aBlockElement, {LeafNodeOption::IgnoreNonEditableNode},
- BlockInlineCheck::UseComputedDisplayOutsideStyle);
+ aBlockElement, {WalkTreeOption::IgnoreNonEditableNode});
if (firstEditableContent == lastEditableContent &&
firstEditableContent->IsHTMLElement(nsGkAtoms::div)) {
// XXX Chrome uses `style="text-align: foo"` instead of the legacy `align`
@@ -7240,7 +7231,7 @@ HTMLEditor::GetRangeExtendedToHardLineEdgesForBlockEditAction(
// endpoint is just after the close of a block.
if (nsIContent* child = HTMLEditUtils::GetLastLeafContent(
*prevVisibleThingOfEndPoint.ElementPtr(),
- {LeafNodeOption::TreatChildBlockAsLeafNode},
+ {LeafNodeType::LeafNodeOrChildBlock},
BlockInlineCheck::UseHTMLDefaultStyle)) {
newRange.SetEnd(EditorRawDOMPoint::After(*child));
}
@@ -7249,8 +7240,8 @@ HTMLEditor::GetRangeExtendedToHardLineEdgesForBlockEditAction(
prevVisibleThingOfEndPoint
.ReachedInlineEditingHostBoundary()) {
// endpoint is just after start of this block
- if (nsIContent* const child = HTMLEditUtils::GetPreviousLeafContent(
- endPoint, {LeafNodeOption::IgnoreNonEditableNode},
+ if (nsIContent* child = HTMLEditUtils::GetPreviousContent(
+ endPoint, {WalkTreeOption::IgnoreNonEditableNode},
BlockInlineCheck::UseHTMLDefaultStyle, &aEditingHost)) {
newRange.SetEnd(EditorRawDOMPoint::After(*child));
}
@@ -7281,7 +7272,7 @@ HTMLEditor::GetRangeExtendedToHardLineEdgesForBlockEditAction(
// startpoint is just before the start of a block.
if (nsIContent* child = HTMLEditUtils::GetFirstLeafContent(
*nextVisibleThingOfStartPoint.ElementPtr(),
- {LeafNodeOption::TreatChildBlockAsLeafNode},
+ {LeafNodeType::LeafNodeOrChildBlock},
BlockInlineCheck::UseHTMLDefaultStyle)) {
newRange.SetStart(EditorRawDOMPoint(child));
}
@@ -7290,8 +7281,8 @@ HTMLEditor::GetRangeExtendedToHardLineEdgesForBlockEditAction(
nextVisibleThingOfStartPoint
.ReachedInlineEditingHostBoundary()) {
// startpoint is just before end of this block
- if (nsIContent* const child = HTMLEditUtils::GetNextLeafContent(
- startPoint, {LeafNodeOption::IgnoreNonEditableNode},
+ if (nsIContent* child = HTMLEditUtils::GetNextContent(
+ startPoint, {WalkTreeOption::IgnoreNonEditableNode},
BlockInlineCheck::UseHTMLDefaultStyle, &aEditingHost)) {
newRange.SetStart(EditorRawDOMPoint(child));
}
@@ -8325,23 +8316,19 @@ HTMLEditor::InsertElementWithSplittingAncestorsWithTransaction(
if (aBRElementNextToSplitPoint == BRElementNextToSplitPoint::Delete) {
// Consume a trailing br, if any. This is to keep an alignment from
// creating extra lines, if possible.
- if (nsCOMPtr<nsIContent> maybeBRContent =
- HTMLEditUtils::GetNextLeafContentOrNextBlockElement(
- splitPoint,
- {LeafNodeOption::IgnoreNonEditableNode,
- LeafNodeOption::TreatChildBlockAsLeafNode},
- BlockInlineCheck::UseComputedDisplayOutsideStyle,
- &aEditingHost)) {
+ if (nsCOMPtr<nsIContent> maybeBRContent = HTMLEditUtils::GetNextContent(
+ splitPoint,
+ {WalkTreeOption::IgnoreNonEditableNode,
+ WalkTreeOption::StopAtBlockBoundary},
+ BlockInlineCheck::UseComputedDisplayOutsideStyle, &aEditingHost)) {
if (maybeBRContent->IsHTMLElement(nsGkAtoms::br) &&
splitPoint.GetChild()) {
// Making use of html structure... if next node after where we are
// putting our div is not a block, then the br we found is in same
// block we are, so it's safe to consume it.
- if (nsIContent* const nextEditableSibling =
- HTMLEditUtils::GetNextSibling(
- *splitPoint.GetChild(),
- {LeafNodeOption::IgnoreNonEditableNode},
- BlockInlineCheck::UseComputedDisplayOutsideStyle)) {
+ if (nsIContent* nextEditableSibling = HTMLEditUtils::GetNextSibling(
+ *splitPoint.GetChild(),
+ {WalkTreeOption::IgnoreNonEditableNode})) {
if (!HTMLEditUtils::IsBlockElement(
*nextEditableSibling,
BlockInlineCheck::UseComputedDisplayOutsideStyle)) {
@@ -8428,18 +8415,16 @@ nsresult HTMLEditor::JoinNearestEditableNodesWithTransaction(
}
// Remember the last left child, and first right child
- const nsCOMPtr<nsIContent> lastEditableChildOfLeftContent =
- HTMLEditUtils::GetLastChild(
- aNodeLeft, {LeafNodeOption::IgnoreNonEditableNode},
- BlockInlineCheck::UseComputedDisplayOutsideStyle);
+ nsCOMPtr<nsIContent> lastEditableChildOfLeftContent =
+ HTMLEditUtils::GetLastChild(aNodeLeft,
+ {WalkTreeOption::IgnoreNonEditableNode});
if (MOZ_UNLIKELY(NS_WARN_IF(!lastEditableChildOfLeftContent))) {
return NS_ERROR_FAILURE;
}
- const nsCOMPtr<nsIContent> firstEditableChildOfRightContent =
- HTMLEditUtils::GetFirstChild(
- aNodeRight, {LeafNodeOption::IgnoreNonEditableNode},
- BlockInlineCheck::UseComputedDisplayOutsideStyle);
+ nsCOMPtr<nsIContent> firstEditableChildOfRightContent =
+ HTMLEditUtils::GetFirstChild(aNodeRight,
+ {WalkTreeOption::IgnoreNonEditableNode});
if (NS_WARN_IF(!firstEditableChildOfRightContent)) {
return NS_ERROR_FAILURE;
}
@@ -8814,10 +8799,10 @@ void HTMLEditor::SetSelectionInterlinePosition() {
// content is `<br>`, does this do right thing?
if (Element* editingHost = ComputeEditingHost()) {
if (nsIContent* previousEditableContentInBlock =
- HTMLEditUtils::GetPreviousLeafContentOrPreviousBlockElement(
+ HTMLEditUtils::GetPreviousContent(
atCaret,
- {LeafNodeOption::IgnoreNonEditableNode,
- LeafNodeOption::TreatChildBlockAsLeafNode},
+ {WalkTreeOption::IgnoreNonEditableNode,
+ WalkTreeOption::StopAtBlockBoundary},
BlockInlineCheck::UseComputedDisplayStyle, editingHost)) {
if (previousEditableContentInBlock->IsHTMLElement(nsGkAtoms::br)) {
DebugOnly<nsresult> rvIgnored = SelectionRef().SetInterlinePosition(
@@ -8839,10 +8824,9 @@ void HTMLEditor::SetSelectionInterlinePosition() {
// XXX Although I don't understand "interline position", if caret is
// immediately after non-editable contents, but previous editable
// content is a block, does this do right thing?
- if (nsIContent* const previousEditableContentInBlockAtCaret =
+ if (nsIContent* previousEditableContentInBlockAtCaret =
HTMLEditUtils::GetPreviousSibling(
- *atCaret.GetChild(), {LeafNodeOption::IgnoreNonEditableNode},
- BlockInlineCheck::UseComputedDisplayOutsideStyle)) {
+ *atCaret.GetChild(), {WalkTreeOption::IgnoreNonEditableNode})) {
if (HTMLEditUtils::IsBlockElement(
*previousEditableContentInBlockAtCaret,
BlockInlineCheck::UseComputedDisplayStyle)) {
@@ -8859,10 +8843,9 @@ void HTMLEditor::SetSelectionInterlinePosition() {
// XXX Although I don't understand "interline position", if caret is
// immediately before non-editable contents, but next editable
// content is a block, does this do right thing?
- if (nsIContent* const nextEditableContentInBlockAtCaret =
+ if (nsIContent* nextEditableContentInBlockAtCaret =
HTMLEditUtils::GetNextSibling(
- *atCaret.GetChild(), {LeafNodeOption::IgnoreNonEditableNode},
- BlockInlineCheck::UseComputedDisplayOutsideStyle)) {
+ *atCaret.GetChild(), {WalkTreeOption::IgnoreNonEditableNode})) {
if (HTMLEditUtils::IsBlockElement(
*nextEditableContentInBlockAtCaret,
BlockInlineCheck::UseComputedDisplayStyle)) {
@@ -8962,8 +8945,8 @@ nsresult HTMLEditor::AdjustCaretPositionAndEnsurePaddingBRElement(
}
if (nsCOMPtr<nsIContent> previousEditableContent =
- HTMLEditUtils::GetPreviousLeafContent(
- point, {LeafNodeOption::IgnoreNonEditableNode},
+ HTMLEditUtils::GetPreviousContent(
+ point, {WalkTreeOption::IgnoreNonEditableNode},
BlockInlineCheck::UseComputedDisplayStyle, editingHost)) {
// If caret and previous editable content are in same block element
// (even if it's a non-editable element), we should put a padding <br>
@@ -9012,10 +8995,10 @@ nsresult HTMLEditor::AdjustCaretPositionAndEnsurePaddingBRElement(
// If it's a visible `<br>` element and next editable content is a
// padding `<br>` element, we need to set interline position.
else if (nsIContent* nextEditableContentInBlock =
- HTMLEditUtils::GetNextLeafContentOrNextBlockElement(
+ HTMLEditUtils::GetNextContent(
*previousEditableContent,
- {LeafNodeOption::IgnoreNonEditableNode,
- LeafNodeOption::TreatChildBlockAsLeafNode},
+ {WalkTreeOption::IgnoreNonEditableNode,
+ WalkTreeOption::StopAtBlockBoundary},
BlockInlineCheck::UseComputedDisplayStyle,
editingHost)) {
if (EditorUtils::IsPaddingBRElementForEmptyLastLine(
@@ -9036,10 +9019,10 @@ nsresult HTMLEditor::AdjustCaretPositionAndEnsurePaddingBRElement(
// If previous editable content in same block is `<br>`, text node, `<img>`
// or `<hr>`, current caret position is fine.
if (nsIContent* const previousEditableContentInBlock =
- HTMLEditUtils::GetPreviousLeafContentOrPreviousBlockElement(
+ HTMLEditUtils::GetPreviousContent(
point,
- {LeafNodeOption::IgnoreNonEditableNode,
- LeafNodeOption::TreatChildBlockAsLeafNode},
+ {WalkTreeOption::IgnoreNonEditableNode,
+ WalkTreeOption::StopAtBlockBoundary},
BlockInlineCheck::UseComputedDisplayStyle, editingHost)) {
if (previousEditableContentInBlock->IsHTMLElement(nsGkAtoms::br) ||
previousEditableContentInBlock->IsText() ||
@@ -9051,12 +9034,11 @@ nsresult HTMLEditor::AdjustCaretPositionAndEnsurePaddingBRElement(
// If next editable content in same block is `<br>`, text node, `<img>` or
// `<hr>`, current caret position is fine.
- if (nsIContent* nextEditableContentInBlock =
- HTMLEditUtils::GetNextLeafContentOrNextBlockElement(
- point,
- {LeafNodeOption::IgnoreNonEditableNode,
- LeafNodeOption::TreatChildBlockAsLeafNode},
- BlockInlineCheck::UseComputedDisplayStyle, editingHost)) {
+ if (nsIContent* nextEditableContentInBlock = HTMLEditUtils::GetNextContent(
+ point,
+ {WalkTreeOption::IgnoreNonEditableNode,
+ WalkTreeOption::StopAtBlockBoundary},
+ BlockInlineCheck::UseComputedDisplayStyle, editingHost)) {
if (nextEditableContentInBlock->IsText() ||
nextEditableContentInBlock->IsAnyOfHTMLElements(
nsGkAtoms::br, nsGkAtoms::img, nsGkAtoms::hr)) {
@@ -9353,11 +9335,9 @@ nsresult HTMLEditor::LiftUpListItemElement(
// if it's first or last list item, don't need to split the list
// otherwise we do.
const bool isFirstListItem = HTMLEditUtils::IsFirstChild(
- aListItemElement, {LeafNodeOption::IgnoreNonEditableNode},
- BlockInlineCheck::UseComputedDisplayOutsideStyle);
+ aListItemElement, {WalkTreeOption::IgnoreNonEditableNode});
const bool isLastListItem = HTMLEditUtils::IsLastChild(
- aListItemElement, {LeafNodeOption::IgnoreNonEditableNode},
- BlockInlineCheck::UseComputedDisplayOutsideStyle);
+ aListItemElement, {WalkTreeOption::IgnoreNonEditableNode});
Element* leftListElement = aListItemElement.GetParentElement();
if (NS_WARN_IF(!leftListElement)) {
@@ -9939,9 +9919,8 @@ HTMLEditor::EnsureHardLineBeginsWithFirstChildOf(
Element& aRemovingContainerElement) {
MOZ_ASSERT(IsEditActionDataAvailable());
- nsIContent* const firstEditableChild = HTMLEditUtils::GetFirstChild(
- aRemovingContainerElement, {LeafNodeOption::IgnoreNonEditableNode},
- BlockInlineCheck::UseComputedDisplayOutsideStyle);
+ nsIContent* firstEditableChild = HTMLEditUtils::GetFirstChild(
+ aRemovingContainerElement, {WalkTreeOption::IgnoreNonEditableNode});
if (!firstEditableChild) {
return CreateElementResult::NotHandled();
}
@@ -9952,9 +9931,8 @@ HTMLEditor::EnsureHardLineBeginsWithFirstChildOf(
return CreateElementResult::NotHandled();
}
- nsIContent* const previousEditableContent = HTMLEditUtils::GetPreviousSibling(
- aRemovingContainerElement, {LeafNodeOption::IgnoreNonEditableNode},
- BlockInlineCheck::UseComputedDisplayOutsideStyle);
+ nsIContent* previousEditableContent = HTMLEditUtils::GetPreviousSibling(
+ aRemovingContainerElement, {WalkTreeOption::IgnoreNonEditableNode});
if (!previousEditableContent) {
return CreateElementResult::NotHandled();
}
@@ -9986,9 +9964,8 @@ HTMLEditor::EnsureHardLineEndsWithLastChildOf(
Element& aRemovingContainerElement) {
MOZ_ASSERT(IsEditActionDataAvailable());
- nsIContent* const firstEditableContent = HTMLEditUtils::GetLastChild(
- aRemovingContainerElement, {LeafNodeOption::IgnoreNonEditableNode},
- BlockInlineCheck::UseComputedDisplayOutsideStyle);
+ nsIContent* firstEditableContent = HTMLEditUtils::GetLastChild(
+ aRemovingContainerElement, {WalkTreeOption::IgnoreNonEditableNode});
if (!firstEditableContent) {
return CreateElementResult::NotHandled();
}
@@ -9999,9 +9976,8 @@ HTMLEditor::EnsureHardLineEndsWithLastChildOf(
return CreateElementResult::NotHandled();
}
- nsIContent* const nextEditableContent = HTMLEditUtils::GetPreviousSibling(
- aRemovingContainerElement, {LeafNodeOption::IgnoreNonEditableNode},
- BlockInlineCheck::UseComputedDisplayOutsideStyle);
+ nsIContent* nextEditableContent = HTMLEditUtils::GetPreviousSibling(
+ aRemovingContainerElement, {WalkTreeOption::IgnoreNonEditableNode});
if (!nextEditableContent) {
return CreateElementResult::NotHandled();
}
@@ -10499,11 +10475,10 @@ nsresult HTMLEditor::MoveSelectedContentsToDivElementToMakeItAbsolutePosition(
// list element in the target `<div>` element for the destination.
// Therefore, duplicate same list element into the target `<div>`
// element.
- nsIContent* const previousEditableContent =
+ nsIContent* previousEditableContent =
createdListElement
? HTMLEditUtils::GetPreviousSibling(
- content, {LeafNodeOption::IgnoreNonEditableNode},
- BlockInlineCheck::UseComputedDisplayOutsideStyle)
+ content, {WalkTreeOption::IgnoreNonEditableNode})
: nullptr;
if (!createdListElement ||
(previousEditableContent &&
@@ -10607,11 +10582,10 @@ nsresult HTMLEditor::MoveSelectedContentsToDivElementToMakeItAbsolutePosition(
}
// If we cannot move the list item element into created list element,
// we need another list element in the target `<div>` element.
- nsIContent* const previousEditableContent =
+ nsIContent* previousEditableContent =
createdListElement
? HTMLEditUtils::GetPreviousSibling(
- *listItemElement, {LeafNodeOption::IgnoreNonEditableNode},
- BlockInlineCheck::UseComputedDisplayOutsideStyle)
+ *listItemElement, {WalkTreeOption::IgnoreNonEditableNode})
: nullptr;
if (!createdListElement ||
(previousEditableContent &&
diff --git a/editor/libeditor/HTMLEditUtils.cpp b/editor/libeditor/HTMLEditUtils.cpp
@@ -57,35 +57,30 @@ namespace mozilla {
using namespace dom;
using EditorType = EditorBase::EditorType;
-template nsIContent* HTMLEditUtils::GetNextLeafContentOrNextBlockElementImpl(
- const EditorDOMPoint&, StopAtBlockSibling, const LeafNodeOptions&,
- BlockInlineCheck, const Element*);
-template nsIContent* HTMLEditUtils::GetNextLeafContentOrNextBlockElementImpl(
- const EditorRawDOMPoint&, StopAtBlockSibling, const LeafNodeOptions&,
- BlockInlineCheck, const Element*);
-template nsIContent* HTMLEditUtils::GetNextLeafContentOrNextBlockElementImpl(
- const EditorDOMPointInText&, StopAtBlockSibling, const LeafNodeOptions&,
- BlockInlineCheck, const Element*);
-template nsIContent* HTMLEditUtils::GetNextLeafContentOrNextBlockElementImpl(
- const EditorRawDOMPointInText&, StopAtBlockSibling, const LeafNodeOptions&,
- BlockInlineCheck, const Element*);
-
-template nsIContent*
-HTMLEditUtils::GetPreviousLeafContentOrPreviousBlockElementImpl(
- const EditorDOMPoint&, StopAtBlockSibling, const LeafNodeOptions&,
- BlockInlineCheck, const Element*);
-template nsIContent*
-HTMLEditUtils::GetPreviousLeafContentOrPreviousBlockElementImpl(
- const EditorRawDOMPoint&, StopAtBlockSibling, const LeafNodeOptions&,
- BlockInlineCheck, const Element*);
-template nsIContent*
-HTMLEditUtils::GetPreviousLeafContentOrPreviousBlockElementImpl(
- const EditorDOMPointInText&, StopAtBlockSibling, const LeafNodeOptions&,
- BlockInlineCheck, const Element*);
-template nsIContent*
-HTMLEditUtils::GetPreviousLeafContentOrPreviousBlockElementImpl(
- const EditorRawDOMPointInText&, StopAtBlockSibling, const LeafNodeOptions&,
- BlockInlineCheck, const Element*);
+template nsIContent* HTMLEditUtils::GetPreviousContent(
+ const EditorDOMPoint& aPoint, const WalkTreeOptions& aOptions,
+ BlockInlineCheck aBlockInlineCheck, const Element* aAncestorLimiter);
+template nsIContent* HTMLEditUtils::GetPreviousContent(
+ const EditorRawDOMPoint& aPoint, const WalkTreeOptions& aOptions,
+ BlockInlineCheck aBlockInlineCheck, const Element* aAncestorLimiter);
+template nsIContent* HTMLEditUtils::GetPreviousContent(
+ const EditorDOMPointInText& aPoint, const WalkTreeOptions& aOptions,
+ BlockInlineCheck aBlockInlineCheck, const Element* aAncestorLimiter);
+template nsIContent* HTMLEditUtils::GetPreviousContent(
+ const EditorRawDOMPointInText& aPoint, const WalkTreeOptions& aOptions,
+ BlockInlineCheck aBlockInlineCheck, const Element* aAncestorLimiter);
+template nsIContent* HTMLEditUtils::GetNextContent(
+ const EditorDOMPoint& aPoint, const WalkTreeOptions& aOptions,
+ BlockInlineCheck aBlockInlineCheck, const Element* aAncestorLimiter);
+template nsIContent* HTMLEditUtils::GetNextContent(
+ const EditorRawDOMPoint& aPoint, const WalkTreeOptions& aOptions,
+ BlockInlineCheck aBlockInlineCheck, const Element* aAncestorLimiter);
+template nsIContent* HTMLEditUtils::GetNextContent(
+ const EditorDOMPointInText& aPoint, const WalkTreeOptions& aOptions,
+ BlockInlineCheck aBlockInlineCheck, const Element* aAncestorLimiter);
+template nsIContent* HTMLEditUtils::GetNextContent(
+ const EditorRawDOMPointInText& aPoint, const WalkTreeOptions& aOptions,
+ BlockInlineCheck aBlockInlineCheck, const Element* aAncestorLimiter);
template EditorDOMPoint HTMLEditUtils::GetPreviousEditablePoint(
nsIContent& aContent, const Element* aAncestorLimiter,
@@ -471,8 +466,7 @@ bool HTMLEditUtils::IsFlexOrGridItem(const nsIContent& aContent) {
}
bool HTMLEditUtils::IsInclusiveAncestorCSSDisplayNone(
- const nsIContent& aContent,
- const nsIContent* aAncestorLimiter /* = nullptr */) {
+ const nsIContent& aContent) {
if (NS_WARN_IF(!aContent.IsInComposedDoc())) {
return true;
}
@@ -480,14 +474,12 @@ bool HTMLEditUtils::IsInclusiveAncestorCSSDisplayNone(
aContent.InclusiveFlatTreeAncestorsOfType<Element>()) {
RefPtr<const ComputedStyle> elementStyle =
nsComputedDOMStyle::GetComputedStyleNoFlush(element);
- if (MOZ_LIKELY(elementStyle)) {
- const nsStyleDisplay* styleDisplay = elementStyle->StyleDisplay();
- if (MOZ_UNLIKELY(styleDisplay->mDisplay == StyleDisplay::None)) {
- return true;
- }
+ if (NS_WARN_IF(!elementStyle)) {
+ continue;
}
- if (element == aAncestorLimiter) {
- break;
+ const nsStyleDisplay* styleDisplay = elementStyle->StyleDisplay();
+ if (MOZ_UNLIKELY(styleDisplay->mDisplay == StyleDisplay::None)) {
+ return true;
}
}
return false;
@@ -501,11 +493,7 @@ bool HTMLEditUtils::IsVisibleElementEvenIfLeafNode(const nsIContent& aContent) {
if (!aContent.IsHTMLElement()) {
return true;
}
- nsIFrame* const primaryFrame = aContent.GetPrimaryFrame();
- if (primaryFrame && aContent.IsInComposedDoc() &&
- HTMLEditUtils::IsInclusiveAncestorCSSDisplayNone(aContent)) {
- return false;
- }
+ // XXX Should we return false if the element is display:none?
if (HTMLEditUtils::IsBlockElement(
aContent, BlockInlineCheck::UseComputedDisplayStyle)) {
return true;
@@ -523,19 +511,18 @@ bool HTMLEditUtils::IsVisibleElementEvenIfLeafNode(const nsIContent& aContent) {
HTMLInputElement::FromNode(&aContent)) {
return inputElement->ControlType() != FormControlType::InputHidden;
}
- if (primaryFrame) {
- // If the frame is not dirty or non-inline container frame, we can trust
- // whether the frame is empty or not.
- if (!primaryFrame->IsSubtreeDirty() || !primaryFrame->IsInlineFrame()) {
- return !primaryFrame->GetSize().IsEmpty();
- }
- // Otherwise, the inner content may have been changed by the editor or JS.
- // Let's treat it's visible only when it has non-zero border or padding.
- return !primaryFrame->IsSelfEmpty();
+ // If the element has a primary frame and it's not empty, the element is
+ // visible.
+ // XXX This method does not guarantee that the layout has already been
+ // updated. Therefore, this check might be wrong in the edge cases.
+ // However, basically, editor apps should not depend on this path, this
+ // is required if last <br> before a block boundary becomes visible because
+ // of followed by empty but styled frame like <span style=padding:1px></span>.
+ if (aContent.GetPrimaryFrame() &&
+ !aContent.GetPrimaryFrame()->GetSize().IsEmpty()) {
+ return true;
}
- // If aContent does not have a primary frame, it may be inserted to the
- // document and has not been flushed the pending notifications. Then, we
- // cannot know the actual style so that let's assume it's invisible.
+ // Maybe, empty inline element such as <span>.
return false;
}
@@ -929,12 +916,9 @@ EditorDOMPoint HTMLEditUtils::LineRequiresPaddingLineBreakToBeVisible(
// because we want to make it visible. Therefore, we cannot use
// WSRunScanner::ScanPreviousVisibleNodeOrBlockBoundary() here.
nsIContent* const previousVisibleLeafOrChildBlock =
- HTMLEditUtils::GetPreviousLeafContentOrPreviousBlockElement(
+ HTMLEditUtils::GetPreviousNonEmptyLeafContentOrPreviousBlockElement(
preferredPaddingLineBreakPoint,
- {LeafNodeOption::TreatChildBlockAsLeafNode,
- LeafNodeOption::IgnoreInvisibleEmptyInlineContainers,
- LeafNodeOption::IgnoreEmptyText},
- BlockInlineCheck::Auto);
+ {LeafNodeType::LeafNodeOrChildBlock}, BlockInlineCheck::Auto);
if (!previousVisibleLeafOrChildBlock) {
// Reached current block.
return true;
@@ -967,12 +951,16 @@ Element* HTMLEditUtils::GetElementOfImmediateBlockBoundary(
auto getNextContent = [&aDirection, &maybeNonEditableAncestorBlock](
const nsIContent& aContent) -> nsIContent* {
return aDirection == WalkTreeDirection::Forward
- ? HTMLEditUtils::GetNextLeafContentOrNextBlockElement(
- aContent, {LeafNodeOption::TreatChildBlockAsLeafNode},
+ ? HTMLEditUtils::GetNextContent(
+ aContent,
+ {WalkTreeOption::IgnoreDataNodeExceptText,
+ WalkTreeOption::StopAtBlockBoundary},
BlockInlineCheck::UseComputedDisplayStyle,
maybeNonEditableAncestorBlock)
- : HTMLEditUtils::GetPreviousLeafContentOrPreviousBlockElement(
- aContent, {LeafNodeOption::TreatChildBlockAsLeafNode},
+ : HTMLEditUtils::GetPreviousContent(
+ aContent,
+ {WalkTreeOption::IgnoreDataNodeExceptText,
+ WalkTreeOption::StopAtBlockBoundary},
BlockInlineCheck::UseComputedDisplayStyle,
maybeNonEditableAncestorBlock);
};
@@ -1097,636 +1085,31 @@ bool HTMLEditUtils::PointIsImmediatelyBeforeCurrentBlockBoundary(
return false;
}
-// static
-HTMLEditUtils::LeafNodeType HTMLEditUtils::GetLeafNodeType(
- const nsIContent& aContent, const LeafNodeOptions& aOptions,
- BlockInlineCheck aBlockInlineCheck, IgnoreChildren aIgnoreChildren) {
- if (!HTMLEditUtils::IsSimplyEditableNode(aContent)) {
- if (aOptions.contains(LeafNodeOption::TreatNonEditableNodeAsLeafNode)) {
- return LeafNodeType::Leaf;
- }
- if (aOptions.contains(LeafNodeOption::IgnoreNonEditableNode)) {
- return LeafNodeType::Ignore;
- }
- }
- if (const Element* const element = Element::FromNode(&aContent)) {
- // If the element is a replaced element, it should be treated as a leaf.
- if (HTMLEditUtils::IsReplacedElement(*element)) {
- return LeafNodeType::Leaf;
- }
- // We're looking for a child block, check the display-outside style.
- if (aOptions.contains(LeafNodeOption::TreatChildBlockAsLeafNode) &&
- HTMLEditUtils::IsBlockElement(
- *element,
- UseComputedDisplayOutsideStyleIfAuto(aBlockInlineCheck))) {
- return LeafNodeType::Leaf;
- }
- // Let's handle invisible void elements even if it has some children.
- if (!HTMLEditUtils::IsContainerNode(*element)) {
- return aOptions.contains(
- LeafNodeOption::IgnoreInvisibleInlineVoidElements) &&
- !HTMLEditUtils::IsVisibleElementEvenIfLeafNode(*element)
- ? LeafNodeType::Ignore
- : LeafNodeType::Leaf;
- }
- if (aIgnoreChildren == IgnoreChildren::No && aContent.HasChildNodes()) {
- return LeafNodeType::NonEmptyContainer;
- }
- // if the element is a flow root, it's meaningful and must be visible.
- if (HTMLEditUtils::IsBlockElement(
- *element, aBlockInlineCheck == BlockInlineCheck::UseHTMLDefaultStyle
- ? BlockInlineCheck::UseHTMLDefaultStyle
- : BlockInlineCheck::UseComputedDisplayStyle)) {
- return LeafNodeType::Leaf;
- }
- if (!HTMLEditUtils::IsContainerNode(*element)) {
- return LeafNodeType::Ignore;
- }
- // Now the element is an empty inline container like <span></span>.
- if (aOptions.contains(LeafNodeOption::IgnoreAnyEmptyInlineContainers)) {
- return LeafNodeType::Ignore;
- }
- if (aOptions.contains(
- LeafNodeOption::IgnoreInvisibleEmptyInlineContainers) &&
- !HTMLEditUtils::IsVisibleElementEvenIfLeafNode(*element)) {
- return LeafNodeType::Ignore;
- }
- return LeafNodeType::Leaf;
- }
- if (const Text* const text = Text::FromNode(aContent)) {
- if (!text->TextDataLength()) {
- return aOptions.contains(LeafNodeOption::IgnoreEmptyText) ||
- aOptions.contains(LeafNodeOption::IgnoreInvisibleText)
- ? LeafNodeType::Ignore
- : LeafNodeType::Leaf;
- }
- return !aOptions.contains(LeafNodeOption::IgnoreInvisibleText) ||
- IsVisibleTextNode(*text)
- ? LeafNodeType::Leaf
- : LeafNodeType::Ignore;
- }
- if (aContent.IsComment()) {
- return aOptions.contains(LeafNodeOption::TreatCommentAsLeafNode)
- ? LeafNodeType::Leaf
- : LeafNodeType::Ignore;
- }
- return LeafNodeType::Ignore;
-}
-
-// static
-nsIContent* HTMLEditUtils::GetLastLeafContent(
- const nsINode& aNode, const LeafNodeOptions& aOptions,
- BlockInlineCheck aBlockInlineCheck /* = BlockInlineCheck::Unused */) {
- MOZ_ASSERT_IF(
- aOptions.contains(LeafNodeOption::IgnoreNonEditableNode),
- !aOptions.contains(LeafNodeOption::TreatNonEditableNodeAsLeafNode));
- MOZ_ASSERT_IF(aOptions.contains(LeafNodeOption::TreatChildBlockAsLeafNode),
- aBlockInlineCheck != BlockInlineCheck::Unused);
- // editor shouldn't touch child nodes which are replaced with native
- // anonymous nodes.
- if (aNode.IsElement() &&
- HTMLEditUtils::IsNeverElementContentsEditableByUser(*aNode.AsElement())) {
- return nullptr;
- }
- for (nsIContent* content = aNode.GetLastChild(); content;) {
- const LeafNodeType type = HTMLEditUtils::GetLeafNodeType(
- *content, aOptions, aBlockInlineCheck, IgnoreChildren::No);
- if (type == LeafNodeType::Leaf) {
- return content;
- }
- if (type == LeafNodeType::NonEmptyContainer) {
- content = content->GetLastChild();
- MOZ_ASSERT(content);
- continue;
- }
- MOZ_ASSERT(type == LeafNodeType::Ignore);
- nsIContent* const prevSibling = content->GetPreviousSibling();
- if (prevSibling) {
- content = prevSibling;
- continue;
- }
- // Okay, content is the first sibling but no meaningful content is not in
- // current container. So, the container can be treated as an empty
- // container.
- nsIContent* const parent = content->GetParent();
- if (!parent || parent == &aNode) {
- return nullptr;
- }
- content = nullptr;
- for (nsIContent* const ancestor :
- parent->InclusiveAncestorsOfType<nsIContent>()) {
- if (ancestor == &aNode) {
- return nullptr; // No meaningful leaf in aNode.
- }
- // All children of current content is ignorable. So, the parent
- // should be treated as empty.
- const LeafNodeType type = HTMLEditUtils::GetLeafNodeType(
- *ancestor, aOptions, aBlockInlineCheck, IgnoreChildren::Yes);
- if (type == LeafNodeType::Leaf) {
- return ancestor;
- }
- MOZ_ASSERT(type == LeafNodeType::Ignore);
- // If the ancestor has a previous sibling, check it.
- if ((content = ancestor->GetPreviousSibling())) {
- break;
- }
- // Otherwise, check the parent of the ancestor.
- }
- }
- return nullptr;
-}
-
-// static
-nsIContent* HTMLEditUtils::GetFirstLeafContent(
- const nsINode& aNode, const LeafNodeOptions& aOptions,
- BlockInlineCheck aBlockInlineCheck /* = BlockInlineCheck::Unused */) {
- MOZ_ASSERT_IF(
- aOptions.contains(LeafNodeOption::IgnoreNonEditableNode),
- !aOptions.contains(LeafNodeOption::TreatNonEditableNodeAsLeafNode));
- MOZ_ASSERT_IF(aOptions.contains(LeafNodeOption::TreatChildBlockAsLeafNode),
- aBlockInlineCheck != BlockInlineCheck::Unused);
- // editor shouldn't touch child nodes which are replaced with native
- // anonymous nodes.
- if (aNode.IsElement() &&
- HTMLEditUtils::IsNeverElementContentsEditableByUser(*aNode.AsElement())) {
- return nullptr;
- }
- for (nsIContent* content = aNode.GetFirstChild(); content;) {
- const LeafNodeType type = HTMLEditUtils::GetLeafNodeType(
- *content, aOptions, aBlockInlineCheck, IgnoreChildren::No);
- if (type == LeafNodeType::Leaf) {
- return content;
- }
- if (type == LeafNodeType::NonEmptyContainer) {
- content = content->GetFirstChild();
- MOZ_ASSERT(content);
- continue;
- }
- MOZ_ASSERT(type == LeafNodeType::Ignore);
- nsIContent* const nextSibling = content->GetNextSibling();
- if (nextSibling) {
- content = nextSibling;
- continue;
- }
- // Okay, content is the last sibling but no meaningful content is not in
- // current container. So, the container can be treated as an empty
- // container.
- nsIContent* const parent = content->GetParent();
- if (!parent || parent == &aNode) {
- return nullptr; // No meaningful leaf in aNode.
- }
- content = nullptr;
- for (nsIContent* const ancestor :
- parent->InclusiveAncestorsOfType<nsIContent>()) {
- if (ancestor == &aNode) {
- return nullptr;
- }
- // All children of current content is ignorable. So, the parent
- // should be treated as empty.
- const LeafNodeType type = HTMLEditUtils::GetLeafNodeType(
- *ancestor, aOptions, aBlockInlineCheck, IgnoreChildren::Yes);
- if (type == LeafNodeType::Leaf) {
- return ancestor;
- }
- MOZ_ASSERT(type == LeafNodeType::Ignore);
- // If the ancestor has a next sibling, check it.
- if ((content = ancestor->GetNextSibling())) {
- break;
- }
- // Otherwise, check the parent of the ancestor.
- }
- }
- return nullptr;
-}
-
-// static
-nsIContent* HTMLEditUtils::GetNextLeafContentOrNextBlockElementImpl(
- const nsIContent& aStartContent, StopAtBlockSibling aStopAtBlockSibling,
- const LeafNodeOptions& aOptions, BlockInlineCheck aBlockInlineCheck,
- const Element* aAncestorLimiter /* = nullptr */) {
- MOZ_ASSERT_IF(
- aOptions.contains(LeafNodeOption::IgnoreNonEditableNode),
- !aOptions.contains(LeafNodeOption::TreatNonEditableNodeAsLeafNode));
-
- if (&aStartContent == aAncestorLimiter) {
- return nullptr;
- }
-
- Element* container = aStartContent.GetParentElement();
- for (nsIContent* nextContent = aStartContent.GetNextSibling();;) {
- if (!nextContent) {
- if (!container) {
- NS_WARNING("Reached orphan node while climbing up the DOM tree");
- return nullptr;
- }
- for (Element* const parentElement :
- container->InclusiveAncestorsOfType<Element>()) {
- if (parentElement == aAncestorLimiter ||
- (static_cast<bool>(aStopAtBlockSibling) &&
- HTMLEditUtils::IsBlockElement(
- *parentElement,
- UseComputedDisplayStyleIfAuto(aBlockInlineCheck)))) {
- return nullptr;
- }
- if (aOptions.contains(LeafNodeOption::TreatNonEditableNodeAsLeafNode) &&
- !parentElement->IsEditable()) {
- return nullptr;
- }
- nextContent = parentElement->GetNextSibling();
- if (nextContent) {
- container = nextContent->GetParentElement();
- break;
- }
- if (!parentElement->GetParentElement()) {
- NS_WARNING("Reached orphan node while climbing up the DOM tree");
- return nullptr;
- }
- }
- MOZ_ASSERT(nextContent);
- }
-
- // We have a next content. If it's a block, return it.
- if (static_cast<bool>(aStopAtBlockSibling) &&
- HTMLEditUtils::IsBlockElement(
- *nextContent,
- PreferDisplayOutsideIfUsingDisplay(
- UseComputedDisplayStyleIfAuto(aBlockInlineCheck)))) {
- return nextContent;
- }
- LeafNodeType type = HTMLEditUtils::GetLeafNodeType(
- *nextContent, aOptions, aBlockInlineCheck, IgnoreChildren::No);
- if (type == LeafNodeType::Leaf) {
- return nextContent;
- }
- if (type == LeafNodeType::Ignore) {
- nextContent = nextContent->GetNextSibling();
- MOZ_ASSERT_IF(nextContent, container == nextContent->GetParentElement());
- continue;
- }
- MOZ_ASSERT(type == LeafNodeType::NonEmptyContainer);
- if (nsIContent* const lastLeaf = HTMLEditUtils::GetFirstLeafContent(
- *nextContent, aOptions,
- PreferDisplayOutsideIfUsingDisplay(aBlockInlineCheck))) {
- return lastLeaf;
- }
- // nextContent has some nodes, but does not have meaningful nodes.
- // Therefore, we can treat it as empty.
- type = HTMLEditUtils::GetLeafNodeType(
- *nextContent, aOptions, aBlockInlineCheck, IgnoreChildren::Yes);
- if (type == LeafNodeType::Leaf) {
- return nextContent;
- }
- MOZ_ASSERT(type == LeafNodeType::Ignore);
- nextContent = nextContent->GetNextSibling();
- MOZ_ASSERT_IF(nextContent, container == nextContent->GetParentElement());
- }
- MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE(
- "Must return from the preceding for-loop");
-}
-
-// static
-template <typename PT, typename CT>
-nsIContent* HTMLEditUtils::GetNextLeafContentOrNextBlockElementImpl(
- const EditorDOMPointBase<PT, CT>& aStartPoint,
- StopAtBlockSibling aStopAtBlockSibling, const LeafNodeOptions& aOptions,
- BlockInlineCheck aBlockInlineCheck,
- const Element* aAncestorLimiter /* = nullptr */) {
- MOZ_ASSERT(aStartPoint.IsSet());
- MOZ_ASSERT_IF(
- aOptions.contains(LeafNodeOption::IgnoreNonEditableNode),
- !aOptions.contains(LeafNodeOption::TreatNonEditableNodeAsLeafNode));
-
- if (!aStartPoint.IsInContentNode()) {
- return nullptr;
- }
- if (!aStartPoint.GetContainer()->IsElement()) {
- return HTMLEditUtils::GetNextLeafContentOrNextBlockElementImpl(
- *aStartPoint.template ContainerAs<nsIContent>(), aStopAtBlockSibling,
- aOptions, aBlockInlineCheck, aAncestorLimiter);
- }
- if (!HTMLEditUtils::IsContainerNode(
- *aStartPoint.template ContainerAs<Element>()) ||
- HTMLEditUtils::IsReplacedElement(
- *aStartPoint.template ContainerAs<Element>())) {
- return HTMLEditUtils::GetNextLeafContentOrNextBlockElementImpl(
- *aStartPoint.template ContainerAs<nsIContent>(), aStopAtBlockSibling,
- aOptions, aBlockInlineCheck, aAncestorLimiter);
- }
-
- for (nsIContent* nextContent = aStartPoint.GetChild();;) {
- if (!nextContent) {
- if (aStartPoint.GetContainer() == aAncestorLimiter ||
- (static_cast<bool>(aStopAtBlockSibling) &&
- HTMLEditUtils::IsBlockElement(
- *aStartPoint.template ContainerAs<Element>(),
- UseComputedDisplayStyleIfAuto(aBlockInlineCheck)))) {
- // We are at end of the block.
- return nullptr;
- }
-
- // We are at end of non-block container
- return HTMLEditUtils::GetNextLeafContentOrNextBlockElementImpl(
- *aStartPoint.template ContainerAs<Element>(), aStopAtBlockSibling,
- aOptions, PreferDisplayOutsideIfUsingDisplay(aBlockInlineCheck),
- aAncestorLimiter);
- }
-
- // We have a next node. If it's a block, return it.
- if (static_cast<bool>(aStopAtBlockSibling) &&
- HTMLEditUtils::IsBlockElement(
- *nextContent,
- UseComputedDisplayOutsideStyleIfAuto(aBlockInlineCheck))) {
- return nextContent;
- }
- LeafNodeType type = HTMLEditUtils::GetLeafNodeType(
- *nextContent, aOptions, aBlockInlineCheck, IgnoreChildren::No);
- if (type == LeafNodeType::Leaf) {
- return nextContent;
- }
- if (type == LeafNodeType::Ignore) {
- nextContent = nextContent->GetNextSibling();
- continue;
- }
- MOZ_ASSERT(type == LeafNodeType::NonEmptyContainer);
- if (nsIContent* const firstLeaf = HTMLEditUtils::GetFirstLeafContent(
- *nextContent, aOptions,
- PreferDisplayOutsideIfUsingDisplay(aBlockInlineCheck))) {
- return firstLeaf;
- }
- // nextContent has some nodes, but does not have meaningful nodes.
- // Therefore, we can treat it as empty.
- type = HTMLEditUtils::GetLeafNodeType(
- *nextContent, aOptions, aBlockInlineCheck, IgnoreChildren::Yes);
- if (type == LeafNodeType::Leaf) {
- return nextContent;
- }
- MOZ_ASSERT(type == LeafNodeType::Ignore);
- nextContent = nextContent->GetNextSibling();
- }
- MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE(
- "Must return from the preceding for-loop");
-}
-
-// static
-nsIContent* HTMLEditUtils::GetPreviousLeafContentOrPreviousBlockElementImpl(
- const nsIContent& aStartContent, StopAtBlockSibling aStopAtBlockSibling,
- const LeafNodeOptions& aOptions, BlockInlineCheck aBlockInlineCheck,
- const Element* aAncestorLimiter /* = nullptr */) {
- MOZ_ASSERT_IF(
- aOptions.contains(LeafNodeOption::IgnoreNonEditableNode),
- !aOptions.contains(LeafNodeOption::TreatNonEditableNodeAsLeafNode));
-
- if (&aStartContent == aAncestorLimiter) {
- return nullptr;
- }
-
- Element* container = aStartContent.GetParentElement();
- for (nsIContent* previousContent = aStartContent.GetPreviousSibling();;) {
- if (!previousContent) {
- if (!container) {
- NS_WARNING("Reached orphan node while climbing up the DOM tree");
- return nullptr;
- }
- for (Element* parentElement :
- container->InclusiveAncestorsOfType<Element>()) {
- if (parentElement == aAncestorLimiter ||
- (static_cast<bool>(aStopAtBlockSibling) &&
- HTMLEditUtils::IsBlockElement(
- *parentElement,
- UseComputedDisplayStyleIfAuto(aBlockInlineCheck)))) {
- return nullptr;
- }
- if (aOptions.contains(LeafNodeOption::TreatNonEditableNodeAsLeafNode) &&
- !parentElement->IsEditable()) {
- return nullptr;
- }
- previousContent = parentElement->GetPreviousSibling();
- if (previousContent) {
- container = previousContent->GetParentElement();
- break;
- }
- if (!parentElement->GetParentElement()) {
- NS_WARNING("Reached orphan node while climbing up the DOM tree");
- return nullptr;
- }
- }
- MOZ_ASSERT(previousContent);
- }
- // We have a next content. If it's a block, return it.
- if (static_cast<bool>(aStopAtBlockSibling) &&
- HTMLEditUtils::IsBlockElement(
- *previousContent,
- PreferDisplayOutsideIfUsingDisplay(
- UseComputedDisplayOutsideStyleIfAuto(aBlockInlineCheck)))) {
- return previousContent;
- }
- LeafNodeType type = HTMLEditUtils::GetLeafNodeType(
- *previousContent, aOptions, aBlockInlineCheck, IgnoreChildren::No);
- if (type == LeafNodeType::Leaf) {
- return previousContent;
- }
- if (type == LeafNodeType::Ignore) {
- previousContent = previousContent->GetPreviousSibling();
- MOZ_ASSERT_IF(previousContent,
- container == previousContent->GetParentElement());
- continue;
- }
- if (nsIContent* const lastLeaf = HTMLEditUtils::GetLastLeafContent(
- *previousContent, aOptions,
- PreferDisplayOutsideIfUsingDisplay(aBlockInlineCheck))) {
- return lastLeaf;
- }
- // previousContent has some nodes, but does not have meaningful nodes.
- // Therefore, we can treat it as empty.
- type = HTMLEditUtils::GetLeafNodeType(
- *previousContent, aOptions, aBlockInlineCheck, IgnoreChildren::Yes);
- if (type == LeafNodeType::Leaf) {
- return previousContent;
- }
- MOZ_ASSERT(type == LeafNodeType::Ignore);
- previousContent = previousContent->GetPreviousSibling();
- MOZ_ASSERT_IF(previousContent,
- container == previousContent->GetParentElement());
- return previousContent;
- }
- MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE(
- "Must return from the preceding for-loop");
-}
-
-// static
-template <typename PT, typename CT>
-nsIContent* HTMLEditUtils::GetPreviousLeafContentOrPreviousBlockElementImpl(
- const EditorDOMPointBase<PT, CT>& aStartPoint,
- StopAtBlockSibling aStopAtBlockSibling, const LeafNodeOptions& aOptions,
- BlockInlineCheck aBlockInlineCheck,
- const Element* aAncestorLimiter /* = nullptr */) {
- MOZ_ASSERT(aStartPoint.IsSet());
- MOZ_ASSERT_IF(
- aOptions.contains(LeafNodeOption::IgnoreNonEditableNode),
- !aOptions.contains(LeafNodeOption::TreatNonEditableNodeAsLeafNode));
-
- if (!aStartPoint.IsInContentNode()) {
- return nullptr;
- }
- if (!aStartPoint.GetContainer()->IsElement()) {
- return HTMLEditUtils::GetPreviousLeafContentOrPreviousBlockElementImpl(
- *aStartPoint.template ContainerAs<nsIContent>(), aStopAtBlockSibling,
- aOptions, aBlockInlineCheck, aAncestorLimiter);
- }
- if (!HTMLEditUtils::IsContainerNode(
- *aStartPoint.template ContainerAs<Element>()) ||
- HTMLEditUtils::IsReplacedElement(
- *aStartPoint.template ContainerAs<Element>())) {
- return HTMLEditUtils::GetPreviousLeafContentOrPreviousBlockElementImpl(
- *aStartPoint.template ContainerAs<Element>(), aStopAtBlockSibling,
- aOptions, aBlockInlineCheck, aAncestorLimiter);
- }
-
- if (aStartPoint.IsStartOfContainer()) {
- if (aStartPoint.GetContainer() == aAncestorLimiter ||
- (static_cast<bool>(aStopAtBlockSibling) &&
- HTMLEditUtils::IsBlockElement(
- *aStartPoint.template ContainerAs<Element>(),
- UseComputedDisplayStyleIfAuto(aBlockInlineCheck)))) {
- // We are at start of the block.
- return nullptr;
- }
-
- // We are at start of non-block container
- return HTMLEditUtils::GetPreviousLeafContentOrPreviousBlockElementImpl(
- *aStartPoint.template ContainerAs<Element>(), aStopAtBlockSibling,
- aOptions, PreferDisplayOutsideIfUsingDisplay(aBlockInlineCheck),
- aAncestorLimiter);
- }
-
- for (nsIContent* previousContent = aStartPoint.GetPreviousSiblingOfChild();
- previousContent;
- previousContent = previousContent->GetPreviousSibling()) {
- // We have a prior node. If it's a block, return it.
- if (static_cast<bool>(aStopAtBlockSibling) &&
- HTMLEditUtils::IsBlockElement(
- *previousContent,
- UseComputedDisplayOutsideStyleIfAuto(aBlockInlineCheck))) {
- return previousContent;
- }
- LeafNodeType type = HTMLEditUtils::GetLeafNodeType(
- *previousContent, aOptions, aBlockInlineCheck, IgnoreChildren::No);
- if (type == LeafNodeType::Leaf) {
- return previousContent;
- }
- if (type == LeafNodeType::Ignore) {
- continue;
- }
- if (nsIContent* const lastLeaf = HTMLEditUtils::GetLastLeafContent(
- *previousContent, aOptions,
- PreferDisplayOutsideIfUsingDisplay(aBlockInlineCheck))) {
- return lastLeaf;
- }
- // previousContent has some nodes, but does not have meaningful nodes.
- // Therefore, we can treat it as empty.
- type = HTMLEditUtils::GetLeafNodeType(
- *previousContent, aOptions, aBlockInlineCheck, IgnoreChildren::Yes);
- if (type == LeafNodeType::Leaf) {
- return previousContent;
- }
- MOZ_ASSERT(type == LeafNodeType::Ignore);
- }
- return nullptr;
-}
-
-nsIContent* HTMLEditUtils::GetSibling(const nsIContent& aContent,
- WalkTreeDirection aDirection,
- const LeafNodeOptions& aOptions,
- BlockInlineCheck aBlockInlineCheck) {
- MOZ_ASSERT(aBlockInlineCheck != BlockInlineCheck::Unused);
- aBlockInlineCheck = UseComputedDisplayOutsideStyleIfAuto(aBlockInlineCheck);
- for (nsIContent* sibling = aDirection == WalkTreeDirection::Backward
- ? aContent.GetPreviousSibling()
- : aContent.GetNextSibling();
- sibling; sibling = aDirection == WalkTreeDirection::Backward
- ? sibling->GetPreviousSibling()
- : sibling->GetNextSibling()) {
- const LeafNodeType leafNodeType = HTMLEditUtils::GetLeafNodeType(
- *sibling, aOptions, aBlockInlineCheck, IgnoreChildren::No);
- if (leafNodeType == LeafNodeType::Ignore) {
- continue;
- }
- if (HTMLEditUtils::IsBlockElement(*sibling, aBlockInlineCheck)) {
- return sibling;
- }
- if (leafNodeType == LeafNodeType::NonEmptyContainer) {
- if (HTMLEditUtils::GetFirstLeafContent(*sibling, aOptions,
- aBlockInlineCheck)) {
- return sibling; // Has meaningful child so that it's meaningful.
- }
- if (HTMLEditUtils::GetLeafNodeType(*sibling, aOptions, aBlockInlineCheck,
- IgnoreChildren::Yes) ==
- LeafNodeType::Ignore) {
- continue; // The sibling itself can be ignored.
- }
- }
- return sibling;
- }
- return nullptr;
-}
-
-nsIContent* HTMLEditUtils::GetFirstOrLastChild(
- const nsINode& aNode, FirstOrLastChild aFirstOrLastChild,
- const LeafNodeOptions& aOptions, BlockInlineCheck aBlockInlineCheck) {
- MOZ_ASSERT(aBlockInlineCheck != BlockInlineCheck::Unused);
- aBlockInlineCheck = UseComputedDisplayOutsideStyleIfAuto(aBlockInlineCheck);
- for (nsIContent* child = aFirstOrLastChild == FirstOrLastChild::First
- ? aNode.GetFirstChild()
- : aNode.GetLastChild();
- child; child = aFirstOrLastChild == FirstOrLastChild::First
- ? child->GetNextSibling()
- : child->GetPreviousSibling()) {
- const LeafNodeType leafNodeType = HTMLEditUtils::GetLeafNodeType(
- *child, aOptions, aBlockInlineCheck, IgnoreChildren::No);
- if (leafNodeType == LeafNodeType::Ignore) {
- continue;
- }
- if (HTMLEditUtils::IsBlockElement(*child, aBlockInlineCheck)) {
- return child;
- }
- if (leafNodeType == LeafNodeType::NonEmptyContainer) {
- if (HTMLEditUtils::GetFirstLeafContent(*child, aOptions,
- aBlockInlineCheck)) {
- return child; // Has meaningful child so that it's meaningful.
- }
- if (HTMLEditUtils::GetLeafNodeType(*child, aOptions, aBlockInlineCheck,
- IgnoreChildren::Yes) ==
- LeafNodeType::Ignore) {
- continue; // The child itself can be ignored.
- }
- }
- return child;
- }
- return nullptr;
-}
-
template <typename EditorLineBreakType>
Maybe<EditorLineBreakType> HTMLEditUtils::GetUnnecessaryLineBreak(
const Element& aBlockElement, ScanLineBreak aScanLineBreak) {
auto* lastLineBreakContent = [&]() -> nsIContent* {
+ const WalkTreeOptions onlyPrecedingLine{
+ WalkTreeOption::StopAtBlockBoundary};
for (nsIContent* content =
aScanLineBreak == ScanLineBreak::AtEndOfBlock
- ? HTMLEditUtils::GetLastLeafContent(aBlockElement, {})
- : HTMLEditUtils::GetPreviousLeafContentOrPreviousBlockElement(
- aBlockElement,
- {LeafNodeOption::TreatChildBlockAsLeafNode},
+ ? HTMLEditUtils::GetLastLeafContent(
+ aBlockElement, {LeafNodeType::OnlyLeafNode})
+ : HTMLEditUtils::GetPreviousContent(
+ aBlockElement, onlyPrecedingLine,
BlockInlineCheck::UseComputedDisplayStyle,
aBlockElement.GetParentElement());
content;
- content = HTMLEditUtils::GetPreviousLeafContentOrPreviousBlockElement(
- *content,
- aScanLineBreak == ScanLineBreak::AtEndOfBlock
- ? LeafNodeOptions{}
- : LeafNodeOptions{LeafNodeOption::TreatChildBlockAsLeafNode},
- BlockInlineCheck::UseComputedDisplayStyle,
+ content =
aScanLineBreak == ScanLineBreak::AtEndOfBlock
- ? &aBlockElement
- : aBlockElement.GetParentElement())) {
+ ? HTMLEditUtils::GetPreviousLeafContentOrPreviousBlockElement(
+ *content, {LeafNodeType::OnlyLeafNode},
+ BlockInlineCheck::UseComputedDisplayStyle,
+ &aBlockElement)
+ : HTMLEditUtils::GetPreviousContent(
+ *content, onlyPrecedingLine,
+ BlockInlineCheck::UseComputedDisplayStyle,
+ aBlockElement.GetParentElement())) {
// If we're scanning preceding <br> element of aBlockElement, we don't
// need to look for a line break in another block because the caller
// needs to handle only preceding <br> element of aBlockElement.
@@ -1793,12 +1176,11 @@ Maybe<EditorLineBreakType> HTMLEditUtils::GetUnnecessaryLineBreak(
BlockInlineCheck::UseComputedDisplayStyle);
for (nsIContent* content =
HTMLEditUtils::GetPreviousLeafContentOrPreviousBlockElement(
- *lastLineBreakContent,
- {LeafNodeOption::TreatChildBlockAsLeafNode},
+ *lastLineBreakContent, {LeafNodeType::LeafNodeOrChildBlock},
BlockInlineCheck::UseComputedDisplayStyle, blockElement);
content;
content = HTMLEditUtils::GetPreviousLeafContentOrPreviousBlockElement(
- *content, {LeafNodeOption::TreatChildBlockAsLeafNode},
+ *content, {LeafNodeType::LeafNodeOrChildBlock},
BlockInlineCheck::UseComputedDisplayStyle, blockElement)) {
if (HTMLEditUtils::IsBlockElement(
*content, BlockInlineCheck::UseComputedDisplayStyle) ||
@@ -2155,10 +1537,6 @@ bool HTMLEditUtils::IsEmptyNode(nsPresContext* aPresContext,
continue;
}
- if (childContent->IsComment()) {
- continue;
- }
-
MOZ_ASSERT(childContent != &aNode);
if (!aOptions.contains(EmptyCheckOption::TreatSingleBRElementAsVisible) &&
@@ -2603,6 +1981,219 @@ bool HTMLEditUtils::IsSingleLineContainer(const nsIContent& aContent) {
}
// static
+template <typename PT, typename CT>
+nsIContent* HTMLEditUtils::GetPreviousContent(
+ const EditorDOMPointBase<PT, CT>& aPoint, const WalkTreeOptions& aOptions,
+ BlockInlineCheck aBlockInlineCheck,
+ const Element* aAncestorLimiter /* = nullptr */) {
+ MOZ_ASSERT(aPoint.IsSetAndValid());
+ NS_WARNING_ASSERTION(
+ !aPoint.IsInDataNode() || aPoint.IsInTextNode(),
+ "GetPreviousContent() doesn't assume that the start point is a "
+ "data node except text node");
+
+ // If we are at the beginning of the node, or it is a text node, then just
+ // look before it.
+ if (aPoint.IsStartOfContainer() || aPoint.IsInTextNode()) {
+ if (aOptions.contains(WalkTreeOption::StopAtBlockBoundary) &&
+ aPoint.IsInContentNode() &&
+ HTMLEditUtils::IsBlockElement(
+ *aPoint.template ContainerAs<nsIContent>(),
+ UseComputedDisplayStyleIfAuto(aBlockInlineCheck))) {
+ // If we aren't allowed to cross blocks, don't look before this block.
+ return nullptr;
+ }
+ return HTMLEditUtils::GetPreviousContent(
+ *aPoint.GetContainer(), aOptions, aBlockInlineCheck, aAncestorLimiter);
+ }
+
+ // else look before the child at 'aOffset'
+ if (aPoint.GetChild()) {
+ return HTMLEditUtils::GetPreviousContent(
+ *aPoint.GetChild(), aOptions, aBlockInlineCheck, aAncestorLimiter);
+ }
+
+ // unless there isn't one, in which case we are at the end of the node
+ // and want the deep-right child.
+ nsIContent* lastLeafContent = HTMLEditUtils::GetLastLeafContent(
+ *aPoint.GetContainer(),
+ {aOptions.contains(WalkTreeOption::StopAtBlockBoundary)
+ ? LeafNodeType::LeafNodeOrChildBlock
+ : LeafNodeType::OnlyLeafNode},
+ aBlockInlineCheck);
+ if (!lastLeafContent) {
+ return nullptr;
+ }
+
+ if (!HTMLEditUtils::IsContentIgnored(*lastLeafContent, aOptions)) {
+ return lastLeafContent;
+ }
+
+ // restart the search from the non-editable node we just found
+ return HTMLEditUtils::GetPreviousContent(*lastLeafContent, aOptions,
+ aBlockInlineCheck, aAncestorLimiter);
+}
+
+// static
+template <typename PT, typename CT>
+nsIContent* HTMLEditUtils::GetNextContent(
+ const EditorDOMPointBase<PT, CT>& aPoint, const WalkTreeOptions& aOptions,
+ BlockInlineCheck aBlockInlineCheck,
+ const Element* aAncestorLimiter /* = nullptr */) {
+ MOZ_ASSERT(aPoint.IsSetAndValid());
+ NS_WARNING_ASSERTION(
+ !aPoint.IsInDataNode() || aPoint.IsInTextNode(),
+ "GetNextContent() doesn't assume that the start point is a "
+ "data node except text node");
+
+ auto point = aPoint.template To<EditorRawDOMPoint>();
+
+ // if the container is a text node, use its location instead
+ if (point.IsInTextNode()) {
+ point.SetAfter(point.GetContainer());
+ if (NS_WARN_IF(!point.IsSet())) {
+ return nullptr;
+ }
+ }
+
+ if (point.GetChild()) {
+ if (aOptions.contains(WalkTreeOption::StopAtBlockBoundary) &&
+ HTMLEditUtils::IsBlockElement(
+ *point.GetChild(),
+ UseComputedDisplayOutsideStyleIfAuto(aBlockInlineCheck))) {
+ return point.GetChild();
+ }
+
+ nsIContent* firstLeafContent = HTMLEditUtils::GetFirstLeafContent(
+ *point.GetChild(),
+ {aOptions.contains(WalkTreeOption::StopAtBlockBoundary)
+ ? LeafNodeType::LeafNodeOrChildBlock
+ : LeafNodeType::OnlyLeafNode},
+ aBlockInlineCheck);
+ if (!firstLeafContent) {
+ return point.GetChild();
+ }
+
+ // XXX Why do we need to do this check? The leaf node must be a descendant
+ // of `point.GetChild()`.
+ if (aAncestorLimiter &&
+ (firstLeafContent == aAncestorLimiter ||
+ !firstLeafContent->IsInclusiveDescendantOf(aAncestorLimiter))) {
+ return nullptr;
+ }
+
+ if (!HTMLEditUtils::IsContentIgnored(*firstLeafContent, aOptions)) {
+ return firstLeafContent;
+ }
+
+ // restart the search from the non-editable node we just found
+ return HTMLEditUtils::GetNextContent(*firstLeafContent, aOptions,
+ aBlockInlineCheck, aAncestorLimiter);
+ }
+
+ // unless there isn't one, in which case we are at the end of the node
+ // and want the next one.
+ if (aOptions.contains(WalkTreeOption::StopAtBlockBoundary) &&
+ point.IsInContentNode() &&
+ HTMLEditUtils::IsBlockElement(
+ *point.template ContainerAs<nsIContent>(),
+ UseComputedDisplayStyleIfAuto(aBlockInlineCheck))) {
+ // don't cross out of parent block
+ return nullptr;
+ }
+
+ return HTMLEditUtils::GetNextContent(*point.GetContainer(), aOptions,
+ aBlockInlineCheck, aAncestorLimiter);
+}
+
+// static
+nsIContent* HTMLEditUtils::GetAdjacentLeafContent(
+ const nsINode& aNode, WalkTreeDirection aWalkTreeDirection,
+ const WalkTreeOptions& aOptions, BlockInlineCheck aBlockInlineCheck,
+ const Element* aAncestorLimiter /* = nullptr */) {
+ // called only by GetPriorNode so we don't need to check params.
+ MOZ_ASSERT(&aNode != aAncestorLimiter);
+ MOZ_ASSERT_IF(aAncestorLimiter,
+ aAncestorLimiter->IsInclusiveDescendantOf(aAncestorLimiter));
+
+ const nsINode* node = &aNode;
+ for (;;) {
+ // if aNode has a sibling in the right direction, return
+ // that sibling's closest child (or itself if it has no children)
+ nsIContent* sibling = aWalkTreeDirection == WalkTreeDirection::Forward
+ ? node->GetNextSibling()
+ : node->GetPreviousSibling();
+ if (sibling) {
+ // XXX If `sibling` belongs to siblings of inclusive ancestors of aNode,
+ // perhaps, we need to use
+ // PreferDisplayOutsideIfUsingDisplay(aBlockInlineCheck) here.
+ if (aOptions.contains(WalkTreeOption::StopAtBlockBoundary) &&
+ HTMLEditUtils::IsBlockElement(
+ *sibling,
+ UseComputedDisplayOutsideStyleIfAuto(aBlockInlineCheck))) {
+ // don't look inside previous sibling, since it is a block
+ return sibling;
+ }
+ const LeafNodeTypes leafNodeTypes = {
+ aOptions.contains(WalkTreeOption::StopAtBlockBoundary)
+ ? LeafNodeType::LeafNodeOrChildBlock
+ : LeafNodeType::OnlyLeafNode};
+ nsIContent* leafContent =
+ aWalkTreeDirection == WalkTreeDirection::Forward
+ ? HTMLEditUtils::GetFirstLeafContent(*sibling, leafNodeTypes,
+ aBlockInlineCheck)
+ : HTMLEditUtils::GetLastLeafContent(*sibling, leafNodeTypes,
+ aBlockInlineCheck);
+ return leafContent ? leafContent : sibling;
+ }
+
+ nsIContent* parent = node->GetParent();
+ if (!parent) {
+ return nullptr;
+ }
+
+ if (parent == aAncestorLimiter ||
+ (aOptions.contains(WalkTreeOption::StopAtBlockBoundary) &&
+ HTMLEditUtils::IsBlockElement(
+ *parent, UseComputedDisplayStyleIfAuto(aBlockInlineCheck)))) {
+ return nullptr;
+ }
+
+ node = parent;
+ }
+
+ MOZ_ASSERT_UNREACHABLE("What part of for(;;) do you not understand?");
+ return nullptr;
+}
+
+// static
+nsIContent* HTMLEditUtils::GetAdjacentContent(
+ const nsINode& aNode, WalkTreeDirection aWalkTreeDirection,
+ const WalkTreeOptions& aOptions, BlockInlineCheck aBlockInlineCheck,
+ const Element* aAncestorLimiter /* = nullptr */) {
+ if (&aNode == aAncestorLimiter) {
+ // Don't allow traversal above the root node! This helps
+ // prevent us from accidentally editing browser content
+ // when the editor is in a text widget.
+ return nullptr;
+ }
+
+ nsIContent* leafContent = HTMLEditUtils::GetAdjacentLeafContent(
+ aNode, aWalkTreeDirection, aOptions, aBlockInlineCheck, aAncestorLimiter);
+ if (!leafContent) {
+ return nullptr;
+ }
+
+ if (!HTMLEditUtils::IsContentIgnored(*leafContent, aOptions)) {
+ return leafContent;
+ }
+
+ return HTMLEditUtils::GetAdjacentContent(*leafContent, aWalkTreeDirection,
+ aOptions, aBlockInlineCheck,
+ aAncestorLimiter);
+}
+
+// static
template <typename EditorDOMPointType>
EditorDOMPointType HTMLEditUtils::GetPreviousEditablePoint(
nsIContent& aContent, const Element* aAncestorLimiter,
@@ -3228,14 +2819,17 @@ nsIContent* HTMLEditUtils::GetContentToPreserveInlineStyles(
if (nextVisibleThing.InVisibleOrCollapsibleCharacters()) {
return nextVisibleThing.TextPtr();
}
- if (nextVisibleThing.ContentIsEditableRoot()) {
+ if (nextVisibleThing.IsContentEditableRoot()) {
break;
}
- // Ignore invisible empty inline container elements because it's not visible
- // for users so that using the style will appear suddenly from point of view
- // of users.
- if (nextVisibleThing.ReachedEditableInvisibleEmptyInlineContainerElement(
- &aEditingHost)) {
+ // Ignore empty inline container elements because it's not visible for
+ // users so that using the style will appear suddenly from point of
+ // view of users.
+ if (nextVisibleThing.ReachedSpecialContent() &&
+ nextVisibleThing.IsContentEditable() &&
+ nextVisibleThing.ContentIsElement() &&
+ !nextVisibleThing.ElementPtr()->HasChildNodes() &&
+ HTMLEditUtils::IsContainerNode(*nextVisibleThing.ElementPtr())) {
point.SetAfter(nextVisibleThing.ElementPtr());
continue;
}
@@ -3372,8 +2966,7 @@ HTMLEditUtils::ComputePointToPutCaretInElementIfOutside(
if (nodeBefore) {
// selection is after block. put at end of block.
const nsIContent* lastEditableContent = HTMLEditUtils::GetLastChild(
- aElement, {LeafNodeOption::IgnoreNonEditableNode},
- BlockInlineCheck::UseComputedDisplayOutsideStyle);
+ aElement, {WalkTreeOption::IgnoreNonEditableNode});
if (!lastEditableContent) {
lastEditableContent = &aElement;
}
@@ -3387,8 +2980,7 @@ HTMLEditUtils::ComputePointToPutCaretInElementIfOutside(
// selection is before block. put at start of block.
const nsIContent* firstEditableContent = HTMLEditUtils::GetFirstChild(
- aElement, {LeafNodeOption::IgnoreNonEditableNode},
- BlockInlineCheck::UseComputedDisplayOutsideStyle);
+ aElement, {WalkTreeOption::IgnoreNonEditableNode});
if (!firstEditableContent) {
firstEditableContent = &aElement;
}
@@ -3467,8 +3059,7 @@ size_t HTMLEditUtils::CollectChildren(
size_t numberOfFoundChildren = 0;
for (nsIContent* content =
- GetFirstChild(aNode, {LeafNodeOption::IgnoreNonEditableNode},
- BlockInlineCheck::UseComputedDisplayOutsideStyle);
+ GetFirstChild(aNode, {WalkTreeOption::IgnoreNonEditableNode});
content; content = content->GetNextSibling()) {
if ((aOptions.contains(CollectChildrenOption::CollectListChildren) &&
(HTMLEditUtils::IsListElement(*content) ||
@@ -3741,7 +3332,6 @@ std::ostream& operator<<(std::ostream& aStream,
"ReturnAncestorLimiterIfNoProperAncestor",
"EditableElement",
};
- MOZ_ASSERT(static_cast<uint32_t>(aType) < std::size(names));
return aStream << names[static_cast<uint32_t>(aType)];
}
@@ -3769,7 +3359,6 @@ std::ostream& operator<<(std::ostream& aStream,
"StopAtTableElement",
"StopAtAnyTableElement",
};
- MOZ_ASSERT(static_cast<uint32_t>(aOption) < std::size(names));
return aStream << names[static_cast<uint32_t>(aOption)];
}
@@ -3798,7 +3387,6 @@ std::ostream& operator<<(std::ostream& aStream,
"TreatCommentAsVisible",
"SafeToAskLayout",
};
- MOZ_ASSERT(static_cast<uint32_t>(aOption) < std::size(names));
return aStream << names[static_cast<uint32_t>(aOption)];
}
@@ -3817,27 +3405,22 @@ std::ostream& operator<<(std::ostream& aStream,
}
std::ostream& operator<<(std::ostream& aStream,
- const HTMLEditUtils::LeafNodeOption& aOption) {
+ const HTMLEditUtils::LeafNodeType& aLeafNodeType) {
constexpr static const char* names[] = {
- "TreatChildBlockAsLeafNode",
- "TreatNonEditableNodeAsLeafNode",
- "IgnoreNonEditableNode",
+ "OnlyLeafNode",
+ "LeafNodeOrChildBlock",
+ "LeafNodeOrNonEditableNode",
+ "OnlyEditableLeafNode",
"TreatCommentAsLeafNode",
- "IgnoreEmptyText",
- "IgnoreInvisibleText",
- "IgnoreInvisibleInlineVoidElements",
- "IgnoreAnyEmptyInlineContainers",
- "IgnoreInvisibleEmptyInlineContainers",
};
- MOZ_ASSERT(static_cast<uint32_t>(aOption) < std::size(names));
- return aStream << names[static_cast<uint32_t>(aOption)];
+ return aStream << names[static_cast<uint32_t>(aLeafNodeType)];
}
std::ostream& operator<<(std::ostream& aStream,
- const HTMLEditUtils::LeafNodeOptions& aOptions) {
+ const HTMLEditUtils::LeafNodeTypes& aLeafNodeTypes) {
aStream << "{";
bool first = true;
- for (const auto t : aOptions) {
+ for (const auto t : aLeafNodeTypes) {
if (!first) {
aStream << ", ";
}
diff --git a/editor/libeditor/HTMLEditUtils.h b/editor/libeditor/HTMLEditUtils.h
@@ -236,13 +236,11 @@ class HTMLEditUtils final {
BlockInlineCheck aBlockInlineCheck);
/**
- * IsVisibleElementEvenIfLeafNode() returns true if aContent is a visible
- * element when aContent is empty. If aContent has a primary frame (even if
- * dirty), this checks whether aContent is actually visible. Otherwise, this
- * guesses it from the element type.
+ * IsVisibleElementEvenIfLeafNode() returns true if aContent is an empty block
+ * element, a visible replaced element such as a form control. This does not
+ * check the layout information.
*/
- [[nodiscard]] static bool IsVisibleElementEvenIfLeafNode(
- const nsIContent& aContent);
+ static bool IsVisibleElementEvenIfLeafNode(const nsIContent& aContent);
/**
* Return true if aContent is an inline element which formats the content
@@ -783,8 +781,7 @@ class HTMLEditUtils final {
/**
* Return true if `display` of inclusive ancestor of aContent is `none`.
*/
- [[nodiscard]] static bool IsInclusiveAncestorCSSDisplayNone(
- const nsIContent& aContent, const nsIContent* aAncestorLimiter = nullptr);
+ static bool IsInclusiveAncestorCSSDisplayNone(const nsIContent& aContent);
/**
* IsVisiblePreformattedNewLine() and IsInvisiblePreformattedNewLine() return
@@ -1130,6 +1127,225 @@ class HTMLEditUtils final {
}
/**
+ * Get adjacent content node of aNode if there is (even if one is in different
+ * parent element).
+ *
+ * @param aNode The node from which we start to walk the DOM
+ * tree.
+ * @param aOptions See WalkTreeOption for the detail.
+ * @param aBlockInlineCheck Whether considering block vs. inline with the
+ * computed style or the HTML default style.
+ * @param aAncestorLimiter Ancestor limiter element which these methods
+ * never cross its boundary. This is typically
+ * the editing host.
+ */
+ enum class WalkTreeOption {
+ IgnoreNonEditableNode, // Ignore non-editable nodes and their children.
+ IgnoreDataNodeExceptText, // Ignore data nodes which are not text node.
+ IgnoreWhiteSpaceOnlyText, // Ignore text nodes having only white-spaces.
+ StopAtBlockBoundary, // Stop waking the tree at a block boundary.
+ };
+ using WalkTreeOptions = EnumSet<WalkTreeOption>;
+ static nsIContent* GetPreviousContent(
+ const nsINode& aNode, const WalkTreeOptions& aOptions,
+ BlockInlineCheck aBlockInlineCheck,
+ const Element* aAncestorLimiter = nullptr) {
+ if (&aNode == aAncestorLimiter ||
+ (aAncestorLimiter &&
+ !aNode.IsInclusiveDescendantOf(aAncestorLimiter))) {
+ return nullptr;
+ }
+ return HTMLEditUtils::GetAdjacentContent(aNode, WalkTreeDirection::Backward,
+ aOptions, aBlockInlineCheck,
+ aAncestorLimiter);
+ }
+ static nsIContent* GetNextContent(const nsINode& aNode,
+ const WalkTreeOptions& aOptions,
+ BlockInlineCheck aBlockInlineCheck,
+ const Element* aAncestorLimiter = nullptr) {
+ if (&aNode == aAncestorLimiter ||
+ (aAncestorLimiter &&
+ !aNode.IsInclusiveDescendantOf(aAncestorLimiter))) {
+ return nullptr;
+ }
+ return HTMLEditUtils::GetAdjacentContent(aNode, WalkTreeDirection::Forward,
+ aOptions, aBlockInlineCheck,
+ aAncestorLimiter);
+ }
+
+ /**
+ * And another version that takes a point in DOM tree rather than a node.
+ */
+ template <typename PT, typename CT>
+ static nsIContent* GetPreviousContent(
+ const EditorDOMPointBase<PT, CT>& aPoint, const WalkTreeOptions& aOptions,
+ BlockInlineCheck aBlockInlineCheck,
+ const Element* aAncestorLimiter = nullptr);
+
+ /**
+ * And another version that takes a point in DOM tree rather than a node.
+ *
+ * Note that this may return the child at the offset. E.g., following code
+ * causes infinite loop.
+ *
+ * EditorRawDOMPoint point(aEditableNode);
+ * while (nsIContent* content =
+ * GetNextContent(point, {WalkTreeOption::IgnoreNonEditableNode})) {
+ * // Do something...
+ * point.Set(content);
+ * }
+ *
+ * Following code must be you expected:
+ *
+ * while (nsIContent* content =
+ * GetNextContent(point, {WalkTreeOption::IgnoreNonEditableNode}) {
+ * // Do something...
+ * DebugOnly<bool> advanced = point.Advanced();
+ * MOZ_ASSERT(advanced);
+ * point.Set(point.GetChild());
+ * }
+ */
+ template <typename PT, typename CT>
+ static nsIContent* GetNextContent(const EditorDOMPointBase<PT, CT>& aPoint,
+ const WalkTreeOptions& aOptions,
+ BlockInlineCheck aBlockInlineCheck,
+ const Element* aAncestorLimiter = nullptr);
+
+ /**
+ * GetPreviousSibling() return the preceding sibling of aContent which matches
+ * with aOption.
+ *
+ * @param aBlockInlineCheck Can be Unused if aOptions does not contain
+ * StopAtBlockBoundary.
+ */
+ static nsIContent* GetPreviousSibling(
+ const nsIContent& aContent, const WalkTreeOptions& aOptions,
+ BlockInlineCheck aBlockInlineCheck = BlockInlineCheck::Unused) {
+ aBlockInlineCheck = UseComputedDisplayOutsideStyleIfAuto(aBlockInlineCheck);
+ for (nsIContent* sibling = aContent.GetPreviousSibling(); sibling;
+ sibling = sibling->GetPreviousSibling()) {
+ if (HTMLEditUtils::IsContentIgnored(*sibling, aOptions)) {
+ continue;
+ }
+ if (aOptions.contains(WalkTreeOption::StopAtBlockBoundary) &&
+ HTMLEditUtils::IsBlockElement(*sibling, aBlockInlineCheck)) {
+ return nullptr;
+ }
+ return sibling;
+ }
+ return nullptr;
+ }
+
+ /**
+ * GetNextSibling() return the following sibling of aContent which matches
+ * with aOption.
+ *
+ * @param aBlockInlineCheck Can be Unused if aOptions does not contain
+ * StopAtBlockBoundary.
+ */
+ static nsIContent* GetNextSibling(
+ const nsIContent& aContent, const WalkTreeOptions& aOptions,
+ BlockInlineCheck aBlockInlineCheck = BlockInlineCheck::Unused) {
+ aBlockInlineCheck = UseComputedDisplayOutsideStyleIfAuto(aBlockInlineCheck);
+ for (nsIContent* sibling = aContent.GetNextSibling(); sibling;
+ sibling = sibling->GetNextSibling()) {
+ if (HTMLEditUtils::IsContentIgnored(*sibling, aOptions)) {
+ continue;
+ }
+ if (aOptions.contains(WalkTreeOption::StopAtBlockBoundary) &&
+ HTMLEditUtils::IsBlockElement(*sibling, aBlockInlineCheck)) {
+ return nullptr;
+ }
+ return sibling;
+ }
+ return nullptr;
+ }
+
+ /**
+ * Return the last child of aNode which matches with aOption.
+ *
+ * @param aBlockInlineCheck Can be unused if aOptions does not contain
+ * StopAtBlockBoundary.
+ */
+ static nsIContent* GetLastChild(
+ const nsINode& aNode, const WalkTreeOptions& aOptions,
+ BlockInlineCheck aBlockInlineCheck = BlockInlineCheck::Unused) {
+ aBlockInlineCheck = UseComputedDisplayOutsideStyleIfAuto(aBlockInlineCheck);
+ for (nsIContent* child = aNode.GetLastChild(); child;
+ child = child->GetPreviousSibling()) {
+ if (HTMLEditUtils::IsContentIgnored(*child, aOptions)) {
+ continue;
+ }
+ if (aOptions.contains(WalkTreeOption::StopAtBlockBoundary) &&
+ HTMLEditUtils::IsBlockElement(*child, aBlockInlineCheck)) {
+ return nullptr;
+ }
+ return child;
+ }
+ return nullptr;
+ }
+
+ /**
+ * Return the first child of aNode which matches with aOption.
+ *
+ * @param aBlockInlineCheck Can be unused if aOptions does not contain
+ * StopAtBlockBoundary.
+ */
+ static nsIContent* GetFirstChild(
+ const nsINode& aNode, const WalkTreeOptions& aOptions,
+ BlockInlineCheck aBlockInlineCheck = BlockInlineCheck::Unused) {
+ aBlockInlineCheck = UseComputedDisplayOutsideStyleIfAuto(aBlockInlineCheck);
+ for (nsIContent* child = aNode.GetFirstChild(); child;
+ child = child->GetNextSibling()) {
+ if (HTMLEditUtils::IsContentIgnored(*child, aOptions)) {
+ continue;
+ }
+ if (aOptions.contains(WalkTreeOption::StopAtBlockBoundary) &&
+ HTMLEditUtils::IsBlockElement(*child, aBlockInlineCheck)) {
+ return nullptr;
+ }
+ return child;
+ }
+ return nullptr;
+ }
+
+ /**
+ * Return true if aContent is the last child of aNode with ignoring all
+ * children which do not match with aOption.
+ *
+ * @param aBlockInlineCheck Can be unused if aOptions does not contain
+ * StopAtBlockBoundary.
+ */
+ static bool IsLastChild(
+ const nsIContent& aContent, const WalkTreeOptions& aOptions,
+ BlockInlineCheck aBlockInlineCheck = BlockInlineCheck::Unused) {
+ nsINode* parentNode = aContent.GetParentNode();
+ if (!parentNode) {
+ return false;
+ }
+ return HTMLEditUtils::GetLastChild(*parentNode, aOptions,
+ aBlockInlineCheck) == &aContent;
+ }
+
+ /**
+ * Return true if aContent is the first child of aNode with ignoring all
+ * children which do not match with aOption.
+ *
+ * @param aBlockInlineCheck Can be unused if aOptions does not contain
+ * StopAtBlockBoundary.
+ */
+ static bool IsFirstChild(
+ const nsIContent& aContent, const WalkTreeOptions& aOptions,
+ BlockInlineCheck aBlockInlineCheck = BlockInlineCheck::Unused) {
+ nsINode* parentNode = aContent.GetParentNode();
+ if (!parentNode) {
+ return false;
+ }
+ return HTMLEditUtils::GetFirstChild(*parentNode, aOptions,
+ aBlockInlineCheck) == &aContent;
+ }
+
+ /**
* GetAdjacentContentToPutCaret() walks the DOM tree to find an editable node
* near aPoint where may be a good point to put caret and keep typing or
* deleting.
@@ -1148,15 +1364,15 @@ class HTMLEditUtils final {
nsIContent* editableContent = nullptr;
if (aWalkTreeDirection == WalkTreeDirection::Backward) {
- editableContent = HTMLEditUtils::GetPreviousLeafContent(
- aPoint, {LeafNodeOption::IgnoreNonEditableNode},
+ editableContent = HTMLEditUtils::GetPreviousContent(
+ aPoint, {WalkTreeOption::IgnoreNonEditableNode},
BlockInlineCheck::Auto, &aEditingHost);
if (!editableContent) {
return nullptr; // Not illegal.
}
} else {
- editableContent = HTMLEditUtils::GetNextLeafContent(
- aPoint, {LeafNodeOption::IgnoreNonEditableNode},
+ editableContent = HTMLEditUtils::GetNextContent(
+ aPoint, {WalkTreeOption::IgnoreNonEditableNode},
BlockInlineCheck::Auto, &aEditingHost);
if (NS_WARN_IF(!editableContent)) {
// Perhaps, illegal because the node pointed by aPoint isn't editable
@@ -1173,15 +1389,15 @@ class HTMLEditUtils final {
!editableContent->IsHTMLElement(nsGkAtoms::br) &&
!HTMLEditUtils::IsImageElement(*editableContent)) {
if (aWalkTreeDirection == WalkTreeDirection::Backward) {
- editableContent = HTMLEditUtils::GetPreviousLeafContent(
- *editableContent, {LeafNodeOption::IgnoreNonEditableNode},
+ editableContent = HTMLEditUtils::GetPreviousContent(
+ *editableContent, {WalkTreeOption::IgnoreNonEditableNode},
BlockInlineCheck::Auto, &aEditingHost);
if (NS_WARN_IF(!editableContent)) {
return nullptr;
}
} else {
- editableContent = HTMLEditUtils::GetNextLeafContent(
- *editableContent, {LeafNodeOption::IgnoreNonEditableNode},
+ editableContent = HTMLEditUtils::GetNextContent(
+ *editableContent, {WalkTreeOption::IgnoreNonEditableNode},
BlockInlineCheck::Auto, &aEditingHost);
if (NS_WARN_IF(!editableContent)) {
return nullptr;
@@ -1203,167 +1419,135 @@ class HTMLEditUtils final {
return editableContent;
}
- enum class LeafNodeOption {
- // Treat a block element as a leaf node.
- TreatChildBlockAsLeafNode,
- // Treat a non-editable node as a leaf node.
- TreatNonEditableNodeAsLeafNode,
- // Ignore non-editable content.
- IgnoreNonEditableNode,
- // Treat a `Comment` node as a significant leaf node.
+ enum class LeafNodeType {
+ // Even if there is a child block, keep scanning a leaf content in it.
+ OnlyLeafNode,
+ // If there is a child block, return it too. Note that this does not
+ // mean that block siblings are not treated as leaf nodes.
+ LeafNodeOrChildBlock,
+ // If there is a non-editable element if and only if scanning from editable
+ // node, return it too.
+ LeafNodeOrNonEditableNode,
+ // Ignore non-editable content at walking the tree.
+ OnlyEditableLeafNode,
+ // Treat `Comment` nodes are empty leaf nodes.
TreatCommentAsLeafNode,
- // Ignore empty `Text` node.
- IgnoreEmptyText,
- // Ignore invisible `Text` node such as empty node or all data is collapsed.
- IgnoreInvisibleText,
- // Ignore invisible void elements such as <wbr> and <input type="hidden">.
- IgnoreInvisibleInlineVoidElements,
- // If set, ignore empty inline containers such as <span></span>.
- IgnoreAnyEmptyInlineContainers,
- // If set, ignore empty inline containers which is not visible. E.g.,
- // <span></span> is not ignored but <span style="border:1px solid"></span>
- // and <span style="border:padding 1px"></span> are not ignored.
- // XXX Currently, this does not work well if the inline container has only
- // `::before` and/or `::after` content and the frame is dirty.
- IgnoreInvisibleEmptyInlineContainers,
};
- using LeafNodeOptions = EnumSet<LeafNodeOption>;
+ using LeafNodeTypes = EnumSet<LeafNodeType>;
friend std::ostream& operator<<(std::ostream& aStream,
- const LeafNodeOption& aOption);
+ const LeafNodeType& aLeafNodeType);
friend std::ostream& operator<<(std::ostream& aStream,
- const LeafNodeOptions& aOptions);
-
- private:
- enum class IgnoreChildren : bool { No, Yes };
- enum class LeafNodeType {
- NonEmptyContainer,
- Leaf,
- Ignore,
- };
- [[nodiscard]] static LeafNodeType GetLeafNodeType(
- const nsIContent& aContent, const LeafNodeOptions& aOptions,
- BlockInlineCheck aBlockInlineCheck, IgnoreChildren aIgnoreChildren);
+ const LeafNodeTypes& aLeafNodeTypes);
- public:
/**
* GetLastLeafContent() returns rightmost leaf content in aNode. It depends
- * on aOptions whether this which types of nodes are treated as leaf
+ * on aLeafNodeTypes whether this which types of nodes are treated as leaf
* nodes.
*
- * @param aBlockInlineCheck Can be Unused if aOptions does not contain
- * TreatChildBlockAsLeafNode.
- */
- [[nodiscard]] static nsIContent* GetLastLeafContent(
- const nsINode& aNode, const LeafNodeOptions& aOptions,
- BlockInlineCheck aBlockInlineCheck = BlockInlineCheck::Unused);
-
- /**
- * GetFirstLeafContent() returns leftmost leaf content in aNode. It depends
- * on aOptions whether this scans into a block child or treat block as a
- * leaf.
- *
- * @param aBlockInlineCheck Can be Unused if aOptions does not contain
- * TreatChildBlockAsLeafNode.
- */
- [[nodiscard]] static nsIContent* GetFirstLeafContent(
- const nsINode& aNode, const LeafNodeOptions& aOptions,
- BlockInlineCheck aBlockInlineCheck = BlockInlineCheck::Unused);
-
- private:
- enum class StopAtBlockSibling : bool { No, Yes };
-
- static nsIContent* GetNextLeafContentOrNextBlockElementImpl(
- const nsIContent& aStartContent, StopAtBlockSibling aStopAtBlockSibling,
- const LeafNodeOptions& aOptions, BlockInlineCheck aBlockInlineCheck,
- const Element* aAncestorLimiter);
- template <typename PT, typename CT>
- static nsIContent* GetNextLeafContentOrNextBlockElementImpl(
- const EditorDOMPointBase<PT, CT>& aStartPoint,
- StopAtBlockSibling aStopAtBlockSibling, const LeafNodeOptions& aOptions,
- BlockInlineCheck aBlockInlineCheck, const Element* aAncestorLimiter);
- static nsIContent* GetPreviousLeafContentOrPreviousBlockElementImpl(
- const nsIContent& aStartContent, StopAtBlockSibling aStopAtBlockSibling,
- const LeafNodeOptions& aOptions, BlockInlineCheck aBlockInlineCheck,
- const Element* aAncestorLimiter);
- template <typename PT, typename CT>
- static nsIContent* GetPreviousLeafContentOrPreviousBlockElementImpl(
- const EditorDOMPointBase<PT, CT>& aStartPoint,
- StopAtBlockSibling aStopAtBlockSibling, const LeafNodeOptions& aOptions,
- BlockInlineCheck aBlockInlineCheck, const Element* aAncestorLimiter);
-
- public:
- /**
- * Return next leaf content of aStartContent inside aAncestorLimiter.
- * This does not stop at a block inclusive ancestor nor a block sibling of an
- * inclusive ancestor different from GetNextLeafContentOrNextBlockElement().
- * However, if you specify LeafNodeOption::TreatChildBlockAsLeafNode, this
- * stops at a child block boundary. So, the behavior becomes complicated so
- * that you need to be careful if you specify that.
- *
- * @param aStartContent The start content to scan next content.
- * @param aOptions See LeafNodeOption.
- * @param aAncestorLimiter Optional, if you set this, it must be an
- * inclusive ancestor of aStartContent.
- */
- static nsIContent* GetNextLeafContent(
- const nsIContent& aStartContent, const LeafNodeOptions& aOptions,
- BlockInlineCheck aBlockInlineCheck,
- const Element* aAncestorLimiter = nullptr) {
- return GetNextLeafContentOrNextBlockElementImpl(
- aStartContent, StopAtBlockSibling::No, aOptions, aBlockInlineCheck,
- aAncestorLimiter);
- }
-
- /**
- * Similar to the above method, but take a DOM point to specify scan start
- * point.
+ * @param aBlockInlineCheck Can be Unused if aLeafNodeTypes does not contain
+ * LeafNodeOrCHildBlock.
*/
- template <typename PT, typename CT>
- static nsIContent* GetNextLeafContent(
- const EditorDOMPointBase<PT, CT>& aStartPoint,
- const LeafNodeOptions& aOptions, BlockInlineCheck aBlockInlineCheck,
+ static nsIContent* GetLastLeafContent(
+ const nsINode& aNode, const LeafNodeTypes& aLeafNodeTypes,
+ BlockInlineCheck aBlockInlineCheck = BlockInlineCheck::Unused,
const Element* aAncestorLimiter = nullptr) {
- return GetNextLeafContentOrNextBlockElementImpl(
- aStartPoint, StopAtBlockSibling::No, aOptions, aBlockInlineCheck,
- aAncestorLimiter);
+ MOZ_ASSERT_IF(
+ aLeafNodeTypes.contains(LeafNodeType::OnlyEditableLeafNode),
+ !aLeafNodeTypes.contains(LeafNodeType::LeafNodeOrNonEditableNode));
+ // editor shouldn't touch child nodes which are replaced with native
+ // anonymous nodes.
+ if (aNode.IsElement() &&
+ HTMLEditUtils::IsNeverElementContentsEditableByUser(
+ *aNode.AsElement())) {
+ return nullptr;
+ }
+ for (nsIContent* content = aNode.GetLastChild(); content;) {
+ if (aLeafNodeTypes.contains(LeafNodeType::OnlyEditableLeafNode) &&
+ !EditorUtils::IsEditableContent(*content,
+ EditorUtils::EditorType::HTML)) {
+ content = HTMLEditUtils::GetPreviousContent(
+ *content, {WalkTreeOption::IgnoreNonEditableNode},
+ aBlockInlineCheck, aAncestorLimiter);
+ continue;
+ }
+ if (!aLeafNodeTypes.contains(LeafNodeType::TreatCommentAsLeafNode) &&
+ content->IsComment()) {
+ content = content->GetPreviousSibling();
+ continue;
+ }
+ if (aLeafNodeTypes.contains(LeafNodeType::LeafNodeOrChildBlock) &&
+ HTMLEditUtils::IsBlockElement(
+ *content,
+ UseComputedDisplayOutsideStyleIfAuto(aBlockInlineCheck))) {
+ return content;
+ }
+ if (!content->HasChildren() ||
+ HTMLEditUtils::IsNeverElementContentsEditableByUser(*content)) {
+ return content;
+ }
+ if (aLeafNodeTypes.contains(LeafNodeType::LeafNodeOrNonEditableNode) &&
+ !HTMLEditUtils::IsSimplyEditableNode(*content)) {
+ return content;
+ }
+ content = content->GetLastChild();
+ }
+ return nullptr;
}
/**
- * Return previous leaf content of aStartContent inside aAncestorLimiter.
- * This does not stop at a block inclusive ancestor nor a block sibling of an
- * inclusive ancestor different from
- * GetPreviousLeafContentOrPreviousBlockElement(). However, if you specify
- * LeafNodeOption::TreatChildBlockAsLeafNode, this stops at a child block
- * boundary. So, the behavior becomes complicated so that you need to be
- * careful if you specify that.
+ * GetFirstLeafContent() returns leftmost leaf content in aNode. It depends
+ * on aLeafNodeTypes whether this scans into a block child or treat block as a
+ * leaf.
*
- * @param aStartContent The start content to scan previous content.
- * @param aOptions See LeafNodeOption.
- * @param aAncestorLimiter Optional, if you set this, it must be an
- * inclusive ancestor of aStartContent.
- */
- static nsIContent* GetPreviousLeafContent(
- const nsIContent& aStartContent, const LeafNodeOptions& aOptions,
- BlockInlineCheck aBlockInlineCheck,
- const Element* aAncestorLimiter = nullptr) {
- return GetPreviousLeafContentOrPreviousBlockElementImpl(
- aStartContent, StopAtBlockSibling::No, aOptions, aBlockInlineCheck,
- aAncestorLimiter);
- }
-
- /**
- * Similar to the above method, but take a DOM point to specify scan start
- * point.
+ * @param aBlockInlineCheck Can be Unused if aLeafNodeTypes does not contain
+ * LeafNodeOrCHildBlock.
*/
- template <typename PT, typename CT>
- static nsIContent* GetPreviousLeafContent(
- const EditorDOMPointBase<PT, CT>& aStartPoint,
- const LeafNodeOptions& aOptions, BlockInlineCheck aBlockInlineCheck,
+ static nsIContent* GetFirstLeafContent(
+ const nsINode& aNode, const LeafNodeTypes& aLeafNodeTypes,
+ BlockInlineCheck aBlockInlineCheck = BlockInlineCheck::Unused,
const Element* aAncestorLimiter = nullptr) {
- return GetPreviousLeafContentOrPreviousBlockElementImpl(
- aStartPoint, StopAtBlockSibling::No, aOptions, aBlockInlineCheck,
- aAncestorLimiter);
+ MOZ_ASSERT_IF(
+ aLeafNodeTypes.contains(LeafNodeType::OnlyEditableLeafNode),
+ !aLeafNodeTypes.contains(LeafNodeType::LeafNodeOrNonEditableNode));
+ // editor shouldn't touch child nodes which are replaced with native
+ // anonymous nodes.
+ if (aNode.IsElement() &&
+ HTMLEditUtils::IsNeverElementContentsEditableByUser(
+ *aNode.AsElement())) {
+ return nullptr;
+ }
+ for (nsIContent* content = aNode.GetFirstChild(); content;) {
+ if (aLeafNodeTypes.contains(LeafNodeType::OnlyEditableLeafNode) &&
+ !EditorUtils::IsEditableContent(*content,
+ EditorUtils::EditorType::HTML)) {
+ content = HTMLEditUtils::GetNextContent(
+ *content, {WalkTreeOption::IgnoreNonEditableNode},
+ aBlockInlineCheck, aAncestorLimiter);
+ continue;
+ }
+ if (!aLeafNodeTypes.contains(LeafNodeType::TreatCommentAsLeafNode) &&
+ content->IsComment()) {
+ content = content->GetNextSibling();
+ continue;
+ }
+ if (aLeafNodeTypes.contains(LeafNodeType::LeafNodeOrChildBlock) &&
+ HTMLEditUtils::IsBlockElement(
+ *content,
+ UseComputedDisplayOutsideStyleIfAuto(aBlockInlineCheck))) {
+ return content;
+ }
+ if (!content->HasChildren() ||
+ HTMLEditUtils::IsNeverElementContentsEditableByUser(*content)) {
+ return content;
+ }
+ if (aLeafNodeTypes.contains(LeafNodeType::LeafNodeOrNonEditableNode) &&
+ !HTMLEditUtils::IsSimplyEditableNode(*content)) {
+ return content;
+ }
+ content = content->GetFirstChild();
+ }
+ return nullptr;
}
/**
@@ -1371,17 +1555,84 @@ class HTMLEditUtils final {
* next block element of aStartContent inside aAncestorLimiter.
*
* @param aStartContent The start content to scan next content.
- * @param aOptions See LeafNodeOption.
+ * @param aLeafNodeTypes See LeafNodeType.
* @param aAncestorLimiter Optional, if you set this, it must be an
* inclusive ancestor of aStartContent.
*/
static nsIContent* GetNextLeafContentOrNextBlockElement(
- const nsIContent& aStartContent, const LeafNodeOptions& aOptions,
+ const nsIContent& aStartContent, const LeafNodeTypes& aLeafNodeTypes,
BlockInlineCheck aBlockInlineCheck,
const Element* aAncestorLimiter = nullptr) {
- return GetNextLeafContentOrNextBlockElementImpl(
- aStartContent, StopAtBlockSibling::Yes, aOptions, aBlockInlineCheck,
- aAncestorLimiter);
+ MOZ_ASSERT_IF(
+ aLeafNodeTypes.contains(LeafNodeType::OnlyEditableLeafNode),
+ !aLeafNodeTypes.contains(LeafNodeType::LeafNodeOrNonEditableNode));
+
+ if (&aStartContent == aAncestorLimiter) {
+ return nullptr;
+ }
+
+ Element* container = aStartContent.GetParentElement();
+ for (nsIContent* nextContent = aStartContent.GetNextSibling();;) {
+ if (!nextContent) {
+ if (!container) {
+ NS_WARNING("Reached orphan node while climbing up the DOM tree");
+ return nullptr;
+ }
+ for (Element* parentElement :
+ container->InclusiveAncestorsOfType<Element>()) {
+ if (parentElement == aAncestorLimiter ||
+ HTMLEditUtils::IsBlockElement(
+ *parentElement,
+ UseComputedDisplayStyleIfAuto(aBlockInlineCheck))) {
+ return nullptr;
+ }
+ if (aLeafNodeTypes.contains(
+ LeafNodeType::LeafNodeOrNonEditableNode) &&
+ !parentElement->IsEditable()) {
+ return nullptr;
+ }
+ nextContent = parentElement->GetNextSibling();
+ if (nextContent) {
+ container = nextContent->GetParentElement();
+ break;
+ }
+ if (!parentElement->GetParentElement()) {
+ NS_WARNING("Reached orphan node while climbing up the DOM tree");
+ return nullptr;
+ }
+ }
+ MOZ_ASSERT(nextContent);
+ }
+
+ if (!aLeafNodeTypes.contains(LeafNodeType::TreatCommentAsLeafNode) &&
+ nextContent->IsComment()) {
+ nextContent = nextContent->GetNextSibling();
+ continue;
+ }
+
+ // We have a next content. If it's a block, return it.
+ if (HTMLEditUtils::IsBlockElement(
+ *nextContent,
+ PreferDisplayOutsideIfUsingDisplay(
+ UseComputedDisplayStyleIfAuto(aBlockInlineCheck)))) {
+ return nextContent;
+ }
+ if (aLeafNodeTypes.contains(LeafNodeType::LeafNodeOrNonEditableNode) &&
+ !nextContent->IsEditable()) {
+ return nextContent;
+ }
+ if (HTMLEditUtils::IsContainerNode(*nextContent)) {
+ // Else if it's a container, get deep leftmost child
+ if (nsIContent* child = HTMLEditUtils::GetFirstLeafContent(
+ *nextContent, aLeafNodeTypes, aBlockInlineCheck)) {
+ return child;
+ }
+ }
+ // Else return the next content itself.
+ return nextContent;
+ }
+ MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE(
+ "Must return from the preceding for-loop");
}
/**
@@ -1391,11 +1642,76 @@ class HTMLEditUtils final {
template <typename PT, typename CT>
static nsIContent* GetNextLeafContentOrNextBlockElement(
const EditorDOMPointBase<PT, CT>& aStartPoint,
- const LeafNodeOptions& aOptions, BlockInlineCheck aBlockInlineCheck,
+ const LeafNodeTypes& aLeafNodeTypes, BlockInlineCheck aBlockInlineCheck,
const Element* aAncestorLimiter = nullptr) {
- return GetNextLeafContentOrNextBlockElementImpl(
- aStartPoint, StopAtBlockSibling::Yes, aOptions, aBlockInlineCheck,
- aAncestorLimiter);
+ MOZ_ASSERT(aStartPoint.IsSet());
+ MOZ_ASSERT_IF(
+ aLeafNodeTypes.contains(LeafNodeType::OnlyEditableLeafNode),
+ !aLeafNodeTypes.contains(LeafNodeType::LeafNodeOrNonEditableNode));
+ NS_ASSERTION(!aLeafNodeTypes.contains(LeafNodeType::OnlyEditableLeafNode),
+ "Not implemented yet");
+
+ if (!aStartPoint.IsInContentNode()) {
+ return nullptr;
+ }
+ if (aStartPoint.IsInTextNode()) {
+ return HTMLEditUtils::GetNextLeafContentOrNextBlockElement(
+ *aStartPoint.template ContainerAs<Text>(), aLeafNodeTypes,
+ aBlockInlineCheck, aAncestorLimiter);
+ }
+ if (!HTMLEditUtils::IsContainerNode(
+ *aStartPoint.template ContainerAs<nsIContent>())) {
+ return HTMLEditUtils::GetNextLeafContentOrNextBlockElement(
+ *aStartPoint.template ContainerAs<nsIContent>(), aLeafNodeTypes,
+ aBlockInlineCheck, aAncestorLimiter);
+ }
+
+ for (nsIContent* nextContent = aStartPoint.GetChild();;) {
+ if (!nextContent) {
+ if (aStartPoint.GetContainer() == aAncestorLimiter ||
+ HTMLEditUtils::IsBlockElement(
+ *aStartPoint.template ContainerAs<nsIContent>(),
+ UseComputedDisplayStyleIfAuto(aBlockInlineCheck))) {
+ // We are at end of the block.
+ return nullptr;
+ }
+
+ // We are at end of non-block container
+ return HTMLEditUtils::GetNextLeafContentOrNextBlockElement(
+ *aStartPoint.template ContainerAs<nsIContent>(), aLeafNodeTypes,
+ PreferDisplayOutsideIfUsingDisplay(aBlockInlineCheck),
+ aAncestorLimiter);
+ }
+
+ if (!aLeafNodeTypes.contains(LeafNodeType::TreatCommentAsLeafNode) &&
+ nextContent->IsComment()) {
+ nextContent = nextContent->GetNextSibling();
+ continue;
+ }
+
+ // We have a next node. If it's a block, return it.
+ if (HTMLEditUtils::IsBlockElement(
+ *nextContent,
+ UseComputedDisplayOutsideStyleIfAuto(aBlockInlineCheck))) {
+ return nextContent;
+ }
+ if (aLeafNodeTypes.contains(LeafNodeType::LeafNodeOrNonEditableNode) &&
+ !HTMLEditUtils::IsSimplyEditableNode(*nextContent)) {
+ return nextContent;
+ }
+ if (HTMLEditUtils::IsContainerNode(*nextContent)) {
+ // else if it's a container, get deep leftmost child
+ if (nsIContent* child = HTMLEditUtils::GetFirstLeafContent(
+ *nextContent, aLeafNodeTypes,
+ PreferDisplayOutsideIfUsingDisplay(aBlockInlineCheck))) {
+ return child;
+ }
+ }
+ // Else return the node itself
+ return nextContent;
+ }
+ MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE(
+ "Must return from the preceding for-loop");
}
/**
@@ -1404,17 +1720,87 @@ class HTMLEditUtils final {
* aAncestorLimiter.
*
* @param aStartContent The start content to scan previous content.
- * @param aOptions See LeafNodeOption.
+ * @param aLeafNodeTypes See LeafNodeType.
* @param aAncestorLimiter Optional, if you set this, it must be an
* inclusive ancestor of aStartContent.
*/
static nsIContent* GetPreviousLeafContentOrPreviousBlockElement(
- const nsIContent& aStartContent, const LeafNodeOptions& aOptions,
+ const nsIContent& aStartContent, const LeafNodeTypes& aLeafNodeTypes,
BlockInlineCheck aBlockInlineCheck,
const Element* aAncestorLimiter = nullptr) {
- return GetPreviousLeafContentOrPreviousBlockElementImpl(
- aStartContent, StopAtBlockSibling::Yes, aOptions, aBlockInlineCheck,
- aAncestorLimiter);
+ MOZ_ASSERT_IF(
+ aLeafNodeTypes.contains(LeafNodeType::OnlyEditableLeafNode),
+ !aLeafNodeTypes.contains(LeafNodeType::LeafNodeOrNonEditableNode));
+ NS_ASSERTION(!aLeafNodeTypes.contains(LeafNodeType::OnlyEditableLeafNode),
+ "Not implemented yet");
+
+ if (&aStartContent == aAncestorLimiter) {
+ return nullptr;
+ }
+
+ Element* container = aStartContent.GetParentElement();
+ for (nsIContent* previousContent = aStartContent.GetPreviousSibling();;) {
+ if (!previousContent) {
+ if (!container) {
+ NS_WARNING("Reached orphan node while climbing up the DOM tree");
+ return nullptr;
+ }
+ for (Element* parentElement :
+ container->InclusiveAncestorsOfType<Element>()) {
+ if (parentElement == aAncestorLimiter ||
+ HTMLEditUtils::IsBlockElement(
+ *parentElement,
+ UseComputedDisplayStyleIfAuto(aBlockInlineCheck))) {
+ return nullptr;
+ }
+ if (aLeafNodeTypes.contains(
+ LeafNodeType::LeafNodeOrNonEditableNode) &&
+ !parentElement->IsEditable()) {
+ return nullptr;
+ }
+ previousContent = parentElement->GetPreviousSibling();
+ if (previousContent) {
+ container = previousContent->GetParentElement();
+ break;
+ }
+ if (!parentElement->GetParentElement()) {
+ NS_WARNING("Reached orphan node while climbing up the DOM tree");
+ return nullptr;
+ }
+ }
+ MOZ_ASSERT(previousContent);
+ }
+
+ if (!aLeafNodeTypes.contains(LeafNodeType::TreatCommentAsLeafNode) &&
+ previousContent->IsComment()) {
+ previousContent = previousContent->GetPreviousSibling();
+ continue;
+ }
+
+ // We have a next content. If it's a block, return it.
+ if (HTMLEditUtils::IsBlockElement(
+ *previousContent,
+ PreferDisplayOutsideIfUsingDisplay(
+ UseComputedDisplayOutsideStyleIfAuto(aBlockInlineCheck)))) {
+ return previousContent;
+ }
+ if (aLeafNodeTypes.contains(LeafNodeType::LeafNodeOrNonEditableNode) &&
+ !HTMLEditUtils::IsSimplyEditableNode(*previousContent)) {
+ return previousContent;
+ }
+ if (HTMLEditUtils::IsContainerNode(*previousContent)) {
+ // Else if it's a container, get deep rightmost child
+ if (nsIContent* child = HTMLEditUtils::GetLastLeafContent(
+ *previousContent, aLeafNodeTypes,
+ PreferDisplayOutsideIfUsingDisplay(aBlockInlineCheck))) {
+ return child;
+ }
+ }
+ // Else return the next content itself.
+ return previousContent;
+ }
+ MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE(
+ "Must return from the preceding for-loop");
}
/**
@@ -1424,122 +1810,167 @@ class HTMLEditUtils final {
template <typename PT, typename CT>
static nsIContent* GetPreviousLeafContentOrPreviousBlockElement(
const EditorDOMPointBase<PT, CT>& aStartPoint,
- const LeafNodeOptions& aOptions, BlockInlineCheck aBlockInlineCheck,
+ const LeafNodeTypes& aLeafNodeTypes, BlockInlineCheck aBlockInlineCheck,
const Element* aAncestorLimiter = nullptr) {
- return GetPreviousLeafContentOrPreviousBlockElementImpl(
- aStartPoint, StopAtBlockSibling::Yes, aOptions, aBlockInlineCheck,
- aAncestorLimiter);
- }
-
- private:
- static nsIContent* GetSibling(const nsIContent& aContent,
- WalkTreeDirection aDirection,
- const LeafNodeOptions& aOptions,
- BlockInlineCheck aBlockInlineCheck);
-
- public:
- /**
- * Return the preceding sibling of aContent with ignoring leaf nodes which are
- * specified by aOptions.
- *
- * @param aOptions If a sibling is a leaf node or does not have meaningful
- * children against aOptions, the sibling is ignored.
- * NOTE: LeafNodeOption::TreatChildBlockAsLeafNode is
- * ignored.
- */
- static nsIContent* GetPreviousSibling(const nsIContent& aContent,
- const LeafNodeOptions& aOptions,
- BlockInlineCheck aBlockInlineCheck) {
- return GetSibling(aContent, WalkTreeDirection::Backward, aOptions,
- aBlockInlineCheck);
- }
-
- /**
- * Return the following sibling of aContent with ignoring leaf nodes which are
- * specified by aOptions.
- *
- * @param aOptions If a sibling is a leaf node or does not have meaningful
- * children against aOptions, the sibling is ignored.
- * NOTE: LeafNodeOption::TreatChildBlockAsLeafNode is
- * ignored.
- */
- static nsIContent* GetNextSibling(const nsIContent& aContent,
- const LeafNodeOptions& aOptions,
- BlockInlineCheck aBlockInlineCheck) {
- return GetSibling(aContent, WalkTreeDirection::Forward, aOptions,
- aBlockInlineCheck);
- }
+ MOZ_ASSERT(aStartPoint.IsSet());
+ MOZ_ASSERT_IF(
+ aLeafNodeTypes.contains(LeafNodeType::OnlyEditableLeafNode),
+ !aLeafNodeTypes.contains(LeafNodeType::LeafNodeOrNonEditableNode));
+ NS_ASSERTION(!aLeafNodeTypes.contains(LeafNodeType::OnlyEditableLeafNode),
+ "Not implemented yet");
- private:
- enum class FirstOrLastChild { First, Last };
- static nsIContent* GetFirstOrLastChild(const nsINode& aNode,
- FirstOrLastChild aFirstOrLastChild,
- const LeafNodeOptions& aOptions,
- BlockInlineCheck aBlockInlineCheck);
+ if (!aStartPoint.IsInContentNode()) {
+ return nullptr;
+ }
+ if (aStartPoint.IsInTextNode()) {
+ return HTMLEditUtils::GetPreviousLeafContentOrPreviousBlockElement(
+ *aStartPoint.template ContainerAs<Text>(), aLeafNodeTypes,
+ aBlockInlineCheck, aAncestorLimiter);
+ }
+ if (!HTMLEditUtils::IsContainerNode(
+ *aStartPoint.template ContainerAs<nsIContent>())) {
+ return HTMLEditUtils::GetPreviousLeafContentOrPreviousBlockElement(
+ *aStartPoint.template ContainerAs<nsIContent>(), aLeafNodeTypes,
+ aBlockInlineCheck, aAncestorLimiter);
+ }
- public:
- /**
- * Return the last child of aNode with ignoring leaf nodes which are specified
- * by aOptions.
- *
- * @param aOptions If a child is a leaf node or does not have meaningful
- * children against aOptions, the child is ignored.
- * NOTE: LeafNodeOption::TreatChildBlockAsLeafNode is
- * ignored.
- */
- static nsIContent* GetLastChild(const nsINode& aNode,
- const LeafNodeOptions& aOptions,
- BlockInlineCheck aBlockInlineCheck) {
- return GetFirstOrLastChild(aNode, FirstOrLastChild::Last, aOptions,
- aBlockInlineCheck);
- }
+ if (aStartPoint.IsStartOfContainer()) {
+ if (aStartPoint.GetContainer() == aAncestorLimiter ||
+ HTMLEditUtils::IsBlockElement(
+ *aStartPoint.template ContainerAs<nsIContent>(),
+ UseComputedDisplayStyleIfAuto(aBlockInlineCheck))) {
+ // We are at start of the block.
+ return nullptr;
+ }
- /**
- * Return the first child of aNode with ignoring leaf nodes which are
- * specified by aOptions.
- *
- * @param aOptions If a child is a leaf node or does not have meaningful
- * children against aOptions, the child is ignored.
- * NOTE: LeafNodeOption::TreatChildBlockAsLeafNode is
- * ignored.
- */
- static nsIContent* GetFirstChild(const nsINode& aNode,
- const LeafNodeOptions& aOptions,
- BlockInlineCheck aBlockInlineCheck) {
- return GetFirstOrLastChild(aNode, FirstOrLastChild::First, aOptions,
- aBlockInlineCheck);
+ // We are at start of non-block container
+ return HTMLEditUtils::GetPreviousLeafContentOrPreviousBlockElement(
+ *aStartPoint.template ContainerAs<nsIContent>(), aLeafNodeTypes,
+ PreferDisplayOutsideIfUsingDisplay(aBlockInlineCheck),
+ aAncestorLimiter);
+ }
+
+ for (nsIContent* previousContent = aStartPoint.GetPreviousSiblingOfChild();
+ previousContent &&
+ (aLeafNodeTypes.contains(LeafNodeType::TreatCommentAsLeafNode) ||
+ !previousContent->IsComment());
+ previousContent = previousContent->GetPreviousSibling()) {
+ // We have a prior node. If it's a block, return it.
+ if (HTMLEditUtils::IsBlockElement(
+ *previousContent,
+ UseComputedDisplayOutsideStyleIfAuto(aBlockInlineCheck))) {
+ return previousContent;
+ }
+ if (aLeafNodeTypes.contains(LeafNodeType::LeafNodeOrNonEditableNode) &&
+ !HTMLEditUtils::IsSimplyEditableNode(*previousContent)) {
+ return previousContent;
+ }
+ if (HTMLEditUtils::IsContainerNode(*previousContent)) {
+ // Else if it's a container, get deep rightmost child
+ if (nsIContent* child = HTMLEditUtils::GetLastLeafContent(
+ *previousContent, aLeafNodeTypes,
+ PreferDisplayOutsideIfUsingDisplay(aBlockInlineCheck))) {
+ return child;
+ }
+ }
+ // Else return the node itself
+ return previousContent;
+ }
+ return nullptr;
}
/**
- * Return true if aContent is the last child of aNode with ignoring all
- * children which are specified by aOptions.
+ * Return previous non-empty leaf content or child block or non-editable
+ * content (depending on aLeafNodeTypes). This ignores invisible inline leaf
+ * element like `<b></b>` and empty `Text` nodes. So, this may return
+ * invisible `Text` node, but it may be useful to consider whether we need to
+ * insert a padding <br> element.
*/
- static bool IsLastChild(const nsIContent& aContent,
- const LeafNodeOptions& aOptions,
- BlockInlineCheck aBlockInlineCheck) {
- nsINode* const parentNode = aContent.GetParentNode();
- if (MOZ_UNLIKELY(!parentNode)) {
- return false;
+ [[nodiscard]] static nsIContent*
+ GetPreviousNonEmptyLeafContentOrPreviousBlockElement(
+ const nsIContent& aContent, const LeafNodeTypes& aLeafNodeTypes,
+ BlockInlineCheck aBlockInlineCheck,
+ const Element* aAncestorLimiter = nullptr) {
+ for (nsIContent* previousContent =
+ HTMLEditUtils::GetPreviousLeafContentOrPreviousBlockElement(
+ aContent, aLeafNodeTypes, aBlockInlineCheck, aAncestorLimiter);
+ previousContent;
+ previousContent =
+ HTMLEditUtils::GetPreviousLeafContentOrPreviousBlockElement(
+ *previousContent, aLeafNodeTypes, aBlockInlineCheck,
+ aAncestorLimiter)) {
+ if (aLeafNodeTypes.contains(LeafNodeType::LeafNodeOrChildBlock) &&
+ HTMLEditUtils::IsBlockElement(
+ *previousContent,
+ PreferDisplayOutsideIfUsingDisplay(
+ UseComputedDisplayOutsideStyleIfAuto(aBlockInlineCheck)))) {
+ return previousContent; // Reached block element
+ }
+ if (aLeafNodeTypes.contains(LeafNodeType::LeafNodeOrNonEditableNode) &&
+ HTMLEditUtils::IsSimplyEditableNode(*previousContent)) {
+ return previousContent; // Reached non-editable content
+ }
+ Text* const previousText = Text::FromNode(previousContent);
+ if (!previousText) {
+ if (!HTMLEditUtils::IsVisibleElementEvenIfLeafNode(*previousContent)) {
+ continue; // Ignore invisible inline elements
+ }
+ return previousContent; // Reached visible inline element
+ }
+ if (!previousText->TextDataLength()) {
+ continue; // Ignore empty Text nodes.
+ }
+ return previousText; // Reached non-empty text
}
- return HTMLEditUtils::GetLastChild(*parentNode, aOptions,
- aBlockInlineCheck) == &aContent;
+ return nullptr;
}
/**
- * Return true if aContent is the first child of aNode with ignoring all
- * children which are specified by aOptions.
+ * Return previous visible leaf content or child block or non-editable content
+ * (depending on aLeafNodeTypes). This ignores invisible inline leaf element
+ * like `<b></b>` and empty `Text` nodes. So, this may return invisible
+ * `Text` node, but it may be useful to consider whether we need to insert a
+ * padding <br> element.
*/
- static bool IsFirstChild(const nsIContent& aContent,
- const LeafNodeOptions& aOptions,
- BlockInlineCheck aBlockInlineCheck) {
- nsINode* const parentNode = aContent.GetParentNode();
- if (MOZ_UNLIKELY(!parentNode)) {
- return false;
+ template <typename PT, typename CT>
+ [[nodiscard]] static nsIContent*
+ GetPreviousNonEmptyLeafContentOrPreviousBlockElement(
+ const EditorDOMPointBase<PT, CT>& aPoint,
+ const LeafNodeTypes& aLeafNodeTypes, BlockInlineCheck aBlockInlineCheck,
+ const Element* aAncestorLimiter = nullptr) {
+ for (nsIContent* previousContent =
+ HTMLEditUtils::GetPreviousLeafContentOrPreviousBlockElement(
+ aPoint, aLeafNodeTypes, aBlockInlineCheck, aAncestorLimiter);
+ previousContent;
+ previousContent =
+ HTMLEditUtils::GetPreviousLeafContentOrPreviousBlockElement(
+ *previousContent, aLeafNodeTypes, aBlockInlineCheck,
+ aAncestorLimiter)) {
+ if (aLeafNodeTypes.contains(LeafNodeType::LeafNodeOrChildBlock) &&
+ HTMLEditUtils::IsBlockElement(
+ *previousContent,
+ PreferDisplayOutsideIfUsingDisplay(
+ UseComputedDisplayOutsideStyleIfAuto(aBlockInlineCheck)))) {
+ return previousContent; // Reached block element
+ }
+ if (aLeafNodeTypes.contains(LeafNodeType::LeafNodeOrNonEditableNode) &&
+ HTMLEditUtils::IsSimplyEditableNode(*previousContent)) {
+ return previousContent; // Reached non-editable content
+ }
+ Text* const previousText = Text::FromNode(previousContent);
+ if (!previousText) {
+ if (!HTMLEditUtils::IsVisibleElementEvenIfLeafNode(*previousContent)) {
+ continue; // Ignore invisible inline elements
+ }
+ return previousContent; // Reached visible inline element
+ }
+ if (!previousText->TextDataLength()) {
+ continue; // Ignore empty Text nodes.
+ }
+ return previousText; // Reached non-empty text
}
- return HTMLEditUtils::GetFirstChild(*parentNode, aOptions,
- aBlockInlineCheck) == &aContent;
+ return nullptr;
}
-
/**
* Returns a content node whose inline styles should be preserved after
* deleting content in a range. Typically, you should set aPoint to start
@@ -1990,30 +2421,6 @@ class HTMLEditUtils final {
return GetTableCellElementIfOnlyOneSelected(*firstRange);
}
- private:
- static uint32_t CountMeaningfulChildren(const nsINode& aNode,
- const LeafNodeOptions& aOptions,
- BlockInlineCheck aBlockInlineCheck) {
- uint32_t count = 0;
- for (nsIContent* child = aNode.GetFirstChild(); child;
- child = child->GetNextSibling()) {
- const LeafNodeType leafNodeType = HTMLEditUtils::GetLeafNodeType(
- *child, aOptions, aBlockInlineCheck, IgnoreChildren::No);
- if (leafNodeType == LeafNodeType::Ignore) {
- continue;
- }
- if (leafNodeType == LeafNodeType::NonEmptyContainer) {
- if (!HTMLEditUtils::GetFirstLeafContent(*child, aOptions,
- aBlockInlineCheck)) {
- continue;
- }
- }
- ++count;
- }
- return count;
- }
-
- public:
/**
* GetInclusiveFirstChildWhichHasOneChild() returns the deepest element whose
* tag name is one of `aFirstElementName` and `aOtherElementNames...` if and
@@ -2028,7 +2435,7 @@ class HTMLEditUtils final {
*/
template <typename FirstElementName, typename... OtherElementNames>
static Element* GetInclusiveDeepestFirstChildWhichHasOneChild(
- const nsINode& aNode, const LeafNodeOptions& aOptions,
+ const nsINode& aNode, const WalkTreeOptions& aOptions,
BlockInlineCheck aBlockInlineCheck, FirstElementName aFirstElementName,
OtherElementNames... aOtherElementNames) {
if (!aNode.IsElement()) {
@@ -2038,10 +2445,11 @@ class HTMLEditUtils final {
for (nsIContent* content = const_cast<nsIContent*>(aNode.AsContent());
content && content->IsElement() &&
content->IsAnyOfHTMLElements(aFirstElementName, aOtherElementNames...);
- content = HTMLEditUtils::GetFirstChild(*content, aOptions,
- aBlockInlineCheck)) {
- if (HTMLEditUtils::CountMeaningfulChildren(*content, aOptions,
- aBlockInlineCheck) != 1) {
+ // XXX Why do we scan only the first child of every element? If it's
+ // not editable, why do we ignore it when aOptions specifies so.
+ content = content->GetFirstChild()) {
+ if (HTMLEditUtils::CountChildren(*content, aOptions, aBlockInlineCheck) !=
+ 1) {
return content->AsElement();
}
parentElement = content->AsElement();
@@ -2059,10 +2467,13 @@ class HTMLEditUtils final {
template <typename EditorLineBreakType>
static Maybe<EditorLineBreakType> GetFirstLineBreak(
const dom::Element& aElement) {
- for (nsIContent* content = HTMLEditUtils::GetFirstLeafContent(aElement, {});
- content; content = HTMLEditUtils::GetNextLeafContent(
- *content, {LeafNodeOption::IgnoreInvisibleText},
- BlockInlineCheck::Auto, &aElement)) {
+ for (nsIContent* content = HTMLEditUtils::GetFirstLeafContent(
+ aElement, {LeafNodeType::OnlyLeafNode});
+ content; content = HTMLEditUtils::GetNextContent(
+ *content,
+ {WalkTreeOption::IgnoreDataNodeExceptText,
+ WalkTreeOption::IgnoreWhiteSpaceOnlyText},
+ BlockInlineCheck::Unused, &aElement)) {
if (auto* brElement = dom::HTMLBRElement::FromNode(*content)) {
return Some(EditorLineBreakType(*brElement));
}
@@ -2854,6 +3265,57 @@ class HTMLEditUtils final {
return !cannotCrossBoundary;
}
+ static bool IsContentIgnored(const nsIContent& aContent,
+ const WalkTreeOptions& aOptions) {
+ if (aOptions.contains(WalkTreeOption::IgnoreNonEditableNode) &&
+ !EditorUtils::IsEditableContent(aContent,
+ EditorUtils::EditorType::HTML)) {
+ return true;
+ }
+ if (aOptions.contains(WalkTreeOption::IgnoreDataNodeExceptText) &&
+ !EditorUtils::IsElementOrText(aContent)) {
+ return true;
+ }
+ if (aOptions.contains(WalkTreeOption::IgnoreWhiteSpaceOnlyText) &&
+ aContent.IsText() &&
+ const_cast<Text*>(aContent.AsText())->TextIsOnlyWhitespace()) {
+ return true;
+ }
+ return false;
+ }
+
+ static uint32_t CountChildren(const nsINode& aNode,
+ const WalkTreeOptions& aOptions,
+ BlockInlineCheck aBlockInlineCheck) {
+ uint32_t count = 0;
+ for (nsIContent* child = aNode.GetFirstChild(); child;
+ child = child->GetNextSibling()) {
+ if (HTMLEditUtils::IsContentIgnored(*child, aOptions)) {
+ continue;
+ }
+ if (aOptions.contains(WalkTreeOption::StopAtBlockBoundary) &&
+ HTMLEditUtils::IsBlockElement(
+ *child,
+ UseComputedDisplayOutsideStyleIfAuto(aBlockInlineCheck))) {
+ break;
+ }
+ ++count;
+ }
+ return count;
+ }
+
+ /**
+ * Helper for GetPreviousContent() and GetNextContent().
+ */
+ static nsIContent* GetAdjacentLeafContent(
+ const nsINode& aNode, WalkTreeDirection aWalkTreeDirection,
+ const WalkTreeOptions& aOptions, BlockInlineCheck aBlockInlineCheck,
+ const Element* aAncestorLimiter = nullptr);
+ static nsIContent* GetAdjacentContent(
+ const nsINode& aNode, WalkTreeDirection aWalkTreeDirection,
+ const WalkTreeOptions& aOptions, BlockInlineCheck aBlockInlineCheck,
+ const Element* aAncestorLimiter = nullptr);
+
/**
* GetElementOfImmediateBlockBoundary() returns a block element if its
* block boundary and aContent may be first visible thing before/after the
diff --git a/editor/libeditor/HTMLEditor.cpp b/editor/libeditor/HTMLEditor.cpp
@@ -100,8 +100,9 @@ using namespace widget;
LazyLogModule gHTMLEditorFocusLog("HTMLEditorFocus");
using EmptyCheckOption = HTMLEditUtils::EmptyCheckOption;
-using LeafNodeOption = HTMLEditUtils::LeafNodeOption;
-using LeafNodeOptions = HTMLEditUtils::LeafNodeOptions;
+using LeafNodeType = HTMLEditUtils::LeafNodeType;
+using LeafNodeTypes = HTMLEditUtils::LeafNodeTypes;
+using WalkTreeOption = HTMLEditUtils::WalkTreeOption;
// Some utilities to handle overloading of "A" tag for link and named anchor.
static bool IsLinkTag(const nsAtom& aTagName) {
@@ -1040,8 +1041,8 @@ nsresult HTMLEditor::CollapseSelectionToEndOfLastLeafNodeOfDocument() const {
}
auto pointToPutCaret = [&]() -> EditorRawDOMPoint {
- nsCOMPtr<nsIContent> lastLeafContent =
- HTMLEditUtils::GetLastLeafContent(*bodyOrDocumentElement, {});
+ nsCOMPtr<nsIContent> lastLeafContent = HTMLEditUtils::GetLastLeafContent(
+ *bodyOrDocumentElement, {LeafNodeType::OnlyLeafNode});
if (!lastLeafContent) {
return EditorRawDOMPoint::AtEndOf(*bodyOrDocumentElement);
}
@@ -1135,15 +1136,11 @@ nsresult HTMLEditor::MaybeCollapseSelectionAtFirstEditableNode(
}
}
- constexpr LeafNodeOptions leafNodeOptions = {
- LeafNodeOption::TreatNonEditableNodeAsLeafNode,
- LeafNodeOption::TreatChildBlockAsLeafNode,
- // FIXME: Ignore empty inline containers such as <span></span> because we
- // cannot visually put caret into it.
- };
for (nsIContent* leafContent = HTMLEditUtils::GetFirstLeafContent(
- *editingHost, leafNodeOptions,
- BlockInlineCheck::UseComputedDisplayStyle);
+ *editingHost,
+ {LeafNodeType::LeafNodeOrNonEditableNode,
+ LeafNodeType::LeafNodeOrChildBlock},
+ BlockInlineCheck::UseComputedDisplayStyle, editingHost);
leafContent;) {
// If we meet a non-editable node first, we should move caret to start
// of the container block or editing host.
@@ -1180,7 +1177,9 @@ nsresult HTMLEditor::MaybeCollapseSelectionAtFirstEditableNode(
// Chromium collapses selection to start of the editing host when this
// is the last leaf content. So, we don't need special handling here.
leafContent = HTMLEditUtils::GetNextLeafContentOrNextBlockElement(
- *leafElement, leafNodeOptions,
+ *leafElement,
+ {LeafNodeType::LeafNodeOrNonEditableNode,
+ LeafNodeType::LeafNodeOrChildBlock},
BlockInlineCheck::UseComputedDisplayStyle, editingHost);
continue;
}
@@ -1204,7 +1203,9 @@ nsresult HTMLEditor::MaybeCollapseSelectionAtFirstEditableNode(
}
// If it's an invisible text node, keep scanning next leaf.
leafContent = HTMLEditUtils::GetNextLeafContentOrNextBlockElement(
- *leafContent, leafNodeOptions,
+ *leafContent,
+ {LeafNodeType::LeafNodeOrNonEditableNode,
+ LeafNodeType::LeafNodeOrChildBlock},
BlockInlineCheck::UseComputedDisplayStyle, editingHost);
continue;
}
@@ -1239,15 +1240,19 @@ nsresult HTMLEditor::MaybeCollapseSelectionAtFirstEditableNode(
EmptyCheckOption::TreatNonEditableContentAsInvisible}) &&
!HTMLEditUtils::IsNeverElementContentsEditableByUser(*leafContent)) {
leafContent = HTMLEditUtils::GetFirstLeafContent(
- *leafContent, leafNodeOptions,
- BlockInlineCheck::UseComputedDisplayStyle);
+ *leafContent,
+ {LeafNodeType::LeafNodeOrNonEditableNode,
+ LeafNodeType::LeafNodeOrChildBlock},
+ BlockInlineCheck::UseComputedDisplayStyle, editingHost);
continue;
}
// Otherwise, we must meet an empty block element or a data node like
// comment node. Let's ignore it.
leafContent = HTMLEditUtils::GetNextLeafContentOrNextBlockElement(
- *leafContent, leafNodeOptions,
+ *leafContent,
+ {LeafNodeType::LeafNodeOrNonEditableNode,
+ LeafNodeType::LeafNodeOrChildBlock},
BlockInlineCheck::UseComputedDisplayStyle, editingHost);
}
@@ -1987,9 +1992,8 @@ nsresult HTMLEditor::InsertElementAtSelectionAsAction(
// check for inserting a whole table at the end of a block. If so insert
// a br after it.
if (!aElement->IsHTMLElement(nsGkAtoms::table) ||
- !HTMLEditUtils::IsLastChild(
- *aElement, {LeafNodeOption::IgnoreNonEditableNode},
- BlockInlineCheck::UseComputedDisplayOutsideStyle)) {
+ !HTMLEditUtils::IsLastChild(*aElement,
+ {WalkTreeOption::IgnoreNonEditableNode})) {
return NS_OK;
}
@@ -3302,9 +3306,7 @@ already_AddRefed<Element> HTMLEditor::GetSelectedElement(const nsAtom* aTagName,
return nullptr;
}
nsIContent* firstEditableLeaf = HTMLEditUtils::GetFirstLeafContent(
- *nextSibling,
- // XXX Should we ignore invisible inline elements and text nodes?
- {});
+ *nextSibling, {LeafNodeType::OnlyLeafNode});
if (firstEditableLeaf &&
firstEditableLeaf->IsHTMLElement(nsGkAtoms::br)) {
return nullptr;
@@ -4932,18 +4934,16 @@ HTMLEditor::RemoveBlockContainerWithTransaction(Element& aElement) {
}
EditorDOMPoint pointToPutCaret;
if (HTMLEditUtils::CanNodeContain(*parentElement, *nsGkAtoms::br)) {
- if (const nsCOMPtr<nsIContent> child = HTMLEditUtils::GetFirstChild(
- aElement, {LeafNodeOption::IgnoreNonEditableNode},
- BlockInlineCheck::UseComputedDisplayOutsideStyle)) {
+ if (nsCOMPtr<nsIContent> child = HTMLEditUtils::GetFirstChild(
+ aElement, {WalkTreeOption::IgnoreNonEditableNode})) {
// The case of aNode not being empty. We need a br at start unless:
// 1) previous sibling of aNode is a block, OR
// 2) previous sibling of aNode is a br, OR
// 3) first child of aNode is a block OR
// 4) either is null
- if (nsIContent* const previousSibling = HTMLEditUtils::GetPreviousSibling(
- aElement, {LeafNodeOption::IgnoreNonEditableNode},
- BlockInlineCheck::UseComputedDisplayOutsideStyle)) {
+ if (nsIContent* previousSibling = HTMLEditUtils::GetPreviousSibling(
+ aElement, {WalkTreeOption::IgnoreNonEditableNode})) {
if (!HTMLEditUtils::IsBlockElement(
*previousSibling,
BlockInlineCheck::UseComputedDisplayOutsideStyle) &&
@@ -4973,15 +4973,14 @@ HTMLEditor::RemoveBlockContainerWithTransaction(Element& aElement) {
// 3) last child of aNode is a br OR
// 4) either is null
- if (nsIContent* const nextSibling = HTMLEditUtils::GetNextSibling(
- aElement, {LeafNodeOption::IgnoreNonEditableNode},
- BlockInlineCheck::UseComputedDisplayOutsideStyle)) {
+ if (nsIContent* nextSibling = HTMLEditUtils::GetNextSibling(
+ aElement, {WalkTreeOption::IgnoreNonEditableNode})) {
if (nextSibling &&
!HTMLEditUtils::IsBlockElement(
*nextSibling, BlockInlineCheck::UseComputedDisplayStyle)) {
- if (nsIContent* const lastChild = HTMLEditUtils::GetLastChild(
- aElement, {LeafNodeOption::IgnoreNonEditableNode},
- BlockInlineCheck::UseComputedDisplayOutsideStyle)) {
+ if (nsIContent* lastChild = HTMLEditUtils::GetLastChild(
+ aElement, {WalkTreeOption::IgnoreNonEditableNode},
+ BlockInlineCheck::Unused)) {
if (!HTMLEditUtils::IsBlockElement(
*lastChild, BlockInlineCheck::UseComputedDisplayStyle) &&
!lastChild->IsHTMLElement(nsGkAtoms::br)) {
@@ -5004,10 +5003,8 @@ HTMLEditor::RemoveBlockContainerWithTransaction(Element& aElement) {
}
}
}
- } else if (nsIContent* const previousSibling =
- HTMLEditUtils::GetPreviousSibling(
- aElement, {LeafNodeOption::IgnoreNonEditableNode},
- BlockInlineCheck::UseComputedDisplayOutsideStyle)) {
+ } else if (nsIContent* previousSibling = HTMLEditUtils::GetPreviousSibling(
+ aElement, {WalkTreeOption::IgnoreNonEditableNode})) {
// The case of aNode being empty. We need a br at start unless:
// 1) previous sibling of aNode is a block, OR
// 2) previous sibling of aNode is a br, OR
@@ -5018,8 +5015,7 @@ HTMLEditor::RemoveBlockContainerWithTransaction(Element& aElement) {
*previousSibling, BlockInlineCheck::UseComputedDisplayStyle) &&
!previousSibling->IsHTMLElement(nsGkAtoms::br)) {
if (nsIContent* nextSibling = HTMLEditUtils::GetNextSibling(
- aElement, {LeafNodeOption::IgnoreNonEditableNode},
- BlockInlineCheck::UseComputedDisplayOutsideStyle)) {
+ aElement, {WalkTreeOption::IgnoreNonEditableNode})) {
if (!HTMLEditUtils::IsBlockElement(
*nextSibling, BlockInlineCheck::UseComputedDisplayStyle) &&
!nextSibling->IsHTMLElement(nsGkAtoms::br)) {
@@ -6684,14 +6680,16 @@ HTMLEditor::CopyLastEditableChildStylesWithTransaction(
// Look for the deepest last editable leaf node in aPreviousBlock.
// Then, if found one is a <br> element, look for non-<br> element.
- nsIContent* deepestEditableContent = HTMLEditUtils::GetPreviousLeafContent(
- EditorRawDOMPoint::AtEndOf(aPreviousBlock),
- {LeafNodeOption::IgnoreNonEditableNode},
- BlockInlineCheck::UseComputedDisplayOutsideStyle);
+ nsIContent* deepestEditableContent = nullptr;
+ for (nsCOMPtr<nsIContent> child = &aPreviousBlock; child;
+ child = HTMLEditUtils::GetLastChild(
+ *child, {WalkTreeOption::IgnoreNonEditableNode})) {
+ deepestEditableContent = child;
+ }
while (deepestEditableContent &&
deepestEditableContent->IsHTMLElement(nsGkAtoms::br)) {
- deepestEditableContent = HTMLEditUtils::GetPreviousLeafContent(
- *deepestEditableContent, {LeafNodeOption::IgnoreNonEditableNode},
+ deepestEditableContent = HTMLEditUtils::GetPreviousContent(
+ *deepestEditableContent, {WalkTreeOption::IgnoreNonEditableNode},
BlockInlineCheck::UseComputedDisplayOutsideStyle, &aEditingHost);
}
if (!deepestEditableContent) {
diff --git a/editor/libeditor/HTMLEditorDataTransfer.cpp b/editor/libeditor/HTMLEditorDataTransfer.cpp
@@ -97,7 +97,7 @@ namespace mozilla {
using namespace dom;
using EmptyCheckOption = HTMLEditUtils::EmptyCheckOption;
-using LeafNodeOption = HTMLEditUtils::LeafNodeOption;
+using LeafNodeType = HTMLEditUtils::LeafNodeType;
#define kInsertCookie "_moz_Insert Here_moz_"
@@ -506,13 +506,9 @@ HTMLEditor::HTMLWithContextInserter::GetNewCaretPointAfterInsertingHTML(
if (!aLastInsertedPoint.GetChild() ||
!aLastInsertedPoint.GetChild()->IsHTMLElement(nsGkAtoms::table)) {
containerContent = HTMLEditUtils::GetLastLeafContent(
- *aLastInsertedPoint.GetChild(),
- {
- LeafNodeOption::IgnoreNonEditableNode,
- LeafNodeOption::IgnoreInvisibleText,
- // FIXME: We cannot visually put caret into empty inline containers
- // like <span></span> so that let's ignore them.
- });
+ *aLastInsertedPoint.GetChild(), {LeafNodeType::OnlyEditableLeafNode},
+ BlockInlineCheck::Unused,
+ aLastInsertedPoint.GetChild()->GetAsElementOrParentElement());
if (containerContent) {
Element* mostDistantInclusiveAncestorTableElement = nullptr;
for (Element* maybeTableElement =
@@ -563,12 +559,9 @@ HTMLEditor::HTMLWithContextInserter::GetNewCaretPointAfterInsertingHTML(
{WSRunScanner::Option::OnlyEditableNodes},
EditorRawDOMPoint(prevVisibleThing.BRElementPtr()));
if (prevVisibleThingOfBRElement.InVisibleOrCollapsibleCharacters()) {
- // XXX Why not end of the text node?
pointToPutCaret = prevVisibleThingOfBRElement
.PointAfterReachedContent<EditorDOMPoint>();
- } else if (prevVisibleThingOfBRElement.ReachedSpecialContent() ||
- prevVisibleThingOfBRElement
- .ReachedEmptyInlineContainerElement()) {
+ } else if (prevVisibleThingOfBRElement.ReachedSpecialContent()) {
pointToPutCaret = prevVisibleThingOfBRElement
.PointAfterReachedContentNode<EditorDOMPoint>();
}
diff --git a/editor/libeditor/HTMLEditorDeleteHandler.cpp b/editor/libeditor/HTMLEditorDeleteHandler.cpp
@@ -60,9 +60,10 @@ using EditablePointOption = HTMLEditUtils::EditablePointOption;
using EditablePointOptions = HTMLEditUtils::EditablePointOptions;
using EmptyCheckOption = HTMLEditUtils::EmptyCheckOption;
using InvisibleWhiteSpaces = HTMLEditUtils::InvisibleWhiteSpaces;
-using LeafNodeOption = HTMLEditUtils::LeafNodeOption;
+using LeafNodeType = HTMLEditUtils::LeafNodeType;
using ScanLineBreak = HTMLEditUtils::ScanLineBreak;
using TableBoundary = HTMLEditUtils::TableBoundary;
+using WalkTreeOption = HTMLEditUtils::WalkTreeOption;
static LazyLogModule gOneLineMoverLog("AutoMoveOneLineHandler");
@@ -865,7 +866,7 @@ nsresult HTMLEditor::AutoDeleteRangesHandler::ComputeRangesToDelete(
if (scanFromCaretPointResult.BRElementPtr() == &aEditingHost) {
return NS_OK;
}
- if (!scanFromCaretPointResult.ContentIsEditable()) {
+ if (!scanFromCaretPointResult.IsContentEditable()) {
return NS_SUCCESS_DOM_NO_OPERATION;
}
if (scanFromCaretPointResult.ReachedInvisibleBRElement()) {
@@ -1161,7 +1162,7 @@ Result<EditActionResult, nsresult> HTMLEditor::AutoDeleteRangesHandler::Run(
if (scanFromCaretPointResult.BRElementPtr() == &aEditingHost) {
return EditActionResult::HandledResult();
}
- if (!scanFromCaretPointResult.ContentIsEditable()) {
+ if (!scanFromCaretPointResult.IsContentEditable()) {
return EditActionResult::CanceledResult();
}
if (scanFromCaretPointResult.ReachedInvisibleBRElement()) {
@@ -1275,7 +1276,6 @@ HTMLEditor::AutoDeleteRangesHandler::ComputeRangesToDeleteAroundCollapsedRanges(
}
if (aScanFromCaretPointResult.ReachedSpecialContent() ||
- aScanFromCaretPointResult.ReachedEmptyInlineContainerElement() ||
aScanFromCaretPointResult.ReachedBRElement() ||
aScanFromCaretPointResult.ReachedHRElement() ||
aScanFromCaretPointResult.ReachedNonEditableOtherBlockElement()) {
@@ -1408,7 +1408,6 @@ HTMLEditor::AutoDeleteRangesHandler::HandleDeleteAroundCollapsedRanges(
}
if (aScanFromCaretPointResult.ReachedSpecialContent() ||
- aScanFromCaretPointResult.ReachedEmptyInlineContainerElement() ||
aScanFromCaretPointResult.ReachedBRElement() ||
aScanFromCaretPointResult.ReachedHRElement() ||
aScanFromCaretPointResult.ReachedNonEditableOtherBlockElement()) {
@@ -1658,8 +1657,7 @@ nsIContent* HTMLEditor::AutoDeleteRangesHandler::GetAtomicContentToDelete(
const WSScanResult& aScanFromCaretPointResult) {
MOZ_ASSERT(aScanFromCaretPointResult.GetContent());
- if (!aScanFromCaretPointResult.ReachedSpecialContent() &&
- !aScanFromCaretPointResult.ReachedEmptyInlineContainerElement()) {
+ if (!aScanFromCaretPointResult.ReachedSpecialContent()) {
return aScanFromCaretPointResult.GetContent();
}
@@ -1957,10 +1955,11 @@ nsIContent* HTMLEditor::AutoDeleteRangesHandler::AutoBlockElementsJoiner::
MOZ_ASSERT(mOtherBlockElement);
return aDirectionAndAmount == nsIEditor::ePrevious
? HTMLEditUtils::GetLastLeafContent(
- *mOtherBlockElement, {LeafNodeOption::IgnoreNonEditableNode})
+ *mOtherBlockElement, {LeafNodeType::OnlyEditableLeafNode},
+ BlockInlineCheck::Unused, mOtherBlockElement)
: HTMLEditUtils::GetFirstLeafContent(
- *mOtherBlockElement,
- {LeafNodeOption::IgnoreNonEditableNode});
+ *mOtherBlockElement, {LeafNodeType::OnlyEditableLeafNode},
+ BlockInlineCheck::Unused, mOtherBlockElement);
}
nsresult HTMLEditor::AutoDeleteRangesHandler::AutoBlockElementsJoiner::
@@ -2083,7 +2082,7 @@ Result<EditActionResult, nsresult> HTMLEditor::AutoDeleteRangesHandler::
const WSScanResult maybePreviousText =
scanner.ScanPreviousVisibleNodeOrBlockBoundaryFrom(
EditorRawDOMPoint(mBRElement));
- if (maybePreviousText.ContentIsEditable() &&
+ if (maybePreviousText.IsContentEditable() &&
maybePreviousText.InVisibleOrCollapsibleCharacters() &&
!HTMLEditor::GetLinkElement(maybePreviousText.TextPtr())) {
return maybePreviousText.PointAfterReachedContent<EditorDOMPoint>();
@@ -2091,7 +2090,7 @@ Result<EditActionResult, nsresult> HTMLEditor::AutoDeleteRangesHandler::
const WSScanResult maybeNextText =
scanner.ScanInclusiveNextVisibleNodeOrBlockBoundaryFrom(
EditorRawDOMPoint::After(*mBRElement));
- if (maybeNextText.ContentIsEditable() &&
+ if (maybeNextText.IsContentEditable() &&
maybeNextText.InVisibleOrCollapsibleCharacters()) {
return maybeNextText.PointAtReachedContent<EditorDOMPoint>();
}
@@ -2550,15 +2549,15 @@ bool HTMLEditor::AutoDeleteRangesHandler::AutoBlockElementsJoiner::
return false;
}
- auto ScanJoinTarget = [&]() MOZ_NEVER_INLINE_DEBUG -> nsIContent* {
+ auto ScanJoinTarget = [&]() -> nsIContent* {
nsIContent* targetContent =
aDirectionAndAmount == nsIEditor::ePrevious
- ? HTMLEditUtils::GetPreviousLeafContent(
- aCurrentBlockElement, {LeafNodeOption::IgnoreNonEditableNode},
- BlockInlineCheck::Auto, &aEditingHost)
- : HTMLEditUtils::GetNextLeafContent(
- aCurrentBlockElement, {LeafNodeOption::IgnoreNonEditableNode},
- BlockInlineCheck::Auto, &aEditingHost);
+ ? HTMLEditUtils::GetPreviousContent(
+ aCurrentBlockElement, {WalkTreeOption::IgnoreNonEditableNode},
+ BlockInlineCheck::Unused, &aEditingHost)
+ : HTMLEditUtils::GetNextContent(
+ aCurrentBlockElement, {WalkTreeOption::IgnoreNonEditableNode},
+ BlockInlineCheck::Unused, &aEditingHost);
// If found content is an invisible text node, let's scan visible things.
auto IsIgnorableDataNode = [](nsIContent* aContent) {
return aContent && HTMLEditUtils::IsRemovableNode(*aContent) &&
@@ -2573,27 +2572,23 @@ bool HTMLEditor::AutoDeleteRangesHandler::AutoBlockElementsJoiner::
MOZ_ASSERT(mSkippedInvisibleContents.IsEmpty());
for (nsIContent* adjacentContent =
aDirectionAndAmount == nsIEditor::ePrevious
- ? HTMLEditUtils::GetPreviousLeafContentOrPreviousBlockElement(
- *targetContent,
- {LeafNodeOption::TreatChildBlockAsLeafNode},
+ ? HTMLEditUtils::GetPreviousContent(
+ *targetContent, {WalkTreeOption::StopAtBlockBoundary},
BlockInlineCheck::UseComputedDisplayOutsideStyle,
&aEditingHost)
- : HTMLEditUtils::GetNextLeafContentOrNextBlockElement(
- *targetContent,
- {LeafNodeOption::TreatChildBlockAsLeafNode},
+ : HTMLEditUtils::GetNextContent(
+ *targetContent, {WalkTreeOption::StopAtBlockBoundary},
BlockInlineCheck::UseComputedDisplayOutsideStyle,
&aEditingHost);
adjacentContent;
adjacentContent =
aDirectionAndAmount == nsIEditor::ePrevious
- ? HTMLEditUtils::GetPreviousLeafContentOrPreviousBlockElement(
- *adjacentContent,
- {LeafNodeOption::TreatChildBlockAsLeafNode},
+ ? HTMLEditUtils::GetPreviousContent(
+ *adjacentContent, {WalkTreeOption::StopAtBlockBoundary},
BlockInlineCheck::UseComputedDisplayOutsideStyle,
&aEditingHost)
- : HTMLEditUtils::GetNextLeafContentOrNextBlockElement(
- *adjacentContent,
- {LeafNodeOption::TreatChildBlockAsLeafNode},
+ : HTMLEditUtils::GetNextContent(
+ *adjacentContent, {WalkTreeOption::StopAtBlockBoundary},
BlockInlineCheck::UseComputedDisplayOutsideStyle,
&aEditingHost)) {
// If non-editable element is found, we should not skip it to avoid
@@ -2608,10 +2603,9 @@ bool HTMLEditor::AutoDeleteRangesHandler::AutoBlockElementsJoiner::
nsIContent* leafContent =
aDirectionAndAmount == nsIEditor::ePrevious
? HTMLEditUtils::GetLastLeafContent(
- *adjacentContent, {LeafNodeOption::IgnoreNonEditableNode})
+ *adjacentContent, {LeafNodeType::OnlyEditableLeafNode})
: HTMLEditUtils::GetFirstLeafContent(
- *adjacentContent,
- {LeafNodeOption::IgnoreNonEditableNode});
+ *adjacentContent, {LeafNodeType::OnlyEditableLeafNode});
mSkippedInvisibleContents.AppendElement(*targetContent);
return leafContent ? leafContent : adjacentContent;
}
@@ -3711,7 +3705,7 @@ Result<EditActionResult, nsresult> HTMLEditor::AutoDeleteRangesHandler::
const WSScanResult maybePreviousText =
WSRunScanner::ScanPreviousVisibleNodeOrBlockBoundary(
{}, startOfRightContent, &aEditingHost);
- if (maybePreviousText.ContentIsEditable() &&
+ if (maybePreviousText.IsContentEditable() &&
maybePreviousText.InVisibleOrCollapsibleCharacters()) {
nsresult rv = aHTMLEditor.CollapseSelectionTo(
maybePreviousText.PointAfterReachedContent<EditorRawDOMPoint>());
@@ -4622,11 +4616,9 @@ Result<EditActionResult, nsresult> HTMLEditor::AutoDeleteRangesHandler::
Element::FromNodeOrNull(moveFirstLineResult.DeleteRangeRef()
.GetClosestCommonInclusiveAncestor());
nsIContent* const previousVisibleLeafOrChildBlock =
- HTMLEditUtils::GetPreviousLeafContentOrPreviousBlockElement(
+ HTMLEditUtils::GetPreviousNonEmptyLeafContentOrPreviousBlockElement(
moveFirstLineResult.DeleteRangeRef().EndRef(),
- {LeafNodeOption::TreatChildBlockAsLeafNode,
- LeafNodeOption::IgnoreInvisibleEmptyInlineContainers,
- LeafNodeOption::IgnoreInvisibleText},
+ {LeafNodeType::LeafNodeOrChildBlock},
BlockInlineCheck::UseComputedDisplayOutsideStyle, commonAncestor);
if (!previousVisibleLeafOrChildBlock) {
return false;
@@ -5049,11 +5041,10 @@ HTMLEditor::AutoDeleteRangesHandler::ComputeRangeToDeleteRangeWithTransaction(
EditorRawDOMPoint caretPoint(aRangeToDelete.StartRef());
if (howToHandleCollapsedRange ==
EditorBase::HowToHandleCollapsedRange::ExtendBackward &&
- caretPoint.IsStartOfContainer() && caretPoint.IsInContentNode()) {
- nsIContent* previousEditableContent = HTMLEditUtils::GetPreviousLeafContent(
- *caretPoint.ContainerAs<nsIContent>(),
- {LeafNodeOption::IgnoreNonEditableNode}, BlockInlineCheck::Auto,
- &aEditingHost);
+ caretPoint.IsStartOfContainer()) {
+ nsIContent* previousEditableContent = HTMLEditUtils::GetPreviousContent(
+ *caretPoint.GetContainer(), {WalkTreeOption::IgnoreNonEditableNode},
+ BlockInlineCheck::Unused, &aEditingHost);
if (!previousEditableContent) {
return NS_OK;
}
@@ -5073,11 +5064,10 @@ HTMLEditor::AutoDeleteRangesHandler::ComputeRangeToDeleteRangeWithTransaction(
if (howToHandleCollapsedRange ==
EditorBase::HowToHandleCollapsedRange::ExtendForward &&
- caretPoint.IsEndOfContainer() && caretPoint.IsInContentNode()) {
- nsIContent* nextEditableContent = HTMLEditUtils::GetNextLeafContent(
- *caretPoint.ContainerAs<nsIContent>(),
- {LeafNodeOption::IgnoreNonEditableNode}, BlockInlineCheck::Auto,
- &aEditingHost);
+ caretPoint.IsEndOfContainer()) {
+ nsIContent* nextEditableContent = HTMLEditUtils::GetNextContent(
+ *caretPoint.GetContainer(), {WalkTreeOption::IgnoreNonEditableNode},
+ BlockInlineCheck::Unused, &aEditingHost);
if (!nextEditableContent) {
return NS_OK;
}
@@ -5114,12 +5104,12 @@ HTMLEditor::AutoDeleteRangesHandler::ComputeRangeToDeleteRangeWithTransaction(
nsIContent* editableContent =
howToHandleCollapsedRange ==
EditorBase::HowToHandleCollapsedRange::ExtendBackward
- ? HTMLEditUtils::GetPreviousLeafContent(
- caretPoint, {LeafNodeOption::IgnoreNonEditableNode},
- BlockInlineCheck::Auto, &aEditingHost)
- : HTMLEditUtils::GetNextLeafContent(
- caretPoint, {LeafNodeOption::IgnoreNonEditableNode},
- BlockInlineCheck::Auto, &aEditingHost);
+ ? HTMLEditUtils::GetPreviousContent(
+ caretPoint, {WalkTreeOption::IgnoreNonEditableNode},
+ BlockInlineCheck::Unused, &aEditingHost)
+ : HTMLEditUtils::GetNextContent(
+ caretPoint, {WalkTreeOption::IgnoreNonEditableNode},
+ BlockInlineCheck::Unused, &aEditingHost);
if (!editableContent) {
return NS_OK;
}
@@ -5128,12 +5118,12 @@ HTMLEditor::AutoDeleteRangesHandler::ComputeRangeToDeleteRangeWithTransaction(
editableContent =
howToHandleCollapsedRange ==
EditorBase::HowToHandleCollapsedRange::ExtendBackward
- ? HTMLEditUtils::GetPreviousLeafContent(
- *editableContent, {LeafNodeOption::IgnoreNonEditableNode},
- BlockInlineCheck::Auto, &aEditingHost)
- : HTMLEditUtils::GetNextLeafContent(
- *editableContent, {LeafNodeOption::IgnoreNonEditableNode},
- BlockInlineCheck::Auto, &aEditingHost);
+ ? HTMLEditUtils::GetPreviousContent(
+ *editableContent, {WalkTreeOption::IgnoreNonEditableNode},
+ BlockInlineCheck::Unused, &aEditingHost)
+ : HTMLEditUtils::GetNextContent(
+ *editableContent, {WalkTreeOption::IgnoreNonEditableNode},
+ BlockInlineCheck::Unused, &aEditingHost);
}
if (!editableContent) {
return NS_OK;
@@ -5594,9 +5584,10 @@ nsresult HTMLEditor::AutoDeleteRangesHandler::AutoBlockElementsJoiner::
atStart.IsEndOfContainer() && range.StartRef().GetChild() &&
HTMLEditUtils::IsInvisibleBRElement(
*range.StartRef().GetChild())
- ? HTMLEditUtils::GetNextLeafContentOrNextBlockElement(
+ ? HTMLEditUtils::GetNextContent(
*atStart.ContainerAs<nsIContent>(),
- {LeafNodeOption::TreatChildBlockAsLeafNode},
+ {WalkTreeOption::IgnoreDataNodeExceptText,
+ WalkTreeOption::StopAtBlockBoundary},
BlockInlineCheck::UseComputedDisplayOutsideStyle,
editingHost)
: nullptr;
@@ -5666,7 +5657,7 @@ Result<DeleteRangeResult, nsresult> HTMLEditor::AutoDeleteRangesHandler::
const WSScanResult maybePreviousText =
WSRunScanner::ScanPreviousVisibleNodeOrBlockBoundary(
{}, maybeDeepStartOfRightContent, &aEditingHost);
- if (maybePreviousText.ContentIsEditable() &&
+ if (maybePreviousText.IsContentEditable() &&
maybePreviousText.InVisibleOrCollapsibleCharacters()) {
return maybePreviousText.PointAfterReachedContent<EditorDOMPoint>();
}
@@ -6370,14 +6361,14 @@ nsresult HTMLEditor::AutoMoveOneLineHandler::
const RefPtr<Text> textNodeEndingWithUnnecessaryLineBreak = [&]() -> Text* {
Text* lastTextNode = Text::FromNodeOrNull(
mMovingToParentBlock
- ? HTMLEditUtils::GetPreviousLeafContentOrPreviousBlockElement(
+ ? HTMLEditUtils::GetPreviousContent(
*mTopmostSrcAncestorBlockInDestBlock,
- {LeafNodeOption::TreatChildBlockAsLeafNode},
+ {WalkTreeOption::StopAtBlockBoundary},
BlockInlineCheck::UseComputedDisplayOutsideStyle,
mDestInclusiveAncestorBlock)
: HTMLEditUtils::GetLastLeafContent(
*mDestInclusiveAncestorBlock,
- {LeafNodeOption::TreatNonEditableNodeAsLeafNode}));
+ {LeafNodeType::LeafNodeOrNonEditableNode}));
if (!lastTextNode ||
!HTMLEditUtils::IsSimplyEditableNode(*lastTextNode)) {
return nullptr;
@@ -7159,10 +7150,8 @@ HTMLEditor::AutoDeleteRangesHandler::AutoEmptyBlockAncestorDeleter::
// last list item is deleted. We should follow it since current
// behavior is annoying when you type new list item with selecting
// all list items.
- if (!HTMLEditUtils::IsFirstChild(
- *mEmptyInclusiveAncestorBlockElement,
- {LeafNodeOption::IgnoreNonEditableNode},
- BlockInlineCheck::UseComputedDisplayOutsideStyle)) {
+ if (!HTMLEditUtils::IsFirstChild(*mEmptyInclusiveAncestorBlockElement,
+ {WalkTreeOption::IgnoreNonEditableNode})) {
return CreateLineBreakResult::NotHandled();
}
@@ -7218,8 +7207,8 @@ Result<CaretPoint, nsresult> HTMLEditor::AutoDeleteRangesHandler::
for (EditorRawDOMPoint scanStartPoint =
EditorRawDOMPoint::After(mEmptyInclusiveAncestorBlockElement);
scanStartPoint.IsInContentNode();) {
- nsIContent* const nextContent = HTMLEditUtils::GetNextLeafContent(
- scanStartPoint, {}, BlockInlineCheck::Auto, &aEditingHost);
+ nsIContent* const nextContent = HTMLEditUtils::GetNextContent(
+ scanStartPoint, {}, BlockInlineCheck::Unused, &aEditingHost);
// Let's ignore invisible `Text`.
if (nextContent && nextContent->IsText() &&
!HTMLEditUtils::IsVisibleTextNode(*nextContent->AsText())) {
@@ -7255,10 +7244,9 @@ Result<CaretPoint, nsresult> HTMLEditor::AutoDeleteRangesHandler::
for (EditorRawDOMPoint scanStartPoint =
EditorRawDOMPoint(mEmptyInclusiveAncestorBlockElement);
scanStartPoint.IsInContentNode();) {
- nsIContent* const previousContent =
- HTMLEditUtils::GetPreviousLeafContent(
- scanStartPoint, {LeafNodeOption::IgnoreNonEditableNode},
- BlockInlineCheck::Auto, &aEditingHost);
+ nsIContent* const previousContent = HTMLEditUtils::GetPreviousContent(
+ scanStartPoint, {WalkTreeOption::IgnoreNonEditableNode},
+ BlockInlineCheck::Unused, &aEditingHost);
// Let's ignore invisible `Text`.
if (previousContent && previousContent->IsText() &&
!HTMLEditUtils::IsVisibleTextNode(*previousContent->AsText())) {
diff --git a/editor/libeditor/HTMLEditorInsertLineBreakHandler.cpp b/editor/libeditor/HTMLEditorInsertLineBreakHandler.cpp
@@ -235,14 +235,10 @@ nsresult HTMLEditor::AutoInsertLineBreakHandler::HandleInsertBRElement() {
pointToPutCaret = forwardScanFromAfterBRElementResult
.PointAtReachedContent<EditorDOMPoint>();
} else if (forwardScanFromAfterBRElementResult.ReachedSpecialContent()) {
- pointToPutCaret = forwardScanFromAfterBRElementResult
- .PointAtReachedContent<EditorDOMPoint>();
- } else if (forwardScanFromAfterBRElementResult
- .ReachedEmptyInlineContainerElement()) {
// Next inserting text should be inserted into styled inline elements if
// they have first visible thing in the new line.
- pointToPutCaret =
- EditorDOMPoint(forwardScanFromAfterBRElementResult.ElementPtr(), 0);
+ pointToPutCaret = forwardScanFromAfterBRElementResult
+ .PointAtReachedContent<EditorDOMPoint>();
}
nsresult rv = mHTMLEditor.CollapseSelectionTo(pointToPutCaret);
diff --git a/editor/libeditor/HTMLEditorInsertParagraphHandler.cpp b/editor/libeditor/HTMLEditorInsertParagraphHandler.cpp
@@ -49,8 +49,9 @@ namespace mozilla {
using namespace dom;
using EmptyCheckOption = HTMLEditUtils::EmptyCheckOption;
using EmptyCheckOptions = HTMLEditUtils::EmptyCheckOptions;
-using LeafNodeOption = HTMLEditUtils::LeafNodeOption;
-using LeafNodeOptions = HTMLEditUtils::LeafNodeOptions;
+using LeafNodeType = HTMLEditUtils::LeafNodeType;
+using LeafNodeTypes = HTMLEditUtils::LeafNodeTypes;
+using WalkTreeOption = HTMLEditUtils::WalkTreeOption;
Result<EditActionResult, nsresult>
HTMLEditor::InsertParagraphSeparatorAsSubAction(const Element& aEditingHost) {
@@ -1059,9 +1060,7 @@ nsresult HTMLEditor::AutoInsertParagraphHandler::
if (!backwardScanFromPointToCreateNewBRElementResult
.InVisibleOrCollapsibleCharacters() &&
!backwardScanFromPointToCreateNewBRElementResult
- .ReachedSpecialContent() &&
- !backwardScanFromPointToCreateNewBRElementResult
- .ReachedEmptyInlineContainerElement()) {
+ .ReachedSpecialContent()) {
return NS_SUCCESS_DOM_NO_OPERATION;
}
const WSScanResult forwardScanFromPointAfterNewBRElementResult =
@@ -1076,8 +1075,6 @@ nsresult HTMLEditor::AutoInsertParagraphHandler::
if (!forwardScanFromPointAfterNewBRElementResult
.InVisibleOrCollapsibleCharacters() &&
!forwardScanFromPointAfterNewBRElementResult.ReachedSpecialContent() &&
- !forwardScanFromPointAfterNewBRElementResult
- .ReachedEmptyInlineContainerElement() &&
// In case we're at the very end.
!forwardScanFromPointAfterNewBRElementResult
.ReachedCurrentBlockBoundary()) {
@@ -1219,8 +1216,7 @@ bool HTMLEditor::AutoInsertParagraphHandler::ShouldCreateNewParagraph(
const auto* const precedingBRElement =
HTMLBRElement::FromNodeOrNull(HTMLEditUtils::GetPreviousSibling(
*aPointToSplit.ContainerAs<Text>(),
- {LeafNodeOption::IgnoreNonEditableNode},
- BlockInlineCheck::UseComputedDisplayOutsideStyle));
+ {WalkTreeOption::IgnoreNonEditableNode}));
return !IsNullOrInvisibleBRElementOrPaddingOneForEmptyLastLine(
precedingBRElement);
}
@@ -1232,8 +1228,7 @@ bool HTMLEditor::AutoInsertParagraphHandler::ShouldCreateNewParagraph(
const auto* const followingBRElement =
HTMLBRElement::FromNodeOrNull(HTMLEditUtils::GetNextSibling(
*aPointToSplit.ContainerAs<Text>(),
- {LeafNodeOption::IgnoreNonEditableNode},
- BlockInlineCheck::UseComputedDisplayOutsideStyle));
+ {WalkTreeOption::IgnoreNonEditableNode}));
return !IsNullOrInvisibleBRElementOrPaddingOneForEmptyLastLine(
followingBRElement);
}
@@ -1251,9 +1246,9 @@ bool HTMLEditor::AutoInsertParagraphHandler::ShouldCreateNewParagraph(
// moving to the caret, but I think that this could be handled in fewer
// cases than this.
const auto* const precedingBRElement =
- HTMLBRElement::FromNodeOrNull(HTMLEditUtils::GetPreviousLeafContent(
- aPointToSplit, {LeafNodeOption::IgnoreNonEditableNode},
- BlockInlineCheck::Auto, &mEditingHost));
+ HTMLBRElement::FromNodeOrNull(HTMLEditUtils::GetPreviousContent(
+ aPointToSplit, {WalkTreeOption::IgnoreNonEditableNode},
+ BlockInlineCheck::Unused, &mEditingHost));
if (!IsNullOrInvisibleBRElementOrPaddingOneForEmptyLastLine(
precedingBRElement)) {
return true;
@@ -1262,9 +1257,9 @@ bool HTMLEditor::AutoInsertParagraphHandler::ShouldCreateNewParagraph(
// followed by a <br> or followed by an invisible <br>, we should not create a
// new paragraph.
const auto* followingBRElement =
- HTMLBRElement::FromNodeOrNull(HTMLEditUtils::GetNextLeafContent(
- aPointToSplit, {LeafNodeOption::IgnoreNonEditableNode},
- BlockInlineCheck::Auto, &mEditingHost));
+ HTMLBRElement::FromNodeOrNull(HTMLEditUtils::GetNextContent(
+ aPointToSplit, {WalkTreeOption::IgnoreNonEditableNode},
+ BlockInlineCheck::Unused, &mEditingHost));
return !IsNullOrInvisibleBRElementOrPaddingOneForEmptyLastLine(
followingBRElement);
}
@@ -1754,7 +1749,7 @@ HTMLEditor::AutoInsertParagraphHandler::SplitParagraphWithTransaction(
// Let's put caret at start of the first leaf container.
nsIContent* child = HTMLEditUtils::GetFirstLeafContent(
- *rightDivOrParagraphElement, {LeafNodeOption::TreatChildBlockAsLeafNode},
+ *rightDivOrParagraphElement, {LeafNodeType::LeafNodeOrChildBlock},
BlockInlineCheck::UseComputedDisplayStyle);
if (MOZ_UNLIKELY(!child)) {
return SplitNodeResult(std::move(splitDivOrPResult),
@@ -1835,9 +1830,8 @@ HTMLEditor::AutoInsertParagraphHandler::HandleInListItemElement(
// If the given list item element is not the last list item element of
// its parent nor not followed by sub list elements, split the parent
// before it.
- if (!HTMLEditUtils::IsLastChild(
- aListItemElement, {LeafNodeOption::IgnoreNonEditableNode},
- BlockInlineCheck::UseComputedDisplayOutsideStyle)) {
+ if (!HTMLEditUtils::IsLastChild(aListItemElement,
+ {WalkTreeOption::IgnoreNonEditableNode})) {
Result<SplitNodeResult, nsresult> splitListItemParentResult =
mHTMLEditor.SplitNodeWithTransaction(
EditorDOMPoint(&aListItemElement));
diff --git a/editor/libeditor/HTMLEditorState.cpp b/editor/libeditor/HTMLEditorState.cpp
@@ -38,7 +38,7 @@ namespace mozilla {
using namespace dom;
using EditorType = EditorUtils::EditorType;
-using LeafNodeOption = HTMLEditUtils::LeafNodeOption;
+using WalkTreeOption = HTMLEditUtils::WalkTreeOption;
/*****************************************************************************
* ListElementSelectionState
@@ -282,9 +282,9 @@ AlignStateAtSelection::AlignStateAtSelection(HTMLEditor& aHTMLEditor,
else if (atStartOfSelection.IsContainerHTMLElement(nsGkAtoms::html) &&
atBodyOrDocumentElement.IsSet() &&
atStartOfSelection.Offset() == atBodyOrDocumentElement.Offset()) {
- editTargetContent = HTMLEditUtils::GetNextLeafContent(
- atStartOfSelection, {LeafNodeOption::IgnoreNonEditableNode},
- BlockInlineCheck::Auto, aHTMLEditor.ComputeEditingHost());
+ editTargetContent = HTMLEditUtils::GetNextContent(
+ atStartOfSelection, {WalkTreeOption::IgnoreNonEditableNode},
+ BlockInlineCheck::Unused, aHTMLEditor.ComputeEditingHost());
if (NS_WARN_IF(!editTargetContent)) {
aRv.Throw(NS_ERROR_FAILURE);
return;
diff --git a/editor/libeditor/HTMLStyleEditor.cpp b/editor/libeditor/HTMLStyleEditor.cpp
@@ -64,7 +64,9 @@ using namespace dom;
using EditablePointOption = HTMLEditUtils::EditablePointOption;
using EditablePointOptions = HTMLEditUtils::EditablePointOptions;
using EmptyCheckOption = HTMLEditUtils::EmptyCheckOption;
-using LeafNodeOption = HTMLEditUtils::LeafNodeOption;
+using LeafNodeType = HTMLEditUtils::LeafNodeType;
+using LeafNodeTypes = HTMLEditUtils::LeafNodeTypes;
+using WalkTreeOption = HTMLEditUtils::WalkTreeOption;
template nsresult HTMLEditor::SetInlinePropertiesAsSubAction(
const AutoTArray<EditorInlineStyleAndValue, 1>& aStylesToSet,
@@ -950,8 +952,7 @@ HTMLEditor::AutoInlineStyleSetter::SplitTextNodeAndApplyStyleToMiddleNode(
if (mAttribute) {
// Look for siblings that are correct type of node
nsIContent* sibling = HTMLEditUtils::GetPreviousSibling(
- *middleTextNode, {LeafNodeOption::IgnoreNonEditableNode},
- BlockInlineCheck::UseComputedDisplayOutsideStyle);
+ *middleTextNode, {WalkTreeOption::IgnoreNonEditableNode});
if (sibling && sibling->IsElement()) {
OwningNonNull<Element> element(*sibling->AsElement());
Result<bool, nsresult> result =
@@ -980,8 +981,7 @@ HTMLEditor::AutoInlineStyleSetter::SplitTextNodeAndApplyStyleToMiddleNode(
}
}
sibling = HTMLEditUtils::GetNextSibling(
- *middleTextNode, {LeafNodeOption::IgnoreNonEditableNode},
- BlockInlineCheck::UseComputedDisplayOutsideStyle);
+ *middleTextNode, {WalkTreeOption::IgnoreNonEditableNode});
if (sibling && sibling->IsElement()) {
OwningNonNull<Element> element(*sibling->AsElement());
Result<bool, nsresult> result =
@@ -1062,13 +1062,10 @@ Result<CaretPoint, nsresult> HTMLEditor::AutoInlineStyleSetter::ApplyStyle(
}
// First check if there's an adjacent sibling we can put our node into.
- const nsCOMPtr<nsIContent> previousSibling =
- HTMLEditUtils::GetPreviousSibling(
- aContent, {LeafNodeOption::IgnoreNonEditableNode},
- BlockInlineCheck::UseComputedDisplayOutsideStyle);
- const nsCOMPtr<nsIContent> nextSibling = HTMLEditUtils::GetNextSibling(
- aContent, {LeafNodeOption::IgnoreNonEditableNode},
- BlockInlineCheck::UseComputedDisplayOutsideStyle);
+ nsCOMPtr<nsIContent> previousSibling = HTMLEditUtils::GetPreviousSibling(
+ aContent, {WalkTreeOption::IgnoreNonEditableNode});
+ nsCOMPtr<nsIContent> nextSibling = HTMLEditUtils::GetNextSibling(
+ aContent, {WalkTreeOption::IgnoreNonEditableNode});
if (RefPtr<Element> previousElement =
Element::FromNodeOrNull(previousSibling)) {
Result<bool, nsresult> canMoveIntoPreviousSibling =
@@ -2397,7 +2394,7 @@ Result<EditorDOMPoint, nsresult> HTMLEditor::ClearStyleAt(
// `<p><b><i>a</i></b><b><i></i></b><b><i>bc</i></b></p>`.
// ^^^^^^^^^^^^^^
nsIContent* firstLeafChildOfNextNode = HTMLEditUtils::GetFirstLeafContent(
- *unwrappedSplitNodeResult.GetNextContent(), {});
+ *unwrappedSplitNodeResult.GetNextContent(), {LeafNodeType::OnlyLeafNode});
EditorDOMPoint atStartOfNextNode(
firstLeafChildOfNextNode ? firstLeafChildOfNextNode
: unwrappedSplitNodeResult.GetNextContent(),
@@ -2487,7 +2484,8 @@ Result<EditorDOMPoint, nsresult> HTMLEditor::ClearStyleAt(
// it was in next node of the first split.
// E.g., `<p><b><i>a</i></b><b><i><br></i></b><b><i>bc</i></b></p>`
nsIContent* firstLeafChildOfPreviousNode = HTMLEditUtils::GetFirstLeafContent(
- *unwrappedSplitResultAtStartOfNextNode.GetPreviousContent(), {});
+ *unwrappedSplitResultAtStartOfNextNode.GetPreviousContent(),
+ {LeafNodeType::OnlyLeafNode});
pointToPutCaret.Set(
firstLeafChildOfPreviousNode
? firstLeafChildOfPreviousNode
@@ -4177,8 +4175,7 @@ Result<CreateElementResult, nsresult> HTMLEditor::SetFontSizeOnTextNode(
aIncrementOrDecrement == FontSize::incr ? nsGkAtoms::big
: nsGkAtoms::small;
nsCOMPtr<nsIContent> sibling = HTMLEditUtils::GetPreviousSibling(
- *textNodeForTheRange, {LeafNodeOption::IgnoreNonEditableNode},
- BlockInlineCheck::UseComputedDisplayOutsideStyle);
+ *textNodeForTheRange, {WalkTreeOption::IgnoreNonEditableNode});
if (sibling && sibling->IsHTMLElement(bigOrSmallTagName)) {
// Previous sib is already right kind of inline node; slide this over
Result<MoveNodeResult, nsresult> moveTextNodeResult =
@@ -4194,8 +4191,7 @@ Result<CreateElementResult, nsresult> HTMLEditor::SetFontSizeOnTextNode(
return CreateElementResult::NotHandled(std::move(pointToPutCaret));
}
sibling = HTMLEditUtils::GetNextSibling(
- *textNodeForTheRange, {LeafNodeOption::IgnoreNonEditableNode},
- BlockInlineCheck::UseComputedDisplayOutsideStyle);
+ *textNodeForTheRange, {WalkTreeOption::IgnoreNonEditableNode});
if (sibling && sibling->IsHTMLElement(bigOrSmallTagName)) {
// Following sib is already right kind of inline node; slide this over
Result<MoveNodeResult, nsresult> moveTextNodeResult =
@@ -4329,8 +4325,7 @@ Result<EditorDOMPoint, nsresult> HTMLEditor::SetFontSizeWithBigOrSmallElement(
// Next, if next or previous is <big> or <small>, move aContent into it.
nsCOMPtr<nsIContent> sibling = HTMLEditUtils::GetPreviousSibling(
- aContent, {LeafNodeOption::IgnoreNonEditableNode},
- BlockInlineCheck::UseComputedDisplayOutsideStyle);
+ aContent, {WalkTreeOption::IgnoreNonEditableNode});
if (sibling && sibling->IsHTMLElement(bigOrSmallTagName)) {
Result<MoveNodeResult, nsresult> moveNodeResult =
MoveNodeToEndWithTransaction(aContent, *sibling);
@@ -4345,8 +4340,7 @@ Result<EditorDOMPoint, nsresult> HTMLEditor::SetFontSizeWithBigOrSmallElement(
}
sibling = HTMLEditUtils::GetNextSibling(
- aContent, {LeafNodeOption::IgnoreNonEditableNode},
- BlockInlineCheck::UseComputedDisplayOutsideStyle);
+ aContent, {WalkTreeOption::IgnoreNonEditableNode});
if (sibling && sibling->IsHTMLElement(bigOrSmallTagName)) {
Result<MoveNodeResult, nsresult> moveNodeResult =
MoveNodeWithTransaction(aContent, EditorDOMPoint(sibling, 0u));
diff --git a/editor/libeditor/TextEditor.cpp b/editor/libeditor/TextEditor.cpp
@@ -10,8 +10,8 @@
#include "EditAction.h"
#include "EditAggregateTransaction.h"
#include "EditorDOMPoint.h"
-#include "EditorUtils.h"
#include "HTMLEditor.h"
+#include "HTMLEditUtils.h"
#include "InternetCiter.h"
#include "PlaceholderTransaction.h"
#include "gfxFontUtils.h"
@@ -89,6 +89,9 @@ static void LogOrWarn(const TextEditor* aTextEditor, LazyLogModule& aLog,
using namespace dom;
+using LeafNodeType = HTMLEditUtils::LeafNodeType;
+using LeafNodeTypes = HTMLEditUtils::LeafNodeTypes;
+
template EditorDOMPoint TextEditor::FindBetterInsertionPoint(
const EditorDOMPoint& aPoint) const;
template EditorRawDOMPoint TextEditor::FindBetterInsertionPoint(
diff --git a/editor/libeditor/WSRunScanner.cpp b/editor/libeditor/WSRunScanner.cpp
@@ -36,7 +36,6 @@ void WSScanResult::AssertIfInvalidData(const WSRunScanner& aScanner) const {
mReason == WSType::CollapsibleWhiteSpaces ||
mReason == WSType::BRElement ||
mReason == WSType::PreformattedLineBreak ||
- mReason == WSType::EmptyInlineContainerElement ||
mReason == WSType::SpecialContent ||
mReason == WSType::CurrentBlockBoundary ||
mReason == WSType::OtherBlockBoundary ||
@@ -73,17 +72,6 @@ void WSScanResult::AssertIfInvalidData(const WSRunScanner& aScanner) const {
mContent->IsHTMLElement(nsGkAtoms::br));
MOZ_ASSERT_IF(mReason == WSType::PreformattedLineBreak,
EditorUtils::IsNewLinePreformatted(*mContent));
- auto MaybeNonVoidEmptyInlineContainerElement = [&]() {
- return HTMLEditUtils::IsInlineContent(
- *mContent,
- aScanner.ReferredHTMLDefaultStyle()
- ? BlockInlineCheck::UseHTMLDefaultStyle
- : BlockInlineCheck::UseComputedDisplayOutsideStyle) &&
- HTMLEditUtils::IsContainerNode(*mContent) &&
- !HTMLEditUtils::IsReplacedElement(*mContent->AsElement());
- };
- MOZ_ASSERT_IF(mReason == WSType::EmptyInlineContainerElement,
- MaybeNonVoidEmptyInlineContainerElement());
MOZ_ASSERT_IF(
mReason == WSType::SpecialContent,
(mContent->IsText() && !mContent->IsEditable()) ||
@@ -92,9 +80,7 @@ void WSScanResult::AssertIfInvalidData(const WSRunScanner& aScanner) const {
*mContent,
aScanner.ReferredHTMLDefaultStyle()
? BlockInlineCheck::UseHTMLDefaultStyle
- : BlockInlineCheck::UseComputedDisplayOutsideStyle)) &&
- (!mContent->IsEditable() ||
- !MaybeNonVoidEmptyInlineContainerElement()));
+ : BlockInlineCheck::UseComputedDisplayOutsideStyle)));
MOZ_ASSERT_IF(
mReason == WSType::OtherBlockBoundary,
HTMLEditUtils::IsBlockElement(
@@ -1044,7 +1030,6 @@ WSRunScanner::ShrinkRangeIfStartsFromOrEndsAfterAtomicContent(
if (textFragmentDataAtStart.EndsByVisibleBRElement()) {
startContent = textFragmentDataAtStart.EndReasonBRElementPtr();
} else if (textFragmentDataAtStart.EndsBySpecialContent() ||
- textFragmentDataAtStart.EndsByEmptyInlineContainerElement() ||
(textFragmentDataAtStart.EndsByOtherBlockElement() &&
!HTMLEditUtils::IsContainerNode(
*textFragmentDataAtStart
@@ -1067,7 +1052,6 @@ WSRunScanner::ShrinkRangeIfStartsFromOrEndsAfterAtomicContent(
if (textFragmentDataAtEnd.StartsFromVisibleBRElement()) {
endContent = textFragmentDataAtEnd.StartReasonBRElementPtr();
} else if (textFragmentDataAtEnd.StartsFromSpecialContent() ||
- textFragmentDataAtEnd.EndsByEmptyInlineContainerElement() ||
(textFragmentDataAtEnd.StartsFromOtherBlockElement() &&
!HTMLEditUtils::IsContainerNode(
*textFragmentDataAtEnd
diff --git a/editor/libeditor/WSRunScanner.h b/editor/libeditor/WSRunScanner.h
@@ -51,11 +51,6 @@ class MOZ_STACK_CLASS WSScanResult final {
CollapsibleWhiteSpaces,
// Visible characters except collapsible white-spaces.
NonCollapsibleCharacters,
- // Empty inline container elemnet such as `<span></span>`. Note that it may
- // be visible if its border/padding is not 0 for example.
- // NOTE: This won't be used if it's the inline editing host at the scan
- // start point.
- EmptyInlineContainerElement,
// Special content such as `<img>`, etc.
SpecialContent,
// <br> element.
@@ -86,8 +81,6 @@ class MOZ_STACK_CLASS WSScanResult final {
return aStream << "WSType::CollapsibleWhiteSpaces";
case WSType::NonCollapsibleCharacters:
return aStream << "WSType::NonCollapsibleCharacters";
- case WSType::EmptyInlineContainerElement:
- return aStream << "WSType::EmptyInlineContainerElement";
case WSType::SpecialContent:
return aStream << "WSType::SpecialContent";
case WSType::BRElement:
@@ -187,22 +180,13 @@ class MOZ_STACK_CLASS WSScanResult final {
}
/**
- * Return true if found or reached content is editable.
+ * Returns true if found or reached content is editable.
*/
- [[nodiscard]] bool ContentIsEditable() const {
- return mContent && HTMLEditUtils::IsSimplyEditableNode(*mContent);
- }
+ bool IsContentEditable() const { return mContent && mContent->IsEditable(); }
- /**
- * Return true if found or reached content is removable node.
- */
- [[nodiscard]] bool ContentIsRemovable() const {
- return mContent && HTMLEditUtils::IsRemovableNode(*mContent);
- }
-
- [[nodiscard]] bool ContentIsEditableRoot() const {
- return ContentIsElement() &&
- HTMLEditUtils::ElementIsEditableRoot(*ElementPtr());
+ [[nodiscard]] bool IsContentEditableRoot() const {
+ return mContent && mContent->IsElement() &&
+ HTMLEditUtils::ElementIsEditableRoot(*mContent->AsElement());
}
/**
@@ -274,97 +258,10 @@ class MOZ_STACK_CLASS WSScanResult final {
}
/**
- * The scanner reached an empty inline container element such as
- * <span></span>. Note that the element may be visible, e.g., may have
- * non-zero border/padding.
- */
- [[nodiscard]] constexpr bool ReachedEmptyInlineContainerElement() const {
- return mReason == WSType::EmptyInlineContainerElement;
- }
- /**
- * The scanner reached an editable empty inline container element such as
- * <span></span>. Note that the element may be visible, e.g., may have
- * non-zero border/padding.
- */
- [[nodiscard]] bool ReachedEditableEmptyInlineContainerElement() const {
- return ReachedEmptyInlineContainerElement() && ContentIsEditable();
- }
- /**
- * The scanner reached a removable empty inline container element such as
- * <span></span>. Note that the element may be visible, e.g., may have
- * non-zero border/padding.
- */
- [[nodiscard]] bool ReachedRemovableEmptyInlineContainerElement() const {
- return ReachedEmptyInlineContainerElement() && ContentIsRemovable();
- }
-
- /**
- * The scanner reached an empty inline container element which is visible.
- */
- [[nodiscard]] bool ReachedVisibleEmptyInlineContainerElement(
- const nsIContent* aAncestorLimiterToCheckDisplayNone = nullptr) const {
- return ReachedEmptyInlineContainerElement() &&
- HTMLEditUtils::IsVisibleElementEvenIfLeafNode(*ElementPtr()) &&
- !HTMLEditUtils::IsInclusiveAncestorCSSDisplayNone(
- *ElementPtr(), aAncestorLimiterToCheckDisplayNone);
- }
- /**
- * The scanner reached an editable empty inline container element which is
- * visible.
- */
- [[nodiscard]] bool ReachedEditableEmptyInlineContainerElement(
- const nsIContent* aAncestorLimiterToCheckDisplayNone = nullptr) const {
- return ReachedVisibleEmptyInlineContainerElement(
- aAncestorLimiterToCheckDisplayNone) &&
- ContentIsEditable();
- }
- /**
- * The scanner reached a removable empty inline container element which is
- * visible.
- */
- [[nodiscard]] bool ReachedRemovableEmptyInlineContainerElement(
- const nsIContent* aAncestorLimiterToCheckDisplayNone = nullptr) const {
- return ReachedVisibleEmptyInlineContainerElement(
- aAncestorLimiterToCheckDisplayNone) &&
- ContentIsRemovable();
- }
-
- /**
- * The scanner reached an empty inline container element which is invisible.
- */
- [[nodiscard]] bool ReachedInvisibleEmptyInlineContainerElement(
- const nsIContent* aAncestorLimiterToCheckDisplayNone = nullptr) const {
- return ReachedEmptyInlineContainerElement() &&
- (!HTMLEditUtils::IsVisibleElementEvenIfLeafNode(*ElementPtr()) ||
- HTMLEditUtils::IsInclusiveAncestorCSSDisplayNone(
- *ElementPtr(), aAncestorLimiterToCheckDisplayNone));
- }
- /**
- * The scanner reached an editable empty inline container element which is
- * invisible.
- */
- [[nodiscard]] bool ReachedEditableInvisibleEmptyInlineContainerElement(
- const nsIContent* aAncestorLimiterToCheckDisplayNone = nullptr) const {
- return ReachedInvisibleEmptyInlineContainerElement(
- aAncestorLimiterToCheckDisplayNone) &&
- ContentIsEditable();
- }
- /**
- * The scanner reached a removable empty inline container element which is
- * invisible.
- */
- [[nodiscard]] bool ReachedRemovableInvisibleEmptyInlineContainerElement(
- const nsIContent* aAncestorLimiterToCheckDisplayNone = nullptr) const {
- return ReachedEditableInvisibleEmptyInlineContainerElement(
- aAncestorLimiterToCheckDisplayNone) &&
- ContentIsRemovable();
- }
-
- /**
* The scanner reached <img> or something which is inline and is not a
* container.
*/
- [[nodiscard]] constexpr bool ReachedSpecialContent() const {
+ bool ReachedSpecialContent() const {
return mReason == WSType::SpecialContent;
}
@@ -500,7 +397,7 @@ class MOZ_STACK_CLASS WSScanResult final {
private:
nsCOMPtr<nsIContent> mContent;
Maybe<uint32_t> mOffset;
- WSType mReason = WSType::NotInitialized;
+ WSType mReason;
ScanDirection mDirection = ScanDirection::Backward;
};
@@ -526,14 +423,6 @@ class MOZ_STACK_CLASS WSRunScanner final {
ReferHTMLDefaultStyle,
// If set, stop scanning the DOM when it reaches a `Comment` node.
StopAtComment,
- // If set, ignore empty inline containers such as <span></span>.
- IgnoreEmptyInlineContainers,
- // If set, ignore empty inline containers which is not visible. E.g.,
- // <span></span> is ignored but <span style="border:1px solid"></span>
- // and <span style="border:padding 1px"></span> are not ignored.
- // XXX Currently, this does not work well if the inline container has only
- // `::before` and/or `::after` content and the frame is dirty.
- IgnoreInvisibleInlines,
};
using Options = EnumSet<Option>;
@@ -560,32 +449,6 @@ class MOZ_STACK_CLASS WSRunScanner final {
aOptions.contains(Option::ReferHTMLDefaultStyle));
}
- private:
- [[nodiscard]] static HTMLEditUtils::LeafNodeOptions ToLeafNodeOptions(
- const Options& aOptions) {
- using LeafNodeOption = HTMLEditUtils::LeafNodeOption;
- using LeafNodeOptions = HTMLEditUtils::LeafNodeOptions;
- auto types =
- aOptions.contains(Option::OnlyEditableNodes)
- ? LeafNodeOptions{LeafNodeOption::TreatNonEditableNodeAsLeafNode}
- : LeafNodeOptions{};
- if (aOptions.contains(Option::StopAtComment)) {
- types += LeafNodeOption::TreatCommentAsLeafNode;
- }
- if (aOptions.contains(Option::IgnoreInvisibleInlines)) {
- types +=
- LeafNodeOptions{LeafNodeOption::IgnoreInvisibleEmptyInlineContainers,
- LeafNodeOption::IgnoreInvisibleInlineVoidElements,
- LeafNodeOption::IgnoreInvisibleText};
- }
- if (aOptions.contains(Option::IgnoreEmptyInlineContainers)) {
- types += LeafNodeOptions{LeafNodeOption::IgnoreAnyEmptyInlineContainers,
- LeafNodeOption::IgnoreEmptyText};
- }
- return types;
- }
-
- public:
template <typename EditorDOMPointType>
WSRunScanner(Options aOptions, // NOLINT(performance-unnecessary-value-param)
const EditorDOMPointType& aScanStartPoint,
@@ -818,12 +681,9 @@ class MOZ_STACK_CLASS WSRunScanner final {
bool StartsFromNonCollapsibleCharacters() const {
return mLeftWSType == WSType::NonCollapsibleCharacters;
}
- [[nodiscard]] constexpr bool StartsFromSpecialContent() const {
+ bool StartsFromSpecialContent() const {
return mLeftWSType == WSType::SpecialContent;
}
- [[nodiscard]] constexpr bool StartsFromEmptyInlineContainerElement() const {
- return mLeftWSType == WSType::EmptyInlineContainerElement;
- }
bool StartsFromPreformattedLineBreak() const {
return mLeftWSType == WSType::PreformattedLineBreak;
}
@@ -838,12 +698,9 @@ class MOZ_STACK_CLASS WSRunScanner final {
bool EndsByTrailingWhiteSpaces() const {
return mRightWSType == WSType::TrailingWhiteSpaces;
}
- [[nodiscard]] constexpr bool EndsBySpecialContent() const {
+ bool EndsBySpecialContent() const {
return mRightWSType == WSType::SpecialContent;
}
- [[nodiscard]] constexpr bool EndsByEmptyInlineContainerElement() const {
- return mRightWSType == WSType::EmptyInlineContainerElement;
- }
bool EndsByBRElement() const { return mRightWSType == WSType::BRElement; }
bool EndsByPreformattedLineBreak() const {
return mRightWSType == WSType::PreformattedLineBreak;
@@ -1051,12 +908,9 @@ class MOZ_STACK_CLASS WSRunScanner final {
bool IsNonCollapsibleCharacters() const {
return mReason == WSType::NonCollapsibleCharacters;
}
- [[nodiscard]] constexpr bool IsSpecialContent() const {
+ bool IsSpecialContent() const {
return mReason == WSType::SpecialContent;
}
- [[nodiscard]] constexpr bool IsEmptyInlineContainerElement() const {
- return mReason == WSType::EmptyInlineContainerElement;
- }
bool IsBRElement() const { return mReason == WSType::BRElement; }
bool IsPreformattedLineBreak() const {
return mReason == WSType::PreformattedLineBreak;
@@ -1106,9 +960,8 @@ class MOZ_STACK_CLASS WSRunScanner final {
EditorDOMPoint mPoint;
// Must be one of WSType::NotInitialized,
// WSType::NonCollapsibleCharacters, WSType::SpecialContent,
- // WSType::EmptyInlineContainerElement, WSType::BRElement,
- // WSType::CurrentBlockBoundary, WSType::OtherBlockBoundary or
- // WSType::InlineEditingHostBoundary.
+ // WSType::BRElement, WSType::CurrentBlockBoundary,
+ // WSType::OtherBlockBoundary or WSType::InlineEditingHostBoundary.
WSType mReason = WSType::NotInitialized;
};
@@ -1173,12 +1026,7 @@ class MOZ_STACK_CLASS WSRunScanner final {
bool StartsFromNonCollapsibleCharacters() const {
return mStart.IsNonCollapsibleCharacters();
}
- [[nodiscard]] bool StartsFromSpecialContent() const {
- return mStart.IsSpecialContent();
- }
- [[nodiscard]] bool StartsFromEmptyInlineContainerElement() const {
- return mStart.IsEmptyInlineContainerElement();
- }
+ bool StartsFromSpecialContent() const { return mStart.IsSpecialContent(); }
bool StartsFromBRElement() const { return mStart.IsBRElement(); }
bool StartsFromVisibleBRElement() const {
return StartsFromBRElement() &&
@@ -1205,12 +1053,7 @@ class MOZ_STACK_CLASS WSRunScanner final {
bool EndsByNonCollapsibleCharacters() const {
return mEnd.IsNonCollapsibleCharacters();
}
- [[nodiscard]] bool EndsBySpecialContent() const {
- return mEnd.IsSpecialContent();
- }
- [[nodiscard]] bool EndsByEmptyInlineContainerElement() const {
- return mEnd.IsEmptyInlineContainerElement();
- }
+ bool EndsBySpecialContent() const { return mEnd.IsSpecialContent(); }
bool EndsByBRElement() const { return mEnd.IsBRElement(); }
bool EndsByVisibleBRElement() const {
return EndsByBRElement() &&
diff --git a/editor/libeditor/WSRunScannerNestedClasses.cpp b/editor/libeditor/WSRunScannerNestedClasses.cpp
@@ -24,8 +24,8 @@ using namespace dom;
using AncestorType = HTMLEditUtils::AncestorType;
using AncestorTypes = HTMLEditUtils::AncestorTypes;
-using LeafNodeOption = HTMLEditUtils::LeafNodeOption;
-using LeafNodeOptions = HTMLEditUtils::LeafNodeOptions;
+using LeafNodeType = HTMLEditUtils::LeafNodeType;
+using LeafNodeTypes = HTMLEditUtils::LeafNodeTypes;
template WSRunScanner::TextFragmentData::TextFragmentData(Options,
const EditorDOMPoint&,
@@ -245,10 +245,18 @@ WSRunScanner::TextFragmentData::BoundaryData WSRunScanner::TextFragmentData::
? BlockInlineCheck::UseHTMLDefaultStyle
: BlockInlineCheck::Auto;
// Then, we need to check previous leaf node.
- const LeafNodeOptions leafNodeOptions = ToLeafNodeOptions(aOptions);
+ const auto leafNodeTypes = [&]() -> LeafNodeTypes {
+ auto types = aOptions.contains(Option::OnlyEditableNodes)
+ ? LeafNodeTypes{LeafNodeType::LeafNodeOrNonEditableNode}
+ : LeafNodeTypes{LeafNodeType::OnlyLeafNode};
+ if (aOptions.contains(Option::StopAtComment)) {
+ types += LeafNodeType::TreatCommentAsLeafNode;
+ }
+ return types;
+ }();
nsIContent* previousLeafContentOrBlock =
HTMLEditUtils::GetPreviousLeafContentOrPreviousBlockElement(
- aPoint, leafNodeOptions, blockInlineCheck, &aAncestorLimiter);
+ aPoint, leafNodeTypes, blockInlineCheck, &aAncestorLimiter);
if (!previousLeafContentOrBlock) {
// No previous content means that we reached the aAncestorLimiter boundary.
return BoundaryData(
@@ -266,35 +274,16 @@ WSRunScanner::TextFragmentData::BoundaryData WSRunScanner::TextFragmentData::
WSType::OtherBlockBoundary);
}
- if (previousLeafContentOrBlock->IsHTMLElement(nsGkAtoms::br)) {
- // <br>
- return BoundaryData(aPoint, *previousLeafContentOrBlock, WSType::BRElement);
- }
-
- if (aOptions.contains(Option::OnlyEditableNodes) &&
- HTMLEditUtils::IsSimplyEditableNode(*previousLeafContentOrBlock) !=
- HTMLEditUtils::IsSimplyEditableNode(aAncestorLimiter)) {
- // Non-editable nodes (assuming the start content is editable).
- return BoundaryData(aPoint, *previousLeafContentOrBlock,
- WSType::SpecialContent);
- }
-
- if (previousLeafContentOrBlock->IsElement() &&
- HTMLEditUtils::IsInlineContent(
- *previousLeafContentOrBlock,
- UseComputedDisplayOutsideStyleIfAuto(blockInlineCheck)) &&
- HTMLEditUtils::IsContainerNode(*previousLeafContentOrBlock) &&
- !HTMLEditUtils::IsReplacedElement(
- *previousLeafContentOrBlock->AsElement())) {
- // Empty inline containers such as <span></span>
- return BoundaryData(aPoint, *previousLeafContentOrBlock,
- WSType::EmptyInlineContainerElement);
- }
-
- if (!previousLeafContentOrBlock->IsText()) {
- // Other elements like <img> or #comment.
+ if (!previousLeafContentOrBlock->IsText() ||
+ (aOptions.contains(Option::OnlyEditableNodes) &&
+ HTMLEditUtils::IsSimplyEditableNode(*previousLeafContentOrBlock) !=
+ HTMLEditUtils::IsSimplyEditableNode(aAncestorLimiter))) {
+ // it's a break or a special node, like <img>, that is not a block and
+ // not a break but still serves as a terminator to ws runs.
return BoundaryData(aPoint, *previousLeafContentOrBlock,
- WSType::SpecialContent);
+ previousLeafContentOrBlock->IsHTMLElement(nsGkAtoms::br)
+ ? WSType::BRElement
+ : WSType::SpecialContent);
}
if (!previousLeafContentOrBlock->AsText()->TextLength()) {
@@ -418,10 +407,18 @@ WSRunScanner::TextFragmentData::BoundaryData::ScanCollapsibleWhiteSpaceEndFrom(
: BlockInlineCheck::Auto;
// Then, we need to check next leaf node.
- const LeafNodeOptions leafNodeOptions = ToLeafNodeOptions(aOptions);
+ const auto leafNodeTypes = [&]() -> LeafNodeTypes {
+ auto types = aOptions.contains(Option::OnlyEditableNodes)
+ ? LeafNodeTypes{LeafNodeType::LeafNodeOrNonEditableNode}
+ : LeafNodeTypes{LeafNodeType::OnlyLeafNode};
+ if (aOptions.contains(Option::StopAtComment)) {
+ types += LeafNodeType::TreatCommentAsLeafNode;
+ }
+ return types;
+ }();
nsIContent* nextLeafContentOrBlock =
HTMLEditUtils::GetNextLeafContentOrNextBlockElement(
- aPoint, leafNodeOptions, blockInlineCheck, &aAncestorLimiter);
+ aPoint, leafNodeTypes, blockInlineCheck, &aAncestorLimiter);
if (!nextLeafContentOrBlock) {
// No next content means that we reached aAncestorLimiter boundary.
return BoundaryData(
@@ -441,34 +438,17 @@ WSRunScanner::TextFragmentData::BoundaryData::ScanCollapsibleWhiteSpaceEndFrom(
WSType::OtherBlockBoundary);
}
- if (nextLeafContentOrBlock->IsHTMLElement(nsGkAtoms::br)) {
- // <br>
- return BoundaryData(aPoint, *nextLeafContentOrBlock, WSType::BRElement);
- }
-
- if (aOptions.contains(Option::OnlyEditableNodes) &&
- HTMLEditUtils::IsSimplyEditableNode(*nextLeafContentOrBlock) !=
- HTMLEditUtils::IsSimplyEditableNode(aAncestorLimiter)) {
- // Non-editable nodes (assuming the start content is editable).
- return BoundaryData(aPoint, *nextLeafContentOrBlock,
- WSType::SpecialContent);
- }
-
- if (nextLeafContentOrBlock->IsElement() &&
- HTMLEditUtils::IsInlineContent(
- *nextLeafContentOrBlock,
- UseComputedDisplayOutsideStyleIfAuto(blockInlineCheck)) &&
- HTMLEditUtils::IsContainerNode(*nextLeafContentOrBlock) &&
- !HTMLEditUtils::IsReplacedElement(*nextLeafContentOrBlock->AsElement())) {
- // Empty inline containers such as <span></span>
- return BoundaryData(aPoint, *nextLeafContentOrBlock,
- WSType::EmptyInlineContainerElement);
- }
-
- if (!nextLeafContentOrBlock->IsText()) {
- // Other elements like <img>, etc.
+ if (!nextLeafContentOrBlock->IsText() ||
+ (aOptions.contains(Option::OnlyEditableNodes) &&
+ HTMLEditUtils::IsSimplyEditableNode(*nextLeafContentOrBlock) !=
+ HTMLEditUtils::IsSimplyEditableNode(aAncestorLimiter))) {
+ // we encountered a break or a special node, like <img>,
+ // that is not a block and not a break but still
+ // serves as a terminator to ws runs.
return BoundaryData(aPoint, *nextLeafContentOrBlock,
- WSType::SpecialContent);
+ nextLeafContentOrBlock->IsHTMLElement(nsGkAtoms::br)
+ ? WSType::BRElement
+ : WSType::SpecialContent);
}
if (!nextLeafContentOrBlock->AsText()->DataBuffer().GetLength()) {
@@ -619,19 +599,9 @@ WSRunScanner::TextFragmentData::VisibleWhiteSpacesDataRef() const {
// If all things are obviously visible, we can return range for all of the
// things quickly.
const bool mayHaveInvisibleLeadingSpace =
- !StartsFromNonCollapsibleCharacters() && !StartsFromSpecialContent() &&
- !(StartsFromEmptyInlineContainerElement() &&
- // XXX I think we don't need to check display:none here for now
- // because in the other cases, we don't do that.
- HTMLEditUtils::IsVisibleElementEvenIfLeafNode(
- *GetStartReasonContent()));
+ !StartsFromNonCollapsibleCharacters() && !StartsFromSpecialContent();
const bool mayHaveInvisibleTrailingWhiteSpace =
!EndsByNonCollapsibleCharacters() && !EndsBySpecialContent() &&
- !(EndsByEmptyInlineContainerElement() &&
- // XXX I think we don't need to check display:none here for now
- // because in the other cases, we don't do that.
- HTMLEditUtils::IsVisibleElementEvenIfLeafNode(
- *GetEndReasonContent())) &&
!EndsByBRElement() && !EndsByInvisiblePreformattedLineBreak();
if (!mayHaveInvisibleLeadingSpace && !mayHaveInvisibleTrailingWhiteSpace) {
@@ -901,26 +871,21 @@ EditorDOMPointType WSRunScanner::TextFragmentData::GetInclusiveNextCharPoint(
aOptions.contains(Option::ReferHTMLDefaultStyle)
? BlockInlineCheck::UseHTMLDefaultStyle
: BlockInlineCheck::Auto;
- const LeafNodeOptions leafNodeOptions =
- ToLeafNodeOptions(aOptions) + LeafNodeOption::TreatChildBlockAsLeafNode;
const EditorRawDOMPoint point = [&]() MOZ_NEVER_INLINE_DEBUG {
- if (!aPoint.CanContainerHaveChildren()) {
- return aPoint.template To<EditorRawDOMPoint>();
- }
- nsIContent* const child =
- aPoint.GetPreviousSiblingOfChild()
- ? HTMLEditUtils::GetNextSibling(
- *aPoint.GetPreviousSiblingOfChild(), leafNodeOptions,
- UseComputedDisplayOutsideStyleIfAuto(blockInlineCheck))
- : HTMLEditUtils::GetFirstChild(
- *aPoint.GetContainer(), leafNodeOptions,
- UseComputedDisplayOutsideStyleIfAuto(blockInlineCheck));
+ nsIContent* const child = [&]() -> nsIContent* {
+ nsIContent* child =
+ aPoint.CanContainerHaveChildren() ? aPoint.GetChild() : nullptr;
+ // XXX Why don't we skip non-editable nodes here?
+ while (child && child->IsComment() &&
+ !aOptions.contains(Option::StopAtComment)) {
+ child = child->GetNextSibling();
+ }
+ return child;
+ }();
if (!child ||
HTMLEditUtils::IsBlockElement(
*child, UseComputedDisplayOutsideStyleIfAuto(blockInlineCheck)) ||
- ((!aOptions.contains(Option::IgnoreEmptyInlineContainers) ||
- !HTMLEditUtils::IsContainerNode(*child)) &&
- HTMLEditUtils::IsVisibleElementEvenIfLeafNode(*child))) {
+ HTMLEditUtils::IsVisibleElementEvenIfLeafNode(*child)) {
return aPoint.template To<EditorRawDOMPoint>();
}
if (!child->HasChildNodes()) {
@@ -934,16 +899,14 @@ EditorDOMPointType WSRunScanner::TextFragmentData::GetInclusiveNextCharPoint(
// block because end reason content should not be the other side of the
// following block boundary.
nsIContent* const leafContent = HTMLEditUtils::GetFirstLeafContent(
- *child, leafNodeOptions, blockInlineCheck);
+ *child, {LeafNodeType::LeafNodeOrChildBlock}, blockInlineCheck);
if (!leafContent) {
return EditorRawDOMPoint(child, 0);
}
if (HTMLEditUtils::IsBlockElement(
*leafContent,
UseComputedDisplayOutsideStyleIfAuto(blockInlineCheck)) ||
- ((!aOptions.contains(Option::IgnoreEmptyInlineContainers) ||
- !HTMLEditUtils::IsContainerNode(*leafContent)) &&
- HTMLEditUtils::IsVisibleElementEvenIfLeafNode(*leafContent))) {
+ HTMLEditUtils::IsVisibleElementEvenIfLeafNode(*leafContent)) {
return EditorRawDOMPoint();
}
return EditorRawDOMPoint(leafContent, 0);
@@ -980,14 +943,24 @@ EditorDOMPointType WSRunScanner::TextFragmentData::GetInclusiveNextCharPoint(
return EditorDOMPointType();
}
+ const auto leafNodeTypes = [&]() -> LeafNodeTypes {
+ auto types = aIgnoreNonEditableNodes == IgnoreNonEditableNodes::Yes
+ ? LeafNodeTypes(LeafNodeType::LeafNodeOrNonEditableNode,
+ LeafNodeType::LeafNodeOrChildBlock)
+ : LeafNodeTypes(LeafNodeType::LeafNodeOrChildBlock);
+ if (aOptions.contains(Option::StopAtComment)) {
+ types += LeafNodeType::TreatCommentAsLeafNode;
+ }
+ return types;
+ }();
for (nsIContent* nextContent =
HTMLEditUtils::GetNextLeafContentOrNextBlockElement(
- *point.ContainerAs<nsIContent>(), leafNodeOptions,
+ *point.ContainerAs<nsIContent>(), leafNodeTypes,
blockInlineCheck,
editableBlockElementOrInlineEditingHostOrNonEditableRootElement);
nextContent;
nextContent = HTMLEditUtils::GetNextLeafContentOrNextBlockElement(
- *nextContent, leafNodeOptions, blockInlineCheck,
+ *nextContent, leafNodeTypes, blockInlineCheck,
editableBlockElementOrInlineEditingHostOrNonEditableRootElement)) {
if (!nextContent->IsText() ||
(aIgnoreNonEditableNodes == IgnoreNonEditableNodes::Yes &&
@@ -996,9 +969,7 @@ EditorDOMPointType WSRunScanner::TextFragmentData::GetInclusiveNextCharPoint(
HTMLEditUtils::IsBlockElement(
*nextContent,
UseComputedDisplayOutsideStyleIfAuto(blockInlineCheck)) ||
- ((!aOptions.contains(Option::IgnoreEmptyInlineContainers) ||
- !HTMLEditUtils::IsContainerNode(*nextContent)) &&
- HTMLEditUtils::IsVisibleElementEvenIfLeafNode(*nextContent))) {
+ HTMLEditUtils::IsVisibleElementEvenIfLeafNode(*nextContent)) {
break; // Reached end of current runs.
}
continue;
@@ -1025,27 +996,23 @@ EditorDOMPointType WSRunScanner::TextFragmentData::GetPreviousCharPoint(
aOptions.contains(Option::ReferHTMLDefaultStyle)
? BlockInlineCheck::UseHTMLDefaultStyle
: BlockInlineCheck::Auto;
- const LeafNodeOptions leafNodeOptions =
- ToLeafNodeOptions(aOptions) + LeafNodeOption::TreatChildBlockAsLeafNode;
const EditorRawDOMPoint point = [&]() MOZ_NEVER_INLINE_DEBUG {
- if (!aPoint.CanContainerHaveChildren()) {
- return aPoint.template To<EditorRawDOMPoint>();
- }
- nsIContent* const previousChild =
- aPoint.GetChild()
- ? HTMLEditUtils::GetPreviousSibling(
- *aPoint.GetChild(), leafNodeOptions,
- UseComputedDisplayOutsideStyleIfAuto(blockInlineCheck))
- : HTMLEditUtils::GetLastChild(
- *aPoint.GetContainer(), leafNodeOptions,
- UseComputedDisplayOutsideStyleIfAuto(blockInlineCheck));
+ nsIContent* const previousChild = [&]() -> nsIContent* {
+ nsIContent* previousChild = aPoint.CanContainerHaveChildren()
+ ? aPoint.GetPreviousSiblingOfChild()
+ : nullptr;
+ // XXX Why don't we skip non-editable nodes here?
+ while (previousChild && previousChild->IsComment() &&
+ !aOptions.contains(Option::StopAtComment)) {
+ previousChild = previousChild->GetPreviousSibling();
+ }
+ return previousChild;
+ }();
if (!previousChild ||
HTMLEditUtils::IsBlockElement(
*previousChild,
UseComputedDisplayOutsideStyleIfAuto(blockInlineCheck)) ||
- ((!aOptions.contains(Option::IgnoreEmptyInlineContainers) ||
- !HTMLEditUtils::IsContainerNode(*previousChild)) &&
- HTMLEditUtils::IsVisibleElementEvenIfLeafNode(*previousChild))) {
+ HTMLEditUtils::IsVisibleElementEvenIfLeafNode(*previousChild)) {
return aPoint.template To<EditorRawDOMPoint>();
}
if (!previousChild->HasChildren()) {
@@ -1060,16 +1027,14 @@ EditorDOMPointType WSRunScanner::TextFragmentData::GetPreviousCharPoint(
// block because end reason content should not be the other side of the
// following block boundary.
nsIContent* const leafContent = HTMLEditUtils::GetLastLeafContent(
- *previousChild, leafNodeOptions, blockInlineCheck);
+ *previousChild, {LeafNodeType::LeafNodeOrChildBlock}, blockInlineCheck);
if (!leafContent) {
return EditorRawDOMPoint::AtEndOf(*previousChild);
}
if (HTMLEditUtils::IsBlockElement(
*leafContent,
UseComputedDisplayOutsideStyleIfAuto(blockInlineCheck)) ||
- ((!aOptions.contains(Option::IgnoreEmptyInlineContainers) ||
- !HTMLEditUtils::IsContainerNode(*leafContent)) &&
- HTMLEditUtils::IsVisibleElementEvenIfLeafNode(*leafContent))) {
+ HTMLEditUtils::IsVisibleElementEvenIfLeafNode(*leafContent)) {
return EditorRawDOMPoint();
}
return EditorRawDOMPoint::AtEndOf(*leafContent);
@@ -1107,16 +1072,25 @@ EditorDOMPointType WSRunScanner::TextFragmentData::GetPreviousCharPoint(
return EditorDOMPointType();
}
+ const auto leafNodeTypes = [&]() -> LeafNodeTypes {
+ auto types = aIgnoreNonEditableNodes == IgnoreNonEditableNodes::Yes
+ ? LeafNodeTypes(LeafNodeType::LeafNodeOrNonEditableNode,
+ LeafNodeType::LeafNodeOrChildBlock)
+ : LeafNodeTypes(LeafNodeType::LeafNodeOrChildBlock);
+ if (aOptions.contains(Option::StopAtComment)) {
+ types += LeafNodeType::TreatCommentAsLeafNode;
+ }
+ return types;
+ }();
for (
nsIContent* previousContent =
HTMLEditUtils::GetPreviousLeafContentOrPreviousBlockElement(
- *point.ContainerAs<nsIContent>(), leafNodeOptions,
- blockInlineCheck,
+ *point.ContainerAs<nsIContent>(), leafNodeTypes, blockInlineCheck,
editableBlockElementOrInlineEditingHostOrNonEditableRootElement);
previousContent;
previousContent =
HTMLEditUtils::GetPreviousLeafContentOrPreviousBlockElement(
- *previousContent, leafNodeOptions, blockInlineCheck,
+ *previousContent, leafNodeTypes, blockInlineCheck,
editableBlockElementOrInlineEditingHostOrNonEditableRootElement)) {
if (!previousContent->IsText() ||
(aIgnoreNonEditableNodes == IgnoreNonEditableNodes::Yes &&
@@ -1125,9 +1099,7 @@ EditorDOMPointType WSRunScanner::TextFragmentData::GetPreviousCharPoint(
HTMLEditUtils::IsBlockElement(
*previousContent,
UseComputedDisplayOutsideStyleIfAuto(blockInlineCheck)) ||
- ((!aOptions.contains(Option::IgnoreEmptyInlineContainers) ||
- !HTMLEditUtils::IsContainerNode(*previousContent)) &&
- HTMLEditUtils::IsVisibleElementEvenIfLeafNode(*previousContent))) {
+ HTMLEditUtils::IsVisibleElementEvenIfLeafNode(*previousContent)) {
break; // Reached start of current runs.
}
continue;
@@ -1426,10 +1398,7 @@ EditorDOMPointInText WSRunScanner::TextFragmentData::
const VisibleWhiteSpacesData& visibleWhiteSpaces =
VisibleWhiteSpacesDataRef();
if (!visibleWhiteSpaces.StartsFromNonCollapsibleCharacters() &&
- !visibleWhiteSpaces.StartsFromSpecialContent() &&
- !(visibleWhiteSpaces.StartsFromEmptyInlineContainerElement() &&
- HTMLEditUtils::IsVisibleElementEvenIfLeafNode(
- *GetStartReasonContent()))) {
+ !visibleWhiteSpaces.StartsFromSpecialContent()) {
return EditorDOMPointInText();
}
return atPreviousChar;
@@ -1487,9 +1456,6 @@ EditorDOMPointInText WSRunScanner::TextFragmentData::
VisibleWhiteSpacesDataRef();
if (!visibleWhiteSpaces.EndsByNonCollapsibleCharacters() &&
!visibleWhiteSpaces.EndsBySpecialContent() &&
- !(visibleWhiteSpaces.EndsByEmptyInlineContainerElement() &&
- HTMLEditUtils::IsVisibleElementEvenIfLeafNode(
- *GetEndReasonContent())) &&
!visibleWhiteSpaces.EndsByBRElement()) {
return EditorDOMPointInText();
}
diff --git a/editor/libeditor/WhiteSpaceVisibilityKeeper.cpp b/editor/libeditor/WhiteSpaceVisibilityKeeper.cpp
@@ -32,7 +32,8 @@ namespace mozilla {
using namespace dom;
-using LeafNodeOption = HTMLEditUtils::LeafNodeOption;
+using LeafNodeType = HTMLEditUtils::LeafNodeType;
+using WalkTreeOption = HTMLEditUtils::WalkTreeOption;
Result<EditorDOMPoint, nsresult>
WhiteSpaceVisibilityKeeper::PrepareToSplitBlockElement(
@@ -922,14 +923,15 @@ WhiteSpaceVisibilityKeeper::NormalizeWhiteSpacesBefore(
aPoint.IsInTextNode() && aPoint.IsEndOfContainer()
? aPoint.ContainerAs<Text>()
: HTMLEditUtils::GetPreviousLeafContentOrPreviousBlockElement(
- aPoint, {LeafNodeOption::TreatChildBlockAsLeafNode},
+ aPoint,
+ {HTMLEditUtils::LeafNodeType::LeafNodeOrChildBlock},
BlockInlineCheck::UseComputedDisplayStyle,
colsetBlockElement);
previousContent;
previousContent =
HTMLEditUtils::GetPreviousLeafContentOrPreviousBlockElement(
EditorRawDOMPoint(previousContent),
- {LeafNodeOption::TreatChildBlockAsLeafNode},
+ {HTMLEditUtils::LeafNodeType::LeafNodeOrChildBlock},
BlockInlineCheck::UseComputedDisplayStyle, colsetBlockElement)) {
if (!HTMLEditUtils::IsSimplyEditableNode(*previousContent)) {
// XXX Assume non-editable nodes are visible.
@@ -1040,13 +1042,14 @@ WhiteSpaceVisibilityKeeper::NormalizeWhiteSpacesAfter(
aPoint.IsInTextNode() && aPoint.IsStartOfContainer()
? aPoint.ContainerAs<Text>()
: HTMLEditUtils::GetNextLeafContentOrNextBlockElement(
- aPoint, {LeafNodeOption::TreatChildBlockAsLeafNode},
+ aPoint,
+ {HTMLEditUtils::LeafNodeType::LeafNodeOrChildBlock},
BlockInlineCheck::UseComputedDisplayStyle,
colsetBlockElement);
nextContent;
nextContent = HTMLEditUtils::GetNextLeafContentOrNextBlockElement(
EditorRawDOMPoint::After(*nextContent),
- {LeafNodeOption::TreatChildBlockAsLeafNode},
+ {HTMLEditUtils::LeafNodeType::LeafNodeOrChildBlock},
BlockInlineCheck::UseComputedDisplayStyle, colsetBlockElement)) {
if (!HTMLEditUtils::IsSimplyEditableNode(*nextContent)) {
// XXX Assume non-editable nodes are visible.
@@ -1293,13 +1296,13 @@ WhiteSpaceVisibilityKeeper::NormalizeWhiteSpacesToSplitAt(
if (!pointToSplit.IsInTextNode() || pointToSplit.IsStartOfContainer()) {
for (nsCOMPtr<nsIContent> previousContent =
HTMLEditUtils::GetPreviousLeafContentOrPreviousBlockElement(
- pointToSplit, {LeafNodeOption::TreatChildBlockAsLeafNode},
+ pointToSplit, {LeafNodeType::LeafNodeOrChildBlock},
BlockInlineCheck::UseComputedDisplayStyle,
closestBlockElement);
previousContent;
previousContent =
HTMLEditUtils::GetPreviousLeafContentOrPreviousBlockElement(
- *previousContent, {LeafNodeOption::TreatChildBlockAsLeafNode},
+ *previousContent, {LeafNodeType::LeafNodeOrChildBlock},
BlockInlineCheck::UseComputedDisplayStyle,
closestBlockElement)) {
if (auto* const textNode = Text::FromNode(previousContent)) {
@@ -1334,12 +1337,12 @@ WhiteSpaceVisibilityKeeper::NormalizeWhiteSpacesToSplitAt(
if (!pointToSplit.IsInTextNode() || pointToSplit.IsEndOfContainer()) {
for (nsCOMPtr<nsIContent> nextContent =
HTMLEditUtils::GetNextLeafContentOrNextBlockElement(
- pointToSplit, {LeafNodeOption::TreatChildBlockAsLeafNode},
+ pointToSplit, {LeafNodeType::LeafNodeOrChildBlock},
BlockInlineCheck::UseComputedDisplayStyle,
closestBlockElement);
nextContent;
nextContent = HTMLEditUtils::GetNextLeafContentOrNextBlockElement(
- *nextContent, {LeafNodeOption::TreatChildBlockAsLeafNode},
+ *nextContent, {LeafNodeType::LeafNodeOrChildBlock},
BlockInlineCheck::UseComputedDisplayStyle, closestBlockElement)) {
if (auto* const textNode = Text::FromNode(nextContent)) {
if (!HTMLEditUtils::IsSimplyEditableNode(*textNode) &&
@@ -1694,14 +1697,14 @@ nsresult WhiteSpaceVisibilityKeeper::EnsureNoInvisibleWhiteSpacesAfter(
for (nsIContent* nextContent =
HTMLEditUtils::GetNextLeafContentOrNextBlockElement(
aPoint,
- {LeafNodeOption::TreatChildBlockAsLeafNode,
- LeafNodeOption::TreatCommentAsLeafNode},
+ {HTMLEditUtils::LeafNodeType::LeafNodeOrChildBlock,
+ HTMLEditUtils::LeafNodeType::TreatCommentAsLeafNode},
BlockInlineCheck::UseComputedDisplayStyle, colsetBlockElement);
nextContent;
nextContent = HTMLEditUtils::GetNextLeafContentOrNextBlockElement(
EditorRawDOMPoint::After(*nextContent),
- {LeafNodeOption::TreatChildBlockAsLeafNode,
- LeafNodeOption::TreatCommentAsLeafNode},
+ {HTMLEditUtils::LeafNodeType::LeafNodeOrChildBlock,
+ HTMLEditUtils::LeafNodeType::TreatCommentAsLeafNode},
BlockInlineCheck::UseComputedDisplayStyle, colsetBlockElement)) {
if (!HTMLEditUtils::IsSimplyEditableNode(*nextContent)) {
// XXX Assume non-editable nodes are visible.
@@ -1778,15 +1781,15 @@ nsresult WhiteSpaceVisibilityKeeper::EnsureNoInvisibleWhiteSpacesBefore(
for (nsIContent* previousContent =
HTMLEditUtils::GetPreviousLeafContentOrPreviousBlockElement(
aPoint,
- {LeafNodeOption::TreatChildBlockAsLeafNode,
- LeafNodeOption::TreatCommentAsLeafNode},
+ {HTMLEditUtils::LeafNodeType::LeafNodeOrChildBlock,
+ HTMLEditUtils::LeafNodeType::TreatCommentAsLeafNode},
BlockInlineCheck::UseComputedDisplayStyle, colsetBlockElement);
previousContent;
previousContent =
HTMLEditUtils::GetPreviousLeafContentOrPreviousBlockElement(
EditorRawDOMPoint(previousContent),
- {LeafNodeOption::TreatChildBlockAsLeafNode,
- LeafNodeOption::TreatCommentAsLeafNode},
+ {HTMLEditUtils::LeafNodeType::LeafNodeOrChildBlock,
+ HTMLEditUtils::LeafNodeType::TreatCommentAsLeafNode},
BlockInlineCheck::UseComputedDisplayStyle, colsetBlockElement)) {
if (!HTMLEditUtils::IsSimplyEditableNode(*previousContent)) {
// XXX Assume non-editable nodes are visible.
@@ -2522,10 +2525,9 @@ WhiteSpaceVisibilityKeeper::DeleteContentNodeAndJoinTextNodesAroundIt(
}
}
- const nsCOMPtr<nsIContent> previousEditableSibling =
+ nsCOMPtr<nsIContent> previousEditableSibling =
HTMLEditUtils::GetPreviousSibling(
- aContentToDelete, {LeafNodeOption::IgnoreNonEditableNode},
- BlockInlineCheck::UseComputedDisplayOutsideStyle);
+ aContentToDelete, {WalkTreeOption::IgnoreNonEditableNode});
// Delete the node, and join like nodes if appropriate
nsresult rv = aHTMLEditor.DeleteNodeWithTransaction(aContentToDelete);
if (NS_FAILED(rv)) {
@@ -2548,9 +2550,8 @@ WhiteSpaceVisibilityKeeper::DeleteContentNodeAndJoinTextNodesAroundIt(
return CaretPoint(std::move(pointToPutCaret));
}
- nsIContent* const nextEditableSibling = HTMLEditUtils::GetNextSibling(
- *previousEditableSibling, {LeafNodeOption::IgnoreNonEditableNode},
- BlockInlineCheck::UseComputedDisplayOutsideStyle);
+ nsIContent* nextEditableSibling = HTMLEditUtils::GetNextSibling(
+ *previousEditableSibling, {WalkTreeOption::IgnoreNonEditableNode});
if (aCaretPoint.GetContainer() != nextEditableSibling) {
return CaretPoint(std::move(pointToPutCaret));
}
diff --git a/editor/libeditor/gtest/TestHTMLEditUtils.cpp b/editor/libeditor/gtest/TestHTMLEditUtils.cpp
@@ -1445,7 +1445,7 @@ TEST(HTMLEditUtilsTest, IsEmptyNode)
struct MOZ_STACK_CLASS GetLeafNodeTest final {
const char16_t* mInnerHTML;
const char* mContentSelector;
- const HTMLEditUtils::LeafNodeOptions mOptions;
+ const HTMLEditUtils::LeafNodeTypes mTypes;
const char* mExpectedTargetSelector;
const char* mExpectedTargetContainerSelector = nullptr;
const uint32_t mExpectedTargetOffset = 0u;
@@ -1469,7 +1469,7 @@ struct MOZ_STACK_CLASS GetLeafNodeTest final {
friend std::ostream& operator<<(std::ostream& aStream,
const GetLeafNodeTest& aTest) {
return aStream << "Scan from \"" << aTest.mContentSelector
- << "\" with options=" << ToString(aTest.mOptions).c_str()
+ << "\" with options=" << ToString(aTest.mTypes).c_str()
<< " in \"" << NS_ConvertUTF16toUTF8(aTest.mInnerHTML).get()
<< "\"";
}
@@ -1477,7 +1477,7 @@ struct MOZ_STACK_CLASS GetLeafNodeTest final {
TEST(HTMLEditUtilsTest, GetLastLeafContent)
{
- using LeafNodeOption = HTMLEditUtils::LeafNodeOption;
+ using LeafNodeType = HTMLEditUtils::LeafNodeType;
const RefPtr<Document> doc = CreateHTMLDoc();
const RefPtr<nsGenericHTMLElement> body = doc->GetBody();
MOZ_RELEASE_ASSERT(body);
@@ -1486,12 +1486,11 @@ TEST(HTMLEditUtilsTest, GetLastLeafContent)
GetLeafNodeTest{u"<div><br></div>", "div", {}, "div > br"},
GetLeafNodeTest{u"<div>abc<br></div>", "div", {}, "div > br"},
GetLeafNodeTest{u"<div>abc</div>", "div", {}, nullptr, "div", 0u},
-
GetLeafNodeTest{
u"<div><div><br></div></div>", "div", {}, "div > div > br"},
GetLeafNodeTest{u"<div><div><br></div></div>",
"div",
- {LeafNodeOption::TreatChildBlockAsLeafNode},
+ {LeafNodeType::LeafNodeOrChildBlock},
"div > div"},
GetLeafNodeTest{u"<div><div><br></div><div><br></div></div>",
"div",
@@ -1499,20 +1498,19 @@ TEST(HTMLEditUtilsTest, GetLastLeafContent)
"div > div + div > br"},
GetLeafNodeTest{u"<div><div><br></div><div><br></div></div>",
"div",
- {LeafNodeOption::TreatChildBlockAsLeafNode},
+ {LeafNodeType::LeafNodeOrChildBlock},
"div > div + div"},
-
GetLeafNodeTest{u"<div><!--abc--></div>", "div", {}, nullptr},
GetLeafNodeTest{u"<div><!--abc--></div>",
"div",
- {LeafNodeOption::TreatCommentAsLeafNode},
+ {LeafNodeType::TreatCommentAsLeafNode},
nullptr,
"div",
0u},
GetLeafNodeTest{u"<div><br><!--abc--></div>", "div", {}, "div > br"},
GetLeafNodeTest{u"<div><br><!--abc--></div>",
"div",
- {LeafNodeOption::TreatCommentAsLeafNode},
+ {LeafNodeType::TreatCommentAsLeafNode},
nullptr,
"div",
1u},
@@ -1524,74 +1522,10 @@ TEST(HTMLEditUtilsTest, GetLastLeafContent)
GetLeafNodeTest{
u"<div><div><br></div><div><br></div><!--abc--></div>",
"div",
- {LeafNodeOption::TreatCommentAsLeafNode},
+ {LeafNodeType::TreatCommentAsLeafNode},
nullptr,
"div",
2u},
-
- GetLeafNodeTest{
- u"<div><span></span></div>", "div", {}, "div > span"},
- GetLeafNodeTest{u"<div><span></span></div>",
- "div",
- {LeafNodeOption::IgnoreAnyEmptyInlineContainers},
- nullptr},
- GetLeafNodeTest{
- u"<div><br><span></span></div>", "div", {}, "div > span"},
- GetLeafNodeTest{u"<div><br><span></span></div>",
- "div",
- {LeafNodeOption::IgnoreAnyEmptyInlineContainers},
- "div > br"},
- GetLeafNodeTest{
- u"<div><div><br></div><div><br></div><span></span></div>",
- "div",
- {},
- "div > span"},
- GetLeafNodeTest{
- u"<div><div><br></div><div><br></div><span></span></div>",
- "div",
- {LeafNodeOption::IgnoreAnyEmptyInlineContainers},
- "div > div + div > br"},
-
- GetLeafNodeTest{u"<div><span><!-- abc --></span></div>",
- "div",
- {},
- "div > span"},
- GetLeafNodeTest{u"<div><span><!-- abc --></span></div>",
- "div",
- {LeafNodeOption::TreatCommentAsLeafNode},
- nullptr,
- "div > span",
- 0u},
- GetLeafNodeTest{u"<div><span><!-- abc --></span></div>",
- "div",
- {LeafNodeOption::IgnoreAnyEmptyInlineContainers},
- nullptr},
- GetLeafNodeTest{u"<div><span><!-- abc --></span></div>",
- "div",
- {LeafNodeOption::IgnoreAnyEmptyInlineContainers,
- LeafNodeOption::TreatCommentAsLeafNode},
- nullptr,
- "div > span",
- 0u},
-
- GetLeafNodeTest{u"<div><br><wbr></div>", "div", {}, "div > wbr"},
- GetLeafNodeTest{u"<div><br><wbr></div>",
- "div",
- {LeafNodeOption::IgnoreAnyEmptyInlineContainers},
- "div > wbr"},
- GetLeafNodeTest{u"<div><br><wbr></div>",
- "div",
- {LeafNodeOption::IgnoreInvisibleInlineVoidElements},
- "div > br"},
-
- GetLeafNodeTest{
- u"<div><span>abc</span> </div>", "div", {}, nullptr, "div", 1u},
- GetLeafNodeTest{u"<div><span>abc</span> </div>",
- "div",
- {LeafNodeOption::IgnoreInvisibleText},
- nullptr,
- "div > span",
- 0u},
}) {
body->SetInnerHTMLTrusted(nsDependentString(testData.mInnerHTML),
doc->NodePrincipal(), IgnoreErrors());
@@ -1599,7 +1533,7 @@ TEST(HTMLEditUtilsTest, GetLastLeafContent)
nsDependentCString(testData.mContentSelector), IgnoreErrors());
MOZ_RELEASE_ASSERT(target);
const nsIContent* result = HTMLEditUtils::GetLastLeafContent(
- *target, testData.mOptions,
+ *target, testData.mTypes,
BlockInlineCheck::UseComputedDisplayOutsideStyle);
EXPECT_EQ(result, testData.GetExpectedTarget(*body))
<< "GetLastLeafContent: " << testData
@@ -1609,7 +1543,7 @@ TEST(HTMLEditUtilsTest, GetLastLeafContent)
TEST(HTMLEditUtilsTest, GetFirstLeafContent)
{
- using LeafNodeOption = HTMLEditUtils::LeafNodeOption;
+ using LeafNodeType = HTMLEditUtils::LeafNodeType;
const RefPtr<Document> doc = CreateHTMLDoc();
const RefPtr<nsGenericHTMLElement> body = doc->GetBody();
MOZ_RELEASE_ASSERT(body);
@@ -1621,10 +1555,9 @@ TEST(HTMLEditUtilsTest, GetFirstLeafContent)
GetLeafNodeTest{u"<div>abc</div>", "div", {}, nullptr, "div", 0u},
GetLeafNodeTest{
u"<div><div><br></div></div>", "div", {}, "div > div > br"},
-
GetLeafNodeTest{u"<div><div><br></div></div>",
"div",
- {LeafNodeOption::TreatChildBlockAsLeafNode},
+ {LeafNodeType::LeafNodeOrChildBlock},
"div > div"},
GetLeafNodeTest{u"<div><div><br></div><div><br></div></div>",
"div",
@@ -1632,20 +1565,19 @@ TEST(HTMLEditUtilsTest, GetFirstLeafContent)
"div > div > br"},
GetLeafNodeTest{u"<div><div><br></div><div><br></div></div>",
"div",
- {LeafNodeOption::TreatChildBlockAsLeafNode},
+ {LeafNodeType::LeafNodeOrChildBlock},
"div > div"},
-
GetLeafNodeTest{u"<div><!--abc--></div>", "div", {}, nullptr},
GetLeafNodeTest{u"<div><!--abc--></div>",
"div",
- {LeafNodeOption::TreatCommentAsLeafNode},
+ {LeafNodeType::TreatCommentAsLeafNode},
nullptr,
"div",
0u},
GetLeafNodeTest{u"<div><!--abc--><br></div>", "div", {}, "div > br"},
GetLeafNodeTest{u"<div><!--abc--><br></div>",
"div",
- {LeafNodeOption::TreatCommentAsLeafNode},
+ {LeafNodeType::TreatCommentAsLeafNode},
nullptr,
"div",
0u},
@@ -1657,72 +1589,10 @@ TEST(HTMLEditUtilsTest, GetFirstLeafContent)
GetLeafNodeTest{
u"<div><!--abc--><div><br></div><div><br></div></div>",
"div",
- {LeafNodeOption::TreatCommentAsLeafNode},
+ {LeafNodeType::TreatCommentAsLeafNode},
nullptr,
"div",
0u},
-
- GetLeafNodeTest{
- u"<div><span></span></div>", "div", {}, "div > span"},
- GetLeafNodeTest{u"<div><span></span></div>",
- "div",
- {LeafNodeOption::IgnoreAnyEmptyInlineContainers},
- nullptr},
- GetLeafNodeTest{u"<div><span></span><br></div>",
- "div",
- {LeafNodeOption::IgnoreAnyEmptyInlineContainers},
- "div > br"},
- GetLeafNodeTest{
- u"<div><span></span><div><br></div><div><br></div></div>",
- "div",
- {},
- "div > span"},
- GetLeafNodeTest{
- u"<div><span></span><div><br></div><div><br></div></div>",
- "div",
- {LeafNodeOption::IgnoreAnyEmptyInlineContainers},
- "div > div > br"},
-
- GetLeafNodeTest{u"<div><span><!-- abc --></span></div>",
- "div",
- {},
- "div > span"},
- GetLeafNodeTest{u"<div><span><!-- abc --></span></div>",
- "div",
- {LeafNodeOption::TreatCommentAsLeafNode},
- nullptr,
- "div > span",
- 0u},
- GetLeafNodeTest{u"<div><span><!-- abc --></span></div>",
- "div",
- {LeafNodeOption::IgnoreAnyEmptyInlineContainers},
- nullptr},
- GetLeafNodeTest{u"<div><span><!-- abc --></span></div>",
- "div",
- {LeafNodeOption::IgnoreAnyEmptyInlineContainers,
- LeafNodeOption::TreatCommentAsLeafNode},
- nullptr,
- "div > span",
- 0u},
-
- GetLeafNodeTest{u"<div><wbr><br></div>", "div", {}, "div > wbr"},
- GetLeafNodeTest{u"<div><wbr><br></div>",
- "div",
- {LeafNodeOption::IgnoreAnyEmptyInlineContainers},
- "div > wbr"},
- GetLeafNodeTest{u"<div><wbr><br></div>",
- "div",
- {LeafNodeOption::IgnoreInvisibleInlineVoidElements},
- "div > br"},
-
- GetLeafNodeTest{
- u"<div> <span>abc</span></div>", "div", {}, nullptr, "div", 0u},
- GetLeafNodeTest{u"<div> <span>abc</span></div>",
- "div",
- {LeafNodeOption::IgnoreInvisibleText},
- nullptr,
- "div > span",
- 0u},
}) {
body->SetInnerHTMLTrusted(nsDependentString(testData.mInnerHTML),
doc->NodePrincipal(), IgnoreErrors());
@@ -1730,7 +1600,7 @@ TEST(HTMLEditUtilsTest, GetFirstLeafContent)
nsDependentCString(testData.mContentSelector), IgnoreErrors());
MOZ_RELEASE_ASSERT(target);
const nsIContent* result = HTMLEditUtils::GetFirstLeafContent(
- *target, testData.mOptions,
+ *target, testData.mTypes,
BlockInlineCheck::UseComputedDisplayOutsideStyle);
EXPECT_EQ(result, testData.GetExpectedTarget(*body))
<< "GetFirstLeafContent: " << testData
@@ -1740,7 +1610,7 @@ TEST(HTMLEditUtilsTest, GetFirstLeafContent)
TEST(HTMLEditUtilsTest, GetNextLeafContentOrNextBlockElement_Content)
{
- using LeafNodeOption = HTMLEditUtils::LeafNodeOption;
+ using LeafNodeType = HTMLEditUtils::LeafNodeType;
const RefPtr<Document> doc = CreateHTMLDoc();
const RefPtr<nsGenericHTMLElement> body = doc->GetBody();
MOZ_RELEASE_ASSERT(body);
@@ -1750,7 +1620,7 @@ TEST(HTMLEditUtilsTest, GetNextLeafContentOrNextBlockElement_Content)
u"<div><br></div><!--abc--><p><br></p>", "div", {}, "p"},
GetLeafNodeTest{u"<div><br></div><!--abc--><p><br></p>",
"div",
- {LeafNodeOption::TreatCommentAsLeafNode},
+ {LeafNodeType::TreatCommentAsLeafNode},
nullptr,
"body",
1u},
@@ -1762,7 +1632,7 @@ TEST(HTMLEditUtilsTest, GetNextLeafContentOrNextBlockElement_Content)
"span > br"},
GetLeafNodeTest{u"<div><br></div><span><!--abc--><br></span>",
"div",
- {LeafNodeOption::TreatCommentAsLeafNode},
+ {LeafNodeType::TreatCommentAsLeafNode},
nullptr,
"span",
0u},
@@ -1774,7 +1644,7 @@ TEST(HTMLEditUtilsTest, GetNextLeafContentOrNextBlockElement_Content)
MOZ_RELEASE_ASSERT(target);
const nsIContent* result =
HTMLEditUtils::GetNextLeafContentOrNextBlockElement(
- *target, testData.mOptions,
+ *target, testData.mTypes,
BlockInlineCheck::UseComputedDisplayOutsideStyle);
EXPECT_EQ(result, testData.GetExpectedTarget(*body->GetParentNode()))
<< "GetNextLeafContentOrNextBlockElement: " << testData
@@ -1786,7 +1656,7 @@ TEST(HTMLEditUtilsTest, GetNextLeafContentOrNextBlockElement_Content)
TEST(HTMLEditUtilsTest, GetPreviousLeafContentOrPreviousBlockElement_Content)
{
- using LeafNodeOption = HTMLEditUtils::LeafNodeOption;
+ using LeafNodeType = HTMLEditUtils::LeafNodeType;
const RefPtr<Document> doc = CreateHTMLDoc();
const RefPtr<nsGenericHTMLElement> body = doc->GetBody();
MOZ_RELEASE_ASSERT(body);
@@ -1796,7 +1666,7 @@ TEST(HTMLEditUtilsTest, GetPreviousLeafContentOrPreviousBlockElement_Content)
u"<p><br></p><!--abc--><div><br></div>", "div", {}, "p"},
GetLeafNodeTest{u"<p><br></p><!--abc--><div><br></div>",
"div",
- {LeafNodeOption::TreatCommentAsLeafNode},
+ {LeafNodeType::TreatCommentAsLeafNode},
nullptr,
"body",
1u},
@@ -1808,7 +1678,7 @@ TEST(HTMLEditUtilsTest, GetPreviousLeafContentOrPreviousBlockElement_Content)
"span > br"},
GetLeafNodeTest{u"<span><br><!--abc--></span><div><br></div>",
"div",
- {LeafNodeOption::TreatCommentAsLeafNode},
+ {LeafNodeType::TreatCommentAsLeafNode},
nullptr,
"span",
1u},
@@ -1820,7 +1690,7 @@ TEST(HTMLEditUtilsTest, GetPreviousLeafContentOrPreviousBlockElement_Content)
MOZ_RELEASE_ASSERT(target);
const nsIContent* result =
HTMLEditUtils::GetPreviousLeafContentOrPreviousBlockElement(
- *target, testData.mOptions,
+ *target, testData.mTypes,
BlockInlineCheck::UseComputedDisplayOutsideStyle);
EXPECT_EQ(result, testData.GetExpectedTarget(*body->GetParentNode()))
<< "GetPreviousLeafContentOrPreviousBlockElement: " << testData
@@ -1831,440 +1701,6 @@ TEST(HTMLEditUtilsTest, GetPreviousLeafContentOrPreviousBlockElement_Content)
// TODO: Test GetPreviousLeafContentOrPreviousBlockElement() which takes
// EditorDOMPoint
-TEST(HTMLEditUtilsTest, GetNextLeafContent_Content)
-{
- using LeafNodeOption = HTMLEditUtils::LeafNodeOption;
- const RefPtr<Document> doc = CreateHTMLDoc();
- const RefPtr<nsGenericHTMLElement> body = doc->GetBody();
- MOZ_RELEASE_ASSERT(body);
- for (const auto& testData : {
- GetLeafNodeTest{u"<div><br></div><p><br></p>", "div", {}, "p > br"},
- GetLeafNodeTest{
- u"<div><br></div><!--abc--><p><br></p>", "div", {}, "p > br"},
- GetLeafNodeTest{u"<div><br></div><!--abc--><p><br></p>",
- "div",
- {LeafNodeOption::TreatCommentAsLeafNode},
- nullptr,
- "body",
- 1u},
- GetLeafNodeTest{
- u"<div><br></div><span><br></span>", "div", {}, "span > br"},
- GetLeafNodeTest{u"<div><br></div><span><!--abc--><br></span>",
- "div",
- {},
- "span > br"},
- GetLeafNodeTest{u"<div><br></div><span><!--abc--><br></span>",
- "div",
- {LeafNodeOption::TreatCommentAsLeafNode},
- nullptr,
- "span",
- 0u},
- }) {
- body->SetInnerHTMLTrusted(nsDependentString(testData.mInnerHTML),
- doc->NodePrincipal(), IgnoreErrors());
- const Element* const target = body->QuerySelector(
- nsDependentCString(testData.mContentSelector), IgnoreErrors());
- MOZ_RELEASE_ASSERT(target);
- const nsIContent* result = HTMLEditUtils::GetNextLeafContent(
- *target, testData.mOptions,
- BlockInlineCheck::UseComputedDisplayOutsideStyle);
- EXPECT_EQ(result, testData.GetExpectedTarget(*body->GetParentNode()))
- << "GetNextLeafContent: " << testData
- << "(Got: " << ToString(RefPtr{result}) << ")";
- }
-}
-
-// TODO: Test GetNextLeafContent() which takes EditorDOMPoint
-
-TEST(HTMLEditUtilsTest, GetPreviousLeafContent_Content)
-{
- using LeafNodeOption = HTMLEditUtils::LeafNodeOption;
- const RefPtr<Document> doc = CreateHTMLDoc();
- const RefPtr<nsGenericHTMLElement> body = doc->GetBody();
- MOZ_RELEASE_ASSERT(body);
- for (const auto& testData : {
- GetLeafNodeTest{u"<p><br></p><div><br></div>", "div", {}, "p > br"},
- GetLeafNodeTest{
- u"<p><br></p><!--abc--><div><br></div>", "div", {}, "p > br"},
- GetLeafNodeTest{u"<p><br></p><!--abc--><div><br></div>",
- "div",
- {LeafNodeOption::TreatCommentAsLeafNode},
- nullptr,
- "body",
- 1u},
- GetLeafNodeTest{
- u"<span><br></span><div><br></div>", "div", {}, "span > br"},
- GetLeafNodeTest{u"<span><br><!--abc--></span><div><br></div>",
- "div",
- {},
- "span > br"},
- GetLeafNodeTest{u"<span><br><!--abc--></span><div><br></div>",
- "div",
- {LeafNodeOption::TreatCommentAsLeafNode},
- nullptr,
- "span",
- 1u},
- }) {
- body->SetInnerHTMLTrusted(nsDependentString(testData.mInnerHTML),
- doc->NodePrincipal(), IgnoreErrors());
- const Element* const target = body->QuerySelector(
- nsDependentCString(testData.mContentSelector), IgnoreErrors());
- MOZ_RELEASE_ASSERT(target);
- const nsIContent* result = HTMLEditUtils::GetPreviousLeafContent(
- *target, testData.mOptions,
- BlockInlineCheck::UseComputedDisplayOutsideStyle);
- EXPECT_EQ(result, testData.GetExpectedTarget(*body->GetParentNode()))
- << "GetPreviousLeafContent: " << testData
- << "(Got: " << ToString(RefPtr{result}) << ")";
- }
-}
-
-// TODO: Test GetPreviousLeafContent() which takes EditorDOMPoint
-
-TEST(HTMLEditUtilsTest, GetPreviousSibling)
-{
- using LeafNodeOption = HTMLEditUtils::LeafNodeOption;
- const RefPtr<Document> doc = CreateHTMLDoc();
- const RefPtr<nsGenericHTMLElement> body = doc->GetBody();
- MOZ_RELEASE_ASSERT(body);
- for (const auto& testData : {
- GetLeafNodeTest{u"<div><p><br></p></div>", "div > p", {}, nullptr},
- GetLeafNodeTest{u"<div><p><br></p><p><br></p></div>",
- "div > p + p",
- {},
- "div > p"},
- GetLeafNodeTest{u"<div><p><br></p><!-- comment --><p><br></p></div>",
- "div p + p",
- {},
- "div > p"},
- GetLeafNodeTest{u"<div><p><br></p><!-- comment --><p><br></p></div>",
- "div > p + p",
- {LeafNodeOption::TreatCommentAsLeafNode},
- nullptr,
- "div",
- 1u},
- GetLeafNodeTest{u"<div><p><br></p> <p><br></p></div>",
- "div > p + p",
- {},
- nullptr,
- "div",
- 1u},
- GetLeafNodeTest{u"<div><p><br></p> <p><br></p></div>",
- "div > p + p",
- {LeafNodeOption::IgnoreInvisibleText},
- "div > p"},
- GetLeafNodeTest{
- u"<div contenteditable><p><br></p><p "
- u"contenteditable=\"false\"><br></p><p><br></p></div>",
- "div > p + p + p",
- {},
- "div > p + p"},
- GetLeafNodeTest{
- u"<div contenteditable><p><br></p><p "
- u"contenteditable=\"false\"><br></p><p><br></p></div>",
- "div > p + p + p",
- {LeafNodeOption::IgnoreNonEditableNode},
- "div > p"},
- GetLeafNodeTest{
- u"<div><b>abc</b><s><!-- comment --></s><i>def</i></div>",
- "div > i",
- {},
- "div > s"},
- GetLeafNodeTest{
- u"<div><b>abc</b><s><!-- comment --></s><i>def</i></div>",
- "div > i",
- {LeafNodeOption::IgnoreInvisibleEmptyInlineContainers},
- "div > b"},
- GetLeafNodeTest{
- u"<div><b>abc</b><s><!-- comment --></s><i>def</i></div>",
- "div > i",
- {LeafNodeOption::IgnoreInvisibleEmptyInlineContainers,
- LeafNodeOption::TreatCommentAsLeafNode},
- "div > s"},
- }) {
- body->SetInnerHTMLTrusted(nsDependentString(testData.mInnerHTML),
- doc->NodePrincipal(), IgnoreErrors());
- const Element* const target = body->QuerySelector(
- nsDependentCString(testData.mContentSelector), IgnoreErrors());
- MOZ_RELEASE_ASSERT(target);
- const nsIContent* result = HTMLEditUtils::GetPreviousSibling(
- *target, testData.mOptions,
- BlockInlineCheck::UseComputedDisplayOutsideStyle);
- EXPECT_EQ(result, testData.GetExpectedTarget(*body->GetParentNode()))
- << "GetPreviousSibling: " << testData
- << "(Got: " << ToString(RefPtr{result}) << ")";
- }
-}
-
-TEST(HTMLEditUtilsTest, GetNextSibling)
-{
- using LeafNodeOption = HTMLEditUtils::LeafNodeOption;
- const RefPtr<Document> doc = CreateHTMLDoc();
- const RefPtr<nsGenericHTMLElement> body = doc->GetBody();
- MOZ_RELEASE_ASSERT(body);
- for (const auto& testData : {
- GetLeafNodeTest{u"<div><p><br></p></div>", "div > p", {}, nullptr},
- GetLeafNodeTest{u"<div><p><br></p><p><br></p></div>",
- "div > p",
- {},
- "div > p + p"},
- GetLeafNodeTest{u"<div><p><br></p><!-- comment --><p><br></p></div>",
- "div > p",
- {},
- "div > p + p"},
- GetLeafNodeTest{u"<div><p><br></p><!-- comment --><p><br></p></div>",
- "div > p",
- {LeafNodeOption::TreatCommentAsLeafNode},
- nullptr,
- "div",
- 1u},
- GetLeafNodeTest{u"<div><p><br></p> <p><br></p></div>",
- "div > p",
- {},
- nullptr,
- "div",
- 1u},
- GetLeafNodeTest{u"<div><p><br></p> <p><br></p></div>",
- "div > p",
- {LeafNodeOption::IgnoreInvisibleText},
- "div > p + p"},
- GetLeafNodeTest{
- u"<div contenteditable><p><br></p><p "
- u"contenteditable=\"false\"><br></p><p><br></p></div>",
- "div p + p",
- {},
- "div > p + p + p"},
- GetLeafNodeTest{
- u"<div contenteditable><p><br></p><p "
- u"contenteditable=\"false\"><br></p><p><br></p></div>",
- "div p",
- {LeafNodeOption::IgnoreNonEditableNode},
- "div > p + p + p"},
- GetLeafNodeTest{
- u"<div><b>abc</b><s><!-- comment --></s><i>def</i></div>",
- "div > b",
- {},
- "div > s"},
- GetLeafNodeTest{
- u"<div><b>abc</b><s><!-- comment --></s><i>def</i></div>",
- "div > b",
- {LeafNodeOption::IgnoreInvisibleEmptyInlineContainers},
- "div > i"},
- GetLeafNodeTest{
- u"<div><b>abc</b><s><!-- comment --></s><i>def</i></div>",
- "div > b",
- {LeafNodeOption::IgnoreInvisibleEmptyInlineContainers,
- LeafNodeOption::TreatCommentAsLeafNode},
- "div > s"},
- }) {
- body->SetInnerHTMLTrusted(nsDependentString(testData.mInnerHTML),
- doc->NodePrincipal(), IgnoreErrors());
- const Element* const target = body->QuerySelector(
- nsDependentCString(testData.mContentSelector), IgnoreErrors());
- MOZ_RELEASE_ASSERT(target);
- const nsIContent* result = HTMLEditUtils::GetNextSibling(
- *target, testData.mOptions,
- BlockInlineCheck::UseComputedDisplayOutsideStyle);
- EXPECT_EQ(result, testData.GetExpectedTarget(*body->GetParentNode()))
- << "GetNextSibling: " << testData
- << "(Got: " << ToString(RefPtr{result}) << ")";
- }
-}
-
-TEST(HTMLEditUtilsTest, GetFirstChild)
-{
- using LeafNodeOption = HTMLEditUtils::LeafNodeOption;
- const RefPtr<Document> doc = CreateHTMLDoc();
- const RefPtr<nsGenericHTMLElement> body = doc->GetBody();
- MOZ_RELEASE_ASSERT(body);
- for (const auto& testData : {
- GetLeafNodeTest{u"<div></div>", "div", {}, nullptr},
- GetLeafNodeTest{u"<div><p><br></p></div>", "div", {}, "div > p"},
- GetLeafNodeTest{
- u"<div><!-- comment --><p><br></p></div>", "div", {}, "div > p"},
- GetLeafNodeTest{u"<div><!-- comment --><p><br></p></div>",
- "div",
- {LeafNodeOption::TreatCommentAsLeafNode},
- nullptr,
- "div",
- 0u},
- GetLeafNodeTest{
- u"<div> <p><br></p></div>", "div", {}, nullptr, "div", 0u},
- GetLeafNodeTest{u"<div> <p><br></p></div>",
- "div",
- {LeafNodeOption::IgnoreInvisibleText},
- "div > p"},
- GetLeafNodeTest{
- u"<div contenteditable><p "
- u"contenteditable=\"false\"><br></p><p><br></p></div>",
- "div",
- {},
- "div > p"},
- GetLeafNodeTest{
- u"<div contenteditable><p "
- u"contenteditable=\"false\"><br></p><p><br></p></div>",
- "div",
- {LeafNodeOption::IgnoreNonEditableNode},
- "div > p + p"},
- GetLeafNodeTest{u"<div><s><!-- comment --></s><i>def</i></div>",
- "div",
- {},
- "div > s"},
- GetLeafNodeTest{
- u"<div><s><!-- comment --></s><i>def</i></div>",
- "div",
- {LeafNodeOption::IgnoreInvisibleEmptyInlineContainers},
- "div > i"},
- GetLeafNodeTest{
- u"<div><s><!-- comment --></s><i>def</i></div>",
- "div",
- {LeafNodeOption::IgnoreInvisibleEmptyInlineContainers,
- LeafNodeOption::TreatCommentAsLeafNode},
- "div > s"},
- }) {
- body->SetInnerHTMLTrusted(nsDependentString(testData.mInnerHTML),
- doc->NodePrincipal(), IgnoreErrors());
- const Element* const target = body->QuerySelector(
- nsDependentCString(testData.mContentSelector), IgnoreErrors());
- MOZ_RELEASE_ASSERT(target);
- const nsIContent* result = HTMLEditUtils::GetFirstChild(
- *target, testData.mOptions,
- BlockInlineCheck::UseComputedDisplayOutsideStyle);
- EXPECT_EQ(result, testData.GetExpectedTarget(*body->GetParentNode()))
- << "GetFirstChild: " << testData << "(Got: " << ToString(RefPtr{result})
- << ")";
- }
-}
-
-TEST(HTMLEditUtilsTest, GetLastChild)
-{
- using LeafNodeOption = HTMLEditUtils::LeafNodeOption;
- const RefPtr<Document> doc = CreateHTMLDoc();
- const RefPtr<nsGenericHTMLElement> body = doc->GetBody();
- MOZ_RELEASE_ASSERT(body);
- for (const auto& testData : {
- GetLeafNodeTest{u"<div></div>", "div", {}, nullptr},
- GetLeafNodeTest{u"<div><p><br></p></div>", "div", {}, "div > p"},
- GetLeafNodeTest{
- u"<div><p><br></p><!-- comment --></div>", "div", {}, "div > p"},
- GetLeafNodeTest{u"<div><p><br></p><!-- comment --></div>",
- "div",
- {LeafNodeOption::TreatCommentAsLeafNode},
- nullptr,
- "div",
- 1u},
- GetLeafNodeTest{
- u"<div><p><br></p> </div>", "div", {}, nullptr, "div", 1u},
- GetLeafNodeTest{u"<div><p><br></p> </div>",
- "div",
- {LeafNodeOption::IgnoreInvisibleText},
- "div > p"},
- GetLeafNodeTest{u"<div contenteditable><p><br></p><p "
- u"contenteditable=\"false\"><br></p></div>",
- "div",
- {},
- "div > p + p"},
- GetLeafNodeTest{u"<div contenteditable><p><br></p><p "
- u"contenteditable=\"false\"><br></p></div>",
- "div",
- {LeafNodeOption::IgnoreNonEditableNode},
- "div > p"},
- GetLeafNodeTest{u"<div><i>def</i><s><!-- comment --></s></div>",
- "div",
- {},
- "div > s"},
- GetLeafNodeTest{
- u"<div><i>def</i><s><!-- comment --></s></div>",
- "div",
- {LeafNodeOption::IgnoreInvisibleEmptyInlineContainers},
- "div > i"},
- GetLeafNodeTest{
- u"<div><i>def</i><s><!-- comment --></s></div>",
- "div",
- {LeafNodeOption::IgnoreInvisibleEmptyInlineContainers,
- LeafNodeOption::TreatCommentAsLeafNode},
- "div > s"},
- }) {
- body->SetInnerHTMLTrusted(nsDependentString(testData.mInnerHTML),
- doc->NodePrincipal(), IgnoreErrors());
- const Element* const target = body->QuerySelector(
- nsDependentCString(testData.mContentSelector), IgnoreErrors());
- MOZ_RELEASE_ASSERT(target);
- const nsIContent* result = HTMLEditUtils::GetLastChild(
- *target, testData.mOptions,
- BlockInlineCheck::UseComputedDisplayOutsideStyle);
- EXPECT_EQ(result, testData.GetExpectedTarget(*body->GetParentNode()))
- << "GetLastChild: " << testData << "(Got: " << ToString(RefPtr{result})
- << ")";
- }
-}
-
-TEST(HTMLEditUtilsTest, GetInclusiveDeepestFirstChildWhichHasOneChild)
-{
- using LeafNodeOption = HTMLEditUtils::LeafNodeOption;
- const RefPtr<Document> doc = CreateHTMLDoc();
- const RefPtr<nsGenericHTMLElement> body = doc->GetBody();
- MOZ_RELEASE_ASSERT(body);
- for (const auto& testData : {
- GetLeafNodeTest{u"<div></div>", "div", {}, "div"},
- GetLeafNodeTest{u"<div><br></div>", "div", {}, "div"},
- GetLeafNodeTest{
- u"<div><div><br></div></div>", "div", {}, "div > div"},
- GetLeafNodeTest{
- u"<div><!-- comment --><br></div>", "div", {}, "div"},
- GetLeafNodeTest{u"<div><!-- comment --><br></div>",
- "div",
- {LeafNodeOption::TreatCommentAsLeafNode},
- "div"},
- GetLeafNodeTest{u"<div><div><!-- comment --><br></div></div>",
- "div",
- {},
- "div > div"},
- GetLeafNodeTest{u"<div><div><!-- comment --><br></div></div>",
- "div",
- {LeafNodeOption::TreatCommentAsLeafNode},
- "div > div"},
- GetLeafNodeTest{u"<div><!-- comment --><div><br></div></div>",
- "div",
- {},
- "div > div"},
- GetLeafNodeTest{u"<div><!-- comment --><div><br></div></div>",
- "div",
- {LeafNodeOption::TreatCommentAsLeafNode},
- "div"},
- GetLeafNodeTest{u"<div> <br></div>", "div", {}, "div"},
- GetLeafNodeTest{u"<div> <br></div>",
- "div",
- {LeafNodeOption::IgnoreInvisibleText},
- "div"},
- GetLeafNodeTest{
- u"<div><div> <br></div></div>", "div", {}, "div > div"},
- GetLeafNodeTest{u"<div><div> <br></div></div>",
- "div",
- {LeafNodeOption::IgnoreInvisibleText},
- "div > div"},
- GetLeafNodeTest{u"<div> <div><br></div></div>", "div", {}, "div"},
- GetLeafNodeTest{u"<div> <div><br></div></div>",
- "div",
- {LeafNodeOption::IgnoreInvisibleText},
- "div > div"},
- }) {
- body->SetInnerHTMLTrusted(nsDependentString(testData.mInnerHTML),
- doc->NodePrincipal(), IgnoreErrors());
- const Element* const target = body->QuerySelector(
- nsDependentCString(testData.mContentSelector), IgnoreErrors());
- MOZ_RELEASE_ASSERT(target);
- const nsIContent* result =
- HTMLEditUtils::GetInclusiveDeepestFirstChildWhichHasOneChild(
- *target, testData.mOptions,
- BlockInlineCheck::UseComputedDisplayOutsideStyle, nsGkAtoms::div,
- nsGkAtoms::blockquote, nsGkAtoms::ul, nsGkAtoms::ol, nsGkAtoms::dl);
- EXPECT_EQ(result, testData.GetExpectedTarget(*body->GetParentNode()))
- << "GetInclusiveDeepestFirstChildWhichHasOneChild: " << testData
- << "(Got: " << ToString(RefPtr{result}) << ")";
- }
-}
-
struct MOZ_STACK_CLASS LineBreakBeforeBlockBoundaryTest final {
const char16_t* const mInnerHTML;
const char* const mContainer;
@@ -2302,7 +1738,11 @@ TEST(HTMLEditUtilsTest, GetLineBreakBeforeBlockBoundaryIfPointIsBetweenThem)
u"<div contenteditable><br> </div>", "div", Some(1), 2, true},
LineBreakBeforeBlockBoundaryTest{
u"<div contenteditable><br><!-- X --></div>", "div", Nothing{},
- 2, true},
+ 2,
+ // FIXME: Currently, this fails with a bug of WSRunScanner
+ // (actually, a bug of
+ // HTMLEditUtils::GetPreviousLeafContentOrPreviousBlockElement)
+ false},
LineBreakBeforeBlockBoundaryTest{
u"<div contenteditable><br><br></div>", "div", Nothing{}, 1,
false},
diff --git a/testing/web-platform/meta/editing/run/delete.html.ini b/testing/web-platform/meta/editing/run/delete.html.ini
@@ -571,6 +571,9 @@
[[["delete",""\]\] "<p>abc</p><ul><li contenteditable=\\"false\\">def</li></ul><p>[\]ghi</p>" compare innerHTML]
expected: FAIL
+ [[["delete",""\]\] "<p>abc</p><ul><li contenteditable=\\"false\\">def</li><li>[\]ghi</li></ul>": execCommand("delete", false, "") return value]
+ expected: FAIL
+
[[["delete",""\]\] "<p>abc</p><ul><li contenteditable=\\"false\\">def</li><li>[\]ghi</li></ul>" compare innerHTML]
expected: FAIL