// The serializeToString(root) method must produce an XML serialization of root passing a value of false for the require well-formed parameter, and return the result.
// 1. Let candidates list be the result of retrieving a list from map where there exists a key in map that matches the value of ns or if there is no such key, then let candidates list be null.
// 1. Let candidates list be the result of retrieving a list from map where there exists a key in map that matches the value of ns or if there is no such key,
// then stop running these steps, and return the null value.
// 1. Let namespace be a context namespace with value null. The context namespace tracks the XML serialization algorithm's current default namespace.
// The context namespace is changed when either an Element Node has a default namespace declaration, or the algorithm generates a default namespace declaration
// for the Element Node to match its own namespace. The algorithm assumes no namespace (null) to start.
Optional<FlyString>namespace_;
// 2. Let prefix map be a new namespace prefix map.
HashMap<FlyString,Vector<String>>prefix_map;
// 3. Add the XML namespace with prefix value "xml" to prefix map.
// 4. Let prefix index be a generated namespace prefix index with value 1. The generated namespace prefix index is used to generate a new unique prefix value
// when no suitable existing namespace prefix is available to serialize a node's namespaceURI (or the namespaceURI of one of node's attributes).
u64prefix_index=1;
// 5. Return the result of running the XML serialization algorithm on node passing the context namespace namespace, namespace prefix map prefix map,
// generated namespace prefix index reference to prefix index, and the flag require well-formed. If an exception occurs during the execution of the algorithm,
// then catch that exception and throw an "InvalidStateError" DOMException.
// NOTE: InvalidStateError exceptions will be created when needed, as this also allows us to have a specific error message for the exception.
// 2. Let attribute prefix be the value of attr's prefix.
autoconst&attribute_prefix=attribute->prefix();
// 3. If the attribute namespace is the XMLNS namespace, then:
if(attribute_namespace==Namespace::XMLNS){
// 1. If attribute prefix is null, then attr is a default namespace declaration. Set the default namespace attr value to attr's value and stop running these steps,
// 2. Let namespace definition be the value of attr's value.
autonamespace_definition=attribute->value();
// 3. If namespace definition is the XML namespace, then stop running these steps, and return to Main to visit the next attribute.
if(namespace_definition==Namespace::XML)
continue;
// 4. If namespace definition is the empty string (the declarative form of having no namespace), then let namespace definition be null instead.
if(namespace_definition.is_empty())
namespace_definition={};
// 5. If prefix definition is found in map given the namespace namespace definition, then stop running these steps, and return to Main to visit the next attribute.
// 7. Add the value of prefix definition as a new key to the local prefixes map, with the namespace definition as the key's value replacing the value of null with the empty string if applicable.
// FIXME: 1. If the require well-formed flag is set (its value is true), and attribute value contains characters that are not matched by the XML Char production,
// then throw an exception; the serialization of this attribute value would fail to produce a well-formed element serialization.
// 2. If attribute value is null, then return the empty string.
if(attribute_value.is_null())
returnString::empty();
// 3. Otherwise, attribute value is a string. Return the value of attribute value, first replacing any occurrences of the following:
// 2. Let localname set be a new empty namespace localname set. This localname set will contain tuples of unique attribute namespaceURI and localName pairs, and is populated as each attr is processed.
// Spec Note: This set is used to [optionally] enforce the well-formed constraint that an element cannot have two attributes with the same namespaceURI and localName.
// This can occur when two otherwise identical attributes on the same element differ only by their prefix values.
Vector<LocalNameSetEntry>local_name_set;
// 3. Loop: For each attribute attr in element's attributes, in the order they are specified in the element's attribute list:
// 1. If the require well-formed flag is set (its value is true), and the localname set contains a tuple whose values match those of a new tuple consisting of attr's namespaceURI attribute and localName attribute,
// then throw an exception; the serialization of this attr would fail to produce a well-formed element serialization.
// 5. If attribute namespace is not null, then run these sub-steps:
if(!attribute_namespace.is_null()){
// 1. Let candidate prefix be the result of retrieving a preferred prefix string from map given namespace attribute namespace with preferred prefix being attr's prefix value.
// 2. If the value of attribute namespace is the XMLNS namespace, then run these steps:
if(attribute_namespace==Namespace::XMLNS){
// 1. If any of the following are true, then stop running these steps and goto Loop to visit the next attribute:
// - the attr's value is the XML namespace;
if(attribute->value()==Namespace::XML)
continue;
// - the attr's prefix is null and the ignore namespace definition attribute flag is true (the Element's default namespace attribute should be skipped);
// and furthermore that the attr's localName (as the prefix to find) is found in the namespace prefix map given the namespace consisting of the attr's value
// (the current namespace prefix definition was exactly defined previously--on an ancestor element not the current element whose attributes are being processed).
// 2. If the require well-formed flag is set (its value is true), and the value of attr's value attribute matches the XMLNS namespace,
// then throw an exception; the serialization of this attribute would produce invalid XML because the XMLNS namespace is reserved and cannot be applied as an element's namespace via XML parsing.
// 7. If candidate prefix is not null, then append to result the concatenation of candidate prefix with ":" (U+003A COLON).
if(candidate_prefix.has_value())
result.appendff("{}:",candidate_prefix.value());
// 8. If the require well-formed flag is set (its value is true), and this attr's localName attribute contains the character ":" (U+003A COLON)
// or does not match the XML Name production or equals "xmlns" and attribute namespace is null, then throw an exception; the serialization of this attr would not be a well-formed attribute.
// 1. If the require well-formed flag is set (its value is true), and this node's localName attribute contains the character ":" (U+003A COLON) or does not match the XML Name production,
// then throw an exception; the serialization of this node would not be a well-formed element.
// 3. Otherwise, append to qualified name the value of node's localName.
else
qualified_name.append(element.local_name());
// 4. Append the value of qualified name to markup.
markup.append(qualified_name.to_string());
}
// 12. Otherwise, inherited ns is not equal to ns (the node's own namespace is different from the context namespace of its parent). Run these sub-steps:
else{
// 1. Let prefix be the value of node's prefix attribute.
autoprefix=element.prefix();
// 2. Let candidate prefix be the result of retrieving a preferred prefix string prefix from map given namespace ns.
// 3. If the value of prefix matches "xmlns", then run the following steps:
if(prefix=="xmlns"sv){
// 1. If the require well-formed flag is set, then throw an error. An Element with prefix "xmlns" will not legally round-trip in a conforming XML parser.
// 2. If the local default namespace is not null (there exists a locally-defined default namespace declaration attribute) and its value is not the XML namespace,
// then let inherited ns get the value of local default namespace unless the local default namespace is the empty string in which case let it get null
// (the context namespace is changed to the declared default, rather than this node's own namespace).
// 3. Append the value of qualified name to markup.
markup.append(qualified_name.to_string());
}
// 5. Otherwise, if prefix is not null, then:
elseif(!prefix.is_null()){
// 1. If the local prefixes map contains a key matching prefix, then let prefix be the result of generating a prefix providing as input map, ns, and prefix index.
// 7. If local default namespace is not null (there exists a locally-defined default namespace declaration attribute),
// then let inherited ns get the value of local default namespace unless the local default namespace is the empty string in which case let it get null.
if(local_default_namespace.has_value()){
if(!local_default_namespace.value().is_empty())
inherited_ns=local_default_namespace.value();
else
inherited_ns={};
}
}
// 6. Otherwise, if local default namespace is null, or local default namespace is not null and its value is not equal to ns, then:
// 7. Otherwise, the node has a local default namespace that matches ns.
// Append to qualified name the value of node's localName, let the value of inherited ns be ns, and append the value of qualified name to markup.
VERIFY(local_default_namespace.has_value());
VERIFY(local_default_namespace.value()==ns);
qualified_name.append(element.local_name());
inherited_ns=ns;
markup.append(qualified_name.to_string());
}
}
// 13. Append to markup the result of the XML serialization of node's attributes given map, prefix index, local prefixes map, ignore namespace definition attribute flag, and require well-formed flag.
// 15. If ns is not the HTML namespace, and the node's list of children is empty, then append "/" (U+002F SOLIDUS) to markup and set the skip end tag flag to true.
if(ns!=Namespace::HTML&&!element.has_children()){
markup.append('/');
skip_end_tag=true;
}
// 16. Append ">" (U+003E GREATER-THAN SIGN) to markup.
markup.append('>');
// 17. If the value of skip end tag is true, then return the value of markup and skip the remaining steps. The node is a leaf-node.
if(skip_end_tag)
returnmarkup.to_string();
// 18. If ns is the HTML namespace, and the node's localName matches the string "template", then this is a template element.
// Append to markup the result of XML serializing a DocumentFragment node given the template element's template contents (a DocumentFragment), providing inherited ns, map, prefix index, and the require well-formed flag.
// 19. Otherwise, append to markup the result of running the XML serialization algorithm on each of node's children, in tree order, providing inherited ns, map, prefix index, and the require well-formed flag.
// 2. For each child child of node, in tree order, run the XML serialization algorithm on the child passing along the provided arguments, and append the result to serialized document.
// FIXME: 1. If the require well-formed flag is set (its value is true), and node's data contains characters that are not matched by the XML Char production,
// then throw an exception; the serialization of this node's data would not be well-formed.
// 2. Let markup be the value of node's data.
Stringmarkup=text.data();
// 3. Replace any occurrences of "&" in markup by "&".
// 2. For each child child of node, in tree order, run the XML serialization algorithm on the child given namespace, prefix map, a reference to prefix index,
// and flag require well-formed. Concatenate the result to markup.
// FIXME: 1. If the require well-formed flag is true and the node's publicId attribute contains characters that are not matched by the XML PubidChar production,
// then throw an exception; the serialization of this node would not be a well-formed document type declaration.
// 2. If the require well-formed flag is true and the node's systemId attribute contains characters that are not matched by the XML Char production or that contains
// both a """ (U+0022 QUOTATION MARK) and a "'" (U+0027 APOSTROPHE), then throw an exception; the serialization of this node would not be a well-formed document type declaration.
// FIXME: Check systemId against the XML Char production.
// 6. Append the value of the node's name attribute to markup. For a node belonging to an HTML document, the value will be all lowercase.
markup.append(document_type.name());
// 7. If the node's publicId is not the empty string then append the following, in the order listed, to markup:
if(!document_type.public_id().is_empty()){
// 1. " " (U+0020 SPACE);
// 2. The string "PUBLIC";
// 3. " " (U+0020 SPACE);
// 4. """ (U+0022 QUOTATION MARK);
markup.append(" PUBLIC \""sv);
// 5. The value of the node's publicId attribute;
markup.append(document_type.public_id());
// 6. """ (U+0022 QUOTATION MARK).
markup.append('"');
}
// 8. If the node's systemId is not the empty string and the node's publicId is set to the empty string, then append the following, in the order listed, to markup:
// 1. If the require well-formed flag is set (its value is true), and node's target contains a ":" (U+003A COLON) character
// or is an ASCII case-insensitive match for the string "xml", then throw an exception; the serialization of this node's target would not be well-formed.
// 2. If the require well-formed flag is set (its value is true), and node's data contains characters that are not matched by the XML Char production or contains
// the string "?>" (U+003F QUESTION MARK, U+003E GREATER-THAN SIGN), then throw an exception; the serialization of this node's data would not be well-formed.
// FIXME: Check data against the XML Char production.