summaryrefslogtreecommitdiffstats
path: root/WebCore/editing/markup.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'WebCore/editing/markup.cpp')
-rw-r--r--WebCore/editing/markup.cpp835
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, ("&amp;"));
@@ -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())