Bug 1359404 - Redesign HTMLEditor::IsVisTextNode() r?m_kato draft
authorMasayuki Nakano <masayuki@d-toybox.com>
Wed, 20 Dec 2017 15:07:37 +0900
changeset 713484 cf4d70ecd846a5ee31c1ddcd4b692344549ecc17
parent 713426 b5fb68f759441343062ae3ef0ac16ffc15510981
child 713485 1e3f369782ed9e17a0d5984f3d0f49af2be70f96
push id93653
push usermasayuki@d-toybox.com
push dateWed, 20 Dec 2017 13:27:25 +0000
reviewersm_kato
bugs1359404
milestone59.0a1
Bug 1359404 - Redesign HTMLEditor::IsVisTextNode() r?m_kato Despite of its name, HTMLEditor::IsVisTextNode() returns true with its out argument when the given text node is empty. And although it returns nsresult, it's almost always NS_OK because it returns error only when the editor isn't usual condition. So, it should return bool and true when the given text node is visible. This patch separates the method. One is for checking the node with frames, called IsInVisibleTextFrames(). The other is for checking it only with its text, called IsVisibleTextNode(). MozReview-Commit-ID: EdQmkOxfNxO
editor/libeditor/HTMLEditRules.cpp
editor/libeditor/HTMLEditor.cpp
editor/libeditor/HTMLEditor.h
--- a/editor/libeditor/HTMLEditRules.cpp
+++ b/editor/libeditor/HTMLEditRules.cpp
@@ -2743,19 +2743,19 @@ HTMLEditRules::WillDeleteSelection(Selec
             // If something visible is deleted, no need to join.  Visible means
             // all nodes except non-visible textnodes and breaks.
             if (join && origCollapsed) {
               if (!somenode->IsContent()) {
                 join = false;
                 continue;
               }
               nsCOMPtr<nsIContent> content = somenode->AsContent();
-              if (content->NodeType() == nsIDOMNode::TEXT_NODE) {
+              if (Text* text = content->GetAsText()) {
                 NS_ENSURE_STATE(mHTMLEditor);
-                mHTMLEditor->IsVisTextNode(content, &join, true);
+                join = !mHTMLEditor->IsInVisibleTextFrames(*text);
               } else {
                 NS_ENSURE_STATE(mHTMLEditor);
                 join = content->IsHTMLElement(nsGkAtoms::br) &&
                        !mHTMLEditor->IsVisibleBRElement(somenode);
               }
             }
           }
         }
