Title: [92330] trunk
Revision
92330
Author
rn...@webkit.org
Date
2011-08-03 16:42:25 -0700 (Wed, 03 Aug 2011)

Log Message

select-all, copy, paste of specialAncestorElements (e.g. pre, h1, etc) nests the element inside itself
https://bugs.webkit.org/show_bug.cgi?id=26483

Reviewed by Enrica Casucci.

Source/WebCore: 

The bug was caused by WebKit serializing pre, h1, etc... to retain structure and appearance when copying
rich content and pasting did not remove such nodes wrapping the copied contents.

Fixed the bug by extending r81887 and r83322 to remove those elements from where WebKit pastes into.

Test: editing/pasteboard/copy-paste-text-in-h1.html

* editing/ReplaceSelectionCommand.cpp:
(WebCore::nodeHasAttributesToPreserve): Extracted from isInlineNodeWithStyle.
(WebCore::isInlineNodeWithStyle): Calls nodeHasAttributesToPreserve.
(WebCore::ReplaceSelectionCommand::doApply): Calls ancestorToRetainStructureAndAppearance.
Remove nodes copied by ancestorToRetainStructureAndAppearance at insertionPos before pasting the fragment.
* editing/markup.cpp:
(WebCore::ancestorToRetainStructureAndAppearance): Takes ShouldIncludeParagraphSeparators.
* editing/markup.h:

LayoutTests: 

* editing/pasteboard/5065605-expected.txt:
* editing/pasteboard/copy-paste-text-in-h1-expected.txt: Added.
* editing/pasteboard/copy-paste-text-in-h1.html: Added.
* editing/pasteboard/display-block-on-spans-expected.txt:
* editing/pasteboard/paste-pre-001-expected.txt:
* editing/pasteboard/paste-pre-002-expected.txt:
* editing/pasteboard/paste-text-011-expected.txt:

Modified Paths

Added Paths

Diff

Modified: trunk/LayoutTests/ChangeLog (92329 => 92330)


--- trunk/LayoutTests/ChangeLog	2011-08-03 23:35:03 UTC (rev 92329)
+++ trunk/LayoutTests/ChangeLog	2011-08-03 23:42:25 UTC (rev 92330)
@@ -1,3 +1,18 @@
+2011-08-03  Ryosuke Niwa  <rn...@webkit.org>
+
+        select-all, copy, paste of specialAncestorElements (e.g. pre, h1, etc) nests the element inside itself
+        https://bugs.webkit.org/show_bug.cgi?id=26483
+
+        Reviewed by Enrica Casucci.
+
+        * editing/pasteboard/5065605-expected.txt:
+        * editing/pasteboard/copy-paste-text-in-h1-expected.txt: Added.
+        * editing/pasteboard/copy-paste-text-in-h1.html: Added.
+        * editing/pasteboard/display-block-on-spans-expected.txt:
+        * editing/pasteboard/paste-pre-001-expected.txt:
+        * editing/pasteboard/paste-pre-002-expected.txt:
+        * editing/pasteboard/paste-text-011-expected.txt:
+
 2011-08-03  Mark Pilgrim  <pilg...@chromium.org>
 
         Remove LegacyDefaultOptionalArguments flag from HTML DOM IDL files

Modified: trunk/LayoutTests/editing/pasteboard/5065605-expected.txt (92329 => 92330)


--- trunk/LayoutTests/editing/pasteboard/5065605-expected.txt	2011-08-03 23:35:03 UTC (rev 92329)
+++ trunk/LayoutTests/editing/pasteboard/5065605-expected.txt	2011-08-03 23:42:25 UTC (rev 92330)
@@ -21,7 +21,7 @@
 EDITING DELEGATE: webViewDidChange:WebViewDidChangeNotification
 EDITING DELEGATE: shouldInsertNode:#document-fragment replacingDOMRange:range from 0 of DIV > DIV > BODY > HTML > #document to 0 of DIV > DIV > BODY > HTML > #document givenAction:WebViewInsertActionPasted
 EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
-EDITING DELEGATE: shouldChangeSelectedDOMRange:(null) toDOMRange:range from 24 of #text > FONT > DIV > SPAN > FONT > DIV > DIV > BODY > HTML > #document to 24 of #text > FONT > DIV > SPAN > FONT > DIV > DIV > BODY > HTML > #document affinity:NSSelectionAffinityDownstream stillSelecting:FALSE
+EDITING DELEGATE: shouldChangeSelectedDOMRange:(null) toDOMRange:range from 24 of #text > FONT > DIV > DIV > BODY > HTML > #document to 24 of #text > FONT > DIV > DIV > BODY > HTML > #document affinity:NSSelectionAffinityDownstream stillSelecting:FALSE
 EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
 EDITING DELEGATE: webViewDidChange:WebViewDidChangeNotification
 EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
