From ac75f9bf575b4e51a1f8f0517ea01b4277dfb7d8 Mon Sep 17 00:00:00 2001 From: Karl Seguin Date: Mon, 2 Jun 2025 15:43:53 +0800 Subject: [PATCH 1/2] Fix url constructor url, base were being joined in the wrong order. Switch to using URL.stitch if a base is given. --- src/browser/page.zig | 4 ++-- src/browser/url/url.zig | 14 +++++++++++++- src/url.zig | 26 +++++++++++++++++++++----- 3 files changed, 36 insertions(+), 8 deletions(-) diff --git a/src/browser/page.zig b/src/browser/page.zig index ebb90528..9cf5068c 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; }; @@ -451,7 +451,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, .{}); } var origin_url = &self.url; diff --git a/src/browser/url/url.zig b/src/browser/url/url.zig index fd790380..9b387070 100644 --- a/src/browser/url/url.zig +++ b/src/browser/url/url.zig @@ -50,7 +50,14 @@ 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, .{ + .alloc = .always, + }); + } else { + raw = try arena.dupe(u8, url); + } const uri = std.Uri.parse(raw) catch return error.TypeError; return init(arena, uri); @@ -276,4 +283,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..2a9bf370 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 = .if_needed, + + 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); } From 2ef7ea6512a0981237a60f865481c5c92d8a2d47 Mon Sep 17 00:00:00 2001 From: Karl Seguin Date: Mon, 2 Jun 2025 19:24:08 +0800 Subject: [PATCH 2/2] change stitch alloc default to .always --- src/browser/page.zig | 2 +- src/browser/url/url.zig | 4 +--- src/url.zig | 2 +- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/src/browser/page.zig b/src/browser/page.zig index 9cf5068c..7585a3ed 100644 --- a/src/browser/page.zig +++ b/src/browser/page.zig @@ -451,7 +451,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 9b387070..a33ac2a3 100644 --- a/src/browser/url/url.zig +++ b/src/browser/url/url.zig @@ -52,9 +52,7 @@ pub const URL = struct { const arena = page.arena; var raw: []const u8 = undefined; if (base) |b| { - raw = try @import("../../url.zig").URL.stitch(arena, url, b, .{ - .alloc = .always, - }); + raw = try @import("../../url.zig").URL.stitch(arena, url, b, .{}); } else { raw = try arena.dupe(u8, url); } diff --git a/src/url.zig b/src/url.zig index 2a9bf370..16f22265 100644 --- a/src/url.zig +++ b/src/url.zig @@ -84,7 +84,7 @@ pub const URL = struct { } const StitchOpts = struct { - alloc: AllocWhen = .if_needed, + alloc: AllocWhen = .always, const AllocWhen = enum { always,