commit 58b4243d101d51384ac4713b5c9916328af95733
parent dd46967eb4b7cb89e09a713c664548c7123b3fb4
Author: Masayuki Nakano <masayuki@d-toybox.com>
Date: Wed, 7 Jan 2026 01:10:21 +0000
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
`HTMLEditUtils::GetNextContent()` and
`HTMLEditUtils::GetPreviousContent()` are wrappers of
`HTMLEditUtils::GetAdjacentContent()` and
`HTMLEditUtils::GetAdjacentLeafContent()`. The behavior is similar to
`HTMLEditUtils::GetNextLeafContentOrNextBlockElement()` or
`HTMLEditUtils::GetPreviousLeafContentOrPreviousBlockElement()` but
they won't stop at block inclusive ancestor or a block sibling of an
inclusive ancestor. Therefore, we can get rid of them and use the new
and better implementation.
Differential Revision: https://phabricator.services.mozilla.com/D277522
Diffstat:
9 files changed, 371 insertions(+), 478 deletions(-)
diff --git a/editor/libeditor/EditorBase.cpp b/editor/libeditor/EditorBase.cpp
@@ -144,6 +144,7 @@ using namespace dom;
using namespace widget;
using EmptyCheckOption = HTMLEditUtils::EmptyCheckOption;
+using LeafNodeOption = HTMLEditUtils::LeafNodeOption;
using WalkTreeOption = HTMLEditUtils::WalkTreeOption;
static LazyLogModule gEventLog("EditorEvent");
@@ -4417,14 +4418,19 @@ 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::GetPreviousContent(
- *point.GetContainer(), {WalkTreeOption::IgnoreNonEditableNode},
+ nsIContent* previousEditableContent = HTMLEditUtils::GetPreviousLeafContent(
+ *point.ContainerAs<nsIContent>(),
+ {LeafNodeOption::IgnoreNonEditableNode},
IsTextEditor() ? BlockInlineCheck::UseHTMLDefaultStyle
: BlockInlineCheck::UseComputedDisplayOutsideStyle,
anonymousDivOrEditingHost);
- if (!previousEditableContent) {
+ if (MOZ_UNLIKELY(!previousEditableContent)) {
NS_WARNING("There was no editable content before the collapsed range");
return nullptr;
}
@@ -4467,14 +4473,19 @@ 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::GetNextContent(
- *point.GetContainer(), {WalkTreeOption::IgnoreNonEditableNode},
+ nsIContent* nextEditableContent = HTMLEditUtils::GetNextLeafContent(
+ *point.ContainerAs<nsIContent>(),
+ {LeafNodeOption::IgnoreNonEditableNode},
IsTextEditor() ? BlockInlineCheck::UseHTMLDefaultStyle
: BlockInlineCheck::UseComputedDisplayOutsideStyle,
anonymousDivOrEditingHost);
- if (!nextEditableContent) {
+ if (MOZ_UNLIKELY(!nextEditableContent)) {
NS_WARNING("There was no editable content after the collapsed range");
return nullptr;
}
@@ -4538,12 +4549,12 @@ EditorBase::CreateTransactionForCollapsedRange(
if (IsHTMLEditor()) {
editableContent =
aHowToHandleCollapsedRange == HowToHandleCollapsedRange::ExtendBackward
- ? HTMLEditUtils::GetPreviousContent(
- point, {WalkTreeOption::IgnoreNonEditableNode},
+ ? HTMLEditUtils::GetPreviousLeafContent(
+ point, {LeafNodeOption::IgnoreNonEditableNode},
BlockInlineCheck::UseComputedDisplayOutsideStyle,
anonymousDivOrEditingHost)
- : HTMLEditUtils::GetNextContent(
- point, {WalkTreeOption::IgnoreNonEditableNode},
+ : HTMLEditUtils::GetNextLeafContent(
+ point, {LeafNodeOption::IgnoreNonEditableNode},
BlockInlineCheck::UseComputedDisplayOutsideStyle,
anonymousDivOrEditingHost);
if (!editableContent) {
@@ -4556,12 +4567,12 @@ EditorBase::CreateTransactionForCollapsedRange(
editableContent =
aHowToHandleCollapsedRange ==
HowToHandleCollapsedRange::ExtendBackward
- ? HTMLEditUtils::GetPreviousContent(
- *editableContent, {WalkTreeOption::IgnoreNonEditableNode},
+ ? HTMLEditUtils::GetPreviousLeafContent(
+ *editableContent, {LeafNodeOption::IgnoreNonEditableNode},
BlockInlineCheck::UseComputedDisplayOutsideStyle,
anonymousDivOrEditingHost)
- : HTMLEditUtils::GetNextContent(
- *editableContent, {WalkTreeOption::IgnoreNonEditableNode},
+ : HTMLEditUtils::GetNextLeafContent(
+ *editableContent, {LeafNodeOption::IgnoreNonEditableNode},
BlockInlineCheck::UseComputedDisplayOutsideStyle,
anonymousDivOrEditingHost);
}
diff --git a/editor/libeditor/HTMLEditSubActionHandler.cpp b/editor/libeditor/HTMLEditSubActionHandler.cpp
@@ -787,7 +787,7 @@ nsresult HTMLEditor::EnsureCaretNotAfterInvisibleBRElement(
return NS_OK;
}
- nsIContent* previousBRElement = HTMLEditUtils::GetPreviousContent(
+ nsIContent* previousBRElement = HTMLEditUtils::GetPreviousLeafContent(
atSelectionStart, {}, BlockInlineCheck::UseComputedDisplayStyle,
&aEditingHost);
if (!previousBRElement || !previousBRElement->IsHTMLElement(nsGkAtoms::br) ||
@@ -4185,8 +4185,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::GetNextContent(
- pointToInsertBlock, {WalkTreeOption::IgnoreNonEditableNode},
+ if (nsCOMPtr<nsIContent> brContent = HTMLEditUtils::GetNextLeafContent(
+ pointToInsertBlock, {LeafNodeOption::IgnoreNonEditableNode},
BlockInlineCheck::UseComputedDisplayOutsideStyle,
&aEditingHost)) {
if (brContent && brContent->IsHTMLElement(nsGkAtoms::br)) {
@@ -7243,8 +7243,8 @@ HTMLEditor::GetRangeExtendedToHardLineEdgesForBlockEditAction(
prevVisibleThingOfEndPoint
.ReachedInlineEditingHostBoundary()) {
// endpoint is just after start of this block
- if (nsIContent* child = HTMLEditUtils::GetPreviousContent(
- endPoint, {WalkTreeOption::IgnoreNonEditableNode},
+ if (nsIContent* const child = HTMLEditUtils::GetPreviousLeafContent(
+ endPoint, {LeafNodeOption::IgnoreNonEditableNode},
BlockInlineCheck::UseHTMLDefaultStyle, &aEditingHost)) {
newRange.SetEnd(EditorRawDOMPoint::After(*child));
}
@@ -7284,8 +7284,8 @@ HTMLEditor::GetRangeExtendedToHardLineEdgesForBlockEditAction(
nextVisibleThingOfStartPoint
.ReachedInlineEditingHostBoundary()) {
// startpoint is just before end of this block
- if (nsIContent* child = HTMLEditUtils::GetNextContent(
- startPoint, {WalkTreeOption::IgnoreNonEditableNode},
+ if (nsIContent* const child = HTMLEditUtils::GetNextLeafContent(
+ startPoint, {LeafNodeOption::IgnoreNonEditableNode},
BlockInlineCheck::UseHTMLDefaultStyle, &aEditingHost)) {
newRange.SetStart(EditorRawDOMPoint(child));
}
@@ -8950,8 +8950,8 @@ nsresult HTMLEditor::AdjustCaretPositionAndEnsurePaddingBRElement(
}
if (nsCOMPtr<nsIContent> previousEditableContent =
- HTMLEditUtils::GetPreviousContent(
- point, {WalkTreeOption::IgnoreNonEditableNode},
+ HTMLEditUtils::GetPreviousLeafContent(
+ point, {LeafNodeOption::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>
diff --git a/editor/libeditor/HTMLEditUtils.cpp b/editor/libeditor/HTMLEditUtils.cpp
@@ -57,60 +57,35 @@ namespace mozilla {
using namespace dom;
using EditorType = EditorBase::EditorType;
-template nsIContent* HTMLEditUtils::GetNextLeafContentOrNextBlockElement(
- const EditorDOMPoint&, const LeafNodeOptions&, BlockInlineCheck,
- const Element*);
-template nsIContent* HTMLEditUtils::GetNextLeafContentOrNextBlockElement(
- const EditorRawDOMPoint&, const LeafNodeOptions&, BlockInlineCheck,
- const Element*);
-template nsIContent* HTMLEditUtils::GetNextLeafContentOrNextBlockElement(
- const EditorDOMPointInText&, const LeafNodeOptions&, BlockInlineCheck,
- const Element*);
-template nsIContent* HTMLEditUtils::GetNextLeafContentOrNextBlockElement(
- const EditorRawDOMPointInText&, const LeafNodeOptions&, BlockInlineCheck,
- const Element*);
+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::GetPreviousLeafContentOrPreviousBlockElement(
- const EditorDOMPoint&, const LeafNodeOptions&, BlockInlineCheck,
- const Element*);
+HTMLEditUtils::GetPreviousLeafContentOrPreviousBlockElementImpl(
+ const EditorDOMPoint&, StopAtBlockSibling, const LeafNodeOptions&,
+ BlockInlineCheck, const Element*);
template nsIContent*
-HTMLEditUtils::GetPreviousLeafContentOrPreviousBlockElement(
- const EditorRawDOMPoint&, const LeafNodeOptions&, BlockInlineCheck,
- const Element*);
+HTMLEditUtils::GetPreviousLeafContentOrPreviousBlockElementImpl(
+ const EditorRawDOMPoint&, StopAtBlockSibling, const LeafNodeOptions&,
+ BlockInlineCheck, const Element*);
template nsIContent*
-HTMLEditUtils::GetPreviousLeafContentOrPreviousBlockElement(
- const EditorDOMPointInText&, const LeafNodeOptions&, BlockInlineCheck,
- const Element*);
+HTMLEditUtils::GetPreviousLeafContentOrPreviousBlockElementImpl(
+ const EditorDOMPointInText&, StopAtBlockSibling, const LeafNodeOptions&,
+ BlockInlineCheck, const Element*);
template nsIContent*
-HTMLEditUtils::GetPreviousLeafContentOrPreviousBlockElement(
- const EditorRawDOMPointInText&, 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);
+HTMLEditUtils::GetPreviousLeafContentOrPreviousBlockElementImpl(
+ const EditorRawDOMPointInText&, StopAtBlockSibling, const LeafNodeOptions&,
+ BlockInlineCheck, const Element*);
template EditorDOMPoint HTMLEditUtils::GetPreviousEditablePoint(
nsIContent& aContent, const Element* aAncestorLimiter,
@@ -1325,9 +1300,9 @@ nsIContent* HTMLEditUtils::GetFirstLeafContent(
}
// static
-nsIContent* HTMLEditUtils::GetNextLeafContentOrNextBlockElement(
- const nsIContent& aStartContent, const LeafNodeOptions& aOptions,
- BlockInlineCheck aBlockInlineCheck,
+nsIContent* HTMLEditUtils::GetNextLeafContentOrNextBlockElementImpl(
+ const nsIContent& aStartContent, StopAtBlockSibling aStopAtBlockSibling,
+ const LeafNodeOptions& aOptions, BlockInlineCheck aBlockInlineCheck,
const Element* aAncestorLimiter /* = nullptr */) {
MOZ_ASSERT_IF(
aOptions.contains(LeafNodeOption::IgnoreNonEditableNode),
@@ -1347,9 +1322,10 @@ nsIContent* HTMLEditUtils::GetNextLeafContentOrNextBlockElement(
for (Element* const parentElement :
container->InclusiveAncestorsOfType<Element>()) {
if (parentElement == aAncestorLimiter ||
- HTMLEditUtils::IsBlockElement(
- *parentElement,
- UseComputedDisplayStyleIfAuto(aBlockInlineCheck))) {
+ (static_cast<bool>(aStopAtBlockSibling) &&
+ HTMLEditUtils::IsBlockElement(
+ *parentElement,
+ UseComputedDisplayStyleIfAuto(aBlockInlineCheck)))) {
return nullptr;
}
if (aOptions.contains(LeafNodeOption::TreatNonEditableNodeAsLeafNode) &&
@@ -1370,7 +1346,8 @@ nsIContent* HTMLEditUtils::GetNextLeafContentOrNextBlockElement(
}
// We have a next content. If it's a block, return it.
- if (HTMLEditUtils::IsBlockElement(
+ if (static_cast<bool>(aStopAtBlockSibling) &&
+ HTMLEditUtils::IsBlockElement(
*nextContent,
PreferDisplayOutsideIfUsingDisplay(
UseComputedDisplayStyleIfAuto(aBlockInlineCheck)))) {
@@ -1409,9 +1386,10 @@ nsIContent* HTMLEditUtils::GetNextLeafContentOrNextBlockElement(
// static
template <typename PT, typename CT>
-nsIContent* HTMLEditUtils::GetNextLeafContentOrNextBlockElement(
+nsIContent* HTMLEditUtils::GetNextLeafContentOrNextBlockElementImpl(
const EditorDOMPointBase<PT, CT>& aStartPoint,
- const LeafNodeOptions& aOptions, BlockInlineCheck aBlockInlineCheck,
+ StopAtBlockSibling aStopAtBlockSibling, const LeafNodeOptions& aOptions,
+ BlockInlineCheck aBlockInlineCheck,
const Element* aAncestorLimiter /* = nullptr */) {
MOZ_ASSERT(aStartPoint.IsSet());
MOZ_ASSERT_IF(
@@ -1422,38 +1400,40 @@ nsIContent* HTMLEditUtils::GetNextLeafContentOrNextBlockElement(
return nullptr;
}
if (!aStartPoint.GetContainer()->IsElement()) {
- return HTMLEditUtils::GetNextLeafContentOrNextBlockElement(
- *aStartPoint.template ContainerAs<nsIContent>(), aOptions,
- aBlockInlineCheck, aAncestorLimiter);
+ 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::GetNextLeafContentOrNextBlockElement(
- *aStartPoint.template ContainerAs<nsIContent>(), aOptions,
- aBlockInlineCheck, aAncestorLimiter);
+ return HTMLEditUtils::GetNextLeafContentOrNextBlockElementImpl(
+ *aStartPoint.template ContainerAs<nsIContent>(), aStopAtBlockSibling,
+ aOptions, aBlockInlineCheck, aAncestorLimiter);
}
for (nsIContent* nextContent = aStartPoint.GetChild();;) {
if (!nextContent) {
if (aStartPoint.GetContainer() == aAncestorLimiter ||
- HTMLEditUtils::IsBlockElement(
- *aStartPoint.template ContainerAs<Element>(),
- UseComputedDisplayStyleIfAuto(aBlockInlineCheck))) {
+ (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::GetNextLeafContentOrNextBlockElement(
- *aStartPoint.template ContainerAs<Element>(), aOptions,
- PreferDisplayOutsideIfUsingDisplay(aBlockInlineCheck),
+ 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 (HTMLEditUtils::IsBlockElement(
+ if (static_cast<bool>(aStopAtBlockSibling) &&
+ HTMLEditUtils::IsBlockElement(
*nextContent,
UseComputedDisplayOutsideStyleIfAuto(aBlockInlineCheck))) {
return nextContent;
@@ -1488,9 +1468,9 @@ nsIContent* HTMLEditUtils::GetNextLeafContentOrNextBlockElement(
}
// static
-nsIContent* HTMLEditUtils::GetPreviousLeafContentOrPreviousBlockElement(
- const nsIContent& aStartContent, const LeafNodeOptions& aOptions,
- BlockInlineCheck aBlockInlineCheck,
+nsIContent* HTMLEditUtils::GetPreviousLeafContentOrPreviousBlockElementImpl(
+ const nsIContent& aStartContent, StopAtBlockSibling aStopAtBlockSibling,
+ const LeafNodeOptions& aOptions, BlockInlineCheck aBlockInlineCheck,
const Element* aAncestorLimiter /* = nullptr */) {
MOZ_ASSERT_IF(
aOptions.contains(LeafNodeOption::IgnoreNonEditableNode),
@@ -1510,9 +1490,10 @@ nsIContent* HTMLEditUtils::GetPreviousLeafContentOrPreviousBlockElement(
for (Element* parentElement :
container->InclusiveAncestorsOfType<Element>()) {
if (parentElement == aAncestorLimiter ||
- HTMLEditUtils::IsBlockElement(
- *parentElement,
- UseComputedDisplayStyleIfAuto(aBlockInlineCheck))) {
+ (static_cast<bool>(aStopAtBlockSibling) &&
+ HTMLEditUtils::IsBlockElement(
+ *parentElement,
+ UseComputedDisplayStyleIfAuto(aBlockInlineCheck)))) {
return nullptr;
}
if (aOptions.contains(LeafNodeOption::TreatNonEditableNodeAsLeafNode) &&
@@ -1532,7 +1513,8 @@ nsIContent* HTMLEditUtils::GetPreviousLeafContentOrPreviousBlockElement(
MOZ_ASSERT(previousContent);
}
// We have a next content. If it's a block, return it.
- if (HTMLEditUtils::IsBlockElement(
+ if (static_cast<bool>(aStopAtBlockSibling) &&
+ HTMLEditUtils::IsBlockElement(
*previousContent,
PreferDisplayOutsideIfUsingDisplay(
UseComputedDisplayOutsideStyleIfAuto(aBlockInlineCheck)))) {
@@ -1573,9 +1555,10 @@ nsIContent* HTMLEditUtils::GetPreviousLeafContentOrPreviousBlockElement(
// static
template <typename PT, typename CT>
-nsIContent* HTMLEditUtils::GetPreviousLeafContentOrPreviousBlockElement(
+nsIContent* HTMLEditUtils::GetPreviousLeafContentOrPreviousBlockElementImpl(
const EditorDOMPointBase<PT, CT>& aStartPoint,
- const LeafNodeOptions& aOptions, BlockInlineCheck aBlockInlineCheck,
+ StopAtBlockSibling aStopAtBlockSibling, const LeafNodeOptions& aOptions,
+ BlockInlineCheck aBlockInlineCheck,
const Element* aAncestorLimiter /* = nullptr */) {
MOZ_ASSERT(aStartPoint.IsSet());
MOZ_ASSERT_IF(
@@ -1586,32 +1569,33 @@ nsIContent* HTMLEditUtils::GetPreviousLeafContentOrPreviousBlockElement(
return nullptr;
}
if (!aStartPoint.GetContainer()->IsElement()) {
- return HTMLEditUtils::GetPreviousLeafContentOrPreviousBlockElement(
- *aStartPoint.template ContainerAs<nsIContent>(), aOptions,
- aBlockInlineCheck, aAncestorLimiter);
+ 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::GetPreviousLeafContentOrPreviousBlockElement(
- *aStartPoint.template ContainerAs<Element>(), aOptions,
- aBlockInlineCheck, aAncestorLimiter);
+ return HTMLEditUtils::GetPreviousLeafContentOrPreviousBlockElementImpl(
+ *aStartPoint.template ContainerAs<Element>(), aStopAtBlockSibling,
+ aOptions, aBlockInlineCheck, aAncestorLimiter);
}
if (aStartPoint.IsStartOfContainer()) {
if (aStartPoint.GetContainer() == aAncestorLimiter ||
- HTMLEditUtils::IsBlockElement(
- *aStartPoint.template ContainerAs<Element>(),
- UseComputedDisplayStyleIfAuto(aBlockInlineCheck))) {
+ (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::GetPreviousLeafContentOrPreviousBlockElement(
- *aStartPoint.template ContainerAs<Element>(), aOptions,
- PreferDisplayOutsideIfUsingDisplay(aBlockInlineCheck),
+ return HTMLEditUtils::GetPreviousLeafContentOrPreviousBlockElementImpl(
+ *aStartPoint.template ContainerAs<Element>(), aStopAtBlockSibling,
+ aOptions, PreferDisplayOutsideIfUsingDisplay(aBlockInlineCheck),
aAncestorLimiter);
}
@@ -1619,7 +1603,8 @@ nsIContent* HTMLEditUtils::GetPreviousLeafContentOrPreviousBlockElement(
previousContent;
previousContent = previousContent->GetPreviousSibling()) {
// We have a prior node. If it's a block, return it.
- if (HTMLEditUtils::IsBlockElement(
+ if (static_cast<bool>(aStopAtBlockSibling) &&
+ HTMLEditUtils::IsBlockElement(
*previousContent,
UseComputedDisplayOutsideStyleIfAuto(aBlockInlineCheck))) {
return previousContent;
@@ -2547,222 +2532,6 @@ 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)
- ? LeafNodeOptions{LeafNodeOption::TreatChildBlockAsLeafNode}
- : LeafNodeOptions{},
- 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)
- ? LeafNodeOptions{LeafNodeOption::TreatChildBlockAsLeafNode}
- : LeafNodeOptions{},
- 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;
- }
- LeafNodeOptions leafNodeOptions;
- if (aOptions.contains(WalkTreeOption::StopAtBlockBoundary)) {
- leafNodeOptions += LeafNodeOption::TreatChildBlockAsLeafNode;
- }
- if (aOptions.contains(WalkTreeOption::IgnoreNonEditableNode)) {
- leafNodeOptions += LeafNodeOption::IgnoreNonEditableNode;
- }
- nsIContent* leafContent =
- aWalkTreeDirection == WalkTreeDirection::Forward
- ? HTMLEditUtils::GetFirstLeafContent(*sibling, leafNodeOptions,
- aBlockInlineCheck)
- : HTMLEditUtils::GetLastLeafContent(*sibling, leafNodeOptions,
- 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,
diff --git a/editor/libeditor/HTMLEditUtils.h b/editor/libeditor/HTMLEditUtils.h
@@ -1129,19 +1129,6 @@ class HTMLEditUtils final {
aFoundLinkElement);
}
- /**
- * 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.
@@ -1149,71 +1136,6 @@ class HTMLEditUtils final {
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.
@@ -1367,15 +1289,15 @@ class HTMLEditUtils final {
nsIContent* editableContent = nullptr;
if (aWalkTreeDirection == WalkTreeDirection::Backward) {
- editableContent = HTMLEditUtils::GetPreviousContent(
- aPoint, {WalkTreeOption::IgnoreNonEditableNode},
+ editableContent = HTMLEditUtils::GetPreviousLeafContent(
+ aPoint, {LeafNodeOption::IgnoreNonEditableNode},
BlockInlineCheck::Auto, &aEditingHost);
if (!editableContent) {
return nullptr; // Not illegal.
}
} else {
- editableContent = HTMLEditUtils::GetNextContent(
- aPoint, {WalkTreeOption::IgnoreNonEditableNode},
+ editableContent = HTMLEditUtils::GetNextLeafContent(
+ aPoint, {LeafNodeOption::IgnoreNonEditableNode},
BlockInlineCheck::Auto, &aEditingHost);
if (NS_WARN_IF(!editableContent)) {
// Perhaps, illegal because the node pointed by aPoint isn't editable
@@ -1392,15 +1314,15 @@ class HTMLEditUtils final {
!editableContent->IsHTMLElement(nsGkAtoms::br) &&
!HTMLEditUtils::IsImageElement(*editableContent)) {
if (aWalkTreeDirection == WalkTreeDirection::Backward) {
- editableContent = HTMLEditUtils::GetPreviousContent(
- *editableContent, {WalkTreeOption::IgnoreNonEditableNode},
+ editableContent = HTMLEditUtils::GetPreviousLeafContent(
+ *editableContent, {LeafNodeOption::IgnoreNonEditableNode},
BlockInlineCheck::Auto, &aEditingHost);
if (NS_WARN_IF(!editableContent)) {
return nullptr;
}
} else {
- editableContent = HTMLEditUtils::GetNextContent(
- *editableContent, {WalkTreeOption::IgnoreNonEditableNode},
+ editableContent = HTMLEditUtils::GetNextLeafContent(
+ *editableContent, {LeafNodeOption::IgnoreNonEditableNode},
BlockInlineCheck::Auto, &aEditingHost);
if (NS_WARN_IF(!editableContent)) {
return nullptr;
@@ -1489,6 +1411,101 @@ class HTMLEditUtils final {
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.
+ */
+ template <typename PT, typename CT>
+ static nsIContent* GetNextLeafContent(
+ const EditorDOMPointBase<PT, CT>& aStartPoint,
+ const LeafNodeOptions& aOptions, BlockInlineCheck aBlockInlineCheck,
+ const Element* aAncestorLimiter = nullptr) {
+ return GetNextLeafContentOrNextBlockElementImpl(
+ aStartPoint, StopAtBlockSibling::No, aOptions, aBlockInlineCheck,
+ aAncestorLimiter);
+ }
+
+ /**
+ * 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.
+ *
+ * @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.
+ */
+ template <typename PT, typename CT>
+ static nsIContent* GetPreviousLeafContent(
+ const EditorDOMPointBase<PT, CT>& aStartPoint,
+ const LeafNodeOptions& aOptions, BlockInlineCheck aBlockInlineCheck,
+ const Element* aAncestorLimiter = nullptr) {
+ return GetPreviousLeafContentOrPreviousBlockElementImpl(
+ aStartPoint, StopAtBlockSibling::No, aOptions, aBlockInlineCheck,
+ aAncestorLimiter);
+ }
+
/**
* GetNextLeafContentOrNextBlockElement() returns next leaf content or
* next block element of aStartContent inside aAncestorLimiter.
@@ -1501,7 +1518,11 @@ class HTMLEditUtils final {
static nsIContent* GetNextLeafContentOrNextBlockElement(
const nsIContent& aStartContent, const LeafNodeOptions& aOptions,
BlockInlineCheck aBlockInlineCheck,
- const Element* aAncestorLimiter = nullptr);
+ const Element* aAncestorLimiter = nullptr) {
+ return GetNextLeafContentOrNextBlockElementImpl(
+ aStartContent, StopAtBlockSibling::Yes, aOptions, aBlockInlineCheck,
+ aAncestorLimiter);
+ }
/**
* Similar to the above method, but take a DOM point to specify scan start
@@ -1511,7 +1532,11 @@ class HTMLEditUtils final {
static nsIContent* GetNextLeafContentOrNextBlockElement(
const EditorDOMPointBase<PT, CT>& aStartPoint,
const LeafNodeOptions& aOptions, BlockInlineCheck aBlockInlineCheck,
- const Element* aAncestorLimiter = nullptr);
+ const Element* aAncestorLimiter = nullptr) {
+ return GetNextLeafContentOrNextBlockElementImpl(
+ aStartPoint, StopAtBlockSibling::Yes, aOptions, aBlockInlineCheck,
+ aAncestorLimiter);
+ }
/**
* GetPreviousLeafContentOrPreviousBlockElement() returns previous leaf
@@ -1526,7 +1551,11 @@ class HTMLEditUtils final {
static nsIContent* GetPreviousLeafContentOrPreviousBlockElement(
const nsIContent& aStartContent, const LeafNodeOptions& aOptions,
BlockInlineCheck aBlockInlineCheck,
- const Element* aAncestorLimiter = nullptr);
+ const Element* aAncestorLimiter = nullptr) {
+ return GetPreviousLeafContentOrPreviousBlockElementImpl(
+ aStartContent, StopAtBlockSibling::Yes, aOptions, aBlockInlineCheck,
+ aAncestorLimiter);
+ }
/**
* Similar to the above method, but take a DOM point to specify scan start
@@ -1536,7 +1565,11 @@ class HTMLEditUtils final {
static nsIContent* GetPreviousLeafContentOrPreviousBlockElement(
const EditorDOMPointBase<PT, CT>& aStartPoint,
const LeafNodeOptions& aOptions, BlockInlineCheck aBlockInlineCheck,
- const Element* aAncestorLimiter = nullptr);
+ const Element* aAncestorLimiter = nullptr) {
+ return GetPreviousLeafContentOrPreviousBlockElementImpl(
+ aStartPoint, StopAtBlockSibling::Yes, aOptions, aBlockInlineCheck,
+ aAncestorLimiter);
+ }
/**
* Returns a content node whose inline styles should be preserved after
@@ -2035,11 +2068,9 @@ class HTMLEditUtils final {
static Maybe<EditorLineBreakType> GetFirstLineBreak(
const dom::Element& aElement) {
for (nsIContent* content = HTMLEditUtils::GetFirstLeafContent(aElement, {});
- content; content = HTMLEditUtils::GetNextContent(
- *content,
- {WalkTreeOption::IgnoreDataNodeExceptText,
- WalkTreeOption::IgnoreWhiteSpaceOnlyText},
- BlockInlineCheck::Unused, &aElement)) {
+ content; content = HTMLEditUtils::GetNextLeafContent(
+ *content, {LeafNodeOption::IgnoreInvisibleText},
+ BlockInlineCheck::Auto, &aElement)) {
if (auto* brElement = dom::HTMLBRElement::FromNode(*content)) {
return Some(EditorLineBreakType(*brElement));
}
@@ -2871,18 +2902,6 @@ class HTMLEditUtils final {
}
/**
- * 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
* boundary. And it may return a <br> element only when aContent is a
diff --git a/editor/libeditor/HTMLEditor.cpp b/editor/libeditor/HTMLEditor.cpp
@@ -6686,8 +6686,8 @@ HTMLEditor::CopyLastEditableChildStylesWithTransaction(
}
while (deepestEditableContent &&
deepestEditableContent->IsHTMLElement(nsGkAtoms::br)) {
- deepestEditableContent = HTMLEditUtils::GetPreviousContent(
- *deepestEditableContent, {WalkTreeOption::IgnoreNonEditableNode},
+ deepestEditableContent = HTMLEditUtils::GetPreviousLeafContent(
+ *deepestEditableContent, {LeafNodeOption::IgnoreNonEditableNode},
BlockInlineCheck::UseComputedDisplayOutsideStyle, &aEditingHost);
}
if (!deepestEditableContent) {
diff --git a/editor/libeditor/HTMLEditorDeleteHandler.cpp b/editor/libeditor/HTMLEditorDeleteHandler.cpp
@@ -2554,11 +2554,11 @@ bool HTMLEditor::AutoDeleteRangesHandler::AutoBlockElementsJoiner::
auto ScanJoinTarget = [&]() MOZ_NEVER_INLINE_DEBUG -> nsIContent* {
nsIContent* targetContent =
aDirectionAndAmount == nsIEditor::ePrevious
- ? HTMLEditUtils::GetPreviousContent(
- aCurrentBlockElement, {WalkTreeOption::IgnoreNonEditableNode},
+ ? HTMLEditUtils::GetPreviousLeafContent(
+ aCurrentBlockElement, {LeafNodeOption::IgnoreNonEditableNode},
BlockInlineCheck::Auto, &aEditingHost)
- : HTMLEditUtils::GetNextContent(
- aCurrentBlockElement, {WalkTreeOption::IgnoreNonEditableNode},
+ : HTMLEditUtils::GetNextLeafContent(
+ aCurrentBlockElement, {LeafNodeOption::IgnoreNonEditableNode},
BlockInlineCheck::Auto, &aEditingHost);
// If found content is an invisible text node, let's scan visible things.
auto IsIgnorableDataNode = [](nsIContent* aContent) {
@@ -5050,10 +5050,11 @@ HTMLEditor::AutoDeleteRangesHandler::ComputeRangeToDeleteRangeWithTransaction(
EditorRawDOMPoint caretPoint(aRangeToDelete.StartRef());
if (howToHandleCollapsedRange ==
EditorBase::HowToHandleCollapsedRange::ExtendBackward &&
- caretPoint.IsStartOfContainer()) {
- nsIContent* previousEditableContent = HTMLEditUtils::GetPreviousContent(
- *caretPoint.GetContainer(), {WalkTreeOption::IgnoreNonEditableNode},
- BlockInlineCheck::Unused, &aEditingHost);
+ caretPoint.IsStartOfContainer() && caretPoint.IsInContentNode()) {
+ nsIContent* previousEditableContent = HTMLEditUtils::GetPreviousLeafContent(
+ *caretPoint.ContainerAs<nsIContent>(),
+ {LeafNodeOption::IgnoreNonEditableNode}, BlockInlineCheck::Auto,
+ &aEditingHost);
if (!previousEditableContent) {
return NS_OK;
}
@@ -5073,10 +5074,11 @@ HTMLEditor::AutoDeleteRangesHandler::ComputeRangeToDeleteRangeWithTransaction(
if (howToHandleCollapsedRange ==
EditorBase::HowToHandleCollapsedRange::ExtendForward &&
- caretPoint.IsEndOfContainer()) {
- nsIContent* nextEditableContent = HTMLEditUtils::GetNextContent(
- *caretPoint.GetContainer(), {WalkTreeOption::IgnoreNonEditableNode},
- BlockInlineCheck::Unused, &aEditingHost);
+ caretPoint.IsEndOfContainer() && caretPoint.IsInContentNode()) {
+ nsIContent* nextEditableContent = HTMLEditUtils::GetNextLeafContent(
+ *caretPoint.ContainerAs<nsIContent>(),
+ {LeafNodeOption::IgnoreNonEditableNode}, BlockInlineCheck::Auto,
+ &aEditingHost);
if (!nextEditableContent) {
return NS_OK;
}
@@ -5113,12 +5115,12 @@ HTMLEditor::AutoDeleteRangesHandler::ComputeRangeToDeleteRangeWithTransaction(
nsIContent* editableContent =
howToHandleCollapsedRange ==
EditorBase::HowToHandleCollapsedRange::ExtendBackward
- ? HTMLEditUtils::GetPreviousContent(
- caretPoint, {WalkTreeOption::IgnoreNonEditableNode},
- BlockInlineCheck::Unused, &aEditingHost)
- : HTMLEditUtils::GetNextContent(
- caretPoint, {WalkTreeOption::IgnoreNonEditableNode},
- BlockInlineCheck::Unused, &aEditingHost);
+ ? HTMLEditUtils::GetPreviousLeafContent(
+ caretPoint, {LeafNodeOption::IgnoreNonEditableNode},
+ BlockInlineCheck::Auto, &aEditingHost)
+ : HTMLEditUtils::GetNextLeafContent(
+ caretPoint, {LeafNodeOption::IgnoreNonEditableNode},
+ BlockInlineCheck::Auto, &aEditingHost);
if (!editableContent) {
return NS_OK;
}
@@ -5127,12 +5129,12 @@ HTMLEditor::AutoDeleteRangesHandler::ComputeRangeToDeleteRangeWithTransaction(
editableContent =
howToHandleCollapsedRange ==
EditorBase::HowToHandleCollapsedRange::ExtendBackward
- ? HTMLEditUtils::GetPreviousContent(
- *editableContent, {WalkTreeOption::IgnoreNonEditableNode},
- BlockInlineCheck::Unused, &aEditingHost)
- : HTMLEditUtils::GetNextContent(
- *editableContent, {WalkTreeOption::IgnoreNonEditableNode},
- BlockInlineCheck::Unused, &aEditingHost);
+ ? HTMLEditUtils::GetPreviousLeafContent(
+ *editableContent, {LeafNodeOption::IgnoreNonEditableNode},
+ BlockInlineCheck::Auto, &aEditingHost)
+ : HTMLEditUtils::GetNextLeafContent(
+ *editableContent, {LeafNodeOption::IgnoreNonEditableNode},
+ BlockInlineCheck::Auto, &aEditingHost);
}
if (!editableContent) {
return NS_OK;
@@ -7215,7 +7217,7 @@ Result<CaretPoint, nsresult> HTMLEditor::AutoDeleteRangesHandler::
for (EditorRawDOMPoint scanStartPoint =
EditorRawDOMPoint::After(mEmptyInclusiveAncestorBlockElement);
scanStartPoint.IsInContentNode();) {
- nsIContent* const nextContent = HTMLEditUtils::GetNextContent(
+ nsIContent* const nextContent = HTMLEditUtils::GetNextLeafContent(
scanStartPoint, {}, BlockInlineCheck::Auto, &aEditingHost);
// Let's ignore invisible `Text`.
if (nextContent && nextContent->IsText() &&
@@ -7252,9 +7254,10 @@ Result<CaretPoint, nsresult> HTMLEditor::AutoDeleteRangesHandler::
for (EditorRawDOMPoint scanStartPoint =
EditorRawDOMPoint(mEmptyInclusiveAncestorBlockElement);
scanStartPoint.IsInContentNode();) {
- nsIContent* const previousContent = HTMLEditUtils::GetPreviousContent(
- scanStartPoint, {WalkTreeOption::IgnoreNonEditableNode},
- BlockInlineCheck::Auto, &aEditingHost);
+ nsIContent* const previousContent =
+ HTMLEditUtils::GetPreviousLeafContent(
+ scanStartPoint, {LeafNodeOption::IgnoreNonEditableNode},
+ BlockInlineCheck::Auto, &aEditingHost);
// Let's ignore invisible `Text`.
if (previousContent && previousContent->IsText() &&
!HTMLEditUtils::IsVisibleTextNode(*previousContent->AsText())) {
diff --git a/editor/libeditor/HTMLEditorInsertParagraphHandler.cpp b/editor/libeditor/HTMLEditorInsertParagraphHandler.cpp
@@ -1250,9 +1250,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::GetPreviousContent(
- aPointToSplit, {WalkTreeOption::IgnoreNonEditableNode},
- BlockInlineCheck::Unused, &mEditingHost));
+ HTMLBRElement::FromNodeOrNull(HTMLEditUtils::GetPreviousLeafContent(
+ aPointToSplit, {LeafNodeOption::IgnoreNonEditableNode},
+ BlockInlineCheck::Auto, &mEditingHost));
if (!IsNullOrInvisibleBRElementOrPaddingOneForEmptyLastLine(
precedingBRElement)) {
return true;
@@ -1261,9 +1261,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::GetNextContent(
- aPointToSplit, {WalkTreeOption::IgnoreNonEditableNode},
- BlockInlineCheck::Unused, &mEditingHost));
+ HTMLBRElement::FromNodeOrNull(HTMLEditUtils::GetNextLeafContent(
+ aPointToSplit, {LeafNodeOption::IgnoreNonEditableNode},
+ BlockInlineCheck::Auto, &mEditingHost));
return !IsNullOrInvisibleBRElementOrPaddingOneForEmptyLastLine(
followingBRElement);
}
diff --git a/editor/libeditor/HTMLEditorState.cpp b/editor/libeditor/HTMLEditorState.cpp
@@ -38,6 +38,7 @@ namespace mozilla {
using namespace dom;
using EditorType = EditorUtils::EditorType;
+using LeafNodeOption = HTMLEditUtils::LeafNodeOption;
using WalkTreeOption = HTMLEditUtils::WalkTreeOption;
/*****************************************************************************
@@ -282,9 +283,9 @@ AlignStateAtSelection::AlignStateAtSelection(HTMLEditor& aHTMLEditor,
else if (atStartOfSelection.IsContainerHTMLElement(nsGkAtoms::html) &&
atBodyOrDocumentElement.IsSet() &&
atStartOfSelection.Offset() == atBodyOrDocumentElement.Offset()) {
- editTargetContent = HTMLEditUtils::GetNextContent(
- atStartOfSelection, {WalkTreeOption::IgnoreNonEditableNode},
- BlockInlineCheck::Unused, aHTMLEditor.ComputeEditingHost());
+ editTargetContent = HTMLEditUtils::GetNextLeafContent(
+ atStartOfSelection, {LeafNodeOption::IgnoreNonEditableNode},
+ BlockInlineCheck::Auto, aHTMLEditor.ComputeEditingHost());
if (NS_WARN_IF(!editTargetContent)) {
aRv.Throw(NS_ERROR_FAILURE);
return;
diff --git a/editor/libeditor/gtest/TestHTMLEditUtils.cpp b/editor/libeditor/gtest/TestHTMLEditUtils.cpp
@@ -1831,6 +1831,96 @@ 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
+
struct MOZ_STACK_CLASS LineBreakBeforeBlockBoundaryTest final {
const char16_t* const mInnerHTML;
const char* const mContainer;