commit 818f37f: [Rework] Redesign html blocks propagation logic

Vsevolod Stakhov vsevolod at highsecure.ru
Tue Jul 20 12:00:04 UTC 2021


Author: Vsevolod Stakhov
Date: 2021-07-20 12:56:42 +0100
URL: https://github.com/rspamd/rspamd/commit/818f37f646fb536047f4408390007eef000a33a3 (HEAD -> master)

[Rework] Redesign html blocks propagation logic

---
 src/libserver/css/css_rule.cxx    |   6 +-
 src/libserver/html/html.cxx       |  14 +--
 src/libserver/html/html_block.hxx | 179 ++++++++++++++++++++++----------------
 src/libserver/html/html_tests.cxx |   3 +-
 src/lua/lua_html.cxx              |   6 +-
 5 files changed, 123 insertions(+), 85 deletions(-)

diff --git a/src/libserver/css/css_rule.cxx b/src/libserver/css/css_rule.cxx
index 17301e724..3a93d97d4 100644
--- a/src/libserver/css/css_rule.cxx
+++ b/src/libserver/css/css_rule.cxx
@@ -456,7 +456,7 @@ css_declarations_block::compile_to_block(rspamd_mempool_t *pool) const -> rspamd
 	}
 
 	/* Optional properties */
