mirror of
https://github.com/lightpanda-io/browser.git
synced 2026-03-28 15:40:04 +00:00
fix: percent-encode pathname in URL.setPathname per URL spec
URL.setPathname() inserted the value verbatim without percent-encoding, so `url.pathname = "c d"` produced `http://a/c d` instead of `http://a/c%20d`. This caused sites using URL polyfills (e.g. Angular's polyfills bundle) to detect broken native URL support and fall back to a polyfill that relies on HTMLInputElement.checkValidity(), which is not implemented — crashing the entire app bootstrap.
This commit is contained in:
@@ -595,11 +595,14 @@ pub fn setPathname(current: [:0]const u8, value: []const u8, allocator: Allocato
|
||||
const search = getSearch(current);
|
||||
const hash = getHash(current);
|
||||
|
||||
// Percent-encode the pathname per the URL spec (spaces → %20, etc.)
|
||||
const encoded = try percentEncodeSegment(allocator, value, .path);
|
||||
|
||||
// Add / prefix if not present and value is not empty
|
||||
const pathname = if (value.len > 0 and value[0] != '/')
|
||||
try std.fmt.allocPrint(allocator, "/{s}", .{value})
|
||||
const pathname = if (encoded.len > 0 and encoded[0] != '/')
|
||||
try std.fmt.allocPrint(allocator, "/{s}", .{encoded})
|
||||
else
|
||||
value;
|
||||
encoded;
|
||||
|
||||
return buildUrl(allocator, protocol, host, pathname, search, hash);
|
||||
}
|
||||
@@ -1422,3 +1425,22 @@ test "URL: getHost" {
|
||||
try testing.expectEqualSlices(u8, "example.com:8080", getHost("https://user:pass@example.com:8080/page"));
|
||||
try testing.expectEqualSlices(u8, "", getHost("not-a-url"));
|
||||
}
|
||||
|
||||
test "URL: setPathname percent-encodes" {
|
||||
// Use arena allocator to match production usage (setPathname makes intermediate allocations)
|
||||
var arena = std.heap.ArenaAllocator.init(testing.allocator);
|
||||
defer arena.deinit();
|
||||
const allocator = arena.allocator();
|
||||
|
||||
// Spaces must be encoded as %20
|
||||
const result1 = try setPathname("http://a/", "c d", allocator);
|
||||
try testing.expectEqualSlices(u8, "http://a/c%20d", result1);
|
||||
|
||||
// Already-encoded sequences must not be double-encoded
|
||||
const result2 = try setPathname("https://example.com/path", "/already%20encoded", allocator);
|
||||
try testing.expectEqualSlices(u8, "https://example.com/already%20encoded", result2);
|
||||
|
||||
// Query and hash must be preserved
|
||||
const result3 = try setPathname("https://example.com/path?a=b#hash", "/new path", allocator);
|
||||
try testing.expectEqualSlices(u8, "https://example.com/new%20path?a=b#hash", result3);
|
||||
}
|
||||
|
||||
@@ -591,6 +591,35 @@
|
||||
testing.expectEqual('/new/path', url.pathname);
|
||||
}
|
||||
|
||||
// Pathname setter must percent-encode spaces and special characters
|
||||
{
|
||||
const url = new URL('http://a/');
|
||||
url.pathname = 'c d';
|
||||
testing.expectEqual('http://a/c%20d', url.href);
|
||||
}
|
||||
|
||||
{
|
||||
const url = new URL('https://example.com/path');
|
||||
url.pathname = '/path with spaces/file name';
|
||||
testing.expectEqual('https://example.com/path%20with%20spaces/file%20name', url.href);
|
||||
testing.expectEqual('/path%20with%20spaces/file%20name', url.pathname);
|
||||
}
|
||||
|
||||
// Already-encoded sequences should not be double-encoded
|
||||
{
|
||||
const url = new URL('https://example.com/path');
|
||||
url.pathname = '/already%20encoded';
|
||||
testing.expectEqual('https://example.com/already%20encoded', url.href);
|
||||
}
|
||||
|
||||
// This is the exact check the URL polyfill uses to decide if native URL is sufficient
|
||||
{
|
||||
const url = new URL('b', 'http://a');
|
||||
url.pathname = 'c d';
|
||||
testing.expectEqual('http://a/c%20d', url.href);
|
||||
testing.expectEqual(true, !!url.searchParams);
|
||||
}
|
||||
|
||||
{
|
||||
const url = new URL('https://example.com/path');
|
||||
url.search = '?a=b';
|
||||
|
||||
Reference in New Issue
Block a user