Repository: LadybirdBrowser/ladybird
File: Libraries/LibWeb/HTML/XMLSerializer.cpp (C++)

Code before:
{
    // 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 "--" (two adjacent U+002D HYPHEN-MINUS characters) or that ends with a "-" (U+002D HYPHEN-MINUS) character, then throw an exception;
    // the serialization of this node's data would not be well-formed.
    if (require_well_formed == RequireWellFormed::Yes) {
        // FIXME: Check comment's data against the XML Char production.

        if (comment.data().contains("--"sv))
            return WebIDL::InvalidStateError::create(comment.realm(), "Comment data contains two adjacent hyphens"_string);

        if (comment.data().ends_with('-'))
            return WebIDL::InvalidStateError::create(comment.realm(), "Comment data ends with a hyphen"_string);
    }

    // Otherwise, return the concatenation of "<!--", node's data, and "-->".
    return MUST(String::formatted("<!--{}-->", comment.data()));
}

// https://w3c.github.io/DOM-Parsing/#xml-serializing-a-text-node
static WebIDL::ExceptionOr<String> serialize_text(DOM::Text const& text, [[maybe_unused]] RequireWellFormed require_well_formed)
{
    // 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.
    if (require_well_formed == RequireWellFormed::Yes) {
        auto const& data = text.data();
        for (auto it = data.code_points().begin(); it != data.code_points().end(); ++it) {
            u32 code_point = *it;
            // XML 1.0 Char production:
            // https://www.w3.org/TR/xml/#charsets
            // #x9 | #xA | #xD | [#x20-#xD7FF] | [#xE000-#xFFFD] | [#x10000-#x10FFFF]
            bool is_valid_xml_char = code_point == 0x9 || code_point == 0xA || code_point == 0xD || (code_point >= 0x20 && code_point <= 0xD7FF) || (code_point >= 0xE000 && code_point <= 0xFFFD) || (code_point >= 0x10000 && code_point <= 0x10FFFF);
            if (!is_valid_xml_char)
                return WebIDL::InvalidStateError::create(text.realm(), "Text contains characters not allowed in XML"_string);
        }
    }

    // 2. Let markup be the value of node's data.
    auto markup = text.data();

    // 3. Replace any occurrences of "&" in markup by "&amp;".
    markup = MUST(markup.replace("&"sv, "&amp;"sv, ReplaceMode::All));

    // 4. Replace any occurrences of "<" in markup by "&lt;".
    markup = MUST(markup.replace("<"sv, "&lt;"sv, ReplaceMode::All));

    // 5. Replace any occurrences of ">" in markup by "&gt;".
    markup = MUST(markup.replace(">"sv, "&gt;"sv, ReplaceMode::All));

    // 6. Return the value of markup.
    return markup;
}

Review comment (refactor):
This is minor, but we can change this to a range-based for loop which is a bit less code / a bit nicer to read (or maybe even our `all_of` algorithm):
```cpp
        for (u32 code_point : text.data().code_points()) {
```

However, having noticed that, from looking around this file there are other places that we need to check against the XML Char production, so it would be nice that were moved into a helper function so this is easy to call in the other places for when that is also implemented (can't see anywhere else we have this type of check already implememented)


Code after:
{
    // 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 "--" (two adjacent U+002D HYPHEN-MINUS characters) or that ends with a "-" (U+002D HYPHEN-MINUS) character, then throw an exception;
    // the serialization of this node's data would not be well-formed.
    if (require_well_formed == RequireWellFormed::Yes) {
        // FIXME: Check comment's data against the XML Char production.

        if (comment.data().contains("--"sv))
            return WebIDL::InvalidStateError::create(comment.realm(), "Comment data contains two adjacent hyphens"_string);

        if (comment.data().ends_with('-'))
            return WebIDL::InvalidStateError::create(comment.realm(), "Comment data ends with a hyphen"_string);
    }

    // Otherwise, return the concatenation of "<!--", node's data, and "-->".
    return MUST(String::formatted("<!--{}-->", comment.data()));
}

static bool is_valid_xml_char(u32 code_point)
{
    // XML 1.0 Char production:
    // https://www.w3.org/TR/xml/#charsets
    // #x9 | #xA | #xD | [#x20-#xD7FF] | [#xE000-#xFFFD] | [#x10000-#x10FFFF]
    return code_point == 0x9 || code_point == 0xA || code_point == 0xD || (code_point >= 0x20 && code_point <= 0xD7FF) || (code_point >= 0xE000 && code_point <= 0xFFFD) || (code_point >= 0x10000 && code_point <= 0x10FFFF);
}

// https://w3c.github.io/DOM-Parsing/#xml-serializing-a-text-node
static WebIDL::ExceptionOr<String> serialize_text(DOM::Text const& text, RequireWellFormed require_well_formed)
{
    // 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.
    if (require_well_formed == RequireWellFormed::Yes) {
        for (u32 code_point : text.data().code_points()) {
            if (!is_valid_xml_char(code_point))
                return WebIDL::InvalidStateError::create(text.realm(), "Text contains characters not allowed in XML"_string);
        }
    }

    // 2. Let markup be the value of node's data.
    auto markup = text.data();

    // 3. Replace any occurrences of "&" in markup by "&amp;".
    markup = MUST(markup.replace("&"sv, "&amp;"sv, ReplaceMode::All));

    // 4. Replace any occurrences of "<" in markup by "&lt;".
    markup = MUST(markup.replace("<"sv, "&lt;"sv, ReplaceMode::All));

    // 5. Replace any occurrences of ">" in markup by "&gt;".
    markup = MUST(markup.replace(">"sv, "&gt;"sv, ReplaceMode::All));

    // 6. Return the value of markup.