Remove style spans to follow the styles of the block element

This CL removes style spans to follow the styles of the block
element(li, pre, td, and h1~6) when the text of the pasted
or merged element becomes a part of the block element.

BUG=226941
TEST=third_party/WebKit/LayoutTests/editing/deleting/backspace-merge-into-block-element.html

Review-Url: https://codereview.chromium.org/2102913002
Cr-Commit-Position: refs/heads/master@{#402659}
diff --git a/third_party/WebKit/LayoutTests/editing/deleting/backspace-merge-into-block.html b/third_party/WebKit/LayoutTests/editing/deleting/backspace-merge-into-block.html
new file mode 100644
index 0000000..f57277f
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/editing/deleting/backspace-merge-into-block.html
@@ -0,0 +1,36 @@
+<!doctype HTML>
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
+<script src="../assert_selection.js"></script>
+<style>
+p {
+    font-size: 20px;
+    line-height: 22px;
+    color: red;
+}
+</style>
+<div id="log"></div>
+<script>
+test(() => {
+    assert_selection(
+        '<div contenteditable="true"><h1>Heading 1:</h1>^<p>|paragraph was merged.</p></div>',
+        'delete',
+        '<div contenteditable="true"><h1>Heading 1:|paragraph was merged.</h1></div>',
+        'Make a paragraph into a heading');
+    assert_selection(
+        '<div contenteditable="true"><pre>Preformatted text:</pre>^<p>|paragraph was merged.</p></div>',
+        'delete',
+        '<div contenteditable="true"><pre>Preformatted text:|paragraph was merged.</pre></div>',
+        'Make a paragraph into a pre');
+    assert_selection(
+        '<div contenteditable="true"><ul><li>List Item:</li></ul>^<p>|paragraph was merged.</p></div>',
+        'delete',
+        '<div contenteditable="true"><ul><li>List Item:|paragraph was merged.</li></ul></div>',
+        'Make a paragraph into a list');
+    assert_selection(
+        '<div contenteditable="true"><table><tbody><tr><td>Table:</td></tr></tbody></table>^<p>|paragraph was merged.</p></div>',
+        'delete',
+        '<div contenteditable="true"><table><tbody><tr><td>Table:|paragraph was merged.</td></tr></tbody></table></div>',
+        'Make a paragraph into a table');
+}, 'merge into a block by backspace');
+</script>
diff --git a/third_party/WebKit/LayoutTests/editing/deleting/backspace-merge-into-list-item.html b/third_party/WebKit/LayoutTests/editing/deleting/backspace-merge-into-list-item.html
deleted file mode 100644
index 3daa5ad..0000000
--- a/third_party/WebKit/LayoutTests/editing/deleting/backspace-merge-into-list-item.html
+++ /dev/null
@@ -1,34 +0,0 @@
-<!doctype HTML>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<style>
-#editable p {
-  font-size: 20px;
-  line-height: 22px;
-  color: red;
-}
-</style>
-<div contenteditable="true" id="editable">
-  <ul>
-    <li>list item 1</li>
-    <li>list item 2</li>
-    <li>list item 3</li>
-  </ul>
-  <p>Paragraph</p>
-</div>
-<script>
-test(function() {
-  var editor = document.getElementById('editable');
-  var range = document.createRange();
-  var selection = window.getSelection();
-  range.setStart(editor.childNodes[2], 0);
-  range.collapse(true);
-  selection.removeAllRanges();
-  selection.addRange(range);
-  editor.focus();
-  document.execCommand('delete');
-
-  var htmlPara = document.getElementsByTagName('li')[2].outerHTML;
-  assert_equals(htmlPara, '<li>list item 3Paragraph</li>');
-}, 'make a paragraph into a list by backspace');
-</script>
diff --git a/third_party/WebKit/LayoutTests/editing/deleting/merge-paragraph-from-p-with-style-3-expected.txt b/third_party/WebKit/LayoutTests/editing/deleting/merge-paragraph-from-p-with-style-3-expected.txt
index 7984090..87d0eb6 100644
--- a/third_party/WebKit/LayoutTests/editing/deleting/merge-paragraph-from-p-with-style-3-expected.txt
+++ b/third_party/WebKit/LayoutTests/editing/deleting/merge-paragraph-from-p-with-style-3-expected.txt
@@ -7,10 +7,7 @@
 | "
 "
 | <h1>
-|   "hello<#selection-caret>"
-|   <span>
-|     style="color: green;"
-|     "world"
+|   "hello<#selection-caret>world"
 | <font>
 |   color="red"
 |   "
diff --git a/third_party/WebKit/Source/core/editing/commands/ReplaceSelectionCommand.cpp b/third_party/WebKit/Source/core/editing/commands/ReplaceSelectionCommand.cpp
index cfbeebd..65e1689 100644
--- a/third_party/WebKit/Source/core/editing/commands/ReplaceSelectionCommand.cpp
+++ b/third_party/WebKit/Source/core/editing/commands/ReplaceSelectionCommand.cpp
@@ -35,6 +35,7 @@
 #include "core/dom/Document.h"
 #include "core/dom/DocumentFragment.h"
 #include "core/dom/Element.h"
+#include "core/dom/NodeComputedStyle.h"
 #include "core/dom/Text.h"
 #include "core/editing/EditingUtilities.h"
 #include "core/editing/FrameSelection.h"
@@ -794,6 +795,23 @@
     }
 }
 
+static bool followBlockElementStyle(const Node* node)
+{
+    if (!node->isHTMLElement())
+        return false;
+
+    const HTMLElement& element = toHTMLElement(*node);
+    return element.computedStyle()->display() == LIST_ITEM
+        || element.computedStyle()->display() == TABLE_CELL
+        || element.hasTagName(preTag)
+        || element.hasTagName(h1Tag)
+        || element.hasTagName(h2Tag)
+        || element.hasTagName(h3Tag)
+        || element.hasTagName(h4Tag)
+        || element.hasTagName(h5Tag)
+        || element.hasTagName(h6Tag);
+}
+
 // Remove style spans before insertion if they are unnecessary.  It's faster because we'll
 // avoid doing a layout.
 static bool handleStyleSpansBeforeInsertion(ReplacementFragment& fragment, const Position& insertionPos)
@@ -807,10 +825,19 @@
     if (isMailPasteAsQuotationHTMLBlockQuoteElement(topNode) || enclosingNodeOfType(firstPositionInOrBeforeNode(topNode), isMailHTMLBlockquoteElement, CanCrossEditingBoundary))
         return false;
 
-    // Remove style spans to follow the styles of list item when |fragment| becomes a list item.
-    // See bug http://crbug.com/335955.
+    // Remove style spans to follow the styles of parent block element when |fragment| becomes a part of it.
+    // See bugs http://crbug.com/226941 and http://crbug.com/335955.
     HTMLSpanElement* wrappingStyleSpan = toHTMLSpanElement(topNode);
-    if (isListItem(enclosingBlock(insertionPos.anchorNode()))) {
+    const Node* node = insertionPos.anchorNode();
+    // |node| can be an inline element like <br> under <li>
+    // e.g.) editing/execCommand/switch-list-type.html
+    //       editing/deleting/backspace-merge-into-block.html
+    if (node->computedStyle()->display() == INLINE) {
+        if (!(node = enclosingBlock(insertionPos.anchorNode())))
+            return false;
+    }
+
+    if (followBlockElementStyle(node)) {
         fragment.removeNodePreservingChildren(wrappingStyleSpan);
         return true;
     }