summaryrefslogtreecommitdiff
path: root/Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp')
-rw-r--r--Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp225
1 files changed, 224 insertions, 1 deletions
diff --git a/Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp b/Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp
index 94beb95b67..3d07d6a248 100644
--- a/Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp
+++ b/Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp
@@ -13,6 +13,7 @@
#include <LibWeb/CSS/Parser/StyleComponentValueRule.h>
#include <LibWeb/CSS/Parser/StyleFunctionRule.h>
#include <LibWeb/CSS/Selector.h>
+#include <LibWeb/Dump.h>
#define CSS_PARSER_TRACE 1
@@ -76,6 +77,8 @@ Vector<QualifiedStyleRule> Parser::parse_as_stylesheet()
dbgln("");
auto selectors = parse_selectors(rule.m_prelude);
+ CSS::Selector selector = Selector(move(selectors));
+ dump_selector(selector);
}
return rules;
@@ -83,8 +86,223 @@ Vector<QualifiedStyleRule> Parser::parse_as_stylesheet()
Vector<CSS::Selector::ComplexSelector> Parser::parse_selectors(Vector<String> parts)
{
- (void)parts;
+ // TODO:
+ // This is a mess because the prelude is parsed as a string.
+ // It should really be parsed as its class, but the cpp gods have forsaken me
+ // and i cant make it work due to cyclic includes.
+
Vector<CSS::Selector::ComplexSelector> selectors;
+
+ size_t index = 0;
+ auto parse_simple_selector = [&]() -> Optional<CSS::Selector::SimpleSelector> {
+ if (index >= parts.size()) {
+ return {};
+ }
+
+ auto currentToken = parts.at(index);
+ CSS::Selector::SimpleSelector::Type type;
+ if (currentToken == "*") {
+ type = CSS::Selector::SimpleSelector::Type::Universal;
+ index++;
+ return CSS::Selector::SimpleSelector {
+ type,
+ CSS::Selector::SimpleSelector::PseudoClass::None,
+ CSS::Selector::SimpleSelector::PseudoElement::None,
+ String(),
+ CSS::Selector::SimpleSelector::AttributeMatchType::None,
+ String(),
+ String()
+ };
+ }
+
+ if (currentToken == ".") {
+ type = CSS::Selector::SimpleSelector::Type::Class;
+ } else if (currentToken == "#") {
+ type = CSS::Selector::SimpleSelector::Type::Id;
+ } else if (currentToken == "*") {
+ type = CSS::Selector::SimpleSelector::Type::Universal;
+ } else {
+ type = CSS::Selector::SimpleSelector::Type::TagName;
+ }
+
+ index++;
+ auto value = currentToken;
+
+ if (type == CSS::Selector::SimpleSelector::Type::TagName) {
+ value = value.to_lowercase();
+ }
+
+ CSS::Selector::SimpleSelector simple_selector {
+ type,
+ CSS::Selector::SimpleSelector::PseudoClass::None,
+ CSS::Selector::SimpleSelector::PseudoElement::None,
+ value,
+ CSS::Selector::SimpleSelector::AttributeMatchType::None,
+ String(),
+ String()
+ };
+
+ if (index >= parts.size()) {
+ return simple_selector;
+ }
+
+ currentToken = parts.at(index);
+ if (currentToken.starts_with('[')) {
+ auto adjusted = currentToken.substring(1, currentToken.length() - 2);
+
+ // TODO: split on String :^)
+ Vector<String> attribute_parts = adjusted.split(',');
+
+ simple_selector.attribute_match_type = CSS::Selector::SimpleSelector::AttributeMatchType::HasAttribute;
+ simple_selector.attribute_name = attribute_parts.first();
+
+ size_t attribute_index = 1;
+ if (attribute_index >= attribute_parts.size()) {
+ return simple_selector;
+ }
+
+ if (attribute_parts.at(attribute_index) == " =") {
+ simple_selector.attribute_match_type = CSS::Selector::SimpleSelector::AttributeMatchType::ExactValueMatch;
+ attribute_index++;
+ }
+
+ if (attribute_parts.at(attribute_index) == " ~") {
+ simple_selector.attribute_match_type = CSS::Selector::SimpleSelector::AttributeMatchType::Contains;
+ attribute_index += 2;
+ }
+
+ if (attribute_parts.at(attribute_index) == " |") {
+ simple_selector.attribute_match_type = CSS::Selector::SimpleSelector::AttributeMatchType::StartsWith;
+ attribute_index += 2;
+ }
+
+ simple_selector.attribute_value = attribute_parts.at(attribute_index);
+ return simple_selector;
+ }
+
+ if (currentToken == ":") {
+ bool is_pseudo = false;
+ index++;
+
+ if (index >= parts.size()) {
+ return {};
+ }
+
+ currentToken = parts.at(index);
+ if (currentToken == ":") {
+ is_pseudo = true;
+ index++;
+ }
+
+ if (index >= parts.size()) {
+ return {};
+ }
+
+ currentToken = parts.at(index);
+ auto pseudo_name = currentToken;
+ index++;
+
+ // Ignore for now, otherwise we produce a "false positive" selector
+ // and apply styles to the element itself, not its pseudo element
+ if (is_pseudo) {
+ return {};
+ }
+
+ if (pseudo_name.equals_ignoring_case("link")) {
+ simple_selector.pseudo_class = CSS::Selector::SimpleSelector::PseudoClass::Link;
+ } else if (pseudo_name.equals_ignoring_case("visited")) {
+ simple_selector.pseudo_class = CSS::Selector::SimpleSelector::PseudoClass::Visited;
+ } else if (pseudo_name.equals_ignoring_case("hover")) {
+ simple_selector.pseudo_class = CSS::Selector::SimpleSelector::PseudoClass::Hover;
+ } else if (pseudo_name.equals_ignoring_case("focus")) {
+ simple_selector.pseudo_class = CSS::Selector::SimpleSelector::PseudoClass::Focus;
+ } else if (pseudo_name.equals_ignoring_case("first-child")) {
+ simple_selector.pseudo_class = CSS::Selector::SimpleSelector::PseudoClass::FirstChild;
+ } else if (pseudo_name.equals_ignoring_case("last-child")) {
+ simple_selector.pseudo_class = CSS::Selector::SimpleSelector::PseudoClass::LastChild;
+ } else if (pseudo_name.equals_ignoring_case("only-child")) {
+ simple_selector.pseudo_class = CSS::Selector::SimpleSelector::PseudoClass::OnlyChild;
+ } else if (pseudo_name.equals_ignoring_case("empty")) {
+ simple_selector.pseudo_class = CSS::Selector::SimpleSelector::PseudoClass::Empty;
+ } else if (pseudo_name.equals_ignoring_case("root")) {
+ simple_selector.pseudo_class = CSS::Selector::SimpleSelector::PseudoClass::Root;
+ } else if (pseudo_name.equals_ignoring_case("first-of-type")) {
+ simple_selector.pseudo_class = CSS::Selector::SimpleSelector::PseudoClass::FirstOfType;
+ } else if (pseudo_name.equals_ignoring_case("last-of-type")) {
+ simple_selector.pseudo_class = CSS::Selector::SimpleSelector::PseudoClass::LastOfType;
+ } else if (pseudo_name.equals_ignoring_case("before")) {
+ simple_selector.pseudo_element = CSS::Selector::SimpleSelector::PseudoElement::Before;
+ } else if (pseudo_name.equals_ignoring_case("after")) {
+ simple_selector.pseudo_element = CSS::Selector::SimpleSelector::PseudoElement::After;
+ } else {
+ dbgln("Unknown pseudo class: '{}'", pseudo_name);
+ return simple_selector;
+ }
+ }
+
+ return simple_selector;
+ };
+
+ auto parse_complex_selector = [&]() -> Optional<CSS::Selector::ComplexSelector> {
+ auto relation = CSS::Selector::ComplexSelector::Relation::Descendant;
+
+ auto currentToken = parts.at(index);
+ if (is_combinator(currentToken)) {
+ if (currentToken == ">") {
+ relation = CSS::Selector::ComplexSelector::Relation::ImmediateChild;
+ }
+ if (currentToken == "+") {
+ relation = CSS::Selector::ComplexSelector::Relation::AdjacentSibling;
+ }
+ if (currentToken == "~") {
+ relation = CSS::Selector::ComplexSelector::Relation::GeneralSibling;
+ }
+ if (currentToken == "||") {
+ relation = CSS::Selector::ComplexSelector::Relation::Column;
+ }
+ index++;
+ }
+
+ Vector<CSS::Selector::SimpleSelector> simple_selectors;
+
+ for (;;) {
+ auto component = parse_simple_selector();
+ if (!component.has_value()) {
+ break;
+ }
+ simple_selectors.append(component.value());
+ }
+
+ if (simple_selectors.is_empty())
+ return {};
+
+ return CSS::Selector::ComplexSelector { relation, move(simple_selectors) };
+ };
+
+ for (;;) {
+ auto complex = parse_complex_selector();
+ if (complex.has_value()) {
+ selectors.append(complex.value());
+ }
+
+ if (index >= parts.size()) {
+ break;
+ }
+
+ auto currentToken = parts.at(index);
+ if (currentToken != ",") {
+ break;
+ }
+
+ index++;
+ }
+
+ if (selectors.is_empty()) {
+ return {};
+ }
+
+ selectors.first().relation = CSS::Selector::ComplexSelector::Relation::None;
+
return selectors;
}
@@ -100,6 +318,11 @@ void Parser::reconsume_current_input_token()
--m_iterator_offset;
}
+bool Parser::is_combinator(String input)
+{
+ return input == ">" || input == "+" || input == "~" || input == "||";
+}
+
Vector<QualifiedStyleRule> Parser::consume_a_list_of_rules(bool top_level)
{
Vector<QualifiedStyleRule> rules;