mirror of
https://github.com/lightpanda-io/browser.git
synced 2025-10-28 22:53:28 +00:00
css: extract public api on its own file
This commit is contained in:
127
src/css/css.zig
Normal file
127
src/css/css.zig
Normal file
@@ -0,0 +1,127 @@
|
||||
// CSS Selector parser and query
|
||||
// This package is a rewrite in Zig of Cascadia CSS Selector parser.
|
||||
// see https://github.com/andybalholm/cascadia
|
||||
const std = @import("std");
|
||||
const Selector = @import("selector.zig").Selector;
|
||||
const parser = @import("parser.zig");
|
||||
|
||||
// Parse parse a selector string and returns the parsed result or an error.
|
||||
pub fn Parse(alloc: std.mem.Allocator, s: []const u8, opts: parser.ParseOptions) parser.ParseError!Selector {
|
||||
var p = parser.Parser{ .s = s, .i = 0, .opts = opts };
|
||||
return p.parse(alloc);
|
||||
}
|
||||
|
||||
test "Parse" {
|
||||
const alloc = std.testing.allocator;
|
||||
|
||||
const testcases = [_][]const u8{
|
||||
"address",
|
||||
"*",
|
||||
"#foo",
|
||||
"li#t1",
|
||||
"*#t4",
|
||||
".t1",
|
||||
"p.t1",
|
||||
"div.teST",
|
||||
".t1.fail",
|
||||
"p.t1.t2",
|
||||
"p.--t1",
|
||||
"p.--t1.--t2",
|
||||
"p[title]",
|
||||
"div[class=\"red\" i]",
|
||||
"address[title=\"foo\"]",
|
||||
"address[title=\"FoOIgnoRECaSe\" i]",
|
||||
"address[title!=\"foo\"]",
|
||||
"address[title!=\"foo\" i]",
|
||||
"p[title!=\"FooBarUFoo\" i]",
|
||||
"[ \t title ~= foo ]",
|
||||
"p[title~=\"FOO\" i]",
|
||||
"p[title~=toofoo i]",
|
||||
"[title~=\"hello world\"]",
|
||||
"[title~=\"hello\" i]",
|
||||
"[title~=\"hello\" I]",
|
||||
"[lang|=\"en\"]",
|
||||
"[lang|=\"EN\" i]",
|
||||
"[lang|=\"EN\" i]",
|
||||
"[title^=\"foo\"]",
|
||||
"[title^=\"foo\" i]",
|
||||
"[title$=\"bar\"]",
|
||||
"[title$=\"BAR\" i]",
|
||||
"[title*=\"bar\"]",
|
||||
"[title*=\"BaRu\" i]",
|
||||
"[title*=\"BaRu\" I]",
|
||||
"p[class$=\" \"]",
|
||||
"p[class$=\"\"]",
|
||||
"p[class^=\" \"]",
|
||||
"p[class^=\"\"]",
|
||||
"p[class*=\" \"]",
|
||||
"p[class*=\"\"]",
|
||||
"input[name=Sex][value=F]",
|
||||
"table[border=\"0\"][cellpadding=\"0\"][cellspacing=\"0\"]",
|
||||
".t1:not(.t2)",
|
||||
"div:not(.t1)",
|
||||
"div:not([class=\"t2\"])",
|
||||
"li:nth-child(odd)",
|
||||
"li:nth-child(even)",
|
||||
"li:nth-child(-n+2)",
|
||||
"li:nth-child(3n+1)",
|
||||
"li:nth-last-child(odd)",
|
||||
"li:nth-last-child(even)",
|
||||
"li:nth-last-child(-n+2)",
|
||||
"li:nth-last-child(3n+1)",
|
||||
"span:first-child",
|
||||
"span:last-child",
|
||||
"p:nth-of-type(2)",
|
||||
"p:nth-last-of-type(2)",
|
||||
"p:last-of-type",
|
||||
"p:first-of-type",
|
||||
"p:only-child",
|
||||
"p:only-of-type",
|
||||
":empty",
|
||||
"div p",
|
||||
"div table p",
|
||||
"div > p",
|
||||
"p ~ p",
|
||||
"p + p",
|
||||
"li, p",
|
||||
"p +/*This is a comment*/ p",
|
||||
"p:contains(\"that wraps\")",
|
||||
"p:containsOwn(\"that wraps\")",
|
||||
":containsOwn(\"inner\")",
|
||||
"p:containsOwn(\"block\")",
|
||||
"div:has(#p1)",
|
||||
"div:has(:containsOwn(\"2\"))",
|
||||
"body :has(:containsOwn(\"2\"))",
|
||||
"body :haschild(:containsOwn(\"2\"))",
|
||||
"p:matches([\\d])",
|
||||
"p:matches([a-z])",
|
||||
"p:matches([a-zA-Z])",
|
||||
"p:matches([^\\d])",
|
||||
"p:matches(^(0|a))",
|
||||
"p:matches(^\\d+$)",
|
||||
"p:not(:matches(^\\d+$))",
|
||||
"div :matchesOwn(^\\d+$)",
|
||||
"[href#=(fina)]:not([href#=(\\/\\/[^\\/]+untrusted)])",
|
||||
"[href#=(^https:\\/\\/[^\\/]*\\/?news)]",
|
||||
":input",
|
||||
":root",
|
||||
"*:root",
|
||||
"html:nth-child(1)",
|
||||
"*:root:first-child",
|
||||
"*:root:nth-child(1)",
|
||||
"a:not(:root)",
|
||||
"body > *:nth-child(3n+2)",
|
||||
"input:disabled",
|
||||
":disabled",
|
||||
":enabled",
|
||||
"div.class1, div.class2",
|
||||
};
|
||||
|
||||
for (testcases) |tc| {
|
||||
const s = Parse(alloc, tc, .{}) catch |e| {
|
||||
std.debug.print("query {s}", .{tc});
|
||||
return e;
|
||||
};
|
||||
defer s.deinit(alloc);
|
||||
}
|
||||
}
|
||||
@@ -50,18 +50,16 @@ pub const ParseOptions = struct {
|
||||
accept_pseudo_elts: bool = true,
|
||||
};
|
||||
|
||||
// Parse parse a selector string and returns the parsed result or an error.
|
||||
pub fn Parse(alloc: std.mem.Allocator, s: []const u8, opts: ParseOptions) ParseError!Selector {
|
||||
var p = Parser{ .s = s, .i = 0, .opts = opts };
|
||||
return p.parseSelector(alloc);
|
||||
}
|
||||
|
||||
const Parser = struct {
|
||||
pub const Parser = struct {
|
||||
s: []const u8, // string to parse
|
||||
i: usize = 0, // current position
|
||||
|
||||
opts: ParseOptions,
|
||||
|
||||
pub fn parse(p: *Parser, alloc: std.mem.Allocator) ParseError!Selector {
|
||||
return p.parseSelector(alloc);
|
||||
}
|
||||
|
||||
// skipWhitespace consumes whitespace characters and comments.
|
||||
// It returns true if there was actually anything to skip.
|
||||
fn skipWhitespace(p: *Parser) bool {
|
||||
@@ -894,118 +892,3 @@ test "parser.parseString" {
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
test "parser." {
|
||||
const alloc = std.testing.allocator;
|
||||
|
||||
const testcases = [_][]const u8{
|
||||
"address",
|
||||
"*",
|
||||
"#foo",
|
||||
"li#t1",
|
||||
"*#t4",
|
||||
".t1",
|
||||
"p.t1",
|
||||
"div.teST",
|
||||
".t1.fail",
|
||||
"p.t1.t2",
|
||||
"p.--t1",
|
||||
"p.--t1.--t2",
|
||||
"p[title]",
|
||||
"div[class=\"red\" i]",
|
||||
"address[title=\"foo\"]",
|
||||
"address[title=\"FoOIgnoRECaSe\" i]",
|
||||
"address[title!=\"foo\"]",
|
||||
"address[title!=\"foo\" i]",
|
||||
"p[title!=\"FooBarUFoo\" i]",
|
||||
"[ \t title ~= foo ]",
|
||||
"p[title~=\"FOO\" i]",
|
||||
"p[title~=toofoo i]",
|
||||
"[title~=\"hello world\"]",
|
||||
"[title~=\"hello\" i]",
|
||||
"[title~=\"hello\" I]",
|
||||
"[lang|=\"en\"]",
|
||||
"[lang|=\"EN\" i]",
|
||||
"[lang|=\"EN\" i]",
|
||||
"[title^=\"foo\"]",
|
||||
"[title^=\"foo\" i]",
|
||||
"[title$=\"bar\"]",
|
||||
"[title$=\"BAR\" i]",
|
||||
"[title*=\"bar\"]",
|
||||
"[title*=\"BaRu\" i]",
|
||||
"[title*=\"BaRu\" I]",
|
||||
"p[class$=\" \"]",
|
||||
"p[class$=\"\"]",
|
||||
"p[class^=\" \"]",
|
||||
"p[class^=\"\"]",
|
||||
"p[class*=\" \"]",
|
||||
"p[class*=\"\"]",
|
||||
"input[name=Sex][value=F]",
|
||||
"table[border=\"0\"][cellpadding=\"0\"][cellspacing=\"0\"]",
|
||||
".t1:not(.t2)",
|
||||
"div:not(.t1)",
|
||||
"div:not([class=\"t2\"])",
|
||||
"li:nth-child(odd)",
|
||||
"li:nth-child(even)",
|
||||
"li:nth-child(-n+2)",
|
||||
"li:nth-child(3n+1)",
|
||||
"li:nth-last-child(odd)",
|
||||
"li:nth-last-child(even)",
|
||||
"li:nth-last-child(-n+2)",
|
||||
"li:nth-last-child(3n+1)",
|
||||
"span:first-child",
|
||||
"span:last-child",
|
||||
"p:nth-of-type(2)",
|
||||
"p:nth-last-of-type(2)",
|
||||
"p:last-of-type",
|
||||
"p:first-of-type",
|
||||
"p:only-child",
|
||||
"p:only-of-type",
|
||||
":empty",
|
||||
"div p",
|
||||
"div table p",
|
||||
"div > p",
|
||||
"p ~ p",
|
||||
"p + p",
|
||||
"li, p",
|
||||
"p +/*This is a comment*/ p",
|
||||
"p:contains(\"that wraps\")",
|
||||
"p:containsOwn(\"that wraps\")",
|
||||
":containsOwn(\"inner\")",
|
||||
"p:containsOwn(\"block\")",
|
||||
"div:has(#p1)",
|
||||
"div:has(:containsOwn(\"2\"))",
|
||||
"body :has(:containsOwn(\"2\"))",
|
||||
"body :haschild(:containsOwn(\"2\"))",
|
||||
"p:matches([\\d])",
|
||||
"p:matches([a-z])",
|
||||
"p:matches([a-zA-Z])",
|
||||
"p:matches([^\\d])",
|
||||
"p:matches(^(0|a))",
|
||||
"p:matches(^\\d+$)",
|
||||
"p:not(:matches(^\\d+$))",
|
||||
"div :matchesOwn(^\\d+$)",
|
||||
"[href#=(fina)]:not([href#=(\\/\\/[^\\/]+untrusted)])",
|
||||
"[href#=(^https:\\/\\/[^\\/]*\\/?news)]",
|
||||
":input",
|
||||
":root",
|
||||
"*:root",
|
||||
"html:nth-child(1)",
|
||||
"*:root:first-child",
|
||||
"*:root:nth-child(1)",
|
||||
"a:not(:root)",
|
||||
"body > *:nth-child(3n+2)",
|
||||
"input:disabled",
|
||||
":disabled",
|
||||
":enabled",
|
||||
"div.class1, div.class2",
|
||||
};
|
||||
|
||||
for (testcases) |tc| {
|
||||
const s = Parse(alloc, tc, .{}) catch |e| {
|
||||
std.debug.print("query {s}", .{tc});
|
||||
return e;
|
||||
};
|
||||
defer s.deinit(alloc);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user