Bug 1627175 - part 46: Move `HTMLEditor::GetPriorHTMLSibling()` to `HTMLEditUtils` r=m_kato

Differential Revision: https://phabricator.services.mozilla.com/D115115
This commit is contained in:
Masayuki Nakano 2021-05-17 03:05:13 +00:00
Родитель 691fb1b366
Коммит 1d94e91f3f
7 изменённых файлов: 94 добавлений и 54 удалений

Просмотреть файл

@ -3376,7 +3376,9 @@ nsresult HTMLEditor::IndentListChild(RefPtr<Element>* aCurList,
// We do this if the previous element is a list, and the list is of
// the same type (li/ol) as aContent was a part of.
if (nsCOMPtr<nsIContent> previousEditableSibling =
GetPriorHTMLSibling(&aContent, SkipWhiteSpace::Yes)) {
HTMLEditUtils::GetPreviousSibling(
aContent, {WalkTreeOption::IgnoreWhiteSpaceOnlyText,
WalkTreeOption::IgnoreNonEditableNode})) {
if (HTMLEditUtils::IsAnyListElement(previousEditableSibling) &&
aCurPoint.GetContainer()->NodeInfo()->NameAtom() ==
previousEditableSibling->NodeInfo()->NameAtom() &&
@ -3396,7 +3398,10 @@ nsresult HTMLEditor::IndentListChild(RefPtr<Element>* aCurList,
// check to see if aCurList is still appropriate. Which it is if
// aContent is still right after it in the same list.
nsIContent* previousEditableSibling =
*aCurList ? GetPriorHTMLSibling(&aContent, SkipWhiteSpace::Yes) : nullptr;
*aCurList ? HTMLEditUtils::GetPreviousSibling(
aContent, {WalkTreeOption::IgnoreWhiteSpaceOnlyText,
WalkTreeOption::IgnoreNonEditableNode})
: nullptr;
if (!*aCurList ||
(previousEditableSibling && previousEditableSibling != *aCurList)) {
nsAtom* containerName = aCurPoint.GetContainer()->NodeInfo()->NameAtom();
@ -3873,7 +3878,9 @@ nsresult HTMLEditor::HandleHTMLIndentAtSelectionInternal() {
// check to see if curList is still appropriate. Which it is if
// content is still right after it in the same list.
nsIContent* previousEditableSibling =
curList ? GetPriorHTMLSibling(listItem) : nullptr;
curList ? HTMLEditUtils::GetPreviousSibling(
*listItem, {WalkTreeOption::IgnoreNonEditableNode})
: nullptr;
if (!curList ||
(previousEditableSibling && previousEditableSibling != curList)) {
EditorDOMPoint atListItem(listItem);
@ -6506,7 +6513,8 @@ nsresult HTMLEditor::HandleInsertParagraphInHeadingElement(Element& aHeader,
// If the previous heading of split point is empty, put a padding <br>
// element for empty last line into it.
nsCOMPtr<nsIContent> prevItem = GetPriorHTMLSibling(&aHeader);
nsCOMPtr<nsIContent> prevItem = HTMLEditUtils::GetPreviousSibling(
aHeader, {WalkTreeOption::IgnoreNonEditableNode});
if (prevItem) {
MOZ_DIAGNOSTIC_ASSERT(HTMLEditUtils::IsHeader(*prevItem));
if (HTMLEditUtils::IsEmptyNode(
@ -6689,7 +6697,11 @@ EditActionResult HTMLEditor::HandleInsertParagraphInParagraph(
// at beginning of text node?
if (atStartOfSelection.IsStartOfContainer()) {
// is there a BR prior to it?
brContent = GetPriorHTMLSibling(atStartOfSelection.GetContainer());
brContent = atStartOfSelection.IsInContentNode()
? HTMLEditUtils::GetPreviousSibling(
*atStartOfSelection.ContainerAsContent(),
{WalkTreeOption::IgnoreNonEditableNode})
: nullptr;
if (!brContent || !HTMLEditUtils::IsVisibleBRElement(*brContent) ||
EditorUtils::IsPaddingBRElementForEmptyLastLine(*brContent)) {
pointToInsertBR.Set(atStartOfSelection.GetContainer());
@ -7031,7 +7043,8 @@ nsresult HTMLEditor::HandleInsertParagraphInListItemElement(Element& aListItem,
// Hack: until I can change the damaged doc range code back to being
// extra-inclusive, I have to manually detect certain list items that may be
// left empty.
nsCOMPtr<nsIContent> prevItem = GetPriorHTMLSibling(&aListItem);
nsCOMPtr<nsIContent> prevItem = HTMLEditUtils::GetPreviousSibling(
aListItem, {WalkTreeOption::IgnoreNonEditableNode});
if (prevItem && HTMLEditUtils::IsListItem(prevItem)) {
if (HTMLEditUtils::IsEmptyNode(
*prevItem, {EmptyCheckOption::TreatSingleBRElementAsVisible})) {
@ -8094,7 +8107,8 @@ void HTMLEditor::SetSelectionInterlinePosition() {
// immediately after non-editable contents, but previous editable
// content is a block, does this do right thing?
if (nsIContent* previousEditableContentInBlockAtCaret =
GetPriorHTMLSibling(atCaret.GetChild())) {
HTMLEditUtils::GetPreviousSibling(
*atCaret.GetChild(), {WalkTreeOption::IgnoreNonEditableNode})) {
if (HTMLEditUtils::IsBlockElement(*previousEditableContentInBlockAtCaret)) {
IgnoredErrorResult ignoredError;
SelectionRef().SetInterlinePosition(true, ignoredError);
@ -9003,8 +9017,8 @@ nsresult HTMLEditor::EnsureHardLineBeginsWithFirstChildOf(
return NS_OK;
}
nsIContent* previousEditableContent =
GetPriorHTMLSibling(&aRemovingContainerElement);
nsIContent* previousEditableContent = HTMLEditUtils::GetPreviousSibling(
aRemovingContainerElement, {WalkTreeOption::IgnoreNonEditableNode});
if (!previousEditableContent) {
return NS_OK;
}
@ -9040,8 +9054,8 @@ nsresult HTMLEditor::EnsureHardLineEndsWithLastChildOf(
return NS_OK;
}
nsIContent* nextEditableContent =
GetPriorHTMLSibling(&aRemovingContainerElement);
nsIContent* nextEditableContent = HTMLEditUtils::GetPreviousSibling(
aRemovingContainerElement, {WalkTreeOption::IgnoreNonEditableNode});
if (!nextEditableContent) {
return NS_OK;
}
@ -9418,7 +9432,10 @@ nsresult HTMLEditor::MoveSelectedContentsToDivElementToMakeItAbsolutePosition(
// Therefore, duplicate same list element into the target `<div>`
// element.
nsIContent* previousEditableContent =
createdListElement ? GetPriorHTMLSibling(content) : nullptr;
createdListElement
? HTMLEditUtils::GetPreviousSibling(
content, {WalkTreeOption::IgnoreNonEditableNode})
: nullptr;
if (!createdListElement ||
(previousEditableContent &&
previousEditableContent != createdListElement)) {
@ -9480,7 +9497,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* previousEditableContent =
createdListElement ? GetPriorHTMLSibling(listItemElement) : nullptr;
createdListElement
? HTMLEditUtils::GetPreviousSibling(
*listItemElement, {WalkTreeOption::IgnoreNonEditableNode})
: nullptr;
if (!createdListElement ||
(previousEditableContent &&
previousEditableContent != createdListElement)) {

Просмотреть файл

@ -996,10 +996,7 @@ nsIContent* HTMLEditUtils::GetPreviousContent(
return nullptr;
}
if ((!aOptions.contains(WalkTreeOption::IgnoreNonEditableNode) ||
EditorUtils::IsEditableContent(*lastLeafContent, EditorType::HTML)) &&
(!aOptions.contains(WalkTreeOption::IgnoreDataNodeExceptText) ||
EditorUtils::IsElementOrText(*lastLeafContent))) {
if (!HTMLEditUtils::IsContentIgnored(*lastLeafContent, aOptions)) {
return lastLeafContent;
}
@ -1052,10 +1049,7 @@ nsIContent* HTMLEditUtils::GetNextContent(
return nullptr;
}
if ((!aOptions.contains(WalkTreeOption::IgnoreNonEditableNode) ||
EditorUtils::IsEditableContent(*firstLeafContent, EditorType::HTML)) &&
(!aOptions.contains(WalkTreeOption::IgnoreDataNodeExceptText) ||
EditorUtils::IsElementOrText(*firstLeafContent))) {
if (!HTMLEditUtils::IsContentIgnored(*firstLeafContent, aOptions)) {
return firstLeafContent;
}
@ -1147,10 +1141,7 @@ nsIContent* HTMLEditUtils::GetAdjacentContent(
return nullptr;
}
if ((!aOptions.contains(WalkTreeOption::IgnoreNonEditableNode) ||
EditorUtils::IsEditableContent(*leafContent, EditorType::HTML)) &&
(!aOptions.contains(WalkTreeOption::IgnoreDataNodeExceptText) ||
EditorUtils::IsElementOrText(*leafContent))) {
if (!HTMLEditUtils::IsContentIgnored(*leafContent, aOptions)) {
return leafContent;
}

Просмотреть файл

@ -401,6 +401,7 @@ class HTMLEditUtils final {
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>;
@ -463,6 +464,26 @@ class HTMLEditUtils final {
const WalkTreeOptions& aOptions,
const Element* aAncestorLimiter = nullptr);
/**
* GetPreviousSibling() returns the nearest sibling of aContent which does
* not match with aOption.
*/
static nsIContent* GetPreviousSibling(const nsIContent& aContent,
const WalkTreeOptions& aOptions) {
for (nsIContent* sibling = aContent.GetPreviousSibling(); sibling;
sibling = sibling->GetPreviousSibling()) {
if (HTMLEditUtils::IsContentIgnored(*sibling, aOptions)) {
continue;
}
if (aOptions.contains(WalkTreeOption::StopAtBlockBoundary) &&
HTMLEditUtils::IsBlockElement(*sibling)) {
return nullptr;
}
return sibling;
}
return nullptr;
}
/**
* GetLastLeafChild() returns rightmost leaf content in aNode. It depends on
* aLeafNodeTypes whether this which types of nodes are treated as leaf nodes.
@ -1161,6 +1182,25 @@ 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<dom::Text*>(aContent.AsText())->TextIsOnlyWhitespace()) {
return true;
}
return false;
}
/**
* Helper for GetPreviousContent() and GetNextContent().
*/

Просмотреть файл

@ -4061,7 +4061,8 @@ nsresult HTMLEditor::RemoveBlockContainerWithTransaction(Element& aElement) {
// 3) first child of aNode is a block OR
// 4) either is null
if (nsIContent* previousSibling = GetPriorHTMLSibling(&aElement)) {
if (nsIContent* previousSibling = HTMLEditUtils::GetPreviousSibling(
aElement, {WalkTreeOption::IgnoreNonEditableNode})) {
if (!HTMLEditUtils::IsBlockElement(*previousSibling) &&
!previousSibling->IsHTMLElement(nsGkAtoms::br) &&
!HTMLEditUtils::IsBlockElement(*child)) {
@ -4099,7 +4100,8 @@ nsresult HTMLEditor::RemoveBlockContainerWithTransaction(Element& aElement) {
}
}
}
} else if (nsIContent* previousSibling = GetPriorHTMLSibling(&aElement)) {
} 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
@ -4926,20 +4928,6 @@ nsresult HTMLEditor::DeleteSelectionAndPrepareToCreateNode() {
return error.StealNSResult();
}
nsIContent* HTMLEditor::GetPriorHTMLSibling(nsINode* aNode,
SkipWhiteSpace aSkipWS) const {
MOZ_ASSERT(aNode);
nsIContent* content = aNode->GetPreviousSibling();
while (content &&
(!EditorUtils::IsEditableContent(*content, EditorType::HTML) ||
SkippableWhiteSpace(content, aSkipWS))) {
content = content->GetPreviousSibling();
}
return content;
}
nsIContent* HTMLEditor::GetNextHTMLSibling(nsINode* aNode,
SkipWhiteSpace aSkipWS) const {
MOZ_ASSERT(aNode);

Просмотреть файл

@ -917,14 +917,7 @@ class HTMLEditor final : public TextEditor,
SplitAncestorStyledInlineElementsAt(const EditorDOMPoint& aPointToSplit,
nsAtom* aProperty, nsAtom* aAttribute);
/**
* GetPriorHTMLSibling() returns the previous editable sibling, if there is
* one within the parent, optionally skipping text nodes that are only
* white-space.
*/
enum class SkipWhiteSpace { Yes, No };
nsIContent* GetPriorHTMLSibling(nsINode* aNode,
SkipWhiteSpace = SkipWhiteSpace::No) const;
/**
* GetNextHTMLSibling() returns the next editable sibling, if there is
@ -934,7 +927,7 @@ class HTMLEditor final : public TextEditor,
nsIContent* GetNextHTMLSibling(nsINode* aNode,
SkipWhiteSpace = SkipWhiteSpace::No) const;
// Helper for GetPriorHTMLSibling/GetNextHTMLSibling.
// Helper for GetNextHTMLSibling.
static bool SkippableWhiteSpace(nsINode* aNode, SkipWhiteSpace aSkipWS) {
return aSkipWS == SkipWhiteSpace::Yes && aNode->IsText() &&
aNode->AsText()->TextIsOnlyWhitespace();

Просмотреть файл

@ -50,6 +50,7 @@ using namespace dom;
using EmptyCheckOption = HTMLEditUtils::EmptyCheckOption;
using LeafNodeType = HTMLEditUtils::LeafNodeType;
using LeafNodeTypes = HTMLEditUtils::LeafNodeTypes;
using WalkTreeOption = HTMLEditUtils::WalkTreeOption;
nsresult HTMLEditor::SetInlinePropertyAsAction(nsAtom& aProperty,
nsAtom* aAttribute,
@ -494,7 +495,8 @@ nsresult HTMLEditor::SetInlinePropertyOnTextNode(
if (aAttribute) {
// Look for siblings that are correct type of node
nsIContent* sibling = GetPriorHTMLSibling(textNodeForTheRange);
nsIContent* sibling = HTMLEditUtils::GetPreviousSibling(
*textNodeForTheRange, {WalkTreeOption::IgnoreNonEditableNode});
if (sibling && sibling->IsElement()) {
OwningNonNull<Element> element(*sibling->AsElement());
Result<bool, nsresult> result = ElementIsGoodContainerForTheStyle(
@ -586,7 +588,8 @@ nsresult HTMLEditor::SetInlinePropertyOnNodeImpl(nsIContent& aContent,
}
// First check if there's an adjacent sibling we can put our node into.
nsCOMPtr<nsIContent> previousSibling = GetPriorHTMLSibling(&aContent);
nsCOMPtr<nsIContent> previousSibling = HTMLEditUtils::GetPreviousSibling(
aContent, {WalkTreeOption::IgnoreNonEditableNode});
nsCOMPtr<nsIContent> nextSibling = GetNextHTMLSibling(&aContent);
if (previousSibling && previousSibling->IsElement()) {
OwningNonNull<Element> previousElement(*previousSibling->AsElement());
@ -2403,6 +2406,7 @@ nsresult HTMLEditor::RelativeFontChangeOnTextNode(FontSize aDir,
NS_WARNING("HTMLEditor::SplitNodeWithTransaction() failed");
return error.StealNSResult();
}
MOZ_DIAGNOSTIC_ASSERT(textNodeForTheRange);
}
// Split at the start of the range.
@ -2420,7 +2424,8 @@ nsresult HTMLEditor::RelativeFontChangeOnTextNode(FontSize aDir,
// Look for siblings that are correct type of node
nsAtom* nodeType = aDir == FontSize::incr ? nsGkAtoms::big : nsGkAtoms::small;
nsCOMPtr<nsIContent> sibling = GetPriorHTMLSibling(textNodeForTheRange);
nsCOMPtr<nsIContent> sibling = HTMLEditUtils::GetPreviousSibling(
*textNodeForTheRange, {WalkTreeOption::IgnoreNonEditableNode});
if (sibling && sibling->IsHTMLElement(nodeType)) {
// Previous sib is already right kind of inline node; slide this over
nsresult rv = MoveNodeToEndWithTransaction(*textNodeForTheRange, *sibling);
@ -2549,7 +2554,8 @@ nsresult HTMLEditor::RelativeFontChangeOnNode(int32_t aSizeChange,
// ok, chuck it in.
// first look at siblings of aNode for matching bigs or smalls.
// if we find one, move aNode into it.
nsCOMPtr<nsIContent> sibling = GetPriorHTMLSibling(aNode);
nsCOMPtr<nsIContent> sibling = HTMLEditUtils::GetPreviousSibling(
*aNode, {WalkTreeOption::IgnoreNonEditableNode});
if (sibling && sibling->IsHTMLElement(atom)) {
// previous sib is already right kind of inline node; slide this over into
// it

Просмотреть файл

@ -38,6 +38,7 @@ using namespace dom;
using LeafNodeType = HTMLEditUtils::LeafNodeType;
using LeafNodeTypes = HTMLEditUtils::LeafNodeTypes;
using WalkTreeOption = HTMLEditUtils::WalkTreeOption;
const char16_t kNBSP = 160;
@ -1201,7 +1202,8 @@ nsresult WhiteSpaceVisibilityKeeper::DeleteContentNodeAndJoinTextNodesAroundIt(
}
nsCOMPtr<nsIContent> previousEditableSibling =
aHTMLEditor.GetPriorHTMLSibling(&aContentToDelete);
HTMLEditUtils::GetPreviousSibling(
aContentToDelete, {WalkTreeOption::IgnoreNonEditableNode});
// Delete the node, and join like nodes if appropriate
rv = aHTMLEditor.DeleteNodeWithTransaction(aContentToDelete);
if (NS_WARN_IF(aHTMLEditor.Destroyed())) {