Loading src/css.cpp +20 −6 Original line number Diff line number Diff line Loading @@ -199,10 +199,18 @@ namespace { // Substitutes every var(...) occurrence in `value`, recursively -- // `resolving` (the set of custom-property names currently being // expanded on this call stack) provides cycle detection: a name already // in it resolves to "" instead of recursing forever. // in it resolves to "" instead of recursing forever. `sawUnresolved` is // set to true (never reset to false) whenever a var() reference turns // out invalid per spec (cyclic, or absent with no fallback) -- callers // use this to tell "resolved cleanly" apart from "silently substituted // an empty string", since the latter, left in a still-live declaration // like `calc( * 1px + ...)`, is worse than dropping the declaration: // it's syntactically-broken-but-not-obviously-invalid text rather than // a clean absence. std::string substituteVars(const std::string &value, const std::map<std::string,std::string> &customProperties, std::set<std::string> &resolving) std::set<std::string> &resolving, bool &sawUnresolved) { if (value.find("var(") == std::string::npos) return value; Loading @@ -226,16 +234,18 @@ namespace { std::string substitution; if (resolving.count(name)) { substitution = ""; // cyclic reference -- invalid per spec sawUnresolved = true; } else { auto it = customProperties.find(name); if (it != customProperties.end()) { resolving.insert(name); substitution = substituteVars(it->second, customProperties, resolving); substitution = substituteVars(it->second, customProperties, resolving, sawUnresolved); resolving.erase(name); } else if (!fallback.empty()) { substitution = substituteVars(fallback, customProperties, resolving); substitution = substituteVars(fallback, customProperties, resolving, sawUnresolved); } else { substitution = ""; // unresolvable, no fallback -- invalid per spec sawUnresolved = true; } } out += substitution; Loading Loading @@ -755,9 +765,13 @@ void libhtmlpp::CSSStyleSheet::collectApproximateMatches( } std::string libhtmlpp::resolveCSSVariables(const std::string &value, const std::map<std::string,std::string> &customProperties) const std::map<std::string,std::string> &customProperties, bool *unresolved) { std::set<std::string> resolving; return substituteVars(value, customProperties, resolving); bool sawUnresolved = false; std::string result = substituteVars(value, customProperties, resolving, sawUnresolved); if (unresolved) *unresolved = sawUnresolved; return result; } src/css.h +12 −2 Original line number Diff line number Diff line Loading @@ -198,9 +198,19 @@ namespace libhtmlpp { * stored value of a custom property is inserted verbatim at the * `var(...)` call's position, exactly as a browser's cascade would; * no CSS value-type awareness (color/length/etc.) is needed or * applied. */ * applied. * * If @p unresolved is non-null, it's set to true when any var() * reference turned out invalid (cyclic, or absent with no fallback) -- * i.e. when the "" substitution happened at least once. A caller that * needs real CSS semantics (an invalid var() invalidates the whole * declaration, not just that one reference) should drop the property * entirely in that case instead of keeping this partially-substituted * text, since e.g. `calc(var(--missing) * 1px)` resolving to * `calc( * 1px)` is syntactically broken rather than cleanly absent. */ std::string resolveCSSVariables(const std::string &value, const std::map<std::string,std::string> &customProperties); const std::map<std::string,std::string> &customProperties, bool *unresolved = nullptr); } Loading
src/css.cpp +20 −6 Original line number Diff line number Diff line Loading @@ -199,10 +199,18 @@ namespace { // Substitutes every var(...) occurrence in `value`, recursively -- // `resolving` (the set of custom-property names currently being // expanded on this call stack) provides cycle detection: a name already // in it resolves to "" instead of recursing forever. // in it resolves to "" instead of recursing forever. `sawUnresolved` is // set to true (never reset to false) whenever a var() reference turns // out invalid per spec (cyclic, or absent with no fallback) -- callers // use this to tell "resolved cleanly" apart from "silently substituted // an empty string", since the latter, left in a still-live declaration // like `calc( * 1px + ...)`, is worse than dropping the declaration: // it's syntactically-broken-but-not-obviously-invalid text rather than // a clean absence. std::string substituteVars(const std::string &value, const std::map<std::string,std::string> &customProperties, std::set<std::string> &resolving) std::set<std::string> &resolving, bool &sawUnresolved) { if (value.find("var(") == std::string::npos) return value; Loading @@ -226,16 +234,18 @@ namespace { std::string substitution; if (resolving.count(name)) { substitution = ""; // cyclic reference -- invalid per spec sawUnresolved = true; } else { auto it = customProperties.find(name); if (it != customProperties.end()) { resolving.insert(name); substitution = substituteVars(it->second, customProperties, resolving); substitution = substituteVars(it->second, customProperties, resolving, sawUnresolved); resolving.erase(name); } else if (!fallback.empty()) { substitution = substituteVars(fallback, customProperties, resolving); substitution = substituteVars(fallback, customProperties, resolving, sawUnresolved); } else { substitution = ""; // unresolvable, no fallback -- invalid per spec sawUnresolved = true; } } out += substitution; Loading Loading @@ -755,9 +765,13 @@ void libhtmlpp::CSSStyleSheet::collectApproximateMatches( } std::string libhtmlpp::resolveCSSVariables(const std::string &value, const std::map<std::string,std::string> &customProperties) const std::map<std::string,std::string> &customProperties, bool *unresolved) { std::set<std::string> resolving; return substituteVars(value, customProperties, resolving); bool sawUnresolved = false; std::string result = substituteVars(value, customProperties, resolving, sawUnresolved); if (unresolved) *unresolved = sawUnresolved; return result; }
src/css.h +12 −2 Original line number Diff line number Diff line Loading @@ -198,9 +198,19 @@ namespace libhtmlpp { * stored value of a custom property is inserted verbatim at the * `var(...)` call's position, exactly as a browser's cascade would; * no CSS value-type awareness (color/length/etc.) is needed or * applied. */ * applied. * * If @p unresolved is non-null, it's set to true when any var() * reference turned out invalid (cyclic, or absent with no fallback) -- * i.e. when the "" substitution happened at least once. A caller that * needs real CSS semantics (an invalid var() invalidates the whole * declaration, not just that one reference) should drop the property * entirely in that case instead of keeping this partially-substituted * text, since e.g. `calc(var(--missing) * 1px)` resolving to * `calc( * 1px)` is syntactically broken rather than cleanly absent. */ std::string resolveCSSVariables(const std::string &value, const std::map<std::string,std::string> &customProperties); const std::map<std::string,std::string> &customProperties, bool *unresolved = nullptr); }