Commit 6f9f4173 authored by jan.koester's avatar jan.koester
Browse files

better import

parent 0fe219f4
Loading
Loading
Loading
Loading
+72 −6
Changes for editor/src/htmlimport.cpp: 72 added lines, 6 removed lines.
Original line number Diff line number Diff line
@@ -1050,14 +1050,64 @@ std::string resolveUrl(const std::string &baseUrl, const std::string &ref)
    return urlOrigin(base) + path;
}

// Rewrites every url(...) reference found anywhere in @p css (declaration
// values -- background/background-image/border-image/list-style-image/mask/
// cursor/@font-face src/... all accept it, so this scans the raw text
// instead of enumerating property names) to absolute, using resolveUrl.
// Quoted ("...", '...') and unquoted forms are both handled; whatever quoting
// (or lack of it) the original used is preserved. Malformed/unterminated
// url(...) is left untouched rather than risk corrupting the stylesheet.
std::string rewriteCssUrls(const std::string &css, const std::string &baseUrl)
{
    std::string out;
    out.reserve(css.size());
    size_t i = 0, n = css.size();
    while (i < n) {
        if (i + 4 <= n && std::tolower(static_cast<unsigned char>(css[i])) == 'u' &&
            std::tolower(static_cast<unsigned char>(css[i + 1])) == 'r' &&
            std::tolower(static_cast<unsigned char>(css[i + 2])) == 'l' &&
            css[i + 3] == '(') {
            size_t close = css.find(')', i + 4);
            if (close == std::string::npos) { out += css[i]; ++i; continue; }

            std::string inner = css.substr(i + 4, close - (i + 4));
            size_t start = inner.find_first_not_of(" \t\r\n");
            size_t end = inner.find_last_not_of(" \t\r\n");
            std::string ref = (start == std::string::npos) ? ""
                             : inner.substr(start, end - start + 1);

            char quote = '\0';
            if (ref.size() >= 2 && (ref.front() == '"' || ref.front() == '\'') &&
                ref.back() == ref.front()) {
                quote = ref.front();
                ref = ref.substr(1, ref.size() - 2);
            }

            out += "url(";
            if (quote) out += quote;
            out += resolveUrl(baseUrl, ref);
            if (quote) out += quote;
            out += ')';
            i = close + 1;
        } else {
            out += css[i];
            ++i;
        }
    }
    return out;
}

// Walks the whole parsed document (unlike collectTopLevelElements, this
// includes <head> -- external stylesheets/scripts live there) and rewrites
// every document-relative resource/link reference to absolute, in place, so
// the rest of the import pipeline (which only ever looks at the attribute
// value, see e.g. the http(s)-prefix checks around the <link>/<script>
// fetch loop in convertHtmlToWidgetXml, or the plain SetAttribute("src", ...)
// in htmlElementToWidgetXml's Image/Video branches) never has to know the
// page came from a URL rather than being pasted/uploaded already-absolute.
// every document-relative resource/link/style reference to absolute, in
// place, so the rest of the import pipeline (which only ever looks at the
// attribute value, see e.g. the http(s)-prefix checks around the <link>/
// <script> fetch loop in convertHtmlToWidgetXml, or the plain
// SetAttribute("src", ...) in htmlElementToWidgetXml's Image/Video branches)
// never has to know the page came from a URL rather than being pasted/
// uploaded already-absolute. <style> block text and externally fetched
// stylesheets are handled separately, at the point convertHtmlToWidgetXml
// reads/fetches them -- see the two rewriteCssUrls call sites there.
void rewriteRelativeUrls(libhtmlpp::Element *node, const std::string &baseUrl)
{
    while (node) {
@@ -1081,6 +1131,12 @@ void rewriteRelativeUrls(libhtmlpp::Element *node, const std::string &baseUrl)
                if (!href.empty()) hel->setAttribute("href", resolveUrl(baseUrl, href));
            }

            // Inline style="...: url(...)" (background-image etc.) -- resolved
            // here rather than where htmlElementToWidgetXml later reads this
            // same attribute, so that code stays unaware baseUrl even exists.
            std::string style = hel->getAtributte("style");
            if (!style.empty()) hel->setAttribute("style", rewriteCssUrls(style, baseUrl));

            rewriteRelativeUrls(hel->firstChild(), baseUrl);
        }
        node = node->nextElement();
@@ -1365,6 +1421,10 @@ bool blogi::htmlimport::convertHtmlToWidgetXml(const std::string &html,
                        styleChild = styleChild->nextElement();
                    }
                    if (!cssText.empty()) {
                        // url(...) here is relative to the page itself (this
                        // <style> block is part of the document), unlike an
                        // external stylesheet's own url(...) below.
                        if (!baseUrl.empty()) cssText = rewriteCssUrls(cssText, baseUrl);
                        libhtmlpp::CSSStyleSheet partial;
                        partial.parse(cssText);
                        for (const auto &rule : partial.getRules()) {
@@ -1395,6 +1455,12 @@ bool blogi::htmlimport::convertHtmlToWidgetXml(const std::string &html,
                                if (fetchExternalResource(href, kMaxStylesheetBytes,
                                                           "text/css", bytes, fetchError)) {
                                    std::string cssText(bytes.begin(), bytes.end());
                                    // This sheet's own url(...) references are
                                    // relative to ITS location (href), not the
                                    // page's -- href is already absolute here
                                    // (isAbsolute above, or rewriteRelativeUrls
                                    // resolved it against the page baseUrl).
                                    cssText = rewriteCssUrls(cssText, href);
                                    libhtmlpp::CSSStyleSheet partial;
                                    partial.parse(cssText);
                                    for (const auto &rule : partial.getRules()) {
+6 −4
Changes for editor/src/htmlimport.h: 6 added lines, 4 removed lines.
Original line number Diff line number Diff line
@@ -131,11 +131,13 @@ namespace htmlimport {
     *  If @p baseUrl is non-empty (the page was fetched from a URL rather
     *  than pasted/uploaded), document-relative references -- <img src>,
     *  <source src>, <video>/<audio> src, <a href>, <link href>, <script
     *  src> -- are resolved against it before anything else runs, so
     *  src>, and any CSS url(...) (inline style="", <style> blocks, and
     *  externally fetched stylesheets, each resolved against its own
     *  location) -- are resolved against it before anything else runs, so
     *  relative resources on the original page still work once hosted by
     *  blogi (see rewriteRelativeUrls in htmlimport.cpp). Already-absolute
     *  references and non-http(s) schemes (data:, mailto:, javascript:,
     *  fragment-only "#...") are left untouched. */
     *  blogi (see rewriteRelativeUrls/rewriteCssUrls in htmlimport.cpp).
     *  Already-absolute references and non-http(s) schemes (data:, mailto:,
     *  javascript:, fragment-only "#...") are left untouched. */
    bool convertHtmlToWidgetXml(const std::string &html,
                                std::string &outXml,
                                std::string &outGlobalCss,