Reviewed by Mitz.
[WebKit-https.git] / WebCore / editing / markup.cpp
index 4d37223..438da6d 100644 (file)
 #include "config.h"
 #include "markup.h"
 
+#include "CDATASection.h"
+#include "CSSComputedStyleDeclaration.h"
+#include "CSSPropertyNames.h"
+#include "CSSRule.h"
+#include "CSSRuleList.h"
+#include "CSSStyleRule.h"
+#include "cssstyleselector.h"
 #include "Comment.h"
+#include "DeleteButtonController.h"
+#include "DeprecatedStringList.h"
 #include "Document.h"
+#include "DocumentFragment.h"
 #include "DocumentType.h"
+#include "Editor.h"
+#include "Frame.h"
 #include "HTMLElement.h"
+#include "HTMLNames.h"
 #include "InlineTextBox.h"
+#include "KURL.h"
 #include "Logging.h"
-#include "VisiblePosition.h"
-#include "CSSComputedStyleDeclaration.h"
-#include "css_valueimpl.h"
+#include "ProcessingInstruction.h"
 #include "Range.h"
-#include "Position.h"
-#include "dom_xmlimpl.h"
+#include "Selection.h"
 #include "htmlediting.h"
-#include "HTMLNames.h"
 #include "visible_units.h"
