| OLD | NEW |
| 1 /* | 1 /* |
| 2 * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2012 Apple Inc. All rights
reserved. | 2 * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2012 Apple Inc. All rights
reserved. |
| 3 * Copyright (C) 2009, 2010 Google Inc. All rights reserved. | 3 * Copyright (C) 2009, 2010 Google Inc. All rights reserved. |
| 4 * | 4 * |
| 5 * Redistribution and use in source and binary forms, with or without | 5 * Redistribution and use in source and binary forms, with or without |
| 6 * modification, are permitted provided that the following conditions | 6 * modification, are permitted provided that the following conditions |
| 7 * are met: | 7 * are met: |
| 8 * 1. Redistributions of source code must retain the above copyright | 8 * 1. Redistributions of source code must retain the above copyright |
| 9 * notice, this list of conditions and the following disclaimer. | 9 * notice, this list of conditions and the following disclaimer. |
| 10 * 2. Redistributions in binary form must reproduce the above copyright | 10 * 2. Redistributions in binary form must reproduce the above copyright |
| (...skipping 29 matching lines...) Expand all Loading... |
| 40 #include "core/editing/Editor.h" | 40 #include "core/editing/Editor.h" |
| 41 #include "core/html/HTMLElement.h" | 41 #include "core/html/HTMLElement.h" |
| 42 #include "core/html/HTMLTemplateElement.h" | 42 #include "core/html/HTMLTemplateElement.h" |
| 43 #include "platform/weborigin/KURL.h" | 43 #include "platform/weborigin/KURL.h" |
| 44 #include "wtf/unicode/CharacterNames.h" | 44 #include "wtf/unicode/CharacterNames.h" |
| 45 | 45 |
| 46 namespace blink { | 46 namespace blink { |
| 47 | 47 |
| 48 using namespace HTMLNames; | 48 using namespace HTMLNames; |
| 49 | 49 |
| 50 struct EntityDescription { | |
| 51 UChar entity; | |
| 52 const CString& reference; | |
| 53 EntityMask mask; | |
| 54 }; | |
| 55 | |
| 56 template <typename CharType> | |
| 57 static inline void appendCharactersReplacingEntitiesInternal(StringBuilder& resu
lt, CharType* text, unsigned length, const EntityDescription entityMaps[], unsig
ned entityMapsCount, EntityMask entityMask) | |
| 58 { | |
| 59 unsigned positionAfterLastEntity = 0; | |
| 60 for (unsigned i = 0; i < length; ++i) { | |
| 61 for (unsigned entityIndex = 0; entityIndex < entityMapsCount; ++entityIn
dex) { | |
| 62 if (text[i] == entityMaps[entityIndex].entity && entityMaps[entityIn
dex].mask & entityMask) { | |
| 63 result.append(text + positionAfterLastEntity, i - positionAfterL
astEntity); | |
| 64 const CString& replacement = entityMaps[entityIndex].reference; | |
| 65 result.append(replacement.data(), replacement.length()); | |
| 66 positionAfterLastEntity = i + 1; | |
| 67 break; | |
| 68 } | |
| 69 } | |
| 70 } | |
| 71 result.append(text + positionAfterLastEntity, length - positionAfterLastEnti
ty); | |
| 72 } | |
| 73 | |
| 74 void MarkupAccumulator::appendCharactersReplacingEntities(StringBuilder& result,
const String& source, unsigned offset, unsigned length, EntityMask entityMask) | |
| 75 { | |
| 76 DEFINE_STATIC_LOCAL(const CString, ampReference, ("&")); | |
| 77 DEFINE_STATIC_LOCAL(const CString, ltReference, ("<")); | |
| 78 DEFINE_STATIC_LOCAL(const CString, gtReference, (">")); | |
| 79 DEFINE_STATIC_LOCAL(const CString, quotReference, (""")); | |
| 80 DEFINE_STATIC_LOCAL(const CString, nbspReference, (" ")); | |
| 81 | |
| 82 static const EntityDescription entityMaps[] = { | |
| 83 { '&', ampReference, EntityAmp }, | |
| 84 { '<', ltReference, EntityLt }, | |
| 85 { '>', gtReference, EntityGt }, | |
| 86 { '"', quotReference, EntityQuot }, | |
| 87 { noBreakSpaceCharacter, nbspReference, EntityNbsp }, | |
| 88 }; | |
| 89 | |
| 90 if (!(offset + length)) | |
| 91 return; | |
| 92 | |
| 93 ASSERT(offset + length <= source.length()); | |
| 94 if (source.is8Bit()) | |
| 95 appendCharactersReplacingEntitiesInternal(result, source.characters8() +
offset, length, entityMaps, WTF_ARRAY_LENGTH(entityMaps), entityMask); | |
| 96 else | |
| 97 appendCharactersReplacingEntitiesInternal(result, source.characters16()
+ offset, length, entityMaps, WTF_ARRAY_LENGTH(entityMaps), entityMask); | |
| 98 } | |
| 99 | |
| 100 size_t MarkupAccumulator::totalLength(const Vector<String>& strings) | |
| 101 { | |
| 102 size_t length = 0; | |
| 103 for (const auto& string : strings) | |
| 104 length += string.length(); | |
| 105 return length; | |
| 106 } | |
| 107 | |
| 108 MarkupAccumulator::MarkupAccumulator(EAbsoluteURLs resolveUrlsMethod, Serializat
ionType serializationType) | 50 MarkupAccumulator::MarkupAccumulator(EAbsoluteURLs resolveUrlsMethod, Serializat
ionType serializationType) |
| 109 : m_resolveURLsMethod(resolveUrlsMethod) | 51 : m_formatter(resolveUrlsMethod, serializationType) |
| 110 , m_serializationType(serializationType) | |
| 111 { | 52 { |
| 112 } | 53 } |
| 113 | 54 |
| 114 MarkupAccumulator::~MarkupAccumulator() | 55 MarkupAccumulator::~MarkupAccumulator() |
| 115 { | 56 { |
| 116 } | 57 } |
| 117 | 58 |
| 118 String MarkupAccumulator::resolveURLIfNeeded(const Element& element, const Strin
g& urlString) const | |
| 119 { | |
| 120 switch (m_resolveURLsMethod) { | |
| 121 case ResolveAllURLs: | |
| 122 return element.document().completeURL(urlString).string(); | |
| 123 | |
| 124 case ResolveNonLocalURLs: | |
| 125 if (!element.document().url().isLocalFile()) | |
| 126 return element.document().completeURL(urlString).string(); | |
| 127 break; | |
| 128 | |
| 129 case DoNotResolveURLs: | |
| 130 break; | |
| 131 } | |
| 132 return urlString; | |
| 133 } | |
| 134 | |
| 135 void MarkupAccumulator::appendString(const String& string) | 59 void MarkupAccumulator::appendString(const String& string) |
| 136 { | 60 { |
| 137 m_markup.append(string); | 61 m_markup.append(string); |
| 138 } | 62 } |
| 139 | 63 |
| 140 void MarkupAccumulator::appendStartTag(Node& node, Namespaces* namespaces) | 64 void MarkupAccumulator::appendStartTag(Node& node, Namespaces* namespaces) |
| 141 { | 65 { |
| 142 appendStartMarkup(m_markup, node, namespaces); | 66 appendStartMarkup(m_markup, node, namespaces); |
| 143 } | 67 } |
| 144 | 68 |
| 145 void MarkupAccumulator::appendEndTag(const Element& element) | 69 void MarkupAccumulator::appendEndTag(const Element& element) |
| 146 { | 70 { |
| 147 appendEndMarkup(m_markup, element); | 71 appendEndMarkup(m_markup, element); |
| 148 } | 72 } |
| 149 | 73 |
| 150 void MarkupAccumulator::appendStartMarkup(StringBuilder& result, Node& node, Nam
espaces* namespaces) | 74 void MarkupAccumulator::appendStartMarkup(StringBuilder& result, Node& node, Nam
espaces* namespaces) |
| 151 { | 75 { |
| 152 switch (node.nodeType()) { | 76 switch (node.nodeType()) { |
| 153 case Node::TEXT_NODE: | 77 case Node::TEXT_NODE: |
| 154 appendText(result, toText(node)); | 78 appendText(result, toText(node)); |
| 155 break; | 79 break; |
| 156 case Node::COMMENT_NODE: | 80 case Node::COMMENT_NODE: |
| 157 appendComment(result, toComment(node).data()); | 81 MarkupFormatter::appendComment(result, toComment(node).data()); |
| 158 break; | 82 break; |
| 159 case Node::DOCUMENT_NODE: | 83 case Node::DOCUMENT_NODE: |
| 160 appendXMLDeclaration(result, toDocument(node)); | 84 appendXMLDeclaration(result, toDocument(node)); |
| 161 break; | 85 break; |
| 162 case Node::DOCUMENT_FRAGMENT_NODE: | 86 case Node::DOCUMENT_FRAGMENT_NODE: |
| 163 break; | 87 break; |
| 164 case Node::DOCUMENT_TYPE_NODE: | 88 case Node::DOCUMENT_TYPE_NODE: |
| 165 appendDocumentType(result, toDocumentType(node)); | 89 appendDocumentType(result, toDocumentType(node)); |
| 166 break; | 90 break; |
| 167 case Node::PROCESSING_INSTRUCTION_NODE: | 91 case Node::PROCESSING_INSTRUCTION_NODE: |
| (...skipping 18 matching lines...) Expand all Loading... |
| 186 | 110 |
| 187 // FIXME: ieForbidsInsertHTML may not be the right function to call here | 111 // FIXME: ieForbidsInsertHTML may not be the right function to call here |
| 188 // ieForbidsInsertHTML is used to disallow setting innerHTML/outerHTML | 112 // ieForbidsInsertHTML is used to disallow setting innerHTML/outerHTML |
| 189 // or createContextualFragment. It does not necessarily align with | 113 // or createContextualFragment. It does not necessarily align with |
| 190 // which elements should be serialized w/o end tags. | 114 // which elements should be serialized w/o end tags. |
| 191 return toHTMLElement(node).ieForbidsInsertHTML(); | 115 return toHTMLElement(node).ieForbidsInsertHTML(); |
| 192 } | 116 } |
| 193 | 117 |
| 194 void MarkupAccumulator::appendEndMarkup(StringBuilder& result, const Element& el
ement) | 118 void MarkupAccumulator::appendEndMarkup(StringBuilder& result, const Element& el
ement) |
| 195 { | 119 { |
| 196 if (shouldSelfClose(element) || (!element.hasChildren() && elementCannotHave
EndTag(element))) | 120 m_formatter.appendEndMarkup(result, element); |
| 197 return; | |
| 198 | |
| 199 result.appendLiteral("</"); | |
| 200 result.append(element.tagQName().toString()); | |
| 201 result.append('>'); | |
| 202 } | 121 } |
| 203 | 122 |
| 204 void MarkupAccumulator::concatenateMarkup(StringBuilder& result) const | 123 void MarkupAccumulator::concatenateMarkup(StringBuilder& result) const |
| 205 { | 124 { |
| 206 result.append(m_markup); | 125 result.append(m_markup); |
| 207 } | 126 } |
| 208 | 127 |
| 209 void MarkupAccumulator::appendAttributeValue(StringBuilder& result, const String
& attribute, bool documentIsHTML) | 128 void MarkupAccumulator::appendAttributeValue(StringBuilder& result, const String
& attribute, bool documentIsHTML) |
| 210 { | 129 { |
| 211 appendCharactersReplacingEntities(result, attribute, 0, attribute.length(), | 130 m_formatter.appendAttributeValue(result, attribute, documentIsHTML); |
| 212 documentIsHTML ? EntityMaskInHTMLAttributeValue : EntityMaskInAttributeV
alue); | |
| 213 } | 131 } |
| 214 | 132 |
| 215 void MarkupAccumulator::appendCustomAttributes(StringBuilder&, const Element&, N
amespaces*) | 133 void MarkupAccumulator::appendCustomAttributes(StringBuilder&, const Element&, N
amespaces*) |
| 216 { | 134 { |
| 217 } | 135 } |
| 218 | 136 |
| 219 void MarkupAccumulator::appendQuotedURLAttributeValue(StringBuilder& result, con
st Element& element, const Attribute& attribute) | |
| 220 { | |
| 221 ASSERT(element.isURLAttribute(attribute)); | |
| 222 const String resolvedURLString = resolveURLIfNeeded(element, attribute.value
()); | |
| 223 UChar quoteChar = '"'; | |
| 224 String strippedURLString = resolvedURLString.stripWhiteSpace(); | |
| 225 if (protocolIsJavaScript(strippedURLString)) { | |
| 226 // minimal escaping for javascript urls | |
| 227 if (strippedURLString.contains('&')) | |
| 228 strippedURLString.replaceWithLiteral('&', "&"); | |
| 229 | |
| 230 if (strippedURLString.contains('"')) { | |
| 231 if (strippedURLString.contains('\'')) | |
| 232 strippedURLString.replaceWithLiteral('"', """); | |
| 233 else | |
| 234 quoteChar = '\''; | |
| 235 } | |
| 236 result.append(quoteChar); | |
| 237 result.append(strippedURLString); | |
| 238 result.append(quoteChar); | |
| 239 return; | |
| 240 } | |
| 241 | |
| 242 // FIXME: This does not fully match other browsers. Firefox percent-escapes
non-ASCII characters for innerHTML. | |
| 243 result.append(quoteChar); | |
| 244 appendAttributeValue(result, resolvedURLString, false); | |
| 245 result.append(quoteChar); | |
| 246 } | |
| 247 | |
| 248 void MarkupAccumulator::appendNamespace(StringBuilder& result, const AtomicStrin
g& prefix, const AtomicString& namespaceURI, Namespaces& namespaces) | 137 void MarkupAccumulator::appendNamespace(StringBuilder& result, const AtomicStrin
g& prefix, const AtomicString& namespaceURI, Namespaces& namespaces) |
| 249 { | 138 { |
| 250 if (namespaceURI.isEmpty()) | 139 m_formatter.appendNamespace(result, prefix, namespaceURI, namespaces); |
| 251 return; | |
| 252 | |
| 253 const AtomicString& lookupKey = (!prefix) ? emptyAtom : prefix; | |
| 254 AtomicString foundURI = namespaces.get(lookupKey); | |
| 255 if (foundURI != namespaceURI) { | |
| 256 namespaces.set(lookupKey, namespaceURI); | |
| 257 result.append(' '); | |
| 258 result.append(xmlnsAtom.string()); | |
| 259 if (!prefix.isEmpty()) { | |
| 260 result.append(':'); | |
| 261 result.append(prefix); | |
| 262 } | |
| 263 | |
| 264 result.appendLiteral("=\""); | |
| 265 appendAttributeValue(result, namespaceURI, false); | |
| 266 result.append('"'); | |
| 267 } | |
| 268 } | 140 } |
| 269 | 141 |
| 270 void MarkupAccumulator::appendText(StringBuilder& result, Text& text) | 142 void MarkupAccumulator::appendText(StringBuilder& result, Text& text) |
| 271 { | 143 { |
| 272 const String& str = text.data(); | 144 m_formatter.appendText(result, text); |
| 273 appendCharactersReplacingEntities(result, str, 0, str.length(), entityMaskFo
rText(text)); | |
| 274 } | |
| 275 | |
| 276 void MarkupAccumulator::appendComment(StringBuilder& result, const String& comme
nt) | |
| 277 { | |
| 278 // FIXME: Comment content is not escaped, but XMLSerializer (and possibly ot
her callers) should raise an exception if it includes "-->". | |
| 279 result.appendLiteral("<!--"); | |
| 280 result.append(comment); | |
| 281 result.appendLiteral("-->"); | |
| 282 } | 145 } |
| 283 | 146 |
| 284 void MarkupAccumulator::appendXMLDeclaration(StringBuilder& result, const Docume
nt& document) | 147 void MarkupAccumulator::appendXMLDeclaration(StringBuilder& result, const Docume
nt& document) |
| 285 { | 148 { |
| 286 if (!document.hasXMLDeclaration()) | 149 m_formatter.appendXMLDeclaration(result, document); |
| 287 return; | |
| 288 | |
| 289 result.appendLiteral("<?xml version=\""); | |
| 290 result.append(document.xmlVersion()); | |
| 291 const String& encoding = document.xmlEncoding(); | |
| 292 if (!encoding.isEmpty()) { | |
| 293 result.appendLiteral("\" encoding=\""); | |
| 294 result.append(encoding); | |
| 295 } | |
| 296 if (document.xmlStandaloneStatus() != Document::StandaloneUnspecified) { | |
| 297 result.appendLiteral("\" standalone=\""); | |
| 298 if (document.xmlStandalone()) | |
| 299 result.appendLiteral("yes"); | |
| 300 else | |
| 301 result.appendLiteral("no"); | |
| 302 } | |
| 303 | |
| 304 result.appendLiteral("\"?>"); | |
| 305 } | 150 } |
| 306 | 151 |
| 307 void MarkupAccumulator::appendDocumentType(StringBuilder& result, const Document
Type& n) | 152 void MarkupAccumulator::appendDocumentType(StringBuilder& result, const Document
Type& n) |
| 308 { | 153 { |
| 309 if (n.name().isEmpty()) | 154 m_formatter.appendDocumentType(result, n); |
| 310 return; | |
| 311 | |
| 312 result.appendLiteral("<!DOCTYPE "); | |
| 313 result.append(n.name()); | |
| 314 if (!n.publicId().isEmpty()) { | |
| 315 result.appendLiteral(" PUBLIC \""); | |
| 316 result.append(n.publicId()); | |
| 317 result.append('"'); | |
| 318 if (!n.systemId().isEmpty()) { | |
| 319 result.appendLiteral(" \""); | |
| 320 result.append(n.systemId()); | |
| 321 result.append('"'); | |
| 322 } | |
| 323 } else if (!n.systemId().isEmpty()) { | |
| 324 result.appendLiteral(" SYSTEM \""); | |
| 325 result.append(n.systemId()); | |
| 326 result.append('"'); | |
| 327 } | |
| 328 result.append('>'); | |
| 329 } | 155 } |
| 330 | 156 |
| 331 void MarkupAccumulator::appendProcessingInstruction(StringBuilder& result, const
String& target, const String& data) | 157 void MarkupAccumulator::appendProcessingInstruction(StringBuilder& result, const
String& target, const String& data) |
| 332 { | 158 { |
| 333 // FIXME: PI data is not escaped, but XMLSerializer (and possibly other call
ers) this should raise an exception if it includes "?>". | 159 m_formatter.appendProcessingInstruction(result, target, data); |
| 334 result.appendLiteral("<?"); | |
| 335 result.append(target); | |
| 336 result.append(' '); | |
| 337 result.append(data); | |
| 338 result.appendLiteral("?>"); | |
| 339 } | 160 } |
| 340 | 161 |
| 341 bool MarkupAccumulator::shouldIgnoreAttribute(const Attribute& attribute) | 162 bool MarkupAccumulator::shouldIgnoreAttribute(const Attribute& attribute) |
| 342 { | 163 { |
| 343 return false; | 164 return false; |
| 344 } | 165 } |
| 345 | 166 |
| 346 void MarkupAccumulator::appendElement(StringBuilder& result, Element& element, N
amespaces* namespaces) | 167 void MarkupAccumulator::appendElement(StringBuilder& result, Element& element, N
amespaces* namespaces) |
| 347 { | 168 { |
| 348 appendOpenTag(result, element, namespaces); | 169 appendOpenTag(result, element, namespaces); |
| 349 | 170 |
| 350 AttributeCollection attributes = element.attributes(); | 171 AttributeCollection attributes = element.attributes(); |
| 351 for (const auto& attribute : attributes) { | 172 for (const auto& attribute : attributes) { |
| 352 if (!shouldIgnoreAttribute(attribute)) | 173 if (!shouldIgnoreAttribute(attribute)) |
| 353 appendAttribute(result, element, attribute, namespaces); | 174 appendAttribute(result, element, attribute, namespaces); |
| 354 } | 175 } |
| 355 | 176 |
| 356 // Give an opportunity to subclasses to add their own attributes. | 177 // Give an opportunity to subclasses to add their own attributes. |
| 357 appendCustomAttributes(result, element, namespaces); | 178 appendCustomAttributes(result, element, namespaces); |
| 358 | 179 |
| 359 appendCloseTag(result, element); | 180 appendCloseTag(result, element); |
| 360 } | 181 } |
| 361 | 182 |
| 362 void MarkupAccumulator::appendOpenTag(StringBuilder& result, const Element& elem
ent, Namespaces* namespaces) | 183 void MarkupAccumulator::appendOpenTag(StringBuilder& result, const Element& elem
ent, Namespaces* namespaces) |
| 363 { | 184 { |
| 364 result.append('<'); | 185 m_formatter.appendOpenTag(result, element, namespaces); |
| 365 result.append(element.tagQName().toString()); | |
| 366 if (!serializeAsHTMLDocument(element) && namespaces && shouldAddNamespaceEle
ment(element, *namespaces)) | |
| 367 appendNamespace(result, element.prefix(), element.namespaceURI(), *names
paces); | |
| 368 } | 186 } |
| 369 | 187 |
| 370 void MarkupAccumulator::appendCloseTag(StringBuilder& result, const Element& ele
ment) | 188 void MarkupAccumulator::appendCloseTag(StringBuilder& result, const Element& ele
ment) |
| 371 { | 189 { |
| 372 if (shouldSelfClose(element)) { | 190 m_formatter.appendCloseTag(result, element); |
| 373 if (element.isHTMLElement()) | |
| 374 result.append(' '); // XHTML 1.0 <-> HTML compatibility. | |
| 375 result.append('/'); | |
| 376 } | |
| 377 result.append('>'); | |
| 378 } | |
| 379 | |
| 380 static inline bool attributeIsInSerializedNamespace(const Attribute& attribute) | |
| 381 { | |
| 382 return attribute.namespaceURI() == XMLNames::xmlNamespaceURI | |
| 383 || attribute.namespaceURI() == XLinkNames::xlinkNamespaceURI | |
| 384 || attribute.namespaceURI() == XMLNSNames::xmlnsNamespaceURI; | |
| 385 } | 191 } |
| 386 | 192 |
| 387 void MarkupAccumulator::appendAttribute(StringBuilder& result, const Element& el
ement, const Attribute& attribute, Namespaces* namespaces) | 193 void MarkupAccumulator::appendAttribute(StringBuilder& result, const Element& el
ement, const Attribute& attribute, Namespaces* namespaces) |
| 388 { | 194 { |
| 389 bool documentIsHTML = serializeAsHTMLDocument(element); | 195 m_formatter.appendAttribute(result, element, attribute, namespaces); |
| 390 | |
| 391 QualifiedName prefixedName = attribute.name(); | |
| 392 if (documentIsHTML && !attributeIsInSerializedNamespace(attribute)) { | |
| 393 result.append(' '); | |
| 394 result.append(attribute.name().localName()); | |
| 395 } else { | |
| 396 if (attribute.namespaceURI() == XMLNSNames::xmlnsNamespaceURI) { | |
| 397 if (!attribute.prefix() && attribute.localName() != xmlnsAtom) | |
| 398 prefixedName.setPrefix(xmlnsAtom); | |
| 399 if (namespaces) { // Account for the namespace attribute we're about
to append. | |
| 400 const AtomicString& lookupKey = (!attribute.prefix()) ? emptyAto
m : attribute.localName(); | |
| 401 namespaces->set(lookupKey, attribute.value()); | |
| 402 } | |
| 403 } else if (attribute.namespaceURI() == XMLNames::xmlNamespaceURI) { | |
| 404 if (!attribute.prefix()) | |
| 405 prefixedName.setPrefix(xmlAtom); | |
| 406 } else { | |
| 407 if (attribute.namespaceURI() == XLinkNames::xlinkNamespaceURI) { | |
| 408 if (!attribute.prefix()) | |
| 409 prefixedName.setPrefix(xlinkAtom); | |
| 410 } | |
| 411 | |
| 412 if (namespaces && shouldAddNamespaceAttribute(attribute, element)) { | |
| 413 if (!prefixedName.prefix()) { | |
| 414 // This behavior is in process of being standardized. See cr
bug.com/248044 and https://www.w3.org/Bugs/Public/show_bug.cgi?id=24208 | |
| 415 String prefixPrefix("ns", 2); | |
| 416 for (unsigned i = attribute.namespaceURI().impl()->existingH
ash(); ; ++i) { | |
| 417 AtomicString newPrefix(String(prefixPrefix + String::num
ber(i))); | |
| 418 AtomicString foundURI = namespaces->get(newPrefix); | |
| 419 if (foundURI == attribute.namespaceURI() || foundURI ==
nullAtom) { | |
| 420 // We already generated a prefix for this namespace. | |
| 421 prefixedName.setPrefix(newPrefix); | |
| 422 break; | |
| 423 } | |
| 424 } | |
| 425 } | |
| 426 ASSERT(prefixedName.prefix()); | |
| 427 appendNamespace(result, prefixedName.prefix(), attribute.namespa
ceURI(), *namespaces); | |
| 428 } | |
| 429 } | |
| 430 result.append(' '); | |
| 431 result.append(prefixedName.toString()); | |
| 432 } | |
| 433 | |
| 434 result.append('='); | |
| 435 | |
| 436 if (element.isURLAttribute(attribute)) { | |
| 437 appendQuotedURLAttributeValue(result, element, attribute); | |
| 438 } else { | |
| 439 result.append('"'); | |
| 440 appendAttributeValue(result, attribute.value(), documentIsHTML); | |
| 441 result.append('"'); | |
| 442 } | |
| 443 } | 196 } |
| 444 | 197 |
| 445 void MarkupAccumulator::appendCDATASection(StringBuilder& result, const String&
section) | 198 void MarkupAccumulator::appendCDATASection(StringBuilder& result, const String&
section) |
| 446 { | 199 { |
| 447 // FIXME: CDATA content is not escaped, but XMLSerializer (and possibly othe
r callers) should raise an exception if it includes "]]>". | 200 m_formatter.appendCDATASection(result, section); |
| 448 result.appendLiteral("<![CDATA["); | |
| 449 result.append(section); | |
| 450 result.appendLiteral("]]>"); | |
| 451 } | 201 } |
| 452 | 202 |
| 453 bool MarkupAccumulator::shouldAddNamespaceElement(const Element& element, Namesp
aces& namespaces) const | 203 bool MarkupAccumulator::shouldAddNamespaceElement(const Element& element, Namesp
aces& namespaces) const |
| 454 { | 204 { |
| 455 // Don't add namespace attribute if it is already defined for this elem. | 205 return m_formatter.shouldAddNamespaceElement(element, namespaces); |
| 456 const AtomicString& prefix = element.prefix(); | |
| 457 if (prefix.isEmpty()) { | |
| 458 if (element.hasAttribute(xmlnsAtom)) { | |
| 459 namespaces.set(emptyAtom, element.namespaceURI()); | |
| 460 return false; | |
| 461 } | |
| 462 return true; | |
| 463 } | |
| 464 | |
| 465 return !element.hasAttribute(WTF::xmlnsWithColon + prefix); | |
| 466 } | 206 } |
| 467 | 207 |
| 468 bool MarkupAccumulator::shouldAddNamespaceAttribute(const Attribute& attribute,
const Element& element) const | 208 bool MarkupAccumulator::shouldAddNamespaceAttribute(const Attribute& attribute,
const Element& element) const |
| 469 { | 209 { |
| 470 // xmlns and xmlns:prefix attributes should be handled by another branch in
appendAttribute. | 210 return m_formatter.shouldAddNamespaceAttribute(attribute, element); |
| 471 ASSERT(attribute.namespaceURI() != XMLNSNames::xmlnsNamespaceURI); | |
| 472 | |
| 473 // Attributes are in the null namespace by default. | |
| 474 if (!attribute.namespaceURI()) | |
| 475 return false; | |
| 476 | |
| 477 // Attributes without a prefix will need one generated for them, and an xmln
s attribute for that prefix. | |
| 478 if (!attribute.prefix()) | |
| 479 return true; | |
| 480 | |
| 481 return !element.hasAttribute(WTF::xmlnsWithColon + attribute.prefix()); | |
| 482 } | 211 } |
| 483 | 212 |
| 484 EntityMask MarkupAccumulator::entityMaskForText(const Text& text) const | 213 EntityMask MarkupAccumulator::entityMaskForText(const Text& text) const |
| 485 { | 214 { |
| 486 if (!serializeAsHTMLDocument(text)) | 215 return m_formatter.entityMaskForText(text); |
| 487 return EntityMaskInPCDATA; | |
| 488 | |
| 489 // TODO(hajimehoshi): We need to switch EditingStrategy. | |
| 490 const QualifiedName* parentName = nullptr; | |
| 491 if (text.parentElement()) | |
| 492 parentName = &(text.parentElement())->tagQName(); | |
| 493 | |
| 494 if (parentName && (*parentName == scriptTag || *parentName == styleTag || *p
arentName == xmpTag)) | |
| 495 return EntityMaskInCDATA; | |
| 496 return EntityMaskInHTMLPCDATA; | |
| 497 } | 216 } |
| 498 | 217 |
| 499 // Rules of self-closure | |
| 500 // 1. No elements in HTML documents use the self-closing syntax. | |
| 501 // 2. Elements w/ children never self-close because they use a separate end tag. | |
| 502 // 3. HTML elements which do not have a "forbidden" end tag will close with a se
parate end tag. | |
| 503 // 4. Other elements self-close. | |
| 504 bool MarkupAccumulator::shouldSelfClose(const Element& element) const | 218 bool MarkupAccumulator::shouldSelfClose(const Element& element) const |
| 505 { | 219 { |
| 506 if (serializeAsHTMLDocument(element)) | 220 return m_formatter.shouldSelfClose(element); |
| 507 return false; | |
| 508 if (element.hasChildren()) | |
| 509 return false; | |
| 510 if (element.isHTMLElement() && !elementCannotHaveEndTag(element)) | |
| 511 return false; | |
| 512 return true; | |
| 513 } | 221 } |
| 514 | 222 |
| 515 bool MarkupAccumulator::serializeAsHTMLDocument(const Node& node) const | 223 bool MarkupAccumulator::serializeAsHTMLDocument(const Node& node) const |
| 516 { | 224 { |
| 517 if (m_serializationType == SerializationType::ForcedXML) | 225 return m_formatter.serializeAsHTMLDocument(node); |
| 518 return false; | |
| 519 return node.document().isHTMLDocument(); | |
| 520 } | 226 } |
| 521 | 227 |
| 522 template<typename Strategy> | 228 template<typename Strategy> |
| 523 static void serializeNodesWithNamespaces(MarkupAccumulator& accumulator, Node& t
argetNode, EChildrenOnly childrenOnly, const Namespaces* namespaces) | 229 static void serializeNodesWithNamespaces(MarkupAccumulator& accumulator, Node& t
argetNode, EChildrenOnly childrenOnly, const Namespaces* namespaces) |
| 524 { | 230 { |
| 525 Namespaces namespaceHash; | 231 Namespaces namespaceHash; |
| 526 if (namespaces) | 232 if (namespaces) |
| 527 namespaceHash = *namespaces; | 233 namespaceHash = *namespaces; |
| 528 | 234 |
| 529 if (!childrenOnly) | 235 if (!childrenOnly) |
| (...skipping 20 matching lines...) Expand all Loading... |
| 550 namespaces = &namespaceHash; | 256 namespaces = &namespaceHash; |
| 551 } | 257 } |
| 552 | 258 |
| 553 serializeNodesWithNamespaces<Strategy>(accumulator, targetNode, childrenOnly
, namespaces); | 259 serializeNodesWithNamespaces<Strategy>(accumulator, targetNode, childrenOnly
, namespaces); |
| 554 return accumulator.toString(); | 260 return accumulator.toString(); |
| 555 } | 261 } |
| 556 | 262 |
| 557 template String serializeNodes<EditingStrategy>(MarkupAccumulator&, Node&, EChil
drenOnly); | 263 template String serializeNodes<EditingStrategy>(MarkupAccumulator&, Node&, EChil
drenOnly); |
| 558 | 264 |
| 559 } | 265 } |
| OLD | NEW |