Skip to content

Commit 306b9df

Browse files
committed
LibHTML: Various little improvements to the CSS parser
The parser now kinda recognizes immediate child selectors, !important, and various other little things. It's still extremely far from robust and/or correct. :^)
1 parent 48f43a7 commit 306b9df

File tree

4 files changed

+98
-13
lines changed

4 files changed

+98
-13
lines changed

Libraries/LibHTML/CSS/Selector.h

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,16 @@ class Selector {
1111
Invalid,
1212
TagName,
1313
Id,
14-
Class
14+
Class,
1515
};
1616
Type type { Type::Invalid };
17+
18+
enum class Relation {
19+
None,
20+
ImmediateChild,
21+
};
22+
Relation relation { Relation::None };
23+
1724
String value;
1825
};
1926

Libraries/LibHTML/CSS/StyleDeclaration.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
struct StyleProperty {
77
String name;
88
NonnullRefPtr<StyleValue> value;
9+
bool important { false };
910
};
1011

1112
class StyleDeclaration : public RefCounted<StyleDeclaration> {

Libraries/LibHTML/Dump.cpp

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,15 @@ void dump_rule(const StyleRule& rule)
148148
type_description = "TagName";
149149
break;
150150
}
151-
dbgprintf(" %s:%s\n", type_description, component.value.characters());
151+
const char* relation_description = "";
152+
switch (component.relation) {
153+
case Selector::Component::Relation::None:
154+
break;
155+
case Selector::Component::Relation::ImmediateChild:
156+
relation_description = "{ImmediateChild}";
157+
break;
158+
}
159+
dbgprintf(" %s:%s %s\n", type_description, component.value.characters(), relation_description);
152160
}
153161
}
154162
dbgprintf(" Declarations:\n");

Libraries/LibHTML/Parser/CSSParser.cpp

Lines changed: 80 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,13 @@
33
#include <ctype.h>
44
#include <stdio.h>
55

6+
#define PARSE_ASSERT(x) \
7+
if (!(x)) { \
8+
dbg() << "CSS PARSER ASSERTION FAILED: " << #x; \
9+
dbg() << "At character# " << index << " in CSS: _" << css << "_"; \
10+
ASSERT_NOT_REACHED(); \
11+
}
12+
613
static Optional<Color> parse_css_color(const StringView& view)
714
{
815
auto color = Color::from_string(view);
@@ -53,7 +60,7 @@ class CSSParser {
5360

5461
char consume_specific(char ch)
5562
{
56-
ASSERT(peek() == ch);
63+
PARSE_ASSERT(peek() == ch);
5764
++index;
5865
return ch;
5966
}
@@ -71,13 +78,23 @@ class CSSParser {
7178

7279
bool is_valid_selector_char(char ch) const
7380
{
74-
return isalnum(ch) || ch == '-' || ch == '_';
81+
return isalnum(ch) || ch == '-' || ch == '_' || ch == '(' || ch == ')' || ch == '@';
7582
}
7683

77-
void parse_selector()
84+
Optional<Selector::Component> parse_selector_component()
7885
{
7986
consume_whitespace();
8087
Selector::Component::Type type;
88+
Selector::Component::Relation relation = Selector::Component::Relation::None;
89+
90+
if (peek() == '{')
91+
return {};
92+
93+
if (peek() == '>') {
94+
relation = Selector::Component::Relation::ImmediateChild;
95+
consume_one();
96+
consume_whitespace();
97+
}
8198

8299
if (peek() == '.') {
83100
type = Selector::Component::Type::Class;
@@ -92,13 +109,43 @@ class CSSParser {
92109
while (is_valid_selector_char(peek()))
93110
buffer.append(consume_one());
94111

95-
ASSERT(!buffer.is_null());
112+
PARSE_ASSERT(!buffer.is_null());
113+
Selector::Component component { type, relation, String::copy(buffer) };
114+
buffer.clear();
115+
116+
if (peek() == '[') {
117+
// FIXME: Implement attribute selectors.
118+
while (peek() != ']') {
119+
consume_one();
120+
}
121+
consume_one();
122+
}
96123

97-
auto component_string = String::copy(buffer);
124+
if (peek() == ':') {
125+
// FIXME: Implement pseudo stuff.
126+
consume_one();
127+
if (peek() == ':')
128+
consume_one();
129+
while (is_valid_selector_char(peek()))
130+
consume_one();
131+
}
98132

133+
return component;
134+
}
135+
136+
void parse_selector()
137+
{
99138
Vector<Selector::Component> components;
100-
components.append({ type, component_string });
101-
buffer.clear();
139+
140+
for (;;) {
141+
auto component = parse_selector_component();
142+
if (component.has_value())
143+
components.append(component.value());
144+
consume_whitespace();
145+
if (peek() == ',' || peek() == '{')
146+
break;
147+
}
148+
102149
current_rule.selectors.append(Selector(move(components)));
103150
};
104151

@@ -123,12 +170,16 @@ class CSSParser {
123170

124171
bool is_valid_property_value_char(char ch) const
125172
{
126-
return !isspace(ch) && ch != ';';
173+
return ch != '!' && ch != ';';
127174
}
128175

129-
void parse_property()
176+
Optional<StyleProperty> parse_property()
130177
{
131178
consume_whitespace();
179+
if (peek() == ';') {
180+
consume_one();
181+
return {};
182+
}
132183
buffer.clear();
133184
while (is_valid_property_name_char(peek()))
134185
buffer.append(consume_one());
@@ -141,14 +192,32 @@ class CSSParser {
141192
buffer.append(consume_one());
142193
auto property_value = String::copy(buffer);
143194
buffer.clear();
195+
consume_whitespace();
196+
bool is_important = false;
197+
if (peek() == '!') {
198+
consume_specific('!');
199+
consume_specific('i');
200+
consume_specific('m');
201+
consume_specific('p');
202+
consume_specific('o');
203+
consume_specific('r');
204+
consume_specific('t');
205+
consume_specific('a');
206+
consume_specific('n');
207+
consume_specific('t');
208+
consume_whitespace();
209+
is_important = true;
210+
}
144211
consume_specific(';');
145-
current_rule.properties.append({ property_name, parse_css_value(property_value) });
212+
return StyleProperty { property_name, parse_css_value(property_value), is_important };
146213
}
147214

148215
void parse_declaration()
149216
{
150217
for (;;) {
151-
parse_property();
218+
auto property = parse_property();
219+
if (property.has_value())
220+
current_rule.properties.append(property.value());
152221
consume_whitespace();
153222
if (peek() == '}')
154223
break;

0 commit comments

Comments
 (0)