From 25661e2ab288e5f5d9291baf9b8b43f68735d4c9 Mon Sep 17 00:00:00 2001 From: Vsevolod Stakhov Date: Tue, 20 Apr 2021 12:03:14 +0100 Subject: [PATCH] [Project] Css: Add rules processing functions and tests --- src/libserver/css/css_parser.cxx | 65 ++++++++++++++++++++++++++++++ src/libserver/css/css_parser.hxx | 10 +++++ src/libserver/css/css_property.hxx | 2 + src/libserver/css/css_rule.cxx | 49 +++++++++++++++++----- src/libserver/css/css_rule.hxx | 5 ++- 5 files changed, 119 insertions(+), 12 deletions(-) diff --git a/src/libserver/css/css_parser.cxx b/src/libserver/css/css_parser.cxx index 8d37a26a8..49b42021c 100644 --- a/src/libserver/css/css_parser.cxx +++ b/src/libserver/css/css_parser.cxx @@ -159,7 +159,14 @@ public: explicit css_parser(css_style_sheet *existing, rspamd_mempool_t *pool) : style_object(existing), pool(pool) {} + /* + * Process input css blocks + */ std::unique_ptr consume_css_blocks(const std::string_view &sv); + /* + * Process a single css rule + */ + std::unique_ptr consume_css_rule(const std::string_view &sv); bool consume_input(const std::string_view &sv); auto get_object_maybe(void) -> tl::expected, css_parse_error> { @@ -569,6 +576,38 @@ css_parser::consume_css_blocks(const std::string_view &sv) -> std::unique_ptr std::unique_ptr +{ + tokeniser = std::make_unique(pool, sv); + auto ret = true; + + auto rule_block = + std::make_unique(css_consumed_block::parser_tag_type::css_simple_block); + + while (!eof && ret) { + auto next_token = tokeniser->next_token(); + + switch (next_token.type) { + case css_parser_token::token_type::eof_token: + eof = true; + break; + case css_parser_token::token_type::whitespace_token: + /* Ignore whitespaces */ + break; + default: + tokeniser->pushback_token(std::move(next_token)); + ret = component_value_consumer(rule_block); + break; + } + + } + + tokeniser.reset(nullptr); /* No longer needed */ + + return rule_block; +} + bool css_parser::consume_input(const std::string_view &sv) { auto &&consumed_blocks = consume_css_blocks(sv); @@ -702,6 +741,32 @@ get_selectors_parser_functor(rspamd_mempool_t *pool, }; } +auto +get_rules_parser_functor(rspamd_mempool_t *pool, + const std::string_view &st) -> blocks_gen_functor +{ + css_parser parser(pool); + + auto &&consumed_blocks = parser.consume_css_rule(st); + const auto &rules = consumed_blocks->get_blocks_or_empty(); + + auto cur = rules.begin(); + auto last = rules.end(); + + return [cur, consumed_blocks = std::move(consumed_blocks), last](void) mutable + -> const css_consumed_block & { + if (cur != last) { + const auto &ret = (*cur); + + ++cur; + + return *ret; + } + + return css_parser_eof_block; + }; +} + /* * Wrapper for the parser diff --git a/src/libserver/css/css_parser.hxx b/src/libserver/css/css_parser.hxx index af79abb68..ec6d5159a 100644 --- a/src/libserver/css/css_parser.hxx +++ b/src/libserver/css/css_parser.hxx @@ -192,9 +192,19 @@ auto parse_css(rspamd_mempool_t *pool, const std::string_view &st, css_style_sheet *other) -> tl::expected, css_parse_error>; +/* + * Creates a functor to consume css selectors sequence + */ auto get_selectors_parser_functor(rspamd_mempool_t *pool, const std::string_view &st) -> blocks_gen_functor; +/* + * Creates a functor to process a rule definition (e.g. from embedded style tag for + * an element) + */ +auto get_rules_parser_functor(rspamd_mempool_t *pool, + const std::string_view &st) -> blocks_gen_functor; + } #endif //RSPAMD_CSS_PARSER_HXX diff --git a/src/libserver/css/css_property.hxx b/src/libserver/css/css_property.hxx index 973000b39..82ef9808c 100644 --- a/src/libserver/css/css_property.hxx +++ b/src/libserver/css/css_property.hxx @@ -54,6 +54,8 @@ struct alignas(int) css_property { css_property_type type; css_property_flag flag; + css_property(css_property_type t, css_property_flag fl = css_property_flag::FLAG_NORMAL) : + type(t), flag(fl) {} static tl::expected from_token( const css_parser_token &tok); diff --git a/src/libserver/css/css_rule.cxx b/src/libserver/css/css_rule.cxx index c301ebdca..3afe522e6 100644 --- a/src/libserver/css/css_rule.cxx +++ b/src/libserver/css/css_rule.cxx @@ -18,6 +18,9 @@ #include "css.hxx" #include +#define DOCTEST_CONFIG_IMPLEMENTATION_IN_DLL +#include "doctest/doctest.h" + namespace rspamd::css { /* Class methods */ @@ -111,8 +114,7 @@ namespace rspamd::css { static auto allowed_property_value(const css_property &prop, const css_consumed_block &parser_block) - -> std::optional -{ +-> std::optional { if (prop.is_color()) { if (parser_block.is_token()) { /* A single token */ @@ -170,8 +172,7 @@ allowed_property_value(const css_property &prop, const css_consumed_block &parse auto process_declaration_tokens(rspamd_mempool_t *pool, blocks_gen_functor &&next_block_functor) - -> css_declarations_block_ptr -{ +-> css_declarations_block_ptr { css_declarations_block_ptr ret; bool can_continue = true; css_property cur_property{css_property_type::PROPERTY_NYI, @@ -315,8 +316,7 @@ auto process_declaration_tokens(rspamd_mempool_t *pool, auto css_declarations_block::merge_block(const css_declarations_block &other, merge_type how) - -> void -{ +-> void { const auto &other_rules = other.get_rules(); for (const auto &rule : other_rules) { @@ -324,7 +324,7 @@ css_declarations_block::merge_block(const css_declarations_block &other, merge_t if (found_it != rules.end()) { /* Duplicate, need to merge */ - switch(how) { + switch (how) { case merge_type::merge_override: /* Override */ rules.insert(rule); @@ -345,9 +345,38 @@ css_declarations_block::merge_block(const css_declarations_block &other, merge_t } } -void css_rule::add_value(const css_value &value) -{ +void css_rule::add_value(const css_value &value) { values.push_back(value); } -} \ No newline at end of file +TEST_SUITE("css rules") { + TEST_CASE("simple css rules") { + const std::vector>> cases{ + { + "font-size:12.0pt;line-height:115%", + {css_property(css_property_type::PROPERTY_FONT_SIZE)} + }, + { + "font-size:12.0pt;display:none", + {css_property(css_property_type::PROPERTY_FONT_SIZE), + css_property(css_property_type::PROPERTY_DISPLAY)} + } + }; + + auto *pool = rspamd_mempool_new(rspamd_mempool_suggest_size(), + "css", 0); + + for (const auto &c : cases) { + auto res = process_declaration_tokens(pool, + get_rules_parser_functor(pool, c.first)); + + CHECK(res.get() != nullptr); + + for (auto i = 0; i < c.second.size(); i ++) { + CHECK(res->has_property(c.second[i])); + } + } + } +} + +} // namespace rspamd::css \ No newline at end of file diff --git a/src/libserver/css/css_rule.hxx b/src/libserver/css/css_rule.hxx index ec9ccde42..adf01437f 100644 --- a/src/libserver/css/css_rule.hxx +++ b/src/libserver/css/css_rule.hxx @@ -76,6 +76,7 @@ public: namespace rspamd::css { + class css_declarations_block { public: using rule_shared_ptr = std::shared_ptr; @@ -95,8 +96,8 @@ public: return rules; } - auto has_rule(const css_rule &rule) const -> bool { - return (rules.find(rule) != rules.end()); + auto has_property(const css_property &prop) const -> bool { + return (rules.find(css_rule{prop}) != rules.end()); } private: robin_hood::unordered_flat_set rules; -- 2.39.5