mirror of
https://github.com/lightpanda-io/browser.git
synced 2026-06-11 17:46:32 -04:00
Merge pull request #2193 from lightpanda-io/worker_apis
Enable more WebAPIs for Workers
This commit is contained in:
@@ -3613,7 +3613,7 @@ pub fn submitForm(self: *Page, submitter_: ?*Element, form_: ?*Element.Html.Form
|
||||
|
||||
// The submitter can be an input box (if enter was entered on the box)
|
||||
// I don't think this is technically correct, but FormData handles it ok
|
||||
const form_data = try FormData.init(form, submitter_, self);
|
||||
const form_data = try FormData.init(form, submitter_, &self.js.execution);
|
||||
|
||||
const arena = try self._session.getArena(.medium, "submitForm");
|
||||
errdefer self._session.releaseArena(arena);
|
||||
|
||||
@@ -302,6 +302,7 @@ fn _createContext(self: *Env, global: anytype, params: ContextParams) !*Context
|
||||
context.execution = .{
|
||||
.url = &global.url,
|
||||
.buf = &global.buf,
|
||||
.charset = &global.charset,
|
||||
.context = context,
|
||||
.arena = global.arena,
|
||||
.call_arena = params.call_arena,
|
||||
|
||||
@@ -45,3 +45,11 @@ _scheduler: *Scheduler,
|
||||
|
||||
// Pointer to the url field (Page or WorkerGlobalScope) - allows access to current url even after navigation
|
||||
url: *[:0]const u8,
|
||||
|
||||
// Pointer to the charset field of the global (Page or WorkerGlobalScope).
|
||||
charset: *[]const u8,
|
||||
|
||||
// Returns the current base URL of the global scope.
|
||||
pub fn base(self: *const Execution) [:0]const u8 {
|
||||
return self.context.global.base();
|
||||
}
|
||||
|
||||
@@ -919,15 +919,21 @@ pub const WorkerJsApis = flattenTypes(&.{
|
||||
@import("../webapi/File.zig"),
|
||||
@import("../webapi/Console.zig"),
|
||||
@import("../webapi/Crypto.zig"),
|
||||
@import("../webapi/net/FormData.zig"),
|
||||
@import("../webapi/net/Headers.zig"),
|
||||
@import("../webapi/net/Request.zig"),
|
||||
@import("../webapi/net/Response.zig"),
|
||||
// @import("../webapi/URL.zig"),
|
||||
// @import("../webapi/Blob.zig"),
|
||||
// @import("../webapi/net/FormData.zig"),
|
||||
// @import("../webapi/Performance.zig"),
|
||||
// @import("../webapi/net/Response.zig"),
|
||||
// @import("../webapi/net/Request.zig"),
|
||||
// @import("../webapi/net/Headers.zig"),
|
||||
// @import("../webapi/AbortSignal.zig"),
|
||||
// @import("../webapi/AbortController.zig"),
|
||||
// @import("../webapi/streams/ReadableStream.zig"),
|
||||
// @import("../webapi/streams/ReadableStreamDefaultReader.zig"),
|
||||
// @import("../webapi/streams/ReadableStreamDefaultController.zig"),
|
||||
// @import("../webapi/streams/WritableStream.zig"),
|
||||
// @import("../webapi/streams/WritableStreamDefaultWriter.zig"),
|
||||
// @import("../webapi/streams/WritableStreamDefaultController.zig"),
|
||||
});
|
||||
|
||||
// Master list of ALL JS APIs across all contexts.
|
||||
|
||||
55
src/browser/tests/worker/api-worker.js
Normal file
55
src/browser/tests/worker/api-worker.js
Normal file
@@ -0,0 +1,55 @@
|
||||
// Exercises WebAPI classes available in WorkerGlobalScope.
|
||||
// Replies with either { ok: true, results: {...} } or { ok: false, err }.
|
||||
(async function() {
|
||||
try {
|
||||
// Headers
|
||||
const headers = new Headers();
|
||||
headers.set('X-Test', 'hello');
|
||||
headers.append('X-Test', 'world');
|
||||
|
||||
// FormData (no form - pure data container)
|
||||
const fd = new FormData();
|
||||
fd.set('name', 'first');
|
||||
fd.append('name', 'second');
|
||||
|
||||
// Request
|
||||
const request = new Request('https://example.com/path', {
|
||||
method: 'POST',
|
||||
headers: { 'x-custom': 'header' },
|
||||
body: 'request body',
|
||||
});
|
||||
const request_body_text = await request.text();
|
||||
|
||||
// Response
|
||||
const response = new Response('response body', {
|
||||
status: 201,
|
||||
statusText: 'Created',
|
||||
headers: { 'content-type': 'text/plain' },
|
||||
});
|
||||
const response_body_text = await response.text();
|
||||
const response_clone_text = await response.clone().text();
|
||||
|
||||
postMessage({
|
||||
ok: true,
|
||||
results: {
|
||||
headers_get: headers.get('x-test'),
|
||||
headers_has: headers.has('x-test'),
|
||||
headers_has_missing: headers.has('x-missing'),
|
||||
formdata_get: fd.get('name'),
|
||||
formdata_getall: fd.getAll('name'),
|
||||
request_url: request.url,
|
||||
request_method: request.method,
|
||||
request_headers_custom: request.headers.get('x-custom'),
|
||||
request_body_text,
|
||||
response_status: response.status,
|
||||
response_status_text: response.statusText,
|
||||
response_ok: response.ok,
|
||||
response_headers_content_type: response.headers.get('content-type'),
|
||||
response_body_text,
|
||||
response_clone_text,
|
||||
},
|
||||
});
|
||||
} catch (e) {
|
||||
postMessage({ ok: false, err: String(e), stack: e.stack });
|
||||
}
|
||||
})();
|
||||
@@ -172,6 +172,47 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<script id="worker_webapi_net" type=module>
|
||||
{
|
||||
const state = await testing.async();
|
||||
const worker = new Worker('./api-worker.js');
|
||||
|
||||
worker.onmessage = function(event) {
|
||||
state.resolve(event.data);
|
||||
};
|
||||
|
||||
await state.done((data) => {
|
||||
testing.expectTrue(data.ok, 'worker api error: ' + data.err);
|
||||
const r = data.results;
|
||||
|
||||
// Headers
|
||||
testing.expectEqual('hello, world', r.headers_get);
|
||||
testing.expectEqual(true, r.headers_has);
|
||||
testing.expectEqual(false, r.headers_has_missing);
|
||||
|
||||
// FormData
|
||||
testing.expectEqual('first', r.formdata_get);
|
||||
testing.expectEqual(2, r.formdata_getall.length);
|
||||
testing.expectEqual('first', r.formdata_getall[0]);
|
||||
testing.expectEqual('second', r.formdata_getall[1]);
|
||||
|
||||
// Request
|
||||
testing.expectEqual('https://example.com/path', r.request_url);
|
||||
testing.expectEqual('POST', r.request_method);
|
||||
testing.expectEqual('header', r.request_headers_custom);
|
||||
testing.expectEqual('request body', r.request_body_text);
|
||||
|
||||
// Response
|
||||
testing.expectEqual(201, r.response_status);
|
||||
testing.expectEqual('Created', r.response_status_text);
|
||||
testing.expectEqual(true, r.response_ok);
|
||||
testing.expectEqual('text/plain', r.response_headers_content_type);
|
||||
testing.expectEqual('response body', r.response_body_text);
|
||||
testing.expectEqual('response body', r.response_clone_text);
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
<script id="worker_structured_clone_nested" type=module>
|
||||
{
|
||||
const state = await testing.async();
|
||||
|
||||
@@ -94,9 +94,9 @@ pub fn init(parts_: ?[]const js.Value, opts_: ?InitOptions, page: *Page) !*Blob
|
||||
}
|
||||
|
||||
/// Creates a new Blob from raw byte slices (for internal Zig use).
|
||||
pub fn initFromBytes(data: []const u8, content_type: []const u8, validate_mime: bool, page: *Page) !*Blob {
|
||||
const arena = try page.getArena(.large, "Blob");
|
||||
errdefer page.releaseArena(arena);
|
||||
pub fn initFromBytes(data: []const u8, content_type: []const u8, validate_mime: bool, session: *Session) !*Blob {
|
||||
const arena = try session.getArena(.large, "Blob");
|
||||
errdefer session.releaseArena(arena);
|
||||
|
||||
const mime = try validateMimeType(arena, content_type, validate_mime);
|
||||
|
||||
@@ -291,7 +291,7 @@ pub fn slice(
|
||||
start_: ?i32,
|
||||
end_: ?i32,
|
||||
content_type_: ?[]const u8,
|
||||
page: *Page,
|
||||
session: *Session,
|
||||
) !*Blob {
|
||||
const data = self._slice;
|
||||
|
||||
@@ -312,7 +312,7 @@ pub fn slice(
|
||||
break :blk @min(data.len, @max(start, @as(u31, @intCast(requested_end))));
|
||||
};
|
||||
|
||||
return Blob.initFromBytes(data[start..end], content_type_ orelse "", false, page);
|
||||
return Blob.initFromBytes(data[start..end], content_type_ orelse "", false, session);
|
||||
}
|
||||
|
||||
/// Returns the size of the Blob in bytes.
|
||||
|
||||
@@ -53,6 +53,8 @@ arena: Allocator,
|
||||
call_arena: Allocator,
|
||||
url: [:0]const u8,
|
||||
buf: [1024]u8 = undefined, // same size as page.buf
|
||||
// Document charset (matches Page.charset). Workers default to UTF-8.
|
||||
charset: []const u8 = "UTF-8",
|
||||
js: *JS.Context,
|
||||
|
||||
// Reference back to the Worker object (for postMessage to page)
|
||||
|
||||
@@ -47,7 +47,7 @@ pub const Input = Request.Input;
|
||||
pub const InitOpts = Request.InitOpts;
|
||||
|
||||
pub fn init(input: Input, options: ?InitOpts, page: *Page) !js.Promise {
|
||||
const request = try Request.init(input, options, page);
|
||||
const request = try Request.init(input, options, &page.js.execution);
|
||||
const resolver = page.js.local.?.createPromiseResolver();
|
||||
|
||||
if (request._signal) |signal| {
|
||||
@@ -61,7 +61,7 @@ pub fn init(input: Input, options: ?InitOpts, page: *Page) !js.Promise {
|
||||
return handleBlobUrl(request._url, resolver, page);
|
||||
}
|
||||
|
||||
const response = try Response.init(null, .{ .status = 0 }, page);
|
||||
const response = try Response.init(null, .{ .status = 0 }, &page.js.execution);
|
||||
errdefer response.deinit(page._session);
|
||||
|
||||
const fetch = try response._arena.create(Fetch);
|
||||
@@ -120,13 +120,13 @@ fn handleBlobUrl(url: []const u8, resolver: js.PromiseResolver, page: *Page) !js
|
||||
return resolver.promise();
|
||||
};
|
||||
|
||||
const response = try Response.init(null, .{ .status = 200 }, page);
|
||||
const response = try Response.init(null, .{ .status = 200 }, &page.js.execution);
|
||||
response._body = .{ .bytes = try response._arena.dupe(u8, blob._slice) };
|
||||
response._url = try response._arena.dupeZ(u8, url);
|
||||
response._type = .basic;
|
||||
|
||||
if (blob._mime.len > 0) {
|
||||
try response._headers.append("Content-Type", blob._mime, page);
|
||||
try response._headers.append("Content-Type", blob._mime, &page.js.execution);
|
||||
}
|
||||
|
||||
const js_val = try page.js.local.?.zigValueToJs(response, .{});
|
||||
@@ -191,7 +191,7 @@ fn httpHeaderDoneCallback(response: HttpClient.Response) !bool {
|
||||
|
||||
var it = response.headerIterator();
|
||||
while (it.next()) |hdr| {
|
||||
try res._headers.append(hdr.name, hdr.value, self._page);
|
||||
try res._headers.append(hdr.name, hdr.value, &self._page.js.execution);
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
@@ -26,6 +26,7 @@ const Element = @import("../Element.zig");
|
||||
const KeyValueList = @import("../KeyValueList.zig");
|
||||
|
||||
const log = lp.log;
|
||||
const Execution = js.Execution;
|
||||
const Allocator = std.mem.Allocator;
|
||||
|
||||
const FormData = @This();
|
||||
@@ -33,21 +34,30 @@ const FormData = @This();
|
||||
_arena: Allocator,
|
||||
_list: KeyValueList,
|
||||
|
||||
pub fn init(form: ?*Form, submitter: ?*Element, page: *Page) !*FormData {
|
||||
const form_data = try page._factory.create(FormData{
|
||||
._arena = page.arena,
|
||||
pub fn init(form_: ?*Form, submitter: ?*Element, exec: *const Execution) !*FormData {
|
||||
const form = form_ orelse {
|
||||
return try exec._factory.create(FormData{
|
||||
._arena = exec.arena,
|
||||
._list = KeyValueList.init(),
|
||||
});
|
||||
};
|
||||
|
||||
const page = switch (exec.context.global) {
|
||||
.page => |p| p,
|
||||
.worker => lp.assert(false, "FormData worker form", .{}),
|
||||
};
|
||||
|
||||
const form_data = try exec._factory.create(FormData{
|
||||
._arena = exec.arena,
|
||||
._list = try collectForm(page.arena, form, submitter, page),
|
||||
});
|
||||
|
||||
// Dispatch `formdata` event if form provided.
|
||||
if (form) |_form| {
|
||||
const form_data_event = try (@import("../event/FormDataEvent.zig")).initTrusted(
|
||||
comptime .wrap("formdata"),
|
||||
.{ .bubbles = true, .cancelable = false, .formData = form_data },
|
||||
page,
|
||||
);
|
||||
try page._event_manager.dispatch(_form.asNode().asEventTarget(), form_data_event.asEvent());
|
||||
}
|
||||
const form_data_event = try (@import("../event/FormDataEvent.zig")).initTrusted(
|
||||
comptime .wrap("formdata"),
|
||||
.{ .bubbles = true, .cancelable = false, .formData = form_data },
|
||||
page,
|
||||
);
|
||||
try page._event_manager.dispatch(form.asNode().asEventTarget(), form_data_event.asEvent());
|
||||
|
||||
return form_data;
|
||||
}
|
||||
@@ -56,8 +66,8 @@ pub fn get(self: *const FormData, name: []const u8) ?[]const u8 {
|
||||
return self._list.get(name);
|
||||
}
|
||||
|
||||
pub fn getAll(self: *const FormData, name: []const u8, page: *Page) ![]const []const u8 {
|
||||
return self._list.getAll(page.call_arena, name);
|
||||
pub fn getAll(self: *const FormData, name: []const u8, exec: *const Execution) ![]const []const u8 {
|
||||
return self._list.getAll(exec.call_arena, name);
|
||||
}
|
||||
|
||||
pub fn has(self: *const FormData, name: []const u8) bool {
|
||||
|
||||
@@ -3,10 +3,10 @@ const lp = @import("lightpanda");
|
||||
|
||||
const js = @import("../../js/js.zig");
|
||||
|
||||
const Page = @import("../../Page.zig");
|
||||
const KeyValueList = @import("../KeyValueList.zig");
|
||||
|
||||
const log = lp.log;
|
||||
const Execution = js.Execution;
|
||||
const Allocator = std.mem.Allocator;
|
||||
|
||||
const Headers = @This();
|
||||
@@ -19,31 +19,31 @@ pub const InitOpts = union(enum) {
|
||||
js_obj: js.Object,
|
||||
};
|
||||
|
||||
pub fn init(opts_: ?InitOpts, page: *Page) !*Headers {
|
||||
pub fn init(opts_: ?InitOpts, exec: *const Execution) !*Headers {
|
||||
const list = if (opts_) |opts| switch (opts) {
|
||||
.obj => |obj| try KeyValueList.copy(page.arena, obj._list),
|
||||
.js_obj => |js_obj| try KeyValueList.fromJsObject(page.arena, js_obj, normalizeHeaderName, &page.buf),
|
||||
.strings => |kvs| try KeyValueList.fromArray(page.arena, kvs, normalizeHeaderName, &page.buf),
|
||||
.obj => |obj| try KeyValueList.copy(exec.arena, obj._list),
|
||||
.js_obj => |js_obj| try KeyValueList.fromJsObject(exec.arena, js_obj, normalizeHeaderName, exec.buf),
|
||||
.strings => |kvs| try KeyValueList.fromArray(exec.arena, kvs, normalizeHeaderName, exec.buf),
|
||||
} else KeyValueList.init();
|
||||
|
||||
return page._factory.create(Headers{
|
||||
return exec._factory.create(Headers{
|
||||
._list = list,
|
||||
});
|
||||
}
|
||||
|
||||
pub fn append(self: *Headers, name: []const u8, value: []const u8, page: *Page) !void {
|
||||
const normalized_name = normalizeHeaderName(name, &page.buf);
|
||||
try self._list.append(page.arena, normalized_name, value);
|
||||
pub fn append(self: *Headers, name: []const u8, value: []const u8, exec: *const Execution) !void {
|
||||
const normalized_name = normalizeHeaderName(name, exec.buf);
|
||||
try self._list.append(exec.arena, normalized_name, value);
|
||||
}
|
||||
|
||||
pub fn delete(self: *Headers, name: []const u8, page: *Page) void {
|
||||
const normalized_name = normalizeHeaderName(name, &page.buf);
|
||||
pub fn delete(self: *Headers, name: []const u8, exec: *const Execution) void {
|
||||
const normalized_name = normalizeHeaderName(name, exec.buf);
|
||||
self._list.delete(normalized_name, null);
|
||||
}
|
||||
|
||||
pub fn get(self: *const Headers, name: []const u8, page: *Page) !?[]const u8 {
|
||||
const normalized_name = normalizeHeaderName(name, &page.buf);
|
||||
const all_values = try self._list.getAll(page.call_arena, normalized_name);
|
||||
pub fn get(self: *const Headers, name: []const u8, exec: *const Execution) !?[]const u8 {
|
||||
const normalized_name = normalizeHeaderName(name, exec.buf);
|
||||
const all_values = try self._list.getAll(exec.call_arena, normalized_name);
|
||||
|
||||
if (all_values.len == 0) {
|
||||
return null;
|
||||
@@ -51,17 +51,17 @@ pub fn get(self: *const Headers, name: []const u8, page: *Page) !?[]const u8 {
|
||||
if (all_values.len == 1) {
|
||||
return all_values[0];
|
||||
}
|
||||
return try std.mem.join(page.call_arena, ", ", all_values);
|
||||
return try std.mem.join(exec.call_arena, ", ", all_values);
|
||||
}
|
||||
|
||||
pub fn has(self: *const Headers, name: []const u8, page: *Page) bool {
|
||||
const normalized_name = normalizeHeaderName(name, &page.buf);
|
||||
pub fn has(self: *const Headers, name: []const u8, exec: *const Execution) bool {
|
||||
const normalized_name = normalizeHeaderName(name, exec.buf);
|
||||
return self._list.has(normalized_name);
|
||||
}
|
||||
|
||||
pub fn set(self: *Headers, name: []const u8, value: []const u8, page: *Page) !void {
|
||||
const normalized_name = normalizeHeaderName(name, &page.buf);
|
||||
try self._list.set(page.arena, normalized_name, value);
|
||||
pub fn set(self: *Headers, name: []const u8, value: []const u8, exec: *const Execution) !void {
|
||||
const normalized_name = normalizeHeaderName(name, exec.buf);
|
||||
try self._list.set(exec.arena, normalized_name, value);
|
||||
}
|
||||
|
||||
pub fn keys(self: *Headers, exec: *const js.Execution) !*KeyValueList.KeyIterator {
|
||||
|
||||
@@ -26,6 +26,7 @@ const Page = @import("../../Page.zig");
|
||||
const Headers = @import("Headers.zig");
|
||||
const Blob = @import("../Blob.zig");
|
||||
const AbortSignal = @import("../AbortSignal.zig");
|
||||
const Execution = js.Execution;
|
||||
const Allocator = std.mem.Allocator;
|
||||
|
||||
const Request = @This();
|
||||
@@ -70,16 +71,16 @@ const Cache = enum {
|
||||
pub const js_enum_from_string = true;
|
||||
};
|
||||
|
||||
pub fn init(input: Input, opts_: ?InitOpts, page: *Page) !*Request {
|
||||
const arena = page.arena;
|
||||
pub fn init(input: Input, opts_: ?InitOpts, exec: *const Execution) !*Request {
|
||||
const arena = exec.arena;
|
||||
const url = switch (input) {
|
||||
.url => |u| try URL.resolve(arena, page.base(), u, .{ .always_dupe = true, .encoding = page.charset }),
|
||||
.url => |u| try URL.resolve(arena, exec.base(), u, .{ .always_dupe = true, .encoding = exec.charset.* }),
|
||||
.request => |r| try arena.dupeZ(u8, r._url),
|
||||
};
|
||||
|
||||
const opts = opts_ orelse InitOpts{};
|
||||
const method = if (opts.method) |m|
|
||||
try parseMethod(m, page)
|
||||
try parseMethod(m, exec)
|
||||
else switch (input) {
|
||||
.url => .GET,
|
||||
.request => |r| r._method,
|
||||
@@ -87,7 +88,7 @@ pub fn init(input: Input, opts_: ?InitOpts, page: *Page) !*Request {
|
||||
|
||||
const headers = if (opts.headers) |headers_init| switch (headers_init) {
|
||||
.obj => |h| h,
|
||||
else => try Headers.init(headers_init, page),
|
||||
else => try Headers.init(headers_init, exec),
|
||||
} else switch (input) {
|
||||
.url => null,
|
||||
.request => |r| r._headers,
|
||||
@@ -107,7 +108,7 @@ pub fn init(input: Input, opts_: ?InitOpts, page: *Page) !*Request {
|
||||
.request => |r| r._signal,
|
||||
};
|
||||
|
||||
return page._factory.create(Request{
|
||||
return exec._factory.create(Request{
|
||||
._url = url,
|
||||
._arena = arena,
|
||||
._method = method,
|
||||
@@ -119,12 +120,12 @@ pub fn init(input: Input, opts_: ?InitOpts, page: *Page) !*Request {
|
||||
});
|
||||
}
|
||||
|
||||
fn parseMethod(method: []const u8, page: *Page) !http.Method {
|
||||
fn parseMethod(method: []const u8, exec: *const Execution) !http.Method {
|
||||
if (method.len > "propfind".len) {
|
||||
return error.InvalidMethod;
|
||||
}
|
||||
|
||||
const lower = std.ascii.lowerString(&page.buf, method);
|
||||
const lower = std.ascii.lowerString(exec.buf, method);
|
||||
|
||||
const method_lookup = std.StaticStringMap(http.Method).initComptime(.{
|
||||
.{ "get", .GET },
|
||||
@@ -159,50 +160,50 @@ pub fn getSignal(self: *const Request) ?*AbortSignal {
|
||||
return self._signal;
|
||||
}
|
||||
|
||||
pub fn getHeaders(self: *Request, page: *Page) !*Headers {
|
||||
pub fn getHeaders(self: *Request, exec: *const Execution) !*Headers {
|
||||
if (self._headers) |headers| {
|
||||
return headers;
|
||||
}
|
||||
|
||||
const headers = try Headers.init(null, page);
|
||||
const headers = try Headers.init(null, exec);
|
||||
self._headers = headers;
|
||||
return headers;
|
||||
}
|
||||
|
||||
pub fn blob(self: *Request, page: *Page) !js.Promise {
|
||||
pub fn blob(self: *Request, exec: *const Execution) !js.Promise {
|
||||
const body = self._body orelse "";
|
||||
const headers = try self.getHeaders(page);
|
||||
const content_type = try headers.get("content-type", page) orelse "";
|
||||
const headers = try self.getHeaders(exec);
|
||||
const content_type = try headers.get("content-type", exec) orelse "";
|
||||
|
||||
const b = try Blob.initFromBytes(body, content_type, true, page);
|
||||
const b = try Blob.initFromBytes(body, content_type, true, exec.context.session);
|
||||
|
||||
return page.js.local.?.resolvePromise(b);
|
||||
return exec.context.local.?.resolvePromise(b);
|
||||
}
|
||||
|
||||
pub fn text(self: *const Request, page: *Page) !js.Promise {
|
||||
pub fn text(self: *const Request, exec: *const Execution) !js.Promise {
|
||||
const body = self._body orelse "";
|
||||
return page.js.local.?.resolvePromise(body);
|
||||
return exec.context.local.?.resolvePromise(body);
|
||||
}
|
||||
|
||||
pub fn json(self: *const Request, page: *Page) !js.Promise {
|
||||
pub fn json(self: *const Request, exec: *const Execution) !js.Promise {
|
||||
const body = self._body orelse "";
|
||||
const local = page.js.local.?;
|
||||
const local = exec.context.local.?;
|
||||
const value = local.parseJSON(body) catch {
|
||||
return local.rejectPromise(.{ .syntax_error = "failed to parse" });
|
||||
};
|
||||
return local.resolvePromise(try value.persist());
|
||||
}
|
||||
|
||||
pub fn arrayBuffer(self: *const Request, page: *Page) !js.Promise {
|
||||
return page.js.local.?.resolvePromise(js.ArrayBuffer{ .values = self._body orelse "" });
|
||||
pub fn arrayBuffer(self: *const Request, exec: *const Execution) !js.Promise {
|
||||
return exec.context.local.?.resolvePromise(js.ArrayBuffer{ .values = self._body orelse "" });
|
||||
}
|
||||
|
||||
pub fn bytes(self: *const Request, page: *Page) !js.Promise {
|
||||
return page.js.local.?.resolvePromise(js.TypedArray(u8){ .values = self._body orelse "" });
|
||||
pub fn bytes(self: *const Request, exec: *const Execution) !js.Promise {
|
||||
return exec.context.local.?.resolvePromise(js.TypedArray(u8){ .values = self._body orelse "" });
|
||||
}
|
||||
|
||||
pub fn clone(self: *const Request, page: *Page) !*Request {
|
||||
return page._factory.create(Request{
|
||||
pub fn clone(self: *const Request, exec: *const Execution) !*Request {
|
||||
return exec._factory.create(Request{
|
||||
._url = self._url,
|
||||
._arena = self._arena,
|
||||
._method = self._method,
|
||||
|
||||
@@ -18,15 +18,18 @@
|
||||
|
||||
const std = @import("std");
|
||||
const lp = @import("lightpanda");
|
||||
const js = @import("../../js/js.zig");
|
||||
const HttpClient = @import("../../HttpClient.zig");
|
||||
|
||||
const js = @import("../../js/js.zig");
|
||||
const Page = @import("../../Page.zig");
|
||||
const Session = @import("../../Session.zig");
|
||||
const Headers = @import("Headers.zig");
|
||||
const ReadableStream = @import("../streams/ReadableStream.zig");
|
||||
const Blob = @import("../Blob.zig");
|
||||
const HttpClient = @import("../../HttpClient.zig");
|
||||
|
||||
const Blob = @import("../Blob.zig");
|
||||
const ReadableStream = @import("../streams/ReadableStream.zig");
|
||||
|
||||
const Headers = @import("Headers.zig");
|
||||
|
||||
const Execution = js.Execution;
|
||||
const Allocator = std.mem.Allocator;
|
||||
|
||||
const Response = @This();
|
||||
@@ -69,9 +72,10 @@ pub const BodyInit = union(enum) {
|
||||
js_val: js.Value,
|
||||
};
|
||||
|
||||
pub fn init(body_: ?BodyInit, opts_: ?InitOpts, page: *Page) !*Response {
|
||||
const arena = try page.getArena(.large, "Response");
|
||||
errdefer page.releaseArena(arena);
|
||||
pub fn init(body_: ?BodyInit, opts_: ?InitOpts, exec: *const Execution) !*Response {
|
||||
const session = exec.context.session;
|
||||
const arena = try session.getArena(.large, "Response");
|
||||
errdefer session.releaseArena(arena);
|
||||
|
||||
const opts = opts_ orelse InitOpts{};
|
||||
const status_text = if (opts.statusText) |st| try arena.dupe(u8, st) else "";
|
||||
@@ -101,7 +105,7 @@ pub fn init(body_: ?BodyInit, opts_: ?InitOpts, page: *Page) !*Response {
|
||||
._body = body,
|
||||
._type = .basic,
|
||||
._is_redirected = false,
|
||||
._headers = try Headers.init(opts.headers, page),
|
||||
._headers = try Headers.init(opts.headers, exec),
|
||||
};
|
||||
return self;
|
||||
}
|
||||
@@ -146,11 +150,18 @@ pub fn getType(self: *const Response) []const u8 {
|
||||
return @tagName(self._type);
|
||||
}
|
||||
|
||||
pub fn getBody(self: *Response, page: *Page) !?*ReadableStream {
|
||||
pub fn getBody(self: *Response, exec: *const Execution) !?*ReadableStream {
|
||||
return switch (self._body) {
|
||||
.empty => null,
|
||||
.stream => |stream| stream,
|
||||
.bytes => |body| {
|
||||
// ReadableStream creation currently requires a Page. Workers
|
||||
// cannot produce stream bodies yet, so falling back to the page
|
||||
// here is safe.
|
||||
const page = switch (exec.context.global) {
|
||||
.page => |p| p,
|
||||
.worker => unreachable,
|
||||
};
|
||||
if (body.len == 0) {
|
||||
const stream = try ReadableStream.init(null, null, page);
|
||||
try stream._controller.close();
|
||||
@@ -165,17 +176,18 @@ pub fn isOK(self: *const Response) bool {
|
||||
return self._status >= 200 and self._status <= 299;
|
||||
}
|
||||
|
||||
pub fn getText(self: *const Response, page: *Page) !js.Promise {
|
||||
pub fn getText(self: *const Response, exec: *const Execution) !js.Promise {
|
||||
const local = exec.context.local.?;
|
||||
const body = switch (self._body) {
|
||||
.bytes => |b| b,
|
||||
.empty => "",
|
||||
.stream => return page.js.local.?.rejectPromise(.{ .type_error = "Cannot read text from stream body" }),
|
||||
.stream => return local.rejectPromise(.{ .type_error = "Cannot read text from stream body" }),
|
||||
};
|
||||
return page.js.local.?.resolvePromise(body);
|
||||
return local.resolvePromise(body);
|
||||
}
|
||||
|
||||
pub fn getJson(self: *Response, page: *Page) !js.Promise {
|
||||
const local = page.js.local.?;
|
||||
pub fn getJson(self: *Response, exec: *const Execution) !js.Promise {
|
||||
const local = exec.context.local.?;
|
||||
const body = switch (self._body) {
|
||||
.bytes => |b| b,
|
||||
.empty => "",
|
||||
@@ -187,11 +199,21 @@ pub fn getJson(self: *Response, page: *Page) !js.Promise {
|
||||
return local.resolvePromise(try value.persist());
|
||||
}
|
||||
|
||||
pub fn arrayBuffer(self: *Response, page: *Page) !js.Promise {
|
||||
pub fn arrayBuffer(self: *Response, exec: *const Execution) !js.Promise {
|
||||
const local = exec.context.local.?;
|
||||
return switch (self._body) {
|
||||
.bytes => |body| page.js.local.?.resolvePromise(js.ArrayBuffer{ .values = body }),
|
||||
.empty => page.js.local.?.resolvePromise(js.ArrayBuffer{ .values = "" }),
|
||||
.stream => |stream| StreamConsumer.start(stream, page),
|
||||
.bytes => |body| local.resolvePromise(js.ArrayBuffer{ .values = body }),
|
||||
.empty => local.resolvePromise(js.ArrayBuffer{ .values = "" }),
|
||||
.stream => |stream| blk: {
|
||||
// StreamConsumer currently requires a Page. Workers cannot
|
||||
// produce stream bodies yet, so falling back to the page here
|
||||
// is safe.
|
||||
const page = switch (exec.context.global) {
|
||||
.page => |p| p,
|
||||
.worker => unreachable,
|
||||
};
|
||||
break :blk StreamConsumer.start(stream, page);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@@ -308,20 +330,20 @@ const StreamConsumer = struct {
|
||||
}
|
||||
};
|
||||
|
||||
pub fn blob(self: *const Response, page: *Page) !js.Promise {
|
||||
const local = page.js.local.?;
|
||||
pub fn blob(self: *const Response, exec: *const Execution) !js.Promise {
|
||||
const local = exec.context.local.?;
|
||||
const body = switch (self._body) {
|
||||
.bytes => |b| b,
|
||||
.empty => "",
|
||||
.stream => return local.rejectPromise(.{ .type_error = "Cannot read blob from stream body" }),
|
||||
};
|
||||
const content_type = try self._headers.get("content-type", page) orelse "";
|
||||
const b = try Blob.initFromBytes(body, content_type, true, page);
|
||||
const content_type = try self._headers.get("content-type", exec) orelse "";
|
||||
const b = try Blob.initFromBytes(body, content_type, true, exec.context.session);
|
||||
return local.resolvePromise(b);
|
||||
}
|
||||
|
||||
pub fn bytes(self: *const Response, page: *Page) !js.Promise {
|
||||
const local = page.js.local.?;
|
||||
pub fn bytes(self: *const Response, exec: *const Execution) !js.Promise {
|
||||
const local = exec.context.local.?;
|
||||
const body = switch (self._body) {
|
||||
.bytes => |b| b,
|
||||
.empty => "",
|
||||
@@ -330,14 +352,15 @@ pub fn bytes(self: *const Response, page: *Page) !js.Promise {
|
||||
return local.resolvePromise(js.TypedArray(u8){ .values = body });
|
||||
}
|
||||
|
||||
pub fn clone(self: *const Response, page: *Page) !*Response {
|
||||
pub fn clone(self: *const Response, exec: *const Execution) !*Response {
|
||||
const session = exec.context.session;
|
||||
const body_len = switch (self._body) {
|
||||
.bytes => |b| b.len,
|
||||
.empty => 0,
|
||||
.stream => 0,
|
||||
};
|
||||
const arena = try page.getArena(body_len + self._url.len + 256, "Response.clone");
|
||||
errdefer page.releaseArena(arena);
|
||||
const arena = try session.getArena(body_len + self._url.len + 256, "Response.clone");
|
||||
errdefer session.releaseArena(arena);
|
||||
|
||||
const body: Body = switch (self._body) {
|
||||
.bytes => |b| .{ .bytes = try arena.dupe(u8, b) },
|
||||
@@ -356,7 +379,7 @@ pub fn clone(self: *const Response, page: *Page) !*Response {
|
||||
._body = body,
|
||||
._type = self._type,
|
||||
._is_redirected = self._is_redirected,
|
||||
._headers = try Headers.init(.{ .obj = self._headers }, page),
|
||||
._headers = try Headers.init(.{ .obj = self._headers }, exec),
|
||||
._http_response = null,
|
||||
};
|
||||
return cloned;
|
||||
|
||||
@@ -466,7 +466,7 @@ fn dispatchMessageEvent(self: *WebSocket, data: []const u8, frame_type: http.WsF
|
||||
switch (self._binary_type) {
|
||||
.arraybuffer => .{ .arraybuffer = .{ .values = data } },
|
||||
.blob => blk: {
|
||||
const blob = try Blob.initFromBytes(data, "", false, page);
|
||||
const blob = try Blob.initFromBytes(data, "", false, page._session);
|
||||
blob.acquireRef();
|
||||
break :blk .{ .blob = blob };
|
||||
},
|
||||
|
||||
@@ -95,7 +95,7 @@ pub fn init(page: *Page) !*XMLHttpRequest {
|
||||
._page = page,
|
||||
._arena = arena,
|
||||
._proto = undefined,
|
||||
._request_headers = try Headers.init(null, page),
|
||||
._request_headers = try Headers.init(null, &page.js.execution),
|
||||
});
|
||||
return self;
|
||||
}
|
||||
@@ -218,7 +218,7 @@ pub fn setRequestHeader(self: *XMLHttpRequest, name: []const u8, value: []const
|
||||
if (self._ready_state != .opened) {
|
||||
return error.InvalidStateError;
|
||||
}
|
||||
return self._request_headers.append(name, value, page);
|
||||
return self._request_headers.append(name, value, &page.js.execution);
|
||||
}
|
||||
|
||||
pub fn send(self: *XMLHttpRequest, body_: ?[]const u8) !void {
|
||||
|
||||
Reference in New Issue
Block a user