-	if (!(block->mask & rspamd::html::html_block::fg_color_mask) && font_rule) {
+	if (!(block->fg_color_mask) && font_rule) {
 		auto &vals = font_rule->get_values();
 
 		for (const auto &val : vals) {
@@ -468,7 +468,7 @@ css_declarations_block::compile_to_block(rspamd_mempool_t *pool) const -> rspamd
 		}
 	}
 
-	if (!(block->mask & rspamd::html::html_block::font_size_mask) && font_rule) {
+	if (!(block->font_mask) && font_rule) {
 		auto &vals = font_rule->get_values();
 
 		for (const auto &val : vals) {
@@ -480,7 +480,7 @@ css_declarations_block::compile_to_block(rspamd_mempool_t *pool) const -> rspamd
 		}
 	}
 
-	if (!(block->mask & rspamd::html::html_block::bg_color_mask) && background_rule) {
+	if (!(block->bg_color_mask) && background_rule) {
 		auto &vals = background_rule->get_values();
 
 		for (const auto &val : vals) {
diff --git a/src/libserver/html/html.cxx b/src/libserver/html/html.cxx
index c5dea793a..0925a7695 100644
--- a/src/libserver/html/html.cxx
+++ b/src/libserver/html/html.cxx
@@ -1851,7 +1851,7 @@ html_process_input(rspamd_mempool_t *pool,
 
 			if (css_block) {
 				if (tag->block) {
-					tag->block->propagate_block(*css_block);
+					tag->block->set_block(*css_block);
 				}
 				else {
 					tag->block = css_block;
@@ -1862,16 +1862,20 @@ html_process_input(rspamd_mempool_t *pool,
 			if (!tag->block->has_display()) {
 				/* If we have no display field, we can check it by tag */
 				if (tag->flags & CM_HEAD) {
-					tag->block->set_display(css::css_display_value::DISPLAY_HIDDEN);
+					tag->block->set_display(css::css_display_value::DISPLAY_HIDDEN,
+							html_block::set);
 				}
 				else if (tag->flags & (CM_BLOCK | CM_TABLE)) {
-					tag->block->set_display_implicit(css::css_display_value::DISPLAY_BLOCK);
+					tag->block->set_display(css::css_display_value::DISPLAY_BLOCK,
+							html_block::implicit);
 				}
 				else if (tag->flags & CM_ROW) {
-					tag->block->set_display_implicit(css::css_display_value::DISPLAY_TABLE_ROW);
+					tag->block->set_display(css::css_display_value::DISPLAY_TABLE_ROW,
+							html_block::implicit);
 				}
 				else {
-					tag->block->set_display_implicit(css::css_display_value::DISPLAY_INLINE);
+					tag->block->set_display(css::css_display_value::DISPLAY_INLINE,
+							html_block::implicit);
 				}
 			}
 
diff --git a/src/libserver/html/html_block.hxx b/src/libserver/html/html_block.hxx
index 59478fd6d..7e3edeb13 100644
--- a/src/libserver/html/html_block.hxx
+++ b/src/libserver/html/html_block.hxx
@@ -30,29 +30,34 @@ struct html_block {
 	rspamd::css::css_color bg_color;
 	std::int16_t height;
 	std::int16_t width;
-	std::uint16_t mask;
 	rspamd::css::css_display_value display;
 	std::int8_t font_size;
 
-	constexpr static const auto fg_color_mask = 0x1 << 0;
-	constexpr static const auto bg_color_mask = 0x1 << 1;
-	constexpr static const auto height_mask = 0x1 << 2;
-	constexpr static const auto width_mask = 0x1 << 3;
-	constexpr static const auto display_mask = 0x1 << 4;
-	constexpr static const auto font_size_mask = 0x1 << 5;
-	constexpr static const auto invisible_flag = 0x1 << 6;
-	constexpr static const auto transparent_flag = 0x1 << 7;
+	unsigned fg_color_mask : 2;
+	unsigned bg_color_mask : 2;
+	unsigned height_mask : 2;
+	unsigned width_mask : 2;
+	unsigned font_mask : 2;
+	unsigned display_mask : 2;
+	unsigned visibility_mask : 2;
+
+	constexpr static const auto unset = 0;
+	constexpr static const auto inherited = 1;
+	constexpr static const auto implicit = 1;
+	constexpr static const auto set = 3;
+	constexpr static const auto invisible_flag = 1;
+	constexpr static const auto transparent_flag = 2;
 
 	/* Helpers to set mask when setting the elements */
-	auto set_fgcolor(const rspamd::css::css_color &c) -> void {
+	auto set_fgcolor(const rspamd::css::css_color &c, int how = set) -> void {
 		fg_color = c;
-		mask |= fg_color_mask;
+		fg_color_mask = how;
 	}
-	auto set_bgcolor(const rspamd::css::css_color &c) -> void {
+	auto set_bgcolor(const rspamd::css::css_color &c, int how = set) -> void {
 		bg_color = c;
-		mask |= bg_color_mask;
+		bg_color_mask = how;
 	}
-	auto set_height(float h, bool is_percent = false) -> void {
+	auto set_height(float h, bool is_percent = false, int how = set) -> void {
 		h = is_percent ? (-h) : h;
 		if (h < INT16_MIN) {
 			/* Negative numbers encode percents... */
@@ -64,9 +69,9 @@ struct html_block {
 		else {
 			height = h;
 		}
-		mask |= height_mask;
+		height_mask = how;
 	}
-	auto set_width(float w, bool is_percent = false) -> void {
+	auto set_width(float w, bool is_percent = false, int how = set) -> void {
 		w = is_percent ? (-w) : w;
 		if (w < INT16_MIN) {
 			width = INT16_MIN;
@@ -77,26 +82,22 @@ struct html_block {
 		else {
 			width = w;
 		}
-		mask |= width_mask;
+		width_mask = how;
 	}
-	auto set_display(bool v) -> void  {
+	auto set_display(bool v, int how = set) -> void  {
 		if (v) {
 			display = rspamd::css::css_display_value::DISPLAY_INLINE;
 		}
 		else {
 			display = rspamd::css::css_display_value::DISPLAY_HIDDEN;
 		}
-		mask |= display_mask;
-	}
-	auto set_display(rspamd::css::css_display_value v) -> void  {
-		display = v;
-		mask |= display_mask;
+		display_mask = how;
 	}
-	/* Set display, do not set mask */
-	auto set_display_implicit(rspamd::css::css_display_value v) -> void  {
+	auto set_display(rspamd::css::css_display_value v, int how = set) -> void  {
 		display = v;
+		display_mask = implicit ? inherited : set;
 	}
-	auto set_font_size(float fs, bool is_percent = false) -> void  {
+	auto set_font_size(float fs, bool is_percent = false, int how = set) -> void  {
 		fs = is_percent ? (-fs) : fs;
 		if (fs < INT8_MIN) {
 			font_size = -100;
@@ -107,7 +108,7 @@ struct html_block {
 		else {
 			font_size = fs;
 		}
-		mask |= font_size_mask;
+		font_mask = implicit ? inherited : set;
 	}
 
 	/**
@@ -116,21 +117,19 @@ struct html_block {
 	 * @return
 	 */
 	auto propagate_block(const html_block &other) -> void {
-		auto simple_prop = [&](auto mask_val, auto &our_val, auto other_val) constexpr -> void {
-			if (!(mask & mask_val) && (other.mask & mask_val)) {
+		auto simple_prop = [](auto mask_val, auto other_mask, auto &our_val,
+				auto other_val) constexpr -> int {
+			if (other_mask && other_mask > mask_val) {
 				our_val = other_val;
-				mask |= mask_val;
+				mask_val = inherited;
 			}
+
+			return mask_val;
 		};
-		simple_prop(fg_color_mask, fg_color, other.fg_color);
-		simple_prop(bg_color_mask, bg_color, other.bg_color);
 
-		if (other.has_display()) {
-			simple_prop(display_mask, display, other.display);
-			if (!other.is_visible()) {
-				mask |= other.mask & (transparent_flag | invisible_flag);
-			}
-		}
+		fg_color_mask = simple_prop(fg_color_mask, other.fg_color_mask, fg_color, other.fg_color);
+		bg_color_mask = simple_prop(bg_color_mask, other.bg_color_mask, bg_color, other.bg_color);
+		display_mask = simple_prop(display_mask, other.display_mask, display, other.display);
 
 		/* Sizes are very different
 		 * We can have multiple cases:
@@ -140,11 +139,12 @@ struct html_block {
 		 * 4) Parent size is > 0 and our size is < 0 - multiply parent by abs(ours)
 		 * 5) Parent size is undefined and our size is < 0 - tricky stuff, assume some defaults
 		 */
-		auto size_prop = [&](auto mask_val, auto &our_val, auto other_val, auto default_val) constexpr -> void {
-			if ((mask & mask_val)) {
+		auto size_prop = [](auto mask_val, auto other_mask, auto &our_val,
+				auto other_val, auto default_val) constexpr -> int {
+			if (mask_val) {
 				/* We have our value */
 				if (our_val < 0) {
-					if (other.mask & mask_val) {
+					if (other_mask > 0) {
 						if (other_val >= 0) {
 							our_val = other_val * (-our_val / 100.0);
 						}
@@ -157,35 +157,62 @@ struct html_block {
 						our_val = default_val * (-our_val / 100.0);
 					}
 				}
-				/* We do nothing as we have our own absolute value */
+				else if (other_mask && other_mask > mask_val) {
+					our_val = other_val;
+					mask_val = inherited;
+				}
 			}
 			else {
 				/* We propagate parent if defined */
-				if (other.mask & mask_val) {
+				if (other_mask && other_mask > mask_val) {
 					our_val = other_val;
-					mask |= mask_val;
+					mask_val = inherited;
 				}
 				/* Otherwise do nothing */
 			}
+
+			return mask_val;
+		};
+
+		height_mask = size_prop(height_mask, other.height_mask, height, other.height, 800);
+		width_mask = size_prop(width_mask, other.width_mask, width, other.width, 1024);
+		font_mask = size_prop(font_mask, other.font_mask, font_size, other.font_size, 1024);
+	}
+
+	/*
+	 * Set block overriding all inherited values
+	 */
+	auto set_block(const html_block &other) -> void {
+		constexpr auto set_value = [](auto mask_val, auto other_mask, auto &our_val,
+									   auto other_val) constexpr -> int {
+			if (other_mask && mask_val != set) {
+				our_val = other_val;
+				mask_val = other_mask;
+			}
+
+			return mask_val;
 		};
 
-		size_prop(height_mask, height, other.height, 800);
-		size_prop(width_mask, width, other.width, 1024);
-		size_prop(font_size_mask, font_size, other.font_size, 1024);
+		fg_color_mask = set_value(fg_color_mask, other.fg_color_mask, fg_color, other.fg_color);
+		bg_color_mask = set_value(bg_color_mask, other.bg_color_mask, bg_color, other.bg_color);
+		display_mask = set_value(display_mask, other.display_mask, display, other.display);
+		height_mask = set_value(height_mask, other.height_mask, height, other.height);
+		width_mask = set_value(width_mask, other.width_mask, width, other.width);
+		font_mask = set_value(font_mask, other.font_mask, font_size, other.font_size);
 	}
 
 	auto compute_visibility(void) -> void {
-		if (mask & display_mask) {
+		if (display_mask) {
 			if (display == css::css_display_value::DISPLAY_HIDDEN) {
-				mask |= invisible_flag;
+				visibility_mask = invisible_flag;
 
 				return;
 			}
 		}
 
-		if (mask & font_size_mask) {
+		if (font_mask) {
 			if (font_size == 0) {
-				mask |= invisible_flag;
+				visibility_mask = invisible_flag;
 
 				return;
 			}
@@ -196,7 +223,7 @@ struct html_block {
 			auto diff_r = ((float)fg.r - bg.r);
 			auto diff_g = ((float)fg.g - bg.g);
 			auto diff_b = ((float)fg.b - bg.b);
-			auto ravg = (fg.r + bg.r) / 2.0f;
+			auto ravg = ((float)fg.r + bg.r) / 2.0f;
 
 			/* Square diffs */
 			diff_r *= diff_r;
@@ -209,56 +236,56 @@ struct html_block {
 			return diff < min_visible_diff;
 		};
 		/* Check if we have both bg/fg colors */
-		if ((mask & (bg_color_mask|fg_color_mask)) == (bg_color_mask|fg_color_mask)) {
+		if (fg_color_mask && bg_color_mask) {
 			if (fg_color.alpha < 10) {
 				/* Too transparent */
-				mask |= invisible_flag|transparent_flag;
+				visibility_mask = transparent_flag;
 
 				return;
 			}
 
 			if (bg_color.alpha > 10) {
 				if (is_similar_colors(fg_color, bg_color)) {
-					mask |= invisible_flag|transparent_flag;
+					visibility_mask = transparent_flag;
 					return;
 				}
 			}
 		}
-		else if (mask & fg_color_mask) {
+		else if (fg_color_mask) {
 			/* Merely fg color */
 			if (fg_color.alpha < 10) {
 				/* Too transparent */
-				mask |= invisible_flag|transparent_flag;
+				visibility_mask = transparent_flag;
 
 				return;
 			}
 
 			/* Implicit fg color */
 			if (is_similar_colors(fg_color, rspamd::css::css_color::white())) {
-				mask |= invisible_flag|transparent_flag;
+				visibility_mask = transparent_flag;
 				return;
 			}
 		}
-		else if (mask & bg_color_mask) {
+		else if (bg_color_mask) {
 			if (is_similar_colors(rspamd::css::css_color::black(), bg_color)) {
-				mask |= invisible_flag|transparent_flag;
+				visibility_mask = transparent_flag;
 				return;
 			}
 		}
 
-		mask &= ~(invisible_flag|transparent_flag);
+		visibility_mask = 0;
 	}
 
 	constexpr auto is_visible(void) const -> bool {
-		return (mask & invisible_flag) == 0;
+		return visibility_mask == 0;
 	}
 
 	constexpr auto is_transparent(void) const -> bool {
-		return (mask & transparent_flag) != 0;
+		return visibility_mask == transparent_flag;
 	}
 
-	constexpr auto has_display(void) const -> bool {
-		return (mask & display_mask) != 0;
+	constexpr auto has_display(int how = set) const -> bool {
+		return display_mask >= how;
 	}
 
 	/**
@@ -266,12 +293,19 @@ struct html_block {
 	 * @return
 	 */
 	static auto default_html_block(void) -> html_block {
-		return html_block{rspamd::css::css_color::black(),
-						  rspamd::css::css_color::white(),
-						  0, 0,
-						  (fg_color_mask|bg_color_mask|font_size_mask),
-						  rspamd::css::css_display_value::DISPLAY_INLINE,
-						  12};
+		return html_block{.fg_color = rspamd::css::css_color::black(),
+				.bg_color = rspamd::css::css_color::white(),
+				.height = 0,
+				.width = 0,
+				.display = rspamd::css::css_display_value::DISPLAY_INLINE,
+				.font_size = 12,
+				.fg_color_mask = inherited,
+				.bg_color_mask = inherited,
+				.height_mask = unset,
+				.width_mask = unset,
+				.font_mask = unset,
+				.display_mask = inherited,
+				.visibility_mask = 0};
 	}
 	/**
 	 * Produces html block with no defined values allocated from the pool
@@ -279,8 +313,7 @@ struct html_block {
 	 * @return
 	 */
 	static auto undefined_html_block_pool(rspamd_mempool_t *pool) -> html_block* {
-		auto *bl = rspamd_mempool_alloc_type(pool, html_block);
-		bl->mask = 0;
+		auto *bl = rspamd_mempool_alloc0_type(pool, html_block);
 
 		return bl;
 	}
diff --git a/src/libserver/html/html_tests.cxx b/src/libserver/html/html_tests.cxx
index 39644300a..21fd04c0a 100644
--- a/src/libserver/html/html_tests.cxx
+++ b/src/libserver/html/html_tests.cxx
@@ -69,6 +69,7 @@ TEST_CASE("html text extraction")
 {
 	using namespace std::string_literals;
 	const std::vector<std::pair<std::string, std::string>> cases{
+			{"<b>foo<i>bar</i>baz</b>", "foobarbaz"},
 			{"test", "test"},
 			{"test\0"s, "test\uFFFD"s},
 			{"test\0test"s, "test\uFFFDtest"s},
@@ -79,7 +80,7 @@ TEST_CASE("html text extraction")
 			{"olo<p>text</p>lolo", "olo\ntext\nlolo"},
 			{"<div>foo</div><div>bar</div>", "foo\nbar\n"},
 			{"<b>foo<i>bar</b>baz</i>", "foobarbaz"},
-			{"<b>foo<i>bar</i>baz</b>", "foobarbaz"},
+
 			{"foo<br>baz", "foo\nbaz"},
 			{"<a href=https://example.com>test</a>", "test"},
 			{"<img alt=test>", "test"},
diff --git a/src/lua/lua_html.cxx b/src/lua/lua_html.cxx
index 370f2230e..250203d6e 100644
--- a/src/lua/lua_html.cxx
+++ b/src/lua/lua_html.cxx
@@ -348,7 +348,7 @@ lua_html_push_block (lua_State *L, const struct rspamd::html::html_block *bl)
 
 	lua_createtable (L, 0, 6);
 
-	if (bl->mask & rspamd::html::html_block::fg_color_mask) {
+	if (bl->fg_color_mask) {
 		lua_pushstring (L, "color");
 		lua_createtable (L, 4, 0);
 		lua_pushinteger (L, bl->fg_color.r);
@@ -361,7 +361,7 @@ lua_html_push_block (lua_State *L, const struct rspamd::html::html_block *bl)
 		lua_rawseti (L, -2, 4);
 		lua_settable (L, -3);
 	}
-	if (bl->mask & rspamd::html::html_block::bg_color_mask) {
+	if (bl->bg_color_mask) {
 		lua_pushstring (L, "bgcolor");
 		lua_createtable (L, 4, 0);
 		lua_pushinteger (L, bl->bg_color.r);
@@ -375,7 +375,7 @@ lua_html_push_block (lua_State *L, const struct rspamd::html::html_block *bl)
 		lua_settable (L, -3);
 	}
 
-	if (bl->mask & rspamd::html::html_block::font_size_mask) {
+	if (bl->font_mask) {
 		lua_pushstring(L, "font_size");
 		lua_pushinteger(L, bl->font_size);
 		lua_settable(L, -3);


More information about the Commits mailing list