mirror of
				https://github.com/lightpanda-io/browser.git
				synced 2025-10-30 07:31:47 +00:00 
			
		
		
		
	css: add attribute matcher
This commit is contained in:
		| @@ -83,6 +83,76 @@ test "matchFirst" { | ||||
|             .n = .{ .child = &.{ .name = "p", .sibling = &.{ .name = "p", .att = "foo t1" } } }, | ||||
|             .exp = 1, | ||||
|         }, | ||||
|         .{ | ||||
|             .q = "[foo]", | ||||
|             .n = .{ .child = &.{ .name = "p", .sibling = &.{ .name = "p" } } }, | ||||
|             .exp = 0, | ||||
|         }, | ||||
|         .{ | ||||
|             .q = "[foo]", | ||||
|             .n = .{ .child = &.{ .name = "p", .sibling = &.{ .name = "p", .att = "bar" } } }, | ||||
|             .exp = 1, | ||||
|         }, | ||||
|         .{ | ||||
|             .q = "[foo=bar]", | ||||
|             .n = .{ .child = &.{ .name = "p", .sibling = &.{ .name = "p", .att = "bar" } } }, | ||||
|             .exp = 1, | ||||
|         }, | ||||
|         .{ | ||||
|             .q = "[foo=baz]", | ||||
|             .n = .{ .child = &.{ .name = "p", .sibling = &.{ .name = "p", .att = "bar" } } }, | ||||
|             .exp = 0, | ||||
|         }, | ||||
|         .{ | ||||
|             .q = "[foo!=bar]", | ||||
|             .n = .{ .child = &.{ .name = "p", .sibling = &.{ .name = "p", .att = "bar" } } }, | ||||
|             .exp = 1, | ||||
|         }, | ||||
|         .{ | ||||
|             .q = "[foo!=baz]", | ||||
|             .n = .{ .child = &.{ .name = "p", .sibling = &.{ .name = "p", .att = "bar" } } }, | ||||
|             .exp = 1, | ||||
|         }, | ||||
|         .{ | ||||
|             .q = "[foo~=bar]", | ||||
|             .n = .{ .child = &.{ .name = "p", .sibling = &.{ .name = "p", .att = "baz bar" } } }, | ||||
|             .exp = 1, | ||||
|         }, | ||||
|         .{ | ||||
|             .q = "[foo~=bar]", | ||||
|             .n = .{ .child = &.{ .name = "p", .sibling = &.{ .name = "p", .att = "barbaz" } } }, | ||||
|             .exp = 0, | ||||
|         }, | ||||
|         .{ | ||||
|             .q = "[foo^=bar]", | ||||
|             .n = .{ .child = &.{ .name = "p", .sibling = &.{ .name = "p", .att = "barbaz" } } }, | ||||
|             .exp = 1, | ||||
|         }, | ||||
|         .{ | ||||
|             .q = "[foo$=baz]", | ||||
|             .n = .{ .child = &.{ .name = "p", .sibling = &.{ .name = "p", .att = "barbaz" } } }, | ||||
|             .exp = 1, | ||||
|         }, | ||||
|         .{ | ||||
|             .q = "[foo*=rb]", | ||||
|             .n = .{ .child = &.{ .name = "p", .sibling = &.{ .name = "p", .att = "barbaz" } } }, | ||||
|             .exp = 1, | ||||
|         }, | ||||
|         .{ | ||||
|             .q = "[foo|=bar]", | ||||
|             .n = .{ .child = &.{ .name = "p", .sibling = &.{ .name = "p", .att = "bar" } } }, | ||||
|             .exp = 1, | ||||
|         }, | ||||
|         .{ | ||||
|             .q = "[foo|=bar]", | ||||
|             .n = .{ .child = &.{ .name = "p", .sibling = &.{ .name = "p", .att = "bar-baz" } } }, | ||||
|             .exp = 1, | ||||
|         }, | ||||
|         .{ | ||||
|             .q = "[foo|=bar]", | ||||
|             .n = .{ .child = &.{ .name = "p", .sibling = &.{ .name = "p", .att = "ba" } } }, | ||||
|             .exp = 0, | ||||
|         }, | ||||
|     }; | ||||
|  | ||||
|     for (testcases) |tc| { | ||||
| @@ -127,6 +197,76 @@ test "matchAll" { | ||||
|             .n = .{ .child = &.{ .name = "p", .sibling = &.{ .name = "p", .att = "foo t1" } } }, | ||||
|             .exp = 1, | ||||
|         }, | ||||
|         .{ | ||||
|             .q = "[foo]", | ||||
|             .n = .{ .child = &.{ .name = "p", .sibling = &.{ .name = "p" } } }, | ||||
|             .exp = 0, | ||||
|         }, | ||||
|         .{ | ||||
|             .q = "[foo]", | ||||
|             .n = .{ .child = &.{ .name = "p", .sibling = &.{ .name = "p", .att = "bar" } } }, | ||||
|             .exp = 1, | ||||
|         }, | ||||
|         .{ | ||||
|             .q = "[foo=bar]", | ||||
|             .n = .{ .child = &.{ .name = "p", .sibling = &.{ .name = "p", .att = "bar" } } }, | ||||
|             .exp = 1, | ||||
|         }, | ||||
|         .{ | ||||
|             .q = "[foo=baz]", | ||||
|             .n = .{ .child = &.{ .name = "p", .sibling = &.{ .name = "p", .att = "bar" } } }, | ||||
|             .exp = 0, | ||||
|         }, | ||||
|         .{ | ||||
|             .q = "[foo!=bar]", | ||||
|             .n = .{ .child = &.{ .name = "p", .sibling = &.{ .name = "p", .att = "bar" } } }, | ||||
|             .exp = 1, | ||||
|         }, | ||||
|         .{ | ||||
|             .q = "[foo!=baz]", | ||||
|             .n = .{ .child = &.{ .name = "p", .sibling = &.{ .name = "p", .att = "bar" } } }, | ||||
|             .exp = 2, | ||||
|         }, | ||||
|         .{ | ||||
|             .q = "[foo~=bar]", | ||||
|             .n = .{ .child = &.{ .name = "p", .sibling = &.{ .name = "p", .att = "baz bar" } } }, | ||||
|             .exp = 1, | ||||
|         }, | ||||
|         .{ | ||||
|             .q = "[foo~=bar]", | ||||
|             .n = .{ .child = &.{ .name = "p", .sibling = &.{ .name = "p", .att = "barbaz" } } }, | ||||
|             .exp = 0, | ||||
|         }, | ||||
|         .{ | ||||
|             .q = "[foo^=bar]", | ||||
|             .n = .{ .child = &.{ .name = "p", .sibling = &.{ .name = "p", .att = "barbaz" } } }, | ||||
|             .exp = 1, | ||||
|         }, | ||||
|         .{ | ||||
|             .q = "[foo$=baz]", | ||||
|             .n = .{ .child = &.{ .name = "p", .sibling = &.{ .name = "p", .att = "barbaz" } } }, | ||||
|             .exp = 1, | ||||
|         }, | ||||
|         .{ | ||||
|             .q = "[foo*=rb]", | ||||
|             .n = .{ .child = &.{ .name = "p", .sibling = &.{ .name = "p", .att = "barbaz" } } }, | ||||
|             .exp = 1, | ||||
|         }, | ||||
|         .{ | ||||
|             .q = "[foo|=bar]", | ||||
|             .n = .{ .child = &.{ .name = "p", .sibling = &.{ .name = "p", .att = "bar" } } }, | ||||
|             .exp = 1, | ||||
|         }, | ||||
|         .{ | ||||
|             .q = "[foo|=bar]", | ||||
|             .n = .{ .child = &.{ .name = "p", .sibling = &.{ .name = "p", .att = "bar-baz" } } }, | ||||
|             .exp = 1, | ||||
|         }, | ||||
|         .{ | ||||
|             .q = "[foo|=bar]", | ||||
|             .n = .{ .child = &.{ .name = "p", .sibling = &.{ .name = "p", .att = "ba" } } }, | ||||
|             .exp = 0, | ||||
|         }, | ||||
|     }; | ||||
|  | ||||
|     for (testcases) |tc| { | ||||
|   | ||||
| @@ -166,20 +166,69 @@ pub const Selector = union(enum) { | ||||
|     pseudo_element: PseudoClass, | ||||
|  | ||||
|     // returns true if s is a whitespace-separated list that includes val. | ||||
|     fn contains(haystack: []const u8, needle: []const u8) bool { | ||||
|     fn word(haystack: []const u8, needle: []const u8, ci: bool) bool { | ||||
|         if (haystack.len == 0) return false; | ||||
|         var it = std.mem.splitAny(u8, haystack, " \t\r\n"); // TODO add \f | ||||
|         while (it.next()) |part| { | ||||
|             if (std.mem.eql(u8, part, needle)) return true; | ||||
|             if (eql(part, needle, ci)) return true; | ||||
|         } | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     fn eql(a: []const u8, b: []const u8, ci: bool) bool { | ||||
|         if (ci) return std.ascii.eqlIgnoreCase(a, b); | ||||
|         return std.mem.eql(u8, a, b); | ||||
|     } | ||||
|  | ||||
|     fn starts(haystack: []const u8, needle: []const u8, ci: bool) bool { | ||||
|         if (ci) return std.ascii.startsWithIgnoreCase(haystack, needle); | ||||
|         return std.mem.startsWith(u8, haystack, needle); | ||||
|     } | ||||
|  | ||||
|     fn ends(haystack: []const u8, needle: []const u8, ci: bool) bool { | ||||
|         if (ci) return std.ascii.endsWithIgnoreCase(haystack, needle); | ||||
|         return std.mem.endsWith(u8, haystack, needle); | ||||
|     } | ||||
|  | ||||
|     fn contains(haystack: []const u8, needle: []const u8, ci: bool) bool { | ||||
|         if (ci) return std.ascii.indexOfIgnoreCase(haystack, needle) != null; | ||||
|         return std.mem.indexOf(u8, haystack, needle) != null; | ||||
|     } | ||||
|  | ||||
|     pub fn match(s: Selector, n: anytype) !bool { | ||||
|         return switch (s) { | ||||
|             .tag => |v| n.isElement() and std.ascii.eqlIgnoreCase(v, try n.tag()), | ||||
|             .id => |v| return n.isElement() and std.mem.eql(u8, v, try n.attr("id") orelse return false), | ||||
|             .class => |v| return n.isElement() and contains(try n.attr("class") orelse return false, v), | ||||
|             .class => |v| return n.isElement() and word(try n.attr("class") orelse return false, v, false), | ||||
|             .attribute => |v| { | ||||
|                 const attr = try n.attr(v.key); | ||||
|  | ||||
|                 if (v.op == null) return attr != null; | ||||
|                 if (v.val == null or v.val.?.len == 0) return false; | ||||
|  | ||||
|                 const val = v.val.?; | ||||
|  | ||||
|                 return switch (v.op.?) { | ||||
|                     .eql => attr != null and eql(attr.?, val, v.ci), | ||||
|                     .not_eql => attr == null or !eql(attr.?, val, v.ci), | ||||
|                     .one_of => attr != null and word(attr.?, val, v.ci), | ||||
|                     .prefix => attr != null and starts(attr.?, val, v.ci), | ||||
|                     .suffix => attr != null and ends(attr.?, val, v.ci), | ||||
|                     .contains => attr != null and contains(attr.?, val, v.ci), | ||||
|                     .prefix_hyphen => { | ||||
|                         if (attr == null) return false; | ||||
|                         if (eql(attr.?, val, v.ci)) return true; | ||||
|  | ||||
|                         if (attr.?.len <= val.len) return false; | ||||
|  | ||||
|                         if (!starts(attr.?, val, v.ci)) return false; | ||||
|  | ||||
|                         return attr.?[val.len] == '-'; | ||||
|                     }, | ||||
|                     .regexp => false, // TODO handle regexp attribute operator. | ||||
|                 }; | ||||
|             }, | ||||
|             .never_match => return false, | ||||
|             else => false, | ||||
|         }; | ||||
|     } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Pierre Tachoire
					Pierre Tachoire