@@ -36,19 +36,12 @@
 |     class="Apple-style-span"
 |     color="#ff0000"
 |     "This text should be red."
+| <font>
+|   class="Apple-style-span"
+|   color="#ff0000"
+|   "This text should be red."
 | <div>
 |   <font>
 |     class="Apple-style-span"
 |     color="#ff0000"
-|     <span>
-|       class="Apple-style-span"
-|       style="color: rgb(0, 0, 0); "
-|       <font>
-|         class="Apple-style-span"
-|         color="#ff0000"
-|         "This text should be red."
-|       <div>
-|         <font>
-|           class="Apple-style-span"
-|           color="#ff0000"
-|           "This text should be red.<#selection-caret>"
+|     "This text should be red.<#selection-caret>"

Added: trunk/LayoutTests/editing/pasteboard/copy-paste-text-in-h1-expected.txt (0 => 92330)


--- trunk/LayoutTests/editing/pasteboard/copy-paste-text-in-h1-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/editing/pasteboard/copy-paste-text-in-h1-expected.txt	2011-08-03 23:42:25 UTC (rev 92330)
@@ -0,0 +1,11 @@
+This tests copying and pasting text in h1. WebKit should not nest h1s.
+To manually test, copy and paste "hello world". You should see single border around "hello world".
+| "
+"
+| <h1>
+|   "hello"
+|   " "
+|   <em>
+|     "world<#selection-caret>"
+| "
+"

Added: trunk/LayoutTests/editing/pasteboard/copy-paste-text-in-h1.html (0 => 92330)


--- trunk/LayoutTests/editing/pasteboard/copy-paste-text-in-h1.html	                        (rev 0)
+++ trunk/LayoutTests/editing/pasteboard/copy-paste-text-in-h1.html	2011-08-03 23:42:25 UTC (rev 92330)
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<html>
+<head>
+<style>
+h1 { border: solid 1px black; padding: 5px; }
+</style>
+</head>
+<body>
+<p id="description">This tests copying and pasting text in h1. WebKit should not nest h1s.
+To manually test, copy and paste "hello world". You should see single border around "hello world".</p>
+<div id="test" contenteditable>
+<h1>hello <em>world</em></h1>
+</div>
+<script src=""
+<script>
+
+var test = document.getElementById('test');
+test.focus();
+
+Markup.description(document.getElementById('description').textContent);
+
+document.execCommand('SelectAll', false, null);
+document.execCommand('Copy', false, null);
+document.execCommand('Paste', false, null);
+
+if (window.layoutTestController)
+    Markup.dump(test);
+else
+    Markup.noAutoDump();
+
+</script>
+</body>
+</html>

Modified: trunk/LayoutTests/editing/pasteboard/display-block-on-spans-expected.txt (92329 => 92330)


--- trunk/LayoutTests/editing/pasteboard/display-block-on-spans-expected.txt	2011-08-03 23:35:03 UTC (rev 92329)
+++ trunk/LayoutTests/editing/pasteboard/display-block-on-spans-expected.txt	2011-08-03 23:42:25 UTC (rev 92330)
@@ -29,8 +29,11 @@
 | <span>
 |   style="display:block"
 |   <b>
-|     "This<#selection-caret>"
-|   <b>
+|     <span>
+|       class="Apple-style-span"
+|       style="font-weight: normal; "
+|       <b>
+|         "This<#selection-caret>"
 |     " is another paragraph."
 |   <br>
 | "

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


--- trunk/LayoutTests/editing/pasteboard/paste-pre-001-expected.txt	2011-08-03 23:35:03 UTC (rev 92329)
+++ trunk/LayoutTests/editing/pasteboard/paste-pre-001-expected.txt	2011-08-03 23:42:25 UTC (rev 92330)
@@ -7,4 +7,4 @@
 foo
 bar
 execCutCommand: <div id="test" class="editing"> <pre><br></pre> </div>
-execPasteCommand: <div id="test" class="editing"> <pre><span class="Apple-style-span" style="font-family: Times; white-space: normal; "><pre>foo bar</pre></span></pre> </div>
+execPasteCommand: <div id="test" class="editing"> <pre>foo bar</pre> </div>

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


