| OLD | NEW |
| (Empty) |
| 1 /* | |
| 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. | |
| 4 * | |
| 5 * Redistribution and use in source and binary forms, with or without | |
| 6 * modification, are permitted provided that the following conditions | |
| 7 * are met: | |
| 8 * 1. Redistributions of source code must retain the above copyright | |
| 9 * notice, this list of conditions and the following disclaimer. | |
| 10 * 2. Redistributions in binary form must reproduce the above copyright | |
| 11 * notice, this list of conditions and the following disclaimer in the | |
| 12 * documentation and/or other materials provided with the distribution. | |
| 13 * | |
| 14 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
| 15 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
| 16 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |
| 17 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |
| 18 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |
| 19 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |
| 20 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |
| 21 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |
| 22 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
| 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. | |
| 25 */ | |
| 26 | |
| 27 #include "sky/engine/config.h" | |
| 28 #include "sky/engine/core/editing/MarkupAccumulator.h" | |
| 29 | |
| 30 #include "gen/sky/core/HTMLNames.h" | |
| 31 #include "sky/engine/core/dom/Document.h" | |
| 32 #include "sky/engine/core/dom/DocumentFragment.h" | |
| 33 #include "sky/engine/core/dom/Text.h" | |
| 34 #include "sky/engine/core/editing/Editor.h" | |
| 35 #include "sky/engine/core/html/HTMLElement.h" | |
| 36 #include "sky/engine/core/html/HTMLTemplateElement.h" | |
| 37 #include "sky/engine/platform/weborigin/KURL.h" | |
| 38 #include "sky/engine/wtf/unicode/CharacterNames.h" | |
| 39 | |
| 40 namespace blink { | |
| 41 | |
| 42 struct EntityDescription { | |
| 43 UChar entity; | |
| 44 const CString& reference; | |
| 45 EntityMask mask; | |
| 46 }; | |
| 47 | |
| 48 template <typename CharType> | |
| 49 static inline void appendCharactersReplacingEntitiesInternal(StringBuilder& resu
lt, CharType* text, unsigned length, const EntityDescription entityMaps[], unsig
ned entityMapsCount, EntityMask entityMask) | |
| 50 { | |
| 51 unsigned positionAfterLastEntity = 0; | |
| 52 for (unsigned i = 0; i < length; ++i) { | |
| 53 for (unsigned entityIndex = 0; entityIndex < entityMapsCount; ++entityIn
dex) { | |
| 54 if (text[i] == entityMaps[entityIndex].entity && entityMaps[entityIn
dex].mask & entityMask) { | |
| 55 result.append(text + positionAfterLastEntity, i - positionAfterL
astEntity); | |
| 56 const CString& replacement = entityMaps[entityIndex].reference; | |
| 57 result.append(replacement.data(), replacement.length()); | |
| 58 positionAfterLastEntity = i + 1; | |
| 59 break; | |
| 60 } | |
| 61 } | |
| 62 } | |
| 63 result.append(text + positionAfterLastEntity, length - positionAfterLastEnti
ty); | |
| 64 } | |
| 65 | |
| 66 void MarkupAccumulator::appendCharactersReplacingEntities(StringBuilder& result,
const String& source, unsigned offset, unsigned length, EntityMask entityMask) | |
| 67 { | |
| 68 DEFINE_STATIC_LOCAL(const CString, ampReference, ("&")); | |
| 69 DEFINE_STATIC_LOCAL(const CString, ltReference, ("<")); | |
| 70 DEFINE_STATIC_LOCAL(const CString, gtReference, (">")); | |
| 71 DEFINE_STATIC_LOCAL(const CString, quotReference, (""")); | |
| 72 DEFINE_STATIC_LOCAL(const CString, nbspReference, (" ")); | |
| 73 | |
| 74 static const EntityDescription entityMaps[] = { | |
| 75 { '&', ampReference, EntityAmp }, | |
| 76 { '<', ltReference, EntityLt }, | |
| 77 { '>', gtReference, EntityGt }, | |
| 78 { '"', quotReference, EntityQuot }, | |
| 79 { noBreakSpace, nbspReference, EntityNbsp }, | |
| 80 }; | |
| 81 | |
| 82 if (!(offset + length)) | |
| 83 return; | |
| 84 | |
| 85 ASSERT(offset + length <= source.length()); | |
| 86 if (source.is8Bit()) | |
| 87 appendCharactersReplacingEntitiesInternal(result, source.characters8() +
offset, length, entityMaps, WTF_ARRAY_LENGTH(entityMaps), entityMask); | |
| 88 else | |
| 89 appendCharactersReplacingEntitiesInternal(result, source.characters16()
+ offset, length, entityMaps, WTF_ARRAY_LENGTH(entityMaps), entityMask); | |
| 90 } | |
| 91 | |
| 92 MarkupAccumulator::MarkupAccumulator(Vector<RawPtr<Node> >* nodes, EAbsoluteURLs
resolveUrlsMethod, const Range* range) | |
| 93 : m_nodes(nodes) | |
| 94 , m_range(range) | |
| 95 , m_resolveURLsMethod(resolveUrlsMethod) | |
| 96 { | |
| 97 } | |
| 98 | |
| 99 MarkupAccumulator::~MarkupAccumulator() | |
| 100 { | |
| 101 } | |
| 102 | |
| 103 String MarkupAccumulator::serializeNodes(Node& targetNode, EChildrenOnly childre
nOnly, Vector<QualifiedName>* tagNamesToSkip) | |
| 104 { | |
| 105 Namespaces* namespaces = 0; | |
| 106 Namespaces namespaceHash; | |
| 107 serializeNodesWithNamespaces(targetNode, childrenOnly, namespaces, tagNamesT
oSkip); | |
| 108 return m_markup.toString(); | |
| 109 } | |
| 110 | |
| 111 void MarkupAccumulator::serializeNodesWithNamespaces(Node& targetNode, EChildren
Only childrenOnly, const Namespaces* namespaces, Vector<QualifiedName>* tagNames
ToSkip) | |
| 112 { | |
| 113 if (tagNamesToSkip && targetNode.isElementNode()) { | |
| 114 for (size_t i = 0; i < tagNamesToSkip->size(); ++i) { | |
| 115 if (toElement(targetNode).hasTagName(tagNamesToSkip->at(i))) | |
| 116 return; | |
| 117 } | |
| 118 } | |
| 119 | |
| 120 Namespaces namespaceHash; | |
| 121 if (namespaces) | |
| 122 namespaceHash = *namespaces; | |
| 123 | |
| 124 if (!childrenOnly) | |
| 125 appendStartTag(targetNode, &namespaceHash); | |
| 126 | |
| 127 Node* current = isHTMLTemplateElement(targetNode) ? toHTMLTemplateElement(ta
rgetNode).content()->firstChild() : targetNode.firstChild(); | |
| 128 for ( ; current; current = current->nextSibling()) | |
| 129 serializeNodesWithNamespaces(*current, IncludeNode, &namespaceHash, tagN
amesToSkip); | |
| 130 | |
| 131 if (!childrenOnly && targetNode.isElementNode()) | |
| 132 appendEndTag(toElement(targetNode)); | |
| 133 } | |
| 134 | |
| 135 String MarkupAccumulator::resolveURLIfNeeded(const Element& element, const Strin
g& urlString) const | |
| 136 { | |
| 137 switch (m_resolveURLsMethod) { | |
| 138 case ResolveAllURLs: | |
| 139 return element.document().completeURL(urlString).string(); | |
| 140 | |
| 141 case ResolveNonLocalURLs: | |
| 142 if (!element.document().url().isLocalFile()) | |
| 143 return element.document().completeURL(urlString).string(); | |
| 144 break; | |
| 145 | |
| 146 case DoNotResolveURLs: | |
| 147 break; | |
| 148 } | |
| 149 return urlString; | |
| 150 } | |
| 151 | |
| 152 void MarkupAccumulator::appendString(const String& string) | |
| 153 { | |
| 154 m_markup.append(string); | |
| 155 } | |
| 156 | |
| 157 void MarkupAccumulator::appendStartTag(Node& node, Namespaces* namespaces) | |
| 158 { | |
| 159 appendStartMarkup(m_markup, node, namespaces); | |
| 160 if (m_nodes) | |
| 161 m_nodes->append(&node); | |
| 162 } | |
| 163 | |
| 164 void MarkupAccumulator::appendEndTag(const Element& element) | |
| 165 { | |
| 166 appendEndMarkup(m_markup, element); | |
| 167 } | |
| 168 | |
| 169 size_t MarkupAccumulator::totalLength(const Vector<String>& strings) | |
| 170 { | |
| 171 size_t length = 0; | |
| 172 for (size_t i = 0; i < strings.size(); ++i) | |
| 173 length += strings[i].length(); | |
| 174 return length; | |
| 175 } | |
| 176 | |
| 177 void MarkupAccumulator::concatenateMarkup(StringBuilder& result) | |
| 178 { | |
| 179 result.append(m_markup); | |
| 180 } | |
| 181 | |
| 182 void MarkupAccumulator::appendAttributeValue(StringBuilder& result, const String
& attribute, bool documentIsHTML) | |
| 183 { | |
| 184 appendCharactersReplacingEntities(result, attribute, 0, attribute.length(), | |
| 185 documentIsHTML ? EntityMaskInHTMLAttributeValue : EntityMaskInAttributeV
alue); | |
| 186 } | |
| 187 | |
| 188 void MarkupAccumulator::appendCustomAttributes(StringBuilder&, const Element&, N
amespaces*) | |
| 189 { | |
| 190 } | |
| 191 | |
| 192 void MarkupAccumulator::appendQuotedURLAttributeValue(StringBuilder& result, con
st Element& element, const Attribute& attribute) | |
| 193 { | |
| 194 ASSERT(element.isURLAttribute(attribute)); | |
| 195 const String resolvedURLString = resolveURLIfNeeded(element, attribute.value
()); | |
| 196 UChar quoteChar = '"'; | |
| 197 String strippedURLString = resolvedURLString.stripWhiteSpace(); | |
| 198 if (protocolIsJavaScript(strippedURLString)) { | |
| 199 // minimal escaping for javascript urls | |
| 200 if (strippedURLString.contains('"')) { | |
| 201 if (strippedURLString.contains('\'')) | |
| 202 strippedURLString.replaceWithLiteral('"', """); | |
| 203 else | |
| 204 quoteChar = '\''; | |
| 205 } | |
| 206 result.append(quoteChar); | |
| 207 result.append(strippedURLString); | |
| 208 result.append(quoteChar); | |
| 209 return; | |
| 210 } | |
| 211 | |
| 212 // FIXME: This does not fully match other browsers. Firefox percent-escapes
non-ASCII characters for innerHTML. | |
| 213 result.append(quoteChar); | |
| 214 appendAttributeValue(result, resolvedURLString, false); | |
| 215 result.append(quoteChar); | |
| 216 } | |
| 217 | |
| 218 EntityMask MarkupAccumulator::entityMaskForText(const Text& text) const | |
| 219 { | |
| 220 const QualifiedName* parentName = 0; | |
| 221 if (text.parentElement()) | |
| 222 parentName = &(text.parentElement())->tagQName(); | |
| 223 | |
| 224 if (parentName && (*parentName == HTMLNames::scriptTag || *parentName == HTM
LNames::styleTag)) | |
| 225 return EntityMaskInCDATA; | |
| 226 return EntityMaskInHTMLPCDATA; | |
| 227 } | |
| 228 | |
| 229 void MarkupAccumulator::appendText(StringBuilder& result, Text& text) | |
| 230 { | |
| 231 const String& str = text.data(); | |
| 232 unsigned length = str.length(); | |
| 233 unsigned start = 0; | |
| 234 | |
| 235 if (m_range) { | |
| 236 if (text == m_range->endContainer()) | |
| 237 length = m_range->endOffset(); | |
| 238 if (text == m_range->startContainer()) { | |
| 239 start = m_range->startOffset(); | |
| 240 length -= start; | |
| 241 } | |
| 242 } | |
| 243 appendCharactersReplacingEntities(result, str, start, length, entityMaskForT
ext(text)); | |
| 244 } | |
| 245 | |
| 246 void MarkupAccumulator::appendElement(StringBuilder& result, Element& element, N
amespaces* namespaces) | |
| 247 { | |
| 248 appendOpenTag(result, element, namespaces); | |
| 249 | |
| 250 AttributeCollection attributes = element.attributes(); | |
| 251 AttributeCollection::iterator end = attributes.end(); | |
| 252 for (AttributeCollection::iterator it = attributes.begin(); it != end; ++it) | |
| 253 appendAttribute(result, element, *it, namespaces); | |
| 254 | |
| 255 // Give an opportunity to subclasses to add their own attributes. | |
| 256 appendCustomAttributes(result, element, namespaces); | |
| 257 | |
| 258 appendCloseTag(result, element); | |
| 259 } | |
| 260 | |
| 261 void MarkupAccumulator::appendOpenTag(StringBuilder& result, const Element& elem
ent, Namespaces* namespaces) | |
| 262 { | |
| 263 result.append('<'); | |
| 264 result.append(element.tagQName().localName()); | |
| 265 } | |
| 266 | |
| 267 void MarkupAccumulator::appendCloseTag(StringBuilder& result, const Element& ele
ment) | |
| 268 { | |
| 269 result.append('>'); | |
| 270 } | |
| 271 | |
| 272 void MarkupAccumulator::appendAttribute(StringBuilder& result, const Element& el
ement, const Attribute& attribute, Namespaces* namespaces) | |
| 273 { | |
| 274 QualifiedName prefixedName = attribute.name(); | |
| 275 result.append(' '); | |
| 276 result.append(attribute.name().localName()); | |
| 277 result.append('='); | |
| 278 | |
| 279 if (element.isURLAttribute(attribute)) { | |
| 280 appendQuotedURLAttributeValue(result, element, attribute); | |
| 281 } else { | |
| 282 result.append('"'); | |
| 283 bool documentIsHTML = true; | |
| 284 appendAttributeValue(result, attribute.value(), documentIsHTML); | |
| 285 result.append('"'); | |
| 286 } | |
| 287 } | |
| 288 | |
| 289 void MarkupAccumulator::appendStartMarkup(StringBuilder& result, Node& node, Nam
espaces* namespaces) | |
| 290 { | |
| 291 switch (node.nodeType()) { | |
| 292 case Node::TEXT_NODE: | |
| 293 appendText(result, toText(node)); | |
| 294 break; | |
| 295 case Node::DOCUMENT_NODE: | |
| 296 break; | |
| 297 case Node::DOCUMENT_FRAGMENT_NODE: | |
| 298 // Not implemented. | |
| 299 break; | |
| 300 case Node::ELEMENT_NODE: | |
| 301 appendElement(result, toElement(node), namespaces); | |
| 302 break; | |
| 303 } | |
| 304 } | |
| 305 | |
| 306 void MarkupAccumulator::appendEndMarkup(StringBuilder& result, const Element& el
ement) | |
| 307 { | |
| 308 result.appendLiteral("</"); | |
| 309 result.append(element.tagQName().localName()); | |
| 310 result.append('>'); | |
| 311 } | |
| 312 | |
| 313 } | |
| OLD | NEW |