srctree

Robin Linden parent 65b70d09 38491584
style: Support the descendant combinator

inlinesplit
style/style.cpp added: 59, removed: 3, total 56
@@ -37,11 +37,41 @@ bool is_match(style::StyledNode const &node, std::string_view selector) {
parts.pop_back();
std::ranges::reverse(parts);
 
// We only check the parent and up here, and if they all match, we fall
// through and check this node.
auto const *current = node.parent;
for (auto part : parts) {
part = util::trim(part);
// TODO(robinlinden): Handle descendant and child combinators in the same selector.
if (part.contains(' ')) {
return false;
}
 
if (current == nullptr || !is_match(*current, part)) {
return false;
}
 
current = current->parent;
}
}
 
// https://developer.mozilla.org/en-US/docs/Web/CSS/Descendant_combinator
if (selector_.contains(' ')) {
// TODO(robinlinden): std::views::reverse and friends when we drop Clang 14 and 15.
auto parts = util::split(selector_, " ");
selector_ = util::trim(parts.back());
parts.pop_back();
std::ranges::reverse(parts);
 
// We only check the parent and up here, and if they all match, we fall
// through and check this node.
auto const *current = node.parent;
for (auto const &part : parts) {
if (current == nullptr || !is_match(*current, util::trim(part))) {
while (current != nullptr && !is_match(*current, util::trim(part))) {
current = current->parent;
}
 
if (current == nullptr) {
return false;
}
 
 
style/style_test.cpp added: 59, removed: 3, total 56
@@ -94,6 +94,32 @@ int main() {
expect(style::is_match(node.children[0], ".logo > span"sv));
});
 
etest::test("is_match: descendant", [] {
using style::StyledNode;
// DOM for div[.logo] { span { a } }
dom::Element dom = dom::Element{"div", {{"class", "logo"}}, {dom::Element{"span", {}, {dom::Element{"a"}}}}};
StyledNode node{dom, {}, {{dom.children[0], {}, {{std::get<dom::Element>(dom.children[0]).children[0]}}}}};
node.children[0].parent = &node;
node.children[0].children[0].parent = &node.children[0];
 
expect(style::is_match(node.children[0], ".logo span"sv));
expect(style::is_match(node.children[0], "div span"sv));
expect(!style::is_match(node, ".logo span"sv));
 
std::get<dom::Element>(dom.children[0]).attributes["class"] = "ohno";
expect(style::is_match(node.children[0], ".logo .ohno"sv));
expect(style::is_match(node.children[0], ".logo span"sv));
 
expect(style::is_match(node.children[0].children[0], "div a"sv));
expect(style::is_match(node.children[0].children[0], ".logo a"sv));
expect(style::is_match(node.children[0].children[0], "span a"sv));
expect(style::is_match(node.children[0].children[0], ".ohno a"sv));
expect(style::is_match(node.children[0].children[0], "div span a"sv));
expect(style::is_match(node.children[0].children[0], ".logo span a"sv));
expect(style::is_match(node.children[0].children[0], "div .ohno a"sv));
expect(style::is_match(node.children[0].children[0], ".logo .ohno a"sv));
});
 
etest::test("matching_rules: simple names", [] {
std::vector<css::Rule> stylesheet;
expect(style::matching_rules(dom::Element{"div"}, stylesheet).empty());