--- trunk/LayoutTests/editing/pasteboard/paste-pre-002-expected.txt	2011-08-03 23:35:03 UTC (rev 92329)
+++ trunk/LayoutTests/editing/pasteboard/paste-pre-002-expected.txt	2011-08-03 23:42:25 UTC (rev 92330)
@@ -2,4 +2,4 @@
 foo
 bar
 execCopyCommand: <div id="test" class="editing"> <pre>foo bar</pre> </div>
-execPasteCommand: <div id="test" class="editing"> <pre><span class="Apple-style-span" style="font-family: Times; white-space: normal; "><pre>foo bar</pre></span></pre> </div>
+execPasteCommand: <div id="test" class="editing"> <pre>foo bar</pre> </div>

Modified: trunk/LayoutTests/editing/pasteboard/paste-text-011-expected.txt (92329 => 92330)


--- trunk/LayoutTests/editing/pasteboard/paste-text-011-expected.txt	2011-08-03 23:35:03 UTC (rev 92329)
+++ trunk/LayoutTests/editing/pasteboard/paste-text-011-expected.txt	2011-08-03 23:42:25 UTC (rev 92330)
@@ -6,7 +6,7 @@
 EDITING DELEGATE: webViewDidChange:WebViewDidChangeNotification
 EDITING DELEGATE: shouldInsertNode:#document-fragment replacingDOMRange:range from 0 of P > BODY > HTML > #document to 0 of P > BODY > HTML > #document givenAction:WebViewInsertActionPasted
 EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
-EDITING DELEGATE: shouldChangeSelectedDOMRange:(null) toDOMRange:range from 5 of #text > B > FONT > P > SPAN > B > FONT > P > BODY > HTML > #document to 5 of #text > B > FONT > P > SPAN > B > FONT > P > BODY > HTML > #document affinity:NSSelectionAffinityDownstream stillSelecting:FALSE
+EDITING DELEGATE: shouldChangeSelectedDOMRange:(null) toDOMRange:range from 5 of #text > B > FONT > P > BODY > HTML > #document to 5 of #text > B > FONT > P > BODY > HTML > #document affinity:NSSelectionAffinityDownstream stillSelecting:FALSE
 EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
 EDITING DELEGATE: webViewDidChange:WebViewDidChangeNotification
 EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
@@ -30,19 +30,12 @@
 |   <font>
 |     face="Monaco"
 |     <b>
-|       <span>
-|         class="Apple-style-span"
-|         style="font-family: Times; font-weight: normal; "
-|         <p>
-|           <font>
-|             face="Monaco"
-|             <b>
-|               "hello"
-|         <p>
-|           <font>
-|             face="Monaco"
-|             <b>
-|               "there<#selection-caret>"
+|       "hello"
+| <p>
+|   <font>
+|     face="Monaco"
+|     <b>
+|       "there<#selection-caret>"
 | "
 
 

Modified: trunk/Source/WebCore/ChangeLog (92329 => 92330)


--- trunk/Source/WebCore/ChangeLog	2011-08-03 23:35:03 UTC (rev 92329)
+++ trunk/Source/WebCore/ChangeLog	2011-08-03 23:42:25 UTC (rev 92330)
@@ -1,3 +1,26 @@
+2011-08-03  Ryosuke Niwa  <rn...@webkit.org>
+
+        select-all, copy, paste of specialAncestorElements (e.g. pre, h1, etc) nests the element inside itself
+        https://bugs.webkit.org/show_bug.cgi?id=26483
+
+        Reviewed by Enrica Casucci.
+
+        The bug was caused by WebKit serializing pre, h1, etc... to retain structure and appearance when copying
+        rich content and pasting did not remove such nodes wrapping the copied contents.
+
+        Fixed the bug by extending r81887 and r83322 to remove those elements from where WebKit pastes into.
+
+        Test: editing/pasteboard/copy-paste-text-in-h1.html
+
+        * editing/ReplaceSelectionCommand.cpp:
+        (WebCore::nodeHasAttributesToPreserve): Extracted from isInlineNodeWithStyle.
+        (WebCore::isInlineNodeWithStyle): Calls nodeHasAttributesToPreserve.
+        (WebCore::ReplaceSelectionCommand::doApply): Calls ancestorToRetainStructureAndAppearance.
+        Remove nodes copied by ancestorToRetainStructureAndAppearance at insertionPos before pasting the fragment.
+        * editing/markup.cpp:
+        (WebCore::ancestorToRetainStructureAndAppearance): Takes ShouldIncludeParagraphSeparators.
+        * editing/markup.h:
+
 2011-08-03  Mark Pilgrim  <pilg...@chromium.org>
 
         Remove LegacyDefaultOptionalArguments flag from Console.idl

