Bug 1454126 - HTMLEditor should adjust selection outside native anonymous subtree when it inserts something at selection r=m_kato a=RyanVM
authorMasayuki Nakano <masayuki@d-toybox.com>
Tue, 17 Apr 2018 18:48:12 +0900
changeset 463494 e41d8e56d3e644e1984bc353ac55bb1c05d016fb
parent 463493 e378f069dee417b32c92799299245bab46b7c09a
child 463495 8471e4e4f0df5888fe04ac0c060d426b94883239
push id1683
push usersfraser@mozilla.com
push dateThu, 26 Apr 2018 16:43:40 +0000
treeherdermozilla-release@5af6cb21869d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersm_kato, RyanVM
bugs1454126
milestone60.0
Bug 1454126 - HTMLEditor should adjust selection outside native anonymous subtree when it inserts something at selection r=m_kato a=RyanVM HTMLEditor::InsertElementAtSelection() doesn't check if selection is in native anonymous subtree. Therefore, it could insert element into <input> element etc. This patch adds new API to EditorDOMPointBase to compute ancestor point which is not in native-anonymous subtree and make HTMLEditor::GetBetterInsertionPointFor() use it to not return point in any native-anonymous subtrees.
editor/libeditor/EditorDOMPoint.h
editor/libeditor/HTMLEditor.cpp
--- a/editor/libeditor/EditorDOMPoint.h
+++ b/editor/libeditor/EditorDOMPoint.h
@@ -230,16 +230,26 @@ public:
    */
   bool
   IsInTextNode() const
   {
     return mParent && mParent->IsNodeOfType(nsINode::eTEXT);
   }
 
   /**
+   * IsInNativeAnonymousSubtree() returns true if the container is in
+   * native anonymous subtree.
+   */
+  bool
+  IsInNativeAnonymousSubtree() const
+  {
+    return mParent && mParent->IsInNativeAnonymousSubtree();
+  }
+
+  /**
    * IsContainerHTMLElement() returns true if the container node is an HTML
    * element node and its node name is aTag.
    */
   bool
   IsContainerHTMLElement(nsAtom* aTag) const
   {
     return mParent && mParent->IsHTMLElement(aTag);
   }
@@ -526,16 +536,43 @@ public:
 
     if (mOffset.isSome()) {
       mOffset = mozilla::Some(mOffset.value() - 1);
     }
     mChild = previousSibling;
     return true;
   }
 
+  /**
+   * GetNonAnonymousSubtreePoint() returns a DOM point which is NOT in
+   * native-anonymous subtree.  If the instance isn't in native-anonymous
+   * subtree, this returns same point.  Otherwise, climbs up until finding
+   * non-native-anonymous parent and returns the point of it.  I.e.,
+   * container is parent of the found non-anonymous-native node.
+   */
+  EditorRawDOMPoint
+  GetNonAnonymousSubtreePoint() const
+  {
+    if (NS_WARN_IF(!IsSet())) {
+      return EditorRawDOMPoint();
+    }
+    if (!IsInNativeAnonymousSubtree()) {
+      return EditorRawDOMPoint(*this);
+    }
+    nsINode* parent;
+    for (parent = mParent->GetParentNode();
+         parent && parent->IsInNativeAnonymousSubtree();
+         parent = mParent->GetParentNode()) {
+    }
+    if (!parent) {
+      return EditorRawDOMPoint();
+    }
+    return EditorRawDOMPoint(parent);
+  }
+
   bool
   IsSet() const
   {
     return mParent && (mIsChildInitialized || mOffset.isSome());
   }
 
   bool
   IsSetAndValid() const
--- a/editor/libeditor/HTMLEditor.cpp
+++ b/editor/libeditor/HTMLEditor.cpp
@@ -1460,60 +1460,66 @@ HTMLEditor::RebuildDocumentFromSource(co
 EditorRawDOMPoint
 HTMLEditor::GetBetterInsertionPointFor(nsINode& aNodeToInsert,
                                        const EditorRawDOMPoint& aPointToInsert)
 {
   if (NS_WARN_IF(!aPointToInsert.IsSet())) {
     return aPointToInsert;
   }
 
+  EditorRawDOMPoint pointToInsert(aPointToInsert.GetNonAnonymousSubtreePoint());
+  if (NS_WARN_IF(!pointToInsert.IsSet())) {
+    // Cannot insert aNodeToInsert into this DOM tree.
+    return EditorRawDOMPoint();
+  }
+
   // If the node to insert is not a block level element, we can insert it
   // at any point.
   if (!IsBlockNode(&aNodeToInsert)) {
-    return aPointToInsert;
-  }
-
-  WSRunObject wsObj(this, aPointToInsert.GetContainer(),
-                    aPointToInsert.Offset());
+    return pointToInsert;
+  }
+
+  WSRunObject wsObj(this, pointToInsert.GetContainer(),
+                    pointToInsert.Offset());
 
   // If the insertion position is after the last visible item in a line,
   // i.e., the insertion position is just before a visible line break <br>,
   // we want to skip to the position just after the line break (see bug 68767).
   nsCOMPtr<nsINode> nextVisibleNode;
   int32_t nextVisibleOffset = 0;
   WSType nextVisibleType;
-  wsObj.NextVisibleNode(aPointToInsert.GetContainer(), aPointToInsert.Offset(),
+  wsObj.NextVisibleNode(pointToInsert.GetContainer(), pointToInsert.Offset(),
                         address_of(nextVisibleNode),
                         &nextVisibleOffset, &nextVisibleType);
   // So, if the next visible node isn't a <br> element, we can insert the block
   // level element to the point.
   if (!nextVisibleNode ||
       !(nextVisibleType & WSType::br)) {
-    return aPointToInsert;
+    return pointToInsert;
   }
 
   // However, we must not skip next <br> element when the caret appears to be
   // positioned at the beginning of a block, in that case skipping the <br>
   // would not insert the <br> at the caret position, but after the current
   // empty line.
   nsCOMPtr<nsINode> previousVisibleNode;
   int32_t previousVisibleOffset = 0;
   WSType previousVisibleType;
-  wsObj.PriorVisibleNode(aPointToInsert.GetContainer(), aPointToInsert.Offset(),
+  wsObj.PriorVisibleNode(pointToInsert.GetContainer(), pointToInsert.Offset(),
                          address_of(previousVisibleNode),
                          &previousVisibleOffset, &previousVisibleType);
   // So, if there is no previous visible node,
   // or, if both nodes of the insertion point is <br> elements,
   // or, if the previous visible node is different block,
   // we need to skip the following <br>.  So, otherwise, we can insert the
   // block at the insertion point.
   if (!previousVisibleNode ||
       (previousVisibleType & WSType::br) ||
       (previousVisibleType & WSType::thisBlock)) {
-    return aPointToInsert;
+    return pointToInsert;
   }
 
   EditorRawDOMPoint afterBRNode(nextVisibleNode);
   DebugOnly<bool> advanced = afterBRNode.AdvanceOffset();
   NS_WARNING_ASSERTION(advanced,
     "Failed to advance offset to after the <br> node");
   return afterBRNode;
 }