Bug 1627175 - part 50: Move `HTMLEditor::GetFirstEditableChild()` to `HTMLEditUtils` r=m_kato
authorMasayuki Nakano <masayuki@d-toybox.com>
Mon, 17 May 2021 07:08:39 +0000
changeset 579743 5de53d489b7d5613710a40b2c64693dbab31e8ee
parent 579742 f191e2cc4529396c6a3c08d2a96ecca920848ed8
child 579744 4faec743b5f1a507d7fcc2e7c7dfb0fd76d56dc5
push id38467
push usernbeleuzu@mozilla.com
push dateMon, 17 May 2021 09:49:28 +0000
treeherdermozilla-central@5de53d489b7d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersm_kato
bugs1627175
milestone90.0a1
first release with
nightly linux32
5de53d489b7d / 90.0a1 / 20210517094928 / files
nightly linux64
5de53d489b7d / 90.0a1 / 20210517094928 / files
nightly mac
5de53d489b7d / 90.0a1 / 20210517094928 / files
nightly win32
5de53d489b7d / 90.0a1 / 20210517094928 / files
nightly win64
5de53d489b7d / 90.0a1 / 20210517094928 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1627175 - part 50: Move `HTMLEditor::GetFirstEditableChild()` to `HTMLEditUtils` r=m_kato Differential Revision: https://phabricator.services.mozilla.com/D115119
editor/libeditor/HTMLEditSubActionHandler.cpp
editor/libeditor/HTMLEditUtils.h
editor/libeditor/HTMLEditor.cpp
editor/libeditor/HTMLEditor.h
editor/libeditor/HTMLStyleEditor.cpp
--- a/editor/libeditor/HTMLEditSubActionHandler.cpp
+++ b/editor/libeditor/HTMLEditSubActionHandler.cpp
@@ -5267,18 +5267,18 @@ nsresult HTMLEditor::AlignContentsInAllT
 }
 
 nsresult HTMLEditor::AlignBlockContentsWithDivElement(
     Element& aBlockElement, const nsAString& aAlignType) {
   MOZ_ASSERT(IsEditActionDataAvailable());
 
   // XXX I don't understand why we should NOT align non-editable children
   //     with modifying EDITABLE `<div>` element.
-  nsCOMPtr<nsIContent> firstEditableContent =
-      GetFirstEditableChild(aBlockElement);
+  nsCOMPtr<nsIContent> firstEditableContent = HTMLEditUtils::GetFirstChild(
+      aBlockElement, {WalkTreeOption::IgnoreNonEditableNode});
   if (!firstEditableContent) {
     // This block has no editable content, nothing to align.
     return NS_OK;
   }
 
   // If there is only one editable content and it's a `<div>` element,
   // just set `align` attribute of it.
   nsCOMPtr<nsIContent> lastEditableContent = HTMLEditUtils::GetLastChild(
@@ -5339,18 +5339,19 @@ nsresult HTMLEditor::AlignBlockContentsW
 size_t HTMLEditor::CollectChildren(
     nsINode& aNode, nsTArray<OwningNonNull<nsIContent>>& aOutArrayOfContents,
     size_t aIndexToInsertChildren, CollectListChildren aCollectListChildren,
     CollectTableChildren aCollectTableChildren,
     CollectNonEditableNodes aCollectNonEditableNodes) const {
   MOZ_ASSERT(IsEditActionDataAvailable());
 
   size_t numberOfFoundChildren = 0;
-  for (nsIContent* content = GetFirstEditableChild(aNode); content;
-       content = content->GetNextSibling()) {
+  for (nsIContent* content = HTMLEditUtils::GetFirstChild(
+           aNode, {WalkTreeOption::IgnoreNonEditableNode});
+       content; content = content->GetNextSibling()) {
     if ((aCollectListChildren == CollectListChildren::Yes &&
          (HTMLEditUtils::IsAnyListElement(content) ||
           HTMLEditUtils::IsListItem(content))) ||
         (aCollectTableChildren == CollectTableChildren::Yes &&
          HTMLEditUtils::IsAnyTableElement(content))) {
       numberOfFoundChildren += CollectChildren(
           *content, aOutArrayOfContents,
           aIndexToInsertChildren + numberOfFoundChildren, aCollectListChildren,
@@ -7761,17 +7762,18 @@ nsresult HTMLEditor::JoinNearestEditable
 
   // Remember the last left child, and first right child
   nsCOMPtr<nsIContent> lastLeft = HTMLEditUtils::GetLastChild(
       aNodeLeft, {WalkTreeOption::IgnoreNonEditableNode});
   if (NS_WARN_IF(!lastLeft)) {
     return NS_ERROR_FAILURE;
   }
 
-  nsCOMPtr<nsIContent> firstRight = GetFirstEditableChild(aNodeRight);
+  nsCOMPtr<nsIContent> firstRight = HTMLEditUtils::GetFirstChild(
+      aNodeRight, {WalkTreeOption::IgnoreNonEditableNode});
   if (NS_WARN_IF(!firstRight)) {
     return NS_ERROR_FAILURE;
   }
 
   // For list items, divs, etc., merge smart
   nsresult rv = JoinNodesWithTransaction(aNodeLeft, aNodeRight);
   if (NS_WARN_IF(Destroyed())) {
     return NS_ERROR_EDITOR_DESTROYED;
@@ -8046,17 +8048,18 @@ nsresult HTMLEditor::EnsureCaretInBlockE
     }
     nsresult rv = CollapseSelectionTo(endPoint);
     NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
                          "HTMLEditor::CollapseSelectionTo() failed");
     return rv;
   }
 
   // selection is before block.  put at start of block.
-  nsIContent* firstEditableContent = GetFirstEditableChild(aElement);
+  nsIContent* firstEditableContent = HTMLEditUtils::GetFirstChild(
+      aElement, {WalkTreeOption::IgnoreNonEditableNode});
   if (!firstEditableContent) {
     firstEditableContent = &aElement;
   }
   EditorRawDOMPoint atStartOfBlock;
   if (firstEditableContent->IsText() ||
       HTMLEditUtils::IsContainerNode(*firstEditableContent)) {
     atStartOfBlock.Set(firstEditableContent);
   } else {
@@ -9012,18 +9015,18 @@ nsresult HTMLEditor::RemoveAlignFromDesc
   }
   return NS_OK;
 }
 
 nsresult HTMLEditor::EnsureHardLineBeginsWithFirstChildOf(
     Element& aRemovingContainerElement) {
   MOZ_ASSERT(IsEditActionDataAvailable());
 
-  nsIContent* firstEditableChild =
-      GetFirstEditableChild(aRemovingContainerElement);
+  nsIContent* firstEditableChild = HTMLEditUtils::GetFirstChild(
+      aRemovingContainerElement, {WalkTreeOption::IgnoreNonEditableNode});
   if (!firstEditableChild) {
     return NS_OK;
   }
 
   if (HTMLEditUtils::IsBlockElement(*firstEditableChild) ||
       firstEditableChild->IsHTMLElement(nsGkAtoms::br)) {
     return NS_OK;
   }
--- a/editor/libeditor/HTMLEditUtils.h
+++ b/editor/libeditor/HTMLEditUtils.h
@@ -496,18 +496,18 @@ class HTMLEditUtils final {
         return nullptr;
       }
       return sibling;
     }
     return nullptr;
   }
 
   /**
-   * GetLastChild() returns the first child of aNode which does not match with
-   * aOption.
+   * GetLastChild() and GetFirstChild() return the first or last child of aNode
+   * which does not match with aOption.
    */
   static nsIContent* GetLastChild(const nsINode& aNode,
                                   const WalkTreeOptions& aOptions) {
     for (nsIContent* child = aNode.GetLastChild(); child;
          child = child->GetPreviousSibling()) {
       if (HTMLEditUtils::IsContentIgnored(*child, aOptions)) {
         continue;
       }
@@ -515,16 +515,32 @@ class HTMLEditUtils final {
           HTMLEditUtils::IsBlockElement(*child)) {
         return nullptr;
       }
       return child;
     }
     return nullptr;
   }
 
+  static nsIContent* GetFirstChild(const nsINode& aNode,
+                                   const WalkTreeOptions& aOptions) {
+    for (nsIContent* child = aNode.GetFirstChild(); child;
+         child = child->GetNextSibling()) {
+      if (HTMLEditUtils::IsContentIgnored(*child, aOptions)) {
+        continue;
+      }
+      if (aOptions.contains(WalkTreeOption::StopAtBlockBoundary) &&
+          HTMLEditUtils::IsBlockElement(*child)) {
+        return nullptr;
+      }
+      return child;
+    }
+    return nullptr;
+  }
+
   /**
    * GetLastLeafChild() returns rightmost leaf content in aNode.  It depends on
    * aLeafNodeTypes whether this which types of nodes are treated as leaf nodes.
    */
   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
--- a/editor/libeditor/HTMLEditor.cpp
+++ b/editor/libeditor/HTMLEditor.cpp
@@ -4047,19 +4047,18 @@ nsresult HTMLEditor::RemoveBlockContaine
   // Two possibilities: the container could be empty of editable content.  If
   // that is the case, we need to compare what is before and after aNode to
   // determine if we need a br.
   //
   // Or it could be not empty, in which case we have to compare previous
   // sibling and first child to determine if we need a leading br, and compare
   // following sibling and last child to determine if we need a trailing br.
 
-  nsCOMPtr<nsIContent> child = GetFirstEditableChild(aElement);
-
-  if (child) {
+  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* previousSibling = HTMLEditUtils::GetPreviousSibling(
             aElement, {WalkTreeOption::IgnoreNonEditableNode})) {
@@ -4933,38 +4932,31 @@ nsresult HTMLEditor::DeleteSelectionAndP
 
 bool HTMLEditor::IsFirstEditableChild(nsINode* aNode) const {
   MOZ_ASSERT(aNode);
   // find first editable child and compare it to aNode
   nsCOMPtr<nsINode> parentNode = aNode->GetParentNode();
   if (NS_WARN_IF(!parentNode)) {
     return false;
   }
-  return GetFirstEditableChild(*parentNode) == aNode;
+  return HTMLEditUtils::GetFirstChild(
+             *parentNode, {WalkTreeOption::IgnoreNonEditableNode}) == aNode;
 }
 
 bool HTMLEditor::IsLastEditableChild(nsINode* aNode) const {
   MOZ_ASSERT(aNode);
   // find last editable child and compare it to aNode
   nsCOMPtr<nsINode> parentNode = aNode->GetParentNode();
   if (NS_WARN_IF(!parentNode)) {
     return false;
   }
   return HTMLEditUtils::GetLastChild(
              *parentNode, {WalkTreeOption::IgnoreNonEditableNode}) == aNode;
 }
 
-nsIContent* HTMLEditor::GetFirstEditableChild(nsINode& aNode) const {
-  nsIContent* child = aNode.GetFirstChild();
-  while (child && !EditorUtils::IsEditableContent(*child, EditorType::HTML)) {
-    child = child->GetNextSibling();
-  }
-  return child;
-}
-
 nsIContent* HTMLEditor::GetFirstEditableLeaf(nsINode& aNode) const {
   Element* editingHost = GetActiveEditingHost();
   if (NS_WARN_IF(!editingHost) ||
       editingHost->IsInclusiveDescendantOf(&aNode)) {
     return nullptr;
   }
   nsIContent* child =
       HTMLEditUtils::GetFirstLeafChild(aNode, {LeafNodeType::OnlyLeafNode});
--- a/editor/libeditor/HTMLEditor.h
+++ b/editor/libeditor/HTMLEditor.h
@@ -914,17 +914,16 @@ class HTMLEditor final : public TextEdit
    *                            returns false.
    */
   [[nodiscard]] MOZ_CAN_RUN_SCRIPT SplitNodeResult
   SplitAncestorStyledInlineElementsAt(const EditorDOMPoint& aPointToSplit,
                                       nsAtom* aProperty, nsAtom* aAttribute);
 
   bool IsFirstEditableChild(nsINode* aNode) const;
   bool IsLastEditableChild(nsINode* aNode) const;
-  nsIContent* GetFirstEditableChild(nsINode& aNode) const;
 
   nsIContent* GetFirstEditableLeaf(nsINode& aNode) const;
   nsIContent* GetLastEditableLeaf(nsINode& aNode) const;
 
   [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult GetInlinePropertyBase(
       nsAtom& aHTMLProperty, nsAtom* aAttribute, const nsAString* aValue,
       bool* aFirst, bool* aAny, bool* aAll, nsAString* outValue) const;
 
--- a/editor/libeditor/HTMLStyleEditor.cpp
+++ b/editor/libeditor/HTMLStyleEditor.cpp
@@ -1431,18 +1431,18 @@ bool HTMLEditor::IsStartOfContainerOrBef
   if (aPoint.IsStartOfContainer()) {
     return true;
   }
 
   if (aPoint.IsInTextNode()) {
     return false;
   }
 
-  nsIContent* firstEditableChild =
-      GetFirstEditableChild(*aPoint.GetContainer());
+  nsIContent* firstEditableChild = HTMLEditUtils::GetFirstChild(
+      *aPoint.GetContainer(), {WalkTreeOption::IgnoreNonEditableNode});
   if (!firstEditableChild) {
     return true;
   }
   return EditorRawDOMPoint(firstEditableChild).Offset() >= aPoint.Offset();
 }
 
 bool HTMLEditor::IsEndOfContainerOrEqualsOrAfterLastEditableChild(
     const EditorRawDOMPoint& aPoint) const {