Fix a few more cases

From dom/nodes/ParentNode-querySelector-escapes.html
This commit is contained in:
Karl Seguin
2026-02-11 21:11:27 +08:00
parent 3412ff94bc
commit b25c91affd
2 changed files with 55 additions and 4 deletions

View File

@@ -294,3 +294,12 @@
testing.expectEqual('Non-ASCII class 1', document.querySelector('div.café').textContent); testing.expectEqual('Non-ASCII class 1', document.querySelector('div.café').textContent);
testing.expectEqual('Non-ASCII ID 1', document.querySelector('span#niño').textContent); testing.expectEqual('Non-ASCII ID 1', document.querySelector('span#niño').textContent);
</script> </script>
<span id=".,:!">Punctuation test</span>
<script id=escapedPunctuation>
{
// Test escaped punctuation in ID selectors
testing.expectEqual('Punctuation test', document.querySelector('#\\.\\,\\:\\!').textContent);
}
</script>

View File

@@ -49,29 +49,71 @@ const ParseError = error{
StringTooLarge, StringTooLarge,
}; };
// CSS Syntax preprocessing: normalize line endings (CRLF → LF, CR → LF)
// https://drafts.csswg.org/css-syntax/#input-preprocessing
fn preprocessInput(arena: Allocator, input: []const u8) ![]const u8 {
var i = std.mem.indexOfScalar(u8, input, '\r') orelse return input;
var result = try std.ArrayList(u8).initCapacity(arena, input.len);
result.appendSliceAssumeCapacity(input[0..i]);
while (i < input.len) {
const c = input[i];
if (c == '\r') {
result.appendAssumeCapacity('\n');
i += 1;
if (i < input.len and input[i] == '\n') {
i += 1;
}
} else {
result.appendAssumeCapacity(c);
i += 1;
}
}
return result.items;
}
pub fn parseList(arena: Allocator, input: []const u8, page: *Page) ParseError![]const Selector.Selector { pub fn parseList(arena: Allocator, input: []const u8, page: *Page) ParseError![]const Selector.Selector {
// Preprocess input to normalize line endings
const preprocessed = try preprocessInput(arena, input);
var selectors: std.ArrayList(Selector.Selector) = .empty; var selectors: std.ArrayList(Selector.Selector) = .empty;
var remaining = input; var remaining = preprocessed;
while (true) { while (true) {
const trimmed = std.mem.trimLeft(u8, remaining, &std.ascii.whitespace); const trimmed = std.mem.trimLeft(u8, remaining, &std.ascii.whitespace);
if (trimmed.len == 0) break; if (trimmed.len == 0) break;
var comma_pos: usize = trimmed.len; var comma_pos: usize = trimmed.len;
var depth: usize = 0; var depth: usize = 0;
for (trimmed, 0..) |c, i| { var i: usize = 0;
while (i < trimmed.len) {
const c = trimmed[i];
switch (c) { switch (c) {
'(' => depth += 1, '\\' => {
// Skip escape sequence (backslash + next character)
i += 1;
if (i < trimmed.len) i += 1;
},
'(' => {
depth += 1;
i += 1;
},
')' => { ')' => {
if (depth > 0) depth -= 1; if (depth > 0) depth -= 1;
i += 1;
}, },
',' => { ',' => {
if (depth == 0) { if (depth == 0) {
comma_pos = i; comma_pos = i;
break; break;
} }
i += 1;
},
else => {
i += 1;
}, },
else => {},
} }
} }