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

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 isHTMLDocument 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";
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, '&amp;').replace(/\u0022/g, '&quot;').replace( /</g, '&lt;').replace(/>/g, '&gt;');
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 });
OLDNEW
« Source/core/xml/DocumentXMLSerializer.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