Browse Source

[Project] Css: Add preliminary stylesheet support

tags/3.0
Vsevolod Stakhov 3 years ago
parent
commit
61d518bf1d

+ 67
- 9
src/libserver/css/css.cxx View File

@@ -16,7 +16,7 @@

#include "css.h"
#include "css.hxx"
#include "css_style.hxx"
#include "contrib/robin-hood/robin_hood.h"
#include "css_parser.hxx"
#define DOCTEST_CONFIG_IMPLEMENTATION_IN_DLL
#define DOCTEST_CONFIG_IMPLEMENT
@@ -28,8 +28,6 @@ rspamd_css_parse_style (rspamd_mempool_t *pool, const guchar *begin, gsize len,
{
auto parse_res = rspamd::css::parse_css(pool, {(const char* )begin, len});

#if 0
/* Return once semantical parsing is done */
if (parse_res.has_value()) {
return reinterpret_cast<rspamd_css>(parse_res.value().release());
}
@@ -39,9 +37,6 @@ rspamd_css_parse_style (rspamd_mempool_t *pool, const guchar *begin, gsize len,
"parse error");
return nullptr;
}
#else
return nullptr;
#endif
}

namespace rspamd::css {
@@ -49,10 +44,73 @@ namespace rspamd::css {
INIT_LOG_MODULE_PUBLIC(css);

class css_style_sheet::impl {

public:
using selector_ptr = std::unique_ptr<css_selector>;
using selectors_hash = robin_hood::unordered_flat_map<selector_ptr, css_declarations_block_ptr>;
using universal_selector_t = std::pair<selector_ptr, css_declarations_block_ptr>;
selectors_hash tags_selector;
selectors_hash class_selectors;
selectors_hash id_selectors;
std::optional<universal_selector_t> universal_selector;
};

css_style_sheet::css_style_sheet () : pimpl(new impl) {}
css_style_sheet::~css_style_sheet () {}
css_style_sheet::css_style_sheet(rspamd_mempool_t *pool)
: pool(pool), pimpl(new impl) {}
css_style_sheet::~css_style_sheet() {}

auto
css_style_sheet::add_selector_rule(std::unique_ptr<css_selector> &&selector,
css_declarations_block_ptr decls) -> void
{
impl::selectors_hash *target_hash = nullptr;

switch(selector->type) {
case css_selector::selector_type::SELECTOR_ALL:
if (pimpl->universal_selector) {
/* Another universal selector */
msg_debug_css("redefined universal selector, merging rules");
pimpl->universal_selector->second->merge_block(*decls);
}
else {
msg_debug_css("added universal selector");
pimpl->universal_selector = std::make_pair(std::move(selector),
decls);
}
break;
case css_selector::selector_type::SELECTOR_CLASS:
target_hash = &pimpl->class_selectors;
break;
case css_selector::selector_type::SELECTOR_ID:
target_hash = &pimpl->id_selectors;
break;
case css_selector::selector_type::SELECTOR_ELEMENT:
target_hash = &pimpl->tags_selector;
break;
}

if (target_hash) {
auto found_it = target_hash->find(selector);

if (found_it == target_hash->end()) {
/* Easy case, new element */
target_hash->insert({std::move(selector), decls});
}
else {
/* The problem with merging is actually in how to handle selectors chains
* For example, we have 2 selectors:
* 1. class id tag -> meaning that we first match class, then we ensure that
* id is also the same and finally we check the tag
* 2. tag class id -> it means that we check first tag, then class and then id
* So we have somehow equal path in the xpath terms.
* I suppose now, that we merely check parent stuff and handle duplicates
* merging when finally resolving paths.
*/
auto sel_str = selector->to_string().value_or("unknown");
msg_debug_css("found duplicate selector: %*s", (int)sel_str.size(),
sel_str.data());
found_it->second->merge_block(*decls);
}
}
}

}

+ 4
- 1
src/libserver/css/css.hxx View File

@@ -40,10 +40,13 @@ extern unsigned int rspamd_css_log_id;

class css_style_sheet {
public:
css_style_sheet();
css_style_sheet(rspamd_mempool_t *pool);
~css_style_sheet(); /* must be declared separately due to pimpl */
auto add_selector_rule(std::unique_ptr<css_selector> &&selector,
css_declarations_block_ptr decls) -> void;
private:
class impl;
rspamd_mempool_t *pool;
std::unique_ptr<impl> pimpl;
};


+ 9
- 4
src/libserver/css/css_parser.cxx View File

@@ -559,7 +559,7 @@ bool css_parser::consume_input(const std::string_view &sv)
return false;
}

