diff --git a/src/browser/browser.zig b/src/browser/browser.zig index 57c2900f..4c1d6f52 100644 --- a/src/browser/browser.zig +++ b/src/browser/browser.zig @@ -184,17 +184,18 @@ 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(arena, specifier); + const body = try page.fetchData( + arena, + specifier, + if (page.current_script) |s| s.src else null, + ); return self.env.compileModule(body, specifier); } @@ -283,6 +284,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, @@ -504,7 +509,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); } @@ -523,7 +528,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); } @@ -545,7 +550,10 @@ 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 { + 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 const opt_src = try parser.elementGetAttribute(s.element, "src"); if (opt_src) |src| { @@ -590,13 +598,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: *const 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(); @@ -622,9 +644,10 @@ 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: *const Page, s: *const 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);