| Index: third_party/WebKit/WebCore/editing/markup.cpp | 
| =================================================================== | 
| --- third_party/WebKit/WebCore/editing/markup.cpp	(revision 9391) | 
| +++ third_party/WebKit/WebCore/editing/markup.cpp	(working copy) | 
| @@ -1,1226 +1,1239 @@ | 
| -/* | 
| - * Copyright (C) 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. | 
| - * | 
| - * Redistribution and use in source and binary forms, with or without | 
| - * modification, are permitted provided that the following conditions | 
| - * are met: | 
| - * 1. Redistributions of source code must retain the above copyright | 
| - *    notice, this list of conditions and the following disclaimer. | 
| - * 2. Redistributions in binary form must reproduce the above copyright | 
| - *    notice, this list of conditions and the following disclaimer in the | 
| - *    documentation and/or other materials provided with the distribution. | 
| - * | 
| - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY | 
| - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | 
| - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR | 
| - * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR | 
| - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | 
| - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | 
| - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR | 
| - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY | 
| - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | 
| - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | 
| - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | 
| - */ | 
| - | 
| -#include "config.h" | 
| -#include "markup.h" | 
| - | 
| -#include "CDATASection.h" | 
| -#include "CharacterNames.h" | 
| -#include "Comment.h" | 
| -#include "CSSComputedStyleDeclaration.h" | 
| -#include "CSSPrimitiveValue.h" | 
| -#include "CSSProperty.h" | 
| -#include "CSSPropertyNames.h" | 
| -#include "CSSRule.h" | 
| -#include "CSSRuleList.h" | 
| -#include "CSSStyleRule.h" | 
| -#include "CSSStyleSelector.h" | 
| -#include "CSSValue.h" | 
| -#include "CSSValueKeywords.h" | 
| -#include "DeleteButtonController.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 "Logging.h" | 
| -#include "ProcessingInstruction.h" | 
| -#include "QualifiedName.h" | 
| -#include "Range.h" | 
| -#include "Selection.h" | 
| -#include "TextIterator.h" | 
| -#include "htmlediting.h" | 
| -#include "visible_units.h" | 
| -#include <wtf/StdLibExtras.h> | 
| - | 
| -using namespace std; | 
| - | 
| -namespace WebCore { | 
| - | 
| -using namespace HTMLNames; | 
| - | 
| -static inline bool shouldSelfClose(const Node *node); | 
| - | 
| -class AttributeChange { | 
| -public: | 
| -    AttributeChange() | 
| -        : m_name(nullAtom, nullAtom, nullAtom) | 
| -    { | 
| -    } | 
| - | 
| -    AttributeChange(PassRefPtr<Element> element, const QualifiedName& name, const String& value) | 
| -        : m_element(element), m_name(name), m_value(value) | 
| -    { | 
| -    } | 
| - | 
| -    void apply() | 
| -    { | 
| -        m_element->setAttribute(m_name, m_value); | 
| -    } | 
| - | 
| -private: | 
| -    RefPtr<Element> m_element; | 
| -    QualifiedName m_name; | 
| -    String m_value; | 
| -}; | 
| - | 
| -static void appendAttributeValue(Vector<UChar>& result, const String& attr, bool escapeNBSP) | 
| -{ | 
| -    const UChar* uchars = attr.characters(); | 
| -    unsigned len = attr.length(); | 
| -    unsigned lastCopiedFrom = 0; | 
| - | 
| -    DEFINE_STATIC_LOCAL(const String, ampEntity, ("&")); | 
| -    DEFINE_STATIC_LOCAL(const String, gtEntity, (">")); | 
| -    DEFINE_STATIC_LOCAL(const String, ltEntity, ("<")); | 
| -    DEFINE_STATIC_LOCAL(const String, quotEntity, (""")); | 
| -    DEFINE_STATIC_LOCAL(const String, nbspEntity, (" ")); | 
| - | 
| -    for (unsigned i = 0; i < len; ++i) { | 
| -        UChar c = uchars[i]; | 
| -        switch (c) { | 
| -            case '&': | 
| -                result.append(uchars + lastCopiedFrom, i - lastCopiedFrom); | 
| -                append(result, ampEntity); | 
| -                lastCopiedFrom = i + 1; | 
| -                break; | 
| -            case '<': | 
| -                result.append(uchars + lastCopiedFrom, i - lastCopiedFrom); | 
| -                append(result, ltEntity); | 
| -                lastCopiedFrom = i + 1; | 
| -                break; | 
| -            case '>': | 
| -                result.append(uchars + lastCopiedFrom, i - lastCopiedFrom); | 
| -                append(result, gtEntity); | 
| -                lastCopiedFrom = i + 1; | 
| -                break; | 
| -            case '"': | 
| -                result.append(uchars + lastCopiedFrom, i - lastCopiedFrom); | 
| -                append(result, quotEntity); | 
| -                lastCopiedFrom = i + 1; | 
| -                break; | 
| -            case noBreakSpace: | 
| -                if (escapeNBSP) { | 
| -                    result.append(uchars + lastCopiedFrom, i - lastCopiedFrom); | 
| -                    append(result, nbspEntity); | 
| -                    lastCopiedFrom = i + 1; | 
| -                } | 
| -                break; | 
| -        } | 
| -    } | 
| - | 
| -    result.append(uchars + lastCopiedFrom, len - lastCopiedFrom); | 
| -} | 
| - | 
| -static void appendEscapedContent(Vector<UChar>& result, pair<const UChar*, size_t> range, bool escapeNBSP) | 
| -{ | 
| -    const UChar* uchars = range.first; | 
| -    unsigned len = range.second; | 
| -    unsigned lastCopiedFrom = 0; | 
| - | 
| -    DEFINE_STATIC_LOCAL(const String, ampEntity, ("&")); | 
| -    DEFINE_STATIC_LOCAL(const String, gtEntity, (">")); | 
| -    DEFINE_STATIC_LOCAL(const String, ltEntity, ("<")); | 
| -    DEFINE_STATIC_LOCAL(const String, nbspEntity, (" ")); | 
| - | 
| -    for (unsigned i = 0; i < len; ++i) { | 
| -        UChar c = uchars[i]; | 
| -        switch (c) { | 
| -            case '&': | 
| -                result.append(uchars + lastCopiedFrom, i - lastCopiedFrom); | 
| -                append(result, ampEntity); | 
| -                lastCopiedFrom = i + 1; | 
| -                break; | 
| -            case '<': | 
| -                result.append(uchars + lastCopiedFrom, i - lastCopiedFrom); | 
| -                append(result, ltEntity); | 
| -                lastCopiedFrom = i + 1; | 
| -                break; | 
| -            case '>': | 
| -                result.append(uchars + lastCopiedFrom, i - lastCopiedFrom); | 
| -                append(result, gtEntity); | 
| -                lastCopiedFrom = i + 1; | 
| -                break; | 
| -            case noBreakSpace: | 
| -                if (escapeNBSP) { | 
| -                    result.append(uchars + lastCopiedFrom, i - lastCopiedFrom); | 
| -                    append(result, nbspEntity); | 
| -                    lastCopiedFrom = i + 1; | 
| -                } | 
| -                break; | 
| -        } | 
| -    } | 
| - | 
| -    result.append(uchars + lastCopiedFrom, len - lastCopiedFrom); | 
| -} | 
| - | 
| -static String escapeContentText(const String& in, bool escapeNBSP) | 
| -{ | 
| -    Vector<UChar> buffer; | 
| -    appendEscapedContent(buffer, make_pair(in.characters(), in.length()), escapeNBSP); | 
| -    return String::adopt(buffer); | 
| -} | 
| - | 
| -static void appendQuotedURLAttributeValue(Vector<UChar>& result, const String& urlString) | 
| -{ | 
| -    UChar quoteChar = '\"'; | 
| -    String strippedURLString = urlString.stripWhiteSpace(); | 
| -    if (protocolIs(strippedURLString, "javascript")) { | 
| -        // minimal escaping for javascript urls | 
| -        if (strippedURLString.contains('"')) { | 
| -            if (strippedURLString.contains('\'')) | 
| -                strippedURLString.replace('\"', """); | 
| -            else | 
| -                quoteChar = '\''; | 
| -        } | 
| -        result.append(quoteChar); | 
| -        append(result, strippedURLString); | 
| -        result.append(quoteChar); | 
| -        return; | 
| -    } | 
| - | 
| -    // FIXME: This does not fully match other browsers. Firefox percent-escapes non-ASCII characters for innerHTML. | 
| -    result.append(quoteChar); | 
| -    appendAttributeValue(result, urlString, false); | 
| -    result.append(quoteChar); | 
| -} | 
| - | 
| -static String stringValueForRange(const Node* node, const Range* range) | 
| -{ | 
| -    if (!range) | 
| -        return node->nodeValue(); | 
| - | 
| -    String str = node->nodeValue(); | 
| -    ExceptionCode ec; | 
| -    if (node == range->endContainer(ec)) | 
| -        str.truncate(range->endOffset(ec)); | 
| -    if (node == range->startContainer(ec)) | 
| -        str.remove(0, range->startOffset(ec)); | 
| -    return str; | 
| -} | 
| - | 
| -static inline pair<const UChar*, size_t> ucharRange(const Node *node, const Range *range) | 
| -{ | 
| -    String str = node->nodeValue(); | 
| -    const UChar* characters = str.characters(); | 
| -    size_t length = str.length(); | 
| - | 
| -    if (range) { | 
| -        ExceptionCode ec; | 
| -        if (node == range->endContainer(ec)) | 
| -            length = range->endOffset(ec); | 
| -        if (node == range->startContainer(ec)) { | 
| -            size_t start = range->startOffset(ec); | 
| -            characters += start; | 
| -            length -= start; | 
| -        } | 
| -    } | 
| - | 
| -    return make_pair(characters, length); | 
| -} | 
| - | 
| -static inline void appendUCharRange(Vector<UChar>& result, const pair<const UChar*, size_t> range) | 
| -{ | 
| -    result.append(range.first, range.second); | 
| -} | 
| - | 
| -static String renderedText(const Node* node, const Range* range) | 
| -{ | 
| -    if (!node->isTextNode()) | 
| -        return String(); | 
| - | 
| -    ExceptionCode ec; | 
| -    const Text* textNode = static_cast<const Text*>(node); | 
| -    unsigned startOffset = 0; | 
| -    unsigned endOffset = textNode->length(); | 
| - | 
| -    if (range && node == range->startContainer(ec)) | 
| -        startOffset = range->startOffset(ec); | 
| -    if (range && node == range->endContainer(ec)) | 
| -        endOffset = range->endOffset(ec); | 
| - | 
| -    Position start(const_cast<Node*>(node), startOffset); | 
| -    Position end(const_cast<Node*>(node), endOffset); | 
| -    return plainText(Range::create(node->document(), start, end).get()); | 
| -} | 
| - | 
| -static PassRefPtr<CSSMutableStyleDeclaration> styleFromMatchedRulesForElement(Element* element, bool authorOnly = true) | 
| -{ | 
| -    RefPtr<CSSMutableStyleDeclaration> style = CSSMutableStyleDeclaration::create(); | 
| -    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 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 void removeDefaultStyles(CSSMutableStyleDeclaration* style, Document* document) | 
| -{ | 
| -    if (!document || !document->documentElement()) | 
| -        return; | 
| - | 
| -    RefPtr<CSSMutableStyleDeclaration> documentStyle = computedStyle(document->documentElement())->copyInheritableProperties(); | 
| -    documentStyle->diff(style); | 
| -} | 
| - | 
| -static bool shouldAddNamespaceElem(const Element* elem) | 
| -{ | 
| -    // Don't add namespace attribute if it is already defined for this elem. | 
| -    const AtomicString& prefix = elem->prefix(); | 
| -    AtomicString attr = !prefix.isEmpty() ? "xmlns:" + prefix : "xmlns"; | 
| -    return !elem->hasAttribute(attr); | 
| -} | 
| - | 
| -static bool shouldAddNamespaceAttr(const Attribute* attr, HashMap<AtomicStringImpl*, AtomicStringImpl*>& namespaces) | 
| -{ | 
| -    // Don't add namespace attributes twice | 
| -    DEFINE_STATIC_LOCAL(const AtomicString, xmlnsURI, ("http://www.w3.org/2000/xmlns/")); | 
| -    DEFINE_STATIC_LOCAL(const QualifiedName, xmlnsAttr, (nullAtom, "xmlns", xmlnsURI)); | 
| -    if (attr->name() == xmlnsAttr) { | 
| -        namespaces.set(emptyAtom.impl(), attr->value().impl()); | 
| -        return false; | 
| -    } | 
| - | 
| -    QualifiedName xmlnsPrefixAttr("xmlns", attr->localName(), xmlnsURI); | 
| -    if (attr->name() == xmlnsPrefixAttr) { | 
| -        namespaces.set(attr->localName().impl(), attr->value().impl()); | 
| -        return false; | 
| -    } | 
| - | 
| -    return true; | 
| -} | 
| - | 
| -static void appendNamespace(Vector<UChar>& result, const AtomicString& prefix, const AtomicString& ns, HashMap<AtomicStringImpl*, AtomicStringImpl*>& namespaces) | 
| -{ | 
| -    if (ns.isEmpty()) | 
| -        return; | 
| - | 
| -    // Use emptyAtoms's impl() for both null and empty strings since the HashMap can't handle 0 as a key | 
| -    AtomicStringImpl* pre = prefix.isEmpty() ? emptyAtom.impl() : prefix.impl(); | 
| -    AtomicStringImpl* foundNS = namespaces.get(pre); | 
| -    if (foundNS != ns.impl()) { | 
| -        namespaces.set(pre, ns.impl()); | 
| -        DEFINE_STATIC_LOCAL(const String, xmlns, ("xmlns")); | 
| -        result.append(' '); | 
| -        append(result, xmlns); | 
| -        if (!prefix.isEmpty()) { | 
| -            result.append(':'); | 
| -            append(result, prefix); | 
| -        } | 
| - | 
| -        result.append('='); | 
| -        result.append('"'); | 
| -        appendAttributeValue(result, ns, false); | 
| -        result.append('"'); | 
| -    } | 
| -} | 
| - | 
| -static void appendDocumentType(Vector<UChar>& result, const DocumentType* n) | 
| -{ | 
| -    if (n->name().isEmpty()) | 
| -        return; | 
| - | 
| -    append(result, "<!DOCTYPE "); | 
| -    append(result, n->name()); | 
| -    if (!n->publicId().isEmpty()) { | 
| -        append(result, " PUBLIC \""); | 
| -        append(result, n->publicId()); | 
| -        append(result, "\""); | 
| -        if (!n->systemId().isEmpty()) { | 
| -            append(result, " \""); | 
| -            append(result, n->systemId()); | 
| -            append(result, "\""); | 
| -        } | 
| -    } else if (!n->systemId().isEmpty()) { | 
| -        append(result, " SYSTEM \""); | 
| -        append(result, n->systemId()); | 
| -        append(result, "\""); | 
| -    } | 
| -    if (!n->internalSubset().isEmpty()) { | 
| -        append(result, " ["); | 
| -        append(result, n->internalSubset()); | 
| -        append(result, "]"); | 
| -    } | 
| -    append(result, ">"); | 
| -} | 
| - | 
| -static void appendStartMarkup(Vector<UChar>& result, const Node *node, const Range *range, EAnnotateForInterchange annotate, bool convertBlocksToInlines = false, HashMap<AtomicStringImpl*, AtomicStringImpl*>* namespaces = 0) | 
| -{ | 
| -    bool documentIsHTML = node->document()->isHTMLDocument(); | 
| -    switch (node->nodeType()) { | 
| -        case Node::TEXT_NODE: { | 
| -            if (Node* parent = node->parentNode()) { | 
| -                if (parent->hasTagName(scriptTag) | 
| -                    || parent->hasTagName(styleTag) | 
| -                    || parent->hasTagName(textareaTag) | 
| -                    || parent->hasTagName(xmpTag)) { | 
| -                    appendUCharRange(result, ucharRange(node, range)); | 
| -                    break; | 
| -                } | 
| -            } | 
| -            if (!annotate) { | 
| -                appendEscapedContent(result, ucharRange(node, range), documentIsHTML); | 
| -                break; | 
| -            } | 
| - | 
| -            bool useRenderedText = !enclosingNodeWithTag(Position(const_cast<Node*>(node), 0), selectTag); | 
| -            String markup = escapeContentText(useRenderedText ? renderedText(node, range) : stringValueForRange(node, range), false); | 
| -            if (annotate) | 
| -                markup = convertHTMLTextToInterchangeFormat(markup, static_cast<const Text*>(node)); | 
| -            append(result, markup); | 
| -            break; | 
| -        } | 
| -        case Node::COMMENT_NODE: | 
| -            // FIXME: Comment content is not escaped, but XMLSerializer (and possibly other callers) should raise an exception if it includes "-->". | 
| -            append(result, "<!--"); | 
| -            append(result, static_cast<const Comment*>(node)->nodeValue()); | 
| -            append(result, "-->"); | 
| -            break; | 
| -        case Node::DOCUMENT_NODE: | 
| -        case Node::DOCUMENT_FRAGMENT_NODE: | 
| -            break; | 
| -        case Node::DOCUMENT_TYPE_NODE: | 
| -            appendDocumentType(result, static_cast<const DocumentType*>(node)); | 
| -            break; | 
| -        case Node::PROCESSING_INSTRUCTION_NODE: { | 
| -            // FIXME: PI data is not escaped, but XMLSerializer (and possibly other callers) this should raise an exception if it includes "?>". | 
| -            const ProcessingInstruction* n = static_cast<const ProcessingInstruction*>(node); | 
| -            append(result, "<?"); | 
| -            append(result, n->target()); | 
| -            append(result, " "); | 
| -            append(result, n->data()); | 
| -            append(result, "?>"); | 
| -            break; | 
| -        } | 
| -        case Node::ELEMENT_NODE: { | 
| -            result.append('<'); | 
| -            const Element* el = static_cast<const Element*>(node); | 
| -            bool convert = convertBlocksToInlines & isBlock(const_cast<Node*>(node)); | 
| -            append(result, el->nodeNamePreservingCase()); | 
| -            NamedAttrMap *attrs = el->attributes(); | 
| -            unsigned length = attrs->length(); | 
| -            if (!documentIsHTML && namespaces && shouldAddNamespaceElem(el)) | 
| -                appendNamespace(result, el->prefix(), el->namespaceURI(), *namespaces); | 
| - | 
| -            for (unsigned int i = 0; i < length; i++) { | 
| -                Attribute *attr = attrs->attributeItem(i); | 
| -                // We'll handle the style attribute separately, below. | 
| -                if (attr->name() == styleAttr && el->isHTMLElement() && (annotate || convert)) | 
| -                    continue; | 
| -                result.append(' '); | 
| - | 
| -                if (documentIsHTML) | 
| -                    append(result, attr->name().localName()); | 
| -                else | 
| -                    append(result, attr->name().toString()); | 
| - | 
| -                result.append('='); | 
| - | 
| -                if (el->isURLAttribute(attr)) | 
| -                    appendQuotedURLAttributeValue(result, attr->value()); | 
| -                else { | 
| -                    result.append('\"'); | 
| -                    appendAttributeValue(result, attr->value(), documentIsHTML); | 
| -                    result.append('\"'); | 
| -                } | 
| - | 
| -                if (!documentIsHTML && namespaces && shouldAddNamespaceAttr(attr, *namespaces)) | 
| -                    appendNamespace(result, attr->prefix(), attr->namespaceURI(), *namespaces); | 
| -            } | 
| - | 
| -            if (el->isHTMLElement() && (annotate || convert)) { | 
| -                Element* element = const_cast<Element*>(el); | 
| -                RefPtr<CSSMutableStyleDeclaration> style = static_cast<HTMLElement*>(element)->getInlineStyleDecl()->copy(); | 
| -                if (annotate) { | 
| -                    RefPtr<CSSMutableStyleDeclaration> styleFromMatchedRules = styleFromMatchedRulesForElement(const_cast<Element*>(el)); | 
| -                    // Styles from the inline style declaration, held in the variable "style", take precedence | 
| -                    // over those from matched rules. | 
| -                    styleFromMatchedRules->merge(style.get()); | 
| -                    style = styleFromMatchedRules; | 
| - | 
| -                    RefPtr<CSSComputedStyleDeclaration> computedStyleForElement = computedStyle(element); | 
| -                    RefPtr<CSSMutableStyleDeclaration> fromComputedStyle = CSSMutableStyleDeclaration::create(); | 
| - | 
| -                    { | 
| -                        CSSMutableStyleDeclaration::const_iterator end = style->end(); | 
| -                        for (CSSMutableStyleDeclaration::const_iterator it = style->begin(); it != end; ++it) { | 
| -                            const CSSProperty& property = *it; | 
| -                            CSSValue* value = property.value(); | 
| -                            // The property value, if it's a percentage, may not reflect the actual computed value. | 
| -                            // For example: style="height: 1%; overflow: visible;" in quirksmode | 
| -                            // FIXME: There are others like this, see <rdar://problem/5195123> Slashdot copy/paste fidelity problem | 
| -                            if (value->cssValueType() == CSSValue::CSS_PRIMITIVE_VALUE) | 
| -                                if (static_cast<CSSPrimitiveValue*>(value)->primitiveType() == CSSPrimitiveValue::CSS_PERCENTAGE) | 
| -                                    if (RefPtr<CSSValue> computedPropertyValue = computedStyleForElement->getPropertyCSSValue(property.id())) | 
| -                                        fromComputedStyle->addParsedProperty(CSSProperty(property.id(), computedPropertyValue)); | 
| -                        } | 
| -                    } | 
| - | 
| -                    style->merge(fromComputedStyle.get()); | 
| -                } | 
| -                if (convert) | 
| -                    style->setProperty(CSSPropertyDisplay, CSSValueInline, true); | 
| -                if (style->length() > 0) { | 
| -                    DEFINE_STATIC_LOCAL(const String, stylePrefix, (" style=\"")); | 
| -                    append(result, stylePrefix); | 
| -                    appendAttributeValue(result, style->cssText(), documentIsHTML); | 
| -                    result.append('\"'); | 
| -                } | 
| -            } | 
| - | 
| -            if (shouldSelfClose(el)) { | 
| -                if (el->isHTMLElement()) | 
| -                    result.append(' '); // XHTML 1.0 <-> HTML compatibility. | 
| -                result.append('/'); | 
| -            } | 
| -            result.append('>'); | 
| -            break; | 
| -        } | 
| -        case Node::CDATA_SECTION_NODE: { | 
| -            // FIXME: CDATA content is not escaped, but XMLSerializer (and possibly other callers) should raise an exception if it includes "]]>". | 
| -            const CDATASection* n = static_cast<const CDATASection*>(node); | 
| -            append(result, "<![CDATA["); | 
| -            append(result, n->data()); | 
| -            append(result, "]]>"); | 
| -            break; | 
| -        } | 
| -        case Node::ATTRIBUTE_NODE: | 
| -        case Node::ENTITY_NODE: | 
| -        case Node::ENTITY_REFERENCE_NODE: | 
| -        case Node::NOTATION_NODE: | 
| -        case Node::XPATH_NAMESPACE_NODE: | 
| -            ASSERT_NOT_REACHED(); | 
| -            break; | 
| -    } | 
| -} | 
| - | 
| -static String getStartMarkup(const Node *node, const Range *range, EAnnotateForInterchange annotate, bool convertBlocksToInlines = false, HashMap<AtomicStringImpl*, AtomicStringImpl*>* namespaces = 0) | 
| -{ | 
| -    Vector<UChar> result; | 
| -    appendStartMarkup(result, node, range, annotate, convertBlocksToInlines, namespaces); | 
| -    return String::adopt(result); | 
| -} | 
| - | 
| -static inline bool doesHTMLForbidEndTag(const Node *node) | 
| -{ | 
| -    if (node->isHTMLElement()) { | 
| -        const HTMLElement* htmlElt = static_cast<const HTMLElement*>(node); | 
| -        return (htmlElt->endTagRequirement() == TagStatusForbidden); | 
| -    } | 
| -    return false; | 
| -} | 
| - | 
| -// Rules of self-closure | 
| -// 1. No elements in HTML documents use the self-closing syntax. | 
| -// 2. Elements w/ children never self-close because they use a separate end tag. | 
| -// 3. HTML elements which do not have a "forbidden" end tag will close with a separate end tag. | 
| -// 4. Other elements self-close. | 
| -static inline bool shouldSelfClose(const Node *node) | 
| -{ | 
| -    if (node->document()->isHTMLDocument()) | 
| -        return false; | 
| -    if (node->hasChildNodes()) | 
| -        return false; | 
| -    if (node->isHTMLElement() && !doesHTMLForbidEndTag(node)) | 
| -        return false; | 
| -    return true; | 
| -} | 
| - | 
| -static void appendEndMarkup(Vector<UChar>& result, const Node* node) | 
| -{ | 
| -    if (!node->isElementNode() || shouldSelfClose(node) || (!node->hasChildNodes() && doesHTMLForbidEndTag(node))) | 
| -        return; | 
| - | 
| -    result.append('<'); | 
| -    result.append('/'); | 
| -    append(result, static_cast<const Element*>(node)->nodeNamePreservingCase()); | 
| -    result.append('>'); | 
| -} | 
| - | 
| -static String getEndMarkup(const Node *node) | 
| -{ | 
| -    Vector<UChar> result; | 
| -    appendEndMarkup(result, node); | 
| -    return String::adopt(result); | 
| -} | 
| - | 
| -static void appendMarkup(Vector<UChar>& result, Node* startNode, bool onlyIncludeChildren, Vector<Node*>* nodes, const HashMap<AtomicStringImpl*, AtomicStringImpl*>* namespaces = 0) | 
| -{ | 
| -    HashMap<AtomicStringImpl*, AtomicStringImpl*> namespaceHash; | 
| -    if (namespaces) | 
| -        namespaceHash = *namespaces; | 
| - | 
| -    if (!onlyIncludeChildren) { | 
| -        if (nodes) | 
| -            nodes->append(startNode); | 
| - | 
| -        appendStartMarkup(result,startNode, 0, DoNotAnnotateForInterchange, false, &namespaceHash); | 
| -    } | 
| -    // print children | 
| -    if (!(startNode->document()->isHTMLDocument() && doesHTMLForbidEndTag(startNode))) | 
| -        for (Node* current = startNode->firstChild(); current; current = current->nextSibling()) | 
| -            appendMarkup(result, current, false, nodes, &namespaceHash); | 
| - | 
| -    // Print my ending tag | 
| -    if (!onlyIncludeChildren) | 
| -        appendEndMarkup(result, startNode); | 
| -} | 
| - | 
| -static void completeURLs(Node* node, const String& baseURL) | 
| -{ | 
| -    Vector<AttributeChange> changes; | 
| - | 
| -    KURL parsedBaseURL(baseURL); | 
| - | 
| -    Node* end = node->traverseNextSibling(); | 
| -    for (Node* n = node; n != end; n = n->traverseNextNode()) { | 
| -        if (n->isElementNode()) { | 
| -            Element* e = static_cast<Element*>(n); | 
| -            NamedAttrMap* attrs = e->attributes(); | 
| -            unsigned length = attrs->length(); | 
| -            for (unsigned i = 0; i < length; i++) { | 
| -                Attribute* attr = attrs->attributeItem(i); | 
| -                if (e->isURLAttribute(attr)) | 
| -                    changes.append(AttributeChange(e, attr->name(), KURL(parsedBaseURL, attr->value()).string())); | 
| -            } | 
| -        } | 
| -    } | 
| - | 
| -    size_t numChanges = changes.size(); | 
| -    for (size_t i = 0; i < numChanges; ++i) | 
| -        changes[i].apply(); | 
| -} | 
| - | 
| -static bool needInterchangeNewlineAfter(const VisiblePosition& v) | 
| -{ | 
| -    VisiblePosition next = v.next(); | 
| -    Node* upstreamNode = next.deepEquivalent().upstream().node(); | 
| -    Node* downstreamNode = v.deepEquivalent().downstream().node(); | 
| -    // Add an interchange newline if a paragraph break is selected and a br won't already be added to the markup to represent it. | 
| -    return isEndOfParagraph(v) && isStartOfParagraph(next) && !(upstreamNode->hasTagName(brTag) && upstreamNode == downstreamNode); | 
| -} | 
| - | 
| -static PassRefPtr<CSSMutableStyleDeclaration> styleFromMatchedRulesAndInlineDecl(const Node* node) | 
| -{ | 
| -    if (!node->isHTMLElement()) | 
| -        return 0; | 
| - | 
| -    // FIXME: Having to const_cast here is ugly, but it is quite a bit of work to untangle | 
| -    // the non-const-ness of styleFromMatchedRulesForElement. | 
| -    HTMLElement* element = const_cast<HTMLElement*>(static_cast<const HTMLElement*>(node)); | 
| -    RefPtr<CSSMutableStyleDeclaration> style = styleFromMatchedRulesForElement(element); | 
| -    RefPtr<CSSMutableStyleDeclaration> inlineStyleDecl = element->getInlineStyleDecl(); | 
| -    style->merge(inlineStyleDecl.get()); | 
| -    return style.release(); | 
| -} | 
| - | 
| -static bool propertyMissingOrEqualToNone(CSSMutableStyleDeclaration* style, int propertyID) | 
| -{ | 
| -    if (!style) | 
| -        return false; | 
| -    RefPtr<CSSValue> value = style->getPropertyCSSValue(propertyID); | 
| -    if (!value) | 
| -        return true; | 
| -    if (!value->isPrimitiveValue()) | 
| -        return false; | 
| -    return static_cast<CSSPrimitiveValue*>(value.get())->getIdent() == CSSValueNone; | 
| -} | 
| - | 
| -static bool elementHasTextDecorationProperty(const Node* node) | 
| -{ | 
| -    RefPtr<CSSMutableStyleDeclaration> style = styleFromMatchedRulesAndInlineDecl(node); | 
| -    if (!style) | 
| -        return false; | 
| -    return !propertyMissingOrEqualToNone(style.get(), CSSPropertyTextDecoration); | 
| -} | 
| - | 
| -static String joinMarkups(const Vector<String>& preMarkups, const Vector<String>& postMarkups) | 
| -{ | 
| -    size_t length = 0; | 
| - | 
| -    size_t preCount = preMarkups.size(); | 
| -    for (size_t i = 0; i < preCount; ++i) | 
| -        length += preMarkups[i].length(); | 
| - | 
| -    size_t postCount = postMarkups.size(); | 
| -    for (size_t i = 0; i < postCount; ++i) | 
| -        length += postMarkups[i].length(); | 
| - | 
| -    Vector<UChar> result; | 
| -    result.reserveInitialCapacity(length); | 
| - | 
| -    for (size_t i = preCount; i > 0; --i) | 
| -        append(result, preMarkups[i - 1]); | 
| - | 
| -    for (size_t i = 0; i < postCount; ++i) | 
| -        append(result, postMarkups[i]); | 
| - | 
| -    return String::adopt(result); | 
| -} | 
| - | 
| -// 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() | 
| -String createMarkup(const Range* range, Vector<Node*>* nodes, EAnnotateForInterchange annotate, bool convertBlocksToInlines) | 
| -{ | 
| -    DEFINE_STATIC_LOCAL(const String, interchangeNewlineString, ("<br class=\"" AppleInterchangeNewline "\">")); | 
| - | 
| -    if (!range) | 
| -        return ""; | 
| - | 
| -    Document* document = range->ownerDocument(); | 
| -    if (!document) | 
| -        return ""; | 
| - | 
| -    bool documentIsHTML = document->isHTMLDocument(); | 
| - | 
| -    // Disable the delete button so it's elements are not serialized into the markup, | 
| -    // but make sure neither endpoint is inside the delete user interface. | 
| -    Frame* frame = document->frame(); | 
| -    DeleteButtonController* deleteButton = frame ? frame->editor()->deleteButtonController() : 0; | 
| -    RefPtr<Range> updatedRange = avoidIntersectionWithNode(range, deleteButton ? deleteButton->containerElement() : 0); | 
| -    if (!updatedRange) | 
| -        return ""; | 
| - | 
| -    if (deleteButton) | 
| -        deleteButton->disable(); | 
| - | 
| -    ExceptionCode ec = 0; | 
| -    bool collapsed = updatedRange->collapsed(ec); | 
| -    ASSERT(ec == 0); | 
| -    if (collapsed) | 
| -        return ""; | 
| -    Node* commonAncestor = updatedRange->commonAncestorContainer(ec); | 
| -    ASSERT(ec == 0); | 
| -    if (!commonAncestor) | 
| -        return ""; | 
| - | 
| -    document->updateLayoutIgnorePendingStylesheets(); | 
| - | 
| -    Vector<String> markups; | 
| -    Vector<String> preMarkups; | 
| -    Node* pastEnd = updatedRange->pastLastNode(); | 
| -    Node* lastClosed = 0; | 
| -    Vector<Node*> ancestorsToClose; | 
| - | 
| -    Node* startNode = updatedRange->firstNode(); | 
| -    VisiblePosition visibleStart(updatedRange->startPosition(), VP_DEFAULT_AFFINITY); | 
| -    VisiblePosition visibleEnd(updatedRange->endPosition(), VP_DEFAULT_AFFINITY); | 
| -    if (annotate && needInterchangeNewlineAfter(visibleStart)) { | 
| -        if (visibleStart == visibleEnd.previous()) { | 
| -            if (deleteButton) | 
| -                deleteButton->enable(); | 
| -            return interchangeNewlineString; | 
| -        } | 
| - | 
| -        markups.append(interchangeNewlineString); | 
| -        startNode = visibleStart.next().deepEquivalent().node(); | 
| -    } | 
| - | 
| -    Node* next; | 
| -    for (Node* n = startNode; n != pastEnd; n = next) { | 
| - | 
| -        // According to <rdar://problem/5730668>, it is possible for n to blow past pastEnd and become null here.  This | 
| -        // shouldn't be possible.  This null check will prevent crashes (but create too much markup) and the ASSERT will | 
| -        // hopefully lead us to understanding the problem. | 
| -        ASSERT(n); | 
| -        if (!n) | 
| -            break; | 
| - | 
| -        next = n->traverseNextNode(); | 
| -        bool skipDescendants = false; | 
| -        bool addMarkupForNode = true; | 
| - | 
| -        if (!n->renderer() && !enclosingNodeWithTag(Position(n, 0), selectTag)) { | 
| -            skipDescendants = true; | 
| -            addMarkupForNode = false; | 
| -            next = n->traverseNextSibling(); | 
| -            // Don't skip over pastEnd. | 
| -            if (pastEnd && pastEnd->isDescendantOf(n)) | 
| -                next = pastEnd; | 
| -        } | 
| - | 
| -        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 (addMarkupForNode) { | 
| -            markups.append(getStartMarkup(n, updatedRange.get(), annotate)); | 
| -            if (nodes) | 
| -                nodes->append(n); | 
| -        } | 
| - | 
| -        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(getEndMarkup(n)); | 
| -                lastClosed = n; | 
| -            } | 
| - | 
| -            // Check if the node is the last leaf of a tree. | 
| -            if (!n->nextSibling() || next == pastEnd) { | 
| -                if (!ancestorsToClose.isEmpty()) { | 
| -                    // Close up the ancestors. | 
| -                    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(getEndMarkup(ancestor)); | 
| -                        lastClosed = ancestor; | 
| -                        ancestorsToClose.removeLast(); | 
| -                    } 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)); | 
| -                        preMarkups.append(getStartMarkup(parent, updatedRange.get(), annotate)); | 
| -                        markups.append(getEndMarkup(parent)); | 
| -                        if (nodes) | 
| -                            nodes->append(parent); | 
| -                        lastClosed = parent; | 
| -                    } | 
| -                } | 
| -            } | 
| -        } else if (addMarkupForNode && !skipDescendants) | 
| -            // We added markup for this node, and we're descending into it.  Set it to close eventually. | 
| -            ancestorsToClose.append(n); | 
| -    } | 
| - | 
| -    // Include ancestors that aren't completely inside the range but are required to retain | 
| -    // the structure and appearance of the copied markup. | 
| -    Node* specialCommonAncestor = 0; | 
| -    Node* commonAncestorBlock = commonAncestor ? enclosingBlock(commonAncestor) : 0; | 
| -    if (annotate && commonAncestorBlock) { | 
| -        if (commonAncestorBlock->hasTagName(tbodyTag) || commonAncestorBlock->hasTagName(trTag)) { | 
| -            Node* table = commonAncestorBlock->parentNode(); | 
| -            while (table && !table->hasTagName(tableTag)) | 
| -                table = table->parentNode(); | 
| -            if (table) | 
| -                specialCommonAncestor = table; | 
| -        } else if (commonAncestorBlock->hasTagName(listingTag) | 
| -                    || commonAncestorBlock->hasTagName(olTag) | 
| -                    || commonAncestorBlock->hasTagName(preTag) | 
| -                    || commonAncestorBlock->hasTagName(tableTag) | 
| -                    || commonAncestorBlock->hasTagName(ulTag) | 
| -                    || commonAncestorBlock->hasTagName(xmpTag)) | 
| -            specialCommonAncestor = commonAncestorBlock; | 
| -    } | 
| - | 
| -    // Retain the Mail quote level by including all ancestor mail block quotes. | 
| -    if (lastClosed && annotate) { | 
| -        for (Node *ancestor = lastClosed->parentNode(); ancestor; ancestor = ancestor->parentNode()) | 
| -            if (isMailBlockquote(ancestor)) | 
| -                specialCommonAncestor = ancestor; | 
| -    } | 
| - | 
| -    Node* checkAncestor = specialCommonAncestor ? specialCommonAncestor : commonAncestor; | 
| -    if (checkAncestor->renderer()) { | 
| -        RefPtr<CSSMutableStyleDeclaration> checkAncestorStyle = computedStyle(checkAncestor)->copyInheritableProperties(); | 
| -        if (!propertyMissingOrEqualToNone(checkAncestorStyle.get(), CSSPropertyWebkitTextDecorationsInEffect)) | 
| -            specialCommonAncestor = enclosingNodeOfType(Position(checkAncestor, 0), &elementHasTextDecorationProperty); | 
| -    } | 
| - | 
| -    // If a single tab is selected, commonAncestor will be a text node inside a tab span. | 
| -    // If two or more tabs are selected, commonAncestor will be the tab span. | 
| -    // In either case, if there is a specialCommonAncestor already, it will necessarily be above | 
| -    // any tab span that needs to be included. | 
| -    if (!specialCommonAncestor && isTabSpanTextNode(commonAncestor)) | 
| -        specialCommonAncestor = commonAncestor->parentNode(); | 
| -    if (!specialCommonAncestor && isTabSpanNode(commonAncestor)) | 
| -        specialCommonAncestor = commonAncestor; | 
| - | 
| -    if (Node *enclosingAnchor = enclosingNodeWithTag(Position(specialCommonAncestor ? specialCommonAncestor : commonAncestor, 0), aTag)) | 
| -        specialCommonAncestor = enclosingAnchor; | 
| - | 
| -    Node* body = enclosingNodeWithTag(Position(commonAncestor, 0), bodyTag); | 
| -    // FIXME: Only include markup for a fully selected root (and ancestors of lastClosed up to that root) if | 
| -    // there are styles/attributes on those nodes that need to be included to preserve the appearance of the copied markup. | 
| -    // FIXME: Do this for all fully selected blocks, not just the body. | 
| -    Node* fullySelectedRoot = body && *Selection::selectionFromContentsOfNode(body).toRange() == *updatedRange ? body : 0; | 
| -    if (annotate && fullySelectedRoot) | 
| -        specialCommonAncestor = fullySelectedRoot; | 
| - | 
| -    if (specialCommonAncestor && lastClosed) { | 
| -        // Also include all of the ancestors of lastClosed up to this special ancestor. | 
| -        for (Node* ancestor = lastClosed->parentNode(); ancestor; ancestor = ancestor->parentNode()) { | 
| -            if (ancestor == fullySelectedRoot && !convertBlocksToInlines) { | 
| -                RefPtr<CSSMutableStyleDeclaration> style = styleFromMatchedRulesAndInlineDecl(fullySelectedRoot); | 
| - | 
| -                // 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(CSSPropertyBackgroundImage) && static_cast<Element*>(fullySelectedRoot)->hasAttribute(backgroundAttr)) | 
| -                    style->setProperty(CSSPropertyBackgroundImage, "url('" + static_cast<Element*>(fullySelectedRoot)->getAttribute(backgroundAttr) + "')"); | 
| - | 
| -                if (style->length()) { | 
| -                    Vector<UChar> openTag; | 
| -                    DEFINE_STATIC_LOCAL(const String, divStyle, ("<div style=\"")); | 
| -                    append(openTag, divStyle); | 
| -                    appendAttributeValue(openTag, style->cssText(), documentIsHTML); | 
| -                    openTag.append('\"'); | 
| -                    openTag.append('>'); | 
| -                    preMarkups.append(String::adopt(openTag)); | 
| - | 
| -                    DEFINE_STATIC_LOCAL(const String, divCloseTag, ("</div>")); | 
| -                    markups.append(divCloseTag); | 
| -                } | 
| -            } else { | 
| -                preMarkups.append(getStartMarkup(ancestor, updatedRange.get(), annotate, convertBlocksToInlines)); | 
| -                markups.append(getEndMarkup(ancestor)); | 
| -            } | 
| -            if (nodes) | 
| -                nodes->append(ancestor); | 
| - | 
| -            lastClosed = ancestor; | 
| - | 
| -            if (ancestor == specialCommonAncestor) | 
| -                break; | 
| -        } | 
| -    } | 
| - | 
| -    DEFINE_STATIC_LOCAL(const String, styleSpanOpen, ("<span class=\"" AppleStyleSpanClass "\" style=\"")); | 
| -    DEFINE_STATIC_LOCAL(const String, styleSpanClose, ("</span>")); | 
| - | 
| -    // Add a wrapper span with the styles that all of the nodes in the markup inherit. | 
| -    Node* parentOfLastClosed = lastClosed ? lastClosed->parentNode() : 0; | 
| -    if (parentOfLastClosed && parentOfLastClosed->renderer()) { | 
| -        RefPtr<CSSMutableStyleDeclaration> style = computedStyle(parentOfLastClosed)->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(), parentOfLastClosed); | 
| - | 
| -        // Document default styles will be added on another wrapper span. | 
| -        removeDefaultStyles(style.get(), document); | 
| - | 
| -        // Since we are converting blocks to inlines, remove any inherited block properties that are in the style. | 
| -        // This cuts out meaningless properties and prevents properties from magically affecting blocks later | 
| -        // if the style is cloned for a new block element during a future editing operation. | 
| -        if (convertBlocksToInlines) | 
| -            style->removeBlockProperties(); | 
| - | 
| -        if (style->length() > 0) { | 
| -            Vector<UChar> openTag; | 
| -            append(openTag, styleSpanOpen); | 
| -            appendAttributeValue(openTag, style->cssText(), documentIsHTML); | 
| -            openTag.append('\"'); | 
| -            openTag.append('>'); | 
| -            preMarkups.append(String::adopt(openTag)); | 
| - | 
| -            markups.append(styleSpanClose); | 
| -        } | 
| -    } | 
| - | 
| -    if (lastClosed && lastClosed != document->documentElement()) { | 
| -        // Add a style span with the document's default styles.  We add these in a separate | 
| -        // span so that at paste time we can differentiate between document defaults and user | 
| -        // applied styles. | 
| -        RefPtr<CSSMutableStyleDeclaration> defaultStyle = computedStyle(document->documentElement())->copyInheritableProperties(); | 
| - | 
| -        if (defaultStyle->length() > 0) { | 
| -            Vector<UChar> openTag; | 
| -            append(openTag, styleSpanOpen); | 
| -            appendAttributeValue(openTag, defaultStyle->cssText(), documentIsHTML); | 
| -            openTag.append('\"'); | 
| -            openTag.append('>'); | 
| -            preMarkups.append(String::adopt(openTag)); | 
| -            markups.append(styleSpanClose); | 
| -        } | 
| -    } | 
| - | 
| -    // FIXME: The interchange newline should be placed in the block that it's in, not after all of the content, unconditionally. | 
| -    if (annotate && needInterchangeNewlineAfter(visibleEnd.previous())) | 
| -        markups.append(interchangeNewlineString); | 
| - | 
| -    if (deleteButton) | 
| -        deleteButton->enable(); | 
| - | 
| -    return joinMarkups(preMarkups, markups); | 
| -} | 
| - | 
| -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()); | 
| - | 
| -    RefPtr<DocumentFragment> fragment = element->createContextualFragment(markup); | 
| - | 
| -    if (fragment && !baseURL.isEmpty() && baseURL != blankURL() && baseURL != document->baseURL()) | 
| -        completeURLs(fragment.get(), baseURL); | 
| - | 
| -    return fragment.release(); | 
| -} | 
| - | 
| -String createMarkup(const Node* node, EChildrenOnly includeChildren, Vector<Node*>* nodes) | 
| -{ | 
| -    Vector<UChar> result; | 
| - | 
| -    if (!node) | 
| -        return ""; | 
| - | 
| -    Document* document = node->document(); | 
| -    Frame* frame = document->frame(); | 
| -    DeleteButtonController* deleteButton = frame ? frame->editor()->deleteButtonController() : 0; | 
| - | 
| -    // disable the delete button so it's elements are not serialized into the markup | 
| -    if (deleteButton) { | 
| -        if (node->isDescendantOf(deleteButton->containerElement())) | 
| -            return ""; | 
| -        deleteButton->disable(); | 
| -    } | 
| - | 
| -    appendMarkup(result, const_cast<Node*>(node), includeChildren, nodes); | 
| - | 
| -    if (deleteButton) | 
| -        deleteButton->enable(); | 
| - | 
| -    return String::adopt(result); | 
| -} | 
| - | 
| -static void fillContainerFromString(ContainerNode* paragraph, const String& string) | 
| -{ | 
| -    Document* document = paragraph->document(); | 
| - | 
| -    ExceptionCode ec = 0; | 
| -    if (string.isEmpty()) { | 
| -        paragraph->appendChild(createBlockPlaceholderElement(document), ec); | 
| -        ASSERT(ec == 0); | 
| -        return; | 
| -    } | 
| - | 
| -    ASSERT(string.find('\n') == -1); | 
| - | 
| -    Vector<String> tabList; | 
| -    string.split('\t', true, tabList); | 
| -    String tabText = ""; | 
| -    bool first = true; | 
| -    size_t numEntries = tabList.size(); | 
| -    for (size_t i = 0; i < numEntries; ++i) { | 
| -        const String& s = tabList[i]; | 
| - | 
| -        // append the non-tab textual part | 
| -        if (!s.isEmpty()) { | 
| -            if (!tabText.isEmpty()) { | 
| -                paragraph->appendChild(createTabSpanElement(document, tabText), ec); | 
| -                ASSERT(ec == 0); | 
| -                tabText = ""; | 
| -            } | 
| -            RefPtr<Node> textNode = document->createTextNode(stringWithRebalancedWhitespace(s, first, i + 1 == numEntries)); | 
| -            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 (i + 1 != numEntries) | 
| -            tabText.append('\t'); | 
| -        else if (!tabText.isEmpty()) { | 
| -            paragraph->appendChild(createTabSpanElement(document, tabText), ec); | 
| -            ASSERT(ec == 0); | 
| -        } | 
| - | 
| -        first = false; | 
| -    } | 
| -} | 
| - | 
| -PassRefPtr<DocumentFragment> createFragmentFromText(Range* context, const String& text) | 
| -{ | 
| -    if (!context) | 
| -        return 0; | 
| - | 
| -    Node* styleNode = context->firstNode(); | 
| -    if (!styleNode) { | 
| -        styleNode = context->startPosition().node(); | 
| -        if (!styleNode) | 
| -            return 0; | 
| -    } | 
| - | 
| -    Document* document = styleNode->document(); | 
| -    RefPtr<DocumentFragment> fragment = document->createDocumentFragment(); | 
| - | 
| -    if (text.isEmpty()) | 
| -        return fragment.release(); | 
| - | 
| -    String string = text; | 
| -    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 = createBreakElement(document); | 
| -            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. | 
| -    Node* blockNode = enclosingBlock(context->firstNode()); | 
| -    Element* block = static_cast<Element*>(blockNode); | 
| -    bool useClonesOfEnclosingBlock = blockNode | 
| -        && blockNode->isElementNode() | 
| -        && !block->hasTagName(bodyTag) | 
| -        && !block->hasTagName(htmlTag) | 
| -        && block != editableRootForPosition(context->startPosition()); | 
| - | 
| -    Vector<String> list; | 
| -    string.split('\n', true, list); // true gets us empty strings in the list | 
| -    size_t numLines = list.size(); | 
| -    for (size_t i = 0; i < numLines; ++i) { | 
| -        const String& s = list[i]; | 
| - | 
| -        RefPtr<Element> element; | 
| -        if (s.isEmpty() && i + 1 == numLines) { | 
| -            // For last line, use the "magic BR" rather than a P. | 
| -            element = createBreakElement(document); | 
| -            element->setAttribute(classAttr, AppleInterchangeNewline); | 
| -        } else { | 
| -            if (useClonesOfEnclosingBlock) | 
| -                element = block->cloneElement(); | 
| -            else | 
| -                element = createDefaultParagraphElement(document); | 
| -            fillContainerFromString(element.get(), s); | 
| -        } | 
| -        fragment->appendChild(element.release(), ec); | 
| -        ASSERT(ec == 0); | 
| -    } | 
| -    return fragment.release(); | 
| -} | 
| - | 
| -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; | 
| -    size_t size = nodes.size(); | 
| -    for (size_t i = 0; i < size; ++i) { | 
| -        RefPtr<Element> element = createDefaultParagraphElement(document); | 
| -        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(); | 
| -} | 
| - | 
| -String createFullMarkup(const Node* node) | 
| -{ | 
| -    if (!node) | 
| -        return String(); | 
| - | 
| -    Document* document = node->document(); | 
| -    if (!document) | 
| -        return String(); | 
| - | 
| -    Frame* frame = document->frame(); | 
| -    if (!frame) | 
| -        return String(); | 
| - | 
| -    // FIXME: This is never "for interchange". Is that right? | 
| -    String markupString = createMarkup(node, IncludeNode, 0); | 
| -    Node::NodeType nodeType = node->nodeType(); | 
| -    if (nodeType != Node::DOCUMENT_NODE && nodeType != Node::DOCUMENT_TYPE_NODE) | 
| -        markupString = frame->documentTypeString() + markupString; | 
| - | 
| -    return markupString; | 
| -} | 
| - | 
| -String createFullMarkup(const Range* range) | 
| -{ | 
| -    if (!range) | 
| -        return String(); | 
| - | 
| -    Node* node = range->startContainer(); | 
| -    if (!node) | 
| -        return String(); | 
| - | 
| -    Document* document = node->document(); | 
| -    if (!document) | 
| -        return String(); | 
| - | 
| -    Frame* frame = document->frame(); | 
| -    if (!frame) | 
| -        return String(); | 
| - | 
| -    // FIXME: This is always "for interchange". Is that right? See the previous method. | 
| -    return frame->documentTypeString() + createMarkup(range, 0, AnnotateForInterchange); | 
| -} | 
| - | 
| -} | 
| - | 
| - | 
| +/* | 
| + * Copyright (C) 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. | 
| + * | 
| + * Redistribution and use in source and binary forms, with or without | 
| + * modification, are permitted provided that the following conditions | 
| + * are met: | 
| + * 1. Redistributions of source code must retain the above copyright | 
| + *    notice, this list of conditions and the following disclaimer. | 
| + * 2. Redistributions in binary form must reproduce the above copyright | 
| + *    notice, this list of conditions and the following disclaimer in the | 
| + *    documentation and/or other materials provided with the distribution. | 
| + * | 
| + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY | 
| + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | 
| + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR | 
| + * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR | 
| + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | 
| + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | 
| + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR | 
| + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY | 
| + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | 
| + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | 
| + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | 
| + */ | 
| + | 
| +#include "config.h" | 
| +#include "markup.h" | 
| + | 
| +#include "CDATASection.h" | 
| +#include "CharacterNames.h" | 
| +#include "Comment.h" | 
| +#include "CSSComputedStyleDeclaration.h" | 
| +#include "CSSPrimitiveValue.h" | 
| +#include "CSSProperty.h" | 
| +#include "CSSPropertyNames.h" | 
| +#include "CSSRule.h" | 
| +#include "CSSRuleList.h" | 
| +#include "CSSStyleRule.h" | 
| +#include "CSSStyleSelector.h" | 
| +#include "CSSValue.h" | 
| +#include "CSSValueKeywords.h" | 
| +#include "DeleteButtonController.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 "Logging.h" | 
| +#include "ProcessingInstruction.h" | 
| +#include "QualifiedName.h" | 
| +#include "Range.h" | 
| +#include "Selection.h" | 
| +#include "TextIterator.h" | 
| +#include "htmlediting.h" | 
| +#include "visible_units.h" | 
| +#include <wtf/StdLibExtras.h> | 
| + | 
| +using namespace std; | 
| + | 
| +namespace WebCore { | 
| + | 
| +using namespace HTMLNames; | 
| + | 
| +static inline bool shouldSelfClose(const Node *node); | 
| + | 
| +class AttributeChange { | 
| +public: | 
| +    AttributeChange() | 
| +        : m_name(nullAtom, nullAtom, nullAtom) | 
| +    { | 
| +    } | 
| + | 
| +    AttributeChange(PassRefPtr<Element> element, const QualifiedName& name, const String& value) | 
| +        : m_element(element), m_name(name), m_value(value) | 
| +    { | 
| +    } | 
| + | 
| +    void apply() | 
| +    { | 
| +        m_element->setAttribute(m_name, m_value); | 
| +    } | 
| + | 
| +private: | 
| +    RefPtr<Element> m_element; | 
| +    QualifiedName m_name; | 
| +    String m_value; | 
| +}; | 
| + | 
| +static void appendAttributeValue(Vector<UChar>& result, const String& attr, bool escapeNBSP) | 
| +{ | 
| +    const UChar* uchars = attr.characters(); | 
| +    unsigned len = attr.length(); | 
| +    unsigned lastCopiedFrom = 0; | 
| + | 
| +    DEFINE_STATIC_LOCAL(const String, ampEntity, ("&")); | 
| +    DEFINE_STATIC_LOCAL(const String, gtEntity, (">")); | 
| +    DEFINE_STATIC_LOCAL(const String, ltEntity, ("<")); | 
| +    DEFINE_STATIC_LOCAL(const String, quotEntity, (""")); | 
| +    DEFINE_STATIC_LOCAL(const String, nbspEntity, (" ")); | 
| + | 
| +    for (unsigned i = 0; i < len; ++i) { | 
| +        UChar c = uchars[i]; | 
| +        switch (c) { | 
| +            case '&': | 
| +                result.append(uchars + lastCopiedFrom, i - lastCopiedFrom); | 
| +                append(result, ampEntity); | 
| +                lastCopiedFrom = i + 1; | 
| +                break; | 
| +            case '<': | 
| +                result.append(uchars + lastCopiedFrom, i - lastCopiedFrom); | 
| +                append(result, ltEntity); | 
| +                lastCopiedFrom = i + 1; | 
| +                break; | 
| +            case '>': | 
| +                result.append(uchars + lastCopiedFrom, i - lastCopiedFrom); | 
| +                append(result, gtEntity); | 
| +                lastCopiedFrom = i + 1; | 
| +                break; | 
| +            case '"': | 
| +                result.append(uchars + lastCopiedFrom, i - lastCopiedFrom); | 
| +                append(result, quotEntity); | 
| +                lastCopiedFrom = i + 1; | 
| +                break; | 
| +            case noBreakSpace: | 
| +                if (escapeNBSP) { | 
| +                    result.append(uchars + lastCopiedFrom, i - lastCopiedFrom); | 
| +                    append(result, nbspEntity); | 
| +                    lastCopiedFrom = i + 1; | 
| +                } | 
| +                break; | 
| +        } | 
| +    } | 
| + | 
| +    result.append(uchars + lastCopiedFrom, len - lastCopiedFrom); | 
| +} | 
| + | 
| +static void appendEscapedContent(Vector<UChar>& result, pair<const UChar*, size_t> range, bool escapeNBSP) | 
| +{ | 
| +    const UChar* uchars = range.first; | 
| +    unsigned len = range.second; | 
| +    unsigned lastCopiedFrom = 0; | 
| + | 
| +    DEFINE_STATIC_LOCAL(const String, ampEntity, ("&")); | 
| +    DEFINE_STATIC_LOCAL(const String, gtEntity, (">")); | 
| +    DEFINE_STATIC_LOCAL(const String, ltEntity, ("<")); | 
| +    DEFINE_STATIC_LOCAL(const String, nbspEntity, (" ")); | 
| + | 
| +    for (unsigned i = 0; i < len; ++i) { | 
| +        UChar c = uchars[i]; | 
| +        switch (c) { | 
| +            case '&': | 
| +                result.append(uchars + lastCopiedFrom, i - lastCopiedFrom); | 
| +                append(result, ampEntity); | 
| +                lastCopiedFrom = i + 1; | 
| +                break; | 
| +            case '<': | 
| +                result.append(uchars + lastCopiedFrom, i - lastCopiedFrom); | 
| +                append(result, ltEntity); | 
| +                lastCopiedFrom = i + 1; | 
| +                break; | 
| +            case '>': | 
| +                result.append(uchars + lastCopiedFrom, i - lastCopiedFrom); | 
| +                append(result, gtEntity); | 
| +                lastCopiedFrom = i + 1; | 
| +                break; | 
| +            case noBreakSpace: | 
| +                if (escapeNBSP) { | 
| +                    result.append(uchars + lastCopiedFrom, i - lastCopiedFrom); | 
| +                    append(result, nbspEntity); | 
| +                    lastCopiedFrom = i + 1; | 
| +                } | 
| +                break; | 
| +        } | 
| +    } | 
| + | 
| +    result.append(uchars + lastCopiedFrom, len - lastCopiedFrom); | 
| +} | 
| + | 
| +static String escapeContentText(const String& in, bool escapeNBSP) | 
| +{ | 
| +    Vector<UChar> buffer; | 
| +    appendEscapedContent(buffer, make_pair(in.characters(), in.length()), escapeNBSP); | 
| +    return String::adopt(buffer); | 
| +} | 
| + | 
| +static void appendQuotedURLAttributeValue(Vector<UChar>& result, const String& urlString) | 
| +{ | 
| +    UChar quoteChar = '\"'; | 
| +    String strippedURLString = urlString.stripWhiteSpace(); | 
| +    if (protocolIs(strippedURLString, "javascript")) { | 
| +        // minimal escaping for javascript urls | 
| +        if (strippedURLString.contains('"')) { | 
| +            if (strippedURLString.contains('\'')) | 
| +                strippedURLString.replace('\"', """); | 
| +            else | 
| +                quoteChar = '\''; | 
| +        } | 
| +        result.append(quoteChar); | 
| +        append(result, strippedURLString); | 
| +        result.append(quoteChar); | 
| +        return; | 
| +    } | 
| + | 
| +    // FIXME: This does not fully match other browsers. Firefox percent-escapes non-ASCII characters for innerHTML. | 
| +    result.append(quoteChar); | 
| +    appendAttributeValue(result, urlString, false); | 
| +    result.append(quoteChar); | 
| +} | 
| + | 
| +static String stringValueForRange(const Node* node, const Range* range) | 
| +{ | 
| +    if (!range) | 
| +        return node->nodeValue(); | 
| + | 
| +    String str = node->nodeValue(); | 
| +    ExceptionCode ec; | 
| +    if (node == range->endContainer(ec)) | 
| +        str.truncate(range->endOffset(ec)); | 
| +    if (node == range->startContainer(ec)) | 
| +        str.remove(0, range->startOffset(ec)); | 
| +    return str; | 
| +} | 
| + | 
| +static inline pair<const UChar*, size_t> ucharRange(const Node *node, const Range *range) | 
| +{ | 
| +    String str = node->nodeValue(); | 
| +    const UChar* characters = str.characters(); | 
| +    size_t length = str.length(); | 
| + | 
| +    if (range) { | 
| +        ExceptionCode ec; | 
| +        if (node == range->endContainer(ec)) | 
| +            length = range->endOffset(ec); | 
| +        if (node == range->startContainer(ec)) { | 
| +            size_t start = range->startOffset(ec); | 
| +            characters += start; | 
| +            length -= start; | 
| +        } | 
| +    } | 
| + | 
| +    return make_pair(characters, length); | 
| +} | 
| + | 
| +static inline void appendUCharRange(Vector<UChar>& result, const pair<const UChar*, size_t> range) | 
| +{ | 
| +    result.append(range.first, range.second); | 
| +} | 
| + | 
| +static String renderedText(const Node* node, const Range* range) | 
| +{ | 
| +    if (!node->isTextNode()) | 
| +        return String(); | 
| + | 
| +    ExceptionCode ec; | 
| +    const Text* textNode = static_cast<const Text*>(node); | 
| +    unsigned startOffset = 0; | 
| +    unsigned endOffset = textNode->length(); | 
| + | 
| +    if (range && node == range->startContainer(ec)) | 
| +        startOffset = range->startOffset(ec); | 
| +    if (range && node == range->endContainer(ec)) | 
| +        endOffset = range->endOffset(ec); | 
| + | 
| +    Position start(const_cast<Node*>(node), startOffset); | 
| +    Position end(const_cast<Node*>(node), endOffset); | 
| +    return plainText(Range::create(node->document(), start, end).get()); | 
| +} | 
| + | 
| +static PassRefPtr<CSSMutableStyleDeclaration> styleFromMatchedRulesForElement(Element* element, bool authorOnly = true) | 
| +{ | 
| +    RefPtr<CSSMutableStyleDeclaration> style = CSSMutableStyleDeclaration::create(); | 
| +    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 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 void removeDefaultStyles(CSSMutableStyleDeclaration* style, Document* document) | 
| +{ | 
| +    if (!document || !document->documentElement()) | 
| +        return; | 
| + | 
| +    RefPtr<CSSMutableStyleDeclaration> documentStyle = computedStyle(document->documentElement())->copyInheritableProperties(); | 
| +    documentStyle->diff(style); | 
| +} | 
| + | 
| +static bool shouldAddNamespaceElem(const Element* elem) | 
| +{ | 
| +    // Don't add namespace attribute if it is already defined for this elem. | 
| +    const AtomicString& prefix = elem->prefix(); | 
| +    AtomicString attr = !prefix.isEmpty() ? "xmlns:" + prefix : "xmlns"; | 
| +    return !elem->hasAttribute(attr); | 
| +} | 
| + | 
| +static bool shouldAddNamespaceAttr(const Attribute* attr, HashMap<AtomicStringImpl*, AtomicStringImpl*>& namespaces) | 
| +{ | 
| +    // Don't add namespace attributes twice | 
| +    DEFINE_STATIC_LOCAL(const AtomicString, xmlnsURI, ("http://www.w3.org/2000/xmlns/")); | 
| +    DEFINE_STATIC_LOCAL(const QualifiedName, xmlnsAttr, (nullAtom, "xmlns", xmlnsURI)); | 
| +    if (attr->name() == xmlnsAttr) { | 
| +        namespaces.set(emptyAtom.impl(), attr->value().impl()); | 
| +        return false; | 
| +    } | 
| + | 
| +    QualifiedName xmlnsPrefixAttr("xmlns", attr->localName(), xmlnsURI); | 
| +    if (attr->name() == xmlnsPrefixAttr) { | 
| +        namespaces.set(attr->localName().impl(), attr->value().impl()); | 
| +        return false; | 
| +    } | 
| + | 
| +    return true; | 
| +} | 
| + | 
| +static void appendNamespace(Vector<UChar>& result, const AtomicString& prefix, const AtomicString& ns, HashMap<AtomicStringImpl*, AtomicStringImpl*>& namespaces) | 
| +{ | 
| +    if (ns.isEmpty()) | 
| +        return; | 
| + | 
| +    // Use emptyAtoms's impl() for both null and empty strings since the HashMap can't handle 0 as a key | 
| +    AtomicStringImpl* pre = prefix.isEmpty() ? emptyAtom.impl() : prefix.impl(); | 
| +    AtomicStringImpl* foundNS = namespaces.get(pre); | 
| +    if (foundNS != ns.impl()) { | 
| +        namespaces.set(pre, ns.impl()); | 
| +        DEFINE_STATIC_LOCAL(const String, xmlns, ("xmlns")); | 
| +        result.append(' '); | 
| +        append(result, xmlns); | 
| +        if (!prefix.isEmpty()) { | 
| +            result.append(':'); | 
| +            append(result, prefix); | 
| +        } | 
| + | 
| +        result.append('='); | 
| +        result.append('"'); | 
| +        appendAttributeValue(result, ns, false); | 
| +        result.append('"'); | 
| +    } | 
| +} | 
| + | 
| +static void appendDocumentType(Vector<UChar>& result, const DocumentType* n) | 
| +{ | 
| +    if (n->name().isEmpty()) | 
| +        return; | 
| + | 
| +    append(result, "<!DOCTYPE "); | 
| +    append(result, n->name()); | 
| +    if (!n->publicId().isEmpty()) { | 
| +        append(result, " PUBLIC \""); | 
| +        append(result, n->publicId()); | 
| +        append(result, "\""); | 
| +        if (!n->systemId().isEmpty()) { | 
| +            append(result, " \""); | 
| +            append(result, n->systemId()); | 
| +            append(result, "\""); | 
| +        } | 
| +    } else if (!n->systemId().isEmpty()) { | 
| +        append(result, " SYSTEM \""); | 
| +        append(result, n->systemId()); | 
| +        append(result, "\""); | 
| +    } | 
| +    if (!n->internalSubset().isEmpty()) { | 
| +        append(result, " ["); | 
| +        append(result, n->internalSubset()); | 
| +        append(result, "]"); | 
| +    } | 
| +    append(result, ">"); | 
| +} | 
| + | 
| +static void appendStartMarkup(Vector<UChar>& result, const Node *node, const Range *range, EAnnotateForInterchange annotate, bool convertBlocksToInlines = false, HashMap<AtomicStringImpl*, AtomicStringImpl*>* namespaces = 0) | 
| +{ | 
| +    bool documentIsHTML = node->document()->isHTMLDocument(); | 
| +    switch (node->nodeType()) { | 
| +        case Node::TEXT_NODE: { | 
| +            if (Node* parent = node->parentNode()) { | 
| +                if (parent->hasTagName(scriptTag) | 
| +                    || parent->hasTagName(styleTag) | 
| +                    || parent->hasTagName(textareaTag) | 
| +                    || parent->hasTagName(xmpTag)) { | 
| +                    appendUCharRange(result, ucharRange(node, range)); | 
| +                    break; | 
| +                } | 
| +            } | 
| +            if (!annotate) { | 
| +                appendEscapedContent(result, ucharRange(node, range), documentIsHTML); | 
| +                break; | 
| +            } | 
| + | 
| +            bool useRenderedText = !enclosingNodeWithTag(Position(const_cast<Node*>(node), 0), selectTag); | 
| +            String markup = escapeContentText(useRenderedText ? renderedText(node, range) : stringValueForRange(node, range), false); | 
| +            if (annotate) | 
| +                markup = convertHTMLTextToInterchangeFormat(markup, static_cast<const Text*>(node)); | 
| +            append(result, markup); | 
| +            break; | 
| +        } | 
| +        case Node::COMMENT_NODE: | 
| +            // FIXME: Comment content is not escaped, but XMLSerializer (and possibly other callers) should raise an exception if it includes "-->". | 
| +            append(result, "<!--"); | 
| +            append(result, static_cast<const Comment*>(node)->nodeValue()); | 
| +            append(result, "-->"); | 
| +            break; | 
| +        case Node::DOCUMENT_NODE: | 
| +        case Node::DOCUMENT_FRAGMENT_NODE: | 
| +            break; | 
| +        case Node::DOCUMENT_TYPE_NODE: | 
| +            appendDocumentType(result, static_cast<const DocumentType*>(node)); | 
| +            break; | 
| +        case Node::PROCESSING_INSTRUCTION_NODE: { | 
| +            // FIXME: PI data is not escaped, but XMLSerializer (and possibly other callers) this should raise an exception if it includes "?>". | 
| +            const ProcessingInstruction* n = static_cast<const ProcessingInstruction*>(node); | 
| +            append(result, "<?"); | 
| +            append(result, n->target()); | 
| +            append(result, " "); | 
| +            append(result, n->data()); | 
| +            append(result, "?>"); | 
| +            break; | 
| +        } | 
| +        case Node::ELEMENT_NODE: { | 
| +            result.append('<'); | 
| +            const Element* el = static_cast<const Element*>(node); | 
| +            bool convert = convertBlocksToInlines & isBlock(const_cast<Node*>(node)); | 
| +            append(result, el->nodeNamePreservingCase()); | 
| +            NamedAttrMap *attrs = el->attributes(); | 
| +            unsigned length = attrs->length(); | 
| +            if (!documentIsHTML && namespaces && shouldAddNamespaceElem(el)) | 
| +                appendNamespace(result, el->prefix(), el->namespaceURI(), *namespaces); | 
| + | 
| +            for (unsigned int i = 0; i < length; i++) { | 
| +                Attribute *attr = attrs->attributeItem(i); | 
| +                // We'll handle the style attribute separately, below. | 
| +                if (attr->name() == styleAttr && el->isHTMLElement() && (annotate || convert)) | 
| +                    continue; | 
| +                result.append(' '); | 
| + | 
| +                if (documentIsHTML) | 
| +                    append(result, attr->name().localName()); | 
| +                else | 
| +                    append(result, attr->name().toString()); | 
| + | 
| +                result.append('='); | 
| + | 
| +                if (el->isURLAttribute(attr)) | 
| +                    appendQuotedURLAttributeValue(result, attr->value()); | 
| +                else { | 
| +                    result.append('\"'); | 
| +                    appendAttributeValue(result, attr->value(), documentIsHTML); | 
| +                    result.append('\"'); | 
| +                } | 
| + | 
| +                if (!documentIsHTML && namespaces && shouldAddNamespaceAttr(attr, *namespaces)) | 
| +                    appendNamespace(result, attr->prefix(), attr->namespaceURI(), *namespaces); | 
| +            } | 
| + | 
| +            if (el->isHTMLElement() && (annotate || convert)) { | 
| +                Element* element = const_cast<Element*>(el); | 
| +                RefPtr<CSSMutableStyleDeclaration> style = static_cast<HTMLElement*>(element)->getInlineStyleDecl()->copy(); | 
| +                if (annotate) { | 
| +                    RefPtr<CSSMutableStyleDeclaration> styleFromMatchedRules = styleFromMatchedRulesForElement(const_cast<Element*>(el)); | 
| +                    // Styles from the inline style declaration, held in the variable "style", take precedence | 
| +                    // over those from matched rules. | 
| +                    styleFromMatchedRules->merge(style.get()); | 
| +                    style = styleFromMatchedRules; | 
| + | 
| +                    RefPtr<CSSComputedStyleDeclaration> computedStyleForElement = computedStyle(element); | 
| +                    RefPtr<CSSMutableStyleDeclaration> fromComputedStyle = CSSMutableStyleDeclaration::create(); | 
| + | 
| +                    { | 
| +                        CSSMutableStyleDeclaration::const_iterator end = style->end(); | 
| +                        for (CSSMutableStyleDeclaration::const_iterator it = style->begin(); it != end; ++it) { | 
| +                            const CSSProperty& property = *it; | 
| +                            CSSValue* value = property.value(); | 
| +                            // The property value, if it's a percentage, may not reflect the actual computed value. | 
| +                            // For example: style="height: 1%; overflow: visible;" in quirksmode | 
| +                            // FIXME: There are others like this, see <rdar://problem/5195123> Slashdot copy/paste fidelity problem | 
| +                            if (value->cssValueType() == CSSValue::CSS_PRIMITIVE_VALUE) | 
| +                                if (static_cast<CSSPrimitiveValue*>(value)->primitiveType() == CSSPrimitiveValue::CSS_PERCENTAGE) | 
| +                                    if (RefPtr<CSSValue> computedPropertyValue = computedStyleForElement->getPropertyCSSValue(property.id())) | 
| +                                        fromComputedStyle->addParsedProperty(CSSProperty(property.id(), computedPropertyValue)); | 
| +                        } | 
| +                    } | 
| + | 
| +                    style->merge(fromComputedStyle.get()); | 
| +                } | 
| +                if (convert) | 
| +                    style->setProperty(CSSPropertyDisplay, CSSValueInline, true); | 
| +                if (style->length() > 0) { | 
| +                    DEFINE_STATIC_LOCAL(const String, stylePrefix, (" style=\"")); | 
| +                    append(result, stylePrefix); | 
| +                    appendAttributeValue(result, style->cssText(), documentIsHTML); | 
| +                    result.append('\"'); | 
| +                } | 
| +            } | 
| + | 
| +            if (shouldSelfClose(el)) { | 
| +                if (el->isHTMLElement()) | 
| +                    result.append(' '); // XHTML 1.0 <-> HTML compatibility. | 
| +                result.append('/'); | 
| +            } | 
| +            result.append('>'); | 
| +            break; | 
| +        } | 
| +        case Node::CDATA_SECTION_NODE: { | 
| +            // FIXME: CDATA content is not escaped, but XMLSerializer (and possibly other callers) should raise an exception if it includes "]]>". | 
| +            const CDATASection* n = static_cast<const CDATASection*>(node); | 
| +            append(result, "<![CDATA["); | 
| +            append(result, n->data()); | 
| +            append(result, "]]>"); | 
| +            break; | 
| +        } | 
| +        case Node::ATTRIBUTE_NODE: | 
| +        case Node::ENTITY_NODE: | 
| +        case Node::ENTITY_REFERENCE_NODE: | 
| +        case Node::NOTATION_NODE: | 
| +        case Node::XPATH_NAMESPACE_NODE: | 
| +            ASSERT_NOT_REACHED(); | 
| +            break; | 
| +    } | 
| +} | 
| + | 
| +static String getStartMarkup(const Node *node, const Range *range, EAnnotateForInterchange annotate, bool convertBlocksToInlines = false, HashMap<AtomicStringImpl*, AtomicStringImpl*>* namespaces = 0) | 
| +{ | 
| +    Vector<UChar> result; | 
| +    appendStartMarkup(result, node, range, annotate, convertBlocksToInlines, namespaces); | 
| +    return String::adopt(result); | 
| +} | 
| + | 
| +static inline bool doesHTMLForbidEndTag(const Node *node) | 
| +{ | 
| +    if (node->isHTMLElement()) { | 
| +        const HTMLElement* htmlElt = static_cast<const HTMLElement*>(node); | 
| +        return (htmlElt->endTagRequirement() == TagStatusForbidden); | 
| +    } | 
| +    return false; | 
| +} | 
| + | 
| +// Rules of self-closure | 
| +// 1. No elements in HTML documents use the self-closing syntax. | 
| +// 2. Elements w/ children never self-close because they use a separate end tag. | 
| +// 3. HTML elements which do not have a "forbidden" end tag will close with a separate end tag. | 
| +// 4. Other elements self-close. | 
| +static inline bool shouldSelfClose(const Node *node) | 
| +{ | 
| +    if (node->document()->isHTMLDocument()) | 
| +        return false; | 
| +    if (node->hasChildNodes()) | 
| +        return false; | 
| +    if (node->isHTMLElement() && !doesHTMLForbidEndTag(node)) | 
| +        return false; | 
| +    return true; | 
| +} | 
| + | 
| +static void appendEndMarkup(Vector<UChar>& result, const Node* node) | 
| +{ | 
| +    if (!node->isElementNode() || shouldSelfClose(node) || (!node->hasChildNodes() && doesHTMLForbidEndTag(node))) | 
| +        return; | 
| + | 
| +    result.append('<'); | 
| +    result.append('/'); | 
| +    append(result, static_cast<const Element*>(node)->nodeNamePreservingCase()); | 
| +    result.append('>'); | 
| +} | 
| + | 
| +static String getEndMarkup(const Node *node) | 
| +{ | 
| +    Vector<UChar> result; | 
| +    appendEndMarkup(result, node); | 
| +    return String::adopt(result); | 
| +} | 
| + | 
| +static void appendMarkup(Vector<UChar>& result, Node* startNode, bool onlyIncludeChildren, Vector<Node*>* nodes, const HashMap<AtomicStringImpl*, AtomicStringImpl*>* namespaces = 0) | 
| +{ | 
| +    HashMap<AtomicStringImpl*, AtomicStringImpl*> namespaceHash; | 
| +    if (namespaces) | 
| +        namespaceHash = *namespaces; | 
| + | 
| +    if (!onlyIncludeChildren) { | 
| +        if (nodes) | 
| +            nodes->append(startNode); | 
| + | 
| +        appendStartMarkup(result,startNode, 0, DoNotAnnotateForInterchange, false, &namespaceHash); | 
| +    } | 
| +    // print children | 
| +    if (!(startNode->document()->isHTMLDocument() && doesHTMLForbidEndTag(startNode))) | 
| +        for (Node* current = startNode->firstChild(); current; current = current->nextSibling()) | 
| +            appendMarkup(result, current, false, nodes, &namespaceHash); | 
| + | 
| +    // Print my ending tag | 
| +    if (!onlyIncludeChildren) | 
| +        appendEndMarkup(result, startNode); | 
| +} | 
| + | 
| +static void completeURLs(Node* node, const String& baseURL) | 
| +{ | 
| +    Vector<AttributeChange> changes; | 
| + | 
| +    KURL parsedBaseURL(baseURL); | 
| + | 
| +    Node* end = node->traverseNextSibling(); | 
| +    for (Node* n = node; n != end; n = n->traverseNextNode()) { | 
| +        if (n->isElementNode()) { | 
| +            Element* e = static_cast<Element*>(n); | 
| +            NamedAttrMap* attrs = e->attributes(); | 
| +            unsigned length = attrs->length(); | 
| +            for (unsigned i = 0; i < length; i++) { | 
| +                Attribute* attr = attrs->attributeItem(i); | 
| +                if (e->isURLAttribute(attr)) | 
| +                    changes.append(AttributeChange(e, attr->name(), KURL(parsedBaseURL, attr->value()).string())); | 
| +            } | 
| +        } | 
| +    } | 
| + | 
| +    size_t numChanges = changes.size(); | 
| +    for (size_t i = 0; i < numChanges; ++i) | 
| +        changes[i].apply(); | 
| +} | 
| + | 
| +static bool needInterchangeNewlineAfter(const VisiblePosition& v) | 
| +{ | 
| +    VisiblePosition next = v.next(); | 
| +    Node* upstreamNode = next.deepEquivalent().upstream().node(); | 
| +    Node* downstreamNode = v.deepEquivalent().downstream().node(); | 
| +    // Add an interchange newline if a paragraph break is selected and a br won't already be added to the markup to represent it. | 
| +    return isEndOfParagraph(v) && isStartOfParagraph(next) && !(upstreamNode->hasTagName(brTag) && upstreamNode == downstreamNode); | 
| +} | 
| + | 
| +static PassRefPtr<CSSMutableStyleDeclaration> styleFromMatchedRulesAndInlineDecl(const Node* node) | 
| +{ | 
| +    if (!node->isHTMLElement()) | 
| +        return 0; | 
| + | 
| +    // FIXME: Having to const_cast here is ugly, but it is quite a bit of work to untangle | 
| +    // the non-const-ness of styleFromMatchedRulesForElement. | 
| +    HTMLElement* element = const_cast<HTMLElement*>(static_cast<const HTMLElement*>(node)); | 
| +    RefPtr<CSSMutableStyleDeclaration> style = styleFromMatchedRulesForElement(element); | 
| +    RefPtr<CSSMutableStyleDeclaration> inlineStyleDecl = element->getInlineStyleDecl(); | 
| +    style->merge(inlineStyleDecl.get()); | 
| +    return style.release(); | 
| +} | 
| + | 
| +static bool propertyMissingOrEqualToNone(CSSMutableStyleDeclaration* style, int propertyID) | 
| +{ | 
| +    if (!style) | 
| +        return false; | 
| +    RefPtr<CSSValue> value = style->getPropertyCSSValue(propertyID); | 
| +    if (!value) | 
| +        return true; | 
| +    if (!value->isPrimitiveValue()) | 
| +        return false; | 
| +    return static_cast<CSSPrimitiveValue*>(value.get())->getIdent() == CSSValueNone; | 
| +} | 
| + | 
| +static bool elementHasTextDecorationProperty(const Node* node) | 
| +{ | 
| +    RefPtr<CSSMutableStyleDeclaration> style = styleFromMatchedRulesAndInlineDecl(node); | 
| +    if (!style) | 
| +        return false; | 
| +    return !propertyMissingOrEqualToNone(style.get(), CSSPropertyTextDecoration); | 
| +} | 
| + | 
| +static String joinMarkups(const Vector<String>& preMarkups, const Vector<String>& postMarkups) | 
| +{ | 
| +    size_t length = 0; | 
| + | 
| +    size_t preCount = preMarkups.size(); | 
| +    for (size_t i = 0; i < preCount; ++i) | 
| +        length += preMarkups[i].length(); | 
| + | 
| +    size_t postCount = postMarkups.size(); | 
| +    for (size_t i = 0; i < postCount; ++i) | 
| +        length += postMarkups[i].length(); | 
| + | 
| +    Vector<UChar> result; | 
| +    result.reserveInitialCapacity(length); | 
| + | 
| +    for (size_t i = preCount; i > 0; --i) | 
| +        append(result, preMarkups[i - 1]); | 
| + | 
| +    for (size_t i = 0; i < postCount; ++i) | 
| +        append(result, postMarkups[i]); | 
| + | 
| +    return String::adopt(result); | 
| +} | 
| + | 
| +bool isSpecialAncestorBlock(Node* node) | 
| +{ | 
| +    if (!node || !isBlock(node)) | 
| +        return false; | 
| + | 
| +    return node->hasTagName(listingTag) || | 
| +           node->hasTagName(olTag) || | 
| +           node->hasTagName(preTag) || | 
| +           node->hasTagName(tableTag) || | 
| +           node->hasTagName(ulTag) || | 
| +           node->hasTagName(xmpTag) || | 
| +           node->hasTagName(h1Tag) || | 
| +           node->hasTagName(h2Tag) || | 
| +           node->hasTagName(h3Tag) || | 
| +           node->hasTagName(h4Tag) || | 
| +           node->hasTagName(h5Tag); | 
| +} | 
| + | 
| +// 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() | 
| +String createMarkup(const Range* range, Vector<Node*>* nodes, EAnnotateForInterchange annotate, bool convertBlocksToInlines) | 
| +{ | 
| +    DEFINE_STATIC_LOCAL(const String, interchangeNewlineString, ("<br class=\"" AppleInterchangeNewline "\">")); | 
| + | 
| +    if (!range) | 
| +        return ""; | 
| + | 
| +    Document* document = range->ownerDocument(); | 
| +    if (!document) | 
| +        return ""; | 
| + | 
| +    bool documentIsHTML = document->isHTMLDocument(); | 
| + | 
| +    // Disable the delete button so it's elements are not serialized into the markup, | 
| +    // but make sure neither endpoint is inside the delete user interface. | 
| +    Frame* frame = document->frame(); | 
| +    DeleteButtonController* deleteButton = frame ? frame->editor()->deleteButtonController() : 0; | 
| +    RefPtr<Range> updatedRange = avoidIntersectionWithNode(range, deleteButton ? deleteButton->containerElement() : 0); | 
| +    if (!updatedRange) | 
| +        return ""; | 
| + | 
| +    if (deleteButton) | 
| +        deleteButton->disable(); | 
| + | 
| +    ExceptionCode ec = 0; | 
| +    bool collapsed = updatedRange->collapsed(ec); | 
| +    ASSERT(ec == 0); | 
| +    if (collapsed) | 
| +        return ""; | 
| +    Node* commonAncestor = updatedRange->commonAncestorContainer(ec); | 
| +    ASSERT(ec == 0); | 
| +    if (!commonAncestor) | 
| +        return ""; | 
| + | 
| +    document->updateLayoutIgnorePendingStylesheets(); | 
| + | 
| +    Vector<String> markups; | 
| +    Vector<String> preMarkups; | 
| +    Node* pastEnd = updatedRange->pastLastNode(); | 
| +    Node* lastClosed = 0; | 
| +    Vector<Node*> ancestorsToClose; | 
| + | 
| +    Node* startNode = updatedRange->firstNode(); | 
| +    VisiblePosition visibleStart(updatedRange->startPosition(), VP_DEFAULT_AFFINITY); | 
| +    VisiblePosition visibleEnd(updatedRange->endPosition(), VP_DEFAULT_AFFINITY); | 
| +    if (annotate && needInterchangeNewlineAfter(visibleStart)) { | 
| +        if (visibleStart == visibleEnd.previous()) { | 
| +            if (deleteButton) | 
| +                deleteButton->enable(); | 
| +            return interchangeNewlineString; | 
| +        } | 
| + | 
| +        markups.append(interchangeNewlineString); | 
| +        startNode = visibleStart.next().deepEquivalent().node(); | 
| +    } | 
| + | 
| +    Node* next; | 
| +    for (Node* n = startNode; n != pastEnd; n = next) { | 
| + | 
| +        // According to <rdar://problem/5730668>, it is possible for n to blow past pastEnd and become null here.  This | 
| +        // shouldn't be possible.  This null check will prevent crashes (but create too much markup) and the ASSERT will | 
| +        // hopefully lead us to understanding the problem. | 
| +        ASSERT(n); | 
| +        if (!n) | 
| +            break; | 
| + | 
| +        next = n->traverseNextNode(); | 
| +        bool skipDescendants = false; | 
| +        bool addMarkupForNode = true; | 
| + | 
| +        if (!n->renderer() && !enclosingNodeWithTag(Position(n, 0), selectTag)) { | 
| +            skipDescendants = true; | 
| +            addMarkupForNode = false; | 
| +            next = n->traverseNextSibling(); | 
| +            // Don't skip over pastEnd. | 
| +            if (pastEnd && pastEnd->isDescendantOf(n)) | 
| +                next = pastEnd; | 
| +        } | 
| + | 
| +        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 (addMarkupForNode) { | 
| +            markups.append(getStartMarkup(n, updatedRange.get(), annotate)); | 
| +            if (nodes) | 
| +                nodes->append(n); | 
| +        } | 
| + | 
| +        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(getEndMarkup(n)); | 
| +                lastClosed = n; | 
| +            } | 
| + | 
| +            // Check if the node is the last leaf of a tree. | 
| +            if (!n->nextSibling() || next == pastEnd) { | 
| +                if (!ancestorsToClose.isEmpty()) { | 
| +                    // Close up the ancestors. | 
| +                    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(getEndMarkup(ancestor)); | 
| +                        lastClosed = ancestor; | 
| +                        ancestorsToClose.removeLast(); | 
| +                    } 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)); | 
| +                        preMarkups.append(getStartMarkup(parent, updatedRange.get(), annotate)); | 
| +                        markups.append(getEndMarkup(parent)); | 
| +                        if (nodes) | 
| +                            nodes->append(parent); | 
| +                        lastClosed = parent; | 
| +                    } | 
| +                } | 
| +            } | 
| +        } else if (addMarkupForNode && !skipDescendants) | 
| +            // We added markup for this node, and we're descending into it.  Set it to close eventually. | 
| +            ancestorsToClose.append(n); | 
| +    } | 
| + | 
| +    // Include ancestors that aren't completely inside the range but are required to retain | 
| +    // the structure and appearance of the copied markup. | 
| +    Node* specialCommonAncestor = 0; | 
| +    Node* commonAncestorBlock = commonAncestor ? enclosingBlock(commonAncestor) : 0; | 
| +    if (annotate && commonAncestorBlock) { | 
| +        if (commonAncestorBlock->hasTagName(tbodyTag) || commonAncestorBlock->hasTagName(trTag)) { | 
| +            Node* table = commonAncestorBlock->parentNode(); | 
| +            while (table && !table->hasTagName(tableTag)) | 
| +                table = table->parentNode(); | 
| +            if (table) | 
| +                specialCommonAncestor = table; | 
| +        } else if (isSpecialAncestorBlock(commonAncestorBlock)) | 
| +            specialCommonAncestor = commonAncestorBlock; | 
| +    } | 
| + | 
| +    // Retain the Mail quote level by including all ancestor mail block quotes. | 
| +    if (lastClosed && annotate) { | 
| +        for (Node *ancestor = lastClosed->parentNode(); ancestor; ancestor = ancestor->parentNode()) | 
| +            if (isMailBlockquote(ancestor)) | 
| +                specialCommonAncestor = ancestor; | 
| +    } | 
| + | 
| +    Node* checkAncestor = specialCommonAncestor ? specialCommonAncestor : commonAncestor; | 
| +    if (checkAncestor->renderer()) { | 
| +        RefPtr<CSSMutableStyleDeclaration> checkAncestorStyle = computedStyle(checkAncestor)->copyInheritableProperties(); | 
| +        if (!propertyMissingOrEqualToNone(checkAncestorStyle.get(), CSSPropertyWebkitTextDecorationsInEffect)) | 
| +            specialCommonAncestor = enclosingNodeOfType(Position(checkAncestor, 0), &elementHasTextDecorationProperty); | 
| +    } | 
| + | 
| +    // If a single tab is selected, commonAncestor will be a text node inside a tab span. | 
| +    // If two or more tabs are selected, commonAncestor will be the tab span. | 
| +    // In either case, if there is a specialCommonAncestor already, it will necessarily be above | 
| +    // any tab span that needs to be included. | 
| +    if (!specialCommonAncestor && isTabSpanTextNode(commonAncestor)) | 
| +        specialCommonAncestor = commonAncestor->parentNode(); | 
| +    if (!specialCommonAncestor && isTabSpanNode(commonAncestor)) | 
| +        specialCommonAncestor = commonAncestor; | 
| + | 
| +    if (Node *enclosingAnchor = enclosingNodeWithTag(Position(specialCommonAncestor ? specialCommonAncestor : commonAncestor, 0), aTag)) | 
| +        specialCommonAncestor = enclosingAnchor; | 
| + | 
| +    Node* body = enclosingNodeWithTag(Position(commonAncestor, 0), bodyTag); | 
| +    // FIXME: Only include markup for a fully selected root (and ancestors of lastClosed up to that root) if | 
| +    // there are styles/attributes on those nodes that need to be included to preserve the appearance of the copied markup. | 
| +    // FIXME: Do this for all fully selected blocks, not just the body. | 
| +    Node* fullySelectedRoot = body && *Selection::selectionFromContentsOfNode(body).toNormalizedRange() == *updatedRange ? body : 0; | 
| +    if (annotate && fullySelectedRoot) | 
| +        specialCommonAncestor = fullySelectedRoot; | 
| + | 
| +    if (specialCommonAncestor && lastClosed) { | 
| +        // Also include all of the ancestors of lastClosed up to this special ancestor. | 
| +        for (Node* ancestor = lastClosed->parentNode(); ancestor; ancestor = ancestor->parentNode()) { | 
| +            if (ancestor == fullySelectedRoot && !convertBlocksToInlines) { | 
| +                RefPtr<CSSMutableStyleDeclaration> style = styleFromMatchedRulesAndInlineDecl(fullySelectedRoot); | 
| + | 
| +                // 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(CSSPropertyBackgroundImage) && static_cast<Element*>(fullySelectedRoot)->hasAttribute(backgroundAttr)) | 
| +                    style->setProperty(CSSPropertyBackgroundImage, "url('" + static_cast<Element*>(fullySelectedRoot)->getAttribute(backgroundAttr) + "')"); | 
| + | 
| +                if (style->length()) { | 
| +                    Vector<UChar> openTag; | 
| +                    DEFINE_STATIC_LOCAL(const String, divStyle, ("<div style=\"")); | 
| +                    append(openTag, divStyle); | 
| +                    appendAttributeValue(openTag, style->cssText(), documentIsHTML); | 
| +                    openTag.append('\"'); | 
| +                    openTag.append('>'); | 
| +                    preMarkups.append(String::adopt(openTag)); | 
| + | 
| +                    DEFINE_STATIC_LOCAL(const String, divCloseTag, ("</div>")); | 
| +                    markups.append(divCloseTag); | 
| +                } | 
| +            } else { | 
| +                preMarkups.append(getStartMarkup(ancestor, updatedRange.get(), annotate, convertBlocksToInlines)); | 
| +                markups.append(getEndMarkup(ancestor)); | 
| +            } | 
| +            if (nodes) | 
| +                nodes->append(ancestor); | 
| + | 
| +            lastClosed = ancestor; | 
| + | 
| +            if (ancestor == specialCommonAncestor) | 
| +                break; | 
| +        } | 
| +    } | 
| + | 
| +    DEFINE_STATIC_LOCAL(const String, styleSpanOpen, ("<span class=\"" AppleStyleSpanClass "\" style=\"")); | 
| +    DEFINE_STATIC_LOCAL(const String, styleSpanClose, ("</span>")); | 
| + | 
| +    // Add a wrapper span with the styles that all of the nodes in the markup inherit. | 
| +    Node* parentOfLastClosed = lastClosed ? lastClosed->parentNode() : 0; | 
| +    if (parentOfLastClosed && parentOfLastClosed->renderer()) { | 
| +        RefPtr<CSSMutableStyleDeclaration> style = computedStyle(parentOfLastClosed)->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(), parentOfLastClosed); | 
| + | 
| +        // Document default styles will be added on another wrapper span. | 
| +        removeDefaultStyles(style.get(), document); | 
| + | 
| +        // Since we are converting blocks to inlines, remove any inherited block properties that are in the style. | 
| +        // This cuts out meaningless properties and prevents properties from magically affecting blocks later | 
| +        // if the style is cloned for a new block element during a future editing operation. | 
| +        if (convertBlocksToInlines) | 
| +            style->removeBlockProperties(); | 
| + | 
| +        if (style->length() > 0) { | 
| +            Vector<UChar> openTag; | 
| +            append(openTag, styleSpanOpen); | 
| +            appendAttributeValue(openTag, style->cssText(), documentIsHTML); | 
| +            openTag.append('\"'); | 
| +            openTag.append('>'); | 
| +            preMarkups.append(String::adopt(openTag)); | 
| + | 
| +            markups.append(styleSpanClose); | 
| +        } | 
| +    } | 
| + | 
| +    if (lastClosed && lastClosed != document->documentElement()) { | 
| +        // Add a style span with the document's default styles.  We add these in a separate | 
| +        // span so that at paste time we can differentiate between document defaults and user | 
| +        // applied styles. | 
| +        RefPtr<CSSMutableStyleDeclaration> defaultStyle = computedStyle(document->documentElement())->copyInheritableProperties(); | 
| + | 
| +        if (defaultStyle->length() > 0) { | 
| +            Vector<UChar> openTag; | 
| +            append(openTag, styleSpanOpen); | 
| +            appendAttributeValue(openTag, defaultStyle->cssText(), documentIsHTML); | 
| +            openTag.append('\"'); | 
| +            openTag.append('>'); | 
| +            preMarkups.append(String::adopt(openTag)); | 
| +            markups.append(styleSpanClose); | 
| +        } | 
| +    } | 
| + | 
| +    // FIXME: The interchange newline should be placed in the block that it's in, not after all of the content, unconditionally. | 
| +    if (annotate && needInterchangeNewlineAfter(visibleEnd.previous())) | 
| +        markups.append(interchangeNewlineString); | 
| + | 
| +    if (deleteButton) | 
| +        deleteButton->enable(); | 
| + | 
| +    return joinMarkups(preMarkups, markups); | 
| +} | 
| + | 
| +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()); | 
| + | 
| +    RefPtr<DocumentFragment> fragment = element->createContextualFragment(markup); | 
| + | 
| +    if (fragment && !baseURL.isEmpty() && baseURL != blankURL() && baseURL != document->baseURL()) | 
| +        completeURLs(fragment.get(), baseURL); | 
| + | 
| +    return fragment.release(); | 
| +} | 
| + | 
| +String createMarkup(const Node* node, EChildrenOnly includeChildren, Vector<Node*>* nodes) | 
| +{ | 
| +    Vector<UChar> result; | 
| + | 
| +    if (!node) | 
| +        return ""; | 
| + | 
| +    Document* document = node->document(); | 
| +    Frame* frame = document->frame(); | 
| +    DeleteButtonController* deleteButton = frame ? frame->editor()->deleteButtonController() : 0; | 
| + | 
| +    // disable the delete button so it's elements are not serialized into the markup | 
| +    if (deleteButton) { | 
| +        if (node->isDescendantOf(deleteButton->containerElement())) | 
| +            return ""; | 
| +        deleteButton->disable(); | 
| +    } | 
| + | 
| +    appendMarkup(result, const_cast<Node*>(node), includeChildren, nodes); | 
| + | 
| +    if (deleteButton) | 
| +        deleteButton->enable(); | 
| + | 
| +    return String::adopt(result); | 
| +} | 
| + | 
| +static void fillContainerFromString(ContainerNode* paragraph, const String& string) | 
| +{ | 
| +    Document* document = paragraph->document(); | 
| + | 
| +    ExceptionCode ec = 0; | 
| +    if (string.isEmpty()) { | 
| +        paragraph->appendChild(createBlockPlaceholderElement(document), ec); | 
| +        ASSERT(ec == 0); | 
| +        return; | 
| +    } | 
| + | 
| +    ASSERT(string.find('\n') == -1); | 
| + | 
| +    Vector<String> tabList; | 
| +    string.split('\t', true, tabList); | 
| +    String tabText = ""; | 
| +    bool first = true; | 
| +    size_t numEntries = tabList.size(); | 
| +    for (size_t i = 0; i < numEntries; ++i) { | 
| +        const String& s = tabList[i]; | 
| + | 
| +        // append the non-tab textual part | 
| +        if (!s.isEmpty()) { | 
| +            if (!tabText.isEmpty()) { | 
| +                paragraph->appendChild(createTabSpanElement(document, tabText), ec); | 
| +                ASSERT(ec == 0); | 
| +                tabText = ""; | 
| +            } | 
| +            RefPtr<Node> textNode = document->createTextNode(stringWithRebalancedWhitespace(s, first, i + 1 == numEntries)); | 
| +            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 (i + 1 != numEntries) | 
| +            tabText.append('\t'); | 
| +        else if (!tabText.isEmpty()) { | 
| +            paragraph->appendChild(createTabSpanElement(document, tabText), ec); | 
| +            ASSERT(ec == 0); | 
| +        } | 
| + | 
| +        first = false; | 
| +    } | 
| +} | 
| + | 
| +PassRefPtr<DocumentFragment> createFragmentFromText(Range* context, const String& text) | 
| +{ | 
| +    if (!context) | 
| +        return 0; | 
| + | 
| +    Node* styleNode = context->firstNode(); | 
| +    if (!styleNode) { | 
| +        styleNode = context->startPosition().node(); | 
| +        if (!styleNode) | 
| +            return 0; | 
| +    } | 
| + | 
| +    Document* document = styleNode->document(); | 
| +    RefPtr<DocumentFragment> fragment = document->createDocumentFragment(); | 
| + | 
| +    if (text.isEmpty()) | 
| +        return fragment.release(); | 
| + | 
| +    String string = text; | 
| +    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 = createBreakElement(document); | 
| +            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. | 
| +    Node* blockNode = enclosingBlock(context->firstNode()); | 
| +    Element* block = static_cast<Element*>(blockNode); | 
| +    bool useClonesOfEnclosingBlock = blockNode | 
| +        && blockNode->isElementNode() | 
| +        && !block->hasTagName(bodyTag) | 
| +        && !block->hasTagName(htmlTag) | 
| +        && block != editableRootForPosition(context->startPosition()); | 
| + | 
| +    Vector<String> list; | 
| +    string.split('\n', true, list); // true gets us empty strings in the list | 
| +    size_t numLines = list.size(); | 
| +    for (size_t i = 0; i < numLines; ++i) { | 
| +        const String& s = list[i]; | 
| + | 
| +        RefPtr<Element> element; | 
| +        if (s.isEmpty() && i + 1 == numLines) { | 
| +            // For last line, use the "magic BR" rather than a P. | 
| +            element = createBreakElement(document); | 
| +            element->setAttribute(classAttr, AppleInterchangeNewline); | 
| +        } else { | 
| +            if (useClonesOfEnclosingBlock) | 
| +                element = block->cloneElement(); | 
| +            else | 
| +                element = createDefaultParagraphElement(document); | 
| +            fillContainerFromString(element.get(), s); | 
| +        } | 
| +        fragment->appendChild(element.release(), ec); | 
| +        ASSERT(ec == 0); | 
| +    } | 
| +    return fragment.release(); | 
| +} | 
| + | 
| +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; | 
| +    size_t size = nodes.size(); | 
| +    for (size_t i = 0; i < size; ++i) { | 
| +        RefPtr<Element> element = createDefaultParagraphElement(document); | 
| +        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(); | 
| +} | 
| + | 
| +String createFullMarkup(const Node* node) | 
| +{ | 
| +    if (!node) | 
| +        return String(); | 
| + | 
| +    Document* document = node->document(); | 
| +    if (!document) | 
| +        return String(); | 
| + | 
| +    Frame* frame = document->frame(); | 
| +    if (!frame) | 
| +        return String(); | 
| + | 
| +    // FIXME: This is never "for interchange". Is that right? | 
| +    String markupString = createMarkup(node, IncludeNode, 0); | 
| +    Node::NodeType nodeType = node->nodeType(); | 
| +    if (nodeType != Node::DOCUMENT_NODE && nodeType != Node::DOCUMENT_TYPE_NODE) | 
| +        markupString = frame->documentTypeString() + markupString; | 
| + | 
| +    return markupString; | 
| +} | 
| + | 
| +String createFullMarkup(const Range* range) | 
| +{ | 
| +    if (!range) | 
| +        return String(); | 
| + | 
| +    Node* node = range->startContainer(); | 
| +    if (!node) | 
| +        return String(); | 
| + | 
| +    Document* document = node->document(); | 
| +    if (!document) | 
| +        return String(); | 
| + | 
| +    Frame* frame = document->frame(); | 
| +    if (!frame) | 
| +        return String(); | 
| + | 
| +    // FIXME: This is always "for interchange". Is that right? See the previous method. | 
| +    return frame->documentTypeString() + createMarkup(range, 0, AnnotateForInterchange); | 
| +} | 
| + | 
| +} | 
| + | 
| + | 
|  |