@@ -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 |
@@ -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 |
@@ -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); | |||
@@ -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); | |||
} | |||
} | |||
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 |
@@ -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; |