From 3fd834794382cb2dd13a4a5667aa92433f987573 Mon Sep 17 00:00:00 2001 From: Pierre Tachoire Date: Fri, 14 Mar 2025 18:55:50 +0100 Subject: [PATCH 1/3] browser: fix module URL resolution --- src/browser/browser.zig | 33 +++++++++++++++++++++++++++++---- 1 file changed, 29 insertions(+), 4 deletions(-) diff --git a/src/browser/browser.zig b/src/browser/browser.zig index 57c2900f..5c59b3f0 100644 --- a/src/browser/browser.zig +++ b/src/browser/browser.zig @@ -194,7 +194,11 @@ pub const Session = struct { // Use the page_arena for this, which has a more appropriate lifetime // and which has more retained memory between sessions and pages. const arena = self.browser.page_arena.allocator(); - const body = try self.page.?.fetchData(arena, specifier); + const body = try self.page.?.fetchData( + arena, + specifier, + if (self.page.?.current_script) |s| s.src else null, + ); return self.env.compileModule(body, specifier); } @@ -283,6 +287,10 @@ pub const Page = struct { raw_data: ?[]const u8 = null, + // current_script is the script currently evaluated by the page. + // current_script could by fetch module to resolve module's url to fetch. + current_script: ?*const Script = null, + fn init(session: *Session) Page { return .{ .session = session, @@ -546,6 +554,9 @@ pub const Page = struct { // if no src is present, we evaluate the text source. // https://html.spec.whatwg.org/multipage/scripting.html#script-processing-model fn evalScript(self: *Page, s: Script) !void { + self.current_script = &s; + defer self.current_script = null; + // https://html.spec.whatwg.org/multipage/webappapis.html#fetch-a-classic-script const opt_src = try parser.elementGetAttribute(s.element, "src"); if (opt_src) |src| { @@ -590,13 +601,27 @@ pub const Page = struct { JsErr, }; + // fetchData returns the data corresponding to the src target. + // It resolves src using the page's uri. + // If a base path is given, src is resolved according to the base first. // the caller owns the returned string - fn fetchData(self: *Page, arena: Allocator, src: []const u8) ![]const u8 { + fn fetchData(self: *Page, arena: Allocator, src: []const u8, base: ?[]const u8) ![]const u8 { log.debug("starting fetch {s}", .{src}); var buffer: [1024]u8 = undefined; var b: []u8 = buffer[0..]; - const u = try std.Uri.resolve_inplace(self.uri, src, &b); + + var res_src = src; + + // if a base path is given, we resolve src using base. + if (base) |_base| { + const dir = std.fs.path.dirname(_base); + if (dir) |_dir| { + res_src = try std.fs.path.resolve(arena, &.{ _dir, src }); + } + } + + const u = try std.Uri.resolve_inplace(self.uri, res_src, &b); var fetchres = try self.session.loader.get(arena, u); defer fetchres.deinit(); @@ -624,7 +649,7 @@ pub const Page = struct { // received. fn fetchScript(self: *Page, s: Script) !void { const arena = self.arena; - const body = try self.fetchData(arena, s.src); + const body = try self.fetchData(arena, s.src, null); // TODO: change to &self.session.env when // https://github.com/lightpanda-io/zig-js-runtime/pull/285 lands try s.eval(arena, self.session.env, body); From 229844d399c551bf701fffb799f3e461651d1ab1 Mon Sep 17 00:00:00 2001 From: Pierre Tachoire Date: Mon, 17 Mar 2025 09:51:01 +0100 Subject: [PATCH 2/3] browser: use *const Script with evalScript --- src/browser/browser.zig | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/browser/browser.zig b/src/browser/browser.zig index 5c59b3f0..01f276a8 100644 --- a/src/browser/browser.zig +++ b/src/browser/browser.zig @@ -512,7 +512,7 @@ pub const Page = struct { // > page. // https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script#notes try parser.documentHTMLSetCurrentScript(html_doc, @ptrCast(e)); - self.evalScript(script) catch |err| log.warn("evaljs: {any}", .{err}); + self.evalScript(&script) catch |err| log.warn("evaljs: {any}", .{err}); try parser.documentHTMLSetCurrentScript(html_doc, null); } @@ -531,7 +531,7 @@ pub const Page = struct { // eval async scripts. for (sasync.items) |s| { try parser.documentHTMLSetCurrentScript(html_doc, @ptrCast(s.element)); - self.evalScript(s) catch |err| log.warn("evaljs: {any}", .{err}); + self.evalScript(&s) catch |err| log.warn("evaljs: {any}", .{err}); try parser.documentHTMLSetCurrentScript(html_doc, null); } @@ -553,8 +553,8 @@ pub const Page = struct { // evalScript evaluates the src in priority. // if no src is present, we evaluate the text source. // https://html.spec.whatwg.org/multipage/scripting.html#script-processing-model - fn evalScript(self: *Page, s: Script) !void { - self.current_script = &s; + fn evalScript(self: *Page, s: *const Script) !void { + self.current_script = s; defer self.current_script = null; // https://html.spec.whatwg.org/multipage/webappapis.html#fetch-a-classic-script @@ -647,8 +647,9 @@ pub const Page = struct { // fetchScript senf a GET request to the src and execute the script // received. - fn fetchScript(self: *Page, s: Script) !void { + fn fetchScript(self: *Page, s: *const Script) !void { const arena = self.arena; + const body = try self.fetchData(arena, s.src, null); // TODO: change to &self.session.env when // https://github.com/lightpanda-io/zig-js-runtime/pull/285 lands From 087a7b5f3cc8961b87513dd158c13fa2924f47b2 Mon Sep 17 00:00:00 2001 From: Pierre Tachoire Date: Mon, 17 Mar 2025 09:58:31 +0100 Subject: [PATCH 3/3] browser: use *const Page with fetchModule --- src/browser/browser.zig | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/src/browser/browser.zig b/src/browser/browser.zig index 01f276a8..4c1d6f52 100644 --- a/src/browser/browser.zig +++ b/src/browser/browser.zig @@ -184,20 +184,17 @@ pub const Session = struct { _ = referrer; const self: *Session = @ptrCast(@alignCast(ctx)); - - if (self.page == null) { - return error.NoPage; - } + const page = &(self.page orelse return error.NoPage); log.debug("fetch module: specifier: {s}", .{specifier}); // fetchModule is called within the context of processing a page. // Use the page_arena for this, which has a more appropriate lifetime // and which has more retained memory between sessions and pages. const arena = self.browser.page_arena.allocator(); - const body = try self.page.?.fetchData( + const body = try page.fetchData( arena, specifier, - if (self.page.?.current_script) |s| s.src else null, + if (page.current_script) |s| s.src else null, ); return self.env.compileModule(body, specifier); } @@ -605,7 +602,7 @@ pub const Page = struct { // It resolves src using the page's uri. // If a base path is given, src is resolved according to the base first. // the caller owns the returned string - fn fetchData(self: *Page, arena: Allocator, src: []const u8, base: ?[]const u8) ![]const u8 { + fn fetchData(self: *const Page, arena: Allocator, src: []const u8, base: ?[]const u8) ![]const u8 { log.debug("starting fetch {s}", .{src}); var buffer: [1024]u8 = undefined; @@ -647,7 +644,7 @@ pub const Page = struct { // fetchScript senf a GET request to the src and execute the script // received. - fn fetchScript(self: *Page, s: *const Script) !void { + fn fetchScript(self: *const Page, s: *const Script) !void { const arena = self.arena; const body = try self.fetchData(arena, s.src, null);