From 1dcccef0800743fcc430212f2175bd1e17cb8221 Mon Sep 17 00:00:00 2001 From: Pierre Tachoire Date: Wed, 24 Dec 2025 15:19:41 +0100 Subject: [PATCH] use V8 json parser with xhr/fetch webAPIs The pure zig JSON parser didn't generate the same type of values than JS JSON.parse command. Using directly V8's JSON parser gives the assurance to have the right JS types. Moreover, it avoid data transformations between Zig and V8. --- src/browser/fetch/Request.zig | 9 ++------- src/browser/fetch/Response.zig | 9 ++------- src/browser/xhr/xhr.zig | 25 ++++++++++--------------- 3 files changed, 14 insertions(+), 29 deletions(-) diff --git a/src/browser/fetch/Request.zig b/src/browser/fetch/Request.zig index f13a8cb8..e22d9f77 100644 --- a/src/browser/fetch/Request.zig +++ b/src/browser/fetch/Request.zig @@ -254,17 +254,12 @@ pub fn _json(self: *Response, page: *Page) !js.Promise { self.body_used = true; if (self.body) |body| { - const p = std.json.parseFromSliceLeaky( - std.json.Value, - page.call_arena, - body, - .{}, - ) catch |e| { + const value = js.Value.fromJson(page.js, body) catch |e| { log.info(.browser, "invalid json", .{ .err = e, .source = "Request" }); return error.SyntaxError; }; - return page.js.resolvePromise(p); + return page.js.resolvePromise(value); } return page.js.resolvePromise(null); } diff --git a/src/browser/fetch/Response.zig b/src/browser/fetch/Response.zig index 69f1c39e..dc83071c 100644 --- a/src/browser/fetch/Response.zig +++ b/src/browser/fetch/Response.zig @@ -179,17 +179,12 @@ pub fn _json(self: *Response, page: *Page) !js.Promise { if (self.body) |body| { self.body_used = true; - const p = std.json.parseFromSliceLeaky( - std.json.Value, - page.call_arena, - body, - .{}, - ) catch |e| { + const value = js.Value.fromJson(page.js, body) catch |e| { log.info(.browser, "invalid json", .{ .err = e, .source = "Response" }); return error.SyntaxError; }; - return page.js.resolvePromise(p); + return page.js.resolvePromise(value); } return page.js.resolvePromise(null); } diff --git a/src/browser/xhr/xhr.zig b/src/browser/xhr/xhr.zig index dfc9ca99..588c103e 100644 --- a/src/browser/xhr/xhr.zig +++ b/src/browser/xhr/xhr.zig @@ -31,6 +31,7 @@ const Mime = @import("../mime.zig").Mime; const parser = @import("../netsurf.zig"); const Page = @import("../page.zig").Page; const Http = @import("../../http/Http.zig"); +const js = @import("../js/js.zig"); // XHR interfaces // https://xhr.spec.whatwg.org/#interface-xmlhttprequest @@ -128,21 +129,19 @@ pub const XMLHttpRequest = struct { JSON, }; - const JSONValue = std.json.Value; - const Response = union(ResponseType) { Empty: void, Text: []const u8, ArrayBuffer: void, Blob: void, Document: *parser.Document, - JSON: JSONValue, + JSON: js.Value, }; const ResponseObj = union(enum) { Document: *parser.Document, Failure: void, - JSON: JSONValue, + JSON: js.Value, fn deinit(self: ResponseObj) void { switch (self) { @@ -605,7 +604,7 @@ pub const XMLHttpRequest = struct { } // https://xhr.spec.whatwg.org/#the-response-attribute - pub fn get_response(self: *XMLHttpRequest) !?Response { + pub fn get_response(self: *XMLHttpRequest, page: *Page) !?Response { if (self.response_type == .Empty or self.response_type == .Text) { if (self.state == .loading or self.state == .done) { return .{ .Text = try self.get_responseText() }; @@ -652,7 +651,7 @@ pub const XMLHttpRequest = struct { // TODO Let jsonObject be the result of running parse JSON from bytes // on this’s received bytes. If that threw an exception, then return // null. - self.setResponseObjJSON(); + self.setResponseObjJSON(page); } if (self.response_obj) |obj| { @@ -691,22 +690,18 @@ pub const XMLHttpRequest = struct { }; } - // setResponseObjJSON parses the received bytes as a std.json.Value. - fn setResponseObjJSON(self: *XMLHttpRequest) void { - // TODO should we use parseFromSliceLeaky if we expect the allocator is - // already an arena? - const p = std.json.parseFromSliceLeaky( - JSONValue, - self.arena, + // setResponseObjJSON parses the received bytes as a js.Value. + fn setResponseObjJSON(self: *XMLHttpRequest, page: *Page) void { + const value = js.Value.fromJson( + page.js, self.response_bytes.items, - .{}, ) catch |e| { log.warn(.http, "invalid json", .{ .err = e, .url = self.url, .source = "xhr" }); self.response_obj = .{ .Failure = {} }; return; }; - self.response_obj = .{ .JSON = p }; + self.response_obj = .{ .JSON = value }; } pub fn get_responseText(self: *XMLHttpRequest) ![]const u8 {