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

Unified 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 side-by-side diff with in-line comments
Download patch
Index: Source/core/xml/XMLSerializer.js
diff --git a/Source/core/xml/XMLSerializer.js b/Source/core/xml/XMLSerializer.js
new file mode 100644
index 0000000000000000000000000000000000000000..d70cf16472eeeff19cfda86a8ff4e85db3eba10f
--- /dev/null
+++ b/Source/core/xml/XMLSerializer.js
@@ -0,0 +1,371 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+"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.
+
+installClass("XMLSerializer", function(XMLSerializerPrototype) {
+
+ var _EMPTY = '';
+
+ var _XML_NAMESPACE_URI = 'http://www.w3.org/XML/1998/namespace';
+ var _XLINK_NAMESPACE_URI = 'http://www.w3.org/1999/xlink';
+ var _XMLNS_NAMESPACE_URI = 'http://www.w3.org/2000/xmlns';
+ var _HTML_NAMESPACE_URI = 'http://www.w3.org/1999/xhtml';
+
+ var _SERIALIZATION_TYPE_AS_OWNER_DOCUMENT = 0;
+ var _SERIALIZATION_TYPE_FORCED_XML = 1;
+
+ function escape(text) {
+ return text.replace(/&/g, '&amp;').replace(/\u0022/g, '&quot;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
+ }
+
+ function validateURI(uri) {
+ 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
+ return uri;
+ return uri.replace(/\/$/g, '');
+ }
+
+ 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
+ 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.
+ for (var i in obj)
+ clone[i] = obj[i];
+ return clone;
+ }
+
+ function isTemplateElement(node) {
+ 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
+ return true;
+ return false;
+ }
+
+ function generatePrefix(namespaces) {
+ var _MAX_RETRY = 10;
+ for (var count = 0; count < _MAX_RETRY; ++count) {
+ 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.
+ if (!namespaces[candidate])
+ return candidate;
+ }
+ return null;
+ }
+
+ function qualifiedTagName(node) {
+ 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
+ if (tagName != node.tagName && node.tagName.toUpperCase() != node.tagName)
+ tagName = node.tagName;
+ return tagName;
+ }
+
+ function attributeIsInSerializedNamespace(attr) {
+ var namespaceURI = validateURI(attr.namespaceURI);
+ 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.
+ namespaceURI == _XLINK_NAMESPACE_URI ||
+ namespaceURI == _XMLNS_NAMESPACE_URI)
+ return true;
+ return false;
+ }
+
+ function appendNamespace(prefix, namespaceURI, namespaces) {
+ if (!namespaceURI)
+ return '';
+
+ var result = '';
+ var lookupKey = _EMPTY;
+ if (prefix)
+ lookupKey = prefix;
+ if (namespaces[lookupKey] != namespaceURI) {
+ namespaces[lookupKey] = namespaceURI;
+ result += ' xmlns';
+ if (prefix) {
yosin_UTC9 2014/09/03 03:35:01 nit: No need to have "{}"
tasak 2014/09/03 09:38:00 Done.
+ result += ':' + prefix;
+ }
+ result += '=\"' + namespaceURI + '"';
+ }
+ return result;
+ }
+
+ function shouldAddNamespaceAttribute(attr, node) {
+ if (!attr.namespaceURI)
+ return false;
+ if (!attr.prefix)
+ return true;
+ if (!node.hasAttribute('xmlns:' + attr.prefix))
+ return true;
+ return false;
+ }
+
+ function shouldAddNamespaceElement(node, namespaces) {
+ var prefix = node.prefix;
+ if (!node.prefix) {
+ if (node.hasAttribute('xmlns')) {
+ namespaces[_EMPTY] = node.namespaceURI;
+ return false;
+ }
+ return true;
+ }
+ if (!node.hasAttribute('xmlns:' + node.prefix))
+ return true;
+ return false;
+ }
+
+ function isHTMLElement(node) {
+ if (node.nodeType != Node.ELEMENT_NODE)
+ return false;
+
+ 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.
+ if (node.nodeType == Node.ELEMENT_NODE) {
+ var namespaceURI = validateURI(node.namespaceURI);
+ if (namespaceURI)
+ return namespaceURI == _HTML_NAMESPACE_URI;
+ }
+ node = node.parentNode;
+ }
+ return false;
+ }
+
+ 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.
+ if (node.nodeType != Node.ELEMENT_NODE)
+ return false;
+ var _IE_FORBIDS_TAG = {'area':true, 'base':true, 'basefont':true, 'br':true, 'col':true, 'embed':true,
+ 'frame':true, 'hr':true, 'image':true, 'img':true, 'input':true, 'link':true,
+ 'meta':true, 'param':true, 'source':true, 'wbr':true};
+ 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
+ if (_IE_FORBIDS_TAG[tagName])
+ return true;
+ return false;
+ }
+
+ 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
+ if (this.serialization_type_ == _SERIALIZATION_TYPE_FORCED_XML)
+ return false;
+ return node.ownerDocument && (node.ownerDocument instanceof HTMLDocument);
+ }
+
+ XMLSerializerPrototype.serializeAttribute = function(node, attr, namespaces) {
+ var documentIsHTML = this.serializeAsHTMLDocument(node);
+ var result = '';
+
+ var attrPrefix = attr.prefix;
+ var attrName = attr.localName;
+ if (documentIsHTML && !attributeIsInSerializedNamespace(attr)) {
+ result += ' ' + attr.localName;
+ } else {
+ var namespaceURI = validateURI(attr.namespaceURI);
+ if (namespaceURI == _XMLNS_NAMESPACE_URI) {
+ if (!attr.prefix && attr.localName != 'xmlns')
+ attrPrefix = 'xmlns';
+ if (namespaces) {
+ var lookupKey = _EMPTY;
+ if (attr.prefix)
+ lookupKey = attr.localName;
+ namespaces[lookupKey] = attr.value;
+ }
+ } else if (namespaceURI == _XML_NAMESPACE_URI) {
+ if (!attr.prefix)
+ attrPrefix = 'xml';
+ } else {
+ if (namespaceURI == _XLINK_NAMESPACE_URI) {
+ if (!attr.prefix)
+ attrPrefix = 'xlink';
+ }
+ if (namespaces && shouldAddNamespaceAttribute(attr, node)) {
+ if (!attrPrefix) {
+ for (var i in namespaces) {
+ if (namespaces[i] == namespaceURI && i) {
+ attrPrefix = i;
+ break;
+ }
+ }
+ if (!attrPrefix)
+ attrPrefix = generatePrefix(namespaces);
+ }
+ result += appendNamespace(attrPrefix, namespaceURI, namespaces);
+ }
+ }
+ if (attrPrefix) {
+ result += ' ' + attrPrefix + ':' + attrName;
+ } else {
+ result += ' ' + attrName;
+ }
+ }
+ if (attr.value)
+ result += '="' + escape(String(attr.value)) + '"';
+ return result;
+ }
+
+ XMLSerializerPrototype.serializeAttributes = function(node, namespaces) {
+ var attributes = node.attributes;
+ if (!attributes)
+ return '';
+
+ var sink = '';
+ var attrs = [];
+ for (var index = 0; index < attributes.length; ++index)
+ attrs.push(attributes[index]);
+
+ var serializer = this;
+ attrs.sort(function(a, b) {
+ return a.name <= b.name ? -1 : 0;
+ }).forEach(function(attr) {
+ if (attr.name.indexOf('_moz') == 0)
+ return;
+ sink += serializer.serializeAttribute(node, attr, namespaces);
+ });
+ return sink;
+ }
+
+ XMLSerializerPrototype.serializeOpenTag = function(node, namespaces) {
+ var sink = '<' + qualifiedTagName(node);
+ if (!this.serializeAsHTMLDocument(node) && namespaces && shouldAddNamespaceElement(node, namespaces))
+ sink += appendNamespace(node.prefix, node.namespaceURI, namespaces);
+ return sink;
+ }
+
+ XMLSerializerPrototype.serializeCloseTag = function(node, namespaces) {
+ var sink = '';
+ if (this.shouldSelfClose(node)) {
+ if (isHTMLElement(node))
+ sink += ' ';
+ sink += '/';
+ }
+ sink += '>';
+ return sink;
+ }
+
+ XMLSerializerPrototype.serializeElement = function(node, namespaces) {
+ var sink = this.serializeOpenTag(node, namespaces);
+
+ sink += this.serializeAttributes(node, namespaces);
+ sink += this.serializeCloseTag(node, namespaces);
+ return sink;
+ }
+
+ XMLSerializerPrototype.serializeText = function(node) {
+ return escape(node.nodeValue);
+ }
+
+ XMLSerializerPrototype.serializeComment = function(node) {
+ return '<!--' + node.nodeValue + '-->';
+ }
+
+ XMLSerializerPrototype.serializeDocument = function(node) {
+ if (!node.hasXMLDeclaration)
+ return '';
+
+ var s = '<?xml';
+ if (node.xmlVersion)
+ s += ' version="' + node.xmlVersion + '"';
+ if (node.xmlEncoding)
+ s += ' encoding="' + node.xmlEncoding + '"';
+ if (node.isXMLStandaloneSpecified) {
+ s += ' standalone="'
+ if (node.xmlStandalone) {
+ s += 'yes';
+ } else {
+ s += 'no';
+ }
+ s += '"';
+ }
+ s += '?>';
+ return s;
+ }
+
+ XMLSerializerPrototype.serializeDocumentType = function(node) {
+ var s = '<!DOCTYPE ' + node.name;
+ if (node.publicId) {
+ s += ' PUBLIC "' + node.publicId + '"';
+ if (node.systemId)
+ s += ' "' + node.systemId + '"';
+ }
+ s += '>';
+ return s;
+ }
+
+ XMLSerializerPrototype.serializeProcessingInstruction = function(node) {
+ var s = '<?' + node.target;
+ if (node.nodeValue)
+ s += ' ' + node.nodeValue;
+ s += '?>';
+ return s;
+ }
+
+ XMLSerializerPrototype.serializeCDataSection = function(node) {
+ return '<![CDATA[' + node.nodeValue + ']]>';
+ }
+
+ XMLSerializerPrototype.serializeStartTag = function(node, namespaces) {
+ if (node.nodeType == Node.TEXT_NODE) {
+ return this.serializeText(node);
+ } else if (node.nodeType == Node.COMMENT_NODE) {
+ return this.serializeComment(node);
+ } else if (node.nodeType == Node.DOCUMENT_NODE) {
+ return this.serializeDocument(node);
+ } else if (node.nodeType == Node.DOCUMENT_TYPE_NODE) {
+ return this.serializeDocumentType(node);
+ } else if (node.nodeType == Node.DOCUMENT_FRAGMENT_NODE) {
+ return '';
+ } else if (node.nodeType == Node.PROCESSING_INSTRUCTION_NODE) {
+ return this.serializeProcessingInstruction(node);
+ } else if (node.nodeType == Node.ELEMENT_NODE) {
+ return this.serializeElement(node, namespaces);
+ } else if (node.nodeType == Node.CDATA_SECTION_NODE) {
+ return this.serializeCDataSection(node);
+ } else {
+ return '';
+ }
+ }
+
+ XMLSerializerPrototype.shouldSelfClose = function(node) {
+ if (this.serializeAsHTMLDocument(node))
+ return false;
+ if (node.hasChildNodes())
+ return false;
+ if (isTemplateElement(node) && node.content && node.content.hasChildNodes())
+ return false;
+ if (isHTMLElement(node) && !elementCannotHaveEndTag(node))
+ return false;
+ return true;
+ }
+
+ XMLSerializerPrototype.serializeEndTag = function(node) {
+ if (this.shouldSelfClose(node) || (!node.hasChildNodes() && elementCannotHaveEndTag(node)))
+ return '';
+ return '</' + qualifiedTagName(node) + '>';
+ }
+
+ XMLSerializerPrototype.serializeNodesWithNamespaces = function(node, namespaces) {
+ var sink = '';
+ var localNamespaces = cloneNamespace(namespaces);
+ sink += this.serializeStartTag(node, localNamespaces);
+
+ if (!(this.serializeAsHTMLDocument(node) && elementCannotHaveEndTag(node))) {
+ var current = node;
+ if (isTemplateElement(node))
+ current = node.content;
+ current = current.firstChild;
+ while (current) {
+ sink += this.serializeNodesWithNamespaces(current, localNamespaces);
+ current = current.nextSibling;
+ }
+ }
+ if (node.nodeType == Node.ELEMENT_NODE)
+ sink += this.serializeEndTag(node);
+ return sink;
+ }
+
+ XMLSerializerPrototype.serializeNodes = function(node) {
+ var namespaces = {};
+ if (!this.serializeAsHTMLDocument(node))
+ namespaces['xml'] = _XML_NAMESPACE_URI;
+
+ return this.serializeNodesWithNamespaces(node, namespaces);
+ }
+
+ XMLSerializerPrototype.serializeToString = function(root) {
+ if (!root)
+ throw new TypeError("Invalid node value.");
+
+ this.serialization_type_ = _SERIALIZATION_TYPE_FORCED_XML;
+ return this.serializeNodes(root);
+ }
+});
« 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