mirror of
https://github.com/lightpanda-io/browser.git
synced 2025-10-29 15:13: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,
|
accept_pseudo_elts: bool = true,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Parse parse a selector string and returns the parsed result or an error.
|
pub const Parser = struct {
|
||||||
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 {
|
|
||||||
s: []const u8, // string to parse
|
s: []const u8, // string to parse
|
||||||
i: usize = 0, // current position
|
i: usize = 0, // current position
|
||||||
|
|
||||||
opts: ParseOptions,
|
opts: ParseOptions,
|
||||||
|
|
||||||
|
pub fn parse(p: *Parser, alloc: std.mem.Allocator) ParseError!Selector {
|
||||||
|
return p.parseSelector(alloc);
|
||||||
|
}
|
||||||
|
|
||||||
// skipWhitespace consumes whitespace characters and comments.
|
// skipWhitespace consumes whitespace characters and comments.
|
||||||
// It returns true if there was actually anything to skip.
|
// It returns true if there was actually anything to skip.
|
||||||
fn skipWhitespace(p: *Parser) bool {
|
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