testing/web-platform/tests/domparsing/XMLSerializer-serializeToString.html
author Kent Tamura <tkent@chromium.org>
Tue, 05 Mar 2019 11:11:22 +0000
changeset 464516 2e35ac1f3c2141c3986777b67b155216346bb028
parent 464361 66b1aba3fb4372ec47986e728b1da9b32dd3d368
child 464535 9af6b8db40bd483d631ba7535aa16a3de3ae345f
permissions -rw-r--r--
Bug 1527540 [wpt PR 15343] - domparsing: Fix an issue that XMLSerializer generates new prefix for an attribute with an existing namespace, a=testonly Automatic update from web-platform-tests domparsing: Fix an issue that XMLSerializer generates new prefix for an attribute with an existing namespace Add an implementation of "retrieving a preferred prefix string" [1], and apply it to attribute serialization. As the specification defines, this CL introduces another map [2] like: namespace URI => [older prefix, ..., newer prefix] in order to retrieve most-recently-declared prefix. When we search the map for a prefix, we check availability of the prefix by the existing prefix-namespace map. There are two failing test cases in XMLSerializer-serializeToString.html. They follow the specification. * The first failing one: An non-well-formed XML is produced if we follow the specification. Chrome's new behavior matches to Firefox. Safari also produces a bad XML. * The second failing one: Firefox, Safari, and Chrome without this CL preserve prefix in such case. This CL keeps the interoperable behavior. [1] https://w3c.github.io/DOM-Parsing/#dfn-retrieving-a-preferred-prefix-string [2] https://w3c.github.io/DOM-Parsing/#dfn-namespace-prefix-map Bug: 906807 Change-Id: Idaea590ba19c9c21acbaf456ea91ce550c74db5a Reviewed-on: https://chromium-review.googlesource.com/c/1445033 Commit-Queue: Kent Tamura <tkent@chromium.org> Reviewed-by: Yoshifumi Inoue <yosin@chromium.org> Auto-Submit: Kent Tamura <tkent@chromium.org> Cr-Commit-Position: refs/heads/master@{#631573} -- wpt-commits: 2647e1e8f6995d12f44edbf0b4fce752e5cb7ec5 wpt-pr: 15343

<!DOCTYPE HTML>
<meta charset=utf-8>
<html>
 <head>
  <title>domparsing Test: XMLSerializer.serializeToString</title>
  <script src="/resources/testharness.js"></script>
  <script src="/resources/testharnessreport.js"></script>
 </head>
 <body>
    <h1>domparsing_XMLSerializer_serializeToString</h1>
  <script>
function createXmlDoc(){
  var input = '<?xml version="1.0" encoding="UTF-8"?><root><child1>value1</child1></root>';
  var parser = new DOMParser();
  return parser.parseFromString(input, 'text/xml');
}

// Returns the root element.
function parse(xmlString) {
  return (new DOMParser()).parseFromString(xmlString, 'text/xml').documentElement;
}

function serialize(node) {
  return (new XMLSerializer()).serializeToString(node);
}

test(function() {
  var serializer = new XMLSerializer();
  var root = createXmlDoc().documentElement;
  var xmlString = serializer.serializeToString(root);
  assert_equals(xmlString, '<root><child1>value1</child1></root>');
}, 'check XMLSerializer.serializeToString method could parsing xmldoc to string');

test(function() {
  var serializer = new XMLSerializer();
  var root = createXmlDoc().documentElement;
  var element = root.ownerDocument.createElementNS('urn:foo', 'another');
  var child1 = root.firstChild;
  root.replaceChild(element, child1);
  element.appendChild(child1);
  var xmlString = serializer.serializeToString(root);
  assert_equals(xmlString, '<root><another xmlns="urn:foo"><child1 xmlns="">value1</child1></another></root>');
}, 'Check if the default namespace is correctly reset.');

test(function() {
  var input = '<root xmlns="urn:bar"><outer xmlns=""><inner>value1</inner></outer></root>';
  var root = (new DOMParser()).parseFromString(input, 'text/xml').documentElement;
  var xmlString = (new XMLSerializer()).serializeToString(root);
  assert_equals(xmlString, '<root xmlns="urn:bar"><outer xmlns=""><inner>value1</inner></outer></root>');
}, 'Check if there is no redundant empty namespace declaration.');

test(function() {
  let root = parse('<r xmlns:xx="uri"></r>');
  root.setAttributeNS('uri', 'name', 'v');
  assert_equals(serialize(root), '<r xmlns:xx="uri" xx:name="v"/>');

  let root2 = parse('<r xmlns:xx="uri"><b/></r>');
  let child = root2.firstChild;
  child.setAttributeNS('uri', 'name', 'v');
  assert_equals(serialize(root2), '<r xmlns:xx="uri"><b xx:name="v"/></r>');

  let root3 = parse('<r xmlns:x0="uri" xmlns:x2="uri"><b xmlns:x1="uri"/></r>');
  let child3 = root3.firstChild;
  child3.setAttributeNS('uri', 'name', 'v');
  assert_equals(serialize(root3),
                '<r xmlns:x0="uri" xmlns:x2="uri"><b xmlns:x1="uri" x1:name="v"/></r>',
                'Should choose the nearest prefix');
}, 'Check if an attribute with namespace and no prefix is serialized with the nearest-declared prefix');

