mirror of
https://github.com/RGBCube/serenity
synced 2025-07-25 02:37:42 +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:
parent
5a6c36dc91
commit
bedb00603c
5 changed files with 53 additions and 2 deletions
|
@ -15,6 +15,11 @@ div > .boo > .bee {
|
||||||
background-color: #000000;
|
background-color: #000000;
|
||||||
color: #ff00ff;
|
color: #ff00ff;
|
||||||
}
|
}
|
||||||
|
#gen_sib ~ div,
|
||||||
|
#adj_sib + div {
|
||||||
|
background-color: #0000ff;
|
||||||
|
color: #ffffff;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
@ -45,5 +50,16 @@ div > .boo > .bee {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div id="gen_sib">All the siblings of this <div> 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 <div> should be blue.</div>
|
||||||
|
<div>First sibling (should be blue)</div>
|
||||||
|
<div>Second sibling (should not be blue)</div>
|
||||||
|
</div>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
@ -19,6 +19,8 @@ public:
|
||||||
None,
|
None,
|
||||||
ImmediateChild,
|
ImmediateChild,
|
||||||
Descendant,
|
Descendant,
|
||||||
|
AdjacentSibling,
|
||||||
|
GeneralSibling,
|
||||||
};
|
};
|
||||||
Relation relation { Relation::None };
|
Relation relation { Relation::None };
|
||||||
|
|
||||||
|
|
|
@ -51,6 +51,18 @@ static bool matches(const Selector& selector, int component_index, const Element
|
||||||
if (!element.parent() || !element.parent()->is_element())
|
if (!element.parent() || !element.parent()->is_element())
|
||||||
return false;
|
return false;
|
||||||
return matches(selector, component_index - 1, static_cast<const Element&>(*element.parent()));
|
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();
|
ASSERT_NOT_REACHED();
|
||||||
}
|
}
|
||||||
|
|
|
@ -158,6 +158,12 @@ void dump_rule(const StyleRule& rule)
|
||||||
case Selector::Component::Relation::Descendant:
|
case Selector::Component::Relation::Descendant:
|
||||||
relation_description = "{Descendant}";
|
relation_description = "{Descendant}";
|
||||||
break;
|
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);
|
dbgprintf(" %s:%s %s\n", type_description, component.value.characters(), relation_description);
|
||||||
}
|
}
|
||||||
|
|
|
@ -84,6 +84,11 @@ public:
|
||||||
return isalnum(ch) || ch == '-' || ch == '_' || ch == '(' || ch == ')' || ch == '@';
|
return isalnum(ch) || ch == '-' || ch == '_' || ch == '(' || ch == ')' || ch == '@';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool is_combinator(char ch) const
|
||||||
|
{
|
||||||
|
return ch == '~' || ch == '>' || ch == '+';
|
||||||
|
}
|
||||||
|
|
||||||
Optional<Selector::Component> parse_selector_component()
|
Optional<Selector::Component> parse_selector_component()
|
||||||
{
|
{
|
||||||
consume_whitespace();
|
consume_whitespace();
|
||||||
|
@ -93,8 +98,18 @@ public:
|
||||||
if (peek() == '{')
|
if (peek() == '{')
|
||||||
return {};
|
return {};
|
||||||
|
|
||||||
if (peek() == '>') {
|
if (is_combinator(peek())) {
|
||||||
relation = Selector::Component::Relation::ImmediateChild;
|
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_one();
|
||||||
consume_whitespace();
|
consume_whitespace();
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue