Skip to content

fix: [#2088] Handle CSS hex escape sequences in querySelector#2134

Open
coffeeandwork wants to merge 1 commit intocapricorn86:masterfrom
coffeeandwork:fix/2088-queryselector-css-hex-escapes
Open

fix: [#2088] Handle CSS hex escape sequences in querySelector#2134
coffeeandwork wants to merge 1 commit intocapricorn86:masterfrom
coffeeandwork:fix/2088-queryselector-css-hex-escapes

Conversation

@coffeeandwork
Copy link
Copy Markdown
Contributor

querySelector and querySelectorAll throw a SyntaxError (or silently return null) when the selector contains CSS hex escape sequences like #\39 7e356d3-..., which is what CSS.escape() produces for IDs starting with a digit.

The root cause was in SelectorParser:

  1. SELECTOR_GROUP_REGEXP wasn't escape-aware — it saw the trailing space in \39 as a descendant combinator and split the selector in half.
  2. SELECTOR_REGEXP's ID/class/attribute patterns only handled single-character escapes (\.), not hex escapes (\39 ), so they couldn't consume the full escaped identifier.
  3. IDs, classes, and attribute names were "unescaped" by just stripping backslashes (/\\/g), which turned \39 7e... into 397e... instead of decoding \39 as the character 9. The cssUnescape() method that handles this correctly already existed — it just wasn't being used for these cases.

Changes

  • Added a non-capturing escape sequence alternative to SELECTOR_GROUP_REGEXP so hex/character escapes are consumed before their trailing space can be misread as a combinator
  • Updated SELECTOR_REGEXP to match hex escapes (\\[0-9a-fA-F]{1,6}\s?) in ID, class, attribute name, and unquoted attribute value patterns
  • Switched ID/class/attribute name handling from the naive backslash strip to cssUnescape()
  • Removed the now-unused ESCAPED_CHARACTER_REGEXP

Testing

  • Added tests for CSS.escape() with a UUID ID, hex escapes in ID selectors, and hex escapes in class selectors
  • All existing querySelector tests still pass

Fixes #2088

…tor selectors

The selector parser was treating the trailing space in CSS hex escapes
(e.g. \39 ) as a descendant combinator, causing selectors produced by
CSS.escape() to throw or silently fail when IDs start with a digit.

Fixed the group regexp to skip over escape sequences, updated the
selector regexp so ID/class/attribute patterns can consume hex escapes,
and switched ID/class/attribute name unescaping from a naive backslash
strip to the existing cssUnescape() which properly decodes hex values.
@coffeeandwork
Copy link
Copy Markdown
Contributor Author

The CI failure is in SyncFetch.test.ts — specifically the cache revalidation test that checks If-Modified-Since behavior with a small max-age. It's a timing issue: when CI is slow, the cache expires before the second request goes out, so the test sees stale headers (max-age=0.0001) instead of the revalidated ones (max-age=1).

This is a known flaky test — #2013 tracks it and there are already PRs (#2014, #2116) trying to fix it. Unrelated to the selector parser changes here. All 157 querySelector tests passed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

querySelector throws SyntaxError on selectors containing CSS hex escapes (e.g. #\39 7e...)

2 participants