Changeset 92823 in webkit


Ignore:
Timestamp:
Aug 10, 2011 11:29:18 PM (13 years ago)
Author:
rniwa@webkit.org
Message:

Copying can result in span around block elements on the clipboard
https://bugs.webkit.org/show_bug.cgi?id=34564

Reviewed by Tony Chang.

Source/WebCore:

Completely overhauled the way WebKit preserves style in copy and paste. Instead of wrapping the entire
serialized contents by a Apple style span, WebKit now adds inline style to the top level elements,
wrap top level text nodes by a style span.

  • editing/EditingStyle.cpp:

(WebCore::EditingStyle::collapseTextDecorationProperties): Remove text-decoration property when the value
of -webkit-text-decorations-in-effect is none.
(WebCore::EditingStyle::removeStyleFromRulesAndContext): Since display: inline and float: none are now
added on copy, remove these properties on paste.
(WebCore::EditingStyle::removePropertiesInElementDefaultStyle): Takes Element* instead of StyledElement*.
(WebCore::EditingStyle::forceInline): Added.
(WebCore::getPropertiesNotIn): Remove properties only when the base style has them.

  • editing/EditingStyle.h:
  • editing/markup.cpp:

(WebCore::StyledMarkupAccumulator::shouldApplyWrappingStyle): Added.
(WebCore::StyledMarkupAccumulator::StyledMarkupAccumulator): Takes highestNodeToBeSerialized.
(WebCore::StyledMarkupAccumulator::wrapWithStyleNode): Calls appendStyleNodeOpenTag and styleNodeCloseTag.
(WebCore::StyledMarkupAccumulator::appendStyleNodeOpenTag): Extracted from wrapWithStyleNode.
(WebCore::StyledMarkupAccumulator::styleNodeCloseTag): Ditto.
(WebCore::StyledMarkupAccumulator::appendText): Wraps text node with a style span if needed.
Set display: inline and float: none so that it won't be converted to a block on paste side.
(WebCore::StyledMarkupAccumulator::appendElement): Add wrapping style if appropriate; Remove any properties
that are overridden by default style and any style that may conflict with the computed style of node to
avoid modifying the appearance of the serialized nodes.
(WebCore::StyledMarkupAccumulator::serializeNodes): Compute wrapping style; copies of this style are
modified as needed when serializing top-level elements or text nodes. We call traverseNodesForSerialization
with NodeTraversalMode set to DoNotEmitString first to compute the highest node to be serialized. The second
call to the function actually serialize the nodes.
(WebCore::StyledMarkupAccumulator::traverseNodesForSerialization): Extracted from serializeNodes.
Outputs string only if NodeTraversalMode is set to EmitString.
(WebCore::createMarkup): No longer adds wrapping spans.

LayoutTests:

This patch overhauled the way we preserve styles when copying and pasting HTML contents. Many tests progressed
and lost wrapping spans while others are observing different markup on the clipboard as expected.

  • editing/deleting/deleting-line-break-preserves-underline-color-expected.txt: span's style attribute now has

a trailing space.

  • editing/execCommand/insert-list-with-noneditable-content-expected.txt: LightGray is now rgb(211, 211, 211).
  • editing/pasteboard/data-transfer-items-expected.txt: Different serialization.
  • editing/pasteboard/onpaste-text-html-expected.txt: Ditto.
  • editing/pasteboard/paste-4039777-fix-expected.txt: No longer nests ul erroneously or aligns the inner list

to the right.

  • editing/pasteboard/paste-code-in-pre-expected.txt: No longer produces erroneous wrapping span.
  • editing/pasteboard/paste-pre-001-expected.txt: Ditto.
  • editing/pasteboard/paste-pre-002-expected.txt: Ditto.
  • editing/pasteboard/paste-text-012-expected.txt: Ditto.
  • editing/pasteboard/paste-text-with-style-4-expected.txt: Ditto.
  • fast/events/ondrop-text-html-expected.txt: Different serialization.
  • platform/chromium-win/editing/pasteboard/paste-code-in-pre-expected.txt: Removed.
  • platform/chromium-win/editing/pasteboard/paste-pre-001-expected.txt: Removed.
  • platform/chromium-win/editing/pasteboard/paste-pre-002-expected.txt: Removed.
  • platform/qt-mac/editing/pasteboard/paste-code-in-pre-expected.txt: Removed.
Location:
trunk
Files:
4 deleted
16 edited

Legend:

