Title: [98899] trunk
Revision
98899
Author
[email protected]
Date
2011-10-31 16:09:12 -0700 (Mon, 31 Oct 2011)

Log Message

WebKit nests pre on copy and paste when the pre is the root editable element
https://bugs.webkit.org/show_bug.cgi?id=70800

Reviewed by Darin Adler.

Source/WebCore: 

Fixed the bug by removing nested block elements in removeRedundantStylesAndKeepStyleSpanInline.

Tests: editing/pasteboard/contenteditable-pre-2.html
       editing/pasteboard/contenteditable-pre.html

* editing/ApplyStyleCommand.cpp:
* editing/ReplaceSelectionCommand.cpp:
(WebCore::ReplaceSelectionCommand::removeRedundantStylesAndKeepStyleSpanInline): Remove block
elements if it's identical to its parent and there are no contents between the two. Also remove
contenteditable attribute from an element if the parent is already richly editable.
(WebCore::ReplaceSelectionCommand::doApply): Remove redundant styles after removing the placeholder
br so that the above check doesn't get affected by the placeholder.
* editing/htmlediting.cpp:
(WebCore::areIdenticalElements): Moved from ApplyStyleCommand.
(WebCore::isNonTableCellHTMLBlockElement): Moved from markup.cpp.
* editing/htmlediting.h:
* editing/markup.cpp:

LayoutTests: 

Added tests for copying and pasting contents inside pre. WebKit should not nest pre's.

* editing/execCommand/insert-list-with-noneditable-content-expected.txt: A redundant
contenteditable attribute is removed.
* editing/pasteboard/4930986-2-expected.txt: Trailing space in style attribute is removed.
* editing/pasteboard/contenteditable-pre-2-expected.txt: Added.
* editing/pasteboard/contenteditable-pre-2.html: Added.
* editing/pasteboard/contenteditable-pre-expected.txt: Added.
* editing/pasteboard/contenteditable-pre.html: Added.
* editing/pasteboard/copy-null-characters-expected.txt: A redundant contenteditable attribute
is removed.
* editing/pasteboard/paste-code-in-pre-expected.txt: Pre no longer nests itself erroneously.
* editing/pasteboard/paste-pre-001-expected.txt: Ditto.
* editing/pasteboard/paste-pre-002-expected.txt: Ditto.
* editing/selection/4895428-4-expected.txt: A redundant contenteditable attribute is removed.

Modified Paths

Added Paths

Diff

Modified: trunk/LayoutTests/ChangeLog (98898 => 98899)


--- trunk/LayoutTests/ChangeLog	2011-10-31 23:08:28 UTC (rev 98898)
+++ trunk/LayoutTests/ChangeLog	2011-10-31 23:09:12 UTC (rev 98899)
@@ -1,3 +1,26 @@
+2011-10-29  Ryosuke Niwa  <[email protected]>
+
+        WebKit nests pre on copy and paste when the pre is the root editable element
+        https://bugs.webkit.org/show_bug.cgi?id=70800
+
+        Reviewed by Darin Adler.
+
+        Added tests for copying and pasting contents inside pre. WebKit should not nest pre's.
+
+        * editing/execCommand/insert-list-with-noneditable-content-expected.txt: A redundant
+        contenteditable attribute is removed.
+        * editing/pasteboard/4930986-2-expected.txt: Trailing space in style attribute is removed.
+        * editing/pasteboard/contenteditable-pre-2-expected.txt: Added.
+        * editing/pasteboard/contenteditable-pre-2.html: Added.
+        * editing/pasteboard/contenteditable-pre-expected.txt: Added.
+        * editing/pasteboard/contenteditable-pre.html: Added.
+        * editing/pasteboard/copy-null-characters-expected.txt: A redundant contenteditable attribute
+        is removed.
+        * editing/pasteboard/paste-code-in-pre-expected.txt: Pre no longer nests itself erroneously.
+        * editing/pasteboard/paste-pre-001-expected.txt: Ditto.
+        * editing/pasteboard/paste-pre-002-expected.txt: Ditto.
+        * editing/selection/4895428-4-expected.txt: A redundant contenteditable attribute is removed.
+
 2011-10-31  Vineet Chaudhary  <[email protected]>
 
         text/plain form encoding ignored and incorrectly specified in request header.