-#include <kxmlcore/Assertions.h>
+#include "TextIterator.h"
+
+using namespace std;
 
 namespace WebCore {
 
@@ -49,13 +61,13 @@ using namespace HTMLNames;
 
 static inline bool shouldSelfClose(const Node *node);
 
-static DeprecatedString escapeTextForMarkup(const DeprecatedString &in)
+static DeprecatedString escapeTextForMarkup(const DeprecatedString &in, bool isAttributeValue)
 {
     DeprecatedString s = "";
 
     unsigned len = in.length();
     for (unsigned i = 0; i < len; ++i) {
-        switch (in[i].latin1()) {
+        switch (in[i].unicode()) {
             case '&':
                 s += "&amp;";
                 break;
@@ -65,6 +77,12 @@ static DeprecatedString escapeTextForMarkup(const DeprecatedString &in)
             case '>':
                 s += "&gt;";
                 break;
+            case '"':
+                if (isAttributeValue) {
+                    s += "&quot;";
+                    break;
+                }
+                // fall through
             default:
                 s += in[i];
         }
@@ -88,17 +106,11 @@ static String stringValueForRange(const Node *node, const Range *range)
 
 static DeprecatedString renderedText(const Node *node, const Range *range)
 {
-    RenderObject *r = node->renderer();
-    if (!r)
-        return DeprecatedString();
-    
     if (!node->isTextNode())
         return DeprecatedString();
 
-    DeprecatedString result = "";
-
     ExceptionCode ec;
-    const Text *textNode = static_cast<const Text *>(node);
+    const Text* textNode = static_cast<const Text*>(node);
     unsigned startOffset = 0;
     unsigned endOffset = textNode->length();
 
@@ -107,65 +119,67 @@ static DeprecatedString renderedText(const Node *node, const Range *range)
     if (range && node == range->endContainer(ec))
         endOffset = range->endOffset(ec);
     
-    RenderText *textRenderer = static_cast<RenderText *>(r);
-    DeprecatedString str = node->nodeValue().deprecatedString();
-    for (InlineTextBox *box = textRenderer->firstTextBox(); box; box = box->nextTextBox()) {
-        unsigned start = box->m_start;
-        unsigned end = box->m_start + box->m_len;
-        if (endOffset < start)
-            break;
-        if (startOffset <= end) {
-            unsigned s = kMax(start, startOffset);
-            unsigned e = kMin(end, endOffset);
-            result.append(str.mid(s, e-s));
-            if (e == end) {
-                // now add in collapsed-away spaces if at the end of the line
-                InlineTextBox *nextBox = box->nextTextBox();
-                if (nextBox && box->root() != nextBox->root()) {
-                    const char nonBreakingSpace = '\xa0';
-                    // count the number of characters between the end of the
-                    // current box and the start of the next box.
-                    int collapsedStart = e;
-                    int collapsedPastEnd = kMin((unsigned)nextBox->m_start, endOffset + 1);
-                    bool addNextNonNBSP = true;
-                    for (int i = collapsedStart; i < collapsedPastEnd; i++) {
-                        if (str[i] == nonBreakingSpace) {
-                            result.append(str[i]);
-                            addNextNonNBSP = true;
-                        }
-                        else if (addNextNonNBSP) {
-                            result.append(str[i]);
-                            addNextNonNBSP = false;
-                        }
-                    }
-                }
+    Position start(const_cast<Node*>(node), startOffset);
+    Position end(const_cast<Node*>(node), endOffset);
+    Range r(node->document(), start, end);
+    return plainText(&r);
+}
+
+static PassRefPtr<CSSMutableStyleDeclaration> styleFromMatchedRulesForElement(Element* element, bool authorOnly = true)
+{
+    RefPtr<CSSMutableStyleDeclaration> style = new CSSMutableStyleDeclaration();
+    RefPtr<CSSRuleList> matchedRules = element->document()->styleSelector()->styleRulesForElement(element, authorOnly);
+    if (matchedRules) {
+        for (unsigned i = 0; i < matchedRules->length(); i++) {
+            if (matchedRules->item(i)->type() == CSSRule::STYLE_RULE) {
+                RefPtr<CSSMutableStyleDeclaration> s = static_cast<CSSStyleRule*>(matchedRules->item(i))->style();
+                style->merge(s.get(), true);
             }
         }
-
     }
     
-    return result;
+    return style.release();
+}
+
+static void removeEnclosingMailBlockquoteStyle(CSSMutableStyleDeclaration* style, Node* node)
+{
+    Node* blockquote = nearestMailBlockquote(node);
+    if (!blockquote || !blockquote->parentNode())
+        return;
+            
+    RefPtr<CSSMutableStyleDeclaration> parentStyle = Position(blockquote->parentNode(), 0).computedStyle()->copyInheritableProperties();
+    RefPtr<CSSMutableStyleDeclaration> blockquoteStyle = Position(blockquote, 0).computedStyle()->copyInheritableProperties();
+    parentStyle->diff(blockquoteStyle.get());
+    blockquoteStyle->diff(style);
 }
 
 static DeprecatedString startMarkup(const Node *node, const Range *range, EAnnotateForInterchange annotate, CSSMutableStyleDeclaration *defaultStyle)
 {
-    bool documentIsHTML = node->getDocument()->isHTMLDocument();
-    Node::NodeType type = node->nodeType();
-    switch (type) {
+    bool documentIsHTML = node->document()->isHTMLDocument();
+    switch (node->nodeType()) {
         case Node::TEXT_NODE: {
-            if (node->parentNode()) {
-                if (node->parentNode()->hasTagName(preTag) ||
-                    node->parentNode()->hasTagName(scriptTag) ||
-                    node->parentNode()->hasTagName(styleTag) ||
-                    node->parentNode()->hasTagName(textareaTag))
+            if (Node* parent = node->parentNode()) {
+                if (parent->hasTagName(listingTag)
+                        || parent->hasTagName(preTag)
+                        || parent->hasTagName(scriptTag)
+                        || parent->hasTagName(styleTag)
+                        || parent->hasTagName(textareaTag)
+                        || parent->hasTagName(xmpTag))
                     return stringValueForRange(node, range).deprecatedString();
             }
-            DeprecatedString markup = annotate ? escapeTextForMarkup(renderedText(node, range)) : escapeTextForMarkup(stringValueForRange(node, range).deprecatedString());            
+            bool useRenderedText = annotate && !enclosingNodeWithTag(const_cast<Node*>(node), selectTag);
+            
+            DeprecatedString markup = useRenderedText ? escapeTextForMarkup(renderedText(node, range), false) : escapeTextForMarkup(stringValueForRange(node, range).deprecatedString(), false);
             if (defaultStyle) {
                 Node *element = node->parentNode();
                 if (element) {
                     RefPtr<CSSComputedStyleDeclaration> computedStyle = Position(element, 0).computedStyle();
                     RefPtr<CSSMutableStyleDeclaration> style = computedStyle->copyInheritableProperties();
+                    // Styles that Mail blockquotes contribute should only be placed on the Mail blockquote, to help
+                    // us differentiate those styles from ones that the user has applied.  This helps us
+                    // get the color of content pasted into blockquotes right.
+                    removeEnclosingMailBlockquoteStyle(style.get(), const_cast<Node*>(node));
+                    
                     defaultStyle->diff(style.get());
                     if (style->length() > 0) {
                         // FIXME: Handle case where style->cssText() has illegal characters in it, like "
@@ -177,7 +191,7 @@ static DeprecatedString startMarkup(const Node *node, const Range *range, EAnnot
             return annotate ? convertHTMLTextToInterchangeFormat(markup) : markup;
         }
         case Node::COMMENT_NODE:
-            return static_cast<const Comment *>(node)->toString().deprecatedString();
+            return static_cast<const Comment*>(node)->toString().deprecatedString();
         case Node::DOCUMENT_NODE: {
             // Documents do not normally contain a docType as a child node, force it to print here instead.
             const DocumentType* docType = static_cast<const Document*>(node)->doctype();
@@ -190,47 +204,54 @@ static DeprecatedString startMarkup(const Node *node, const Range *range, EAnnot
         case Node::DOCUMENT_TYPE_NODE:
             return static_cast<const DocumentType*>(node)->toString().deprecatedString();
         case Node::PROCESSING_INSTRUCTION_NODE:
-            return static_cast<const ProcessingInstruction *>(node)->toString().deprecatedString();
+            return static_cast<const ProcessingInstruction*>(node)->toString().deprecatedString();
         case Node::ELEMENT_NODE: {
-            DeprecatedString markup = QChar('<') + node->nodeName().deprecatedString();
-            if (type == Node::ELEMENT_NODE) {
-                const Element *el = static_cast<const Element *>(node);
-                String additionalStyle;
-                if (defaultStyle && el->isHTMLElement()) {
-                    RefPtr<CSSComputedStyleDeclaration> computedStyle = Position(const_cast<Element *>(el), 0).computedStyle();
-                    RefPtr<CSSMutableStyleDeclaration> style = computedStyle->copyInheritableProperties();
-                    defaultStyle->diff(style.get());
-                    if (style->length() > 0) {
-                        CSSMutableStyleDeclaration *inlineStyleDecl = static_cast<const HTMLElement *>(el)->inlineStyleDecl();
-                        if (inlineStyleDecl)
-                            inlineStyleDecl->diff(style.get());
-                        additionalStyle = style->cssText();
-                    }
-                }
-                NamedAttrMap *attrs = el->attributes();
-                unsigned length = attrs->length();
-                if (length == 0 && additionalStyle.length() > 0) {
-                    // FIXME: Handle case where additionalStyle has illegal characters in it, like "
-                    markup += " " +  styleAttr.localName().deprecatedString() + "=\"" + additionalStyle.deprecatedString() + "\"";
+            DeprecatedString markup = DeprecatedChar('<');
+            const Element* el = static_cast<const Element*>(node);
+            markup += el->nodeNamePreservingCase().deprecatedString();
+            String additionalStyle;
+            if (defaultStyle && el->isHTMLElement() && !isMailBlockquote(node)) {
+                RefPtr<CSSComputedStyleDeclaration> computedStyle = Position(const_cast<Element*>(el), 0).computedStyle();
+                RefPtr<CSSMutableStyleDeclaration> style = computedStyle->copyInheritableProperties();
+                style->merge(styleFromMatchedRulesForElement(const_cast<Element*>(el)).get());
+                
+                // Styles that Mail blockquotes contribute should only be placed on the Mail blockquote, to help
+                // us differentiate those styles from ones that the user has applied.  This helps us
+                // get the color of content pasted into blockquotes right.
+                removeEnclosingMailBlockquoteStyle(style.get(), const_cast<Node*>(node));
+                
+                defaultStyle->diff(style.get());
+                if (style->length() > 0) {
+                    CSSMutableStyleDeclaration *inlineStyleDecl = static_cast<const HTMLElement*>(el)->inlineStyleDecl();
+                    if (inlineStyleDecl)
+                        inlineStyleDecl->diff(style.get());
+                    additionalStyle = style->cssText();
                 }
-                else {
-                    for (unsigned int i=0; i<length; i++) {
-                        Attribute *attr = attrs->attributeItem(i);
-                        String value = attr->value();
-                        if (attr->name() == styleAttr && additionalStyle.length() > 0)
-                            value += "; " + additionalStyle;
-                        // FIXME: Handle case where value has illegal characters in it, like "
-                        if (documentIsHTML)
-                            markup += " " + attr->name().localName().deprecatedString();
-                        else
-                            markup += " " + attr->name().toString().deprecatedString();
-                        markup += "=\"" + escapeTextForMarkup(value.deprecatedString()) + "\"";
-                    }
+            }
+            NamedAttrMap *attrs = el->attributes();
+            unsigned length = attrs->length();
+
+            for (unsigned int i = 0; i < length; i++) {
+                Attribute *attr = attrs->attributeItem(i);
+                String value = attr->value();
+                if (attr->name() == styleAttr && additionalStyle.length() > 0) {
+                    value += "; " + additionalStyle;
+                    additionalStyle = "";
                 }
+                // FIXME: Handle case where value has illegal characters in it, like "
+                if (documentIsHTML)
+                    markup += " " + attr->name().localName().deprecatedString();
+                else
+                    markup += " " + attr->name().toString().deprecatedString();
+                markup += "=\"" + escapeTextForMarkup(value.deprecatedString(), true) + "\"";
             }
             
-            if (shouldSelfClose(node)) {
-                if (node->isHTMLElement())
+            if (additionalStyle.length() > 0)
+                // FIXME: Handle case where additionalStyle has illegal characters in it, like "
+                markup += " " +  styleAttr.localName().deprecatedString() + "=\"" + additionalStyle.deprecatedString() + "\"";
+            
+            if (shouldSelfClose(el)) {
+                if (el->isHTMLElement())
                     markup += " "; // XHTML 1.0 <-> HTML compatibility.
                 markup += "/>";
             } else
@@ -238,11 +259,13 @@ static DeprecatedString startMarkup(const Node *node, const Range *range, EAnnot
             
             return markup;
         }
-        case Node::ATTRIBUTE_NODE:
         case Node::CDATA_SECTION_NODE:
+            return static_cast<const CDATASection*>(node)->toString().deprecatedString();
+        case Node::ATTRIBUTE_NODE:
         case Node::ENTITY_NODE:
         case Node::ENTITY_REFERENCE_NODE:
         case Node::NOTATION_NODE:
+        case Node::XPATH_NAMESPACE_NODE:
             break;
     }
     return "";
@@ -264,7 +287,7 @@ static inline bool doesHTMLForbidEndTag(const Node *node)
 // 4. Other elements self-close.
 static inline bool shouldSelfClose(const Node *node)
 {
-    if (node->getDocument()->isHTMLDocument())
+    if (node->document()->isHTMLDocument())
         return false;
     if (node->hasChildNodes())
         return false;
@@ -275,17 +298,17 @@ static inline bool shouldSelfClose(const Node *node)
 
 static DeprecatedString endMarkup(const Node *node)
 {
-    if (node->isElementNode() && !shouldSelfClose(node) && !doesHTMLForbidEndTag(node))
-        return "</" + node->nodeName().deprecatedString() + ">";
+    if (node->isElementNode() && !shouldSelfClose(node) && (node->hasChildNodes() || !doesHTMLForbidEndTag(node)))
+        return "</" + static_cast<const Element*>(node)->nodeNamePreservingCase().deprecatedString() + ">";
     return "";
 }
 
-static DeprecatedString markup(const Node *startNode, bool onlyIncludeChildren, bool includeSiblings, DeprecatedPtrList<Node> *nodes)
+static DeprecatedString markup(Node* startNode, bool onlyIncludeChildren, bool includeSiblings, Vector<Node*> *nodes)
 {
     // Doesn't make sense to only include children and include siblings.
     ASSERT(!(onlyIncludeChildren && includeSiblings));
     DeprecatedString me = "";
-    for (const Node *current = startNode; current != NULL; current = includeSiblings ? current->nextSibling() : NULL) {
+    for (Node* current = startNode; current != NULL; current = includeSiblings ? current->nextSibling() : NULL) {
         if (!onlyIncludeChildren) {
             if (nodes)
                 nodes->append(current);
@@ -293,7 +316,7 @@ static DeprecatedString markup(const Node *startNode, bool onlyIncludeChildren,
         }
         // print children
         if (Node *n = current->firstChild())
-            if (!(n->getDocument()->isHTMLDocument() && doesHTMLForbidEndTag(current)))
+            if (!(n->document()->isHTMLDocument() && doesHTMLForbidEndTag(current)))
                 me += markup(n, false, true, nodes);
         
         // Print my ending tag
@@ -308,7 +331,7 @@ static void completeURLs(Node *node, const DeprecatedString &baseURL)
     Node *end = node->traverseNextSibling();
     for (Node *n = node; n != end; n = n->traverseNextNode()) {
         if (n->isElementNode()) {
-            Element *e = static_cast<Element *>(n);
+            Element *e = static_cast<Element*>(n);
             NamedAttrMap *attrs = e->attributes();
             unsigned length = attrs->length();
             for (unsigned i = 0; i < length; i++) {
@@ -320,7 +343,15 @@ static void completeURLs(Node *node, const DeprecatedString &baseURL)
     }
 }
 
-DeprecatedString createMarkup(const Range *range, DeprecatedPtrList<Node> *nodes, EAnnotateForInterchange annotate)
+static bool needInterchangeNewlineAfter(const VisiblePosition& v)
+{
+    VisiblePosition next = v.next();
+    return isEndOfParagraph(v) && isStartOfParagraph(next) && !next.deepEquivalent().upstream().node()->hasTagName(brTag);
+}
+
+// FIXME: Shouldn't we omit style info when annotate == DoNotAnnotateForInterchange? 
+// FIXME: At least, annotation and style info should probably not be included in range.markupString()
+DeprecatedString createMarkup(const Range *range, Vector<Node*>* nodes, EAnnotateForInterchange annotate)
 {
     if (!range || range->isDetached())
         return DeprecatedString();
@@ -328,99 +359,121 @@ DeprecatedString createMarkup(const Range *range, DeprecatedPtrList<Node> *nodes
     static const DeprecatedString interchangeNewlineString = DeprecatedString("<br class=\"") + AppleInterchangeNewline + "\">";
 
     ExceptionCode ec = 0;
+    if (range->collapsed(ec))
+        return "";
+    ASSERT(ec == 0);
     Node *commonAncestor = range->commonAncestorContainer(ec);
     ASSERT(ec == 0);
 
-    Document *doc = commonAncestor->getDocument();
+    Document *doc = commonAncestor->document();
+    // disable the delete button so it's elements are not serialized into the markup
+    doc->frame()->editor()->deleteButtonController()->disable();
     doc->updateLayoutIgnorePendingStylesheets();
 
     Node *commonAncestorBlock = 0;
-    if (commonAncestor != 0) {
-        commonAncestorBlock = commonAncestor->enclosingBlockFlowElement();
-    }
-    if (commonAncestorBlock == 0) {
-        return "";    
+    if (commonAncestor) {
+        commonAncestorBlock = enclosingBlock(commonAncestor);
+        if (commonAncestorBlock && commonAncestorBlock->hasTagName(tbodyTag)) {
+            Node* table = commonAncestorBlock->parentNode();
+            while (table && !table->hasTagName(tableTag))
+                table = table->parentNode();
+            if (table)
+                commonAncestorBlock = table;
+        }
     }
 
     DeprecatedStringList markups;
     Node *pastEnd = range->pastEndNode();
     Node *lastClosed = 0;
-    DeprecatedPtrList<Node> ancestorsToClose;
+    Vector<Node*> ancestorsToClose;
 
-    // calculate the "default style" for this markup
+    // Calculate the "default style" for this markup.
     Position pos(doc->documentElement(), 0);
     RefPtr<CSSComputedStyleDeclaration> computedStyle = pos.computedStyle();
     RefPtr<CSSMutableStyleDeclaration> defaultStyle = computedStyle->copyInheritableProperties();
     
-    Node *startNode = range->startNode();
+    NodestartNode = range->startNode();
     VisiblePosition visibleStart(range->startPosition(), VP_DEFAULT_AFFINITY);
     VisiblePosition visibleEnd(range->endPosition(), VP_DEFAULT_AFFINITY);
-    if (!inSameBlock(visibleStart, visibleStart.next())) {
-        if (visibleStart == visibleEnd.previous())
+    if (annotate && needInterchangeNewlineAfter(visibleStart)) {
+        if (visibleStart == visibleEnd.previous()) {
+            doc->frame()->editor()->deleteButtonController()->enable();
             return interchangeNewlineString;
+        }
+
         markups.append(interchangeNewlineString);
-        startNode = startNode->traverseNextNode();
+        startNode = visibleStart.next().deepEquivalent().node();
     }
-    
-    // Iterate through the nodes of the range.
+
     Node *next;
     for (Node *n = startNode; n != pastEnd; n = next) {
         next = n->traverseNextNode();
+        bool skipDescendants = false;
+        bool addMarkupForNode = true;
+        
+        if (!n->renderer() && !enclosingNodeWithTag(n, selectTag)) {
+            skipDescendants = true;
+            addMarkupForNode = false;
+            next = n->traverseNextSibling();
+            // Don't skip over pastEnd.
+            if (pastEnd && pastEnd->isDescendantOf(n))
+                next = pastEnd;
+        }
 
-        if (n->isBlockFlow() && next == pastEnd) {
-            // Don't write out an empty block.
+        if (isBlock(n) && canHaveChildrenForEditing(n) && next == pastEnd)
+            // Don't write out empty block containers that aren't fully selected.
             continue;
-        }
         
         // Add the node to the markup.
-        if (n->renderer()) {
+        if (addMarkupForNode) {
             markups.append(startMarkup(n, range, annotate, defaultStyle.get()));
-            if (nodes) {
+            if (nodes)
                 nodes->append(n);
-            }
         }
         
-        if (n->firstChild() == 0) {
-            // Node has no children, add its close tag now.
-            if (n->renderer()) {
+        if (n->firstChild() == 0 || skipDescendants) {
+            // Node has no children, or we are skipping it's descendants, add its close tag now.
+            if (addMarkupForNode) {
                 markups.append(endMarkup(n));
                 lastClosed = n;
             }
             
             // Check if the node is the last leaf of a tree.
-            if (n->nextSibling() == 0 || next == pastEnd) {
+            if (!n->nextSibling() || next == pastEnd) {
                 if (!ancestorsToClose.isEmpty()) {
                     // Close up the ancestors.
-                    while (Node *ancestor = ancestorsToClose.last()) {
-                        if (next != pastEnd && ancestor == next->parentNode()) {
+                    do {
+                        Node *ancestor = ancestorsToClose.last();
+                        if (next != pastEnd && next->isDescendantOf(ancestor))
                             break;
-                        }
                         // Not at the end of the range, close ancestors up to sibling of next node.
                         markups.append(endMarkup(ancestor));
                         lastClosed = ancestor;
                         ancestorsToClose.removeLast();
-                    }
-                } else {
-                    // No ancestors to close, but need to add ancestors not in range since next node is in another tree. 
-                    if (next != pastEnd) {
-                        Node *nextParent = next->parentNode();
-                        if (n != nextParent) {
-                            for (Node *parent = n->parent(); parent != 0 && parent != nextParent; parent = parent->parentNode()) {
-                                markups.prepend(startMarkup(parent, range, annotate, defaultStyle.get()));
-                                markups.append(endMarkup(parent));
-                                if (nodes) {
-                                    nodes->append(parent);
-                                }                            
-                                lastClosed = parent;
-                            }
-                        }
+                    } while (!ancestorsToClose.isEmpty());
+                }
+                
+                // Surround the currently accumulated markup with markup for ancestors we never opened as we leave the subtree(s) rooted at those ancestors.
+                Node* nextParent = next ? next->parentNode() : 0;
+                if (next != pastEnd && n != nextParent) {
+                    Node* lastAncestorClosedOrSelf = n->isDescendantOf(lastClosed) ? lastClosed : n;
+                    for (Node *parent = lastAncestorClosedOrSelf->parent(); parent != 0 && parent != nextParent; parent = parent->parentNode()) {
+                        // All ancestors that aren't in the ancestorsToClose list should either be a) unrendered:
+                        if (!parent->renderer())
+                            continue;
+                        // or b) ancestors that we never encountered during a pre-order traversal starting at startNode:
+                        ASSERT(startNode->isDescendantOf(parent));
+                        markups.prepend(startMarkup(parent, range, annotate, defaultStyle.get()));
+                        markups.append(endMarkup(parent));
+                        if (nodes)
+                            nodes->append(parent);
+                        lastClosed = parent;
                     }
                 }
             }
-        } else if (n->renderer()) {
-            // Node is an ancestor, set it to close eventually.
+        } else if (addMarkupForNode && !skipDescendants)
+            // We added markup for this node, and we're descending into it.  Set it to close eventually.
             ancestorsToClose.append(n);
-        }
     }
     
     Node *rangeStartNode = range->startNode();
@@ -437,71 +490,103 @@ DeprecatedString createMarkup(const Range *range, DeprecatedPtrList<Node> *nodes
             bool breakAtEnd = false;
             if (commonAncestorBlock == ancestor) {
                 // Include ancestors that are required to retain the appearance of the copied markup.
-                if (ancestor->hasTagName(preTag) || ancestor->hasTagName(tableTag) ||
-                    ancestor->hasTagName(olTag) || ancestor->hasTagName(ulTag)) {
+                if (annotate &&
+                    (ancestor->hasTagName(listingTag)
+                        || ancestor->hasTagName(olTag)
+                        || ancestor->hasTagName(preTag)
+                        || ancestor->hasTagName(tableTag)
+                        || ancestor->hasTagName(ulTag)
+                        || ancestor->hasTagName(xmpTag))) {
                     breakAtEnd = true;
-                } else {
+                } else
                     break;
-                }
             }
             markups.prepend(startMarkup(ancestor, range, annotate, defaultStyle.get()));
             markups.append(endMarkup(ancestor));
             if (nodes) {
                 nodes->append(ancestor);
             }        
-            if (breakAtEnd) {
+            if (breakAtEnd)
                 break;
-            }
         }
     }
 
-    if (annotate) {
-        if (!inSameBlock(visibleEnd, visibleEnd.previous()))
-            markups.append(interchangeNewlineString);
-    }
+    if (annotate && needInterchangeNewlineAfter(visibleEnd.previous()))
+        markups.append(interchangeNewlineString);
 
     // Retain the Mail quote level by including all ancestor mail block quotes.
-    for (Node *ancestor = commonAncestorBlock; ancestor; ancestor = ancestor->parentNode()) {
-        if (isMailBlockquote(ancestor)) {
-            markups.prepend(startMarkup(ancestor, range, annotate, defaultStyle.get()));
-            markups.append(endMarkup(ancestor));
+    if (annotate) {
+        for (Node *ancestor = commonAncestorBlock; ancestor; ancestor = ancestor->parentNode()) {
+            if (isMailBlockquote(ancestor)) {
+                markups.prepend(startMarkup(ancestor, range, annotate, defaultStyle.get()));
+                markups.append(endMarkup(ancestor));
+            }
         }
     }
     
+    // FIXME: Do this for all fully selected blocks, not just a body.
+    Node* root = range->startPosition().node();
+    while (root && !root->hasTagName(bodyTag))
+        root = root->parentNode();
+    if (root && *Selection::selectionFromContentsOfNode(root).toRange() == *range) {
+        CSSMutableStyleDeclaration* inlineStyleDecl = static_cast<HTMLElement*>(root)->inlineStyleDecl();
+        RefPtr<CSSMutableStyleDeclaration> style = inlineStyleDecl ? inlineStyleDecl->copy() : new CSSMutableStyleDeclaration();
+        style->merge(styleFromMatchedRulesForElement(static_cast<Element*>(root)).get());
+        
+        defaultStyle->diff(style.get());
+        
+        // Bring the background attribute over, but not as an attribute because a background attribute on a div
+        // appears to have no effect.
+        if (!style->getPropertyCSSValue(CSS_PROP_BACKGROUND_IMAGE) && static_cast<Element*>(root)->hasAttribute(backgroundAttr))
+            style->setProperty(CSS_PROP_BACKGROUND_IMAGE, "url('" + static_cast<Element*>(root)->getAttribute(backgroundAttr) + "')");
+        
+        markups.prepend("<div style='" + style->cssText().deprecatedString() + "'>");
+        markups.append("</div>");
+    }
+    
     // add in the "default style" for this markup
     // FIXME: Handle case where value has illegal characters in it, like "
     DeprecatedString openTag = DeprecatedString("<span class=\"") + AppleStyleSpanClass + "\" style=\"" + defaultStyle->cssText().deprecatedString() + "\">";
     markups.prepend(openTag);
     markups.append("</span>");
 
+    doc->frame()->editor()->deleteButtonController()->enable();
     return markups.join("");
 }
 
-PassRefPtr<DocumentFragment> createFragmentFromMarkup(Document* document, const DeprecatedString& markup, const DeprecatedString& baseURL)
+PassRefPtr<DocumentFragment> createFragmentFromMarkup(Document* document, const String& markup, const String& baseURL)
 {
     ASSERT(document->documentElement()->isHTMLElement());
     // FIXME: What if the document element is not an HTML element?
-    HTMLElement *element = static_cast<HTMLElement *>(document->documentElement());
+    HTMLElement *element = static_cast<HTMLElement*>(document->documentElement());
 
     RefPtr<DocumentFragment> fragment = element->createContextualFragment(markup);
     ASSERT(fragment);
 
     if (!baseURL.isEmpty() && baseURL != document->baseURL())
-        completeURLs(fragment.get(), baseURL);
+        completeURLs(fragment.get(), baseURL.deprecatedString());
 
     return fragment.release();
 }
 
-DeprecatedString createMarkup(const WebCore::Node *node, EChildrenOnly includeChildren,
-    DeprecatedPtrList<WebCore::Node> *nodes, EAnnotateForInterchange annotate)
+DeprecatedString createMarkup(const Node* node, EChildrenOnly includeChildren,
+    Vector<Node*>* nodes, EAnnotateForInterchange annotate)
 {
     ASSERT(annotate == DoNotAnnotateForInterchange); // annotation not yet implemented for this code path
-    node->getDocument()->updateLayoutIgnorePendingStylesheets();
-    return markup(node, includeChildren, false, nodes);
+    // disable the delete button so it's elements are not serialized into the markup
+    if (node->document()->frame())
+        node->document()->frame()->editor()->deleteButtonController()->disable();
+    node->document()->updateLayoutIgnorePendingStylesheets();
+    DeprecatedString result(markup(const_cast<Node*>(node), includeChildren, false, nodes));
+    if (node->document()->frame())
+        node->document()->frame()->editor()->deleteButtonController()->enable();
+    return result;
 }
 
-static void createParagraphContentsFromString(WebCore::Document *document, Element *paragraph, const DeprecatedString &string)
+static void fillContainerFromString(ContainerNode* paragraph, const DeprecatedString& string)
 {
+    Document* document = paragraph->document();
+
     ExceptionCode ec = 0;
     if (string.isEmpty()) {
         paragraph->appendChild(createBlockPlaceholderElement(document), ec);
@@ -513,86 +598,126 @@ static void createParagraphContentsFromString(WebCore::Document *document, Eleme
 
     DeprecatedStringList tabList = DeprecatedStringList::split('\t', string, true);
     DeprecatedString tabText = "";
+    bool first = true;
     while (!tabList.isEmpty()) {
         DeprecatedString s = tabList.first();
         tabList.pop_front();
 
         // append the non-tab textual part
         if (!s.isEmpty()) {
-            if (tabText != "") {
+            if (!tabText.isEmpty()) {
                 paragraph->appendChild(createTabSpanElement(document, tabText), ec);
                 ASSERT(ec == 0);
                 tabText = "";
             }
-            RefPtr<Node> textNode = document->createTextNode(s);
-            rebalanceWhitespaceInTextNode(textNode.get(), 0, s.length());
+            RefPtr<Node> textNode = document->createTextNode(stringWithRebalancedWhitespace(s, first, tabList.isEmpty()));
             paragraph->appendChild(textNode.release(), ec);
             ASSERT(ec == 0);
         }
 
         // there is a tab after every entry, except the last entry
         // (if the last character is a tab, the list gets an extra empty entry)
-        if (!tabList.isEmpty()) {
+        if (!tabList.isEmpty())
             tabText += '\t';
-        } else if (tabText != "") {
+        else if (!tabText.isEmpty()) {
             paragraph->appendChild(createTabSpanElement(document, tabText), ec);
             ASSERT(ec == 0);
         }
+        
+        first = false;
     }
 }
 
-PassRefPtr<DocumentFragment> createFragmentFromText(Document *document, const DeprecatedString &text)
+PassRefPtr<DocumentFragment> createFragmentFromText(Range* context, const String& text)
 {
-    if (!document)
+    if (!context)
         return 0;
 
+    Node* styleNode = context->startNode();
+    if (!styleNode) {
+        styleNode = context->startPosition().node();
+        if (!styleNode)
+            return 0;
+    }
+
+    Document* document = styleNode->document();
     RefPtr<DocumentFragment> fragment = document->createDocumentFragment();
     
-    if (!text.isEmpty()) {
-        DeprecatedString string = text;
-
-        // Break string into paragraphs. Extra line breaks turn into empty paragraphs.
-        string.replace(DeprecatedString("\r\n"), "\n");
-        string.replace('\r', '\n');
-        DeprecatedStringList list = DeprecatedStringList::split('\n', string, true); // true gets us empty strings in the list
-        while (!list.isEmpty()) {
-            DeprecatedString s = list.first();
-            list.pop_front();
-
-            ExceptionCode ec = 0;
+    if (text.isEmpty())
+        return fragment.release();
+
+    DeprecatedString string = text.deprecatedString();
+    string.replace("\r\n", "\n");
+    string.replace('\r', '\n');
+
+    ExceptionCode ec = 0;
+    RenderObject* renderer = styleNode->renderer();
+    if (renderer && renderer->style()->preserveNewline()) {
+        fragment->appendChild(document->createTextNode(string), ec);
+        ASSERT(ec == 0);
+        if (string.endsWith("\n")) {
             RefPtr<Element> element;
-            if (s.isEmpty() && list.isEmpty()) {
-                // For last line, use the "magic BR" rather than a P.
-                element = document->createElementNS(xhtmlNamespaceURI, "br", ec);
-                ASSERT(ec == 0);
-                element->setAttribute(classAttr, AppleInterchangeNewline);            
-            } else {
-                element = createDefaultParagraphElement(document);
-                createParagraphContentsFromString(document, element.get(), s);
-            }
-            fragment->appendChild(element.get(), ec);
+            element = document->createElementNS(xhtmlNamespaceURI, "br", ec);
+            ASSERT(ec == 0);
+            element->setAttribute(classAttr, AppleInterchangeNewline);            
+            fragment->appendChild(element.release(), ec);
+            ASSERT(ec == 0);
+        }
+        return fragment.release();
+    }
+
+    // A string with no newlines gets added inline, rather than being put into a paragraph.
+    if (string.find('\n') == -1) {
+        fillContainerFromString(fragment.get(), string);
+        return fragment.release();
+    }
+
+    // Break string into paragraphs. Extra line breaks turn into empty paragraphs.
+    DeprecatedStringList list = DeprecatedStringList::split('\n', string, true); // true gets us empty strings in the list
+    while (!list.isEmpty()) {
+        DeprecatedString s = list.first();
+        list.pop_front();
+
+        RefPtr<Element> element;
+        if (s.isEmpty() && list.isEmpty()) {
+            // For last line, use the "magic BR" rather than a P.
+            element = document->createElementNS(xhtmlNamespaceURI, "br", ec);
             ASSERT(ec == 0);
+            element->setAttribute(classAttr, AppleInterchangeNewline);            
+        } else {
+            element = createDefaultParagraphElement(document);
+            fillContainerFromString(element.get(), s);
         }
+        fragment->appendChild(element.release(), ec);
+        ASSERT(ec == 0);
     }
     return fragment.release();
 }
 
-PassRefPtr<DocumentFragment> createFragmentFromNodeList(Document *document, const DeprecatedPtrList<Node> &nodeList)
+PassRefPtr<DocumentFragment> createFragmentFromNodes(Document *document, const Vector<Node*>& nodes)
 {
     if (!document)
         return 0;
-    
+
+    // disable the delete button so it's elements are not serialized into the markup
+    if (document->frame())
+        document->frame()->editor()->deleteButtonController()->disable();
+
     RefPtr<DocumentFragment> fragment = document->createDocumentFragment();
 
     ExceptionCode ec = 0;
-    for (DeprecatedPtrListIterator<Node> i(nodeList); i.current(); ++i) {
+    size_t size = nodes.size();
+    for (size_t i = 0; i < size; ++i) {
         RefPtr<Element> element = createDefaultParagraphElement(document);
-        element->appendChild(i.current(), ec);
+        element->appendChild(nodes[i], ec);
         ASSERT(ec == 0);
         fragment->appendChild(element.release(), ec);
         ASSERT(ec == 0);
     }
 
+    if (document->frame())
+        document->frame()->editor()->deleteButtonController()->enable();
+
     return fragment.release();
 }