Modified: trunk/Source/WebCore/editing/ReplaceSelectionCommand.cpp (92329 => 92330)


--- trunk/Source/WebCore/editing/ReplaceSelectionCommand.cpp	2011-08-03 23:35:03 UTC (rev 92329)
+++ trunk/Source/WebCore/editing/ReplaceSelectionCommand.cpp	2011-08-03 23:42:25 UTC (rev 92330)
@@ -760,6 +760,12 @@
     return node;
 }
 
+static bool nodeHasAttributesToPreserve(const HTMLElement* element)
+{
+    const NamedNodeMap* attributeMap = element->attributeMap();
+    return attributeMap && !attributeMap->isEmpty() && (attributeMap->length() > 1 || !element->hasAttribute(styleAttr));
+}
+
 static bool isInlineNodeWithStyle(const Node* node)
 {
     // We don't want to skip over any block elements.
@@ -781,11 +787,7 @@
 
     // We can skip inline elements that don't have attributes or whose only
     // attribute is the style attribute.
-    const NamedNodeMap* attributeMap = element->attributeMap();
-    if (!attributeMap || attributeMap->isEmpty() || (attributeMap->length() == 1 && element->hasAttribute(styleAttr)))
-        return true;
-
-    return false;
+    return !nodeHasAttributesToPreserve(static_cast<const HTMLElement*>(node));
 }
     
 void ReplaceSelectionCommand::doApply()
@@ -937,16 +939,21 @@
     // We can skip this optimization for fragments not wrapped in one of
     // our style spans and for positions inside list items
     // since insertAsListItems already does the right thing.
