|
|
@@ -19,12 +19,26 @@ |
|
|
|
#include "frozen/unordered_map.h" |
|
|
|
#include "frozen/string.h" |
|
|
|
#include "contrib/robin-hood/robin_hood.h" |
|
|
|
#include "fmt/core.h" |
|
|
|
|
|
|
|
#define DOCTEST_CONFIG_IMPLEMENTATION_IN_DLL |
|
|
|
#include "doctest/doctest.h" |
|
|
|
|
|
|
|
/* Helper for unit test stringification */ |
|
|
|
namespace doctest { |
|
|
|
template<> struct StringMaker<rspamd::css::css_color> { |
|
|
|
static String convert(const rspamd::css::css_color& value) { |
|
|
|
return fmt::format("r={};g={};b={};alpha={}", |
|
|
|
value.r, value.g, value.b, value.alpha).c_str(); |
|
|
|
} |
|
|
|
}; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
namespace rspamd::css { |
|
|
|
|
|
|
|
auto css_value::maybe_color_from_string(const std::string_view &input) |
|
|
|
-> std::optional<css_value> |
|
|
|
{ |
|
|
|
-> std::optional<css_value> { |
|
|
|
auto found_it = css_colors_map.find(input); |
|
|
|
|
|
|
|
if (found_it != css_colors_map.end()) { |
|
|
@@ -34,17 +48,16 @@ auto css_value::maybe_color_from_string(const std::string_view &input) |
|
|
|
return std::nullopt; |
|
|
|
} |
|
|
|
|
|
|
|
constexpr static inline auto hexpair_decode(char c1, char c2) -> std::uint8_t |
|
|
|
{ |
|
|
|
constexpr static inline auto hexpair_decode(char c1, char c2) -> std::uint8_t { |
|
|
|
std::uint8_t ret = 0; |
|
|
|
|
|
|
|
if (c1 >= '0' && c1 <= '9') ret = c1 - '0'; |
|
|
|
if (c1 >= '0' && c1 <= '9') ret = c1 - '0'; |
|
|
|
else if (c1 >= 'A' && c1 <= 'F') ret = c1 - 'A' + 10; |
|
|
|
else if (c1 >= 'a' && c1 <= 'f') ret = c1 - 'a' + 10; |
|
|
|
|
|
|
|
ret *= 16; |
|
|
|
|
|
|
|
if (c2 >= '0' && c2 <= '9') ret += c2 - '0'; |
|
|
|
if (c2 >= '0' && c2 <= '9') ret += c2 - '0'; |
|
|
|
else if (c2 >= 'A' && c2 <= 'F') ret += c2 - 'A' + 10; |
|
|
|
else if (c2 >= 'a' && c2 <= 'f') ret += c2 - 'a' + 10; |
|
|
|
|
|
|
@@ -52,8 +65,7 @@ constexpr static inline auto hexpair_decode(char c1, char c2) -> std::uint8_t |
|
|
|
} |
|
|
|
|
|
|
|
auto css_value::maybe_color_from_hex(const std::string_view &input) |
|
|
|
-> std::optional<css_value> |
|
|
|
{ |
|
|
|
-> std::optional<css_value> { |
|
|
|
if (input.length() == 6) { |
|
|
|
/* Plain RGB */ |
|
|
|
css_color col(hexpair_decode(input[0], input[1]), |
|
|
@@ -81,8 +93,7 @@ auto css_value::maybe_color_from_hex(const std::string_view &input) |
|
|
|
} |
|
|
|
|
|
|
|
constexpr static inline auto rgb_color_component_convert(const css_parser_token &tok) |
|
|
|
-> std::uint8_t |
|
|
|
{ |
|
|
|
-> std::uint8_t { |
|
|
|
std::uint8_t ret = 0; |
|
|
|
|
|
|
|
if (tok.type == css_parser_token::token_type::number_token) { |
|
|
@@ -95,7 +106,7 @@ constexpr static inline auto rgb_color_component_convert(const css_parser_token |
|
|
|
else if (dbl < 0) { |
|
|
|
dbl = 0; |
|
|
|
} |
|
|
|
ret = (std::uint8_t)(dbl / 100.0 * 255.0); |
|
|
|
ret = (std::uint8_t) (dbl / 100.0 * 255.0); |
|
|
|
} |
|
|
|
else { |
|
|
|
if (dbl > 1) { |
|
|
@@ -105,7 +116,7 @@ constexpr static inline auto rgb_color_component_convert(const css_parser_token |
|
|
|
dbl = 0; |
|
|
|
} |
|
|
|
|
|
|
|
ret = (std::uint8_t)(dbl * 255.0); |
|
|
|
ret = (std::uint8_t) (dbl * 255.0); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
@@ -113,8 +124,7 @@ constexpr static inline auto rgb_color_component_convert(const css_parser_token |
|
|
|
} |
|
|
|
|
|
|
|
constexpr static inline auto alpha_component_convert(const css_parser_token &tok) |
|
|
|
-> std::uint8_t |
|
|
|
{ |
|
|
|
-> std::uint8_t { |
|
|
|
double ret = 1.0; |
|
|
|
|
|
|
|
if (tok.type == css_parser_token::token_type::number_token) { |
|
|
@@ -141,12 +151,11 @@ constexpr static inline auto alpha_component_convert(const css_parser_token &tok |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
return (std::uint8_t)(ret * 255.0); |
|
|
|
return (std::uint8_t) (ret * 255.0); |
|
|
|
} |
|
|
|
|
|
|
|
constexpr static inline auto h_component_convert(const css_parser_token &tok) |
|
|
|
-> double |
|
|
|
{ |
|
|
|
-> double { |
|
|
|
double ret = 0.0; |
|
|
|
|
|
|
|
if (tok.type == css_parser_token::token_type::number_token) { |
|
|
@@ -162,7 +171,7 @@ constexpr static inline auto h_component_convert(const css_parser_token &tok) |
|
|
|
ret = (dbl / 100.0); |
|
|
|
} |
|
|
|
else { |
|
|
|
dbl = ((((int)dbl % 360) + 360) % 360); /* Deal with rotations */ |
|
|
|
dbl = ((((int) dbl % 360) + 360) % 360); /* Deal with rotations */ |
|
|
|
ret = dbl / 360.0; /* Normalize to 0..1 */ |
|
|
|
} |
|
|
|
} |
|
|
@@ -171,8 +180,7 @@ constexpr static inline auto h_component_convert(const css_parser_token &tok) |
|
|
|
} |
|
|
|
|
|
|
|
constexpr static inline auto sl_component_convert(const css_parser_token &tok) |
|
|
|
-> double |
|
|
|
{ |
|
|
|
-> double { |
|
|
|
double ret = 0.0; |
|
|
|
|
|
|
|
if (tok.type == css_parser_token::token_type::number_token) { |
|
|
@@ -183,8 +191,7 @@ constexpr static inline auto sl_component_convert(const css_parser_token &tok) |
|
|
|
} |
|
|
|
|
|
|
|
static inline auto hsl_to_rgb(double h, double s, double l) |
|
|
|
-> css_color |
|
|
|
{ |
|
|
|
-> css_color { |
|
|
|
css_color ret; |
|
|
|
|
|
|
|
constexpr auto hue2rgb = [](auto p, auto q, auto t) -> auto { |
|
|
@@ -201,7 +208,7 @@ static inline auto hsl_to_rgb(double h, double s, double l) |
|
|
|
return q; |
|
|
|
} |
|
|
|
if (t * 3. < 2.) { |
|
|
|
return p + (q - p) * (2.0/3.0 - t) * 6.0; |
|
|
|
return p + (q - p) * (2.0 / 3.0 - t) * 6.0; |
|
|
|
} |
|
|
|
return p; |
|
|
|
}; |
|
|
@@ -226,8 +233,7 @@ static inline auto hsl_to_rgb(double h, double s, double l) |
|
|
|
} |
|
|
|
|
|
|
|
auto css_value::maybe_color_from_function(const css_consumed_block::css_function_block &func) |
|
|
|
-> std::optional<css_value> |
|
|
|
{ |
|
|
|
-> std::optional<css_value> { |
|
|
|
|
|
|
|
if (func.as_string() == "rgb" && func.args.size() == 3) { |
|
|
|
css_color col{rgb_color_component_convert(func.args[0]->get_token_or_empty()), |
|
|
@@ -268,8 +274,7 @@ auto css_value::maybe_color_from_function(const css_consumed_block::css_function |
|
|
|
} |
|
|
|
|
|
|
|
auto css_value::maybe_dimension_from_number(const css_parser_token &tok) |
|
|
|
-> std::optional<css_value> |
|
|
|
{ |
|
|
|
-> std::optional<css_value> { |
|
|
|
if (std::holds_alternative<double>(tok.value)) { |
|
|
|
auto dbl = std::get<double>(tok.value); |
|
|
|
css_dimension dim; |
|
|
@@ -290,34 +295,33 @@ auto css_value::maybe_dimension_from_number(const css_parser_token &tok) |
|
|
|
} |
|
|
|
|
|
|
|
constexpr const auto display_names_map = frozen::make_unordered_map<frozen::string, css_display_value>({ |
|
|
|
{"hidden", css_display_value::DISPLAY_HIDDEN}, |
|
|
|
{"none", css_display_value::DISPLAY_HIDDEN}, |
|
|
|
{"inline", css_display_value::DISPLAY_NORMAL}, |
|
|
|
{"block", css_display_value::DISPLAY_NORMAL}, |
|
|
|
{"content", css_display_value::DISPLAY_NORMAL}, |
|
|
|
{"flex", css_display_value::DISPLAY_NORMAL}, |
|
|
|
{"grid" , css_display_value::DISPLAY_NORMAL}, |
|
|
|
{"inline-block", css_display_value::DISPLAY_NORMAL}, |
|
|
|
{"inline-flex", css_display_value::DISPLAY_NORMAL}, |
|
|
|
{"inline-grid", css_display_value::DISPLAY_NORMAL}, |
|
|
|
{"inline-table", css_display_value::DISPLAY_NORMAL}, |
|
|
|
{"list-item", css_display_value::DISPLAY_NORMAL}, |
|
|
|
{"run-in", css_display_value::DISPLAY_NORMAL}, |
|
|
|
{"table", css_display_value::DISPLAY_NORMAL}, |
|
|
|
{"table-caption", css_display_value::DISPLAY_NORMAL}, |
|
|
|
{"hidden", css_display_value::DISPLAY_HIDDEN}, |
|
|
|
{"none", css_display_value::DISPLAY_HIDDEN}, |
|
|
|
{"inline", css_display_value::DISPLAY_NORMAL}, |
|
|
|
{"block", css_display_value::DISPLAY_NORMAL}, |
|
|
|
{"content", css_display_value::DISPLAY_NORMAL}, |
|
|
|
{"flex", css_display_value::DISPLAY_NORMAL}, |
|
|
|
{"grid", css_display_value::DISPLAY_NORMAL}, |
|
|
|
{"inline-block", css_display_value::DISPLAY_NORMAL}, |
|
|
|
{"inline-flex", css_display_value::DISPLAY_NORMAL}, |
|
|
|
{"inline-grid", css_display_value::DISPLAY_NORMAL}, |
|
|
|
{"inline-table", css_display_value::DISPLAY_NORMAL}, |
|
|
|
{"list-item", css_display_value::DISPLAY_NORMAL}, |
|
|
|
{"run-in", css_display_value::DISPLAY_NORMAL}, |
|
|
|
{"table", css_display_value::DISPLAY_NORMAL}, |
|
|
|
{"table-caption", css_display_value::DISPLAY_NORMAL}, |
|
|
|
{"table-column-group", css_display_value::DISPLAY_NORMAL}, |
|
|
|
{"table-header-group", css_display_value::DISPLAY_NORMAL}, |
|
|
|
{"table-footer-group", css_display_value::DISPLAY_NORMAL}, |
|
|
|
{"table-row-group", css_display_value::DISPLAY_NORMAL}, |
|
|
|
{"table-cell", css_display_value::DISPLAY_NORMAL}, |
|
|
|
{"table-column", css_display_value::DISPLAY_NORMAL}, |
|
|
|
{"table-row", css_display_value::DISPLAY_NORMAL}, |
|
|
|
{"initial", css_display_value::DISPLAY_NORMAL}, |
|
|
|
{"table-row-group", css_display_value::DISPLAY_NORMAL}, |
|
|
|
{"table-cell", css_display_value::DISPLAY_NORMAL}, |
|
|
|
{"table-column", css_display_value::DISPLAY_NORMAL}, |
|
|
|
{"table-row", css_display_value::DISPLAY_NORMAL}, |
|
|
|
{"initial", css_display_value::DISPLAY_NORMAL}, |
|
|
|
}); |
|
|
|
|
|
|
|
auto css_value::maybe_display_from_string(const std::string_view &input) |
|
|
|
-> std::optional<css_value> |
|
|
|
{ |
|
|
|
-> std::optional<css_value> { |
|
|
|
auto f = display_names_map.find(input); |
|
|
|
|
|
|
|
if (f != display_names_map.end()) { |
|
|
@@ -328,18 +332,17 @@ auto css_value::maybe_display_from_string(const std::string_view &input) |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
auto css_value::debug_str() const -> std::string |
|
|
|
{ |
|
|
|
auto css_value::debug_str() const -> std::string { |
|
|
|
std::string ret; |
|
|
|
|
|
|
|
std::visit([&](const auto& arg) { |
|
|
|
std::visit([&](const auto &arg) { |
|
|
|
using T = std::decay_t<decltype(arg)>; |
|
|
|
|
|
|
|
if constexpr (std::is_same_v<T, css_color>) { |
|
|
|
ret += "color: r=" + std::to_string(arg.r) + |
|
|
|
"; g=" + std::to_string(arg.g) + |
|
|
|
"; b=" + std::to_string(arg.b) + |
|
|
|
"; a=" + std::to_string(arg.alpha); |
|
|
|
"; g=" + std::to_string(arg.g) + |
|
|
|
"; b=" + std::to_string(arg.b) + |
|
|
|
"; a=" + std::to_string(arg.alpha); |
|
|
|
} |
|
|
|
else if constexpr (std::is_same_v<T, double>) { |
|
|
|
ret += "size: " + std::to_string(arg); |
|
|
@@ -365,4 +368,24 @@ auto css_value::debug_str() const -> std::string |
|
|
|
return ret; |
|
|
|
} |
|
|
|
|
|
|
|
TEST_SUITE("css values") { |
|
|
|
TEST_CASE("css hex colors") { |
|
|
|
const std::pair<const char*, css_color> hex_tests[] = { |
|
|
|
{"000", css_color(0, 0, 0)}, |
|
|
|
{"000000", css_color(0, 0, 0)}, |
|
|
|
{"f00", css_color(255, 0, 0)}, |
|
|
|
{"FEDCBA", css_color(254, 220, 186)}, |
|
|
|
{"234", css_color(34, 51, 68)}, |
|
|
|
}; |
|
|
|
|
|
|
|
for (const auto &p : hex_tests) { |
|
|
|
auto col_parsed = css_value::maybe_color_from_hex(p.first); |
|
|
|
//CHECK_UNARY(col_parsed); |
|
|
|
//CHECK_UNARY(col_parsed.value().to_color()); |
|
|
|
auto final_col = col_parsed.value().to_color().value(); |
|
|
|
CHECK(final_col == p.second); |
|
|
|
} |
|
|
|
} |
|
|
|
}; |
|
|
|
|
|
|
|
} |