]> source.dussan.org Git - rspamd.git/commitdiff
[Project] Css: Add rules processing functions and tests
authorVsevolod Stakhov <vsevolod@highsecure.ru>
Tue, 20 Apr 2021 11:03:14 +0000 (12:03 +0100)
committerVsevolod Stakhov <vsevolod@highsecure.ru>
Tue, 20 Apr 2021 11:03:14 +0000 (12:03 +0100)
src/libserver/css/css_parser.cxx
src/libserver/css/css_parser.hxx
src/libserver/css/css_property.hxx
src/libserver/css/css_rule.cxx
src/libserver/css/css_rule.hxx

index 8d37a26a82b2d74b27f5bfc068838d768380e36b..49b42021c83dc728f7d509d44a6fbe5eeb3e42a8 100644 (file)
@@ -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<css_consumed_block> consume_css_blocks(const std::string_view &sv);
+       /*
+        * Process a single css rule
+        */
+       std::unique_ptr<css_consumed_block> consume_css_rule(const std::string_view &sv);
        bool consume_input(const std::string_view &sv);
 
        auto get_object_maybe(void) -> tl::expected<std::unique_ptr<css_style_sheet>, css_parse_error> {
@@ -569,6 +576,38 @@ css_parser::consume_css_blocks(const std::string_view &sv) -> std::unique_ptr<cs
        return consumed_blocks;
 }
 
+auto
+css_parser::consume_css_rule(const std::string_view &sv) -> std::unique_ptr<css_consumed_block>
+{
+       tokeniser = std::make_unique<css_tokeniser>(pool, sv);
+       auto ret = true;
+
+       auto rule_block =
+                       std::make_unique<css_consumed_block>(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
index af79abb68f423ab67805250be1e0549f105befdb..ec6d5159a267c5194b8189e093dd4d7e710af39b 100644 (file)
@@ -192,9 +192,19 @@ auto parse_css(rspamd_mempool_t *pool, const std::string_view &st,
                                          css_style_sheet *other)
        -> tl::expected<std::unique_ptr<css_style_sheet>, 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
index 973000b39a7e0b229b946e822039a17513d21e0d..82ef9808cc120ba07fa23d75d43989bcb99b144c 100644 (file)
@@ -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<css_property,css_parse_error> from_token(
                        const css_parser_token &tok);
 
index c301ebdca2309b9b6fdab2f4fd919a47a66eba29..3afe522e667d5d2262ee225d97492c06a63fa34c 100644 (file)
@@ -18,6 +18,9 @@
 #include "css.hxx"
 #include <limits>
 
+#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<css_value>
-{
+-> std::optional<css_value> {
        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<std::pair<const char *, std::vector<css_property>>> 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
index ec9ccde4285541bca9a52097c281963f3883dd04..adf01437f81acd4f9ac6e3235c87b8802cc2eb4e 100644 (file)
@@ -76,6 +76,7 @@ public:
 
 namespace rspamd::css {
 
+
 class css_declarations_block {
 public:
        using rule_shared_ptr = std::shared_ptr<css_rule>;
@@ -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<rule_shared_ptr, rule_shared_hash, rule_shared_eq> rules;