1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-05-31 14:38:11 +00:00

LibHTML: Add adjacent (+) and general (~) sibling combinators

This patch implements two more selector features:

- "div + p" matches the <p> sibling immediately after a <div>.
- "div ~ p" matches all <p> siblings after a <div>.
This commit is contained in:
Andreas Kling 2019-10-06 19:59:07 +02:00
parent 5a6c36dc91
commit bedb00603c
5 changed files with 53 additions and 2 deletions

View file

@ -15,6 +15,11 @@ div > .boo > .bee {
background-color: #000000;
color: #ff00ff;
}
#gen_sib ~ div,
#adj_sib + div {
background-color: #0000ff;
color: #ffffff;
}
</style>
</head>
<body>
@ -45,5 +50,16 @@ div > .boo > .bee {
</div>
</div>
</div>
</div>
<div>
<div id="gen_sib">All the siblings of this &lt;div&gt; should be blue.</div>
<div>First sibling (should be blue)</div>
<div>Second sibling (should be blue)</div>
</div>
<div>
<div id="adj_sib">The first sibling of this &lt;div&gt; should be blue.</div>
<div>First sibling (should be blue)</div>
<div>Second sibling (should not be blue)</div>
</div>
</body>
</html>

View file

@ -19,6 +19,8 @@ public:
None,
ImmediateChild,
Descendant,
AdjacentSibling,
GeneralSibling,
};
Relation relation { Relation::None };

View file

@ -51,6 +51,18 @@ static bool matches(const Selector& selector, int component_index, const Element
if (!element.parent() || !element.parent()->is_element())
return false;
return matches(selector, component_index - 1, static_cast<const Element&>(*element.parent()));
case Selector::Component::Relation::AdjacentSibling:
ASSERT(component_index != 0);
if (auto* sibling = element.previous_element_sibling())
return matches(selector, component_index - 1, *sibling);
return false;
case Selector::Component::Relation::GeneralSibling:
ASSERT(component_index != 0);
for (auto* sibling = element.previous_element_sibling(); sibling; sibling = sibling->previous_element_sibling()) {
if (matches(selector, component_index - 1, *sibling))
return true;
}
return false;
}
ASSERT_NOT_REACHED();
}

View file

@ -158,6 +158,12 @@ void dump_rule(const StyleRule& rule)
case Selector::Component::Relation::Descendant:
relation_description = "{Descendant}";
break;
case Selector::Component::Relation::AdjacentSibling:
relation_description = "{AdjacentSibling}";
break;
case Selector::Component::Relation::GeneralSibling:
relation_description = "{GeneralSibling}";
break;
}
dbgprintf(" %s:%s %s\n", type_description, component.value.characters(), relation_description);
}

View file

@ -84,6 +84,11 @@ public:
return isalnum(ch) || ch == '-' || ch == '_' || ch == '(' || ch == ')' || ch == '@';
}
bool is_combinator(char ch) const
{
return ch == '~' || ch == '>' || ch == '+';
}
Optional<Selector::Component> parse_selector_component()
{
consume_whitespace();
@ -93,8 +98,18 @@ public:
if (peek() == '{')
return {};
if (peek() == '>') {
if (is_combinator(peek())) {
switch (peek()) {
case '>':
relation = Selector::Component::Relation::ImmediateChild;
break;
case '+':
relation = Selector::Component::Relation::AdjacentSibling;
break;
case '~':
relation = Selector::Component::Relation::GeneralSibling;
break;
}
consume_one();
consume_whitespace();
}