diff options
Diffstat (limited to 'WebCore/editing/markup.cpp')
-rw-r--r-- | WebCore/editing/markup.cpp | 835 |
1 files changed, 446 insertions, 389 deletions
diff --git a/WebCore/editing/markup.cpp b/WebCore/editing/markup.cpp index b1ec07c..1baf1bc 100644 --- a/WebCore/editing/markup.cpp +++ b/WebCore/editing/markup.cpp @@ -26,6 +26,7 @@ #include "config.h" #include "markup.h" +#include "ApplyStyleCommand.h" #include "CDATASection.h" #include "CharacterNames.h" #include "Comment.h" @@ -45,6 +46,7 @@ #include "DocumentType.h" #include "Editor.h" #include "Frame.h" +#include "HTMLBodyElement.h" #include "HTMLElement.h" #include "HTMLNames.h" #include "InlineTextBox.h" @@ -52,13 +54,12 @@ #include "Logging.h" #include "ProcessingInstruction.h" #include "Range.h" -#include "VisibleSelection.h" #include "TextIterator.h" +#include "VisibleSelection.h" #include "XMLNSNames.h" #include "htmlediting.h" #include "visible_units.h" #include <wtf/StdLibExtras.h> -#include "ApplyStyleCommand.h" using namespace std; @@ -66,7 +67,7 @@ namespace WebCore { using namespace HTMLNames; -static inline bool shouldSelfClose(const Node *node); +static bool propertyMissingOrEqualToNone(CSSStyleDeclaration*, int propertyID); class AttributeChange { public: @@ -91,10 +92,139 @@ private: String m_value; }; -static void appendAttributeValue(Vector<UChar>& result, const String& attr, bool escapeNBSP) +typedef HashMap<AtomicStringImpl*, AtomicStringImpl*> Namespaces; + +class MarkupAccumulator { +public: + enum RangeFullySelectsNode { DoesFullySelectNode, DoesNotFullySelectNode }; + + MarkupAccumulator(Vector<Node*>* nodes, EAbsoluteURLs shouldResolveURLs, EAnnotateForInterchange shouldAnnotate, const Range* range = 0) + : m_nodes(nodes) + , m_shouldResolveURLs(shouldResolveURLs) + , m_shouldAnnotate(shouldAnnotate) + , m_range(range) + { + } + void appendString(const String&); + void appendStartTag(Node*, Namespaces* = 0); + void appendEndTag(Node*); + void wrapWithNode(Node*, bool convertBlocksToInlines = false, RangeFullySelectsNode = DoesFullySelectNode); + void wrapWithStyleNode(CSSStyleDeclaration*, Document*, bool isBlock = false); + String takeResults(); + +private: + void appendAttributeValue(Vector<UChar>& result, const String& attribute, bool escapeNBSP); + String escapeContentText(const String&, bool escapeNBSP); + void appendQuotedURLAttributeValue(Vector<UChar>& result, const String& urlString); + String stringValueForRange(const Node*, const Range*); + pair<const UChar*, size_t> ucharRange(const Node*, const Range *); + void appendUCharRange(Vector<UChar>& result, const pair<const UChar*, size_t>& range); + String renderedText(const Node*, const Range*); + bool shouldAddNamespaceElement(const Element*); + bool shouldAddNamespaceAttribute(const Attribute*, Namespaces&); + void appendNamespace(Vector<UChar>& result, const AtomicString& prefix, const AtomicString& namespaceURI, Namespaces&); + void appendText(Vector<UChar>& out, Text*); + void appendComment(Vector<UChar>& out, const String& comment); + void appendDocumentType(Vector<UChar>& result, const DocumentType*); + void appendProcessingInstruction(Vector<UChar>& out, const String& target, const String& data); + void removeExteriorStyles(CSSMutableStyleDeclaration*); + void appendElement(Vector<UChar>& out, Element* element, bool addDisplayInline, Namespaces*, RangeFullySelectsNode); + void appendCDATASection(Vector<UChar>& out, const String& section); + void appendStartMarkup(Vector<UChar>& result, const Node*, bool convertBlocksToInlines, Namespaces*, RangeFullySelectsNode); + bool shouldSelfClose(const Node*); + void appendEndMarkup(Vector<UChar>& result, const Node*); + + bool shouldResolveURLs() { return m_shouldResolveURLs == AbsoluteURLs; } + bool shouldAnnotate() { return m_shouldAnnotate == AnnotateForInterchange; } + + Vector<Node*>* const m_nodes; + const bool m_shouldResolveURLs; + const EAnnotateForInterchange m_shouldAnnotate; + const Range* const m_range; + Vector<String> m_reversedPrecedingMarkup; + Vector<String> m_succeedingMarkup; +}; + +void MarkupAccumulator::appendString(const String& string) +{ + m_succeedingMarkup.append(string); +} + +void MarkupAccumulator::appendStartTag(Node* node, Namespaces* namespaces) +{ + Vector<UChar> markup; + appendStartMarkup(markup, node, false, namespaces, DoesFullySelectNode); + m_succeedingMarkup.append(String::adopt(markup)); + if (m_nodes) + m_nodes->append(node); +} + +void MarkupAccumulator::appendEndTag(Node* node) +{ + Vector<UChar> markup; + appendEndMarkup(markup, node); + m_succeedingMarkup.append(String::adopt(markup)); +} + +void MarkupAccumulator::wrapWithNode(Node* node, bool convertBlocksToInlines, RangeFullySelectsNode rangeFullySelectsNode) +{ + Vector<UChar> markup; + appendStartMarkup(markup, node, convertBlocksToInlines, 0, rangeFullySelectsNode); + m_reversedPrecedingMarkup.append(String::adopt(markup)); + appendEndTag(node); + if (m_nodes) + m_nodes->append(node); +} + +void MarkupAccumulator::wrapWithStyleNode(CSSStyleDeclaration* style, Document* document, bool isBlock) +{ + // All text-decoration-related elements should have been treated as special ancestors + // If we ever hit this ASSERT, we should export StyleChange in ApplyStyleCommand and use it here + ASSERT(propertyMissingOrEqualToNone(style, CSSPropertyTextDecoration) && propertyMissingOrEqualToNone(style, CSSPropertyWebkitTextDecorationsInEffect)); + DEFINE_STATIC_LOCAL(const String, divStyle, ("<div style=\"")); + DEFINE_STATIC_LOCAL(const String, divClose, ("</div>")); + DEFINE_STATIC_LOCAL(const String, styleSpanOpen, ("<span class=\"" AppleStyleSpanClass "\" style=\"")); + DEFINE_STATIC_LOCAL(const String, styleSpanClose, ("</span>")); + Vector<UChar> openTag; + append(openTag, isBlock ? divStyle : styleSpanOpen); + appendAttributeValue(openTag, style->cssText(), document->isHTMLDocument()); + openTag.append('\"'); + openTag.append('>'); + m_reversedPrecedingMarkup.append(String::adopt(openTag)); + m_succeedingMarkup.append(isBlock ? divClose : styleSpanClose); +} + +// FIXME: This is a very inefficient way of accumulating the markup. +// We're converting results of appendStartMarkup and appendEndMarkup from Vector<UChar> to String +// and then back to Vector<UChar> and again to String here. +String MarkupAccumulator::takeResults() +{ + size_t length = 0; + + size_t preCount = m_reversedPrecedingMarkup.size(); + for (size_t i = 0; i < preCount; ++i) + length += m_reversedPrecedingMarkup[i].length(); + + size_t postCount = m_succeedingMarkup.size(); + for (size_t i = 0; i < postCount; ++i) + length += m_succeedingMarkup[i].length(); + + Vector<UChar> result; + result.reserveInitialCapacity(length); + + for (size_t i = preCount; i > 0; --i) + append(result, m_reversedPrecedingMarkup[i - 1]); + + for (size_t i = 0; i < postCount; ++i) + append(result, m_succeedingMarkup[i]); + + return String::adopt(result); +} + +void MarkupAccumulator::appendAttributeValue(Vector<UChar>& result, const String& attribute, bool escapeNBSP) { - const UChar* uchars = attr.characters(); - unsigned len = attr.length(); + const UChar* uchars = attribute.characters(); + unsigned len = attribute.length(); unsigned lastCopiedFrom = 0; DEFINE_STATIC_LOCAL(const String, ampEntity, ("&")); @@ -106,33 +236,32 @@ static void appendAttributeValue(Vector<UChar>& result, const String& attr, bool 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; - } + 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) break; + result.append(uchars + lastCopiedFrom, i - lastCopiedFrom); + append(result, nbspEntity); + lastCopiedFrom = i + 1; } } @@ -153,42 +282,42 @@ static void appendEscapedContent(Vector<UChar>& result, pair<const UChar*, size_ 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; - } + 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) break; + 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) +String MarkupAccumulator::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) + +void MarkupAccumulator::appendQuotedURLAttributeValue(Vector<UChar>& result, const String& urlString) { UChar quoteChar = '\"'; String strippedURLString = urlString.stripWhiteSpace(); @@ -211,8 +340,8 @@ static void appendQuotedURLAttributeValue(Vector<UChar>& result, const String& u appendAttributeValue(result, urlString, false); result.append(quoteChar); } - -static String stringValueForRange(const Node* node, const Range* range) + +String MarkupAccumulator::stringValueForRange(const Node* node, const Range* range) { if (!range) return node->nodeValue(); @@ -226,7 +355,7 @@ static String stringValueForRange(const Node* node, const Range* range) return str; } -static inline pair<const UChar*, size_t> ucharRange(const Node *node, const Range *range) +pair<const UChar*, size_t> MarkupAccumulator::ucharRange(const Node* node, const Range* range) { String str = node->nodeValue(); const UChar* characters = str.characters(); @@ -245,13 +374,13 @@ static inline pair<const UChar*, size_t> ucharRange(const Node *node, const Rang return make_pair(characters, length); } - -static inline void appendUCharRange(Vector<UChar>& result, const pair<const UChar*, size_t> range) + +void MarkupAccumulator::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) + +String MarkupAccumulator::renderedText(const Node* node, const Range* range) { if (!node->isTextNode()) return String(); @@ -304,44 +433,44 @@ static void removeDefaultStyles(CSSMutableStyleDeclaration* style, Document* doc prepareEditingStyleToApplyAt(style, Position(document->documentElement(), 0)); } -static bool shouldAddNamespaceElem(const Element* elem) +bool MarkupAccumulator::shouldAddNamespaceElement(const Element* element) { // Don't add namespace attribute if it is already defined for this elem. - const AtomicString& prefix = elem->prefix(); + const AtomicString& prefix = element->prefix(); AtomicString attr = !prefix.isEmpty() ? "xmlns:" + prefix : "xmlns"; - return !elem->hasAttribute(attr); + return !element->hasAttribute(attr); } -static bool shouldAddNamespaceAttr(const Attribute* attr, HashMap<AtomicStringImpl*, AtomicStringImpl*>& namespaces) +bool MarkupAccumulator::shouldAddNamespaceAttribute(const Attribute* attribute, Namespaces& namespaces) { namespaces.checkConsistency(); // Don't add namespace attributes twice - if (attr->name() == XMLNSNames::xmlnsAttr) { - namespaces.set(emptyAtom.impl(), attr->value().impl()); + if (attribute->name() == XMLNSNames::xmlnsAttr) { + namespaces.set(emptyAtom.impl(), attribute->value().impl()); return false; } - QualifiedName xmlnsPrefixAttr(xmlnsAtom, attr->localName(), XMLNSNames::xmlnsNamespaceURI); - if (attr->name() == xmlnsPrefixAttr) { - namespaces.set(attr->localName().impl(), attr->value().impl()); + QualifiedName xmlnsPrefixAttr(xmlnsAtom, attribute->localName(), XMLNSNames::xmlnsNamespaceURI); + if (attribute->name() == xmlnsPrefixAttr) { + namespaces.set(attribute->localName().impl(), attribute->value().impl()); return false; } return true; } -static void appendNamespace(Vector<UChar>& result, const AtomicString& prefix, const AtomicString& ns, HashMap<AtomicStringImpl*, AtomicStringImpl*>& namespaces) +void MarkupAccumulator::appendNamespace(Vector<UChar>& result, const AtomicString& prefix, const AtomicString& namespaceURI, Namespaces& namespaces) { namespaces.checkConsistency(); - if (ns.isEmpty()) + if (namespaceURI.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()); + if (foundNS != namespaceURI.impl()) { + namespaces.set(pre, namespaceURI.impl()); result.append(' '); append(result, xmlnsAtom.string()); if (!prefix.isEmpty()) { @@ -351,12 +480,42 @@ static void appendNamespace(Vector<UChar>& result, const AtomicString& prefix, c result.append('='); result.append('"'); - appendAttributeValue(result, ns, false); + appendAttributeValue(result, namespaceURI, false); result.append('"'); } } -static void appendDocumentType(Vector<UChar>& result, const DocumentType* n) +void MarkupAccumulator::appendText(Vector<UChar>& out, Text* text) +{ + const QualifiedName* parentName = 0; + if (text->parentElement()) + parentName = &static_cast<Element*>(text->parentElement())->tagQName(); + + if (parentName && (*parentName == scriptTag || *parentName == styleTag || *parentName == xmpTag)) { + appendUCharRange(out, ucharRange(text, m_range)); + return; + } + + if (!shouldAnnotate() || (parentName && *parentName == textareaTag)) { + appendEscapedContent(out, ucharRange(text, m_range), text->document()->isHTMLDocument()); + return; + } + + bool useRenderedText = !enclosingNodeWithTag(Position(text, 0), selectTag); + String markup = escapeContentText(useRenderedText ? renderedText(text, m_range) : stringValueForRange(text, m_range), false); + markup = convertHTMLTextToInterchangeFormat(markup, text); + append(out, markup); +} + +void MarkupAccumulator::appendComment(Vector<UChar>& out, const String& comment) +{ + // FIXME: Comment content is not escaped, but XMLSerializer (and possibly other callers) should raise an exception if it includes "-->". + append(out, "<!--"); + append(out, comment); + append(out, "-->"); +} + +void MarkupAccumulator::appendDocumentType(Vector<UChar>& result, const DocumentType* n) { if (n->name().isEmpty()) return; @@ -385,183 +544,167 @@ static void appendDocumentType(Vector<UChar>& result, const DocumentType* n) append(result, ">"); } -static void removeExteriorStyles(CSSMutableStyleDeclaration* style) +void MarkupAccumulator::appendProcessingInstruction(Vector<UChar>& out, const String& target, const String& data) { - style->removeProperty(CSSPropertyFloat); + // FIXME: PI data is not escaped, but XMLSerializer (and possibly other callers) this should raise an exception if it includes "?>". + append(out, "<?"); + append(out, target); + append(out, " "); + append(out, data); + append(out, "?>"); } -enum RangeFullySelectsNode { DoesFullySelectNode, DoesNotFullySelectNode }; - -static void appendStartMarkup(Vector<UChar>& result, const Node* node, const Range* range, EAnnotateForInterchange annotate, EAbsoluteURLs absoluteURLs, bool convertBlocksToInlines, HashMap<AtomicStringImpl*, AtomicStringImpl*>* namespaces, RangeFullySelectsNode rangeFullySelectsNode = DoesFullySelectNode) +void MarkupAccumulator::removeExteriorStyles(CSSMutableStyleDeclaration* style) { - if (namespaces) - namespaces->checkConsistency(); + style->removeProperty(CSSPropertyFloat); +} - 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(xmpTag)) { - appendUCharRange(result, ucharRange(node, range)); - break; - } else if (parent->hasTagName(textareaTag)) { - appendEscapedContent(result, ucharRange(node, range), documentIsHTML); - 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); - 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)->data()); - 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; +void MarkupAccumulator::appendElement(Vector<UChar>& out, Element* element, bool addDisplayInline, Namespaces* namespaces, RangeFullySelectsNode rangeFullySelectsNode) +{ + bool documentIsHTML = element->document()->isHTMLDocument(); + out.append('<'); + append(out, element->nodeNamePreservingCase()); + if (!documentIsHTML && namespaces && shouldAddNamespaceElement(element)) + appendNamespace(out, element->prefix(), element->namespaceURI(), *namespaces); + + NamedNodeMap* attributes = element->attributes(); + unsigned length = attributes->length(); + for (unsigned int i = 0; i < length; i++) { + Attribute* attribute = attributes->attributeItem(i); + // We'll handle the style attribute separately, below. + if (attribute->name() == styleAttr && element->isHTMLElement() && (shouldAnnotate() || addDisplayInline)) + continue; + out.append(' '); + + if (documentIsHTML) + append(out, attribute->name().localName()); + else + append(out, attribute->name().toString()); + + out.append('='); + + if (element->isURLAttribute(attribute)) { + // We don't want to complete file:/// URLs because it may contain sensitive information + // about the user's system. + if (shouldResolveURLs() && !element->document()->url().isLocalFile()) + appendQuotedURLAttributeValue(out, element->document()->completeURL(attribute->value()).string()); + else + appendQuotedURLAttributeValue(out, attribute->value().string()); + } else { + out.append('\"'); + appendAttributeValue(out, attribute->value(), documentIsHTML); + out.append('\"'); } - 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()); - NamedNodeMap *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)) { - // We don't want to complete file:/// URLs because it may contain sensitive information - // about the user's system. - if (absoluteURLs == AbsoluteURLs && !node->document()->url().isLocalFile()) - appendQuotedURLAttributeValue(result, node->document()->completeURL(attr->value()).string()); - else - appendQuotedURLAttributeValue(result, attr->value().string()); - } 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 the node is not fully selected by the range, then we don't want to keep styles that affect its relationship to the nodes around it - // only the ones that affect it and the nodes within it. - if (rangeFullySelectsNode == DoesNotFullySelectNode) - removeExteriorStyles(style.get()); - if (style->length() > 0) { - DEFINE_STATIC_LOCAL(const String, stylePrefix, (" style=\"")); - append(result, stylePrefix); - appendAttributeValue(result, style->cssText(), documentIsHTML); - result.append('\"'); + if (!documentIsHTML && namespaces && shouldAddNamespaceAttribute(attribute, *namespaces)) + appendNamespace(out, attribute->prefix(), attribute->namespaceURI(), *namespaces); + } + + if (element->isHTMLElement() && (shouldAnnotate() || addDisplayInline)) { + RefPtr<CSSMutableStyleDeclaration> style = static_cast<HTMLElement*>(element)->getInlineStyleDecl()->copy(); + if (shouldAnnotate()) { + RefPtr<CSSMutableStyleDeclaration> styleFromMatchedRules = styleFromMatchedRulesForElement(const_cast<Element*>(element)); + // 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)); } } - - if (shouldSelfClose(el)) { - if (el->isHTMLElement()) - result.append(' '); // XHTML 1.0 <-> HTML compatibility. - result.append('/'); - } - result.append('>'); - break; + style->merge(fromComputedStyle.get()); } - 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; + if (addDisplayInline) + style->setProperty(CSSPropertyDisplay, CSSValueInline, true); + // If the node is not fully selected by the range, then we don't want to keep styles that affect its relationship to the nodes around it + // only the ones that affect it and the nodes within it. + if (rangeFullySelectsNode == DoesNotFullySelectNode) + removeExteriorStyles(style.get()); + if (style->length() > 0) { + DEFINE_STATIC_LOCAL(const String, stylePrefix, (" style=\"")); + append(out, stylePrefix); + appendAttributeValue(out, style->cssText(), documentIsHTML); + out.append('\"'); } - 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; } + + if (shouldSelfClose(element)) { + if (element->isHTMLElement()) + out.append(' '); // XHTML 1.0 <-> HTML compatibility. + out.append('/'); + } + out.append('>'); } -static inline bool doesHTMLForbidEndTag(const Node *node) +void MarkupAccumulator::appendCDATASection(Vector<UChar>& out, const String& section) { - if (node->isHTMLElement()) { - const HTMLElement* htmlElt = static_cast<const HTMLElement*>(node); - return (htmlElt->endTagRequirement() == TagStatusForbidden); + // FIXME: CDATA content is not escaped, but XMLSerializer (and possibly other callers) should raise an exception if it includes "]]>". + append(out, "<![CDATA["); + append(out, section); + append(out, "]]>"); +} + +void MarkupAccumulator::appendStartMarkup(Vector<UChar>& result, const Node* node, bool convertBlocksToInlines, Namespaces* namespaces, RangeFullySelectsNode rangeFullySelectsNode) +{ + if (namespaces) + namespaces->checkConsistency(); + + switch (node->nodeType()) { + case Node::TEXT_NODE: + appendText(result, static_cast<Text*>(const_cast<Node*>(node))); + break; + case Node::COMMENT_NODE: + appendComment(result, static_cast<const Comment*>(node)->data()); + 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: + appendProcessingInstruction(result, static_cast<const ProcessingInstruction*>(node)->target(), static_cast<const ProcessingInstruction*>(node)->data()); + break; + case Node::ELEMENT_NODE: + appendElement(result, static_cast<Element*>(const_cast<Node*>(node)), convertBlocksToInlines && isBlock(const_cast<Node*>(node)), namespaces, rangeFullySelectsNode); + break; + case Node::CDATA_SECTION_NODE: + appendCDATASection(result, static_cast<const CDATASection*>(node)->data()); + 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; } - return false; +} + +static inline bool elementCannotHaveEndTag(const Node *node) +{ + if (!node->isHTMLElement()) + return false; + + // FIXME: ieForbidsInsertHTML may not be the right function to call here + // ieForbidsInsertHTML is used to disallow setting innerHTML/outerHTML + // or createContextualFragment. It does not necessarily align with + // which elements should be serialized w/o end tags. + return static_cast<const HTMLElement*>(node)->ieForbidsInsertHTML(); } // Rules of self-closure @@ -569,20 +712,20 @@ static inline bool doesHTMLForbidEndTag(const Node *node) // 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) +bool MarkupAccumulator::shouldSelfClose(const Node* node) { if (node->document()->isHTMLDocument()) return false; if (node->hasChildNodes()) return false; - if (node->isHTMLElement() && !doesHTMLForbidEndTag(node)) + if (node->isHTMLElement() && !elementCannotHaveEndTag(node)) return false; return true; } -static void appendEndMarkup(Vector<UChar>& result, const Node* node) +void MarkupAccumulator::appendEndMarkup(Vector<UChar>& result, const Node* node) { - if (!node->isElementNode() || shouldSelfClose(node) || (!node->hasChildNodes() && doesHTMLForbidEndTag(node))) + if (!node->isElementNode() || shouldSelfClose(node) || (!node->hasChildNodes() && elementCannotHaveEndTag(node))) return; result.append('<'); @@ -601,12 +744,12 @@ static void completeURLs(Node* node, const String& baseURL) for (Node* n = node; n != end; n = n->traverseNextNode()) { if (n->isElementNode()) { Element* e = static_cast<Element*>(n); - NamedNodeMap* attrs = e->attributes(); - unsigned length = attrs->length(); + NamedNodeMap* attributes = e->attributes(); + unsigned length = attributes->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())); + Attribute* attribute = attributes->attributeItem(i); + if (e->isURLAttribute(attribute)) + changes.append(AttributeChange(e, attribute->name(), KURL(parsedBaseURL, attribute->value()).string())); } } } @@ -653,8 +796,8 @@ static bool propertyMissingOrEqualToNone(CSSStyleDeclaration* style, int propert static bool isElementPresentational(const Node* node) { - if (node->hasTagName(uTag) || node->hasTagName(sTag) || node->hasTagName(strikeTag) || - node->hasTagName(iTag) || node->hasTagName(emTag) || node->hasTagName(bTag) || node->hasTagName(strongTag)) + if (node->hasTagName(uTag) || node->hasTagName(sTag) || node->hasTagName(strikeTag) + || node->hasTagName(iTag) || node->hasTagName(emTag) || node->hasTagName(bTag) || node->hasTagName(strongTag)) return true; RefPtr<CSSMutableStyleDeclaration> style = styleFromMatchedRulesAndInlineDecl(node); if (!style) @@ -667,17 +810,17 @@ static 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); + return node->hasTagName(listingTag) + || node->hasTagName(olTag) + || node->hasTagName(preTag) + || node->hasTagName(tableTag) + || node->hasTagName(ulTag) + || node->hasTagName(xmpTag) + || node->hasTagName(h1Tag) + || node->hasTagName(h2Tag) + || node->hasTagName(h3Tag) + || node->hasTagName(h4Tag) + || node->hasTagName(h5Tag); } static bool shouldIncludeWrapperForFullySelectedRoot(Node* fullySelectedRoot, CSSMutableStyleDeclaration* style) @@ -685,100 +828,10 @@ static bool shouldIncludeWrapperForFullySelectedRoot(Node* fullySelectedRoot, CS if (fullySelectedRoot->isElementNode() && static_cast<Element*>(fullySelectedRoot)->hasAttribute(backgroundAttr)) return true; - return style->getPropertyCSSValue(CSSPropertyBackgroundImage) || - style->getPropertyCSSValue(CSSPropertyBackgroundColor); + return style->getPropertyCSSValue(CSSPropertyBackgroundImage) || style->getPropertyCSSValue(CSSPropertyBackgroundColor); } -class MarkupAccumulatorWrapper { -public: - MarkupAccumulatorWrapper(Vector<Node*>* nodes) - : m_nodes(nodes) - { - } - - void insertString(const String& s) - { - postMarkups.append(s); - } - - void insertOpenTag(const Node* node, const Range* range, EAnnotateForInterchange annotate, EAbsoluteURLs absoluteURLs, bool convertBlocksToInlines = false, HashMap<AtomicStringImpl*, AtomicStringImpl*>* namespaces = 0, RangeFullySelectsNode rangeFullySelectsNode = DoesFullySelectNode) - { - Vector<UChar> result; - appendStartMarkup(result, node, range, annotate, absoluteURLs, convertBlocksToInlines, namespaces, rangeFullySelectsNode); - postMarkups.append(String::adopt(result)); - if (m_nodes) - m_nodes->append(const_cast<Node*>(node)); - } - - void insertEndTag(const Node* node) - { - Vector<UChar> result; - appendEndMarkup(result, node); - postMarkups.append(String::adopt(result)); - } - - void wrapWithNode(const Node* node, const Range* range, EAnnotateForInterchange annotate, EAbsoluteURLs absoluteURLs, bool convertBlocksToInlines = false, HashMap<AtomicStringImpl*, AtomicStringImpl*>* namespaces = 0, RangeFullySelectsNode rangeFullySelectsNode = DoesFullySelectNode) - { - Vector<UChar> result; - appendStartMarkup(result, node, range, annotate, absoluteURLs, convertBlocksToInlines, namespaces, rangeFullySelectsNode); - preMarkups.append(String::adopt(result)); - insertEndTag(node); - if (m_nodes) - m_nodes->append(const_cast<Node*>(node)); - } - - void wrapWithStyleNode(CSSStyleDeclaration* style, Document* document, bool isBlock = false) - { - // All text-decoration-related elements should have been treated as special ancestors - // If we ever hit this ASSERT, we should export StyleChange in ApplyStyleCommand and use it here - ASSERT(propertyMissingOrEqualToNone(style, CSSPropertyTextDecoration) && propertyMissingOrEqualToNone(style, CSSPropertyWebkitTextDecorationsInEffect)); - DEFINE_STATIC_LOCAL(const String, divStyle, ("<div style=\"")); - DEFINE_STATIC_LOCAL(const String, divClose, ("</div>")); - DEFINE_STATIC_LOCAL(const String, styleSpanOpen, ("<span class=\"" AppleStyleSpanClass "\" style=\"")); - DEFINE_STATIC_LOCAL(const String, styleSpanClose, ("</span>")); - Vector<UChar> openTag; - WTF::append(openTag, isBlock ? divStyle : styleSpanOpen); - appendAttributeValue(openTag, style->cssText(), document->isHTMLDocument()); - openTag.append('\"'); - openTag.append('>'); - preMarkups.append(String::adopt(openTag)); - postMarkups.append(isBlock ? divClose : styleSpanClose); - } - - // FIXME: This is a very inefficient way of accumulating the markup. - // We're converting results of appendStartMarkup and appendEndMarkup from Vector<UChar> to String - // and then back to Vector<UChar> and again to String here. - String takeResults() - { - 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) - WTF::append(result, preMarkups[i - 1]); - - for (size_t i = 0; i < postCount; ++i) - WTF::append(result, postMarkups[i]); - - return String::adopt(result); - } - -private: - Vector<Node*>* m_nodes; - Vector<String> preMarkups; - Vector<String> postMarkups; -}; - -static Node* serializeNodes(MarkupAccumulatorWrapper& accumulator, Node* startNode, Node* pastEnd, const Range* range, EAnnotateForInterchange annotate, EAbsoluteURLs absoluteURLs) +static Node* serializeNodes(MarkupAccumulator& accumulator, Node* startNode, Node* pastEnd) { Vector<Node*> ancestorsToClose; Node* next; @@ -806,11 +859,11 @@ static Node* serializeNodes(MarkupAccumulatorWrapper& accumulator, Node* startNo next = pastEnd; } else { // Add the node to the markup if we're not skipping the descendants - accumulator.insertOpenTag(n, range, annotate, absoluteURLs); + accumulator.appendStartTag(n); // If node has no children, close the tag now. if (!n->childNodeCount()) { - accumulator.insertEndTag(n); + accumulator.appendEndTag(n); lastClosed = n; } else { openedTag = true; @@ -827,7 +880,7 @@ static Node* serializeNodes(MarkupAccumulatorWrapper& accumulator, Node* startNo if (next != pastEnd && next->isDescendantOf(ancestor)) break; // Not at the end of the range, close ancestors up to sibling of next node. - accumulator.insertEndTag(ancestor); + accumulator.appendEndTag(ancestor); lastClosed = ancestor; ancestorsToClose.removeLast(); } @@ -842,7 +895,7 @@ static Node* serializeNodes(MarkupAccumulatorWrapper& accumulator, Node* startNo continue; // or b) ancestors that we never encountered during a pre-order traversal starting at startNode: ASSERT(startNode->isDescendantOf(parent)); - accumulator.wrapWithNode(parent, range, annotate, absoluteURLs); + accumulator.wrapWithNode(parent); lastClosed = parent; } } @@ -854,7 +907,7 @@ static Node* serializeNodes(MarkupAccumulatorWrapper& accumulator, Node* startNo // 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, EAbsoluteURLs absoluteURLs) +String createMarkup(const Range* range, Vector<Node*>* nodes, EAnnotateForInterchange shouldAnnotate, bool convertBlocksToInlines, EAbsoluteURLs shouldResolveURLs) { DEFINE_STATIC_LOCAL(const String, interchangeNewlineString, ("<br class=\"" AppleInterchangeNewline "\">")); @@ -878,30 +931,30 @@ String createMarkup(const Range* range, Vector<Node*>* nodes, EAnnotateForInterc ExceptionCode ec = 0; bool collapsed = updatedRange->collapsed(ec); - ASSERT(ec == 0); + ASSERT(!ec); if (collapsed) return ""; Node* commonAncestor = updatedRange->commonAncestorContainer(ec); - ASSERT(ec == 0); + ASSERT(!ec); if (!commonAncestor) return ""; document->updateLayoutIgnorePendingStylesheets(); - MarkupAccumulatorWrapper accumulator(nodes); + MarkupAccumulator accumulator(nodes, shouldResolveURLs, shouldAnnotate, updatedRange.get()); Node* pastEnd = updatedRange->pastLastNode(); Node* startNode = updatedRange->firstNode(); VisiblePosition visibleStart(updatedRange->startPosition(), VP_DEFAULT_AFFINITY); VisiblePosition visibleEnd(updatedRange->endPosition(), VP_DEFAULT_AFFINITY); - if (annotate && needInterchangeNewlineAfter(visibleStart)) { + if (shouldAnnotate == AnnotateForInterchange && needInterchangeNewlineAfter(visibleStart)) { if (visibleStart == visibleEnd.previous()) { if (deleteButton) deleteButton->enable(); return interchangeNewlineString; } - accumulator.insertString(interchangeNewlineString); + accumulator.appendString(interchangeNewlineString); startNode = visibleStart.next().deepEquivalent().node(); if (pastEnd && Range::compareBoundaryPoints(startNode, 0, pastEnd, 0) >= 0) { @@ -911,13 +964,13 @@ String createMarkup(const Range* range, Vector<Node*>* nodes, EAnnotateForInterc } } - Node* lastClosed = serializeNodes(accumulator, startNode, pastEnd, range, annotate, absoluteURLs); + Node* lastClosed = serializeNodes(accumulator, startNode, pastEnd); // 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 (shouldAnnotate == AnnotateForInterchange && commonAncestorBlock) { if (commonAncestorBlock->hasTagName(tbodyTag) || commonAncestorBlock->hasTagName(trTag)) { Node* table = commonAncestorBlock->parentNode(); while (table && !table->hasTagName(tableTag)) @@ -929,7 +982,7 @@ String createMarkup(const Range* range, Vector<Node*>* nodes, EAnnotateForInterc } // Retain the Mail quote level by including all ancestor mail block quotes. - if (lastClosed && annotate) { + if (lastClosed && shouldAnnotate == AnnotateForInterchange) { for (Node *ancestor = lastClosed->parentNode(); ancestor; ancestor = ancestor->parentNode()) if (isMailBlockquote(ancestor)) specialCommonAncestor = ancestor; @@ -958,7 +1011,7 @@ String createMarkup(const Range* range, Vector<Node*>* nodes, EAnnotateForInterc // FIXME: Do this for all fully selected blocks, not just the body. Node* fullySelectedRoot = body && areRangesEqual(VisibleSelection::selectionFromContentsOfNode(body).toNormalizedRange().get(), updatedRange.get()) ? body : 0; RefPtr<CSSMutableStyleDeclaration> fullySelectedRootStyle = fullySelectedRoot ? styleFromMatchedRulesAndInlineDecl(fullySelectedRoot) : 0; - if (annotate && fullySelectedRoot) { + if (shouldAnnotate == AnnotateForInterchange && fullySelectedRoot) { if (shouldIncludeWrapperForFullySelectedRoot(fullySelectedRoot, fullySelectedRootStyle.get())) specialCommonAncestor = fullySelectedRoot; } @@ -986,7 +1039,7 @@ String createMarkup(const Range* range, Vector<Node*>* nodes, EAnnotateForInterc } else { // Since this node and all the other ancestors are not in the selection we want to set RangeFullySelectsNode to DoesNotFullySelectNode // so that styles that affect the exterior of the node are not included. - accumulator.wrapWithNode(ancestor, updatedRange.get(), annotate, absoluteURLs, convertBlocksToInlines, 0, DoesNotFullySelectNode); + accumulator.wrapWithNode(ancestor, convertBlocksToInlines, MarkupAccumulator::DoesNotFullySelectNode); } if (nodes) nodes->append(ancestor); @@ -1032,8 +1085,8 @@ String createMarkup(const Range* range, Vector<Node*>* nodes, EAnnotateForInterc } // 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())) - accumulator.insertString(interchangeNewlineString); + if (shouldAnnotate == AnnotateForInterchange && needInterchangeNewlineAfter(visibleEnd.previous())) + accumulator.appendString(interchangeNewlineString); if (deleteButton) deleteButton->enable(); @@ -1043,8 +1096,12 @@ String createMarkup(const Range* range, Vector<Node*>* nodes, EAnnotateForInterc PassRefPtr<DocumentFragment> createFragmentFromMarkup(Document* document, const String& markup, const String& baseURL, FragmentScriptingPermission scriptingPermission) { + // We use a fake body element here to trick the HTML parser to using the + // InBody insertion mode. Really, all this code is wrong and need to be + // changed not to use deprecatedCreateContextualFragment. + RefPtr<HTMLBodyElement> fakeBody = HTMLBodyElement::create(document); // FIXME: This should not use deprecatedCreateContextualFragment - RefPtr<DocumentFragment> fragment = document->documentElement()->deprecatedCreateContextualFragment(markup, scriptingPermission); + RefPtr<DocumentFragment> fragment = fakeBody->deprecatedCreateContextualFragment(markup, scriptingPermission); if (fragment && !baseURL.isEmpty() && baseURL != blankURL() && baseURL != document->baseURL()) completeURLs(fragment.get(), baseURL); @@ -1052,28 +1109,28 @@ PassRefPtr<DocumentFragment> createFragmentFromMarkup(Document* document, const return fragment.release(); } -static void serializeNodesWithNamespaces(MarkupAccumulatorWrapper& accumulator, const Node* node, Node* nodeToSkip, EChildrenOnly childrenOnly, EAbsoluteURLs absoluteURLs, const HashMap<AtomicStringImpl*, AtomicStringImpl*>* namespaces) +static void serializeNodesWithNamespaces(MarkupAccumulator& accumulator, Node* node, Node* nodeToSkip, EChildrenOnly childrenOnly, const Namespaces* namespaces) { if (node == nodeToSkip) return; - HashMap<AtomicStringImpl*, AtomicStringImpl*> namespaceHash; + Namespaces namespaceHash; if (namespaces) namespaceHash = *namespaces; if (!childrenOnly) - accumulator.insertOpenTag(node, 0, DoNotAnnotateForInterchange, absoluteURLs, false, &namespaceHash); + accumulator.appendStartTag(node, &namespaceHash); - if (!(node->document()->isHTMLDocument() && doesHTMLForbidEndTag(node))) { + if (!(node->document()->isHTMLDocument() && elementCannotHaveEndTag(node))) { for (Node* current = node->firstChild(); current; current = current->nextSibling()) - serializeNodesWithNamespaces(accumulator, current, nodeToSkip, IncludeNode, absoluteURLs, &namespaceHash); + serializeNodesWithNamespaces(accumulator, current, nodeToSkip, IncludeNode, &namespaceHash); } if (!childrenOnly) - accumulator.insertEndTag(node); + accumulator.appendEndTag(node); } -String createMarkup(const Node* node, EChildrenOnly childrenOnly, Vector<Node*>* nodes, EAbsoluteURLs absoluteURLs) +String createMarkup(const Node* node, EChildrenOnly childrenOnly, Vector<Node*>* nodes, EAbsoluteURLs shouldResolveURLs) { if (!node) return ""; @@ -1085,8 +1142,8 @@ String createMarkup(const Node* node, EChildrenOnly childrenOnly, Vector<Node*>* return ""; } - MarkupAccumulatorWrapper accumulator(nodes); - serializeNodesWithNamespaces(accumulator, node, deleteButtonContainerElement, childrenOnly, absoluteURLs, 0); + MarkupAccumulator accumulator(nodes, shouldResolveURLs, DoNotAnnotateForInterchange); + serializeNodesWithNamespaces(accumulator, const_cast<Node*>(node), deleteButtonContainerElement, childrenOnly, 0); return accumulator.takeResults(); } @@ -1097,7 +1154,7 @@ static void fillContainerFromString(ContainerNode* paragraph, const String& stri ExceptionCode ec = 0; if (string.isEmpty()) { paragraph->appendChild(createBlockPlaceholderElement(document), ec); - ASSERT(ec == 0); + ASSERT(!ec); return; } @@ -1115,12 +1172,12 @@ static void fillContainerFromString(ContainerNode* paragraph, const String& stri if (!s.isEmpty()) { if (!tabText.isEmpty()) { paragraph->appendChild(createTabSpanElement(document, tabText), ec); - ASSERT(ec == 0); + ASSERT(!ec); tabText = ""; } RefPtr<Node> textNode = document->createTextNode(stringWithRebalancedWhitespace(s, first, i + 1 == numEntries)); paragraph->appendChild(textNode.release(), ec); - ASSERT(ec == 0); + ASSERT(!ec); } // there is a tab after every entry, except the last entry @@ -1129,7 +1186,7 @@ static void fillContainerFromString(ContainerNode* paragraph, const String& stri tabText.append('\t'); else if (!tabText.isEmpty()) { paragraph->appendChild(createTabSpanElement(document, tabText), ec); - ASSERT(ec == 0); + ASSERT(!ec); } first = false; @@ -1173,12 +1230,12 @@ PassRefPtr<DocumentFragment> createFragmentFromText(Range* context, const String RenderObject* renderer = styleNode->renderer(); if (renderer && renderer->style()->preserveNewline()) { fragment->appendChild(document->createTextNode(string), ec); - ASSERT(ec == 0); + ASSERT(!ec); if (string.endsWith("\n")) { RefPtr<Element> element = createBreakElement(document); element->setAttribute(classAttr, AppleInterchangeNewline); fragment->appendChild(element.release(), ec); - ASSERT(ec == 0); + ASSERT(!ec); } return fragment.release(); } @@ -1217,7 +1274,7 @@ PassRefPtr<DocumentFragment> createFragmentFromText(Range* context, const String fillContainerFromString(element.get(), s); } fragment->appendChild(element.release(), ec); - ASSERT(ec == 0); + ASSERT(!ec); } return fragment.release(); } @@ -1238,9 +1295,9 @@ PassRefPtr<DocumentFragment> createFragmentFromNodes(Document *document, const V for (size_t i = 0; i < size; ++i) { RefPtr<Element> element = createDefaultParagraphElement(document); element->appendChild(nodes[i], ec); - ASSERT(ec == 0); + ASSERT(!ec); fragment->appendChild(element.release(), ec); - ASSERT(ec == 0); + ASSERT(!ec); } if (document->frame()) |