Modified: trunk/LayoutTests/editing/execCommand/insert-list-with-noneditable-content-expected.txt (98898 => 98899)


--- trunk/LayoutTests/editing/execCommand/insert-list-with-noneditable-content-expected.txt	2011-10-31 23:08:28 UTC (rev 98898)
+++ trunk/LayoutTests/editing/execCommand/insert-list-with-noneditable-content-expected.txt	2011-10-31 23:09:12 UTC (rev 98899)
@@ -3,7 +3,6 @@
 |   <li>
 |     "Editabl<#selection-anchor>e paragraph containing a "
 |     <span>
-|       contenteditable="false"
 |       style="background-color: rgb(211, 211, 211); "
 |       "non-editable"
 |     " in the middle"

Modified: trunk/LayoutTests/editing/pasteboard/4930986-2-expected.txt (98898 => 98899)


--- trunk/LayoutTests/editing/pasteboard/4930986-2-expected.txt	2011-10-31 23:08:28 UTC (rev 98898)
+++ trunk/LayoutTests/editing/pasteboard/4930986-2-expected.txt	2011-10-31 23:09:12 UTC (rev 98899)
@@ -1,2 +1,2 @@
 This tests to make sure that content that is colored by the user is pasted with that color during a Paste as Quotation.
-<blockquote><span class="Apple-style-span" style="color: red; ">This text should be red (it should be wrapped in a style span).</span></blockquote>
+<blockquote><span class="Apple-style-span" style="color: red;">This text should be red (it should be wrapped in a style span).</span></blockquote>

Added: trunk/LayoutTests/editing/pasteboard/contenteditable-pre-2-expected.txt (0 => 98899)


--- trunk/LayoutTests/editing/pasteboard/contenteditable-pre-2-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/editing/pasteboard/contenteditable-pre-2-expected.txt	2011-10-31 23:09:12 UTC (rev 98899)
@@ -0,0 +1,14 @@
+This test copies and pastes content inside pre that is an editing host. WebKit should not clone pre.
+To manually test, cut and paste "hello\nworld" WebKit should not nest pre (no red borders).
+
+Before cut paste:
+| <pre>
+|   "<#selection-anchor>hello"
+|   <br>
+|   "world<#selection-focus>"
+
+After cut paste:
+| <pre>
+|   "hello"
+|   <br>
+|   "world<#selection-caret>"

Added: trunk/LayoutTests/editing/pasteboard/contenteditable-pre-2.html (0 => 98899)


--- trunk/LayoutTests/editing/pasteboard/contenteditable-pre-2.html	                        (rev 0)
+++ trunk/LayoutTests/editing/pasteboard/contenteditable-pre-2.html	2011-10-31 23:09:12 UTC (rev 98899)
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<html>
+<body>
+<p id="description">This test copies and pastes content inside pre that is an editing host. WebKit should not clone pre.
+To manually test, cut and paste "hello\nworld" WebKit should not nest pre (no red borders).</p>
+<style> body > *[contenteditable] {border: solid 2px blue;} pre > pre, div > pre {border: solid 2px red;} </style>
+<div contenteditable><pre>hello<br>world</pre></div>
+<script src=""
+<script>
+
+Markup.description(document.getElementById('description').textContent);
+
+var container = document.querySelector('div');
+container.focus();
+document.execCommand('selectAll', false, null);
+
+Markup.dump(container, 'Before cut paste');
+
+document.execCommand('cut', false, null);
+document.execCommand('paste', false, null);
+
+Markup.dump(container, 'After cut paste');
+
+</script>
+</body>
+</html>

Added: trunk/LayoutTests/editing/pasteboard/contenteditable-pre-expected.txt (0 => 98899)


