commit 162b69d: [Project] Css: Implement styles merging

Vsevolod Stakhov vsevolod at highsecure.ru
Mon Mar 29 14:21:18 UTC 2021


Author: Vsevolod Stakhov
Date: 2021-03-29 15:17:09 +0100
URL: https://github.com/rspamd/rspamd/commit/162b69d568210372475ca79bcefba8c070875b05 (HEAD -> master)

[Project] Css: Implement styles merging

---
 src/libserver/css/css.cxx        | 17 ++++++++++-----
 src/libserver/css/css.h          | 10 +++++----
 src/libserver/css/css_parser.cxx | 45 +++++++++++++++++++++++++++++++++-------
 src/libserver/css/css_parser.hxx |  8 +++++--
 src/libserver/html.c             |  3 ++-
 src/libserver/html.h             |  1 +
 6 files changed, 65 insertions(+), 19 deletions(-)

diff --git a/src/libserver/css/css.cxx b/src/libserver/css/css.cxx
index 17ec165fc..033ecdc22 100644
--- a/src/libserver/css/css.cxx
+++ b/src/libserver/css/css.cxx
@@ -32,19 +32,26 @@ rspamd_css_dtor(void *p)
 	delete style;
 }
 
-rspamd_css
+rspamd_css_ptr
 rspamd_css_parse_style(rspamd_mempool_t *pool, const guchar *begin, gsize len,
-						GError **err)
+					   rspamd_css_ptr existing_style,
+					   GError **err)
 {
-	auto parse_res = rspamd::css::parse_css(pool, {(const char* )begin, len});
+	auto parse_res = rspamd::css::parse_css(pool, {(const char* )begin, len},
+			reinterpret_cast<rspamd::css::css_style_sheet*>(existing_style));
 
 	if (parse_res.has_value()) {
 		/*
 		 * Detach style pointer from the unique_ptr as it will be managed by
 		 * C memory pool
 		 */
-		auto *detached_style = reinterpret_cast<rspamd_css>(parse_res.value().release());
-		rspamd_mempool_add_destructor(pool, rspamd_css_dtor, (void *)detached_style);
+		auto *detached_style = reinterpret_cast<rspamd_css_ptr>(parse_res.value().release());
+
+		/* We attach dtor merely if the existing style is not null */
+		if (!existing_style) {
+			rspamd_mempool_add_destructor(pool, rspamd_css_dtor, (void *) detached_style);
+		}
+
 		return detached_style;
 	}
 	else {
diff --git a/src/libserver/css/css.h b/src/libserver/css/css.h
index 78f24a549..1dabf00b8 100644
--- a/src/libserver/css/css.h
+++ b/src/libserver/css/css.h
@@ -23,11 +23,13 @@
 #ifdef  __cplusplus
 extern "C" {
 #endif
-typedef void * rspamd_css;
+typedef void * rspamd_css_ptr;
 
-rspamd_css rspamd_css_parse_style (rspamd_mempool_t *pool,
-								   const guchar *begin,
-								   gsize len, GError **err);
+rspamd_css_ptr rspamd_css_parse_style (rspamd_mempool_t *pool,
+									   const guchar *begin,
+									   gsize len,
+									   rspamd_css_ptr existing_style,
+									   GError **err);
 
 /*
  * Unescape css
diff --git a/src/libserver/css/css_parser.cxx b/src/libserver/css/css_parser.cxx
index 104792693..8d37a26a8 100644
--- a/src/libserver/css/css_parser.cxx
+++ b/src/libserver/css/css_parser.cxx
@@ -147,7 +147,17 @@ auto css_consumed_block::debug_str(void) -> std::string {
 class css_parser {
 public:
 	css_parser(void) = delete; /* Require mempool to be set for logging */
-	explicit css_parser(rspamd_mempool_t *pool) : pool (pool) {}
+	explicit css_parser(rspamd_mempool_t *pool) : pool (pool) {
+		/* Int this case we need to remove style in case of parser error */
+		owned_style = true;
+	}
+
+	/*
+	 * This constructor captures existing via unique_ptr, but it does not
+	 * destruct it on errors (we assume that it is owned somewhere else)
+	 */
+	explicit css_parser(css_style_sheet *existing, rspamd_mempool_t *pool) :
+			style_object(existing), pool(pool) {}
 
 	std::unique_ptr<css_consumed_block> consume_css_blocks(const std::string_view &sv);
 	bool consume_input(const std::string_view &sv);
@@ -163,6 +173,13 @@ public:
 	/* Helper parser methods */
 	bool need_unescape(const std::string_view &sv);
 
+	~css_parser() {
+		if (!owned_style && style_object) {
+			/* Avoid double free */
+			(void)style_object.release();
+		}
+	}
+
 private:
 	std::unique_ptr<css_style_sheet> style_object;
 	std::unique_ptr<css_tokeniser> tokeniser;
@@ -173,6 +190,7 @@ private:
 	int rec_level = 0;
 	const int max_rec = 20;
 	bool eof = false;
+	bool owned_style = false;
 
 	/* Consumers */
 	auto component_value_consumer(std::unique_ptr<css_consumed_block> &top) -> bool;
@@ -560,7 +578,9 @@ bool css_parser::consume_input(const std::string_view &sv)
 		return false;
 	}
 
-	style_object = std::make_unique<css_style_sheet>(pool);
+	if (!style_object) {
+		style_object = std::make_unique<css_style_sheet>(pool);
+	}
 
 	for (auto &&rule : rules) {
 		/*
@@ -686,10 +706,11 @@ get_selectors_parser_functor(rspamd_mempool_t *pool,
 /*
  * Wrapper for the parser
  */
-auto parse_css(rspamd_mempool_t *pool, const std::string_view &st) ->
-		tl::expected<std::unique_ptr<css_style_sheet>, css_parse_error>
+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>
 {
-	css_parser parser(pool);
+	css_parser parser(other, pool);
 	std::string_view processed_input;
 
 	if (parser.need_unescape(st)) {
@@ -712,7 +733,7 @@ auto parse_css(rspamd_mempool_t *pool, const std::string_view &st) ->
 	}
 
 	return tl::make_unexpected(css_parse_error{css_parse_error_type::PARSE_ERROR_INVALID_SYNTAX,
-											"cannot parse input"});
+											   "cannot parse input"});
 }
 
 TEST_SUITE("css parser") {
@@ -752,9 +773,19 @@ TEST_SUITE("css parser") {
 		rspamd_mempool_t *pool = rspamd_mempool_new(rspamd_mempool_suggest_size(),
 				"css", 0);
 		for (const auto &c : cases) {
-			CHECK_UNARY(parse_css(pool, c));
+			CHECK(parse_css(pool, c, nullptr).value().get() != nullptr);
+		}
+
+		/* We now merge all styles together */
+		css_style_sheet *merged = nullptr;
+		for (const auto &c : cases) {
+			auto ret = parse_css(pool, c, merged);
+			/* Avoid destruction as we are using this to interoperate with C */
+			merged = ret->release();
 		}
 
+		CHECK(merged != nullptr);
+
 		rspamd_mempool_delete(pool);
 	}
 }
diff --git a/src/libserver/css/css_parser.hxx b/src/libserver/css/css_parser.hxx
index 4b5598fa9..af79abb68 100644
--- a/src/libserver/css/css_parser.hxx
+++ b/src/libserver/css/css_parser.hxx
@@ -185,8 +185,12 @@ extern const css_consumed_block css_parser_eof_block;
 using blocks_gen_functor = fu2::unique_function<const css_consumed_block &(void)>;
 
 class css_style_sheet;
-auto parse_css(rspamd_mempool_t *pool, const std::string_view &st) ->
-	tl::expected<std::unique_ptr<css_style_sheet>, css_parse_error>;
+/*
+ * Update the existing stylesheet with another stylesheet
+ */
+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>;
 
 auto get_selectors_parser_functor(rspamd_mempool_t *pool,
 								  const std::string_view &st) -> blocks_gen_functor;
diff --git a/src/libserver/html.c b/src/libserver/html.c
index b56f3ef32..8dc8a8dac 100644
--- a/src/libserver/html.c
+++ b/src/libserver/html.c
@@ -3137,7 +3137,8 @@ rspamd_html_process_part_full (rspamd_mempool_t *pool,
 
 				if (allow_css) {
 					GError *err = NULL;
-					(void)rspamd_css_parse_style (pool, p, end_style, &err);
+					hc->css_style = rspamd_css_parse_style (pool, p, end_style, hc->css_style,
+							&err);
 
 					if (err) {
 						msg_info_pool ("cannot parse css: %e", err);
diff --git a/src/libserver/html.h b/src/libserver/html.h
index f8a5e18e4..83a2a7ee7 100644
--- a/src/libserver/html.h
+++ b/src/libserver/html.h
@@ -132,6 +132,7 @@ struct html_content {
 	GPtrArray *images;
 	GPtrArray *blocks;
 	GByteArray *parsed;
+	void *css_style;
 };
 
 /*


More information about the Commits mailing list