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

Unified 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, 2 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
« no previous file with comments | « Source/core/xml/XMLSerializer.idl ('k') | public/blink_resources.grd » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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..4fdef031f3592338835e9e92548d843b9fcac4b3
--- /dev/null
+++ b/Source/core/xml/XMLSerializer.js
@@ -0,0 +1,373 @@
+// 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';
+
+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 escapePCDATA(text) {
+ return text.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
+ }
+
+ function escapeHTMLPCDATA(text) {
+ return escapePCDATA(text).replace(/\u00A0/g, '&nbsp;');
+ }
+
+ function escapeAttributeValue(text) {
+ return text.replace(/&/g, '&amp;').replace(/"/g, '&quot;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
+ }
+
+ function escapeHTMLAttributeValue(text) {
+ text.replace(/&|"|\u00A0/g, function(c) {
+ switch (c) {
+ case '&': return '&amp;';
+ case '"': return '&quot;';
+ case '\u00A0': return '&nbsp;';
+ }
+ });
+ }
+
+ function validateURI(uri) {
+ return uri ? uri.replace(/\/$/g, '') : '';
+ }
+
+ function cloneNamespace(obj) {
+ var clone = Object.create(null);
+ for (var i in obj)
+ clone[i] = obj[i];
+ return clone;
+ }
+
+ function isTemplateElement(node) {
+ return isHTMLElement(node) && node.localName == 'template';
+ }
+
+ function generatePrefix(namespaces) {
+ var nsRegex = /^ns([0-9]+)$/g;
+ var maxValue = 0;
+ for (var key in namespaces) {
+ var match = nsRegex.exec(key);
+ if (match) {
+ var value = parseInt(match[1], 10);
+ if (maxValue < value)
+ maxValue = value;
+ }
+ }
+ return 'ns' + (maxValue + 1);
+ }
+
+ function qualifiedTagName(node) {
+ return isHTMLElement(node) ? node.localName : node.tagName;
+ }
+
+ var SERIALIZED_NAMESPACES = {
+ XML_NAMESPACE_URI: true,
+ XLINK_NAMESPACE_URI: true,
+ XMLNS_NAMESPACE_URI: true
+ };
+
+ function attributeIsInSerializedNamespace(attr) {
+ return SERIALIZED_NAMESPACES[validateURI(attr.namespaceURI)];
+ }
+
+ 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)
+ 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;
+
+ for (var runner = node; runner; runner = runner.parentNode) {
+ if (runner.nodeType == Node.ELEMENT_NODE && runner.namespaceURI != null)
+ return validateURI(runner.namespaceURI) == HTML_NAMESPACE_URI;
+ }
+ return false;
+ }
+
+ // This tables comes from HTMLElement::ieForbidsInsertHTML().
+ var END_TAG_OMISSIBLE_TAGS = {
+ '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
+ };
+
+ function isEndTagOmissible(node) {
+ if (node.nodeType != Node.ELEMENT_NODE)
+ return false;
+ return END_TAG_OMISSIBLE_TAGS[node.localName];
+ }
+
+ XMLSerializerPrototype.serializeAsHTMLDocument_ = function(node) {
+ if (this.serializationType_ == 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) {
+ var attrValue;
+ if (documentIsHTML) {
+ attrValue = escapeHTMLAttributeValue(String(attr.value));
+ } else {
+ attrValue = escapeAttributeValue(String(attr.value));
+ }
+ result += '="' + attrValue + '"';
+ }
+ return result;
+ };
+
+ XMLSerializerPrototype.serializeAttributes_ = function(node, namespaces) {
+ if (!node.attributes)
+ return '';
+
+ var sink = '';
+ for (var i = 0; i < node.attributes.length; ++i)
+ sink += this.serializeAttribute_(node, node.attributes[i], 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) {
+ return this.serializeOpenTag_(node, namespaces) +
+ this.serializeAttributes_(node, namespaces) +
+ this.serializeCloseTag_(node, namespaces);
+ };
+
+ XMLSerializerPrototype.serializeText_ = function(node) {
+ if (!node.nodeValue)
+ return '';
+
+ if (this.serializeAsHTMLDocument_(node)) {
+ if (node.parentNode && isHTMLElement(node.parentNode)) {
+ var tagName = node.parentNode.localName;
+ if (tagName == 'script' || tagName == 'style' || tagName == 'xmp')
+ return node.nodeValue;
+ }
+ return escapeHTMLPCDATA(node.nodeValue);
+ }
+ return escapePCDATA(node.nodeValue);
+ };
+
+ XMLSerializerPrototype.serializeComment_ = function(node) {
+ return '<!--' + node.nodeValue + '-->';
+ };
+
+ XMLSerializerPrototype.serializeDocument_ = function(node) {
+ return this.documentXMLDeclaration_;
+ };
+
+ XMLSerializerPrototype.serializeDocumentType_ = function(node) {
+ var s = '<!DOCTYPE ' + node.name;
+ if (node.publicId) {
+ s += ' PUBLIC "' + node.publicId + '"';
+ if (node.systemId)
+ s += ' "' + node.systemId + '"';
+ } else if (node.systemId) {
+ s += ' SYSTEM "' + 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) {
+ switch (node.nodeType) {
+ case Node.TEXT_NODE:
+ return this.serializeText_(node);
+ case Node.COMMENT_NODE:
+ return this.serializeComment_(node);
+ case Node.DOCUMENT_NODE:
+ return this.serializeDocument_(node);
+ case Node.DOCUMENT_TYPE_NODE:
+ return this.serializeDocumentType_(node);
+ case Node.PROCESSING_INSTRUCTION_NODE:
+ return this.serializeProcessingInstruction_(node);
+ case Node.ELEMENT_NODE:
+ return this.serializeElement_(node, namespaces);
+ case Node.CDATA_SECTION_NODE:
+ return this.serializeCDataSection_(node);
+ case Node.DOCUMENT_FRAGMENT_NODE:
+ default:
+ 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) && !isEndTagOmissible(node))
+ return false;
+ return true;
+ };
+
+ XMLSerializerPrototype.serializeEndTag_ = function(node) {
+ if (this.shouldSelfClose_(node) || (!node.hasChildNodes() && isEndTagOmissible(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) && isEndTagOmissible(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 = Object.create(null);
+ if (!this.serializeAsHTMLDocument_(node)) {
+ namespaces['xml'] = XML_NAMESPACE_URI;
+ namespaces['xmlns'] = XMLNS_NAMESPACE_URI;
+ }
+
+ return this.serializeNodesWithNamespaces_(node, namespaces);
+ };
+
+ XMLSerializerPrototype.serializeToStringForPrivateScript = function(root, xmlDeclaration) {
tasak 2014/10/08 12:04:26 Done.
+ this.serializationType_ = SERIALIZATION_TYPE_FORCED_XML;
+ this.documentXMLDeclaration_ = xmlDeclaration;
+ return this.serializeNodes_(root);
+ };
+});
« no previous file with comments | « Source/core/xml/XMLSerializer.idl ('k') | public/blink_resources.grd » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698