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 |