-    if (!m_matchStyle && !enclosingList(insertionPos.containerNode()) && isStyleSpan(fragment.firstChild())) {
+    if (!m_matchStyle && !enclosingList(insertionPos.containerNode()) && isStyleSpan(fragment.firstChild())
+        && VisiblePosition(firstPositionInNode(insertionPos.containerNode())) == VisiblePosition(lastPositionInNode(insertionPos.containerNode()))) {
         if (insertionPos.containerNode()->isTextNode() && insertionPos.offsetInContainerNode() && !insertionPos.atLastEditingPositionForNode()) {
             splitTextNodeContainingElement(insertionPos.containerText(), insertionPos.offsetInContainerNode());
             insertionPos = firstPositionInNode(insertionPos.containerNode());
         }
 
-        // FIXME: isInlineNodeWithStyle does not check editability.
-        if (RefPtr<Node> nodeToSplitTo = highestEnclosingNodeOfType(insertionPos, isInlineNodeWithStyle)) {
-            if (insertionPos.containerNode() != nodeToSplitTo) {
-                nodeToSplitTo = splitTreeToNode(insertionPos.anchorNode(), nodeToSplitTo.get(), true).get();
+        RefPtr<Node> nodeToSplitTo = highestEnclosingNodeOfType(insertionPos, isInlineNodeWithStyle);
+        if (HTMLElement* ancestor = ancestorToRetainStructureAndAppearance(nodeToSplitTo ? nodeToSplitTo.get() : insertionPos.containerNode(), IncludeParagraphSeparators)) {
+            if (ancestor->parentNode() && unsplittableElementForPosition(insertionPos)->contains(ancestor->parentNode()) && !nodeHasAttributesToPreserve(ancestor))
+                nodeToSplitTo = ancestor;
+        }
+        if (nodeToSplitTo) {
+            if (insertionPos.containerNode() != nodeToSplitTo->parentNode()) {
+                nodeToSplitTo = splitTreeToNode(insertionPos.anchorNode(), nodeToSplitTo->parentNode()).get();
                 insertionPos = positionInParentBeforeNode(nodeToSplitTo.get());
             }
         }

Modified: trunk/Source/WebCore/editing/markup.cpp (92329 => 92330)


--- trunk/Source/WebCore/editing/markup.cpp	2011-08-03 23:35:03 UTC (rev 92329)
+++ trunk/Source/WebCore/editing/markup.cpp	2011-08-03 23:42:25 UTC (rev 92330)
@@ -350,7 +350,7 @@
     return lastClosed;
 }
 
-static Node* ancestorToRetainStructureAndAppearance(Node* commonAncestor)
+HTMLElement* ancestorToRetainStructureAndAppearance(Node* commonAncestor, ShouldIncludeParagraphSeparators shouldIncludeParagraphSeparators)
 {
     Node* commonAncestorBlock = enclosingBlock(commonAncestor);
 
@@ -362,7 +362,7 @@
         while (table && !table->hasTagName(tableTag))
             table = table->parentNode();
 
-        return table;
+        return toHTMLElement(table);
     }
 
     if (commonAncestorBlock->hasTagName(listingTag)
@@ -376,8 +376,12 @@
         || commonAncestorBlock->hasTagName(h3Tag)
         || commonAncestorBlock->hasTagName(h4Tag)
         || commonAncestorBlock->hasTagName(h5Tag))
-        return commonAncestorBlock;
+        return toHTMLElement(commonAncestorBlock);
 
+    if (shouldIncludeParagraphSeparators == IncludeParagraphSeparators
+        && (commonAncestorBlock->hasTagName(pTag) || commonAncestorBlock->hasTagName(divTag)))
+        return toHTMLElement(commonAncestorBlock);
+
     return 0;
 }
 

Modified: trunk/Source/WebCore/editing/markup.h (92329 => 92330)


--- trunk/Source/WebCore/editing/markup.h	2011-08-03 23:35:03 UTC (rev 92329)
+++ trunk/Source/WebCore/editing/markup.h	2011-08-03 23:42:25 UTC (rev 92330)
@@ -33,31 +33,35 @@
 
 namespace WebCore {
 
-    class Document;
-    class DocumentFragment;
-    class Element;
-    class KURL;
-    class Node;
-    class Range;
+class Document;
+class DocumentFragment;
+class Element;
+class HTMLElement;
+class KURL;
+class Node;
+class Range;
 
-    enum EChildrenOnly { IncludeNode, ChildrenOnly };
-    enum EAbsoluteURLs { DoNotResolveURLs, AbsoluteURLs };
+enum EChildrenOnly { IncludeNode, ChildrenOnly };
+enum EAbsoluteURLs { DoNotResolveURLs, AbsoluteURLs };
 
-    PassRefPtr<DocumentFragment> createFragmentFromText(Range* context, const String& text);
-    PassRefPtr<DocumentFragment> createFragmentFromMarkup(Document*, const String& markup, const String& baseURL, FragmentScriptingPermission = FragmentScriptingAllowed);
-    PassRefPtr<DocumentFragment> createFragmentFromNodes(Document*, const Vector<Node*>&);
+PassRefPtr<DocumentFragment> createFragmentFromText(Range* context, const String& text);
+PassRefPtr<DocumentFragment> createFragmentFromMarkup(Document*, const String& markup, const String& baseURL, FragmentScriptingPermission = FragmentScriptingAllowed);
+PassRefPtr<DocumentFragment> createFragmentFromNodes(Document*, const Vector<Node*>&);
 
-    bool isPlainTextMarkup(Node *node);
+bool isPlainTextMarkup(Node*);
 
-    String createMarkup(const Range*,
-        Vector<Node*>* = 0, EAnnotateForInterchange = DoNotAnnotateForInterchange, bool convertBlocksToInlines = false, EAbsoluteURLs = DoNotResolveURLs);
-    String createMarkup(const Node*, EChildrenOnly = IncludeNode, Vector<Node*>* = 0, EAbsoluteURLs = DoNotResolveURLs);
-    
-    String createFullMarkup(const Node*);
-    String createFullMarkup(const Range*);
+String createMarkup(const Range*, Vector<Node*>* = 0, EAnnotateForInterchange = DoNotAnnotateForInterchange, bool convertBlocksToInlines = false, EAbsoluteURLs = DoNotResolveURLs);
+String createMarkup(const Node*, EChildrenOnly = IncludeNode, Vector<Node*>* = 0, EAbsoluteURLs = DoNotResolveURLs);
 
-    String urlToMarkup(const KURL&, const String& title);
-    String imageToMarkup(const KURL&, Element*);
+String createFullMarkup(const Node*);
+String createFullMarkup(const Range*);
+
+String urlToMarkup(const KURL&, const String& title);
+String imageToMarkup(const KURL&, Element*);
+
+enum ShouldIncludeParagraphSeparators { DoNotIncludeParagraphSeparators, IncludeParagraphSeparators };
+HTMLElement* ancestorToRetainStructureAndAppearance(Node*, ShouldIncludeParagraphSeparators = DoNotIncludeParagraphSeparators);
+
 }
 
 #endif // markup_h
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
http://lists.webkit.org/mailman/listinfo.cgi/webkit-changes

Reply via email to