@@ -2834,24 +2834,28 @@ HTMLEditRules::WillDeleteSelection(Selec
  * that doesn't correctly skip over it.
  *
  * If deleting the node fails (like if it's not editable), the caller should
  * proceed as usual, so don't return any errors.
  */
 void
 HTMLEditRules::DeleteNodeIfCollapsedText(nsINode& aNode)
 {
-  if (!aNode.GetAsText()) {
+  Text* text = aNode.GetAsText();
+  if (!text) {
     return;
   }
-  bool empty;
-  nsresult rv = mHTMLEditor->IsVisTextNode(aNode.AsContent(), &empty, false);
-  NS_ENSURE_SUCCESS_VOID(rv);
-  if (empty) {
-    mHTMLEditor->DeleteNode(&aNode);
+
+  if (NS_WARN_IF(!mHTMLEditor)) {
+    return;
+  }
+
+  if (!mHTMLEditor->IsVisibleTextNode(*text)) {
+    RefPtr<HTMLEditor> htmlEditor(mHTMLEditor);
+    htmlEditor->DeleteNode(&aNode);
   }
 }
 
 
 /**
  * InsertBRIfNeeded() determines if a br is needed for current selection to not
  * be spastic.  If so, it inserts one.  Callers responsibility to only call
  * with collapsed selection.
@@ -6232,22 +6236,19 @@ HTMLEditRules::GetNodesForOperation(
       if (HTMLEditUtils::IsListItem(node)) {
         int32_t j = i;
         aOutArrayOfNodes.RemoveElementAt(i);
         GetInnerContent(*node, aOutArrayOfNodes, &j);
       }
     }
     // Empty text node shouldn't be selected if unnecessary
     for (int32_t i = aOutArrayOfNodes.Length() - 1; i >= 0; i--) {
-      OwningNonNull<nsINode> node = aOutArrayOfNodes[i];
-      if (EditorBase::IsTextNode(node)) {
+      if (Text* text = aOutArrayOfNodes[i]->GetAsText()) {
         // Don't select empty text except to empty block
-        bool isEmpty = false;
-        htmlEditor->IsVisTextNode(node->AsContent(), &isEmpty, false);
-        if (isEmpty) {
+        if (!htmlEditor->IsVisibleTextNode(*text)) {
           aOutArrayOfNodes.RemoveElementAt(i);
         }
       }
     }
   }
   // Indent/outdent already do something special for list items, but we still
   // need to make sure we don't act on table elements
   else if (aOperationType == EditAction::outdent ||
--- a/editor/libeditor/HTMLEditor.cpp
+++ b/editor/libeditor/HTMLEditor.cpp
@@ -3933,65 +3933,65 @@ HTMLEditor::GetLastEditableLeaf(nsINode&
     if (!aNode.Contains(child)) {
       return nullptr;
     }
   }
 
   return child;
 }
 
-/**
- * IsVisTextNode() figures out if textnode aTextNode has any visible content.
- */
-nsresult
-HTMLEditor::IsVisTextNode(nsIContent* aNode,
-                          bool* outIsEmptyNode,
-                          bool aSafeToAskFrames)
+bool
+HTMLEditor::IsInVisibleTextFrames(Text& aText)
 {
-  MOZ_ASSERT(aNode);
-  MOZ_ASSERT(aNode->NodeType() == nsIDOMNode::TEXT_NODE);
-  MOZ_ASSERT(outIsEmptyNode);
-
-  *outIsEmptyNode = true;
-
-  uint32_t length = aNode->TextLength();
-  if (aSafeToAskFrames) {
-    nsISelectionController* selectionController = GetSelectionController();
-    if (NS_WARN_IF(!selectionController)) {
-      return NS_ERROR_FAILURE;
-    }
-    bool isVisible = false;
-    // ask the selection controller for information about whether any
-    // of the data in the node is really rendered.  This is really
-    // something that frames know about, but we aren't supposed to talk to frames.
-    // So we put a call in the selection controller interface, since it's already
-    // in bed with frames anyway.  (this is a fix for bug 22227, and a
-    // partial fix for bug 46209)
-    nsresult rv = selectionController->CheckVisibilityContent(aNode, 0, length,
-                                                              &isVisible);
-    NS_ENSURE_SUCCESS(rv, rv);
-    if (isVisible) {
-      *outIsEmptyNode = false;
-    }
-  } else if (length) {
-    if (aNode->TextIsOnlyWhitespace()) {
-      WSRunObject wsRunObj(this, aNode, 0);
-      nsCOMPtr<nsINode> visNode;
-      int32_t outVisOffset=0;
-      WSType visType;
-      wsRunObj.NextVisibleNode(aNode, 0, address_of(visNode),
-                               &outVisOffset, &visType);
-      if (visType == WSType::normalWS || visType == WSType::text) {
-        *outIsEmptyNode = (aNode != visNode);
-      }
-    } else {
-      *outIsEmptyNode = false;
-    }
-  }
-  return NS_OK;
+  nsISelectionController* selectionController = GetSelectionController();
+  if (NS_WARN_IF(!selectionController)) {
+    return false;
+  }
+
+  if (!aText.TextDataLength()) {
+    return false;
+  }
+
+  // Ask the selection controller for information about whether any of the
+  // data in the node is really rendered.  This is really something that
+  // frames know about, but we aren't supposed to talk to frames.  So we put
+  // a call in the selection controller interface, since it's already in bed
+  // with frames anyway.  (This is a fix for bug 22227, and a partial fix for
+  // bug 46209.)
+  bool isVisible = false;
+  nsresult rv =
+    selectionController->CheckVisibilityContent(&aText,
+                                                0, aText.TextDataLength(),
+                                                &isVisible);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return false;
+  }
+  return isVisible;
+}
+
+bool
+HTMLEditor::IsVisibleTextNode(Text& aText)
+{
+  if (!aText.TextDataLength()) {
+    return false;
+  }
+
+  if (!aText.TextIsOnlyWhitespace()) {
+    return true;
+  }
+
+  WSRunObject wsRunObj(this, &aText, 0);
+  nsCOMPtr<nsINode> nextVisibleNode;
+  int32_t unused = 0;
+  WSType visibleNodeType;
+  wsRunObj.NextVisibleNode(&aText, 0, address_of(nextVisibleNode),
+                           &unused, &visibleNodeType);
+  return (visibleNodeType == WSType::normalWS ||
+          visibleNodeType == WSType::text) &&
+         &aText == nextVisibleNode;
 }
 
 /**
  * IsEmptyNode() figures out if aNode is an empty node.  A block can have
  * children and still be considered empty, if the children are empty or
  * non-editable.
  */
 nsresult
@@ -4028,18 +4028,20 @@ HTMLEditor::IsEmptyNodeImpl(nsINode* aNo
                             bool* outIsEmptyNode,
                             bool aSingleBRDoesntCount,
                             bool aListOrCellNotEmpty,
                             bool aSafeToAskFrames,
                             bool* aSeenBR)
 {
   NS_ENSURE_TRUE(aNode && outIsEmptyNode && aSeenBR, NS_ERROR_NULL_POINTER);
 
-  if (aNode->NodeType() == nsIDOMNode::TEXT_NODE) {
-    return IsVisTextNode(static_cast<nsIContent*>(aNode), outIsEmptyNode, aSafeToAskFrames);
+  if (Text* text = aNode->GetAsText()) {
+    *outIsEmptyNode = aSafeToAskFrames ? !IsInVisibleTextFrames(*text) :
+                                         !IsVisibleTextNode(*text);
+    return NS_OK;
   }
 
   // if it's not a text node (handled above) and it's not a container,
   // then we don't call it empty (it's an <hr>, or <br>, etc.).
   // Also, if it's an anchor then don't treat it as empty - even though
   // anchors are containers, named anchors are "empty" but we don't
   // want to treat them as such.  Also, don't call ListItems or table
   // cells empty if caller desires.  Form Widgets not empty.
@@ -4059,21 +4061,21 @@ HTMLEditor::IsEmptyNodeImpl(nsINode* aNo
 
   // loop over children of node. if no children, or all children are either
   // empty text nodes or non-editable, then node qualifies as empty
   for (nsCOMPtr<nsIContent> child = aNode->GetFirstChild();
        child;
        child = child->GetNextSibling()) {
     // Is the child editable and non-empty?  if so, return false
     if (EditorBase::IsEditable(child)) {
-      if (child->NodeType() == nsIDOMNode::TEXT_NODE) {
-        nsresult rv = IsVisTextNode(child, outIsEmptyNode, aSafeToAskFrames);
-        NS_ENSURE_SUCCESS(rv, rv);
-        // break out if we find we aren't emtpy
-        if (!*outIsEmptyNode) {
+      if (Text* text = child->GetAsText()) {
+        // break out if we find we aren't empty
+        *outIsEmptyNode = aSafeToAskFrames ? !IsInVisibleTextFrames(*text) :
+                                             !IsVisibleTextNode(*text);
+        if (!*outIsEmptyNode){
           return NS_OK;
         }
       } else {
         // An editable, non-text node. We need to check its content.
         // Is it the node we are iterating over?
         if (child == aNode) {
           break;
         }
--- a/editor/libeditor/HTMLEditor.h
+++ b/editor/libeditor/HTMLEditor.h
@@ -368,22 +368,31 @@ public:
    * This will stop at a table, however, since we don't want to
    * "drill down" into nested tables.
    * @param aSelection      Optional. If null, we get current selection.
    */
   void CollapseSelectionToDeepestNonTableFirstChild(Selection* aSelection,
                                                     nsINode* aNode);
 
   /**
+   * IsInVisibleTextFrames() returns true if all text in aText is in visible
+   * text frames.  Callers have to guarantee that there is no pending reflow.
+   */
+  bool IsInVisibleTextFrames(dom::Text& aText);
+
+  /**
+   * IsVisibleTextNode() returns true if aText has visible text.  If it has
+   * only whitespaces and they are collapsed, returns false.
+   */
+  bool IsVisibleTextNode(Text& aText);
+
+  /**
    * aNode must be a non-null text node.
    * outIsEmptyNode must be non-null.
    */
-  nsresult IsVisTextNode(nsIContent* aNode,
-                         bool* outIsEmptyNode,
-                         bool aSafeToAskFrames);
   nsresult IsEmptyNode(nsIDOMNode* aNode, bool* outIsEmptyBlock,
                        bool aMozBRDoesntCount = false,
                        bool aListOrCellNotEmpty = false,
                        bool aSafeToAskFrames = false);
   nsresult IsEmptyNode(nsINode* aNode, bool* outIsEmptyBlock,
                        bool aMozBRDoesntCount = false,
                        bool aListOrCellNotEmpty = false,
                        bool aSafeToAskFrames = false);