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> {
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);
};
}
+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
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
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);
#include "css.hxx"
#include <limits>
+#define DOCTEST_CONFIG_IMPLEMENTATION_IN_DLL
+#include "doctest/doctest.h"
+
namespace rspamd::css {
/* Class methods */
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 */
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,
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) {
if (found_it != rules.end()) {
/* Duplicate, need to merge */
- switch(how) {
+ switch (how) {
case merge_type::merge_override:
/* Override */
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);
}
-}
\ 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
namespace rspamd::css {
+
class css_declarations_block {
public:
using rule_shared_ptr = std::shared_ptr<css_rule>;
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;