commit 25661e2: [Project] Css: Add rules processing functions and tests

Vsevolod Stakhov vsevolod at highsecure.ru
Tue Apr 20 11:07:04 UTC 2021


Author: Vsevolod Stakhov
Date: 2021-04-20 12:03:14 +0100
URL: https://github.com/rspamd/rspamd/commit/25661e2ab288e5f5d9291baf9b8b43f68735d4c9 (HEAD -> master)

[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<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
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<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
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<css_property,css_parse_error> 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 <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
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<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;


More information about the Commits mailing list