Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
sleepy-monax committed May 7, 2024
1 parent cbdd458 commit c352663
Show file tree
Hide file tree
Showing 3 changed files with 181 additions and 38 deletions.
Empty file added src/web/web-select/Select2.cpp
Empty file.
174 changes: 151 additions & 23 deletions src/web/web-select/match.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,47 +11,175 @@ namespace Web::Select {
struct Spec {
bool match;
isize a, b, c;
// a: The number of ID selectors in the selector.
// b: The number of class selectors, attributes selectors, and pseudo-classes in the selector.
// c: The number of type selectors and pseudo-elements in the selector.

static Spec NOMATCH;
static Spec MATCH;
static Spec const NOMATCH;
static Spec const ZERO, A, B, C;

Spec operator and(Spec const &other) const {
return Spec{
match && other.match,
Spec(None)
: match(false), a(0), b(0), c(0) {}

Spec(isize a, isize b, isize c)
: match(true), a(a), b(b), c(c) {}

Spec operator+(Spec const &other) const {
return {
a + other.a,
b + other.b,
c + other.c
c + other.c,
};
}

Spec operator or(Spec const &other) const {
return Spec{
match || other.match,
Spec operator and(Spec const &other) const {
if (!match or !other.match)
return Spec::NOMATCH;
return {
a + other.a,
b + other.b,
c + other.c
c + other.c,
};
}

Spec operator or(Spec const &other) const {
if (*this > other)
return *this;
return other;
}

Spec operator not() const {
return {
a,
b,
c
};
}

explicit operator bool() const {
return match;
}

bool operator==(Spec const &other) const = default;
auto operator<=>(Spec const &other) const = default;
};

Spec const Spec::NOMATCH = NONE;
Spec const Spec::ZERO = {0, 0, 0};
Spec const Spec::A = {1, 0, 0};
Spec const Spec::B = {0, 1, 0};
Spec const Spec::C = {0, 0, 1};

Spec match(Selector const &sel, Dom::Element &el);

Spec match(Combinator const &s, Dom::Element &el) {
switch (s.type) {
// 4.1. Selector Lists
// https://www.w3.org/TR/selectors-4/#grouping
case Combinator::LIST: {
Spec result = Spec::NOMATCH;
for (auto &inner : s.inners)
result = result or match(inner, el);
return result;
}

// 4.2. The Matches-Any Pseudo-class: :is()
// https://www.w3.org/TR/selectors-4/#matches
case Combinator::IS: {
Spec result = Spec::NOMATCH;
for (auto &inner : s.inners) {
if (auto sel = match(inner, el); sel > result)
result = sel;
}
return result + Spec::B;
}

// 4.3. The Negation (Matches-None) Pseudo-class: :not()
// https://www.w3.org/TR/selectors-4/#negation
case Combinator::NOT: {
Spec result = {0, 0, 0};
for (auto &inner : s.inners)
result = result and not match(inner, el);
return result + Spec::B;
}

// 4.4. The Specificity-adjustment Pseudo-class: :where()
// https://www.w3.org/TR/selectors-4/#zero-matches
case Combinator::WHERE: {
Spec result = Spec::NOMATCH;
for (auto &inner : s.inners)
result = result or match(inner, el);
return result ? Spec::ZERO : Spec::NOMATCH;
}

default:
notImplemented();
}
}

// 5.1. Type (tag name) selector
// https://www.w3.org/TR/selectors-4/#type
Spec match(TypeSelector const &s, Dom::Element &el) {
if (el.tagName == s.type)
return Spec::C;
return Spec::NOMATCH;
}

// 5.2. Universal selector
// https://www.w3.org/TR/selectors-4/#the-universal-selector
Spec match(UniversalSelector const &, Dom::Element &) {
return Spec::NOMATCH;
}

// 6. Attribute selectors
// https://www.w3.org/TR/selectors-4/#attribute-selectors
Spec match(AttributeSelector const &s, Dom::Element &el) {
if (auto attr = el.getAttribute(s.name); attr) {
switch (s.match) {
case AttributeSelector::PRESENT:
return Spec::B;
case AttributeSelector::EXACT:
if (attr == s.value)
return Spec::B;
break;
case AttributeSelector::CONTAINS:
if (attr.contains(s.value))
return Spec::B;
break;
case AttributeSelector::HYPHENATED:
if (attr == s.value or attr.startsWith(s.value + "-"))
return Spec::B;
break;
case AttributeSelector::STR_START_WITH:
if (attr.startsWith(s.value))
return Spec::B;
break;
case AttributeSelector::STR_END_WITH:
if (attr.endsWith(s.value))
return Spec::B;
break;
case AttributeSelector::STR_CONTAIN:
if (attr.contains(s.value))
return Spec::B;
break;
}
}
return Spec::NOMATCH;
}

Spec match(Selector const &sel, Dom::Element &el) {
return sel.visit(Visitor{
[&](Combinator const &s) -> Spec {
switch (s.type) {
case Combinator::LIST:
return match(*s.lhs, el) or match(*s.rhs, el);

case Combinator::DESCENDANT:
return match(*s.lhs, el) or el.parentNode().map([&](auto &parent) {
return match(*s.rhs, parent);
}).value_or(Spec::NOMATCH);

default:
return Spec::NOMATCH;
}
return match(s, el);
},
[&](TypeSelector const &s) -> Spec {
return match(s, el);
},
[&](UniversalSelector const &s) -> Spec {
return match(s, el);
},
[&](auto &) -> Spec {
return Spec::NOMATCH;
notImplemented();
}
});
}
Expand Down
45 changes: 30 additions & 15 deletions src/web/web-select/select2.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,31 +15,24 @@ struct Combinator {
CHILD,
ADJACENT,
SUBSEQUENT,
COLUMN
};

using enum _Type;
_Type type;
Box<Selector> lhs;
Box<Selector> rhs;
};

struct LogicalCombinator {
enum struct _Type {
COLUMN,
IS,
NOT,
WHERE,
HAS,
};

using enum _Type;
_Type type;
Box<Selector> inner;
Vec<Selector> inners;
};

struct TypeSelector {
TagName type;
};

struct UniversalSelector {
};

struct IdSelector {
String id;
};
Expand All @@ -48,12 +41,34 @@ struct ClassSelector {
String class_;
};

struct AttributeSelector {
enum Case {
SENSITIVE,
INSENSITIVE,
};

enum Match {
PRESENT, //< [attr]

EXACT, //< [attr="value"]
CONTAINS, //< [attr~="value"]
HYPHENATED, //< [attr|="value"]

STR_START_WITH, //< [attr^="value"]
STR_END_WITH, //< [attr$="value"]
STR_CONTAIN, //< [attr*="value"]
};

String name;
String value;
};

using _Selector = Union<
Combinator,
LogicalCombinator,
TypeSelector,
IdSelector,
ClassSelector>;
ClassSelector,
AttributeSelector>;

struct Selector : public _Selector {
using _Selector::_Selector;
Expand Down

0 comments on commit c352663

Please sign in to comment.