Bug 1411345 - HTMLEditRules::GetHighestInlineParent() shouldn't return editing host even when it's the highest inline parent of aNode r?m_kato draft
authorMasayuki Nakano <masayuki@d-toybox.com>
Tue, 31 Oct 2017 01:14:58 +0900
changeset 689417 6f33cdab67a49d51ecf1c44004e42e5cbdfeeecc
parent 689416 d45b5efd0110c53ff5e95c5616b523748296726c
child 690027 3f08b9baa7832d95e7c28508219cce95ad51ffba
child 690101 32bd88be6db5346053cef285146e800705a8a1cf
child 691840 590986adf090c074d336d90e019e074bbe898733
push id87024
push usermasayuki@d-toybox.com
push dateTue, 31 Oct 2017 15:23:24 +0000
reviewersm_kato
bugs1411345, 1402196
milestone58.0a1
Bug 1411345 - HTMLEditRules::GetHighestInlineParent() shouldn't return editing host even when it's the highest inline parent of aNode r?m_kato HTMLEditRules::BustUpInlinesAtRangeEndpoints() tries to split all inline nodes at range start and range end. It uses EditorBase::SplitNodeDeep() to split the nodes and HTMLEditRules::GetHighestInlineParent() to retrieve the highest inline parent of them. Currently, HTMLEditRules::GetHighestInlineParent() may return editing host or ancestor of it if active editing host is not block. Then, it may cause splitting editing host or its parents and following methods of HTMLEditRules will fail to modify the nodes created outside the editing host. So, HTMLEditRules::GetHighestInlineParent() should return only one of the descendants of active editing host. Unfortunately, even if just adding the test case as a crash test, I cannot reproduce the crash with automated tests. Therefore, this patch doesn't include any automated tests. And this patch changes a crash test, 1402196.html, which expects that an inline editing host is split by execCommand("insertOrderedList"). However, this patch fixes this wrong behavior. Therefore, this patch changes the event target of event listener from <p> inside the editing host to the editing host itself. MozReview-Commit-ID: 8i5ci1fcrDd
editor/libeditor/HTMLEditRules.cpp
editor/libeditor/HTMLEditRules.h
editor/libeditor/crashtests/1402196.html
--- a/editor/libeditor/HTMLEditRules.cpp
+++ b/editor/libeditor/HTMLEditRules.cpp
@@ -6356,22 +6356,47 @@ HTMLEditRules::BustUpInlinesAtBRs(
 }
 
 nsIContent*
 HTMLEditRules::GetHighestInlineParent(nsINode& aNode)
 {
   if (!aNode.IsContent() || IsBlockNode(aNode)) {
     return nullptr;
   }
-  OwningNonNull<nsIContent> node = *aNode.AsContent();
-
-  while (node->GetParent() && IsInlineNode(*node->GetParent())) {
-    node = *node->GetParent();
-  }
-  return node;
+
+  if (NS_WARN_IF(!mHTMLEditor)) {
+    return nullptr;
+  }
+
+  Element* host = mHTMLEditor->GetActiveEditingHost();
+  if (NS_WARN_IF(!host)) {
+    return nullptr;
+  }
+
+  // If aNode is the editing host itself, there is no modifiable inline parent.
+  if (&aNode == host) {
+    return nullptr;
+  }
+
+  // If aNode is outside of the <body> element, we don't support to edit
+  // such elements for now.
+  // XXX This should be MOZ_ASSERT after fixing bug 1413131 for avoiding
+  //     calling this expensive method.
+  if (NS_WARN_IF(!EditorUtils::IsDescendantOf(&aNode, host))) {
+    return nullptr;
+  }
+
+  // Looks for the highest inline parent in the editing host.
+  nsIContent* content = aNode.AsContent();
+  for (nsIContent* parent = content->GetParent();
+       parent && parent != host && IsInlineNode(*parent);
+       parent = parent->GetParent()) {
+    content = parent;
+  }
+  return content;
 }
 
 /**
  * GetNodesFromPoint() constructs a list of nodes from a point that will be
  * operated on.
  */
 nsresult
 HTMLEditRules::GetNodesFromPoint(
--- a/editor/libeditor/HTMLEditRules.h
+++ b/editor/libeditor/HTMLEditRules.h
@@ -373,16 +373,21 @@ protected:
   nsresult GetParagraphFormatNodes(
              nsTArray<OwningNonNull<nsINode>>& outArrayOfNodes,
              TouchContent aTouchContent = TouchContent::yes);
   void LookInsideDivBQandList(nsTArray<OwningNonNull<nsINode>>& aNodeArray);
   nsresult BustUpInlinesAtRangeEndpoints(RangeItem& inRange);
   nsresult BustUpInlinesAtBRs(
              nsIContent& aNode,
              nsTArray<OwningNonNull<nsINode>>& aOutArrayOfNodes);
+  /**
+   * GetHiestInlineParent() returns the highest inline node parent between
+   * aNode and the editing host.  Even if the editing host is an inline
+   * element, this method never returns the editing host as the result.
+   */
   nsIContent* GetHighestInlineParent(nsINode& aNode);
   void MakeTransitionList(nsTArray<OwningNonNull<nsINode>>& aNodeArray,
                           nsTArray<bool>& aTransitionArray);
   nsresult RemoveBlockStyle(nsTArray<OwningNonNull<nsINode>>& aNodeArray);
   nsresult ApplyBlockStyle(nsTArray<OwningNonNull<nsINode>>& aNodeArray,
                            nsAtom& aBlockTag);
   nsresult MakeBlockquote(nsTArray<OwningNonNull<nsINode>>& aNodeArray);
   nsresult SplitAsNeeded(nsAtom& aTag, OwningNonNull<nsINode>& inOutParent,
--- a/editor/libeditor/crashtests/1402196.html
+++ b/editor/libeditor/crashtests/1402196.html
@@ -1,14 +1,14 @@
 <!DOCTYPE html>
 <html class="reftest-wait">
 <head>
 <script>
 function load() {
-  document.getElementById("p").addEventListener("DOMNodeInserted", () => {
+  document.getElementById("spacer").addEventListener("DOMNodeInserted", () => {
     document.getElementById("style").appendChild(
                                        document.getElementById("table"));
     document.documentElement.classList.remove("reftest-wait");
   });
   document.execCommand("insertOrderedList", false);
 }
 </script>
 </head>