explicit css_parser(css_style_sheet *existing, rspamd_mempool_t *pool) : | explicit css_parser(css_style_sheet *existing, rspamd_mempool_t *pool) : | ||||
style_object(existing), pool(pool) {} | style_object(existing), pool(pool) {} | ||||
/* | |||||
* Process input css blocks | |||||
*/ | |||||
std::unique_ptr<css_consumed_block> consume_css_blocks(const std::string_view &sv); | 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); | bool consume_input(const std::string_view &sv); | ||||
auto get_object_maybe(void) -> tl::expected<std::unique_ptr<css_style_sheet>, css_parse_error> { | auto get_object_maybe(void) -> tl::expected<std::unique_ptr<css_style_sheet>, css_parse_error> { | ||||
return consumed_blocks; | 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) | bool css_parser::consume_input(const std::string_view &sv) | ||||
{ | { | ||||
auto &&consumed_blocks = consume_css_blocks(sv); | auto &&consumed_blocks = consume_css_blocks(sv); | ||||
}; | }; | ||||
} | } | ||||
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 | * Wrapper for the parser |
css_style_sheet *other) | css_style_sheet *other) | ||||
-> tl::expected<std::unique_ptr<css_style_sheet>, css_parse_error>; | -> 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, | auto get_selectors_parser_functor(rspamd_mempool_t *pool, | ||||
const std::string_view &st) -> blocks_gen_functor; | 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 | #endif //RSPAMD_CSS_PARSER_HXX |
css_property_type type; | css_property_type type; | ||||
css_property_flag flag; | 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( | static tl::expected<css_property,css_parse_error> from_token( | ||||
const css_parser_token &tok); | const css_parser_token &tok); | ||||
#include "css.hxx" | #include "css.hxx" | ||||
#include <limits> | #include <limits> | ||||
#define DOCTEST_CONFIG_IMPLEMENTATION_IN_DLL | |||||
#include "doctest/doctest.h" | |||||
namespace rspamd::css { | namespace rspamd::css { | ||||
/* Class methods */ | /* Class methods */ | ||||
static auto | static auto | ||||
allowed_property_value(const css_property &prop, const css_consumed_block &parser_block) | 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 (prop.is_color()) { | ||||
if (parser_block.is_token()) { | if (parser_block.is_token()) { | ||||
/* A single token */ | /* A single token */ | ||||
auto process_declaration_tokens(rspamd_mempool_t *pool, | auto process_declaration_tokens(rspamd_mempool_t *pool, | ||||
blocks_gen_functor &&next_block_functor) | blocks_gen_functor &&next_block_functor) | ||||
-> css_declarations_block_ptr | |||||
{ | |||||
-> css_declarations_block_ptr { | |||||
css_declarations_block_ptr ret; | css_declarations_block_ptr ret; | ||||
bool can_continue = true; | bool can_continue = true; | ||||
css_property cur_property{css_property_type::PROPERTY_NYI, | css_property cur_property{css_property_type::PROPERTY_NYI, | ||||
auto | auto | ||||
css_declarations_block::merge_block(const css_declarations_block &other, merge_type how) | css_declarations_block::merge_block(const css_declarations_block &other, merge_type how) | ||||
-> void | |||||
{ | |||||
-> void { | |||||
const auto &other_rules = other.get_rules(); | const auto &other_rules = other.get_rules(); | ||||
for (const auto &rule : other_rules) { | for (const auto &rule : other_rules) { | ||||
if (found_it != rules.end()) { | if (found_it != rules.end()) { | ||||
/* Duplicate, need to merge */ | /* Duplicate, need to merge */ | ||||
switch(how) { | |||||
switch (how) { | |||||
case merge_type::merge_override: | case merge_type::merge_override: | ||||
/* Override */ | /* Override */ | ||||
rules.insert(rule); | rules.insert(rule); | ||||
} | } | ||||
} | } | ||||
void css_rule::add_value(const css_value &value) | |||||
{ | |||||
void css_rule::add_value(const css_value &value) { | |||||
values.push_back(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 |
namespace rspamd::css { | namespace rspamd::css { | ||||
class css_declarations_block { | class css_declarations_block { | ||||
public: | public: | ||||
using rule_shared_ptr = std::shared_ptr<css_rule>; | using rule_shared_ptr = std::shared_ptr<css_rule>; | ||||
return rules; | 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: | private: | ||||
robin_hood::unordered_flat_set<rule_shared_ptr, rule_shared_hash, rule_shared_eq> rules; | robin_hood::unordered_flat_set<rule_shared_ptr, rule_shared_hash, rule_shared_eq> rules; |