From 253efcae6e2e10310e603ac2a574b55b74159b00 Mon Sep 17 00:00:00 2001 From: Navid EMAD Date: Mon, 27 Apr 2026 05:30:01 +0200 Subject: [PATCH] browser: trigger navigation when Location.pathname or .search is assigned MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The JsApi binding for Location.prototype declared `pathname` and `search` as read-only accessors (`null` setter), so assigning either had no observable effect — the URL was never re-parsed and no navigation was queued. Per HTML Living Standard §7.4.4.5.5/§7.4.4.5.6, both setters must run the URL parser against a copy of the current URL and then "Location-object navigate" to the result. Add `setPathname` / `setSearch` member functions that mirror the existing `setHash` shape: compute the new full URL via the matching helper in `src/browser/URL.zig` and forward to `Frame.scheduleNavigation` with `kind = .push` (matches Chrome's history-handling for these setters). Wire them into the `pathname` / `search` accessors in place of the previous `null` setters. Closes #2256 --- src/browser/tests/window/location.html | 8 ++++++++ src/browser/webapi/Location.zig | 21 +++++++++++++++++++-- 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/src/browser/tests/window/location.html b/src/browser/tests/window/location.html index b82adfb4..29527cba 100644 --- a/src/browser/tests/window/location.html +++ b/src/browser/tests/window/location.html @@ -23,3 +23,11 @@ testing.expectEqual("", location.hash); testing.expectEqual(testing.BASE_URL + 'window/location.html', location.href); + + + + diff --git a/src/browser/webapi/Location.zig b/src/browser/webapi/Location.zig index 2a00d81e..f57db2c0 100644 --- a/src/browser/webapi/Location.zig +++ b/src/browser/webapi/Location.zig @@ -20,6 +20,7 @@ const std = @import("std"); const js = @import("../js/js.zig"); const URL = @import("URL.zig"); +const U = @import("../URL.zig"); const Frame = @import("../Frame.zig"); const Location = @This(); @@ -65,6 +66,22 @@ pub fn getHash(self: *const Location) []const u8 { return self._url.getHash(); } +pub fn setPathname(_: *const Location, pathname: []const u8, frame: *Frame) !void { + const new_url = try U.setPathname(frame.url, pathname, frame.call_arena); + return frame.scheduleNavigation(new_url, .{ + .reason = .script, + .kind = .{ .push = null }, + }, .{ .script = frame }); +} + +pub fn setSearch(_: *const Location, search: []const u8, frame: *Frame) !void { + const new_url = try U.setSearch(frame.url, search, frame.call_arena); + return frame.scheduleNavigation(new_url, .{ + .reason = .script, + .kind = .{ .push = null }, + }, .{ .script = frame }); +} + pub fn setHash(_: *const Location, hash: []const u8, frame: *Frame) !void { const normalized_hash = blk: { if (hash.len == 0) { @@ -117,9 +134,9 @@ pub const JsApi = struct { return self.assign(url, frame); } - pub const search = bridge.accessor(Location.getSearch, null, .{}); + pub const search = bridge.accessor(Location.getSearch, Location.setSearch, .{}); pub const hash = bridge.accessor(Location.getHash, Location.setHash, .{}); - pub const pathname = bridge.accessor(Location.getPathname, null, .{}); + pub const pathname = bridge.accessor(Location.getPathname, Location.setPathname, .{}); pub const hostname = bridge.accessor(Location.getHostname, null, .{}); pub const host = bridge.accessor(Location.getHost, null, .{}); pub const port = bridge.accessor(Location.getPort, null, .{});