Commit b1ac31d8 authored by Jan Köster's avatar Jan Köster
Browse files

test

parent ff098d9e
Loading
Loading
Loading
Loading
+108 −0
Original line number Diff line number Diff line
@@ -639,6 +639,21 @@ void libhtmlpp::HtmlString::_buildTree() {
                        p = base + i;
                        continue;
                    }
                    if (starts_with_ci(p, end, "<style")) {
                        // Same raw-text rationale as <script> above: CSS
                        // content can contain '<'/'>' (child-combinator
                        // selectors, stray markup pasted into a comment,
                        // duplicate/unclosed <style> tags from a page
                        // builder concatenating snippets, ...) that the
                        // ordinary tag-tokenizing path below would
                        // misinterpret as nested markup, corrupting
                        // everything parsed after it.
                        add_element_node(&lastEl);
                        size_t i = static_cast<size_t>(p - base);
                        i = HtmlElement::parseStyleElement(_Data, lastEl->element, i, lastEl->terminator);
                        p = base + i;
                        continue;
                    }
                } else if (c1 == 't') {
                    if (starts_with_ci(p, end, "<textarea")) {
                        add_element_node(&lastEl);
@@ -1120,6 +1135,99 @@ size_t libhtmlpp::HtmlElement::parseElement(
    return foundClose ? close + 1 : close;
}

size_t libhtmlpp::HtmlElement::parseStyleElement(
    const std::vector<char>& in,
    std::unique_ptr<Element>& el,
    size_t start,
    bool& termination
){
    termination = false;
    el = std::make_unique<HtmlElement>("style");
    auto* self = static_cast<HtmlElement*>(el.get());

    size_t i = start;
    if (i >= in.size() || in[i] != HTMLTAG_OPEN) {
        return start;
    }

    auto iequals = [](char a, char b) {
        return std::tolower(static_cast<unsigned char>(a)) ==
               std::tolower(static_cast<unsigned char>(b));
    };
    auto match_ci = [&](size_t pos, const char* k) -> bool {
        for (size_t j = 0; k[j]; ++j) {
            if (pos + j >= in.size() || !iequals(in[pos + j], k[j])) {
                return false;
            }
        }
        return true;
    };

    ++i; // consume '<'
    while (i < in.size() && std::isspace(static_cast<unsigned char>(in[i]))) ++i;

    const char* tag_keyword = "style";
    size_t keyword_len = std::char_traits<char>::length(tag_keyword);

    if (i + keyword_len >= in.size() || !match_ci(i, tag_keyword)) {
        // Not actually a <style -- skip to the next '>' like the other
        // dedicated parseElement variants do for their own mismatched case.
        while (i < in.size() && in[i] != HTMLTAG_CLOSE) ++i;
        if (i < in.size()) ++i;
        return i;
    }
    i += keyword_len; // consume "style"

    // Find the opening tag's own '>' (attributes only -- style's opening
    // tag is ordinary markup, just its content that needs raw-text care).
    while (i < in.size() && in[i] != HTMLTAG_CLOSE) ++i;

    if (i > start && i < in.size() && in[i] == HTMLTAG_CLOSE) {
        std::vector<char> raw_tag_data(in.begin() + start, in.begin() + i + 1);
        self->_serialelize(raw_tag_data);
    }

    if (i >= in.size() || in[i] != HTMLTAG_CLOSE) {
        // Opening tag itself was never closed (e.g. EOF mid-attribute-list).
        return i;
    }

    ++i; // consume '>'
    size_t content_begin = i;

    // Raw-text scan for the literal closing sequence: </style -- everything
    // in between, including any '<'/'>' a CSS selector/comment happens to
    // contain, is opaque content, exactly like <script>'s own handling.
    for (; i < in.size(); ++i) {
        if (in[i] == HTMLTAG_OPEN && match_ci(i, "</style")) {
            if (i > content_begin) {
                std::string cssText(in.begin() + content_begin, in.begin() + i);
                TextElement textEl(cssText);
                self->appendChild(&textEl);
            }

            size_t closing_tag_end_pos = i + keyword_len + 2; // +2 for '</'
            while (closing_tag_end_pos < in.size() && in[closing_tag_end_pos] != HTMLTAG_CLOSE) {
                ++closing_tag_end_pos;
            }

            if (closing_tag_end_pos < in.size() && in[closing_tag_end_pos] == HTMLTAG_CLOSE) {
                return closing_tag_end_pos + 1;
            }
            return closing_tag_end_pos;
        }
    }

    // No closing </style> before EOF -- capture whatever's left, same
    // best-effort fallback ScriptElement::parseElement uses.
    if (in.size() > content_begin) {
        std::string cssText(in.begin() + content_begin, in.end());
        TextElement textEl(cssText);
        self->appendChild(&textEl);
    }
    return i;
}

namespace libhtmlpp {

    void _copy(libhtmlpp::Element *dest,const libhtmlpp::Element *src){
+11 −0
Original line number Diff line number Diff line
@@ -166,6 +166,17 @@ namespace libhtmlpp {
        void   remove(Element* el);

        static size_t parseElement(const std::vector<char> &in,std::unique_ptr<libhtmlpp::Element> &el,size_t start,bool &termination);

        /** Parses a <style> tag the same "raw text until literal </style>"
         *  way ScriptElement::parseElement parses <script> -- CSS content
         *  can contain '<'/'>' (child-combinator selectors, stray markup
         *  pasted into a comment, etc.) that the ordinary tag-tokenizing
         *  path would misinterpret as nested markup. Unlike script/svg/
         *  textarea, this deliberately still produces a plain HtmlElement
         *  (tag "style") with one TextElement child holding the raw CSS,
         *  not a dedicated element type -- callers (e.g. blogi's htmlimport)
         *  already read style content that way, keyed off the tag name. */
        static size_t parseStyleElement(const std::vector<char> &in,std::unique_ptr<libhtmlpp::Element> &el,size_t start,bool &termination);
    protected:

        std::unique_ptr<Element>  _childElement=nullptr;