style_object = std::make_unique<css_style_sheet>();
style_object = std::make_unique<css_style_sheet>(pool);

for (auto &&rule : rules) {
/*
@@ -627,6 +627,10 @@ bool css_parser::consume_input(const std::string_view &sv)
msg_debug_css("processed %d rules",
(int)declarations_vec->get_rules().size());

for (auto &&selector : selectors_vec) {
style_object->add_selector_rule(std::move(selector),
declarations_vec);
}
}
}
}
@@ -677,15 +681,16 @@ get_selectors_parser_functor(rspamd_mempool_t *pool,
* Wrapper for the parser
*/
auto parse_css(rspamd_mempool_t *pool, const std::string_view &st) ->
bool
tl::expected<std::unique_ptr<css_style_sheet>, css_parse_error>
{
css_parser parser(pool);

if (parser.consume_input(st)) {
return true;
return parser.get_object_maybe();
}

return false;
return tl::make_unexpected(css_parse_error{css_parse_error_type::PARSE_ERROR_INVALID_SYNTAX,
"cannot parse input"});
}

TEST_SUITE("css parser") {

+ 2
- 1
src/libserver/css/css_parser.hxx View File

@@ -183,8 +183,9 @@ extern const css_consumed_block css_parser_eof_block;

using blocks_gen_functor = std::function<const css_consumed_block &(void)>;

class css_style_sheet;
auto parse_css(rspamd_mempool_t *pool, const std::string_view &st) ->
bool;
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;

+ 32
- 0
src/libserver/css/css_rule.cxx View File

@@ -313,6 +313,38 @@ auto process_declaration_tokens(rspamd_mempool_t *pool,
return ret; /* copy elision */
}

auto
css_declarations_block::merge_block(const css_declarations_block &other, merge_type how)
-> void
{
const auto &other_rules = other.get_rules();

for (const auto &rule : other_rules) {
auto &&found_it = rules.find(rule);

if (found_it != rules.end()) {
/* Duplicate, need to merge */
switch(how) {
case merge_type::merge_override:
/* Override */
rules.insert(rule);
break;
case merge_type::merge_duplicate:
/* Merge values */
(*found_it)->merge_values(*rule);
break;
case merge_type::merge_parent:
/* Do not merge parent rule if more specific local one is presented */
break;
}
}
else {
/* New property, just insert */
rules.insert(rule);
}
}
}

void css_rule::add_value(const css_value &value)
{
values.push_back(value);

+ 8
- 0
src/libserver/css/css_rule.hxx View File

@@ -81,8 +81,16 @@ public:
using rule_shared_ptr = std::shared_ptr<css_rule>;
using rule_shared_hash = shared_ptr_hash<css_rule>;
using rule_shared_eq = shared_ptr_equal<css_rule>;
enum class merge_type {
merge_duplicate,
merge_parent,
merge_override
};

css_declarations_block() = default;
auto add_rule(rule_shared_ptr &&rule) -> bool;
auto merge_block(const css_declarations_block &other,
merge_type how = merge_type::merge_duplicate) -> void;
auto get_rules(void) const -> const auto & {
return rules;
}

Loading…
Cancel
Save