test(function() {
  let root = parse('<el1 xmlns:p="u1" xmlns:q="u1"><el2 xmlns:q="u2"/></el1>');
  root.firstChild.setAttributeNS('u1', 'name', 'v');
  assert_equals(serialize(root),
                '<el1 xmlns:p="u1" xmlns:q="u1"><el2 xmlns:q="u2" q:name="v"/></el1>');
  // Maybe this is a specification error.
}, 'Check if an attribute with namespace and no prefix is serialized with the nearest-declared prefix even if the prefix is assigned to another namespace.');

test(function() {
  let root = parse('<r xmlns:xx="uri"></r>');
  root.setAttributeNS('uri', 'p:name', 'v');
  assert_equals(serialize(root), '<r xmlns:xx="uri" xx:name="v"/>');

  let root2 = parse('<r xmlns:xx="uri"><b/></r>');
  let child = root2.firstChild;
  child.setAttributeNS('uri', 'p:name', 'value');
  assert_equals(serialize(root2),
                '<r xmlns:xx="uri"><b xx:name="value"/></r>');
}, 'Check if the prefix of an attribute is replaced with another existing prefix mapped to the same namespace URI.');

test(function() {
  let root = parse('<r xmlns:xx="uri"></r>');
  root.setAttributeNS('uri2', 'p:name', 'value');
  assert_equals(serialize(root),
                '<r xmlns:xx="uri" xmlns:ns1="uri2" ns1:name="value"/>');
}, 'Check if the prefix of an attribute is NOT preserved in a case where neither its prefix nor its namespace URI is not already used.');

test(function() {
  let root = parse('<r xmlns:xx="uri"></r>');
  root.setAttributeNS('uri2', 'xx:name', 'value');
  assert_equals(serialize(root),
                '<r xmlns:xx="uri" xmlns:ns1="uri2" ns1:name="value"/>');
}, 'Check if the prefix of an attribute is replaced with a generated one in a case where the prefix is already mapped to a different namespace URI.');

test(function() {
  var serializer = new XMLSerializer();
  var parser = new DOMParser();
  var root = parser.parseFromString('<root />', 'text/xml').documentElement;
  root.setAttribute('attr', '\t');
  assert_in_array(serializer.serializeToString(root), [
    '<root attr="&#9;"/>', '<root attr="&#x9;"/>']);
  root.setAttribute('attr', '\n');
  assert_in_array(serializer.serializeToString(root), [
    '<root attr="&#xA;"/>', '<root attr="&#10;"/>']);
  root.setAttribute('attr', '\r');
  assert_in_array(serializer.serializeToString(root), [
    '<root attr="&#xD;"/>', '<root attr="&#13;"/>']);
}, 'check XMLSerializer.serializeToString escapes attribute values for roundtripping');

test(function() {
  const root = (new Document()).createElement('root');
  root.setAttributeNS('uri1', 'p:foobar', 'value1');
  root.setAttributeNS('http://www.w3.org/2000/xmlns/', 'xmlns:p', 'uri2');
  const xmlString = (new XMLSerializer()).serializeToString(root);
  assert_equals(xmlString, '<root xmlns:ns1="uri1" ns1:foobar="value1" xmlns:p="uri2"/>');
}, 'Check if attribute serialization takes into account of following xmlns:* attributes');

test(function() {
  const input = '<root xmlns:p="uri1"><child/></root>';
  const root = (new DOMParser()).parseFromString(input, 'text/xml').documentElement;
  root.firstChild.setAttributeNS('uri2', 'p:foobar', 'v');
  const xmlString = (new XMLSerializer()).serializeToString(root);
  assert_equals(xmlString, '<root xmlns:p="uri1"><child xmlns:ns1="uri2" ns1:foobar="v"/></root>');
}, 'Check if attribute serialization takes into account of the same prefix declared in an ancestor element');

test(function() {
  const input = '<root><child1/><child2/></root>';
  const root = (new DOMParser()).parseFromString(input, 'text/xml').documentElement;
  root.firstChild.setAttributeNS('uri1', 'attr1', 'value1');
  root.firstChild.setAttributeNS('uri2', 'attr2', 'value2');
  root.lastChild.setAttributeNS('uri3', 'attr3', 'value3');
  const xmlString = (new XMLSerializer()).serializeToString(root);
  assert_equals(xmlString, '<root><child1 xmlns:ns1="uri1" ns1:attr1="value1" xmlns:ns2="uri2" ns2:attr2="value2"/><child2 xmlns:ns3="uri3" ns3:attr3="value3"/></root>');
}, 'Check if generated prefixes match to "ns${index}".');

test(function() {
  const input = '<root xmlns:ns2="uri2"><child xmlns:ns1="uri1"/></root>';
  const root = (new DOMParser()).parseFromString(input, 'text/xml').documentElement;
  root.firstChild.setAttributeNS('uri3', 'attr1', 'value1');
  const xmlString = (new XMLSerializer()).serializeToString(root);
  // According to 'DOM Parsing and Serialization' draft as of 2018-12-11,
  // 'generate a prefix' result can conflict with an existing xmlns:ns* declaration.
  assert_equals(xmlString, '<root xmlns:ns2="uri2"><child xmlns:ns1="uri1" xmlns:ns1="uri3" ns1:attr1="value1"/></root>');
}, 'Check if "ns1" is generated even if the element already has xmlns:ns1.');

</script>
 </body>
</html>