Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(77)

Side by Side Diff: Source/core/xml/XMLSerializer.js

Issue 534583002: Implemented XMLSerializer in PrivateScript. (Closed) Base URL: svn://svn.chromium.org/blink/trunk
Patch Set: Created 6 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(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';
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 escapePCDATA(text) {
20 return text.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, ' &gt;');
21 }
22
23 function escapeHTMLPCDATA(text) {
24 return escapePCDATA(text).replace(/\u00A0/g, '&nbsp;');
25 }
26
27 function escapeAttributeValue(text) {
28 return text.replace(/&/g, '&amp;').replace(/"/g, '&quot;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
29 }
30
31 function escapeHTMLAttributeValue(text) {
32 return text.replace(/&/g, '&amp;').replace(/"/g, '&quot;').replace(/\u00 A0/g, '&nbsp;');
33 }
34
35 function validateURI(uri) {
36 return uri ? uri.replace(/\/$/g, '') : '';
37 }
38
39 function cloneNamespace(obj) {
40 var clone = {};
41 for (var i in obj)
42 clone[i] = obj[i];
43 return clone;
44 }
45
46 function isTemplateElement(node) {
47 return isHTMLElement(node) && node.tagName.toLowerCase() == 'template';
48 }
49
50 function generatePrefix(namespaces) {
51 var nsRegex = new RegExp('^ns([0-9]+)$', 'g');
52 var maxValue = 0;
53 for (var key in namespaces) {
54 var match = nsRegex.exec(key);
55 if (match) {
56 var value = parseInt(match[1]);
57 if (maxValue < value)
58 maxValue = value;
59 }
60 }
61 return 'ns' + (maxValue + 1);
62 }
63
64 function qualifiedTagName(node) {
65 if (isHTMLElement(node))
66 return node.tagName.toLowerCase();
67 return node.tagName;
68 }
69
70 function attributeIsInSerializedNamespace(attr) {
71 var _SERIALIZED_NAMESPACES = {
72 _XML_NAMESPACE_URI:true, _XLINK_NAMESPACE_URI:true,
73 _XMLNS_NAMESPACE_URI:true
74 };
75 return _SERIALIZED_NAMESPACES[validateURI(attr.namespaceURI)];
76 }
77
78 function appendNamespace(prefix, namespaceURI, namespaces) {
79 if (!namespaceURI)
80 return '';
81
82 var result = '';
83 var lookupKey = _EMPTY;
84 if (prefix)
85 lookupKey = prefix;
86 if (namespaces[lookupKey] != namespaceURI) {
87 namespaces[lookupKey] = namespaceURI;
88 result += ' xmlns';
89 if (prefix)
90 result += ':' + prefix;
91 result += '="' + namespaceURI + '"';
92 }
93 return result;
94 }
95
96 function shouldAddNamespaceAttribute(attr, node) {
97 if (!attr.namespaceURI)
98 return false;
99 if (!attr.prefix)
100 return true;
101 if (!node.hasAttribute('xmlns:' + attr.prefix))
102 return true;
103 return false;
104 }
105
106 function shouldAddNamespaceElement(node, namespaces) {
107 var prefix = node.prefix;
108 if (!node.prefix) {
109 if (node.hasAttribute('xmlns')) {
110 namespaces[_EMPTY] = node.namespaceURI;
111 return false;
112 }
113 return true;
114 }
115 if (!node.hasAttribute('xmlns:' + node.prefix))
116 return true;
117 return false;
118 }
119
120 function isHTMLElement(node) {
121 if (node.nodeType != Node.ELEMENT_NODE)
122 return false;
123
124 for (var runner = node; runner; runner = runner.parentNode) {
125 if (runner.nodeType == Node.ELEMENT_NODE && runner.namespaceURI != n ull)
126 return validateURI(runner.namespaceURI) == _HTML_NAMESPACE_URI;
127 }
128 return false;
129 }
130
131 function isEndTagOmissible(node) {
132 if (node.nodeType != Node.ELEMENT_NODE)
133 return false;
134 // This tables comes from HTMLElement::ieForbidsInsertHTML().
135 var _END_TAG_OMISSIBLE_TAGS = {
136 'area':true, 'base':true, 'basefont':true, 'br':true, 'col':true, 'e mbed':true,
137 'frame':true, 'hr':true, 'image':true, 'img':true, 'input':true, 'li nk':true,
138 'meta':true, 'param':true, 'source':true, 'wbr':true
139 };
140 return _END_TAG_OMISSIBLE_TAGS[node.tagName.toLowerCase()];
141 }
142
143 XMLSerializerPrototype.serializeAsHTMLDocument_ = function(node) {
144 if (this.serializationType_ == _SERIALIZATION_TYPE_FORCED_XML)
145 return false;
146 return node.ownerDocument && (node.ownerDocument instanceof HTMLDocument );
147 }
148
149 XMLSerializerPrototype.serializeAttribute_ = function(node, attr, namespaces ) {
150 var documentIsHTML = this.serializeAsHTMLDocument_(node);
151 var result = '';
152
153 var attrPrefix = attr.prefix;
154 var attrName = attr.localName;
155 if (documentIsHTML && !attributeIsInSerializedNamespace(attr)) {
156 result += ' ' + attr.localName;
157 } else {
158 var namespaceURI = validateURI(attr.namespaceURI);
159 if (namespaceURI == _XMLNS_NAMESPACE_URI) {
160 if (!attr.prefix && attr.localName != 'xmlns')
161 attrPrefix = 'xmlns';
162 if (namespaces) {
163 var lookupKey = _EMPTY;
164 if (attr.prefix)
165 lookupKey = attr.localName;
166 namespaces[lookupKey] = attr.value;
167 }
168 } else if (namespaceURI == _XML_NAMESPACE_URI) {
169 if (!attr.prefix)
170 attrPrefix = 'xml';
171 } else {
172 if (namespaceURI == _XLINK_NAMESPACE_URI) {
173 if (!attr.prefix)
174 attrPrefix = 'xlink';
175 }
176 if (namespaces && shouldAddNamespaceAttribute(attr, node)) {
177 if (!attrPrefix) {
178 for (var i in namespaces) {
179 if (namespaces[i] == namespaceURI && i) {
180 attrPrefix = i;
181 break;
182 }
183 }
184 if (!attrPrefix)
185 attrPrefix = generatePrefix(namespaces);
186 }
187 result += appendNamespace(attrPrefix, namespaceURI, namespac es);
188 }
189 }
190 if (attrPrefix) {
191 result += ' ' + attrPrefix + ':' + attrName;
192 } else {
193 result += ' ' + attrName;
194 }
195 }
196 if (attr.value) {
197 var attrValue;
198 if (documentIsHTML) {
199 attrValue = escapeHTMLAttributeValue(String(attr.value));
200 } else {
201 attrValue = escapeAttributeValue(String(attr.value));
202 }
203 result += '="' + attrValue + '"';
204 }
205 return result;
206 }
207
208 XMLSerializerPrototype.serializeAttributes_ = function(node, namespaces) {
209 if (!node.attributes)
210 return '';
211
212 var sink = '';
213 for (var i = 0; i < node.attributes.length; ++i) {
214 var attr = node.attributes.item(i);
215 if (attr.name.indexOf('_moz') == 0)
216 continue;
217 sink += this.serializeAttribute_(node, attr, namespaces);
218 }
219 return sink;
220 }
221
222 XMLSerializerPrototype.serializeOpenTag_ = function(node, namespaces) {
223 var sink = '<' + qualifiedTagName(node);
224 if (!this.serializeAsHTMLDocument_(node) && namespaces && shouldAddNames paceElement(node, namespaces))
225 sink += appendNamespace(node.prefix, node.namespaceURI, namespaces);
226 return sink;
227 }
228
229 XMLSerializerPrototype.serializeCloseTag_ = function(node, namespaces) {
230 var sink = '';
231 if (this.shouldSelfClose_(node)) {
232 if (isHTMLElement(node))
233 sink += ' ';
234 sink += '/';
235 }
236 sink += '>';
237 return sink;
238 }
239
240 XMLSerializerPrototype.serializeElement_ = function(node, namespaces) {
241 var sink = this.serializeOpenTag_(node, namespaces);
242
243 sink += this.serializeAttributes_(node, namespaces);
244 sink += this.serializeCloseTag_(node, namespaces);
245 return sink;
246 }
247
248 XMLSerializerPrototype.serializeText_ = function(node) {
249 if (!node.nodeValue)
250 return '';
251
252 if (this.serializeAsHTMLDocument_(node)) {
253 if (node.parentNode && isHTMLElement(node.parentNode)) {
254 var tagName = node.parentNode.tagName.toLowerCase();
255 if (tagName == 'script' || tagName == 'style' || tagName == 'xmp ')
256 return node.nodeValue;
257 }
258 return escapeHTMLPCDATA(node.nodeValue);
259 }
260 return escapePCDATA(node.nodeValue);
261 }
262
263 XMLSerializerPrototype.serializeComment_ = function(node) {
264 return '<!--' + node.nodeValue + '-->';
265 }
266
267 XMLSerializerPrototype.serializeDocument_ = function(node) {
268 return this.documentXMLDeclaration_;
269 }
270
271 XMLSerializerPrototype.serializeDocumentType_ = function(node) {
272 var s = '<!DOCTYPE ' + node.name;
273 if (node.publicId) {
274 s += ' PUBLIC "' + node.publicId + '"';
275 if (node.systemId)
276 s += ' "' + node.systemId + '"';
277 } else if (node.systemId) {
278 s += ' SYSTEM "' + 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) {
yosin_UTC9 2014/09/04 01:10:42 nit: We don't need to have |else| after |return|.
tasak 2014/09/04 11:17:35 Done.
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) && !isEndTagOmissible(node))
326 return false;
327 return true;
328 }
329
330 XMLSerializerPrototype.serializeEndTag_ = function(node) {
331 if (this.shouldSelfClose_(node) || (!node.hasChildNodes() && isEndTagOmi ssible(node)))
332 return '';
333 return '</' + qualifiedTagName(node) + '>';
334 }
335
336 XMLSerializerPrototype.serializeNodesWithNamespaces_ = function(node, namesp aces) {
337 var sink = '';
338 var localNamespaces = cloneNamespace(namespaces);
339 sink += this.serializeStartTag(node, localNamespaces);
340
341 if (!(this.serializeAsHTMLDocument_(node) && isEndTagOmissible(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, localNamespa ces);
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 namespaces['xmlns'] = _XMLNS_NAMESPACE_URI;
361 }
362
363 return this.serializeNodesWithNamespaces_(node, namespaces);
364 }
365
366 XMLSerializerPrototype.serializeToStringInternal = function(root, xmlDeclara tion) {
367 this.serializationType_ = _SERIALIZATION_TYPE_FORCED_XML;
368 this.documentXMLDeclaration_ = xmlDeclaration;
369 return this.serializeNodes_(root);
370 }
371 });
OLDNEW
« Source/core/xml/XMLSerializer.idl ('K') | « Source/core/xml/XMLSerializer.idl ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698