Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright 2014 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 "use strict"; | |
|
yosin_UTC9
2014/09/03 03:35:02
Let's use one kind of quote.
I suggest to use sing
tasak
2014/09/03 09:38:00
Done.
| |
| 6 | |
| 7 installClass("XMLSerializer", function(XMLSerializerPrototype) { | |
| 8 | |
| 9 var _EMPTY = ''; | |
| 10 | |
| 11 var _XML_NAMESPACE_URI = 'http://www.w3.org/XML/1998/namespace'; | |
| 12 var _XLINK_NAMESPACE_URI = 'http://www.w3.org/1999/xlink'; | |
| 13 var _XMLNS_NAMESPACE_URI = 'http://www.w3.org/2000/xmlns'; | |
| 14 var _HTML_NAMESPACE_URI = 'http://www.w3.org/1999/xhtml'; | |
| 15 | |
| 16 var _SERIALIZATION_TYPE_AS_OWNER_DOCUMENT = 0; | |
| 17 var _SERIALIZATION_TYPE_FORCED_XML = 1; | |
| 18 | |
| 19 function escape(text) { | |
| 20 return text.replace(/&/g, '&').replace(/\u0022/g, '"').replace( /</g, '<').replace(/>/g, '>'); | |
| 21 } | |
| 22 | |
| 23 function validateURI(uri) { | |
| 24 if (!uri) | |
|
yosin_UTC9
2014/09/03 03:35:02
nit: return uri === '' ? '' : uri.replace(/\/$/g,
tasak
2014/09/03 09:38:00
We need to consier the case that uri is null.
So
| |
| 25 return uri; | |
| 26 return uri.replace(/\/$/g, ''); | |
| 27 } | |
| 28 | |
| 29 function cloneNamespace(obj) { | |
|
yosin_UTC9
2014/09/03 03:35:01
This function make clone of object, not sure why w
tasak
2014/09/03 09:38:00
When we visit child nodes, one of the child nodes
| |
| 30 var clone = {}; | |
|
yosin_UTC9
2014/09/03 03:35:01
nit: return Object.create(obj);
tasak
2014/09/03 09:38:00
I think, this doesn't work.
| |
| 31 for (var i in obj) | |
| 32 clone[i] = obj[i]; | |
| 33 return clone; | |
| 34 } | |
| 35 | |
| 36 function isTemplateElement(node) { | |
| 37 if (isHTMLElement(node) && node.tagName.toLowerCase() == 'template') | |
|
yosin_UTC9
2014/09/03 03:35:01
nit: Just use |return| statement.
nit: We don't ne
tasak
2014/09/03 09:38:00
I don't think so. If we always compare with 'TEMPL
| |
| 38 return true; | |
| 39 return false; | |
| 40 } | |
| 41 | |
| 42 function generatePrefix(namespaces) { | |
| 43 var _MAX_RETRY = 10; | |
| 44 for (var count = 0; count < _MAX_RETRY; ++count) { | |
| 45 var candidate = 'ns' + Date.now(); | |
|
yosin_UTC9
2014/09/03 03:35:01
We should have closed variable, rather than using
tasak
2014/09/03 09:38:00
I tried another approach.
| |
| 46 if (!namespaces[candidate]) | |
| 47 return candidate; | |
| 48 } | |
| 49 return null; | |
| 50 } | |
| 51 | |
| 52 function qualifiedTagName(node) { | |
| 53 var tagName = node.tagName.toLowerCase(); | |
|
yosin_UTC9
2014/09/03 03:35:02
not obvious what you want to accomplish.
Can we us
tasak
2014/09/03 09:38:00
I updated.
If a given node is HTMLElement, we shou
| |
| 54 if (tagName != node.tagName && node.tagName.toUpperCase() != node.tagNam e) | |
| 55 tagName = node.tagName; | |
| 56 return tagName; | |
| 57 } | |
| 58 | |
| 59 function attributeIsInSerializedNamespace(attr) { | |
| 60 var namespaceURI = validateURI(attr.namespaceURI); | |
| 61 if (namespaceURI == _XML_NAMESPACE_URI || | |
|
yosin_UTC9
2014/09/03 03:35:02
We should have |Set| which member is well know nam
tasak
2014/09/03 09:38:00
Done.
| |
| 62 namespaceURI == _XLINK_NAMESPACE_URI || | |
| 63 namespaceURI == _XMLNS_NAMESPACE_URI) | |
| 64 return true; | |
| 65 return false; | |
| 66 } | |
| 67 | |
| 68 function appendNamespace(prefix, namespaceURI, namespaces) { | |
| 69 if (!namespaceURI) | |
| 70 return ''; | |
| 71 | |
| 72 var result = ''; | |
| 73 var lookupKey = _EMPTY; | |
| 74 if (prefix) | |
| 75 lookupKey = prefix; | |
| 76 if (namespaces[lookupKey] != namespaceURI) { | |
| 77 namespaces[lookupKey] = namespaceURI; | |
| 78 result += ' xmlns'; | |
| 79 if (prefix) { | |
|
yosin_UTC9
2014/09/03 03:35:01
nit: No need to have "{}"
tasak
2014/09/03 09:38:00
Done.
| |
| 80 result += ':' + prefix; | |
| 81 } | |
| 82 result += '=\"' + namespaceURI + '"'; | |
| 83 } | |
| 84 return result; | |
| 85 } | |
| 86 | |
| 87 function shouldAddNamespaceAttribute(attr, node) { | |
| 88 if (!attr.namespaceURI) | |
| 89 return false; | |
| 90 if (!attr.prefix) | |
| 91 return true; | |
| 92 if (!node.hasAttribute('xmlns:' + attr.prefix)) | |
| 93 return true; | |
| 94 return false; | |
| 95 } | |
| 96 | |
| 97 function shouldAddNamespaceElement(node, namespaces) { | |
| 98 var prefix = node.prefix; | |
| 99 if (!node.prefix) { | |
| 100 if (node.hasAttribute('xmlns')) { | |
| 101 namespaces[_EMPTY] = node.namespaceURI; | |
| 102 return false; | |
| 103 } | |
| 104 return true; | |
| 105 } | |
| 106 if (!node.hasAttribute('xmlns:' + node.prefix)) | |
| 107 return true; | |
| 108 return false; | |
| 109 } | |
| 110 | |
| 111 function isHTMLElement(node) { | |
| 112 if (node.nodeType != Node.ELEMENT_NODE) | |
| 113 return false; | |
| 114 | |
| 115 while (node) { | |
|
yosin_UTC9
2014/09/03 03:35:01
nit: it is better to use for statement
for (var r
tasak
2014/09/03 09:38:00
Done.
| |
| 116 if (node.nodeType == Node.ELEMENT_NODE) { | |
| 117 var namespaceURI = validateURI(node.namespaceURI); | |
| 118 if (namespaceURI) | |
| 119 return namespaceURI == _HTML_NAMESPACE_URI; | |
| 120 } | |
| 121 node = node.parentNode; | |
| 122 } | |
| 123 return false; | |
| 124 } | |
| 125 | |
| 126 function elementCannotHaveEndTag(node) { | |
|
yosin_UTC9
2014/09/03 03:35:01
nit: HTML5 use term "Tag ommission in text/html"
h
tasak
2014/09/03 09:38:00
Done.
| |
| 127 if (node.nodeType != Node.ELEMENT_NODE) | |
| 128 return false; | |
| 129 var _IE_FORBIDS_TAG = {'area':true, 'base':true, 'basefont':true, 'br':t rue, 'col':true, 'embed':true, | |
| 130 'frame':true, 'hr':true, 'image':true, 'img':true , 'input':true, 'link':true, | |
| 131 'meta':true, 'param':true, 'source':true, 'wbr':t rue}; | |
| 132 var tagName = node.tagName.toLowerCase(); | |
|
yosin_UTC9
2014/09/03 03:35:01
nit: To avoid string object construction, we shoul
tasak
2014/09/03 09:38:00
I found layout test failure if using upper case le
| |
| 133 if (_IE_FORBIDS_TAG[tagName]) | |
| 134 return true; | |
| 135 return false; | |
| 136 } | |
| 137 | |
| 138 XMLSerializerPrototype.serializeAsHTMLDocument = function(node) { | |
|
yosin_UTC9
2014/09/03 03:35:01
I suggest to use
Object.defineProperties(XMLSerial
tasak
2014/09/03 09:38:00
I'm not sure whether we allow users to see interna
yosin_UTC9
2014/09/04 00:58:44
When you use this JS file in standalone environmen
| |
| 139 if (this.serialization_type_ == _SERIALIZATION_TYPE_FORCED_XML) | |
| 140 return false; | |
| 141 return node.ownerDocument && (node.ownerDocument instanceof HTMLDocument ); | |
| 142 } | |
| 143 | |
| 144 XMLSerializerPrototype.serializeAttribute = function(node, attr, namespaces) { | |
| 145 var documentIsHTML = this.serializeAsHTMLDocument(node); | |
| 146 var result = ''; | |
| 147 | |
| 148 var attrPrefix = attr.prefix; | |
| 149 var attrName = attr.localName; | |
| 150 if (documentIsHTML && !attributeIsInSerializedNamespace(attr)) { | |
| 151 result += ' ' + attr.localName; | |
| 152 } else { | |
| 153 var namespaceURI = validateURI(attr.namespaceURI); | |
| 154 if (namespaceURI == _XMLNS_NAMESPACE_URI) { | |
| 155 if (!attr.prefix && attr.localName != 'xmlns') | |
| 156 attrPrefix = 'xmlns'; | |
| 157 if (namespaces) { | |
| 158 var lookupKey = _EMPTY; | |
| 159 if (attr.prefix) | |
| 160 lookupKey = attr.localName; | |
| 161 namespaces[lookupKey] = attr.value; | |
| 162 } | |
| 163 } else if (namespaceURI == _XML_NAMESPACE_URI) { | |
| 164 if (!attr.prefix) | |
| 165 attrPrefix = 'xml'; | |
| 166 } else { | |
| 167 if (namespaceURI == _XLINK_NAMESPACE_URI) { | |
| 168 if (!attr.prefix) | |
| 169 attrPrefix = 'xlink'; | |
| 170 } | |
| 171 if (namespaces && shouldAddNamespaceAttribute(attr, node)) { | |
| 172 if (!attrPrefix) { | |
| 173 for (var i in namespaces) { | |
| 174 if (namespaces[i] == namespaceURI && i) { | |
| 175 attrPrefix = i; | |
| 176 break; | |
| 177 } | |
| 178 } | |
| 179 if (!attrPrefix) | |
| 180 attrPrefix = generatePrefix(namespaces); | |
| 181 } | |
| 182 result += appendNamespace(attrPrefix, namespaceURI, namespac es); | |
| 183 } | |
| 184 } | |
| 185 if (attrPrefix) { | |
| 186 result += ' ' + attrPrefix + ':' + attrName; | |
| 187 } else { | |
| 188 result += ' ' + attrName; | |
| 189 } | |
| 190 } | |
| 191 if (attr.value) | |
| 192 result += '="' + escape(String(attr.value)) + '"'; | |
| 193 return result; | |
| 194 } | |
| 195 | |
| 196 XMLSerializerPrototype.serializeAttributes = function(node, namespaces) { | |
| 197 var attributes = node.attributes; | |
| 198 if (!attributes) | |
| 199 return ''; | |
| 200 | |
| 201 var sink = ''; | |
| 202 var attrs = []; | |
| 203 for (var index = 0; index < attributes.length; ++index) | |
| 204 attrs.push(attributes[index]); | |
| 205 | |
| 206 var serializer = this; | |
| 207 attrs.sort(function(a, b) { | |
| 208 return a.name <= b.name ? -1 : 0; | |
| 209 }).forEach(function(attr) { | |
| 210 if (attr.name.indexOf('_moz') == 0) | |
| 211 return; | |
| 212 sink += serializer.serializeAttribute(node, attr, namespaces); | |
| 213 }); | |
| 214 return sink; | |
| 215 } | |
| 216 | |
| 217 XMLSerializerPrototype.serializeOpenTag = function(node, namespaces) { | |
| 218 var sink = '<' + qualifiedTagName(node); | |
| 219 if (!this.serializeAsHTMLDocument(node) && namespaces && shouldAddNamesp aceElement(node, namespaces)) | |
| 220 sink += appendNamespace(node.prefix, node.namespaceURI, namespaces); | |
| 221 return sink; | |
| 222 } | |
| 223 | |
| 224 XMLSerializerPrototype.serializeCloseTag = function(node, namespaces) { | |
| 225 var sink = ''; | |
| 226 if (this.shouldSelfClose(node)) { | |
| 227 if (isHTMLElement(node)) | |
| 228 sink += ' '; | |
| 229 sink += '/'; | |
| 230 } | |
| 231 sink += '>'; | |
| 232 return sink; | |
| 233 } | |
| 234 | |
| 235 XMLSerializerPrototype.serializeElement = function(node, namespaces) { | |
| 236 var sink = this.serializeOpenTag(node, namespaces); | |
| 237 | |
| 238 sink += this.serializeAttributes(node, namespaces); | |
| 239 sink += this.serializeCloseTag(node, namespaces); | |
| 240 return sink; | |
| 241 } | |
| 242 | |
| 243 XMLSerializerPrototype.serializeText = function(node) { | |
| 244 return escape(node.nodeValue); | |
| 245 } | |
| 246 | |
| 247 XMLSerializerPrototype.serializeComment = function(node) { | |
| 248 return '<!--' + node.nodeValue + '-->'; | |
| 249 } | |
| 250 | |
| 251 XMLSerializerPrototype.serializeDocument = function(node) { | |
| 252 if (!node.hasXMLDeclaration) | |
| 253 return ''; | |
| 254 | |
| 255 var s = '<?xml'; | |
| 256 if (node.xmlVersion) | |
| 257 s += ' version="' + node.xmlVersion + '"'; | |
| 258 if (node.xmlEncoding) | |
| 259 s += ' encoding="' + node.xmlEncoding + '"'; | |
| 260 if (node.isXMLStandaloneSpecified) { | |
| 261 s += ' standalone="' | |
| 262 if (node.xmlStandalone) { | |
| 263 s += 'yes'; | |
| 264 } else { | |
| 265 s += 'no'; | |
| 266 } | |
| 267 s += '"'; | |
| 268 } | |
| 269 s += '?>'; | |
| 270 return s; | |
| 271 } | |
| 272 | |
| 273 XMLSerializerPrototype.serializeDocumentType = function(node) { | |
| 274 var s = '<!DOCTYPE ' + node.name; | |
| 275 if (node.publicId) { | |
| 276 s += ' PUBLIC "' + node.publicId + '"'; | |
| 277 if (node.systemId) | |
| 278 s += ' "' + node.systemId + '"'; | |
| 279 } | |
| 280 s += '>'; | |
| 281 return s; | |
| 282 } | |
| 283 | |
| 284 XMLSerializerPrototype.serializeProcessingInstruction = function(node) { | |
| 285 var s = '<?' + node.target; | |
| 286 if (node.nodeValue) | |
| 287 s += ' ' + node.nodeValue; | |
| 288 s += '?>'; | |
| 289 return s; | |
| 290 } | |
| 291 | |
| 292 XMLSerializerPrototype.serializeCDataSection = function(node) { | |
| 293 return '<![CDATA[' + node.nodeValue + ']]>'; | |
| 294 } | |
| 295 | |
| 296 XMLSerializerPrototype.serializeStartTag = function(node, namespaces) { | |
| 297 if (node.nodeType == Node.TEXT_NODE) { | |
| 298 return this.serializeText(node); | |
| 299 } else if (node.nodeType == Node.COMMENT_NODE) { | |
| 300 return this.serializeComment(node); | |
| 301 } else if (node.nodeType == Node.DOCUMENT_NODE) { | |
| 302 return this.serializeDocument(node); | |
| 303 } else if (node.nodeType == Node.DOCUMENT_TYPE_NODE) { | |
| 304 return this.serializeDocumentType(node); | |
| 305 } else if (node.nodeType == Node.DOCUMENT_FRAGMENT_NODE) { | |
| 306 return ''; | |
| 307 } else if (node.nodeType == Node.PROCESSING_INSTRUCTION_NODE) { | |
| 308 return this.serializeProcessingInstruction(node); | |
| 309 } else if (node.nodeType == Node.ELEMENT_NODE) { | |
| 310 return this.serializeElement(node, namespaces); | |
| 311 } else if (node.nodeType == Node.CDATA_SECTION_NODE) { | |
| 312 return this.serializeCDataSection(node); | |
| 313 } else { | |
| 314 return ''; | |
| 315 } | |
| 316 } | |
| 317 | |
| 318 XMLSerializerPrototype.shouldSelfClose = function(node) { | |
| 319 if (this.serializeAsHTMLDocument(node)) | |
| 320 return false; | |
| 321 if (node.hasChildNodes()) | |
| 322 return false; | |
| 323 if (isTemplateElement(node) && node.content && node.content.hasChildNode s()) | |
| 324 return false; | |
| 325 if (isHTMLElement(node) && !elementCannotHaveEndTag(node)) | |
| 326 return false; | |
| 327 return true; | |
| 328 } | |
| 329 | |
| 330 XMLSerializerPrototype.serializeEndTag = function(node) { | |
| 331 if (this.shouldSelfClose(node) || (!node.hasChildNodes() && elementCanno tHaveEndTag(node))) | |
| 332 return ''; | |
| 333 return '</' + qualifiedTagName(node) + '>'; | |
| 334 } | |
| 335 | |
| 336 XMLSerializerPrototype.serializeNodesWithNamespaces = function(node, namespa ces) { | |
| 337 var sink = ''; | |
| 338 var localNamespaces = cloneNamespace(namespaces); | |
| 339 sink += this.serializeStartTag(node, localNamespaces); | |
| 340 | |
| 341 if (!(this.serializeAsHTMLDocument(node) && elementCannotHaveEndTag(node ))) { | |
| 342 var current = node; | |
| 343 if (isTemplateElement(node)) | |
| 344 current = node.content; | |
| 345 current = current.firstChild; | |
| 346 while (current) { | |
| 347 sink += this.serializeNodesWithNamespaces(current, localNamespac es); | |
| 348 current = current.nextSibling; | |
| 349 } | |
| 350 } | |
| 351 if (node.nodeType == Node.ELEMENT_NODE) | |
| 352 sink += this.serializeEndTag(node); | |
| 353 return sink; | |
| 354 } | |
| 355 | |
| 356 XMLSerializerPrototype.serializeNodes = function(node) { | |
| 357 var namespaces = {}; | |
| 358 if (!this.serializeAsHTMLDocument(node)) | |
| 359 namespaces['xml'] = _XML_NAMESPACE_URI; | |
| 360 | |
| 361 return this.serializeNodesWithNamespaces(node, namespaces); | |
| 362 } | |
| 363 | |
| 364 XMLSerializerPrototype.serializeToString = function(root) { | |
| 365 if (!root) | |
| 366 throw new TypeError("Invalid node value."); | |
| 367 | |
| 368 this.serialization_type_ = _SERIALIZATION_TYPE_FORCED_XML; | |
| 369 return this.serializeNodes(root); | |
| 370 } | |
| 371 }); | |
| OLD | NEW |