diff --git a/src/browser/page.zig b/src/browser/page.zig index 5366fb7a..9f14d982 100644 --- a/src/browser/page.zig +++ b/src/browser/page.zig @@ -145,7 +145,7 @@ pub const Page = struct { const file_src = blk: { if (base) |_base| { - break :blk try URL.stitch(self.arena, specifier, _base); + break :blk try URL.stitch(self.arena, specifier, _base, .{}); } else break :blk specifier; }; @@ -454,7 +454,7 @@ pub const Page = struct { // if a base path is given, we resolve src using base. if (base) |_base| { - res_src = try URL.stitch(arena, src, _base); + res_src = try URL.stitch(arena, src, _base, .{ .alloc = .if_needed }); } var origin_url = &self.url; diff --git a/src/browser/url/url.zig b/src/browser/url/url.zig index fd790380..a33ac2a3 100644 --- a/src/browser/url/url.zig +++ b/src/browser/url/url.zig @@ -50,7 +50,12 @@ pub const URL = struct { page: *Page, ) !URL { const arena = page.arena; - const raw = try std.mem.concat(arena, u8, &[_][]const u8{ url, base orelse "" }); + var raw: []const u8 = undefined; + if (base) |b| { + raw = try @import("../../url.zig").URL.stitch(arena, url, b, .{}); + } else { + raw = try arena.dupe(u8, url); + } const uri = std.Uri.parse(raw) catch return error.TypeError; return init(arena, uri); @@ -276,4 +281,9 @@ test "Browser.URL" { .{ "url.searchParams.delete('a')", "undefined" }, .{ "url.searchParams.get('a')", "" }, }, .{}); + + try runner.testCases(&.{ + .{ "var url = new URL('over?9000', 'https://lightpanda.io')", null }, + .{ "url.href", "https://lightpanda.io/over?9000" }, + }, .{}); } diff --git a/src/url.zig b/src/url.zig index adaa0447..16f22265 100644 --- a/src/url.zig +++ b/src/url.zig @@ -83,12 +83,28 @@ pub const URL = struct { return WebApiURL.init(allocator, self.uri); } + const StitchOpts = struct { + alloc: AllocWhen = .always, + + const AllocWhen = enum { + always, + if_needed, + }; + }; /// Properly stitches two URL fragments together. /// /// For URLs with a path, it will replace the last entry with the src. /// For URLs without a path, it will add src as the path. - pub fn stitch(allocator: Allocator, src: []const u8, base: []const u8) ![]const u8 { + pub fn stitch( + allocator: Allocator, + src: []const u8, + base: []const u8, + opts: StitchOpts, + ) ![]const u8 { if (base.len == 0) { + if (opts.alloc == .always) { + return allocator.dupe(u8, src); + } return src; } @@ -161,7 +177,7 @@ test "URL: Stitching Base & Src URLs (Basic)" { const base = "https://www.google.com/xyz/abc/123"; const src = "something.js"; - const result = try URL.stitch(allocator, src, base); + const result = try URL.stitch(allocator, src, base, .{}); defer allocator.free(result); try testing.expectString("https://www.google.com/xyz/abc/something.js", result); } @@ -171,7 +187,7 @@ test "URL: Stitching Base & Src URLs (Just Ending Slash)" { const base = "https://www.google.com/"; const src = "something.js"; - const result = try URL.stitch(allocator, src, base); + const result = try URL.stitch(allocator, src, base, .{}); defer allocator.free(result); try testing.expectString("https://www.google.com/something.js", result); } @@ -181,7 +197,7 @@ test "URL: Stitching Base & Src URLs (No Ending Slash)" { const base = "https://www.google.com"; const src = "something.js"; - const result = try URL.stitch(allocator, src, base); + const result = try URL.stitch(allocator, src, base, .{}); defer allocator.free(result); try testing.expectString("https://www.google.com/something.js", result); } @@ -191,7 +207,7 @@ test "URL: Stiching Base & Src URLs (Both Local)" { const base = "./abcdef/123.js"; const src = "something.js"; - const result = try URL.stitch(allocator, src, base); + const result = try URL.stitch(allocator, src, base, .{}); defer allocator.free(result); try testing.expectString("./abcdef/something.js", result); }