Align domainToAscii with current WhatWG spec

The WhatWG URL Standard changed its "domain to ASCII" algorithm when
beStrict is false and the domain is an ASCII string, it now returns
the domain lowercased regardless of Unicode ToASCII's outcome, for web
compatibility. Invalid or ambiguous "xn--" (ACE) labels are no longer
rejected or validated; they are lowercased and accepted, matching
browsers and the web-platform-tests URL cases.

This supersedes the earlier spec revision that only lowercased ASCII
domains whose labels did not start with "xn--". Drop the now-obsolete
"xn--" label detection (which was dead code anyway due to a typo) and
unconditionally lowercase ASCII domains.

Closes gh-37018
This commit is contained in:
Sébastien Deleuze
2026-07-08 15:11:42 +02:00
parent ed13afa0d4
commit 7e0818f4e9
2 changed files with 26 additions and 24 deletions
@@ -23,6 +23,7 @@ import static org.assertj.core.api.Assertions.assertThat;
/**
* @author Arjen Poutsma
* @author Sebastien Deleuze
*/
class WhatWgUrlParserTests {
@@ -43,6 +44,25 @@ class WhatWgUrlParserTests {
testParse("//other.info/parent/../foo/bar", "", "other.info", null, "/foo/bar", null, null);
}
@Test
void parseAsciiHost() {
// Pure ASCII host is lowercased
testParse("https://EXAMPLE.com/foo", "https", "example.com", null, "/foo", null, null);
// ASCII "xn--" (ACE) labels are accepted and lowercased (in any case), not validated or rejected.
// See https://url.spec.whatwg.org/#concept-domain-to-ascii and web-platform-tests url cases.
testParse("https://a.b.c.xn--pokxncvks", "https", "a.b.c.xn--pokxncvks", null, "", null, null);
testParse("https://a.b.c.XN--pokxncvks", "https", "a.b.c.xn--pokxncvks", null, "", null, null);
testParse("https://a.b.c.Xn--pokxncvks", "https", "a.b.c.xn--pokxncvks", null, "", null, null);
// A trailing non-numeric "xn--" label keeps a numeric-looking host as a domain, not an IPv4 address.
testParse("https://10.0.0.xn--pokxncvks", "https", "10.0.0.xn--pokxncvks", null, "", null, null);
testParse("https://10.0.0.XN--pokxncvks", "https", "10.0.0.xn--pokxncvks", null, "", null, null);
testParse("https://10.0.0.xN--pokxncvks", "https", "10.0.0.xn--pokxncvks", null, "", null, null);
// Leading ACE label is handled too, and an empty "xn--" label is accepted (not a failure)
testParse("https://XN--pokxncvks.example", "https", "xn--pokxncvks.example", null, "", null, null);
testParse("https://xn--/", "https", "xn--", null, "/", null, null);
testParse("file://xn--/p", "file", "xn--", null, "/p", null, null);
}
private void testParse(String input, String scheme, @Nullable String host, @Nullable String port, String path, @Nullable String query, @Nullable String fragment) {
WhatWgUrlParser.UrlRecord result = WhatWgUrlParser.parse(input, EMPTY_URL_RECORD, null, null);
assertThat(result.scheme()).as("Invalid scheme").isEqualTo(scheme);