Unmodified
Added
Removed
  • trunk/LayoutTests/ChangeLog

    r92822 r92823  
     12011-08-10  Ryosuke Niwa  <rniwa@webkit.org>
     2
     3        Copying can result in span around block elements on the clipboard
     4        https://bugs.webkit.org/show_bug.cgi?id=34564
     5
     6        Reviewed by Tony Chang.
     7
     8        This patch overhauled the way we preserve styles when copying and pasting HTML contents. Many tests progressed
     9        and lost wrapping spans while others are observing different markup on the clipboard as expected.
     10
     11        * editing/deleting/deleting-line-break-preserves-underline-color-expected.txt: span's style attribute now has
     12        a trailing space.
     13        * editing/execCommand/insert-list-with-noneditable-content-expected.txt: LightGray is now rgb(211, 211, 211).
     14        * editing/pasteboard/data-transfer-items-expected.txt: Different serialization.
     15        * editing/pasteboard/onpaste-text-html-expected.txt: Ditto.
     16        * editing/pasteboard/paste-4039777-fix-expected.txt: No longer nests ul erroneously or aligns the inner list
     17        to the right.
     18        * editing/pasteboard/paste-code-in-pre-expected.txt: No longer produces erroneous wrapping span.
     19        * editing/pasteboard/paste-pre-001-expected.txt: Ditto.
     20        * editing/pasteboard/paste-pre-002-expected.txt: Ditto.
     21        * editing/pasteboard/paste-text-012-expected.txt: Ditto.
     22        * editing/pasteboard/paste-text-with-style-4-expected.txt: Ditto.
     23        * fast/events/ondrop-text-html-expected.txt: Different serialization.
     24        * platform/chromium-win/editing/pasteboard/paste-code-in-pre-expected.txt: Removed.
     25        * platform/chromium-win/editing/pasteboard/paste-pre-001-expected.txt: Removed.
     26        * platform/chromium-win/editing/pasteboard/paste-pre-002-expected.txt: Removed.
     27        * platform/qt-mac/editing/pasteboard/paste-code-in-pre-expected.txt: Removed.
     28
    1292011-08-10  Ryosuke Niwa  <rniwa@webkit.org>
    230
  • trunk/LayoutTests/editing/deleting/deleting-line-break-preserves-underline-color-expected.txt

    r86619 r92823  
    1414|   "This should not be underlined.<#selection-caret>"
    1515|   <span>
    16 |     style="text-decoration: underline; color: blue;"
     16|     style="text-decoration: underline; color: blue; "
    1717|     <span>
    1818|       style="color:red;"
  • trunk/LayoutTests/editing/execCommand/insert-list-with-noneditable-content-expected.txt

    r80780 r92823  
    55|     <span>
    66|       contenteditable="false"
    7 |       style="background-color: LightGray;"
     7|       style="background-color: rgb(211, 211, 211); "
    88|       "non-editable"
    99|     " in the middle"
  • trunk/LayoutTests/editing/pasteboard/data-transfer-items-expected.txt

    r87466 r92823  
    2121copy: items[0] value: Hello World!
    2222copy: items[1] value: <b>Hello World!
    23 paste: items[0] value: <span class="Apple-style-span" style="color: rgb(0, 0, 0); font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: normal; orphans: 2; text-align: -webkit-auto; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; -webkit-text-decorations-in-effect: none; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px; font-size: medium; ">This file tests the basic functionality and properties of DataTransferItems. This test requires DRT.</span>
     23paste: items[0] value: <span class="Apple-style-span" style="color: rgb(0, 0, 0); font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: normal; orphans: 2; text-align: -webkit-auto; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px; font-size: medium; display: inline !important; float: none; ">This file tests the basic functionality and properties of DataTransferItems. This test requires DRT.</span>
    2424paste: items[1] value: This file tests the basic functionality and properties of DataTransferItems. This test requires DRT.
    2525
  • trunk/LayoutTests/editing/pasteboard/onpaste-text-html-expected.txt

    r87466 r92823  
    11CONSOLE MESSAGE: line 21: text/plain: This test verifies that we can get text/html from the clipboard during an onpaste event.
    2 CONSOLE MESSAGE: line 23: text/html: <span class="Apple-style-span" style="color: rgb(0, 0, 0);  font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: normal; orphans: 2; text-align: -webkit-auto; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; -webkit-text-decorations-in-effect: none; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px; font-size: medium; ">This test verifies that we can get text/html from the clipboard during an onpaste event.<span class="Apple-converted-space"> </span></span>
     2CONSOLE MESSAGE: line 23: text/html: <span class="Apple-style-span" style="color: rgb(0, 0, 0);  font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: normal; orphans: 2; text-align: -webkit-auto; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px; font-size: medium; display: inline !important; float: none; ">This test verifies that we can get text/html from the clipboard during an onpaste event.<span class="Apple-converted-space"> </span></span>
    33This test verifies that we can get text/html from the clipboard during an onpaste event. This test requires DRT.
    44Paste content in this div.This test verifies that we can get text/html from the clipboard during an onpaste event. 
  • trunk/LayoutTests/editing/pasteboard/paste-4039777-fix-expected.txt

    r86983 r92823  
    5252| <ul>
    5353|   style="text-align:right;"
    54 |   <ul>
    55 |     style="text-align: right; "
    56 |     <li>
    57 |       "A"
     54|   <li>
     55|     "A"
    5856|   <div>
     57|     style="text-align: -webkit-auto; "
    5958|     <ul>
    6059|       <li>
  • trunk/LayoutTests/editing/pasteboard/paste-code-in-pre-expected.txt

    r87468 r92823  
    44|   contenteditable="true"
    55|   id="pre"
    6 |   <span>
    7 |     class="Apple-style-span"
    8 |     style="font-family: Times; white-space: normal; "
    9 |     <pre>
    10 |       contenteditable="true"
    11 |       id="pre"
    12 |       "<input type='button'>foo<br>bar<b>baz</b><#selection-caret>"
     6|   <pre>
     7|     contenteditable="true"
     8|     id="pre"
     9|     "<input type='button'>foo<br>bar<b>baz</b><#selection-caret>"
  • trunk/LayoutTests/editing/pasteboard/paste-pre-001-expected.txt

    r92537 r92823  
    88bar
    99execCutCommand: <div id="test" class="editing"> <pre><br></pre> </div>
    10 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>
     10execPasteCommand: <div id="test" class="editing"> <pre><pre>foo bar</pre></pre> </div>
  • trunk/LayoutTests/editing/pasteboard/paste-pre-002-expected.txt

    r92537 r92823  
    33bar
    44execCopyCommand: <div id="test" class="editing"> <pre>foo bar</pre> </div>
    5 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>
     5execPasteCommand: <div id="test" class="editing"> <pre><pre>foo bar</pre></pre> </div>
  • trunk/LayoutTests/editing/pasteboard/paste-text-012-expected.txt

    r87400 r92823  
    77
    88execCopyCommand: <div id="test" class="editing"><div><blockquote>foo</blockquote></div></div> <div class="editing"></div>
    9 execPasteCommand: <div id="test" class="editing"><div><blockquote>foo</blockquote></div></div> <div class="editing"><span class="Apple-style-span" style="font-size: medium; "><div id="test" class="editing"><div><blockquote>foo</blockquote></div><div><br></div></div></span></div>
     9execPasteCommand: <div id="test" class="editing"><div><blockquote>foo</blockquote></div></div> <div class="editing"><div id="test" class="editing"><div><blockquote>foo</blockquote></div><div><br></div></div></div>
  • trunk/LayoutTests/editing/pasteboard/paste-text-with-style-4-expected.txt

    r92695 r92823  
    1010| <b>
    1111|   style="border: solid 5px blue;padding: 5px;"
    12 |   <span>
    13 |     class="Apple-style-span"
     12|   <i>
    1413|     style="font-weight: normal; "
    15 |     <i>
    16 |       "hello<#selection-caret>"
     14|     "hello<#selection-caret>"
    1715|   <br>
  • trunk/LayoutTests/fast/events/ondrop-text-html-expected.txt

    r87466 r92823  
    11CONSOLE MESSAGE: line 21: text/plain: This test verifies that we can get text/html from the drag object during an ondrop event.
    2 CONSOLE MESSAGE: line 23: text/html: <span class="Apple-style-span" style="color: rgb(0, 0, 0);  font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: normal; orphans: 2; text-align: -webkit-auto; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; -webkit-text-decorations-in-effect: none; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px; font-size: medium; ">This test verifies that we can get text/html from the drag object during an ondrop event.<span class="Apple-converted-space"> </span></span>
     2CONSOLE MESSAGE: line 23: text/html: <span class="Apple-style-span" style="color: rgb(0, 0, 0);  font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: normal; orphans: 2; text-align: -webkit-auto; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px; font-size: medium; display: inline !important; float: none; ">This test verifies that we can get text/html from the drag object during an ondrop event.<span class="Apple-converted-space"> </span></span>
    33This test verifies that we can get text/html from the drag object during an ondrop event. This test requires DRT.
    44PASS
  • trunk/Source/WebCore/ChangeLog

    r92816 r92823  
     12011-08-10  Ryosuke Niwa  <rniwa@webkit.org>
     2
     3        Copying can result in span around block elements on the clipboard
     4        https://bugs.webkit.org/show_bug.cgi?id=34564
     5
     6        Reviewed by Tony Chang.
     7
     8        Completely overhauled the way WebKit preserves style in copy and paste. Instead of wrapping the entire
     9        serialized contents by a Apple style span, WebKit now adds inline style to the top level elements,
     10        wrap top level text nodes by a style span.
     11
     12        * editing/EditingStyle.cpp:
     13        (WebCore::EditingStyle::collapseTextDecorationProperties): Remove text-decoration property when the value
     14        of -webkit-text-decorations-in-effect is none.
     15        (WebCore::EditingStyle::removeStyleFromRulesAndContext): Since display: inline and float: none are now
     16        added on copy, remove these properties on paste.
     17        (WebCore::EditingStyle::removePropertiesInElementDefaultStyle): Takes Element* instead of StyledElement*.
     18        (WebCore::EditingStyle::forceInline): Added.
     19        (WebCore::getPropertiesNotIn): Remove properties only when the base style has them.
     20        * editing/EditingStyle.h:
     21        * editing/markup.cpp:
     22        (WebCore::StyledMarkupAccumulator::shouldApplyWrappingStyle): Added.
     23        (WebCore::StyledMarkupAccumulator::StyledMarkupAccumulator): Takes highestNodeToBeSerialized.
     24        (WebCore::StyledMarkupAccumulator::wrapWithStyleNode): Calls appendStyleNodeOpenTag and styleNodeCloseTag.
     25        (WebCore::StyledMarkupAccumulator::appendStyleNodeOpenTag): Extracted from wrapWithStyleNode.
     26        (WebCore::StyledMarkupAccumulator::styleNodeCloseTag): Ditto.
     27        (WebCore::StyledMarkupAccumulator::appendText): Wraps text node with a style span if needed.
     28        Set display: inline and float: none so that it won't be converted to a block on paste side.
     29        (WebCore::StyledMarkupAccumulator::appendElement): Add wrapping style if appropriate; Remove any properties
     30        that are overridden by default style and any style that may conflict with the computed style of node to
     31        avoid modifying the appearance of the serialized nodes.
     32        (WebCore::StyledMarkupAccumulator::serializeNodes): Compute wrapping style; copies of this style are
     33        modified as needed when serializing top-level elements or text nodes. We call traverseNodesForSerialization
     34        with NodeTraversalMode set to DoNotEmitString first to compute the highest node to be serialized. The second
     35        call to the function actually serialize the nodes.
     36        (WebCore::StyledMarkupAccumulator::traverseNodesForSerialization): Extracted from serializeNodes.
     37        Outputs string only if NodeTraversalMode is set to EmitString.
     38        (WebCore::createMarkup): No longer adds wrapping spans.
     39
    1402011-08-10  Adam Barth  <abarth@webkit.org>
    241
  • trunk/Source/WebCore/editing/EditingStyle.cpp

    r92695 r92823  
    558558        return;
    559559
    560     m_mutableStyle->setProperty(CSSPropertyTextDecoration, textDecorationsInEffect->cssText(), m_mutableStyle->getPropertyPriority(CSSPropertyTextDecoration));
     560    if (textDecorationsInEffect->isValueList())
     561        m_mutableStyle->setProperty(CSSPropertyTextDecoration, textDecorationsInEffect->cssText(), m_mutableStyle->getPropertyPriority(CSSPropertyTextDecoration));
     562    else
     563        m_mutableStyle->removeProperty(CSSPropertyTextDecoration);
    561564    m_mutableStyle->removeProperty(CSSPropertyWebkitTextDecorationsInEffect);
    562565}
     
    958961        m_mutableStyle = getPropertiesNotIn(m_mutableStyle.get(), computedStyle->m_mutableStyle.get());
    959962    }
    960 }
    961 
    962 void EditingStyle::removePropertiesInElementDefaultStyle(StyledElement* element)
     963
     964    // 3. If this element is a span and has display: inline or float: none, remove them unless they are overriden by rules.
     965    // These rules are added by serialization code to wrap text nodes.
     966    if (isStyleSpan(element)) {
     967        if (!styleFromMatchedRules->getPropertyCSSValue(CSSPropertyDisplay) && getIdentifierValue(m_mutableStyle.get(), CSSPropertyDisplay) == CSSValueInline)
     968            m_mutableStyle->removeProperty(CSSPropertyDisplay);
     969        if (!styleFromMatchedRules->getPropertyCSSValue(CSSPropertyFloat) && getIdentifierValue(m_mutableStyle.get(), CSSPropertyFloat) == CSSValueNone)
     970            m_mutableStyle->removeProperty(CSSPropertyFloat);
     971    }
     972}
     973
     974void EditingStyle::removePropertiesInElementDefaultStyle(Element* element)
    963975{
    964976    if (!m_mutableStyle || !m_mutableStyle->length())
     
    974986
    975987    m_mutableStyle->removePropertiesInSet(propertiesToRemove.data(), propertiesToRemove.size());
     988}
     989
     990void EditingStyle::forceInline()
     991{
     992    if (!m_mutableStyle)
     993        m_mutableStyle = CSSMutableStyleDeclaration::create();
     994    const bool propertyIsImportant = true;
     995    m_mutableStyle->setProperty(CSSPropertyDisplay, CSSValueInline, propertyIsImportant);
    976996}
    977997   
     
    11791199    diffTextDecorations(result.get(), CSSPropertyWebkitTextDecorationsInEffect, baseTextDecorationsInEffect.get());
    11801200
    1181     if (fontWeightIsBold(result.get()) == fontWeightIsBold(baseStyle))
     1201    if (baseStyle->getPropertyCSSValue(CSSPropertyFontSize) && fontWeightIsBold(result.get()) == fontWeightIsBold(baseStyle))
    11821202        result->removeProperty(CSSPropertyFontWeight);
    11831203
    1184     if (getRGBAFontColor(result.get()) == getRGBAFontColor(baseStyle))
     1204    if (baseStyle->getPropertyCSSValue(CSSPropertyColor) && getRGBAFontColor(result.get()) == getRGBAFontColor(baseStyle))
    11851205        result->removeProperty(CSSPropertyColor);
    11861206
    1187     if (getTextAlignment(result.get()) == getTextAlignment(baseStyle))
     1207    if (baseStyle->getPropertyCSSValue(CSSPropertyTextAlign) && getTextAlignment(result.get()) == getTextAlignment(baseStyle))
    11881208        result->removeProperty(CSSPropertyTextAlign);
    11891209
  • trunk/Source/WebCore/editing/EditingStyle.h

    r92695 r92823  
    4848class CSSValue;
    4949class Document;
     50class Element;
    5051class HTMLElement;
    5152class Node;
     
    127128    void mergeStyleFromRulesForSerialization(StyledElement*);
    128129    void removeStyleFromRulesAndContext(StyledElement*, Node* context);
     130    void removePropertiesInElementDefaultStyle(Element*);
     131    void forceInline();
    129132
    130133    float fontSizeDelta() const { return m_fontSizeDelta; }
     
    145148    bool conflictsWithInlineStyleOfElement(StyledElement*, EditingStyle* extractedStyle, Vector<CSSPropertyID>* conflictingProperties) const;
    146149    void mergeStyle(CSSMutableStyleDeclaration*);
    147     void removePropertiesInElementDefaultStyle(StyledElement*);
    148150
    149151    RefPtr<CSSMutableStyleDeclaration> m_mutableStyle;
  • trunk/Source/WebCore/editing/markup.cpp

    r92769 r92823  
    121121    enum RangeFullySelectsNode { DoesFullySelectNode, DoesNotFullySelectNode };
    122122
    123     StyledMarkupAccumulator(Vector<Node*>* nodes, EAbsoluteURLs shouldResolveURLs, EAnnotateForInterchange shouldAnnotate, const Range* range)
    124     : MarkupAccumulator(nodes, shouldResolveURLs, range)
    125     , m_shouldAnnotate(shouldAnnotate)
    126     {
    127     }
    128 
     123    StyledMarkupAccumulator(Vector<Node*>* nodes, EAbsoluteURLs, EAnnotateForInterchange, const Range*, Node* highestNodeToBeSerialized = 0);
    129124    Node* serializeNodes(Node* startNode, Node* pastEnd);
    130125    virtual void appendString(const String& s) { return MarkupAccumulator::appendString(s); }
     
    134129
    135130private:
     131    void appendStyleNodeOpenTag(Vector<UChar>&, CSSStyleDeclaration*, Document*, bool isBlock = false);
     132    const String styleNodeCloseTag(bool isBlock = false);
    136133    virtual void appendText(Vector<UChar>& out, Text*);
    137134    String renderedText(const Node*, const Range*);
     
    140137    void appendElement(Vector<UChar>& out, Element* element, Namespaces*) { appendElement(out, element, false, DoesFullySelectNode); }
    141138
     139    enum NodeTraversalMode { EmitString, DoNotEmitString };
     140    Node* traverseNodesForSerialization(Node* startNode, Node* pastEnd, NodeTraversalMode);
     141
    142142    bool shouldAnnotate() { return m_shouldAnnotate == AnnotateForInterchange; }
     143    bool shouldApplyWrappingStyle(Node* node) const
     144    {
     145        return m_highestNodeToBeSerialized && m_highestNodeToBeSerialized->parentNode() == node->parentNode()
     146            && m_wrappingStyle && m_wrappingStyle->style();
     147    }
    143148
    144149    Vector<String> m_reversedPrecedingMarkup;
    145150    const EAnnotateForInterchange m_shouldAnnotate;
     151    Node* m_highestNodeToBeSerialized;
     152    RefPtr<EditingStyle> m_wrappingStyle;
    146153};
     154
     155inline StyledMarkupAccumulator::StyledMarkupAccumulator(Vector<Node*>* nodes, EAbsoluteURLs shouldResolveURLs, EAnnotateForInterchange shouldAnnotate,
     156    const Range* range, Node* highestNodeToBeSerialized)
     157    : MarkupAccumulator(nodes, shouldResolveURLs, range)
     158    , m_shouldAnnotate(shouldAnnotate)
     159    , m_highestNodeToBeSerialized(highestNodeToBeSerialized)
     160{
     161}
    147162
    148163void StyledMarkupAccumulator::wrapWithNode(Node* node, bool convertBlocksToInlines, RangeFullySelectsNode rangeFullySelectsNode)
     
    161176void StyledMarkupAccumulator::wrapWithStyleNode(CSSStyleDeclaration* style, Document* document, bool isBlock)
    162177{
     178    Vector<UChar> openTag;
     179    appendStyleNodeOpenTag(openTag, style, document, isBlock);
     180    m_reversedPrecedingMarkup.append(String::adopt(openTag));
     181    appendString(styleNodeCloseTag(isBlock));
     182}
     183
     184void StyledMarkupAccumulator::appendStyleNodeOpenTag(Vector<UChar>& out, CSSStyleDeclaration* style, Document* document, bool isBlock)
     185{
    163186    // All text-decoration-related elements should have been treated as special ancestors
    164187    // If we ever hit this ASSERT, we should export StyleChange in ApplyStyleCommand and use it here
    165188    ASSERT(propertyMissingOrEqualToNone(style, CSSPropertyTextDecoration) && propertyMissingOrEqualToNone(style, CSSPropertyWebkitTextDecorationsInEffect));
    166189    DEFINE_STATIC_LOCAL(const String, divStyle, ("<div style=\""));
     190    DEFINE_STATIC_LOCAL(const String, styleSpanOpen, ("<span class=\"" AppleStyleSpanClass "\" style=\""));
     191    append(out, isBlock ? divStyle : styleSpanOpen);
     192    appendAttributeValue(out, style->cssText(), document->isHTMLDocument());
     193    out.append('\"');
     194    out.append('>');
     195}
     196
     197const String StyledMarkupAccumulator::styleNodeCloseTag(bool isBlock)
     198{
    167199    DEFINE_STATIC_LOCAL(const String, divClose, ("</div>"));
    168     DEFINE_STATIC_LOCAL(const String, styleSpanOpen, ("<span class=\"" AppleStyleSpanClass "\" style=\""));
    169200    DEFINE_STATIC_LOCAL(const String, styleSpanClose, ("</span>"));
    170     Vector<UChar> openTag;
    171     append(openTag, isBlock ? divStyle : styleSpanOpen);
    172     appendAttributeValue(openTag, style->cssText(), document->isHTMLDocument());
    173     openTag.append('\"');
    174     openTag.append('>');
    175     m_reversedPrecedingMarkup.append(String::adopt(openTag));
    176     appendString(isBlock ? divClose : styleSpanClose);
     201    return isBlock ? divClose : styleSpanClose;
    177202}
    178203
     
    192217
    193218void StyledMarkupAccumulator::appendText(Vector<UChar>& out, Text* text)
    194 {
    195     if (!shouldAnnotate() || (text->parentElement() && text->parentElement()->tagQName() == textareaTag)) {
     219{   
     220    const bool parentIsTextarea = text->parentElement() && text->parentElement()->tagQName() == textareaTag;
     221    const bool wrappingSpan = shouldApplyWrappingStyle(text) && !parentIsTextarea;
     222    if (wrappingSpan) {
     223        RefPtr<EditingStyle> wrappingStyle = m_wrappingStyle->copy();
     224        // FIXME: <rdar://problem/5371536> Style rules that match pasted content can change it's appearance
     225        // Make sure spans are inline style in paste side e.g. span { display: block }.
     226        wrappingStyle->forceInline();
     227        // FIXME: Should this be included in forceInline?
     228        wrappingStyle->style()->setProperty(CSSPropertyFloat, CSSValueNone);
     229
     230        Vector<UChar> openTag;
     231        appendStyleNodeOpenTag(openTag, wrappingStyle->style(), text->document());
     232        append(out, String::adopt(openTag));
     233    }
     234
     235    if (!shouldAnnotate() || parentIsTextarea)
    196236        MarkupAccumulator::appendText(out, text);
    197         return;
    198     }
    199 
    200     bool useRenderedText = !enclosingNodeWithTag(firstPositionInNode(text), selectTag);
    201     String content = useRenderedText ? renderedText(text, m_range) : stringValueForRange(text, m_range);
    202     Vector<UChar> buffer;
    203     appendCharactersReplacingEntities(buffer, content.characters(), content.length(), EntityMaskInPCDATA);
    204     append(out, convertHTMLTextToInterchangeFormat(String::adopt(buffer), text));
     237    else {
     238        const bool useRenderedText = !enclosingNodeWithTag(firstPositionInNode(text), selectTag);
     239        String content = useRenderedText ? renderedText(text, m_range) : stringValueForRange(text, m_range);
     240        Vector<UChar> buffer;
     241        appendCharactersReplacingEntities(buffer, content.characters(), content.length(), EntityMaskInPCDATA);
     242        append(out, convertHTMLTextToInterchangeFormat(String::adopt(buffer), text));
     243    }
     244
     245    if (wrappingSpan)
     246        append(out, styleNodeCloseTag());
    205247}
    206248   
     
    241283void StyledMarkupAccumulator::appendElement(Vector<UChar>& out, Element* element, bool addDisplayInline, RangeFullySelectsNode rangeFullySelectsNode)
    242284{
    243     bool documentIsHTML = element->document()->isHTMLDocument();
     285    const bool documentIsHTML = element->document()->isHTMLDocument();
    244286    appendOpenTag(out, element, 0);
    245287
    246288    NamedNodeMap* attributes = element->attributes();
    247     unsigned length = attributes->length();
     289    const unsigned length = attributes->length();
     290    const bool shouldAnnotateOrForceInline = element->isHTMLElement() && (shouldAnnotate() || addDisplayInline);
     291    const bool shouldOverrideStyleAttr = shouldAnnotateOrForceInline || shouldApplyWrappingStyle(element);
    248292    for (unsigned int i = 0; i < length; i++) {
    249293        Attribute* attribute = attributes->attributeItem(i);
    250294        // We'll handle the style attribute separately, below.
    251         if (attribute->name() == styleAttr && element->isHTMLElement() && (shouldAnnotate() || addDisplayInline))
     295        if (attribute->name() == styleAttr && shouldOverrideStyleAttr)
    252296            continue;
    253297        appendAttribute(out, element, *attribute, 0);
    254298    }
    255299
    256     if (element->isHTMLElement() && (shouldAnnotate() || addDisplayInline)) {
    257         RefPtr<EditingStyle> style = EditingStyle::create(toHTMLElement(element)->getInlineStyleDecl());
    258         if (shouldAnnotate())
    259             style->mergeStyleFromRulesForSerialization(toHTMLElement(element));
    260         if (addDisplayInline)
    261             style->style()->setProperty(CSSPropertyDisplay, CSSValueInline, true);
    262 
    263         // If the node is not fully selected by the range, then we don't want to keep styles that affect its relationship to the nodes around it
    264         // only the ones that affect it and the nodes within it.
    265         if (rangeFullySelectsNode == DoesNotFullySelectNode)
    266             style->style()->removeProperty(CSSPropertyFloat);
    267 
    268         if (!style->isEmpty()) {
     300    if (shouldOverrideStyleAttr) {
     301        RefPtr<EditingStyle> newInlineStyle;
     302
     303        if (shouldApplyWrappingStyle(element)) {
     304            newInlineStyle = m_wrappingStyle->copy();
     305            newInlineStyle->removePropertiesInElementDefaultStyle(element);
     306            newInlineStyle->removeStyleConflictingWithStyleOfNode(element);
     307        } else
     308            newInlineStyle = EditingStyle::create();
     309
     310        if (element->isStyledElement() && static_cast<StyledElement*>(element)->inlineStyleDecl())
     311            newInlineStyle->overrideWithStyle(static_cast<StyledElement*>(element)->inlineStyleDecl());
     312
     313        if (shouldAnnotateOrForceInline) {
     314            if (shouldAnnotate())
     315                newInlineStyle->mergeStyleFromRulesForSerialization(toHTMLElement(element));
     316
     317            if (addDisplayInline)
     318                newInlineStyle->forceInline();
     319
     320            // If the node is not fully selected by the range, then we don't want to keep styles that affect its relationship to the nodes around it
     321            // only the ones that affect it and the nodes within it.
     322            if (rangeFullySelectsNode == DoesNotFullySelectNode && newInlineStyle->style())
     323                newInlineStyle->style()->removeProperty(CSSPropertyFloat);
     324        }
     325
     326        if (!newInlineStyle->isEmpty()) {
    269327            DEFINE_STATIC_LOCAL(const String, stylePrefix, (" style=\""));
    270328            append(out, stylePrefix);
    271             appendAttributeValue(out, style->style()->cssText(), documentIsHTML);
     329            appendAttributeValue(out, newInlineStyle->style()->cssText(), documentIsHTML);
    272330            out.append('\"');
    273331        }
     
    279337Node* StyledMarkupAccumulator::serializeNodes(Node* startNode, Node* pastEnd)
    280338{
     339    if (!m_highestNodeToBeSerialized) {
     340        Node* lastClosed = traverseNodesForSerialization(startNode, pastEnd, DoNotEmitString);
     341        m_highestNodeToBeSerialized = lastClosed;
     342    }
     343
     344    Node* parentOfHighestNode = m_highestNodeToBeSerialized ? m_highestNodeToBeSerialized->parentNode() : 0;
     345    if (parentOfHighestNode) {
     346        m_wrappingStyle = EditingStyle::create(parentOfHighestNode, EditingStyle::EditingInheritablePropertiesAndBackgroundColorInEffect);
     347
     348        // Styles that Mail blockquotes contribute should only be placed on the Mail blockquote,
     349        // to help us differentiate those styles from ones that the user has applied.
     350        // This helps us get the color of content pasted into blockquotes right.
     351        m_wrappingStyle->removeStyleAddedByNode(enclosingNodeOfType(firstPositionInOrBeforeNode(parentOfHighestNode), isMailBlockquote, CanCrossEditingBoundary));
     352
     353        // Call collapseTextDecorationProperties first or otherwise it'll copy the value over from in-effect to text-decorations.
     354        m_wrappingStyle->collapseTextDecorationProperties();
     355    }
     356
     357    return traverseNodesForSerialization(startNode, pastEnd, EmitString);
     358}
     359
     360Node* StyledMarkupAccumulator::traverseNodesForSerialization(Node* startNode, Node* pastEnd, NodeTraversalMode traversalMode)
     361{
     362    const bool shouldEmit = traversalMode == EmitString;
    281363    Vector<Node*> ancestorsToClose;
    282364    Node* next;
     
    305387        } else {
    306388            // Add the node to the markup if we're not skipping the descendants
    307             appendStartTag(n);
     389            if (shouldEmit)
     390                appendStartTag(n);
    308391
    309392            // If node has no children, close the tag now.
    310393            if (!n->childNodeCount()) {
    311                 appendEndTag(n);
     394                if (shouldEmit)
     395                    appendEndTag(n);
    312396                lastClosed = n;
    313397            } else {
     
    326410                    break;
    327411                // Not at the end of the range, close ancestors up to sibling of next node.
    328                 appendEndTag(ancestor);
     412                if (shouldEmit)
     413                    appendEndTag(ancestor);
    329414                lastClosed = ancestor;
    330415                ancestorsToClose.removeLast();
     
    341426                    // or b) ancestors that we never encountered during a pre-order traversal starting at startNode:
    342427                    ASSERT(startNode->isDescendantOf(parent));
    343                     wrapWithNode(parent);
     428                    if (shouldEmit)
     429                        wrapWithNode(parent);
    344430                    lastClosed = parent;
    345431                }
     
    514600    document->updateLayoutIgnorePendingStylesheets();
    515601
    516     StyledMarkupAccumulator accumulator(nodes, shouldResolveURLs, shouldAnnotate, updatedRange.get());
     602    Node* body = enclosingNodeWithTag(firstPositionInNode(commonAncestor), bodyTag);
     603    Node* fullySelectedRoot = 0;
     604    // FIXME: Do this for all fully selected blocks, not just the body.
     605    if (body && areRangesEqual(VisibleSelection::selectionFromContentsOfNode(body).toNormalizedRange().get(), range))
     606        fullySelectedRoot = body;
     607    Node* specialCommonAncestor = highestAncestorToWrapMarkup(updatedRange.get(), fullySelectedRoot, shouldAnnotate);
     608    StyledMarkupAccumulator accumulator(nodes, shouldResolveURLs, shouldAnnotate, updatedRange.get(), specialCommonAncestor);
    517609    Node* pastEnd = updatedRange->pastLastNode();
    518610
     
    539631        ASSERT(!ec);
    540632    }
    541 
    542     Node* body = enclosingNodeWithTag(firstPositionInNode(commonAncestor), bodyTag);
    543     Node* fullySelectedRoot = 0;
    544     // FIXME: Do this for all fully selected blocks, not just the body.
    545     if (body && areRangesEqual(VisibleSelection::selectionFromContentsOfNode(body).toNormalizedRange().get(), range))
    546         fullySelectedRoot = body;
    547 
    548     Node* specialCommonAncestor = highestAncestorToWrapMarkup(updatedRange.get(), fullySelectedRoot, shouldAnnotate);
    549633
    550634    Node* lastClosed = accumulator.serializeNodes(startNode, pastEnd);
     
    587671    }
    588672
    589     // Add a wrapper span with the styles that all of the nodes in the markup inherit.
    590     ContainerNode* parentOfLastClosed = lastClosed ? lastClosed->parentNode() : 0;
    591     if (parentOfLastClosed && parentOfLastClosed->renderer()) {
    592         RefPtr<EditingStyle> style = EditingStyle::create(parentOfLastClosed, EditingStyle::EditingInheritablePropertiesAndBackgroundColorInEffect);
    593 
    594         // Styles that Mail blockquotes contribute should only be placed on the Mail blockquote, to help
    595         // us differentiate those styles from ones that the user has applied.  This helps us
    596         // get the color of content pasted into blockquotes right.
    597         style->removeStyleAddedByNode(enclosingNodeOfType(firstPositionInNode(parentOfLastClosed), isMailBlockquote, CanCrossEditingBoundary));
    598 
    599         if (!style->isEmpty())
    600             accumulator.wrapWithStyleNode(style->style(), document);
    601     }
    602 
    603673    // FIXME: The interchange newline should be placed in the block that it's in, not after all of the content, unconditionally.
    604674    if (shouldAnnotate == AnnotateForInterchange && needInterchangeNewlineAfter(visibleEnd.previous()))
Note: See TracChangeset for help on using the changeset viewer.