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

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: Removed backdoors added to Document. 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(/\u0022/g, '&quot;').replace( /</g, '&lt;').replace(/>/g, '&gt;');
29 }
Jens Widell 2014/09/03 10:29:33 I think \u0022 can be written as ". What's the rea
tasak 2014/09/03 11:40:47 Done.
30
31 function escapeHTMLAttributeValue(text) {
32 return text.replace(/&/g, '&amp;').replace(/\u0022/g, '&quot;').replace( /\u00A0/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 + '"';
Jens Widell 2014/09/03 10:29:33 No need to escape " in either string here.
tasak 2014/09/03 11:40:46 Done.
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 var attributes = node.attributes;
210 if (!attributes)
211 return '';
212
213 var sink = '';
214 var attrs = [];
215 for (var index = 0; index < attributes.length; ++index)
216 attrs.push(attributes[index]);
217
218 var serializer = this;
219 attrs.sort(function(a, b) {
220 return a.name <= b.name ? -1 : 0;
Jens Widell 2014/09/03 10:29:33 This is an unusual comparison function: If a < b,
tasak 2014/09/03 11:40:46 I see. I found that we don't need to sort attribut
221 }).forEach(function(attr) {
222 if (attr.name.indexOf('_moz') == 0)
223 return;
224 sink += serializer.serializeAttribute_(node, attr, namespaces);
225 });
226 return sink;
227 }
228
229 XMLSerializerPrototype.serializeOpenTag_ = function(node, namespaces) {
230 var sink = '<' + qualifiedTagName(node);
231 if (!this.serializeAsHTMLDocument_(node) && namespaces && shouldAddNames paceElement(node, namespaces))
232 sink += appendNamespace(node.prefix, node.namespaceURI, namespaces);
233 return sink;
234 }
235
236 XMLSerializerPrototype.serializeCloseTag_ = function(node, namespaces) {
237 var sink = '';
238 if (this.shouldSelfClose_(node)) {
239 if (isHTMLElement(node))
240 sink += ' ';
241 sink += '/';
242 }
243 sink += '>';
244 return sink;
245 }
246
247 XMLSerializerPrototype.serializeElement_ = function(node, namespaces) {
248 var sink = this.serializeOpenTag_(node, namespaces);
249
250 sink += this.serializeAttributes_(node, namespaces);
251 sink += this.serializeCloseTag_(node, namespaces);
252 return sink;
253 }
254
255 XMLSerializerPrototype.serializeText_ = function(node) {
256 if (!node.nodeValue)
257 return '';
258
259 if (this.serializeAsHTMLDocument_(node)) {
260 if (node.parentNode && isHTMLElement(node.parentNode)) {
261 var tagName = node.parentNode.tagName.toLowerCase();
262 if (tagName == 'script' || tagName == 'style' || tagName == 'xmp ')
263 return node.nodeValue;
264 }
265 return escapeHTMLPCDATA(node.nodeValue);
266 }
267 return escapePCDATA(node.nodeValue);
268 }
269
270 XMLSerializerPrototype.serializeComment_ = function(node) {
271 return '<!--' + node.nodeValue + '-->';
272 }
273
274 XMLSerializerPrototype.serializeDocument_ = function(node) {
275 return this.documentXMLDeclaration_;
276 }
277
278 XMLSerializerPrototype.serializeDocumentType_ = function(node) {
279 var s = '<!DOCTYPE ' + node.name;
280 if (node.publicId) {
281 s += ' PUBLIC "' + node.publicId + '"';
282 if (node.systemId)
Jens Widell 2014/09/03 10:29:33 There can also be just a system id, in which case
tasak 2014/09/03 11:40:47 Done.
283 s += ' "' + node.systemId + '"';
284 }
285 s += '>';
286 return s;
287 }
288
289 XMLSerializerPrototype.serializeProcessingInstruction_ = function(node) {
290 var s = '<?' + node.target;
291 if (node.nodeValue)
292 s += ' ' + node.nodeValue;
293 s += '?>';
294 return s;
295 }
296
297 XMLSerializerPrototype.serializeCDataSection_ = function(node) {
298 return '<![CDATA[' + node.nodeValue + ']]>';
299 }
300
301 XMLSerializerPrototype.serializeStartTag = function(node, namespaces) {
302 if (node.nodeType == Node.TEXT_NODE) {
303 return this.serializeText_(node);
304 } else if (node.nodeType == Node.COMMENT_NODE) {
305 return this.serializeComment_(node);
306 } else if (node.nodeType == Node.DOCUMENT_NODE) {
307 return this.serializeDocument_(node);
308 } else if (node.nodeType == Node.DOCUMENT_TYPE_NODE) {
309 return this.serializeDocumentType_(node);
310 } else if (node.nodeType == Node.DOCUMENT_FRAGMENT_NODE) {
311 return '';
312 } else if (node.nodeType == Node.PROCESSING_INSTRUCTION_NODE) {
313 return this.serializeProcessingInstruction_(node);
314 } else if (node.nodeType == Node.ELEMENT_NODE) {
315 return this.serializeElement_(node, namespaces);
316 } else if (node.nodeType == Node.CDATA_SECTION_NODE) {
317 return this.serializeCDataSection_(node);
318 } else {
319 return '';
320 }
321 }
322
323 XMLSerializerPrototype.shouldSelfClose_ = function(node) {
324 if (this.serializeAsHTMLDocument_(node))
325 return false;
326 if (node.hasChildNodes())
327 return false;
328 if (isTemplateElement(node) && node.content && node.content.hasChildNode s())
329 return false;
330 if (isHTMLElement(node) && !isEndTagOmissible(node))
331 return false;
332 return true;
333 }
334
335 XMLSerializerPrototype.serializeEndTag_ = function(node) {
336 if (this.shouldSelfClose_(node) || (!node.hasChildNodes() && isEndTagOmi ssible(node)))
337 return '';
338 return '</' + qualifiedTagName(node) + '>';
339 }
340
341 XMLSerializerPrototype.serializeNodesWithNamespaces_ = function(node, namesp aces) {
342 var sink = '';
343 var localNamespaces = cloneNamespace(namespaces);
344 sink += this.serializeStartTag(node, localNamespaces);
345
346 if (!(this.serializeAsHTMLDocument_(node) && isEndTagOmissible(node))) {
347 var current = node;
348 if (isTemplateElement(node))
349 current = node.content;
350 current = current.firstChild;
351 while (current) {
352 sink += this.serializeNodesWithNamespaces_(current, localNamespa ces);
353 current = current.nextSibling;
354 }
355 }
356 if (node.nodeType == Node.ELEMENT_NODE)
357 sink += this.serializeEndTag_(node);
358 return sink;
359 }
360
361 XMLSerializerPrototype.serializeNodes_ = function(node) {
362 var namespaces = {};
363 if (!this.serializeAsHTMLDocument_(node))
364 namespaces['xml'] = _XML_NAMESPACE_URI;
Jens Widell 2014/09/03 10:29:34 Could add 'xmlns' as well, I guess. It too is impl
tasak 2014/09/03 11:40:47 Done.
365
366 return this.serializeNodesWithNamespaces_(node, namespaces);
367 }
368
369 XMLSerializerPrototype.serializeToStringInternal = function(root, xmlDeclara tion) {
370 this.serializationType_ = _SERIALIZATION_TYPE_FORCED_XML;
371 this.documentXMLDeclaration_ = xmlDeclaration ? xmlDeclaration : '';
372 return this.serializeNodes_(root);
373 }
374 });
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