mirror of
https://github.com/lightpanda-io/browser.git
synced 2026-08-02 18:59:36 -04:00
selector: attribute names match case-sensitively on foreign elements
Fixes WPT /dom/nodes/querySelector-mixed-case.html: per the Selectors spec, attribute names in selectors match ASCII case-insensitively against HTML elements (in an HTML document) but case-sensitively against foreign (SVG/MathML) elements. We lowercased the selector's attribute name at parse time for everyone, so [viewBox] never matched the case-preserved viewBox attribute on an SVG element. The parsed attribute selector now also keeps the name as written; matching picks the lowercased name for HTML elements (whose stored attributes are normalized) and the original for foreign elements (whose attributes are stored as written). Coverage: /dom/nodes/querySelector-mixed-case.html 0/1 -> 1/1 (fully green); Element-matches.html stays 669/669. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
committed by
Karl Seguin
parent
40c18f6142
commit
9ff221e2c0
@@ -445,7 +445,11 @@ fn matchesPart(el: *Node.Element, part: Part, scope: *Node, frame: *Frame) bool
|
||||
}
|
||||
|
||||
fn matchesAttribute(el: *Node.Element, attr: Selector.Attribute) bool {
|
||||
const value = el.getAttributeSafe(attr.name) orelse {
|
||||
// Attribute names match ASCII case-insensitively on HTML elements (both
|
||||
// sides lowercased) and case-sensitively on foreign elements, whose
|
||||
// attributes are stored as written.
|
||||
const name = if (el._namespace == .html) attr.name else attr.original_name;
|
||||
const value = el.getAttributeSafe(name) orelse {
|
||||
return false;
|
||||
};
|
||||
|
||||
|
||||
@@ -962,6 +962,8 @@ fn attribute(self: *Parser, arena: Allocator) !Selector.Attribute {
|
||||
}
|
||||
|
||||
const attr_name = try self.attributeName(arena);
|
||||
// As written, for the case-sensitive match on foreign elements.
|
||||
const original_name = try arena.dupe(u8, attr_name);
|
||||
|
||||
// Normalize the name to lowercase for fast matching (consistent with Attribute.normalizeNameForLookup)
|
||||
const name = try Attribute.normalizeNameForLookupAlloc(arena, .wrap(attr_name));
|
||||
@@ -974,7 +976,7 @@ fn attribute(self: *Parser, arena: Allocator) !Selector.Attribute {
|
||||
if (self.peek() == ']') {
|
||||
self.input = self.input[1..];
|
||||
}
|
||||
return .{ .name = name, .matcher = .presence, .case_insensitive = case_insensitive };
|
||||
return .{ .name = name, .original_name = .wrap(original_name), .matcher = .presence, .case_insensitive = case_insensitive };
|
||||
}
|
||||
|
||||
const matcher_type = try self.attributeMatcher();
|
||||
@@ -1012,7 +1014,7 @@ fn attribute(self: *Parser, arena: Allocator) !Selector.Attribute {
|
||||
.presence => unreachable,
|
||||
};
|
||||
|
||||
return .{ .name = name, .matcher = matcher, .case_insensitive = case_insensitive };
|
||||
return .{ .name = name, .original_name = .wrap(original_name), .matcher = matcher, .case_insensitive = case_insensitive };
|
||||
}
|
||||
|
||||
fn attributeName(self: *Parser, arena: Allocator) ![]const u8 {
|
||||
|
||||
@@ -229,7 +229,11 @@ pub const Part = union(enum) {
|
||||
};
|
||||
|
||||
pub const Attribute = struct {
|
||||
// Lowercased for the HTML-element case-insensitive match.
|
||||
name: String,
|
||||
// As written in the selector: attribute names match case-sensitively on
|
||||
// foreign (SVG/MathML) elements, whose attributes are stored unnormalized.
|
||||
original_name: String,
|
||||
matcher: AttributeMatcher,
|
||||
case_insensitive: bool,
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user