Loading editor/src/htmlimport.cpp +72 −6 Changes for editor/src/htmlimport.cpp: 72 added lines, 6 removed lines. Original line number Diff line number Diff line Loading @@ -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) { Loading @@ -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(); Loading Loading @@ -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()) { Loading Loading @@ -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()) { Loading editor/src/htmlimport.h +6 −4 Changes for editor/src/htmlimport.h: 6 added lines, 4 removed lines. Original line number Diff line number Diff line Loading @@ -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, Loading Loading
editor/src/htmlimport.cpp +72 −6 Changes for editor/src/htmlimport.cpp: 72 added lines, 6 removed lines. Original line number Diff line number Diff line Loading @@ -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) { Loading @@ -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(); Loading Loading @@ -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()) { Loading Loading @@ -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()) { Loading
editor/src/htmlimport.h +6 −4 Changes for editor/src/htmlimport.h: 6 added lines, 4 removed lines. Original line number Diff line number Diff line Loading @@ -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, Loading