mirror of
https://github.com/lightpanda-io/browser.git
synced 2026-02-04 06:23:45 +00:00
Merge pull request #1293 from lightpanda-io/v8-json-parse
Some checks failed
e2e-test / zig build release (push) Has been cancelled
zig-test / zig build dev (push) Has been cancelled
zig-test / zig test (push) Has been cancelled
e2e-test / demo-scripts (push) Has been cancelled
e2e-test / cdp-and-hyperfine-bench (push) Has been cancelled
e2e-test / perf-fmt (push) Has been cancelled
zig-test / browser fetch (push) Has been cancelled
zig-test / perf-fmt (push) Has been cancelled
nightly build / build-linux-x86_64 (push) Has been cancelled
nightly build / build-linux-aarch64 (push) Has been cancelled
nightly build / build-macos-aarch64 (push) Has been cancelled
nightly build / build-macos-x86_64 (push) Has been cancelled
wpt / web platform tests json output (push) Has been cancelled
wpt / perf-fmt (push) Has been cancelled
e2e-integration-test / zig build release (push) Has been cancelled
e2e-integration-test / demo-integration-scripts (push) Has been cancelled
Some checks failed
e2e-test / zig build release (push) Has been cancelled
zig-test / zig build dev (push) Has been cancelled
zig-test / zig test (push) Has been cancelled
e2e-test / demo-scripts (push) Has been cancelled
e2e-test / cdp-and-hyperfine-bench (push) Has been cancelled
e2e-test / perf-fmt (push) Has been cancelled
zig-test / browser fetch (push) Has been cancelled
zig-test / perf-fmt (push) Has been cancelled
nightly build / build-linux-x86_64 (push) Has been cancelled
nightly build / build-linux-aarch64 (push) Has been cancelled
nightly build / build-macos-aarch64 (push) Has been cancelled
nightly build / build-macos-x86_64 (push) Has been cancelled
wpt / web platform tests json output (push) Has been cancelled
wpt / perf-fmt (push) Has been cancelled
e2e-integration-test / zig build release (push) Has been cancelled
e2e-integration-test / demo-integration-scripts (push) Has been cancelled
Use V8 to parse JSON with fetch/xhr
This commit is contained in:
@@ -254,17 +254,13 @@ 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;
|
||||
};
|
||||
const pvalue = try value.persist(page.js);
|
||||
|
||||
return page.js.resolvePromise(p);
|
||||
return page.js.resolvePromise(pvalue);
|
||||
}
|
||||
return page.js.resolvePromise(null);
|
||||
}
|
||||
|
||||
@@ -179,17 +179,13 @@ 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;
|
||||
};
|
||||
const pvalue = try value.persist(page.js);
|
||||
|
||||
return page.js.resolvePromise(p);
|
||||
return page.js.resolvePromise(pvalue);
|
||||
}
|
||||
return page.js.resolvePromise(null);
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ const types = @import("types.zig");
|
||||
const Caller = @import("Caller.zig");
|
||||
const NamedFunction = Caller.NamedFunction;
|
||||
const PersistentObject = v8.Persistent(v8.Object);
|
||||
const PersistentValue = v8.Persistent(v8.Value);
|
||||
const PersistentModule = v8.Persistent(v8.Module);
|
||||
const PersistentPromise = v8.Persistent(v8.Promise);
|
||||
const PersistentFunction = v8.Persistent(v8.Function);
|
||||
@@ -70,6 +71,9 @@ identity_map: std.AutoHashMapUnmanaged(usize, PersistentObject) = .empty,
|
||||
// we now simply persist every time persist() is called.
|
||||
js_object_list: std.ArrayListUnmanaged(PersistentObject) = .empty,
|
||||
|
||||
// js_value_list tracks persisted js values.
|
||||
js_value_list: std.ArrayListUnmanaged(PersistentValue) = .empty,
|
||||
|
||||
// Various web APIs depend on having a persistent promise resolver. They
|
||||
// require for this PromiseResolver to be valid for a lifetime longer than
|
||||
// the function that resolves/rejects them.
|
||||
@@ -149,6 +153,10 @@ pub fn deinit(self: *Context) void {
|
||||
p.deinit();
|
||||
}
|
||||
|
||||
for (self.js_value_list.items) |*p| {
|
||||
p.deinit();
|
||||
}
|
||||
|
||||
for (self.persisted_promise_resolvers.items) |*p| {
|
||||
p.deinit();
|
||||
}
|
||||
|
||||
@@ -148,6 +148,8 @@ pub const Exception = struct {
|
||||
};
|
||||
|
||||
pub const Value = struct {
|
||||
const PersistentValue = v8.Persistent(v8.Value);
|
||||
|
||||
value: v8.Value,
|
||||
context: *const Context,
|
||||
|
||||
@@ -161,6 +163,15 @@ pub const Value = struct {
|
||||
const value = try v8.Json.parse(ctx.v8_context, json_string);
|
||||
return Value{ .context = ctx, .value = value };
|
||||
}
|
||||
|
||||
pub fn persist(self: Value, context: *Context) !Value {
|
||||
const js_value = self.value;
|
||||
|
||||
const persisted = PersistentValue.init(context.isolate, js_value);
|
||||
try context.js_value_list.append(context.arena, persisted);
|
||||
|
||||
return Value{ .context = context, .value = persisted.toValue() };
|
||||
}
|
||||
};
|
||||
|
||||
pub const ValueIterator = struct {
|
||||
|
||||
@@ -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,24 @@ 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 };
|
||||
const pvalue = value.persist(page.js) catch |e| {
|
||||
log.warn(.http, "persist v8 json value", .{ .err = e, .url = self.url, .source = "xhr" });
|
||||
self.response_obj = .{ .Failure = {} };
|
||||
return;
|
||||
};
|
||||
|
||||
self.response_obj = .{ .JSON = pvalue };
|
||||
}
|
||||
|
||||
pub fn get_responseText(self: *XMLHttpRequest) ![]const u8 {
|
||||
|
||||
@@ -862,7 +862,7 @@ fn testHTTPHandler(req: *std.http.Server.Request) !void {
|
||||
}
|
||||
|
||||
if (std.mem.eql(u8, path, "/xhr/json")) {
|
||||
return req.respond("{\"over\":\"9000!!!\"}", .{
|
||||
return req.respond("{\"over\":\"9000!!!\",\"updated_at\":1765867200000}", .{
|
||||
.extra_headers = &.{
|
||||
.{ .name = "Content-Type", .value = "application/json" },
|
||||
},
|
||||
|
||||
@@ -12,7 +12,8 @@
|
||||
});
|
||||
|
||||
testing.async(promise1, (json) => {
|
||||
testing.expectEqual({over: '9000!!!'}, json);
|
||||
testing.expectEqual("number", typeof json.updated_at);
|
||||
testing.expectEqual({over: '9000!!!',updated_at:1765867200000}, json);
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -29,6 +30,7 @@
|
||||
});
|
||||
|
||||
testing.async(promise1, (json) => {
|
||||
testing.expectEqual({over: '9000!!!'}, json);
|
||||
testing.expectEqual("number", typeof json.updated_at);
|
||||
testing.expectEqual({over: '9000!!!',updated_at:1765867200000}, json);
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -65,6 +65,8 @@
|
||||
testing.expectEqual(200, req3.status);
|
||||
testing.expectEqual('OK', req3.statusText);
|
||||
testing.expectEqual('9000!!!', req3.response.over);
|
||||
testing.expectEqual("number", typeof req3.response.updated_at);
|
||||
testing.expectEqual(1765867200000, req3.response.updated_at);
|
||||
});
|
||||
</script>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user