mirror of
https://github.com/lightpanda-io/browser.git
synced 2026-07-31 17:55:59 -04:00
Merge pull request #2751 from lightpanda-io/url_parse_and_setters
webapi, url: Add URL.parse, better setter compliance
This commit is contained in:
@@ -330,6 +330,37 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<script id=parse_static>
|
||||
testing.expectEqual('function', typeof URL.parse);
|
||||
|
||||
// Parseable input returns a URL instance (mirrors the constructor).
|
||||
{
|
||||
const url = URL.parse('https://example.com/path?a=b#h');
|
||||
testing.expectEqual(true, url instanceof URL);
|
||||
testing.expectEqual('https://example.com/path?a=b#h', url.href);
|
||||
}
|
||||
|
||||
// Relative input resolves against a valid base.
|
||||
{
|
||||
const url = URL.parse('/x', 'http://example.com/a/b');
|
||||
testing.expectEqual('http://example.com/x', url.href);
|
||||
}
|
||||
|
||||
// Unlike the constructor, a parse failure returns null instead of throwing.
|
||||
{
|
||||
testing.expectEqual(null, URL.parse('foo.js'));
|
||||
testing.expectEqual(null, URL.parse(''));
|
||||
testing.expectEqual(null, URL.parse('http://example.com', 'invalid base'));
|
||||
}
|
||||
|
||||
// Each call returns a distinct object.
|
||||
{
|
||||
const a = URL.parse('https://example.com/');
|
||||
const b = URL.parse('https://example.com/');
|
||||
testing.expectEqual(false, a === b);
|
||||
}
|
||||
</script>
|
||||
|
||||
<script id=username>
|
||||
// Username is parsed from the userinfo component.
|
||||
{
|
||||
@@ -412,6 +443,14 @@
|
||||
url.username = 'a b';
|
||||
testing.expectEqual('a%20b', url.username);
|
||||
}
|
||||
|
||||
// A URL that cannot have a username silently ignores the setter.
|
||||
{
|
||||
const url = new URL('mailto:foo@bar.com');
|
||||
url.username = 'user';
|
||||
testing.expectEqual('', url.username);
|
||||
testing.expectEqual('mailto:foo@bar.com', url.href);
|
||||
}
|
||||
</script>
|
||||
|
||||
<script id=hostname>
|
||||
@@ -465,6 +504,16 @@
|
||||
testing.expectEqual('newhost.com', url.hostname);
|
||||
testing.expectEqual('https://newhost.com:8080/path', url.href);
|
||||
}
|
||||
|
||||
// An invalid hostname is silently ignored, leaving it unchanged (the setter
|
||||
// must never throw, per WHATWG).
|
||||
{
|
||||
const url = new URL('https://example.com/path');
|
||||
url.hostname = ''; // empty host is invalid for a special scheme
|
||||
testing.expectEqual('example.com', url.hostname);
|
||||
url.hostname = '[google.com]'; // broken IPv6 literal
|
||||
testing.expectEqual('example.com', url.hostname);
|
||||
}
|
||||
</script>
|
||||
|
||||
<script id=host>
|
||||
@@ -530,6 +579,16 @@
|
||||
testing.expectEqual('newhost.com', url.host);
|
||||
testing.expectEqual('https://newhost.com/path', url.href);
|
||||
}
|
||||
|
||||
// An invalid host is silently ignored, leaving it unchanged (the setter must
|
||||
// never throw, per WHATWG).
|
||||
{
|
||||
const url = new URL('https://example.com/path');
|
||||
url.host = ''; // empty host is invalid for a special scheme
|
||||
testing.expectEqual('example.com', url.host);
|
||||
url.host = '[::1.2.3.]'; // broken IPv6 literal
|
||||
testing.expectEqual('example.com', url.host);
|
||||
}
|
||||
</script>
|
||||
|
||||
<script id=port>
|
||||
@@ -615,6 +674,24 @@
|
||||
testing.expectEqual('https:', url.protocol);
|
||||
testing.expectEqual('https://example.com/path', url.href);
|
||||
}
|
||||
|
||||
// An invalid scheme is silently ignored, leaving the protocol unchanged (the
|
||||
// setter must never throw, per WHATWG).
|
||||
{
|
||||
const url = new URL('https://example.com/path');
|
||||
url.protocol = ''; // the empty string is not a valid scheme
|
||||
testing.expectEqual('https:', url.protocol);
|
||||
url.protocol = '0b'; // a scheme cannot start with a digit
|
||||
testing.expectEqual('https:', url.protocol);
|
||||
}
|
||||
|
||||
// Can't switch a special scheme to a non-special one.
|
||||
{
|
||||
const url = new URL('http://example.com/path');
|
||||
url.protocol = 'ssh';
|
||||
testing.expectEqual('http:', url.protocol);
|
||||
testing.expectEqual('http://example.com/path', url.href);
|
||||
}
|
||||
</script>
|
||||
|
||||
<script id=pathname>
|
||||
|
||||
@@ -73,11 +73,8 @@ pub fn getUsername(self: *const URL) []const u8 {
|
||||
return out[0..len];
|
||||
}
|
||||
|
||||
pub fn setUsername(self: *URL, value: []const u8) !void {
|
||||
const res = U.url_set_username(self._url, value.ptr, value.len);
|
||||
if (res != 0) {
|
||||
return error.SetUsername;
|
||||
}
|
||||
pub fn setUsername(self: *URL, value: []const u8) void {
|
||||
_ = U.url_set_username(self._url, value.ptr, value.len);
|
||||
}
|
||||
|
||||
pub fn getPassword(self: *const URL) []const u8 {
|
||||
@@ -90,11 +87,8 @@ pub fn getPassword(self: *const URL) []const u8 {
|
||||
return out[0..len];
|
||||
}
|
||||
|
||||
pub fn setPassword(self: *URL, value: []const u8) !void {
|
||||
const res = U.url_set_password(self._url, value.ptr, value.len);
|
||||
if (res != 0) {
|
||||
return error.SetPassword;
|
||||
}
|
||||
pub fn setPassword(self: *URL, value: []const u8) void {
|
||||
_ = U.url_set_password(self._url, value.ptr, value.len);
|
||||
}
|
||||
|
||||
pub fn getPathname(self: *const URL) []const u8 {
|
||||
@@ -104,11 +98,8 @@ pub fn getPathname(self: *const URL) []const u8 {
|
||||
return out[0..len];
|
||||
}
|
||||
|
||||
pub fn setPathname(self: *URL, value: []const u8) !void {
|
||||
const res = U.url_set_path(self._url, value.ptr, value.len);
|
||||
if (res != 0) {
|
||||
return error.SetPathname;
|
||||
}
|
||||
pub fn setPathname(self: *URL, value: []const u8) void {
|
||||
_ = U.url_set_path(self._url, value.ptr, value.len);
|
||||
}
|
||||
|
||||
pub fn getProtocol(self: *const URL) []const u8 {
|
||||
@@ -120,11 +111,8 @@ pub fn getProtocol(self: *const URL) []const u8 {
|
||||
return out[0 .. len + 1];
|
||||
}
|
||||
|
||||
pub fn setProtocol(self: *URL, value: []const u8) !void {
|
||||
const res = U.url_set_scheme(self._url, value.ptr, value.len);
|
||||
if (res != 0) {
|
||||
return error.SetProtocol;
|
||||
}
|
||||
pub fn setProtocol(self: *URL, value: []const u8) void {
|
||||
_ = U.url_set_scheme(self._url, value.ptr, value.len);
|
||||
}
|
||||
|
||||
pub fn getHostname(self: *const URL) []const u8 {
|
||||
@@ -136,11 +124,8 @@ pub fn getHostname(self: *const URL) []const u8 {
|
||||
return out[0..len];
|
||||
}
|
||||
|
||||
pub fn setHostname(self: *URL, value: []const u8) !void {
|
||||
const res = U.url_set_hostname(self._url, value.ptr, value.len);
|
||||
if (res != 0) {
|
||||
return error.SetHostname;
|
||||
}
|
||||
pub fn setHostname(self: *URL, value: []const u8) void {
|
||||
_ = U.url_set_hostname(self._url, value.ptr, value.len);
|
||||
}
|
||||
|
||||
pub fn getHost(self: *const URL) []const u8 {
|
||||
@@ -152,11 +137,8 @@ pub fn getHost(self: *const URL) []const u8 {
|
||||
return out[0..len];
|
||||
}
|
||||
|
||||
pub fn setHost(self: *URL, value: []const u8) !void {
|
||||
const res = U.url_set_host(self._url, value.ptr, value.len);
|
||||
if (res != 0) {
|
||||
return error.SetHost;
|
||||
}
|
||||
pub fn setHost(self: *URL, value: []const u8) void {
|
||||
_ = U.url_set_host(self._url, value.ptr, value.len);
|
||||
}
|
||||
|
||||
pub fn getPort(self: *URL) []const u8 {
|
||||
@@ -219,9 +201,8 @@ pub fn setSearch(self: *URL, value: []const u8, exec: *const Execution) !void {
|
||||
// Strip a single leading '?', then set the query.
|
||||
const query = if (value[0] == '?') value[1..] else value;
|
||||
|
||||
const res = U.url_set_query(self._url, query.ptr, query.len);
|
||||
if (res != 0) {
|
||||
return error.SetSearch;
|
||||
if (U.url_set_query(self._url, query.ptr, query.len) != 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If searchParams exists, update it too.
|
||||
@@ -245,7 +226,7 @@ pub fn getHash(self: *const URL) []const u8 {
|
||||
return (out - 1)[0 .. len + 1];
|
||||
}
|
||||
|
||||
pub fn setHash(self: *URL, value: []const u8) !void {
|
||||
pub fn setHash(self: *URL, value: []const u8) void {
|
||||
// An empty value clears the fragment entirely (removes the '#').
|
||||
if (value.len == 0) {
|
||||
U.url_set_fragment_to_null(self._url);
|
||||
@@ -253,10 +234,7 @@ pub fn setHash(self: *URL, value: []const u8) !void {
|
||||
}
|
||||
// Strip a single leading '#', then set the fragment.
|
||||
const fragment = if (value[0] == '#') value[1..] else value;
|
||||
const res = U.url_set_fragment(self._url, fragment.ptr, fragment.len);
|
||||
if (res != 0) {
|
||||
return error.SetHash;
|
||||
}
|
||||
_ = U.url_set_fragment(self._url, fragment.ptr, fragment.len);
|
||||
}
|
||||
|
||||
pub fn getSearchParams(self: *URL, exec: *const Execution) !*URLSearchParams {
|
||||
@@ -320,6 +298,14 @@ pub fn toString(self: *const URL, exec: *const Execution) ![]const u8 {
|
||||
return out[0..len];
|
||||
}
|
||||
|
||||
/// Like the constructor, but returns null instead of throwing when parsing fails.
|
||||
pub fn parse(url: []const u8, maybe_base: ?[]const u8, exec: *const Execution) !?*URL {
|
||||
return URL.init(url, maybe_base, exec) catch |err| switch (err) {
|
||||
error.TypeError => null,
|
||||
else => err,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn canParse(url: []const u8, maybe_base: ?[]const u8) bool {
|
||||
if (maybe_base) |base| {
|
||||
return U.url_can_parse_with_base(base.ptr, base.len, url.ptr, url.len);
|
||||
@@ -370,6 +356,7 @@ pub const JsApi = struct {
|
||||
};
|
||||
|
||||
pub const constructor = bridge.constructor(URL.init, .{});
|
||||
pub const parse = bridge.function(URL.parse, .{ .static = true });
|
||||
pub const canParse = bridge.function(URL.canParse, .{ .static = true });
|
||||
pub const createObjectURL = bridge.function(URL.createObjectURL, .{ .static = true });
|
||||
pub const revokeObjectURL = bridge.function(URL.revokeObjectURL, .{ .static = true });
|
||||
|
||||
Reference in New Issue
Block a user