--- trunk/LayoutTests/editing/pasteboard/contenteditable-pre-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/editing/pasteboard/contenteditable-pre-expected.txt	2011-10-31 23:09:12 UTC (rev 98899)
@@ -0,0 +1,10 @@
+This test copies and pastes content inside pre that is an editing host. WebKit should not clone pre.
+To manually test, copy and paste "hello" and then paste it into the boxes below.
+WebKit should not clone pre (nest pre) and the pasted content should not have nested borders.
+
+Pasting into pre; the pre should not be listed below:
+| "hello<#selection-caret>"
+
+Pasting into div; the pasted content should be in pre:
+| <pre>
+|   "hello<#selection-caret>"

Added: trunk/LayoutTests/editing/pasteboard/contenteditable-pre.html (0 => 98899)


--- trunk/LayoutTests/editing/pasteboard/contenteditable-pre.html	                        (rev 0)
+++ trunk/LayoutTests/editing/pasteboard/contenteditable-pre.html	2011-10-31 23:09:12 UTC (rev 98899)
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<html>
+<body>
+<p id="description">This test copies and pastes content inside pre that is an editing host. WebKit should not clone pre.
+To manually test, copy and paste "hello" and then paste it into the boxes below.
+WebKit should not clone pre (nest pre) and the pasted content should not have nested borders.</p>
+<style> #container pre, #container div {border: solid 2px blue;} </style>
+<div id="container">
+<pre contenteditable>hello</pre>
+<div contenteditable></div>
+</div>
+<script src=""
+<script>
+
+Markup.description(document.getElementById('description').textContent);
+
+var container = document.querySelector('#container pre');
+container.focus();
+document.execCommand('selectAll', false, null);
+document.execCommand('copy', false, null);
+document.execCommand('paste', false, null);
+
+Markup.dump(container, "Pasting into pre; the pre should not be listed below");
+
+var container = document.querySelector('#container div');
+container.focus();
+document.execCommand('paste', false, null);
+
+Markup.dump(container, "Pasting into div; the pasted content should be in pre");
+
+</script>
+</body>
+</html>

Modified: trunk/LayoutTests/editing/pasteboard/copy-null-characters-expected.txt (98898 => 98899)


--- trunk/LayoutTests/editing/pasteboard/copy-null-characters-expected.txt	2011-10-31 23:08:28 UTC (rev 98898)
+++ trunk/LayoutTests/editing/pasteboard/copy-null-characters-expected.txt	2011-10-31 23:09:12 UTC (rev 98899)
@@ -24,7 +24,6 @@
 |     <b>
 |       "bold"
 |   <div>
-|     contenteditable="true"
 |     id="source"
 |     "Copy paste me"
 |   <span>

Modified: trunk/LayoutTests/editing/pasteboard/paste-code-in-pre-expected.txt (98898 => 98899)


--- trunk/LayoutTests/editing/pasteboard/paste-code-in-pre-expected.txt	2011-10-31 23:08:28 UTC (rev 98898)
+++ trunk/LayoutTests/editing/pasteboard/paste-code-in-pre-expected.txt	2011-10-31 23:09:12 UTC (rev 98899)
@@ -3,7 +3,4 @@
 | <pre>
 |   contenteditable="true"
 |   id="pre"
-|   <pre>
-|     contenteditable="true"
-|     id="pre"
-|     "<input type='button'>foo<br>bar<b>baz</b><#selection-caret>"
+|   "<input type='button'>foo<br>bar<b>baz</b><#selection-caret>"

Modified: trunk/LayoutTests/editing/pasteboard/paste-pre-001-expected.txt (98898 => 98899)


--- trunk/LayoutTests/editing/pasteboard/paste-pre-001-expected.txt	2011-10-31 23:08:28 UTC (rev 98898)
+++ trunk/LayoutTests/editing/pasteboard/paste-pre-001-expected.txt	2011-10-31 23:09:12 UTC (rev 98899)
@@ -7,4 +7,4 @@
 foo
 bar
 execCutCommand: <div id="test" class="editing"> <pre><br></pre> </div>
-execPasteCommand: <div id="test" class="editing"> <pre><pre>foo bar</pre></pre> </div>
+execPasteCommand: <div id="test" class="editing"> <pre>foo bar</pre> </div>

