Commit 7fe4cba7 authored by jan.koester's avatar jan.koester
Browse files

test

parent 37fa8bd2
Loading
Loading
Loading
Loading
+40 −5
Changes for editor/src/htmlimport.cpp: 40 added lines, 5 removed lines.
Original line number Diff line number Diff line
@@ -260,7 +260,8 @@ void blogi::htmlimport::mergeStylesheetRules(
    const std::string &cssClass,
    const std::string &id,
    std::map<std::string,std::string> &props,
    std::string &mediaRules)
    std::string &mediaRules,
    std::set<std::string> &seenMediaBlocks)
{
    std::vector<std::string> classes;
    if (!cssClass.empty()) {
@@ -313,19 +314,38 @@ void blogi::htmlimport::mergeStylesheetRules(
            singleSel = singleSel.substr(start, end - start + 1);

            std::string matchSel = singleSel;
            bool hadCombinator = false;
            {
                size_t lastSep = matchSel.find_last_of(" >+~");
                if (lastSep != std::string::npos) {
                    size_t mstart = matchSel.find_first_not_of(" >+~", lastSep);
                    if (mstart != std::string::npos) {
                        matchSel = matchSel.substr(mstart);
                        hadCombinator = true;
                    }
                }
            }

            bool matches = false;

            if (!matchSel.empty() && matchSel[0] == '#') {
            // A bare tag name left over after stripping a combinator's
            // ancestor part (e.g. "[data-atom=header]>div" reduces to "div")
            // can't be verified here -- this function only ever sees the
            // current element's own tag/class/id, never its ancestor chain
            // -- so treating it as a match would apply the rule to literally
            // every element of that tag in the whole document. Confirmed
            // against a real scraped page where exactly this pattern matched
            // ~24,000 times and blew up the imported output to 36MB. A
            // class/id qualifier on the rightmost compound (e.g. "div.foo")
            // is still specific enough to check below.
            bool bareTagAfterCombinator = hadCombinator &&
                !matchSel.empty() && matchSel[0] != '#' && matchSel[0] != '.' &&
                matchSel.find('.') == std::string::npos &&
                matchSel.find('#') == std::string::npos;

            if (bareTagAfterCombinator) {
                // matches stays false
            } else if (!matchSel.empty() && matchSel[0] == '#') {
                if (!id.empty() && matchSel == "#" + id) matches = true;
            } else if (!matchSel.empty() && matchSel[0] == '.') {
                std::string clsSel = matchSel.substr(1);
@@ -368,7 +388,16 @@ void blogi::htmlimport::mergeStylesheetRules(
                        block += prop.getName() + ": " + prop.getValue() + "; ";
                    }
                    block += "} } ";
                    // The same @media block, once present anywhere in the
                    // final page's HTML, applies document-wide regardless of
                    // which widget's <style> tag carries it -- so including
                    // it more than once across the whole import is pure
                    // bloat, not a correctness requirement. Real scraped
                    // pages routinely have many widgets share one rule (e.g.
                    // by class), each independently re-matching it here.
                    if (seenMediaBlocks.insert(block).second) {
                        mediaRules += block;
                    }
                } else {
                    for (const auto &prop : rule.getDeclaration().getProperties()) {
                        if (props.find(prop.getName()) == props.end()) {
@@ -573,6 +602,7 @@ void blogi::htmlimport::htmlElementToWidgetXml(
    tinyxml2::XMLDocument &doc,
    tinyxml2::XMLElement *parentXml,
    const libhtmlpp::CSSStyleSheet &sheet,
    std::set<std::string> &seenMediaBlocks,
    libhtmlpp::Element *stopBefore)
{
    if (!htmlEl) return;
@@ -650,7 +680,7 @@ void blogi::htmlimport::htmlElementToWidgetXml(
            std::string elemId = el->getAtributte("id");

            std::string mediaRules;
            mergeStylesheetRules(sheet, tag, cssClass, elemId, cssProps, mediaRules);
            mergeStylesheetRules(sheet, tag, cssClass, elemId, cssProps, mediaRules, seenMediaBlocks);

            // ---- Map HTML tags to widget types ----

@@ -1824,16 +1854,21 @@ bool blogi::htmlimport::convertHtmlToWidgetXml(const std::string &html,
        xmlRoot->SetAttribute("GlobalJs", globalJs.c_str());
    }

    // Shared across every top-level node imported below (not per-node) so
    // an @media block already emitted via one node's widgets isn't repeated
    // by another's -- see mergeStylesheetRules's dedup comment.
    std::set<std::string> seenMediaBlocks;

    if (root) {
        if (selectedPaths) {
            for (const auto &path : *selectedPaths) {
                libhtmlpp::Element *el = resolveImportPath(root, path);
                if (el) {
                    htmlElementToWidgetXml(el, xmlDoc, xmlRoot, stylesheet, el->nextElement());
                    htmlElementToWidgetXml(el, xmlDoc, xmlRoot, stylesheet, seenMediaBlocks, el->nextElement());
                }
            }
        } else {
            htmlElementToWidgetXml(root, xmlDoc, xmlRoot, stylesheet);
            htmlElementToWidgetXml(root, xmlDoc, xmlRoot, stylesheet, seenMediaBlocks);
        }
    }

+13 −1
Changes for editor/src/htmlimport.h: 13 added lines, 1 removed line.
Original line number Diff line number Diff line
@@ -74,13 +74,24 @@ namespace htmlimport {
    std::map<std::string,std::string> declarationToMap(
        const libhtmlpp::CSSDeclaration &decl);

    /** @p seenMediaBlocks accumulates the exact text of every @media block
     *  already appended to some element's @p mediaRules across the whole
     *  import (see convertHtmlToWidgetXml/htmlElementToWidgetXml, which
     *  share one instance across every element processed) -- a block whose
     *  text is already present is skipped rather than appended again. This
     *  is safe because a <style> block's effect is document-wide regardless
     *  of which widget's XML happens to carry it, and it matters because a
     *  rule matched by a real page can independently match thousands of
     *  elements (e.g. by shared class), each otherwise re-appending the
     *  identical block text. */
    void mergeStylesheetRules(
        const libhtmlpp::CSSStyleSheet &sheet,
        const std::string &tag,
        const std::string &cssClass,
        const std::string &id,
        std::map<std::string,std::string> &props,
        std::string &mediaRules);
        std::string &mediaRules,
        std::set<std::string> &seenMediaBlocks);

    std::string extractCustomCss(
        std::map<std::string,std::string> &props,
@@ -116,6 +127,7 @@ namespace htmlimport {
        tinyxml2::XMLDocument &doc,
        tinyxml2::XMLElement *parentXml,
        const libhtmlpp::CSSStyleSheet &sheet,
        std::set<std::string> &seenMediaBlocks,
        libhtmlpp::Element *stopBefore = nullptr);

    /** One node of the HTML-import block picker's tree: an element reachable