123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291 |
- #include <LibHTML/CSS/StyleSheet.h>
- #include <LibHTML/Parser/CSSParser.h>
- #include <ctype.h>
- #include <stdio.h>
- #define PARSE_ASSERT(x) \
- if (!(x)) { \
- dbg() << "CSS PARSER ASSERTION FAILED: " << #x; \
- dbg() << "At character# " << index << " in CSS: _" << css << "_"; \
- ASSERT_NOT_REACHED(); \
- }
- static Optional<Color> parse_css_color(const StringView& view)
- {
- auto color = Color::from_string(view);
- if (color.has_value())
- return color;
- // FIXME: Parse all valid color strings :^)
- return {};
- }
- NonnullRefPtr<StyleValue> parse_css_value(const StringView& view)
- {
- String string(view);
- bool ok;
- int as_int = string.to_int(ok);
- if (ok)
- return LengthStyleValue::create(Length(as_int, Length::Type::Absolute));
- unsigned as_uint = string.to_uint(ok);
- if (ok)
- return LengthStyleValue::create(Length(as_uint, Length::Type::Absolute));
- if (string == "inherit")
- return InheritStyleValue::create();
- if (string == "initial")
- return InitialStyleValue::create();
- if (string == "auto")
- return LengthStyleValue::create(Length());
- auto color = parse_css_color(view);
- if (color.has_value())
- return ColorStyleValue::create(color.value());
- return StringStyleValue::create(string);
- }
- class CSSParser {
- public:
- CSSParser(const StringView& input)
- : css(input)
- {
- }
- char peek() const
- {
- if (index < css.length())
- return css[index];
- return 0;
- }
- char consume_specific(char ch)
- {
- PARSE_ASSERT(peek() == ch);
- ++index;
- return ch;
- }
- char consume_one()
- {
- return css[index++];
- };
- void consume_whitespace()
- {
- while (isspace(peek()))
- ++index;
- }
- bool is_valid_selector_char(char ch) const
- {
- return isalnum(ch) || ch == '-' || ch == '_' || ch == '(' || ch == ')' || ch == '@';
- }
- Optional<Selector::Component> parse_selector_component()
- {
- consume_whitespace();
- Selector::Component::Type type;
- Selector::Component::Relation relation = Selector::Component::Relation::None;
- if (peek() == '{')
- return {};
- if (peek() == '>') {
- relation = Selector::Component::Relation::ImmediateChild;
- consume_one();
- consume_whitespace();
- }
- if (peek() == '.') {
- type = Selector::Component::Type::Class;
- consume_one();
- } else if (peek() == '#') {
- type = Selector::Component::Type::Id;
- consume_one();
- } else {
- type = Selector::Component::Type::TagName;
- }
- while (is_valid_selector_char(peek()))
- buffer.append(consume_one());
- PARSE_ASSERT(!buffer.is_null());
- Selector::Component component { type, relation, String::copy(buffer) };
- buffer.clear();
- if (peek() == '[') {
- // FIXME: Implement attribute selectors.
- while (peek() != ']') {
- consume_one();
- }
- consume_one();
- }
- if (peek() == ':') {
- // FIXME: Implement pseudo stuff.
- consume_one();
- if (peek() == ':')
- consume_one();
- while (is_valid_selector_char(peek()))
- consume_one();
- }
- return component;
- }
- void parse_selector()
- {
- Vector<Selector::Component> components;
- for (;;) {
- auto component = parse_selector_component();
- if (component.has_value())
- components.append(component.value());
- consume_whitespace();
- if (peek() == ',' || peek() == '{')
- break;
- }
- current_rule.selectors.append(Selector(move(components)));
- };
- void parse_selector_list()
- {
- for (;;) {
- parse_selector();
- consume_whitespace();
- if (peek() == ',') {
- consume_one();
- continue;
- }
- if (peek() == '{')
- break;
- }
- }
- bool is_valid_property_name_char(char ch) const
- {
- return !isspace(ch) && ch != ':';
- }
- bool is_valid_property_value_char(char ch) const
- {
- return ch != '!' && ch != ';';
- }
- Optional<StyleProperty> parse_property()
- {
- consume_whitespace();
- if (peek() == ';') {
- consume_one();
- return {};
- }
- buffer.clear();
- while (is_valid_property_name_char(peek()))
- buffer.append(consume_one());
- auto property_name = String::copy(buffer);
- buffer.clear();
- consume_whitespace();
- consume_specific(':');
- consume_whitespace();
- while (is_valid_property_value_char(peek()))
- buffer.append(consume_one());
- auto property_value = String::copy(buffer);
- buffer.clear();
- consume_whitespace();
- bool is_important = false;
- if (peek() == '!') {
- consume_specific('!');
- consume_specific('i');
- consume_specific('m');
- consume_specific('p');
- consume_specific('o');
- consume_specific('r');
- consume_specific('t');
- consume_specific('a');
- consume_specific('n');
- consume_specific('t');
- consume_whitespace();
- is_important = true;
- }
- consume_specific(';');
- return StyleProperty { property_name, parse_css_value(property_value), is_important };
- }
- void parse_declaration()
- {
- for (;;) {
- auto property = parse_property();
- if (property.has_value())
- current_rule.properties.append(property.value());
- consume_whitespace();
- if (peek() == '}')
- break;
- }
- }
- void parse_rule()
- {
- parse_selector_list();
- consume_specific('{');
- parse_declaration();
- consume_specific('}');
- rules.append(StyleRule::create(move(current_rule.selectors), StyleDeclaration::create(move(current_rule.properties))));
- consume_whitespace();
- }
- NonnullRefPtr<StyleSheet> parse_sheet()
- {
- while (index < css.length()) {
- parse_rule();
- }
- return StyleSheet::create(move(rules));
- }
- NonnullRefPtr<StyleDeclaration> parse_standalone_declaration()
- {
- consume_whitespace();
- for (;;) {
- parse_property();
- consume_whitespace();
- if (!peek())
- break;
- }
- return StyleDeclaration::create(move(current_rule.properties));
- }
- private:
- NonnullRefPtrVector<StyleRule> rules;
- enum class State {
- Free,
- InSelectorComponent,
- InPropertyName,
- InPropertyValue,
- };
- struct CurrentRule {
- Vector<Selector> selectors;
- Vector<StyleProperty> properties;
- };
- CurrentRule current_rule;
- Vector<char> buffer;
- int index = 0;
- String css;
- };
- NonnullRefPtr<StyleSheet> parse_css(const String& css)
- {
- CSSParser parser(css);
- return parser.parse_sheet();
- }
- NonnullRefPtr<StyleDeclaration> parse_css_declaration(const String& css)
- {
- CSSParser parser(css);
- return parser.parse_standalone_declaration();
- }
|