Modified: trunk/LayoutTests/editing/pasteboard/paste-pre-002-expected.txt (98898 => 98899)


--- trunk/LayoutTests/editing/pasteboard/paste-pre-002-expected.txt	2011-10-31 23:08:28 UTC (rev 98898)
+++ trunk/LayoutTests/editing/pasteboard/paste-pre-002-expected.txt	2011-10-31 23:09:12 UTC (rev 98899)
@@ -2,4 +2,4 @@
 foo
 bar
 execCopyCommand: <div id="test" class="editing"> <pre>foo bar</pre> </div>
-execPasteCommand: <div id="test" class="editing"> <pre><pre>foo bar</pre></pre> </div>
+execPasteCommand: <div id="test" class="editing"> <pre>foo bar</pre> </div>

Modified: trunk/LayoutTests/editing/selection/4895428-4-expected.txt (98898 => 98899)


--- trunk/LayoutTests/editing/selection/4895428-4-expected.txt	2011-10-31 23:08:28 UTC (rev 98898)
+++ trunk/LayoutTests/editing/selection/4895428-4-expected.txt	2011-10-31 23:09:12 UTC (rev 98899)
@@ -19,7 +19,6 @@
 |   <#selection-anchor>
 |   <table>
 |     border="1"
-|     contenteditable="false"
 |     <tbody>
 |       <tr>
 |         <td>

Modified: trunk/Source/WebCore/ChangeLog (98898 => 98899)


--- trunk/Source/WebCore/ChangeLog	2011-10-31 23:08:28 UTC (rev 98898)
+++ trunk/Source/WebCore/ChangeLog	2011-10-31 23:09:12 UTC (rev 98899)
@@ -1,3 +1,28 @@
+2011-10-29  Ryosuke Niwa  <[email protected]>
+
+        WebKit nests pre on copy and paste when the pre is the root editable element
+        https://bugs.webkit.org/show_bug.cgi?id=70800
+
+        Reviewed by Darin Adler.
+
+        Fixed the bug by removing nested block elements in removeRedundantStylesAndKeepStyleSpanInline.
+
+        Tests: editing/pasteboard/contenteditable-pre-2.html
+               editing/pasteboard/contenteditable-pre.html
+
+        * editing/ApplyStyleCommand.cpp:
+        * editing/ReplaceSelectionCommand.cpp:
+        (WebCore::ReplaceSelectionCommand::removeRedundantStylesAndKeepStyleSpanInline): Remove block
+        elements if it's identical to its parent and there are no contents between the two. Also remove
+        contenteditable attribute from an element if the parent is already richly editable.
+        (WebCore::ReplaceSelectionCommand::doApply): Remove redundant styles after removing the placeholder
+        br so that the above check doesn't get affected by the placeholder.
+        * editing/htmlediting.cpp:
+        (WebCore::areIdenticalElements): Moved from ApplyStyleCommand.
+        (WebCore::isNonTableCellHTMLBlockElement): Moved from markup.cpp.
+        * editing/htmlediting.h:
+        * editing/markup.cpp:
+
 2011-10-31  Vineet Chaudhary  <[email protected]>
 
         text/plain form encoding ignored and incorrectly specified in request header.

Modified: trunk/Source/WebCore/editing/ApplyStyleCommand.cpp (98898 => 98899)


--- trunk/Source/WebCore/editing/ApplyStyleCommand.cpp	2011-10-31 23:08:28 UTC (rev 98898)
+++ trunk/Source/WebCore/editing/ApplyStyleCommand.cpp	2011-10-31 23:09:12 UTC (rev 98899)
@@ -1188,41 +1188,6 @@
     return offsetInText > caretMinOffset(node) && offsetInText < caretMaxOffset(node);
 }
 
