Skip to content

Commit 98cad6b

Browse files
committed
Accept popover-over pseudo selector
Optimize pseudo-selector parsing. Make comparison case insensitive, bucket comparisons by length, and process input as integers.
1 parent bdc49a6 commit 98cad6b

File tree

2 files changed

+99
-43
lines changed

2 files changed

+99
-43
lines changed

src/browser/css/parser.zig

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -605,7 +605,7 @@ pub const Parser = struct {
605605
.after, .backdrop, .before, .cue, .first_letter => return .{ .pseudo_element = pseudo_class },
606606
.first_line, .grammar_error, .marker, .placeholder => return .{ .pseudo_element = pseudo_class },
607607
.selection, .spelling_error => return .{ .pseudo_element = pseudo_class },
608-
.modal => return .{ .pseudo_element = pseudo_class },
608+
.modal, .popover_open => return .{ .pseudo_element = pseudo_class },
609609
}
610610
}
611611

src/browser/css/selector.zig

Lines changed: 98 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,7 @@ pub const PseudoClass = enum {
9999
selection,
100100
spelling_error,
101101
modal,
102+
popover_open,
102103

103104
pub const Error = error{
104105
InvalidPseudoClass,
@@ -114,52 +115,107 @@ pub const PseudoClass = enum {
114115
}
115116

116117
pub fn parse(s: []const u8) Error!PseudoClass {
117-
if (std.ascii.eqlIgnoreCase(s, "not")) return .not;
118-
if (std.ascii.eqlIgnoreCase(s, "has")) return .has;
119-
if (std.ascii.eqlIgnoreCase(s, "haschild")) return .haschild;
120-
if (std.ascii.eqlIgnoreCase(s, "contains")) return .contains;
121-
if (std.ascii.eqlIgnoreCase(s, "containsown")) return .containsown;
122-
if (std.ascii.eqlIgnoreCase(s, "matches")) return .matches;
123-
if (std.ascii.eqlIgnoreCase(s, "matchesown")) return .matchesown;
124-
if (std.ascii.eqlIgnoreCase(s, "nth-child")) return .nth_child;
125-
if (std.ascii.eqlIgnoreCase(s, "nth-last-child")) return .nth_last_child;
126-
if (std.ascii.eqlIgnoreCase(s, "nth-of-type")) return .nth_of_type;
127-
if (std.ascii.eqlIgnoreCase(s, "nth-last-of-type")) return .nth_last_of_type;
128-
if (std.ascii.eqlIgnoreCase(s, "first-child")) return .first_child;
129-
if (std.ascii.eqlIgnoreCase(s, "last-child")) return .last_child;
130-
if (std.ascii.eqlIgnoreCase(s, "first-of-type")) return .first_of_type;
131-
if (std.ascii.eqlIgnoreCase(s, "last-of-type")) return .last_of_type;
132-
if (std.ascii.eqlIgnoreCase(s, "only-child")) return .only_child;
133-
if (std.ascii.eqlIgnoreCase(s, "only-of-type")) return .only_of_type;
134-
if (std.ascii.eqlIgnoreCase(s, "input")) return .input;
135-
if (std.ascii.eqlIgnoreCase(s, "empty")) return .empty;
136-
if (std.ascii.eqlIgnoreCase(s, "root")) return .root;
137-
if (std.ascii.eqlIgnoreCase(s, "link")) return .link;
138-
if (std.ascii.eqlIgnoreCase(s, "lang")) return .lang;
139-
if (std.ascii.eqlIgnoreCase(s, "enabled")) return .enabled;
140-
if (std.ascii.eqlIgnoreCase(s, "disabled")) return .disabled;
141-
if (std.ascii.eqlIgnoreCase(s, "checked")) return .checked;
142-
if (std.ascii.eqlIgnoreCase(s, "visited")) return .visited;
143-
if (std.ascii.eqlIgnoreCase(s, "hover")) return .hover;
144-
if (std.ascii.eqlIgnoreCase(s, "active")) return .active;
145-
if (std.ascii.eqlIgnoreCase(s, "focus")) return .focus;
146-
if (std.ascii.eqlIgnoreCase(s, "target")) return .target;
147-
if (std.ascii.eqlIgnoreCase(s, "after")) return .after;
148-
if (std.ascii.eqlIgnoreCase(s, "backdrop")) return .backdrop;
149-
if (std.ascii.eqlIgnoreCase(s, "before")) return .before;
150-
if (std.ascii.eqlIgnoreCase(s, "cue")) return .cue;
151-
if (std.ascii.eqlIgnoreCase(s, "first-letter")) return .first_letter;
152-
if (std.ascii.eqlIgnoreCase(s, "first-line")) return .first_line;
153-
if (std.ascii.eqlIgnoreCase(s, "grammar-error")) return .grammar_error;
154-
if (std.ascii.eqlIgnoreCase(s, "marker")) return .marker;
155-
if (std.ascii.eqlIgnoreCase(s, "placeholder")) return .placeholder;
156-
if (std.ascii.eqlIgnoreCase(s, "selection")) return .selection;
157-
if (std.ascii.eqlIgnoreCase(s, "spelling-error")) return .spelling_error;
158-
if (std.ascii.eqlIgnoreCase(s, "modal")) return .modal;
118+
const longest_selector = "nth-last-of-type";
119+
if (s.len > longest_selector.len) {
120+
return Error.InvalidPseudoClass;
121+
}
122+
123+
var buf: [longest_selector.len]u8 = undefined;
124+
const selector = std.ascii.lowerString(&buf, s);
125+
126+
switch (selector.len) {
127+
3 => switch (@as(u24, @bitCast(selector[0..3].*))) {
128+
asUint(u24, "cue") => return .cue,
129+
asUint(u24, "has") => return .has,
130+
asUint(u24, "not") => return .not,
131+
else => {},
132+
},
133+
4 => switch (@as(u32, @bitCast(selector[0..4].*))) {
134+
asUint(u32, "lang") => return .lang,
135+
asUint(u32, "link") => return .link,
136+
asUint(u32, "root") => return .root,
137+
else => {},
138+
},
139+
5 => switch (@as(u40, @bitCast(selector[0..5].*))) {
140+
asUint(u40, "after") => return .after,
141+
asUint(u40, "empty") => return .empty,
142+
asUint(u40, "focus") => return .focus,
143+
asUint(u40, "hover") => return .hover,
144+
asUint(u40, "input") => return .input,
145+
asUint(u40, "modal") => return .modal,
146+
else => {},
147+
},
148+
6 => switch (@as(u48, @bitCast(selector[0..6].*))) {
149+
asUint(u48, "active") => return .active,
150+
asUint(u48, "before") => return .before,
151+
asUint(u48, "marker") => return .marker,
152+
asUint(u48, "target") => return .target,
153+
else => {},
154+
},
155+
7 => switch (@as(u56, @bitCast(selector[0..7].*))) {
156+
asUint(u56, "checked") => return .checked,
157+
asUint(u56, "enabled") => return .enabled,
158+
asUint(u56, "matches") => return .matches,
159+
asUint(u56, "visited") => return .visited,
160+
else => {},
161+
},
162+
8 => switch (@as(u64, @bitCast(selector[0..8].*))) {
163+
asUint(u64, "backdrop") => return .backdrop,
164+
asUint(u64, "contains") => return .contains,
165+
asUint(u64, "disabled") => return .disabled,
166+
asUint(u64, "haschild") => return .haschild,
167+
else => {},
168+
},
169+
9 => switch (@as(u72, @bitCast(selector[0..9].*))) {
170+
asUint(u72, "nth-child") => return .nth_child,
171+
asUint(u72, "selection") => return .selection,
172+
else => {},
173+
},
174+
10 => switch (@as(u80, @bitCast(selector[0..10].*))) {
175+
asUint(u80, "first-line") => return .first_line,
176+
asUint(u80, "last-child") => return .last_child,
177+
asUint(u80, "matchesown") => return .matchesown,
178+
asUint(u80, "only-child") => return .only_child,
179+
else => {},
180+
},
181+
11 => switch (@as(u88, @bitCast(selector[0..11].*))) {
182+
asUint(u88, "containsown") => return .containsown,
183+
asUint(u88, "first-child") => return .first_child,
184+
asUint(u88, "nth-of-type") => return .nth_of_type,
185+
asUint(u88, "placeholder") => return .placeholder,
186+
else => {},
187+
},
188+
12 => switch (@as(u96, @bitCast(selector[0..12].*))) {
189+
asUint(u96, "first-letter") => return .first_letter,
190+
asUint(u96, "last-of-type") => return .last_of_type,
191+
asUint(u96, "only-of-type") => return .only_of_type,
192+
asUint(u96, "popover-open") => return .popover_open,
193+
else => {},
194+
},
195+
13 => switch (@as(u104, @bitCast(selector[0..13].*))) {
196+
asUint(u104, "first-of-type") => return .first_of_type,
197+
asUint(u104, "grammar-error") => return .grammar_error,
198+
else => {},
199+
},
200+
14 => switch (@as(u112, @bitCast(selector[0..14].*))) {
201+
asUint(u112, "nth-last-child") => return .nth_last_child,
202+
asUint(u112, "spelling-error") => return .spelling_error,
203+
else => {},
204+
},
205+
16 => switch (@as(u128, @bitCast(selector[0..16].*))) {
206+
asUint(u128, "nth-last-of-type") => return .nth_last_of_type,
207+
else => {},
208+
},
209+
else => {},
210+
}
159211
return Error.InvalidPseudoClass;
160212
}
161213
};
162214

215+
fn asUint(comptime T: type, comptime string: []const u8) T {
216+
return @bitCast(string[0..string.len].*);
217+
}
218+
163219
pub const Selector = union(enum) {
164220
pub const Error = error{
165221
UnknownCombinedCombinator,

0 commit comments

Comments
 (0)