mirror of
https://github.com/lightpanda-io/browser.git
synced 2025-10-29 15:13:28 +00:00
css: implement :only-child and :only-of-type
This commit is contained in:
@@ -399,6 +399,70 @@ test "matchAll" {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
test "pseudo class" {
|
||||||
|
const alloc = std.testing.allocator;
|
||||||
|
|
||||||
|
var matcher = Matcher.init(alloc);
|
||||||
|
defer matcher.deinit();
|
||||||
|
|
||||||
|
var p1: Node = .{ .name = "p" };
|
||||||
|
var p2: Node = .{ .name = "p" };
|
||||||
|
var a1: Node = .{ .name = "a" };
|
||||||
|
|
||||||
|
p1.sibling = &p2;
|
||||||
|
p2.prev = &p1;
|
||||||
|
|
||||||
|
p2.sibling = &a1;
|
||||||
|
a1.prev = &p2;
|
||||||
|
|
||||||
|
var root: Node = .{ .child = &p1, .last = &a1 };
|
||||||
|
p1.par = &root;
|
||||||
|
p2.par = &root;
|
||||||
|
a1.par = &root;
|
||||||
|
|
||||||
|
const testcases = [_]struct {
|
||||||
|
q: []const u8,
|
||||||
|
n: Node,
|
||||||
|
exp: ?*const Node,
|
||||||
|
}{
|
||||||
|
.{ .q = "p:only-child", .n = root, .exp = null },
|
||||||
|
.{ .q = "a:only-of-type", .n = root, .exp = &a1 },
|
||||||
|
};
|
||||||
|
|
||||||
|
for (testcases) |tc| {
|
||||||
|
matcher.reset();
|
||||||
|
|
||||||
|
const s = try css.parse(alloc, tc.q, .{});
|
||||||
|
defer s.deinit(alloc);
|
||||||
|
|
||||||
|
css.matchAll(s, &tc.n, &matcher) catch |e| {
|
||||||
|
std.debug.print("query: {s}, parsed selector: {any}\n", .{ tc.q, s });
|
||||||
|
return e;
|
||||||
|
};
|
||||||
|
|
||||||
|
if (tc.exp) |exp_n| {
|
||||||
|
const exp: usize = 1;
|
||||||
|
std.testing.expectEqual(exp, matcher.nodes.items.len) catch |e| {
|
||||||
|
std.debug.print("query: {s}, parsed selector: {any}\n", .{ tc.q, s });
|
||||||
|
return e;
|
||||||
|
};
|
||||||
|
|
||||||
|
std.testing.expectEqual(exp_n, matcher.nodes.items[0]) catch |e| {
|
||||||
|
std.debug.print("query: {s}, parsed selector: {any}\n", .{ tc.q, s });
|
||||||
|
return e;
|
||||||
|
};
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const exp: usize = 0;
|
||||||
|
std.testing.expectEqual(exp, matcher.nodes.items.len) catch |e| {
|
||||||
|
std.debug.print("query: {s}, parsed selector: {any}\n", .{ tc.q, s });
|
||||||
|
return e;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
test "nth pseudo class" {
|
test "nth pseudo class" {
|
||||||
const alloc = std.testing.allocator;
|
const alloc = std.testing.allocator;
|
||||||
|
|
||||||
|
|||||||
@@ -145,6 +145,7 @@ pub const Selector = union(enum) {
|
|||||||
UnknownCombinedCombinator,
|
UnknownCombinedCombinator,
|
||||||
UnsupportedRelativePseudoClass,
|
UnsupportedRelativePseudoClass,
|
||||||
UnsupportedContainsPseudoClass,
|
UnsupportedContainsPseudoClass,
|
||||||
|
UnsupportedPseudoClass,
|
||||||
UnsupportedRegexpPseudoClass,
|
UnsupportedRegexpPseudoClass,
|
||||||
UnsupportedAttrRegexpOperator,
|
UnsupportedAttrRegexpOperator,
|
||||||
};
|
};
|
||||||
@@ -315,13 +316,61 @@ pub const Selector = union(enum) {
|
|||||||
}
|
}
|
||||||
return nthChildMatch(v.a, v.b, v.last, v.of_type, n);
|
return nthChildMatch(v.a, v.b, v.last, v.of_type, n);
|
||||||
},
|
},
|
||||||
.pseudo_class => return false,
|
.pseudo_class => |v| {
|
||||||
.pseudo_class_only_child => return false,
|
switch (v) {
|
||||||
|
.input => return Error.UnsupportedPseudoClass,
|
||||||
|
.empty => return Error.UnsupportedPseudoClass,
|
||||||
|
.root => return Error.UnsupportedPseudoClass,
|
||||||
|
.link => return Error.UnsupportedPseudoClass,
|
||||||
|
.enabled => return Error.UnsupportedPseudoClass,
|
||||||
|
.disabled => return Error.UnsupportedPseudoClass,
|
||||||
|
.checked => return Error.UnsupportedPseudoClass,
|
||||||
|
.visited => return Error.UnsupportedPseudoClass,
|
||||||
|
.hover => return Error.UnsupportedPseudoClass,
|
||||||
|
.active => return Error.UnsupportedPseudoClass,
|
||||||
|
.focus => return Error.UnsupportedPseudoClass,
|
||||||
|
.target => return Error.UnsupportedPseudoClass,
|
||||||
|
|
||||||
|
// all others pseudo class are handled by specialized
|
||||||
|
// pseudo_class_X selectors.
|
||||||
|
else => return Error.UnsupportedPseudoClass,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
.pseudo_class_only_child => |v| onlyChildMatch(v, n),
|
||||||
.pseudo_class_lang => return false,
|
.pseudo_class_lang => return false,
|
||||||
.pseudo_element => return false,
|
.pseudo_element => return false,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// onlyChildMatch implements :only-child
|
||||||
|
// If `ofType` is true, it implements :only-of-type instead.
|
||||||
|
fn onlyChildMatch(of_type: bool, n: anytype) anyerror!bool {
|
||||||
|
if (!n.isElement()) return false;
|
||||||
|
|
||||||
|
const p = try n.parent();
|
||||||
|
if (p == null) return false;
|
||||||
|
|
||||||
|
const ntag = try n.tag();
|
||||||
|
|
||||||
|
var count: usize = 0;
|
||||||
|
var c = try p.?.firstChild();
|
||||||
|
// loop hover all n siblings.
|
||||||
|
while (c != null) {
|
||||||
|
// ignore non elements or others tags if of-type is true.
|
||||||
|
if (!c.?.isElement() or (of_type and !std.mem.eql(u8, ntag, try c.?.tag()))) {
|
||||||
|
c = try c.?.nextSibling();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
count += 1;
|
||||||
|
if (count > 1) return false;
|
||||||
|
|
||||||
|
c = try c.?.nextSibling();
|
||||||
|
}
|
||||||
|
|
||||||
|
return count == 1;
|
||||||
|
}
|
||||||
|
|
||||||
// simpleNthLastChildMatch implements :nth-last-child(b).
|
// simpleNthLastChildMatch implements :nth-last-child(b).
|
||||||
// If ofType is true, implements :nth-last-of-type instead.
|
// If ofType is true, implements :nth-last-of-type instead.
|
||||||
fn simpleNthLastChildMatch(b: isize, of_type: bool, n: anytype) anyerror!bool {
|
fn simpleNthLastChildMatch(b: isize, of_type: bool, n: anytype) anyerror!bool {
|
||||||
|
|||||||
Reference in New Issue
Block a user