-static bool areIdenticalElements(Node *first, Node *second)
-{
-    // check that tag name and all attribute names and values are identical
-
-    if (!first->isElementNode())
-        return false;
-    
-    if (!second->isElementNode())
-        return false;
-
-    Element *firstElement = static_cast<Element *>(first);
-    Element *secondElement = static_cast<Element *>(second);
-    
-    if (!firstElement->tagQName().matches(secondElement->tagQName()))
-        return false;
-
-    NamedNodeMap *firstMap = firstElement->attributes();
-    NamedNodeMap *secondMap = secondElement->attributes();
-
-    unsigned firstLength = firstMap->length();
-
-    if (firstLength != secondMap->length())
-        return false;
-
-    for (unsigned i = 0; i < firstLength; i++) {
-        Attribute *attribute = firstMap->attributeItem(i);
-        Attribute *secondAttribute = secondMap->getAttributeItem(attribute->name());
-
-        if (!secondAttribute || attribute->value() != secondAttribute->value())
-            return false;
-    }
-    
-    return true;
-}
-
 bool ApplyStyleCommand::mergeStartWithPreviousIfIdentical(const Position& start, const Position& end)
 {
     Node* startNode = start.containerNode();

Modified: trunk/Source/WebCore/editing/ReplaceSelectionCommand.cpp (98898 => 98899)


--- trunk/Source/WebCore/editing/ReplaceSelectionCommand.cpp	2011-10-31 23:08:28 UTC (rev 98898)
+++ trunk/Source/WebCore/editing/ReplaceSelectionCommand.cpp	2011-10-31 23:09:12 UTC (rev 98899)
@@ -512,11 +512,24 @@
             if (isStyleSpanOrSpanWithOnlyStyleAttribute(element)) {
                 insertedNodes.willRemoveNodePreservingChildren(element);
                 removeNodePreservingChildren(element);
+                continue;
             } else
                 removeNodeAttribute(element, styleAttr);
         } else if (newInlineStyle->style()->length() != inlineStyle->length())
             setNodeAttribute(element, styleAttr, newInlineStyle->style()->cssText());
 
+        // FIXME: Tolerate differences in id, class, and style attributes.
+        if (isNonTableCellHTMLBlockElement(element) && areIdenticalElements(element, element->parentNode())
+            && VisiblePosition(firstPositionInNode(element->parentNode())) == VisiblePosition(firstPositionInNode(element))
+            && VisiblePosition(lastPositionInNode(element->parentNode())) == VisiblePosition(lastPositionInNode(element))) {
+            insertedNodes.willRemoveNodePreservingChildren(element);
+            removeNodePreservingChildren(element);
+            continue;
+        }
+
+        if (element->parentNode()->rendererIsRichlyEditable())
+            removeNodeAttribute(element, contenteditableAttr);
+
         // WebKit used to not add display: inline and float: none on copy.
         // Keep this code around for backward compatibility
         if (isLegacyAppleStyleSpan(element)) {
@@ -976,8 +989,6 @@
         node = next;
     }
 
-    removeRedundantStylesAndKeepStyleSpanInline(insertedNodes);
-
     removeUnrenderedTextNodesAtEnds(insertedNodes);
 
     if (!handledStyleSpans)
@@ -1004,6 +1015,8 @@
         }
     }
 
+    removeRedundantStylesAndKeepStyleSpanInline(insertedNodes);
+
     // Setup m_startOfInsertedContent and m_endOfInsertedContent. This should be the last two lines of code that access insertedNodes.
     m_startOfInsertedContent = firstPositionInOrBeforeNode(insertedNodes.firstNodeInserted());
     m_endOfInsertedContent = lastPositionInOrAfterNode(insertedNodes.lastLeafInserted());

Modified: trunk/Source/WebCore/editing/htmlediting.cpp (98898 => 98899)


--- trunk/Source/WebCore/editing/htmlediting.cpp	2011-10-31 23:08:28 UTC (rev 98898)
+++ trunk/Source/WebCore/editing/htmlediting.cpp	2011-10-31 23:09:12 UTC (rev 98899)
@@ -1140,6 +1140,48 @@
     return renderer && ((renderer->isTable() && !renderer->isInline()) || (renderer->isImage() && !renderer->isInline()) || renderer->isHR());
 }
 
