| 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 |
| 11 * notice, this list of conditions and the following disclaimer in the | 11 * notice, this list of conditions and the following disclaimer in the |
| 12 * documentation and/or other materials provided with the distribution. | 12 * documentation and/or other materials provided with the distribution. |
| 13 * | 13 * |
| 14 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | 14 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| 15 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | 15 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| 16 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | 16 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| 17 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | 17 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| 18 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | 18 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| 19 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | 19 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| 20 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | 20 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| 21 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | 21 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| 22 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | 22 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| 23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | 23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| 24 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | 24 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| 25 */ | 25 */ |
| 26 | 26 |
| 27 #include "config.h" | 27 #include "config.h" |
| 28 #include "core/editing/MarkupAccumulator.h" | 28 #include "core/editing/MarkupFormatter.h" |
| 29 | 29 |
| 30 #include "core/HTMLNames.h" | 30 #include "core/HTMLNames.h" |
| 31 #include "core/XLinkNames.h" | 31 #include "core/XLinkNames.h" |
| 32 #include "core/XMLNSNames.h" | 32 #include "core/XMLNSNames.h" |
| 33 #include "core/XMLNames.h" | 33 #include "core/XMLNames.h" |
| 34 #include "core/dom/CDATASection.h" | 34 #include "core/dom/CDATASection.h" |
| 35 #include "core/dom/Comment.h" | 35 #include "core/dom/Comment.h" |
| 36 #include "core/dom/Document.h" | 36 #include "core/dom/Document.h" |
| 37 #include "core/dom/DocumentFragment.h" | 37 #include "core/dom/DocumentFragment.h" |
| 38 #include "core/dom/DocumentType.h" | 38 #include "core/dom/DocumentType.h" |
| (...skipping 25 matching lines...) Expand all Loading... |
| 64 const CString& replacement = entityMaps[entityIndex].reference; | 64 const CString& replacement = entityMaps[entityIndex].reference; |
| 65 result.append(replacement.data(), replacement.length()); | 65 result.append(replacement.data(), replacement.length()); |
| 66 positionAfterLastEntity = i + 1; | 66 positionAfterLastEntity = i + 1; |
| 67 break; | 67 break; |
| 68 } | 68 } |
| 69 } | 69 } |
| 70 } | 70 } |
| 71 result.append(text + positionAfterLastEntity, length - positionAfterLastEnti
ty); | 71 result.append(text + positionAfterLastEntity, length - positionAfterLastEnti
ty); |
| 72 } | 72 } |
| 73 | 73 |
| 74 void MarkupAccumulator::appendCharactersReplacingEntities(StringBuilder& result,
const String& source, unsigned offset, unsigned length, EntityMask entityMask) | 74 void MarkupFormatter::appendCharactersReplacingEntities(StringBuilder& result, c
onst String& source, unsigned offset, unsigned length, EntityMask entityMask) |
| 75 { | 75 { |
| 76 DEFINE_STATIC_LOCAL(const CString, ampReference, ("&")); | 76 DEFINE_STATIC_LOCAL(const CString, ampReference, ("&")); |
| 77 DEFINE_STATIC_LOCAL(const CString, ltReference, ("<")); | 77 DEFINE_STATIC_LOCAL(const CString, ltReference, ("<")); |
| 78 DEFINE_STATIC_LOCAL(const CString, gtReference, (">")); | 78 DEFINE_STATIC_LOCAL(const CString, gtReference, (">")); |
| 79 DEFINE_STATIC_LOCAL(const CString, quotReference, (""")); | 79 DEFINE_STATIC_LOCAL(const CString, quotReference, (""")); |
| 80 DEFINE_STATIC_LOCAL(const CString, nbspReference, (" ")); | 80 DEFINE_STATIC_LOCAL(const CString, nbspReference, (" ")); |
| 81 | 81 |
| 82 static const EntityDescription entityMaps[] = { | 82 static const EntityDescription entityMaps[] = { |
| 83 { '&', ampReference, EntityAmp }, | 83 { '&', ampReference, EntityAmp }, |
| 84 { '<', ltReference, EntityLt }, | 84 { '<', ltReference, EntityLt }, |
| 85 { '>', gtReference, EntityGt }, | 85 { '>', gtReference, EntityGt }, |
| 86 { '"', quotReference, EntityQuot }, | 86 { '"', quotReference, EntityQuot }, |
| 87 { noBreakSpaceCharacter, nbspReference, EntityNbsp }, | 87 { noBreakSpaceCharacter, nbspReference, EntityNbsp }, |
| 88 }; | 88 }; |
| 89 | 89 |
| 90 if (!(offset + length)) | 90 if (!(offset + length)) |
| 91 return; | 91 return; |
| 92 | 92 |
| 93 ASSERT(offset + length <= source.length()); | 93 ASSERT(offset + length <= source.length()); |
| 94 if (source.is8Bit()) | 94 if (source.is8Bit()) |
| 95 appendCharactersReplacingEntitiesInternal(result, source.characters8() +
offset, length, entityMaps, WTF_ARRAY_LENGTH(entityMaps), entityMask); | 95 appendCharactersReplacingEntitiesInternal(result, source.characters8() +
offset, length, entityMaps, WTF_ARRAY_LENGTH(entityMaps), entityMask); |
| 96 else | 96 else |
| 97 appendCharactersReplacingEntitiesInternal(result, source.characters16()
+ offset, length, entityMaps, WTF_ARRAY_LENGTH(entityMaps), entityMask); | 97 appendCharactersReplacingEntitiesInternal(result, source.characters16()
+ offset, length, entityMaps, WTF_ARRAY_LENGTH(entityMaps), entityMask); |
| 98 } | 98 } |
| 99 | 99 |
| 100 size_t MarkupAccumulator::totalLength(const Vector<String>& strings) | 100 size_t MarkupFormatter::totalLength(const Vector<String>& strings) |
| 101 { | 101 { |
| 102 size_t length = 0; | 102 size_t length = 0; |
| 103 for (const auto& string : strings) | 103 for (const auto& string : strings) |
| 104 length += string.length(); | 104 length += string.length(); |
| 105 return length; | 105 return length; |
| 106 } | 106 } |
| 107 | 107 |
| 108 MarkupAccumulator::MarkupAccumulator(EAbsoluteURLs resolveUrlsMethod, Serializat
ionType serializationType) | 108 MarkupFormatter::MarkupFormatter(EAbsoluteURLs resolveUrlsMethod, SerializationT
ype serializationType) |
| 109 : m_resolveURLsMethod(resolveUrlsMethod) | 109 : m_resolveURLsMethod(resolveUrlsMethod) |
| 110 , m_serializationType(serializationType) | 110 , m_serializationType(serializationType) |
| 111 { | 111 { |
| 112 } | 112 } |
| 113 | 113 |
| 114 MarkupAccumulator::~MarkupAccumulator() | 114 MarkupFormatter::~MarkupFormatter() |
| 115 { | 115 { |
| 116 } | 116 } |
| 117 | 117 |
| 118 String MarkupAccumulator::resolveURLIfNeeded(const Element& element, const Strin
g& urlString) const | 118 String MarkupFormatter::resolveURLIfNeeded(const Element& element, const String&
urlString) const |
| 119 { | 119 { |
| 120 switch (m_resolveURLsMethod) { | 120 switch (m_resolveURLsMethod) { |
| 121 case ResolveAllURLs: | 121 case ResolveAllURLs: |
| 122 return element.document().completeURL(urlString).string(); | 122 return element.document().completeURL(urlString).string(); |
| 123 | 123 |
| 124 case ResolveNonLocalURLs: | 124 case ResolveNonLocalURLs: |
| 125 if (!element.document().url().isLocalFile()) | 125 if (!element.document().url().isLocalFile()) |
| 126 return element.document().completeURL(urlString).string(); | 126 return element.document().completeURL(urlString).string(); |
| 127 break; | 127 break; |
| 128 | 128 |
| 129 case DoNotResolveURLs: | 129 case DoNotResolveURLs: |
| 130 break; | 130 break; |
| 131 } | 131 } |
| 132 return urlString; | 132 return urlString; |
| 133 } | 133 } |
| 134 | 134 |
| 135 void MarkupAccumulator::appendString(const String& string) | |
| 136 { | |
| 137 m_markup.append(string); | |
| 138 } | |
| 139 | |
| 140 void MarkupAccumulator::appendStartTag(Node& node, Namespaces* namespaces) | |
| 141 { | |
| 142 appendStartMarkup(m_markup, node, namespaces); | |
| 143 } | |
| 144 | |
| 145 void MarkupAccumulator::appendEndTag(const Element& element) | |
| 146 { | |
| 147 appendEndMarkup(m_markup, element); | |
| 148 } | |
| 149 | |
| 150 void MarkupAccumulator::appendStartMarkup(StringBuilder& result, Node& node, Nam
espaces* namespaces) | |
| 151 { | |
| 152 switch (node.nodeType()) { | |
| 153 case Node::TEXT_NODE: | |
| 154 appendText(result, toText(node)); | |
| 155 break; | |
| 156 case Node::COMMENT_NODE: | |
| 157 appendComment(result, toComment(node).data()); | |
| 158 break; | |
| 159 case Node::DOCUMENT_NODE: | |
| 160 appendXMLDeclaration(result, toDocument(node)); | |
| 161 break; | |
| 162 case Node::DOCUMENT_FRAGMENT_NODE: | |
| 163 break; | |
| 164 case Node::DOCUMENT_TYPE_NODE: | |
| 165 appendDocumentType(result, toDocumentType(node)); | |
| 166 break; | |
| 167 case Node::PROCESSING_INSTRUCTION_NODE: | |
| 168 appendProcessingInstruction(result, toProcessingInstruction(node).target
(), toProcessingInstruction(node).data()); | |
| 169 break; | |
| 170 case Node::ELEMENT_NODE: | |
| 171 appendElement(result, toElement(node), namespaces); | |
| 172 break; | |
| 173 case Node::CDATA_SECTION_NODE: | |
| 174 appendCDATASection(result, toCDATASection(node).data()); | |
| 175 break; | |
| 176 case Node::ATTRIBUTE_NODE: | |
| 177 ASSERT_NOT_REACHED(); | |
| 178 break; | |
| 179 } | |
| 180 } | |
| 181 | |
| 182 static bool elementCannotHaveEndTag(const Node& node) | 135 static bool elementCannotHaveEndTag(const Node& node) |
| 183 { | 136 { |
| 184 if (!node.isHTMLElement()) | 137 if (!node.isHTMLElement()) |
| 185 return false; | 138 return false; |
| 186 | 139 |
| 187 // FIXME: ieForbidsInsertHTML may not be the right function to call here | 140 // FIXME: ieForbidsInsertHTML may not be the right function to call here |
| 188 // ieForbidsInsertHTML is used to disallow setting innerHTML/outerHTML | 141 // ieForbidsInsertHTML is used to disallow setting innerHTML/outerHTML |
| 189 // or createContextualFragment. It does not necessarily align with | 142 // or createContextualFragment. It does not necessarily align with |
| 190 // which elements should be serialized w/o end tags. | 143 // which elements should be serialized w/o end tags. |
| 191 return toHTMLElement(node).ieForbidsInsertHTML(); | 144 return toHTMLElement(node).ieForbidsInsertHTML(); |
| 192 } | 145 } |
| 193 | 146 |
| 194 void MarkupAccumulator::appendEndMarkup(StringBuilder& result, const Element& el
ement) | 147 void MarkupFormatter::appendEndMarkup(StringBuilder& result, const Element& elem
ent) |
| 195 { | 148 { |
| 196 if (shouldSelfClose(element) || (!element.hasChildren() && elementCannotHave
EndTag(element))) | 149 if (shouldSelfClose(element) || (!element.hasChildren() && elementCannotHave
EndTag(element))) |
| 197 return; | 150 return; |
| 198 | 151 |
| 199 result.appendLiteral("</"); | 152 result.appendLiteral("</"); |
| 200 result.append(element.tagQName().toString()); | 153 result.append(element.tagQName().toString()); |
| 201 result.append('>'); | 154 result.append('>'); |
| 202 } | 155 } |
| 203 | 156 |
| 204 void MarkupAccumulator::concatenateMarkup(StringBuilder& result) const | 157 void MarkupFormatter::appendAttributeValue(StringBuilder& result, const String&
attribute, bool documentIsHTML) |
| 205 { | |
| 206 result.append(m_markup); | |
| 207 } | |
| 208 | |
| 209 void MarkupAccumulator::appendAttributeValue(StringBuilder& result, const String
& attribute, bool documentIsHTML) | |
| 210 { | 158 { |
| 211 appendCharactersReplacingEntities(result, attribute, 0, attribute.length(), | 159 appendCharactersReplacingEntities(result, attribute, 0, attribute.length(), |
| 212 documentIsHTML ? EntityMaskInHTMLAttributeValue : EntityMaskInAttributeV
alue); | 160 documentIsHTML ? EntityMaskInHTMLAttributeValue : EntityMaskInAttributeV
alue); |
| 213 } | 161 } |
| 214 | 162 |
| 215 void MarkupAccumulator::appendCustomAttributes(StringBuilder&, const Element&, N
amespaces*) | 163 void MarkupFormatter::appendQuotedURLAttributeValue(StringBuilder& result, const
Element& element, const Attribute& attribute) |
| 216 { | |
| 217 } | |
| 218 | |
| 219 void MarkupAccumulator::appendQuotedURLAttributeValue(StringBuilder& result, con
st Element& element, const Attribute& attribute) | |
| 220 { | 164 { |
| 221 ASSERT(element.isURLAttribute(attribute)); | 165 ASSERT(element.isURLAttribute(attribute)); |
| 222 const String resolvedURLString = resolveURLIfNeeded(element, attribute.value
()); | 166 const String resolvedURLString = resolveURLIfNeeded(element, attribute.value
()); |
| 223 UChar quoteChar = '"'; | 167 UChar quoteChar = '"'; |
| 224 String strippedURLString = resolvedURLString.stripWhiteSpace(); | 168 String strippedURLString = resolvedURLString.stripWhiteSpace(); |
| 225 if (protocolIsJavaScript(strippedURLString)) { | 169 if (protocolIsJavaScript(strippedURLString)) { |
| 226 // minimal escaping for javascript urls | 170 // minimal escaping for javascript urls |
| 227 if (strippedURLString.contains('&')) | 171 if (strippedURLString.contains('&')) |
| 228 strippedURLString.replaceWithLiteral('&', "&"); | 172 strippedURLString.replaceWithLiteral('&', "&"); |
| 229 | 173 |
| 230 if (strippedURLString.contains('"')) { | 174 if (strippedURLString.contains('"')) { |
| 231 if (strippedURLString.contains('\'')) | 175 if (strippedURLString.contains('\'')) |
| 232 strippedURLString.replaceWithLiteral('"', """); | 176 strippedURLString.replaceWithLiteral('"', """); |
| 233 else | 177 else |
| 234 quoteChar = '\''; | 178 quoteChar = '\''; |
| 235 } | 179 } |
| 236 result.append(quoteChar); | 180 result.append(quoteChar); |
| 237 result.append(strippedURLString); | 181 result.append(strippedURLString); |
| 238 result.append(quoteChar); | 182 result.append(quoteChar); |
| 239 return; | 183 return; |
| 240 } | 184 } |
| 241 | 185 |
| 242 // FIXME: This does not fully match other browsers. Firefox percent-escapes
non-ASCII characters for innerHTML. | 186 // FIXME: This does not fully match other browsers. Firefox percent-escapes
non-ASCII characters for innerHTML. |
| 243 result.append(quoteChar); | 187 result.append(quoteChar); |
| 244 appendAttributeValue(result, resolvedURLString, false); | 188 appendAttributeValue(result, resolvedURLString, false); |
| 245 result.append(quoteChar); | 189 result.append(quoteChar); |
| 246 } | 190 } |
| 247 | 191 |
| 248 void MarkupAccumulator::appendNamespace(StringBuilder& result, const AtomicStrin
g& prefix, const AtomicString& namespaceURI, Namespaces& namespaces) | 192 void MarkupFormatter::appendNamespace(StringBuilder& result, const AtomicString&
prefix, const AtomicString& namespaceURI, Namespaces& namespaces) |
| 249 { | 193 { |
| 250 if (namespaceURI.isEmpty()) | 194 if (namespaceURI.isEmpty()) |
| 251 return; | 195 return; |
| 252 | 196 |
| 253 const AtomicString& lookupKey = (!prefix) ? emptyAtom : prefix; | 197 const AtomicString& lookupKey = (!prefix) ? emptyAtom : prefix; |
| 254 AtomicString foundURI = namespaces.get(lookupKey); | 198 AtomicString foundURI = namespaces.get(lookupKey); |
| 255 if (foundURI != namespaceURI) { | 199 if (foundURI != namespaceURI) { |
| 256 namespaces.set(lookupKey, namespaceURI); | 200 namespaces.set(lookupKey, namespaceURI); |
| 257 result.append(' '); | 201 result.append(' '); |
| 258 result.append(xmlnsAtom.string()); | 202 result.append(xmlnsAtom.string()); |
| 259 if (!prefix.isEmpty()) { | 203 if (!prefix.isEmpty()) { |
| 260 result.append(':'); | 204 result.append(':'); |
| 261 result.append(prefix); | 205 result.append(prefix); |
| 262 } | 206 } |
| 263 | 207 |
| 264 result.appendLiteral("=\""); | 208 result.appendLiteral("=\""); |
| 265 appendAttributeValue(result, namespaceURI, false); | 209 appendAttributeValue(result, namespaceURI, false); |
| 266 result.append('"'); | 210 result.append('"'); |
| 267 } | 211 } |
| 268 } | 212 } |
| 269 | 213 |
| 270 void MarkupAccumulator::appendText(StringBuilder& result, Text& text) | 214 void MarkupFormatter::appendText(StringBuilder& result, Text& text) |
| 271 { | 215 { |
| 272 const String& str = text.data(); | 216 const String& str = text.data(); |
| 273 appendCharactersReplacingEntities(result, str, 0, str.length(), entityMaskFo
rText(text)); | 217 appendCharactersReplacingEntities(result, str, 0, str.length(), entityMaskFo
rText(text)); |
| 274 } | 218 } |
| 275 | 219 |
| 276 void MarkupAccumulator::appendComment(StringBuilder& result, const String& comme
nt) | 220 void MarkupFormatter::appendComment(StringBuilder& result, const String& comment
) |
| 277 { | 221 { |
| 278 // FIXME: Comment content is not escaped, but XMLSerializer (and possibly ot
her callers) should raise an exception if it includes "-->". | 222 // FIXME: Comment content is not escaped, but XMLSerializer (and possibly ot
her callers) should raise an exception if it includes "-->". |
| 279 result.appendLiteral("<!--"); | 223 result.appendLiteral("<!--"); |
| 280 result.append(comment); | 224 result.append(comment); |
| 281 result.appendLiteral("-->"); | 225 result.appendLiteral("-->"); |
| 282 } | 226 } |
| 283 | 227 |
| 284 void MarkupAccumulator::appendXMLDeclaration(StringBuilder& result, const Docume
nt& document) | 228 void MarkupFormatter::appendXMLDeclaration(StringBuilder& result, const Document
& document) |
| 285 { | 229 { |
| 286 if (!document.hasXMLDeclaration()) | 230 if (!document.hasXMLDeclaration()) |
| 287 return; | 231 return; |
| 288 | 232 |
| 289 result.appendLiteral("<?xml version=\""); | 233 result.appendLiteral("<?xml version=\""); |
| 290 result.append(document.xmlVersion()); | 234 result.append(document.xmlVersion()); |
| 291 const String& encoding = document.xmlEncoding(); | 235 const String& encoding = document.xmlEncoding(); |
| 292 if (!encoding.isEmpty()) { | 236 if (!encoding.isEmpty()) { |
| 293 result.appendLiteral("\" encoding=\""); | 237 result.appendLiteral("\" encoding=\""); |
| 294 result.append(encoding); | 238 result.append(encoding); |
| 295 } | 239 } |
| 296 if (document.xmlStandaloneStatus() != Document::StandaloneUnspecified) { | 240 if (document.xmlStandaloneStatus() != Document::StandaloneUnspecified) { |
| 297 result.appendLiteral("\" standalone=\""); | 241 result.appendLiteral("\" standalone=\""); |
| 298 if (document.xmlStandalone()) | 242 if (document.xmlStandalone()) |
| 299 result.appendLiteral("yes"); | 243 result.appendLiteral("yes"); |
| 300 else | 244 else |
| 301 result.appendLiteral("no"); | 245 result.appendLiteral("no"); |
| 302 } | 246 } |
| 303 | 247 |
| 304 result.appendLiteral("\"?>"); | 248 result.appendLiteral("\"?>"); |
| 305 } | 249 } |
| 306 | 250 |
| 307 void MarkupAccumulator::appendDocumentType(StringBuilder& result, const Document
Type& n) | 251 void MarkupFormatter::appendDocumentType(StringBuilder& result, const DocumentTy
pe& n) |
| 308 { | 252 { |
| 309 if (n.name().isEmpty()) | 253 if (n.name().isEmpty()) |
| 310 return; | 254 return; |
| 311 | 255 |
| 312 result.appendLiteral("<!DOCTYPE "); | 256 result.appendLiteral("<!DOCTYPE "); |
| 313 result.append(n.name()); | 257 result.append(n.name()); |
| 314 if (!n.publicId().isEmpty()) { | 258 if (!n.publicId().isEmpty()) { |
| 315 result.appendLiteral(" PUBLIC \""); | 259 result.appendLiteral(" PUBLIC \""); |
| 316 result.append(n.publicId()); | 260 result.append(n.publicId()); |
| 317 result.append('"'); | 261 result.append('"'); |
| 318 if (!n.systemId().isEmpty()) { | 262 if (!n.systemId().isEmpty()) { |
| 319 result.appendLiteral(" \""); | 263 result.appendLiteral(" \""); |
| 320 result.append(n.systemId()); | 264 result.append(n.systemId()); |
| 321 result.append('"'); | 265 result.append('"'); |
| 322 } | 266 } |
| 323 } else if (!n.systemId().isEmpty()) { | 267 } else if (!n.systemId().isEmpty()) { |
| 324 result.appendLiteral(" SYSTEM \""); | 268 result.appendLiteral(" SYSTEM \""); |
| 325 result.append(n.systemId()); | 269 result.append(n.systemId()); |
| 326 result.append('"'); | 270 result.append('"'); |
| 327 } | 271 } |
| 328 result.append('>'); | 272 result.append('>'); |
| 329 } | 273 } |
| 330 | 274 |
| 331 void MarkupAccumulator::appendProcessingInstruction(StringBuilder& result, const
String& target, const String& data) | 275 void MarkupFormatter::appendProcessingInstruction(StringBuilder& result, const S
tring& target, const String& data) |
| 332 { | 276 { |
| 333 // FIXME: PI data is not escaped, but XMLSerializer (and possibly other call
ers) this should raise an exception if it includes "?>". | 277 // FIXME: PI data is not escaped, but XMLSerializer (and possibly other call
ers) this should raise an exception if it includes "?>". |
| 334 result.appendLiteral("<?"); | 278 result.appendLiteral("<?"); |
| 335 result.append(target); | 279 result.append(target); |
| 336 result.append(' '); | 280 result.append(' '); |
| 337 result.append(data); | 281 result.append(data); |
| 338 result.appendLiteral("?>"); | 282 result.appendLiteral("?>"); |
| 339 } | 283 } |
| 340 | 284 |
| 341 bool MarkupAccumulator::shouldIgnoreAttribute(const Attribute& attribute) | 285 void MarkupFormatter::appendOpenTag(StringBuilder& result, const Element& elemen
t, Namespaces* namespaces) |
| 342 { | |
| 343 return false; | |
| 344 } | |
| 345 | |
| 346 void MarkupAccumulator::appendElement(StringBuilder& result, Element& element, N
amespaces* namespaces) | |
| 347 { | |
| 348 appendOpenTag(result, element, namespaces); | |
| 349 | |
| 350 AttributeCollection attributes = element.attributes(); | |
| 351 for (const auto& attribute : attributes) { | |
| 352 if (!shouldIgnoreAttribute(attribute)) | |
| 353 appendAttribute(result, element, attribute, namespaces); | |
| 354 } | |
| 355 | |
| 356 // Give an opportunity to subclasses to add their own attributes. | |
| 357 appendCustomAttributes(result, element, namespaces); | |
| 358 | |
| 359 appendCloseTag(result, element); | |
| 360 } | |
| 361 | |
| 362 void MarkupAccumulator::appendOpenTag(StringBuilder& result, const Element& elem
ent, Namespaces* namespaces) | |
| 363 { | 286 { |
| 364 result.append('<'); | 287 result.append('<'); |
| 365 result.append(element.tagQName().toString()); | 288 result.append(element.tagQName().toString()); |
| 366 if (!serializeAsHTMLDocument(element) && namespaces && shouldAddNamespaceEle
ment(element, *namespaces)) | 289 if (!serializeAsHTMLDocument(element) && namespaces && shouldAddNamespaceEle
ment(element, *namespaces)) |
| 367 appendNamespace(result, element.prefix(), element.namespaceURI(), *names
paces); | 290 appendNamespace(result, element.prefix(), element.namespaceURI(), *names
paces); |
| 368 } | 291 } |
| 369 | 292 |
| 370 void MarkupAccumulator::appendCloseTag(StringBuilder& result, const Element& ele
ment) | 293 void MarkupFormatter::appendCloseTag(StringBuilder& result, const Element& eleme
nt) |
| 371 { | 294 { |
| 372 if (shouldSelfClose(element)) { | 295 if (shouldSelfClose(element)) { |
| 373 if (element.isHTMLElement()) | 296 if (element.isHTMLElement()) |
| 374 result.append(' '); // XHTML 1.0 <-> HTML compatibility. | 297 result.append(' '); // XHTML 1.0 <-> HTML compatibility. |
| 375 result.append('/'); | 298 result.append('/'); |
| 376 } | 299 } |
| 377 result.append('>'); | 300 result.append('>'); |
| 378 } | 301 } |
| 379 | 302 |
| 380 static inline bool attributeIsInSerializedNamespace(const Attribute& attribute) | 303 static inline bool attributeIsInSerializedNamespace(const Attribute& attribute) |
| 381 { | 304 { |
| 382 return attribute.namespaceURI() == XMLNames::xmlNamespaceURI | 305 return attribute.namespaceURI() == XMLNames::xmlNamespaceURI |
| 383 || attribute.namespaceURI() == XLinkNames::xlinkNamespaceURI | 306 || attribute.namespaceURI() == XLinkNames::xlinkNamespaceURI |
| 384 || attribute.namespaceURI() == XMLNSNames::xmlnsNamespaceURI; | 307 || attribute.namespaceURI() == XMLNSNames::xmlnsNamespaceURI; |
| 385 } | 308 } |
| 386 | 309 |
| 387 void MarkupAccumulator::appendAttribute(StringBuilder& result, const Element& el
ement, const Attribute& attribute, Namespaces* namespaces) | 310 void MarkupFormatter::appendAttribute(StringBuilder& result, const Element& elem
ent, const Attribute& attribute, Namespaces* namespaces) |
| 388 { | 311 { |
| 389 bool documentIsHTML = serializeAsHTMLDocument(element); | 312 bool documentIsHTML = serializeAsHTMLDocument(element); |
| 390 | 313 |
| 391 QualifiedName prefixedName = attribute.name(); | 314 QualifiedName prefixedName = attribute.name(); |
| 392 if (documentIsHTML && !attributeIsInSerializedNamespace(attribute)) { | 315 if (documentIsHTML && !attributeIsInSerializedNamespace(attribute)) { |
| 393 result.append(' '); | 316 result.append(' '); |
| 394 result.append(attribute.name().localName()); | 317 result.append(attribute.name().localName()); |
| 395 } else { | 318 } else { |
| 396 if (attribute.namespaceURI() == XMLNSNames::xmlnsNamespaceURI) { | 319 if (attribute.namespaceURI() == XMLNSNames::xmlnsNamespaceURI) { |
| 397 if (!attribute.prefix() && attribute.localName() != xmlnsAtom) | 320 if (!attribute.prefix() && attribute.localName() != xmlnsAtom) |
| (...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 435 | 358 |
| 436 if (element.isURLAttribute(attribute)) { | 359 if (element.isURLAttribute(attribute)) { |
| 437 appendQuotedURLAttributeValue(result, element, attribute); | 360 appendQuotedURLAttributeValue(result, element, attribute); |
| 438 } else { | 361 } else { |
| 439 result.append('"'); | 362 result.append('"'); |
| 440 appendAttributeValue(result, attribute.value(), documentIsHTML); | 363 appendAttributeValue(result, attribute.value(), documentIsHTML); |
| 441 result.append('"'); | 364 result.append('"'); |
| 442 } | 365 } |
| 443 } | 366 } |
| 444 | 367 |
| 445 void MarkupAccumulator::appendCDATASection(StringBuilder& result, const String&
section) | 368 void MarkupFormatter::appendCDATASection(StringBuilder& result, const String& se
ction) |
| 446 { | 369 { |
| 447 // FIXME: CDATA content is not escaped, but XMLSerializer (and possibly othe
r callers) should raise an exception if it includes "]]>". | 370 // FIXME: CDATA content is not escaped, but XMLSerializer (and possibly othe
r callers) should raise an exception if it includes "]]>". |
| 448 result.appendLiteral("<![CDATA["); | 371 result.appendLiteral("<![CDATA["); |
| 449 result.append(section); | 372 result.append(section); |
| 450 result.appendLiteral("]]>"); | 373 result.appendLiteral("]]>"); |
| 451 } | 374 } |
| 452 | 375 |
| 453 bool MarkupAccumulator::shouldAddNamespaceElement(const Element& element, Namesp
aces& namespaces) const | 376 bool MarkupFormatter::shouldAddNamespaceElement(const Element& element, Namespac
es& namespaces) const |
| 454 { | 377 { |
| 455 // Don't add namespace attribute if it is already defined for this elem. | 378 // Don't add namespace attribute if it is already defined for this elem. |
| 456 const AtomicString& prefix = element.prefix(); | 379 const AtomicString& prefix = element.prefix(); |
| 457 if (prefix.isEmpty()) { | 380 if (prefix.isEmpty()) { |
| 458 if (element.hasAttribute(xmlnsAtom)) { | 381 if (element.hasAttribute(xmlnsAtom)) { |
| 459 namespaces.set(emptyAtom, element.namespaceURI()); | 382 namespaces.set(emptyAtom, element.namespaceURI()); |
| 460 return false; | 383 return false; |
| 461 } | 384 } |
| 462 return true; | 385 return true; |
| 463 } | 386 } |
| 464 | 387 |
| 465 return !element.hasAttribute(WTF::xmlnsWithColon + prefix); | 388 return !element.hasAttribute(WTF::xmlnsWithColon + prefix); |
| 466 } | 389 } |
| 467 | 390 |
| 468 bool MarkupAccumulator::shouldAddNamespaceAttribute(const Attribute& attribute,
const Element& element) const | 391 bool MarkupFormatter::shouldAddNamespaceAttribute(const Attribute& attribute, co
nst Element& element) const |
| 469 { | 392 { |
| 470 // xmlns and xmlns:prefix attributes should be handled by another branch in
appendAttribute. | 393 // xmlns and xmlns:prefix attributes should be handled by another branch in
appendAttribute. |
| 471 ASSERT(attribute.namespaceURI() != XMLNSNames::xmlnsNamespaceURI); | 394 ASSERT(attribute.namespaceURI() != XMLNSNames::xmlnsNamespaceURI); |
| 472 | 395 |
| 473 // Attributes are in the null namespace by default. | 396 // Attributes are in the null namespace by default. |
| 474 if (!attribute.namespaceURI()) | 397 if (!attribute.namespaceURI()) |
| 475 return false; | 398 return false; |
| 476 | 399 |
| 477 // Attributes without a prefix will need one generated for them, and an xmln
s attribute for that prefix. | 400 // Attributes without a prefix will need one generated for them, and an xmln
s attribute for that prefix. |
| 478 if (!attribute.prefix()) | 401 if (!attribute.prefix()) |
| 479 return true; | 402 return true; |
| 480 | 403 |
| 481 return !element.hasAttribute(WTF::xmlnsWithColon + attribute.prefix()); | 404 return !element.hasAttribute(WTF::xmlnsWithColon + attribute.prefix()); |
| 482 } | 405 } |
| 483 | 406 |
| 484 EntityMask MarkupAccumulator::entityMaskForText(const Text& text) const | 407 EntityMask MarkupFormatter::entityMaskForText(const Text& text) const |
| 485 { | 408 { |
| 486 if (!serializeAsHTMLDocument(text)) | 409 if (!serializeAsHTMLDocument(text)) |
| 487 return EntityMaskInPCDATA; | 410 return EntityMaskInPCDATA; |
| 488 | 411 |
| 489 // TODO(hajimehoshi): We need to switch EditingStrategy. | 412 // TODO(hajimehoshi): We need to switch EditingStrategy. |
| 490 const QualifiedName* parentName = nullptr; | 413 const QualifiedName* parentName = nullptr; |
| 491 if (text.parentElement()) | 414 if (text.parentElement()) |
| 492 parentName = &(text.parentElement())->tagQName(); | 415 parentName = &(text.parentElement())->tagQName(); |
| 493 | 416 |
| 494 if (parentName && (*parentName == scriptTag || *parentName == styleTag || *p
arentName == xmpTag)) | 417 if (parentName && (*parentName == scriptTag || *parentName == styleTag || *p
arentName == xmpTag)) |
| 495 return EntityMaskInCDATA; | 418 return EntityMaskInCDATA; |
| 496 return EntityMaskInHTMLPCDATA; | 419 return EntityMaskInHTMLPCDATA; |
| 497 } | 420 } |
| 498 | 421 |
| 499 // Rules of self-closure | 422 // Rules of self-closure |
| 500 // 1. No elements in HTML documents use the self-closing syntax. | 423 // 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. | 424 // 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. | 425 // 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. | 426 // 4. Other elements self-close. |
| 504 bool MarkupAccumulator::shouldSelfClose(const Element& element) const | 427 bool MarkupFormatter::shouldSelfClose(const Element& element) const |
| 505 { | 428 { |
| 506 if (serializeAsHTMLDocument(element)) | 429 if (serializeAsHTMLDocument(element)) |
| 507 return false; | 430 return false; |
| 508 if (element.hasChildren()) | 431 if (element.hasChildren()) |
| 509 return false; | 432 return false; |
| 510 if (element.isHTMLElement() && !elementCannotHaveEndTag(element)) | 433 if (element.isHTMLElement() && !elementCannotHaveEndTag(element)) |
| 511 return false; | 434 return false; |
| 512 return true; | 435 return true; |
| 513 } | 436 } |
| 514 | 437 |
| 515 bool MarkupAccumulator::serializeAsHTMLDocument(const Node& node) const | 438 bool MarkupFormatter::serializeAsHTMLDocument(const Node& node) const |
| 516 { | 439 { |
| 517 if (m_serializationType == SerializationType::ForcedXML) | 440 if (m_serializationType == SerializationType::ForcedXML) |
| 518 return false; | 441 return false; |
| 519 return node.document().isHTMLDocument(); | 442 return node.document().isHTMLDocument(); |
| 520 } | 443 } |
| 521 | 444 |
| 522 template<typename Strategy> | |
| 523 static void serializeNodesWithNamespaces(MarkupAccumulator& accumulator, Node& t
argetNode, EChildrenOnly childrenOnly, const Namespaces* namespaces) | |
| 524 { | |
| 525 Namespaces namespaceHash; | |
| 526 if (namespaces) | |
| 527 namespaceHash = *namespaces; | |
| 528 | |
| 529 if (!childrenOnly) | |
| 530 accumulator.appendStartTag(targetNode, &namespaceHash); | |
| 531 | |
| 532 if (!(accumulator.serializeAsHTMLDocument(targetNode) && elementCannotHaveEn
dTag(targetNode))) { | |
| 533 Node* current = isHTMLTemplateElement(targetNode) ? Strategy::firstChild
(*toHTMLTemplateElement(targetNode).content()) : Strategy::firstChild(targetNode
); | |
| 534 for ( ; current; current = Strategy::nextSibling(*current)) | |
| 535 serializeNodesWithNamespaces<Strategy>(accumulator, *current, Includ
eNode, &namespaceHash); | |
| 536 } | |
| 537 | |
| 538 if (!childrenOnly && targetNode.isElementNode()) | |
| 539 accumulator.appendEndTag(toElement(targetNode)); | |
| 540 } | 445 } |
| 541 | |
| 542 template<typename Strategy> | |
| 543 String serializeNodes(MarkupAccumulator& accumulator, Node& targetNode, EChildre
nOnly childrenOnly) | |
| 544 { | |
| 545 Namespaces* namespaces = nullptr; | |
| 546 Namespaces namespaceHash; | |
| 547 if (!accumulator.serializeAsHTMLDocument(targetNode)) { | |
| 548 // Add pre-bound namespaces for XML fragments. | |
| 549 namespaceHash.set(xmlAtom, XMLNames::xmlNamespaceURI); | |
| 550 namespaces = &namespaceHash; | |
| 551 } | |
| 552 | |
| 553 serializeNodesWithNamespaces<Strategy>(accumulator, targetNode, childrenOnly
, namespaces); | |
| 554 return accumulator.toString(); | |
| 555 } | |
| 556 | |
| 557 template String serializeNodes<EditingStrategy>(MarkupAccumulator&, Node&, EChil
drenOnly); | |
| 558 | |
| 559 } | |
| OLD | NEW |