+bool areIdenticalElements(const Node* first, const Node* second)
+{
+    // check that tag name and all attribute names and values are identical
+
+    if (!first->isElementNode() || !second->isElementNode())
+        return false;
+
+    if (!toElement(first)->tagQName().matches(toElement(second)->tagQName()))
+        return false;
+
+    NamedNodeMap* firstMap = toElement(first)->attributes();
+    NamedNodeMap* secondMap = toElement(second)->attributes();
+    unsigned firstLength = firstMap->length();
+
+    if (firstLength != secondMap->length())
+        return false;
+
+    for (unsigned i = 0; i < firstLength; i++) {
+        Attribute* attribute = firstMap->attributeItem(i);
+        Attribute* secondAttribute = secondMap->getAttributeItem(attribute->name());
+        if (!secondAttribute || attribute->value() != secondAttribute->value())
+            return false;
+    }
+
+    return true;
+}
+
+bool isNonTableCellHTMLBlockElement(const Node* node)
+{
+    return node->hasTagName(listingTag)
+        || node->hasTagName(olTag)
+        || node->hasTagName(preTag)
+        || node->hasTagName(tableTag)
+        || node->hasTagName(ulTag)
+        || node->hasTagName(xmpTag)
+        || node->hasTagName(h1Tag)
+        || node->hasTagName(h2Tag)
+        || node->hasTagName(h3Tag)
+        || node->hasTagName(h4Tag)
+        || node->hasTagName(h5Tag);
+}
+
 PassRefPtr<Range> avoidIntersectionWithNode(const Range* range, Node* node)
 {
     if (!range)

Modified: trunk/Source/WebCore/editing/htmlediting.h (98898 => 98899)


--- trunk/Source/WebCore/editing/htmlediting.h	2011-10-31 23:08:28 UTC (rev 98898)
+++ trunk/Source/WebCore/editing/htmlediting.h	2011-10-31 23:09:12 UTC (rev 98899)
@@ -109,7 +109,8 @@
 bool isNodeRendered(const Node*);
 bool isNodeVisiblyContainedWithin(Node*, const Range*);
 bool isRenderedAsNonInlineTableImageOrHR(const Node*);
-    
+bool areIdenticalElements(const Node*, const Node*);
+bool isNonTableCellHTMLBlockElement(const Node*);
 TextDirection directionOfEnclosingBlock(const Position&);
 
 // -------------------------------------------------------------------------

Modified: trunk/Source/WebCore/editing/markup.cpp (98898 => 98899)


--- trunk/Source/WebCore/editing/markup.cpp	2011-10-31 23:08:28 UTC (rev 98898)
+++ trunk/Source/WebCore/editing/markup.cpp	2011-10-31 23:09:12 UTC (rev 98899)
@@ -427,21 +427,6 @@
     return lastClosed;
 }
 
-static bool isNonTableCellHTMLBlockElement(const Node* node)
-{
-    return node->hasTagName(listingTag)
-        || node->hasTagName(olTag)
-        || node->hasTagName(preTag)
-        || node->hasTagName(tableTag)
-        || node->hasTagName(ulTag)
-        || node->hasTagName(xmpTag)
-        || node->hasTagName(h1Tag)
-        || node->hasTagName(h2Tag)
-        || node->hasTagName(h3Tag)
-        || node->hasTagName(h4Tag)
-        || node->hasTagName(h5Tag);
-}
-
 static bool isHTMLBlockElement(const Node* node)
 {
     return node->hasTagName(tdTag)
@@ -449,9 +434,6 @@
         || isNonTableCellHTMLBlockElement(node);
 }
 
-// FIXME: Do we want to handle mail quotes here instead?
-// This is currently handled in highestAncestorToWrapMarkup but it might make more
-// sense to move that into here.
 static Node* ancestorToRetainStructureAndAppearanceForBlock(Node* commonAncestorBlock)
 {
     if (!commonAncestorBlock)
_______________________________________________
webkit-changes mailing list
[email protected]
http://lists.webkit.org/mailman/listinfo.cgi/webkit-changes

Reply via email to