Merge pull request #1109 from lightpanda-io/remove_generic_js
Some checks failed
e2e-test / zig build release (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 / zig build dev (push) Has been cancelled
zig-test / browser fetch (push) Has been cancelled
zig-test / zig test (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

Remove the generic nature of Env and most of the JS classes
This commit is contained in:
Karl Seguin
2025-10-02 10:58:34 +08:00
committed by GitHub
59 changed files with 4574 additions and 5684 deletions

View File

@@ -4,7 +4,7 @@ const Allocator = std.mem.Allocator;
const log = @import("log.zig");
const Http = @import("http/Http.zig");
const Platform = @import("runtime/js.zig").Platform;
const Platform = @import("browser/js/js.zig").Platform;
const Telemetry = @import("telemetry/telemetry.zig").Telemetry;
const Notification = @import("notification.zig").Notification;

View File

@@ -18,10 +18,10 @@
const std = @import("std");
const js = @import("js/js.zig");
const log = @import("../log.zig");
const parser = @import("netsurf.zig");
const Env = @import("env.zig").Env;
const Page = @import("page.zig").Page;
const DataURI = @import("DataURI.zig");
const Http = @import("../http/Http.zig");
@@ -627,7 +627,7 @@ const Script = struct {
const Callback = union(enum) {
string: []const u8,
function: Env.Function,
function: js.Function,
};
const Source = union(enum) {
@@ -664,7 +664,7 @@ const Script = struct {
});
const js_context = page.main_context;
var try_catch: Env.TryCatch = undefined;
var try_catch: js.TryCatch = undefined;
try_catch.init(js_context);
defer try_catch.deinit();
@@ -706,7 +706,7 @@ const Script = struct {
switch (callback) {
.string => |str| {
var try_catch: Env.TryCatch = undefined;
var try_catch: js.TryCatch = undefined;
try_catch.init(page.main_context);
defer try_catch.deinit();
@@ -728,7 +728,7 @@ const Script = struct {
};
defer parser.eventDestroy(loadevt);
var result: Env.Function.Result = undefined;
var result: js.Function.Result = undefined;
const iface = Event.toInterface(loadevt);
f.tryCall(void, .{iface}, &result) catch {
log.warn(.user_script, "script callback", .{

View File

@@ -26,7 +26,7 @@
// this quickly proved necessary, since different fields are needed on the same
// data at different levels of the prototype chain. This isn't memory efficient.
const Env = @import("env.zig").Env;
const js = @import("js/js.zig");
const parser = @import("netsurf.zig");
const DataSet = @import("html/DataSet.zig");
const ShadowRoot = @import("dom/shadow_root.zig").ShadowRoot;
@@ -34,8 +34,8 @@ const StyleSheet = @import("cssom/StyleSheet.zig");
const CSSStyleDeclaration = @import("cssom/CSSStyleDeclaration.zig");
// for HTMLScript (but probably needs to be added to more)
onload: ?Env.Function = null,
onerror: ?Env.Function = null,
onload: ?js.Function = null,
onerror: ?js.Function = null,
// for HTMLElement
style: CSSStyleDeclaration = .empty,
@@ -53,7 +53,7 @@ style_sheet: ?*StyleSheet = null,
// for dom/document
active_element: ?*parser.Element = null,
adopted_style_sheets: ?Env.JsObject = null,
adopted_style_sheets: ?js.JsObject = null,
// for HTMLSelectElement
// By default, if no option is explicitly selected, the first option should

View File

@@ -21,8 +21,8 @@ const std = @import("std");
const Allocator = std.mem.Allocator;
const ArenaAllocator = std.heap.ArenaAllocator;
const js = @import("js/js.zig");
const State = @import("State.zig");
const Env = @import("env.zig").Env;
const App = @import("../app.zig").App;
const Session = @import("session.zig").Session;
const Notification = @import("../notification.zig").Notification;
@@ -34,7 +34,7 @@ const HttpClient = @import("../http/Client.zig");
// You can create multiple browser instances.
// A browser contains only one session.
pub const Browser = struct {
env: *Env,
env: *js.Env,
app: *App,
session: ?Session,
allocator: Allocator,
@@ -48,7 +48,7 @@ pub const Browser = struct {
pub fn init(app: *App) !Browser {
const allocator = app.allocator;
const env = try Env.init(allocator, &app.platform, .{});
const env = try js.Env.init(allocator, &app.platform, .{});
errdefer env.deinit();
const notification = try Notification.init(allocator, app.notification);

View File

@@ -20,47 +20,47 @@ const std = @import("std");
const builtin = @import("builtin");
const log = @import("../../log.zig");
const js = @import("../js/js.zig");
const Page = @import("../page.zig").Page;
const JsObject = @import("../env.zig").Env.JsObject;
pub const Console = struct {
// TODO: configurable writer
timers: std.StringHashMapUnmanaged(u32) = .{},
counts: std.StringHashMapUnmanaged(u32) = .{},
pub fn _lp(values: []JsObject, page: *Page) !void {
pub fn _lp(values: []js.JsObject, page: *Page) !void {
if (values.len == 0) {
return;
}
log.fatal(.console, "lightpanda", .{ .args = try serializeValues(values, page) });
}
pub fn _log(values: []JsObject, page: *Page) !void {
pub fn _log(values: []js.JsObject, page: *Page) !void {
if (values.len == 0) {
return;
}
log.info(.console, "info", .{ .args = try serializeValues(values, page) });
}
pub fn _info(values: []JsObject, page: *Page) !void {
pub fn _info(values: []js.JsObject, page: *Page) !void {
return _log(values, page);
}
pub fn _debug(values: []JsObject, page: *Page) !void {
pub fn _debug(values: []js.JsObject, page: *Page) !void {
if (values.len == 0) {
return;
}
log.debug(.console, "debug", .{ .args = try serializeValues(values, page) });
}
pub fn _warn(values: []JsObject, page: *Page) !void {
pub fn _warn(values: []js.JsObject, page: *Page) !void {
if (values.len == 0) {
return;
}
log.warn(.console, "warn", .{ .args = try serializeValues(values, page) });
}
pub fn _error(values: []JsObject, page: *Page) !void {
pub fn _error(values: []js.JsObject, page: *Page) !void {
if (values.len == 0) {
return;
}
@@ -132,7 +132,7 @@ pub const Console = struct {
log.warn(.console, "timer stop", .{ .label = label, .elapsed = elapsed - kv.value });
}
pub fn _assert(assertion: JsObject, values: []JsObject, page: *Page) !void {
pub fn _assert(assertion: js.JsObject, values: []js.JsObject, page: *Page) !void {
if (assertion.isTruthy()) {
return;
}
@@ -143,7 +143,7 @@ pub const Console = struct {
log.info(.console, "assertion failed", .{ .values = serialized_values });
}
fn serializeValues(values: []JsObject, page: *Page) ![]const u8 {
fn serializeValues(values: []js.JsObject, page: *Page) ![]const u8 {
if (values.len == 0) {
return "";
}

View File

@@ -17,14 +17,14 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>.
const std = @import("std");
const Env = @import("../env.zig").Env;
const js = @import("../js/js.zig");
const uuidv4 = @import("../../id.zig").uuidv4;
// https://w3c.github.io/webcrypto/#crypto-interface
pub const Crypto = struct {
_not_empty: bool = true,
pub fn _getRandomValues(_: *const Crypto, js_obj: Env.JsObject) !Env.JsObject {
pub fn _getRandomValues(_: *const Crypto, js_obj: js.JsObject) !js.JsObject {
var into = try js_obj.toZig(Crypto, "getRandomValues", RandomValues);
const buf = into.asBuffer();
if (buf.len > 65_536) {

View File

@@ -18,7 +18,7 @@
const std = @import("std");
const Env = @import("../env.zig").Env;
const js = @import("../js/js.zig");
const Page = @import("../page.zig").Page;
const StyleSheet = @import("StyleSheet.zig");
const CSSRuleList = @import("CSSRuleList.zig");
@@ -73,7 +73,7 @@ pub fn _deleteRule(self: *CSSStyleSheet, index: usize) !void {
_ = self.css_rules.list.orderedRemove(index);
}
pub fn _replace(self: *CSSStyleSheet, text: []const u8, page: *Page) !Env.Promise {
pub fn _replace(self: *CSSStyleSheet, text: []const u8, page: *Page) !js.Promise {
_ = self;
_ = text;
// TODO: clear self.css_rules

View File

@@ -18,19 +18,17 @@
const std = @import("std");
const js = @import("../js/js.zig");
const Page = @import("../page.zig").Page;
const JsObject = @import("../env.zig").JsObject;
const Promise = @import("../env.zig").Promise;
const PromiseResolver = @import("../env.zig").PromiseResolver;
const Animation = @This();
effect: ?JsObject,
timeline: ?JsObject,
ready_resolver: ?PromiseResolver,
finished_resolver: ?PromiseResolver,
effect: ?js.JsObject,
timeline: ?js.JsObject,
ready_resolver: ?js.PromiseResolver,
finished_resolver: ?js.PromiseResolver,
pub fn constructor(effect: ?JsObject, timeline: ?JsObject) !Animation {
pub fn constructor(effect: ?js.JsObject, timeline: ?js.JsObject) !Animation {
return .{
.effect = if (effect) |eo| try eo.persist() else null,
.timeline = if (timeline) |to| try to.persist() else null,
@@ -49,7 +47,7 @@ pub fn get_pending(self: *const Animation) bool {
return false;
}
pub fn get_finished(self: *Animation, page: *Page) !Promise {
pub fn get_finished(self: *Animation, page: *Page) !js.Promise {
if (self.finished_resolver == null) {
const resolver = page.main_context.createPromiseResolver();
try resolver.resolve(self);
@@ -58,7 +56,7 @@ pub fn get_finished(self: *Animation, page: *Page) !Promise {
return self.finished_resolver.?.promise();
}
pub fn get_ready(self: *Animation, page: *Page) !Promise {
pub fn get_ready(self: *Animation, page: *Page) !js.Promise {
// never resolved, because we're always "finished"
if (self.ready_resolver == null) {
const resolver = page.main_context.createPromiseResolver();
@@ -67,19 +65,19 @@ pub fn get_ready(self: *Animation, page: *Page) !Promise {
return self.ready_resolver.?.promise();
}
pub fn get_effect(self: *const Animation) ?JsObject {
pub fn get_effect(self: *const Animation) ?js.JsObject {
return self.effect;
}
pub fn set_effect(self: *Animation, effect: JsObject) !void {
pub fn set_effect(self: *Animation, effect: js.JsObject) !void {
self.effect = try effect.persist();
}
pub fn get_timeline(self: *const Animation) ?JsObject {
pub fn get_timeline(self: *const Animation) ?js.JsObject {
return self.timeline;
}
pub fn set_timeline(self: *Animation, timeline: JsObject) !void {
pub fn set_timeline(self: *Animation, timeline: js.JsObject) !void {
self.timeline = try timeline.persist();
}

View File

@@ -20,13 +20,11 @@ const std = @import("std");
const log = @import("../../log.zig");
const parser = @import("../netsurf.zig");
const Env = @import("../env.zig").Env;
const js = @import("../js/js.zig");
const Page = @import("../page.zig").Page;
const EventTarget = @import("../dom/event_target.zig").EventTarget;
const EventHandler = @import("../events/event.zig").EventHandler;
const JsObject = Env.JsObject;
const Function = Env.Function;
const Allocator = std.mem.Allocator;
const MAX_QUEUE_SIZE = 10;
@@ -72,22 +70,22 @@ pub const MessagePort = struct {
pair: *MessagePort,
closed: bool = false,
started: bool = false,
onmessage_cbk: ?Function = null,
onmessageerror_cbk: ?Function = null,
onmessage_cbk: ?js.Function = null,
onmessageerror_cbk: ?js.Function = null,
// This is the queue of messages to dispatch to THIS MessagePort when the
// MessagePort is started.
queue: std.ArrayListUnmanaged(JsObject) = .empty,
queue: std.ArrayListUnmanaged(js.JsObject) = .empty,
pub const PostMessageOption = union(enum) {
transfer: JsObject,
transfer: js.JsObject,
options: Opts,
pub const Opts = struct {
transfer: JsObject,
transfer: js.JsObject,
};
};
pub fn _postMessage(self: *MessagePort, obj: JsObject, opts_: ?PostMessageOption, page: *Page) !void {
pub fn _postMessage(self: *MessagePort, obj: js.JsObject, opts_: ?PostMessageOption, page: *Page) !void {
if (self.closed) {
return;
}
@@ -124,10 +122,10 @@ pub const MessagePort = struct {
self.pair.closed = true;
}
pub fn get_onmessage(self: *MessagePort) ?Function {
pub fn get_onmessage(self: *MessagePort) ?js.Function {
return self.onmessage_cbk;
}
pub fn get_onmessageerror(self: *MessagePort) ?Function {
pub fn get_onmessageerror(self: *MessagePort) ?js.Function {
return self.onmessageerror_cbk;
}
@@ -152,7 +150,7 @@ pub const MessagePort = struct {
// called from our pair. If port1.postMessage("x") is called, then this
// will be called on port2.
fn dispatchOrQueue(self: *MessagePort, obj: JsObject, arena: Allocator) !void {
fn dispatchOrQueue(self: *MessagePort, obj: js.JsObject, arena: Allocator) !void {
// our pair should have checked this already
std.debug.assert(self.closed == false);
@@ -167,7 +165,7 @@ pub const MessagePort = struct {
return self.queue.append(arena, try obj.persist());
}
fn dispatch(self: *MessagePort, obj: JsObject) !void {
fn dispatch(self: *MessagePort, obj: js.JsObject) !void {
// obj is already persisted, don't use `MessageEvent.constructor`, but
// go directly to `init`, which assumes persisted objects.
var evt = try MessageEvent.init(.{ .data = obj });
@@ -182,7 +180,7 @@ pub const MessagePort = struct {
alloc: Allocator,
typ: []const u8,
listener: EventHandler.Listener,
) !?Function {
) !?js.Function {
const target = @as(*parser.EventTarget, @ptrCast(self));
const eh = (try EventHandler.register(alloc, target, typ, listener, null)) orelse unreachable;
return eh.callback;
@@ -207,12 +205,12 @@ pub const MessageEvent = struct {
pub const union_make_copy = true;
proto: parser.Event,
data: ?JsObject,
data: ?js.JsObject,
// You would think if port1 sends to port2, the source would be port2
// (which is how I read the documentation), but it appears to always be
// null. It can always be set explicitly via the constructor;
source: ?JsObject,
source: ?js.JsObject,
origin: []const u8,
@@ -226,8 +224,8 @@ pub const MessageEvent = struct {
ports: []*MessagePort,
const Options = struct {
data: ?JsObject = null,
source: ?JsObject = null,
data: ?js.JsObject = null,
source: ?js.JsObject = null,
origin: []const u8 = "",
lastEventId: []const u8 = "",
ports: []*MessagePort = &.{},
@@ -243,7 +241,7 @@ pub const MessageEvent = struct {
});
}
// This is like "constructor", but it assumes JsObjects have already been
// This is like "constructor", but it assumes js.JsObjects have already been
// persisted. Necessary because this `new MessageEvent()` can be called
// directly from JS OR from a port.postMessage. In the latter case, data
// may have already been persisted (as it might need to be queued);
@@ -263,7 +261,7 @@ pub const MessageEvent = struct {
};
}
pub fn get_data(self: *const MessageEvent) !?JsObject {
pub fn get_data(self: *const MessageEvent) !?js.JsObject {
return self.data;
}
@@ -271,7 +269,7 @@ pub const MessageEvent = struct {
return self.origin;
}
pub fn get_source(self: *const MessageEvent) ?JsObject {
pub fn get_source(self: *const MessageEvent) ?js.JsObject {
return self.source;
}

View File

@@ -18,6 +18,7 @@
const std = @import("std");
const js = @import("../js/js.zig");
const parser = @import("../netsurf.zig");
const Page = @import("../page.zig").Page;
@@ -37,8 +38,6 @@ const Range = @import("range.zig").Range;
const CustomEvent = @import("../events/custom_event.zig").CustomEvent;
const Env = @import("../env.zig").Env;
const DOMImplementation = @import("implementation.zig").DOMImplementation;
// WEB IDL https://dom.spec.whatwg.org/#document
@@ -155,13 +154,13 @@ pub const Document = struct {
// the spec changed to return an HTMLCollection instead.
// That's why we reimplemented getElementsByTagName by using an
// HTMLCollection in zig here.
pub fn _getElementsByTagName(self: *parser.Document, tag_name: Env.String) !collection.HTMLCollection {
pub fn _getElementsByTagName(self: *parser.Document, tag_name: js.String) !collection.HTMLCollection {
return collection.HTMLCollectionByTagName(parser.documentToNode(self), tag_name.string, .{
.include_root = true,
});
}
pub fn _getElementsByClassName(self: *parser.Document, class_names: Env.String) !collection.HTMLCollection {
pub fn _getElementsByClassName(self: *parser.Document, class_names: js.String) !collection.HTMLCollection {
return collection.HTMLCollectionByClassName(parser.documentToNode(self), class_names.string, .{
.include_root = true,
});
@@ -299,7 +298,7 @@ pub const Document = struct {
return &.{};
}
pub fn get_adoptedStyleSheets(self: *parser.Document, page: *Page) !Env.JsObject {
pub fn get_adoptedStyleSheets(self: *parser.Document, page: *Page) !js.JsObject {
const state = try page.getOrCreateNodeState(@ptrCast(@alignCast(self)));
if (state.adopted_style_sheets) |obj| {
return obj;
@@ -310,7 +309,7 @@ pub const Document = struct {
return obj;
}
pub fn set_adoptedStyleSheets(self: *parser.Document, sheets: Env.JsObject, page: *Page) !void {
pub fn set_adoptedStyleSheets(self: *parser.Document, sheets: js.JsObject, page: *Page) !void {
const state = try page.getOrCreateNodeState(@ptrCast(@alignCast(self)));
state.adopted_style_sheets = try sheets.persist();
}

View File

@@ -18,8 +18,8 @@
const std = @import("std");
const js = @import("../js/js.zig");
const parser = @import("../netsurf.zig");
const Env = @import("../env.zig").Env;
const Page = @import("../page.zig").Page;
const css = @import("css.zig");
@@ -34,7 +34,6 @@ const HTMLElem = @import("../html/elements.zig");
const ShadowRoot = @import("../dom/shadow_root.zig").ShadowRoot;
const Animation = @import("Animation.zig");
const JsObject = @import("../env.zig").JsObject;
pub const Union = @import("../html/elements.zig").Union;
@@ -436,7 +435,7 @@ pub const Element = struct {
return try parser.elementRemoveAttributeNode(self, attr);
}
pub fn _getElementsByTagName(self: *parser.Element, tag_name: Env.String) !collection.HTMLCollection {
pub fn _getElementsByTagName(self: *parser.Element, tag_name: js.String) !collection.HTMLCollection {
return collection.HTMLCollectionByTagName(
parser.elementToNode(self),
tag_name.string,
@@ -444,7 +443,7 @@ pub const Element = struct {
);
}
pub fn _getElementsByClassName(self: *parser.Element, class_names: Env.String) !collection.HTMLCollection {
pub fn _getElementsByClassName(self: *parser.Element, class_names: js.String) !collection.HTMLCollection {
return try collection.HTMLCollectionByClassName(
parser.elementToNode(self),
class_names.string,
@@ -661,7 +660,7 @@ pub const Element = struct {
return sr;
}
pub fn _animate(self: *parser.Element, effect: JsObject, opts: JsObject) !Animation {
pub fn _animate(self: *parser.Element, effect: js.JsObject, opts: js.JsObject) !Animation {
_ = self;
_ = opts;
return Animation.constructor(effect, null);

View File

@@ -23,7 +23,6 @@ const parser = @import("../netsurf.zig");
const Element = @import("element.zig").Element;
const Union = @import("element.zig").Union;
const JsThis = @import("../env.zig").JsThis;
const Walker = @import("walker.zig").Walker;
const Matcher = union(enum) {

View File

@@ -18,11 +18,11 @@
const std = @import("std");
const js = @import("../js/js.zig");
const log = @import("../../log.zig");
const parser = @import("../netsurf.zig");
const Page = @import("../page.zig").Page;
const Env = @import("../env.zig").Env;
const Element = @import("element.zig").Element;
pub const Interfaces = .{
@@ -40,14 +40,14 @@ pub const Interfaces = .{
// https://developer.mozilla.org/en-US/docs/Web/API/IntersectionObserver
pub const IntersectionObserver = struct {
page: *Page,
callback: Env.Function,
callback: js.Function,
options: IntersectionObserverOptions,
observed_entries: std.ArrayListUnmanaged(IntersectionObserverEntry),
// new IntersectionObserver(callback)
// new IntersectionObserver(callback, options) [not supported yet]
pub fn constructor(callback: Env.Function, options_: ?IntersectionObserverOptions, page: *Page) !IntersectionObserver {
pub fn constructor(callback: js.Function, options_: ?IntersectionObserverOptions, page: *Page) !IntersectionObserver {
var options = IntersectionObserverOptions{
.root = parser.documentToNode(parser.documentHTMLToDocument(page.window.document)),
.rootMargin = "0px 0px 0px 0px",
@@ -84,7 +84,7 @@ pub const IntersectionObserver = struct {
.options = &self.options,
});
var result: Env.Function.Result = undefined;
var result: js.Function.Result = undefined;
self.callback.tryCall(void, .{self.observed_entries.items}, &result) catch {
log.debug(.user_script, "callback error", .{
.err = result.exception,

View File

@@ -18,11 +18,11 @@
const std = @import("std");
const js = @import("../js/js.zig");
const log = @import("../../log.zig");
const parser = @import("../netsurf.zig");
const Page = @import("../page.zig").Page;
const Env = @import("../env.zig").Env;
const NodeList = @import("nodelist.zig").NodeList;
pub const Interfaces = .{
@@ -35,7 +35,7 @@ const Walker = @import("../dom/walker.zig").WalkerChildren;
// WEB IDL https://dom.spec.whatwg.org/#interface-mutationobserver
pub const MutationObserver = struct {
page: *Page,
cbk: Env.Function,
cbk: js.Function,
scheduled: bool,
observers: std.ArrayListUnmanaged(*Observer),
@@ -43,7 +43,7 @@ pub const MutationObserver = struct {
// execute our callback with it.
observed: std.ArrayListUnmanaged(MutationRecord),
pub fn constructor(cbk: Env.Function, page: *Page) !MutationObserver {
pub fn constructor(cbk: js.Function, page: *Page) !MutationObserver {
return .{
.cbk = cbk,
.page = page,
@@ -122,7 +122,7 @@ pub const MutationObserver = struct {
defer self.observed.clearRetainingCapacity();
var result: Env.Function.Result = undefined;
var result: js.Function.Result = undefined;
self.cbk.tryCallWithThis(void, self, .{records}, &result) catch {
log.debug(.user_script, "callback error", .{
.err = result.exception,

View File

@@ -20,7 +20,7 @@ const std = @import("std");
const log = @import("../../log.zig");
const parser = @import("../netsurf.zig");
const generate = @import("../../runtime/generate.zig");
const generate = @import("../js/generate.zig");
const Page = @import("../page.zig").Page;
const EventTarget = @import("event_target.zig").EventTarget;

View File

@@ -17,8 +17,8 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>.
const std = @import("std");
const js = @import("../js/js.zig");
const parser = @import("../netsurf.zig");
const Env = @import("../env.zig").Env;
const Node = @import("node.zig").Node;
pub const NodeFilter = struct {
@@ -43,7 +43,7 @@ pub const NodeFilter = struct {
const VerifyResult = enum { accept, skip, reject };
pub fn verify(what_to_show: u32, filter: ?Env.Function, node: *parser.Node) !VerifyResult {
pub fn verify(what_to_show: u32, filter: ?js.Function, node: *parser.Node) !VerifyResult {
const node_type = parser.nodeType(node);
// Verify that we can show this node type.

View File

@@ -18,8 +18,8 @@
const std = @import("std");
const js = @import("../js/js.zig");
const parser = @import("../netsurf.zig");
const Env = @import("../env.zig").Env;
const NodeFilter = @import("node_filter.zig");
const Node = @import("node.zig").Node;
const NodeUnion = @import("node.zig").Union;
@@ -37,7 +37,7 @@ pub const NodeIterator = struct {
reference_node: *parser.Node,
what_to_show: u32,
filter: ?NodeIteratorOpts,
filter_func: ?Env.Function,
filter_func: ?js.Function,
pointer_before_current: bool = true,
// used to track / block recursive filters
is_in_callback: bool = false,
@@ -45,15 +45,15 @@ pub const NodeIterator = struct {
// One of the few cases where null and undefined resolve to different default.
// We need the raw JsObject so that we can probe the tri state:
// null, undefined or i32.
pub const WhatToShow = Env.JsObject;
pub const WhatToShow = js.JsObject;
pub const NodeIteratorOpts = union(enum) {
function: Env.Function,
object: struct { acceptNode: Env.Function },
function: js.Function,
object: struct { acceptNode: js.Function },
};
pub fn init(node: *parser.Node, what_to_show_: ?WhatToShow, filter: ?NodeIteratorOpts) !NodeIterator {
var filter_func: ?Env.Function = null;
var filter_func: ?js.Function = null;
if (filter) |f| {
filter_func = switch (f) {
.function => |func| func,

View File

@@ -19,11 +19,10 @@
const std = @import("std");
const Allocator = std.mem.Allocator;
const js = @import("../js/js.zig");
const log = @import("../../log.zig");
const parser = @import("../netsurf.zig");
const JsThis = @import("../env.zig").JsThis;
const Function = @import("../env.zig").Function;
const NodeUnion = @import("node.zig").Union;
const Node = @import("node.zig").Node;
@@ -148,10 +147,10 @@ pub const NodeList = struct {
// };
// }
pub fn _forEach(self: *NodeList, cbk: Function) !void { // TODO handle thisArg
pub fn _forEach(self: *NodeList, cbk: js.Function) !void { // TODO handle thisArg
for (self.nodes.items, 0..) |n, i| {
const ii: u32 = @intCast(i);
var result: Function.Result = undefined;
var result: js.Function.Result = undefined;
cbk.tryCall(void, .{ n, ii, self }, &result) catch {
log.debug(.user_script, "forEach callback", .{ .err = result.exception, .stack = result.stack });
};
@@ -175,7 +174,7 @@ pub const NodeList = struct {
}
// TODO entries() https://developer.mozilla.org/en-US/docs/Web/API/NodeList/entries
pub fn postAttach(self: *NodeList, js_this: JsThis) !void {
pub fn postAttach(self: *NodeList, js_this: js.JsThis) !void {
const len = self.get_length();
for (0..len) |i| {
const node = try self._item(@intCast(i)) orelse unreachable;

View File

@@ -18,9 +18,9 @@
const std = @import("std");
const js = @import("../js/js.zig");
const parser = @import("../netsurf.zig");
const EventTarget = @import("../dom/event_target.zig").EventTarget;
const Env = @import("../env.zig").Env;
const Page = @import("../page.zig").Page;
const milliTimestamp = @import("../../datetime.zig").milliTimestamp;
@@ -61,7 +61,7 @@ pub const Performance = struct {
return milliTimestamp() - self.time_origin;
}
pub fn _mark(_: *Performance, name: Env.String, _options: ?PerformanceMark.Options, page: *Page) !PerformanceMark {
pub fn _mark(_: *Performance, name: js.String, _options: ?PerformanceMark.Options, page: *Page) !PerformanceMark {
const mark: PerformanceMark = try .constructor(name, _options, page);
// TODO: Should store this in an entries list
return mark;
@@ -148,14 +148,14 @@ pub const PerformanceMark = struct {
pub const prototype = *PerformanceEntry;
proto: PerformanceEntry,
detail: ?Env.JsObject,
detail: ?js.JsObject,
const Options = struct {
detail: ?Env.JsObject = null,
detail: ?js.JsObject = null,
startTime: ?f64 = null,
};
pub fn constructor(name: Env.String, _options: ?Options, page: *Page) !PerformanceMark {
pub fn constructor(name: js.String, _options: ?Options, page: *Page) !PerformanceMark {
const perf = &page.window.performance;
const options = _options orelse Options{};
@@ -171,7 +171,7 @@ pub const PerformanceMark = struct {
return .{ .proto = proto, .detail = detail };
}
pub fn get_detail(self: *const PerformanceMark) ?Env.JsObject {
pub fn get_detail(self: *const PerformanceMark) ?js.JsObject {
return self.detail;
}
};

View File

@@ -17,7 +17,7 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>.
const std = @import("std");
const Env = @import("../env.zig").Env;
const js = @import("../js/js.zig");
const PerformanceEntry = @import("performance.zig").PerformanceEntry;
@@ -25,7 +25,7 @@ const PerformanceEntry = @import("performance.zig").PerformanceEntry;
pub const PerformanceObserver = struct {
pub const _supportedEntryTypes = [0][]const u8{};
pub fn constructor(cbk: Env.Function) PerformanceObserver {
pub fn constructor(cbk: js.Function) PerformanceObserver {
_ = cbk;
return .{};
}

View File

@@ -16,7 +16,7 @@
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
const Env = @import("../env.zig").Env;
const js = @import("../js/js.zig");
const parser = @import("../netsurf.zig");
pub const Interfaces = .{
@@ -25,7 +25,7 @@ pub const Interfaces = .{
// WEB IDL https://drafts.csswg.org/resize-observer/#resize-observer-interface
pub const ResizeObserver = struct {
pub fn constructor(cbk: Env.Function) ResizeObserver {
pub fn constructor(cbk: js.Function) ResizeObserver {
_ = cbk;
return .{};
}

View File

@@ -20,7 +20,7 @@ const std = @import("std");
const dump = @import("../dump.zig");
const parser = @import("../netsurf.zig");
const Env = @import("../env.zig").Env;
const js = @import(".././js/js.zig");
const Page = @import("../page.zig").Page;
const Node = @import("node.zig").Node;
const Element = @import("element.zig").Element;
@@ -34,7 +34,7 @@ pub const ShadowRoot = struct {
mode: Mode,
host: *parser.Element,
proto: *parser.DocumentFragment,
adopted_style_sheets: ?Env.JsObject = null,
adopted_style_sheets: ?js.JsObject = null,
pub const Mode = enum {
open,
@@ -45,7 +45,7 @@ pub const ShadowRoot = struct {
return Element.toInterface(self.host);
}
pub fn get_adoptedStyleSheets(self: *ShadowRoot, page: *Page) !Env.JsObject {
pub fn get_adoptedStyleSheets(self: *ShadowRoot, page: *Page) !js.JsObject {
if (self.adopted_style_sheets) |obj| {
return obj;
}
@@ -55,7 +55,7 @@ pub const ShadowRoot = struct {
return obj;
}
pub fn set_adoptedStyleSheets(self: *ShadowRoot, sheets: Env.JsObject) !void {
pub fn set_adoptedStyleSheets(self: *ShadowRoot, sheets: js.JsObject) !void {
self.adopted_style_sheets = try sheets.persist();
}

View File

@@ -18,12 +18,11 @@
const std = @import("std");
const js = @import("../js/js.zig");
const log = @import("../../log.zig");
const parser = @import("../netsurf.zig");
const iterator = @import("../iterator/iterator.zig");
const Function = @import("../env.zig").Function;
const JsObject = @import("../env.zig").JsObject;
const DOMException = @import("exceptions.zig").DOMException;
pub const Interfaces = .{
@@ -137,10 +136,10 @@ pub const DOMTokenList = struct {
}
// TODO handle thisArg
pub fn _forEach(self: *parser.TokenList, cbk: Function, this_arg: JsObject) !void {
pub fn _forEach(self: *parser.TokenList, cbk: js.Function, this_arg: js.JsObject) !void {
var entries = _entries(self);
while (try entries._next()) |entry| {
var result: Function.Result = undefined;
var result: js.Function.Result = undefined;
cbk.tryCallWithThis(void, this_arg, .{ entry.@"1", entry.@"0", self }, &result) catch {
log.debug(.user_script, "callback error", .{
.err = result.exception,

View File

@@ -17,10 +17,10 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>.
const std = @import("std");
const js = @import("../js/js.zig");
const parser = @import("../netsurf.zig");
const NodeFilter = @import("node_filter.zig");
const Env = @import("../env.zig").Env;
const Node = @import("node.zig").Node;
const NodeUnion = @import("node.zig").Union;
@@ -30,20 +30,20 @@ pub const TreeWalker = struct {
current_node: *parser.Node,
what_to_show: u32,
filter: ?TreeWalkerOpts,
filter_func: ?Env.Function,
filter_func: ?js.Function,
// One of the few cases where null and undefined resolve to different default.
// We need the raw JsObject so that we can probe the tri state:
// null, undefined or i32.
pub const WhatToShow = Env.JsObject;
pub const WhatToShow = js.JsObject;
pub const TreeWalkerOpts = union(enum) {
function: Env.Function,
object: struct { acceptNode: Env.Function },
function: js.Function,
object: struct { acceptNode: js.Function },
};
pub fn init(node: *parser.Node, what_to_show_: ?WhatToShow, filter: ?TreeWalkerOpts) !TreeWalker {
var filter_func: ?Env.Function = null;
var filter_func: ?js.Function = null;
if (filter) |f| {
filter_func = switch (f) {

View File

@@ -19,7 +19,6 @@
const std = @import("std");
const log = @import("../../log.zig");
const Env = @import("../env.zig").Env;
const Page = @import("../page.zig").Page;
// https://encoding.spec.whatwg.org/#interface-textdecoder

View File

@@ -18,7 +18,7 @@
const std = @import("std");
const Env = @import("../env.zig").Env;
const js = @import("../js/js.zig");
// https://encoding.spec.whatwg.org/#interface-textencoder
const TextEncoder = @This();
@@ -31,7 +31,7 @@ pub fn get_encoding(_: *const TextEncoder) []const u8 {
return "utf-8";
}
pub fn _encode(_: *const TextEncoder, v: []const u8) !Env.TypedArray(u8) {
pub fn _encode(_: *const TextEncoder, v: []const u8) !js.TypedArray(u8) {
// Ensure the input is a valid utf-8
// It seems chrome accepts invalid utf-8 sequence.
//

View File

@@ -1,51 +0,0 @@
const std = @import("std");
const Page = @import("page.zig").Page;
const js = @import("../runtime/js.zig");
const generate = @import("../runtime/generate.zig");
const WebApis = struct {
// Wrapped like this for debug ergonomics.
// When we create our Env, a few lines down, we define it as:
// pub const Env = js.Env(*Page, WebApis);
//
// If there's a compile time error witht he Env, it's type will be readable,
// i.e.: runtime.js.Env(*browser.env.Page, browser.env.WebApis)
//
// But if we didn't wrap it in the struct, like we once didn't, and defined
// env as:
// pub const Env = js.Env(*Page, Interfaces);
//
// Because Interfaces is an anynoumous type, it doesn't have a friendly name
// and errors would be something like:
// runtime.js.Env(*browser.Page, .{...A HUNDRED TYPES...})
pub const Interfaces = generate.Tuple(.{
@import("crypto/crypto.zig").Crypto,
@import("console/console.zig").Console,
@import("css/css.zig").Interfaces,
@import("cssom/cssom.zig").Interfaces,
@import("dom/dom.zig").Interfaces,
@import("dom/shadow_root.zig").ShadowRoot,
@import("encoding/encoding.zig").Interfaces,
@import("events/event.zig").Interfaces,
@import("html/html.zig").Interfaces,
@import("iterator/iterator.zig").Interfaces,
@import("storage/storage.zig").Interfaces,
@import("url/url.zig").Interfaces,
@import("xhr/xhr.zig").Interfaces,
@import("xhr/form_data.zig").Interfaces,
@import("xhr/File.zig"),
@import("xmlserializer/xmlserializer.zig").Interfaces,
@import("fetch/fetch.zig").Interfaces,
@import("streams/streams.zig").Interfaces,
});
};
pub const JsThis = Env.JsThis;
pub const JsObject = Env.JsObject;
pub const Function = Env.Function;
pub const Promise = Env.Promise;
pub const PromiseResolver = Env.PromiseResolver;
pub const Env = js.Env(*Page, WebApis);
pub const Global = @import("html/window.zig").Window;

View File

@@ -16,9 +16,10 @@
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
const js = @import("../js/js.zig");
const parser = @import("../netsurf.zig");
const Event = @import("event.zig").Event;
const JsObject = @import("../env.zig").JsObject;
const netsurf = @import("../netsurf.zig");
// https://dom.spec.whatwg.org/#interface-customevent
@@ -27,13 +28,13 @@ pub const CustomEvent = struct {
pub const union_make_copy = true;
proto: parser.Event,
detail: ?JsObject,
detail: ?js.JsObject,
const CustomEventInit = struct {
bubbles: bool = false,
cancelable: bool = false,
composed: bool = false,
detail: ?JsObject = null,
detail: ?js.JsObject = null,
};
pub fn constructor(event_type: []const u8, opts_: ?CustomEventInit) !CustomEvent {
@@ -53,7 +54,7 @@ pub const CustomEvent = struct {
};
}
pub fn get_detail(self: *CustomEvent) ?JsObject {
pub fn get_detail(self: *CustomEvent) ?js.JsObject {
return self.detail;
}
@@ -64,7 +65,7 @@ pub const CustomEvent = struct {
event_type: []const u8,
can_bubble: bool,
cancelable: bool,
maybe_detail: ?JsObject,
maybe_detail: ?js.JsObject,
) !void {
// This function can only be called after the constructor has called.
// So we assume proto is initialized already by constructor.

View File

@@ -21,7 +21,7 @@ const Allocator = std.mem.Allocator;
const log = @import("../../log.zig");
const parser = @import("../netsurf.zig");
const generate = @import("../../runtime/generate.zig");
const generate = @import("../js/generate.zig");
const Page = @import("../page.zig").Page;
const Node = @import("../dom/node.zig").Node;
@@ -219,18 +219,17 @@ pub const Event = struct {
pub const EventHandler = struct {
once: bool,
capture: bool,
callback: Function,
callback: js.Function,
node: parser.EventNode,
listener: *parser.EventListener,
const Env = @import("../env.zig").Env;
const Function = Env.Function;
const js = @import("../js/js.zig");
pub const Listener = union(enum) {
function: Function,
object: Env.JsObject,
function: js.Function,
object: js.JsObject,
pub fn callback(self: Listener, target: *parser.EventTarget) !?Function {
pub fn callback(self: Listener, target: *parser.EventTarget) !?js.Function {
return switch (self) {
.function => |func| try func.withThis(target),
.object => |obj| blk: {
@@ -331,7 +330,7 @@ pub const EventHandler = struct {
fn handle(node: *parser.EventNode, event: *parser.Event) void {
const ievent = Event.toInterface(event);
const self: *EventHandler = @fieldParentPtr("node", node);
var result: Function.Result = undefined;
var result: js.Function.Result = undefined;
self.callback.tryCall(void, .{ievent}, &result) catch {
log.debug(.user_script, "callback error", .{
.err = result.exception,

View File

@@ -17,6 +17,7 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>.
const std = @import("std");
const js = @import("../js/js.zig");
const log = @import("../../log.zig");
const URL = @import("../../url.zig").URL;
const Page = @import("../page.zig").Page;
@@ -24,8 +25,6 @@ const Page = @import("../page.zig").Page;
const iterator = @import("../iterator/iterator.zig");
const v8 = @import("v8");
const Env = @import("../env.zig").Env;
// https://developer.mozilla.org/en-US/docs/Web/API/Headers
const Headers = @This();
@@ -69,7 +68,7 @@ pub const HeadersInit = union(enum) {
// Headers
headers: *Headers,
// Mappings
object: Env.JsObject,
object: js.JsObject,
};
pub fn constructor(_init: ?HeadersInit, page: *Page) !Headers {
@@ -159,7 +158,7 @@ pub fn _entries(self: *const Headers) HeadersEntryIterable {
};
}
pub fn _forEach(self: *Headers, callback_fn: Env.Function, this_arg: ?Env.JsObject) !void {
pub fn _forEach(self: *Headers, callback_fn: js.Function, this_arg: ?js.JsObject) !void {
var iter = self.headers.iterator();
const cb = if (this_arg) |this| try callback_fn.withThis(this) else callback_fn;

View File

@@ -17,6 +17,7 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>.
const std = @import("std");
const js = @import("../js/js.zig");
const log = @import("../../log.zig");
const URL = @import("../../url.zig").URL;
@@ -27,7 +28,6 @@ const Http = @import("../../http/Http.zig");
const ReadableStream = @import("../streams/ReadableStream.zig");
const v8 = @import("v8");
const Env = @import("../env.zig").Env;
const Headers = @import("Headers.zig");
const HeadersInit = @import("Headers.zig").HeadersInit;
@@ -241,7 +241,7 @@ pub fn _clone(self: *Request) !Request {
};
}
pub fn _bytes(self: *Response, page: *Page) !Env.Promise {
pub fn _bytes(self: *Response, page: *Page) !js.Promise {
if (self.body_used) {
return error.TypeError;
}
@@ -253,7 +253,7 @@ pub fn _bytes(self: *Response, page: *Page) !Env.Promise {
return resolver.promise();
}
pub fn _json(self: *Response, page: *Page) !Env.Promise {
pub fn _json(self: *Response, page: *Page) !js.Promise {
if (self.body_used) {
return error.TypeError;
}
@@ -280,7 +280,7 @@ pub fn _json(self: *Response, page: *Page) !Env.Promise {
return resolver.promise();
}
pub fn _text(self: *Response, page: *Page) !Env.Promise {
pub fn _text(self: *Response, page: *Page) !js.Promise {
if (self.body_used) {
return error.TypeError;
}

View File

@@ -17,6 +17,7 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>.
const std = @import("std");
const js = @import("../js/js.zig");
const log = @import("../../log.zig");
const v8 = @import("v8");
@@ -29,7 +30,6 @@ const ReadableStream = @import("../streams/ReadableStream.zig");
const Headers = @import("Headers.zig");
const HeadersInit = @import("Headers.zig").HeadersInit;
const Env = @import("../env.zig").Env;
const Mime = @import("../mime.zig").Mime;
const Page = @import("../page.zig").Page;
@@ -165,12 +165,12 @@ pub fn _clone(self: *const Response) !Response {
};
}
pub fn _bytes(self: *Response, page: *Page) !Env.Promise {
pub fn _bytes(self: *Response, page: *Page) !js.Promise {
if (self.body_used) {
return error.TypeError;
}
const resolver = Env.PromiseResolver{
const resolver = js.PromiseResolver{
.js_context = page.main_context,
.resolver = v8.PromiseResolver.init(page.main_context.v8_context),
};
@@ -180,7 +180,7 @@ pub fn _bytes(self: *Response, page: *Page) !Env.Promise {
return resolver.promise();
}
pub fn _json(self: *Response, page: *Page) !Env.Promise {
pub fn _json(self: *Response, page: *Page) !js.Promise {
if (self.body_used) {
return error.TypeError;
}
@@ -207,7 +207,7 @@ pub fn _json(self: *Response, page: *Page) !Env.Promise {
return resolver.promise();
}
pub fn _text(self: *Response, page: *Page) !Env.Promise {
pub fn _text(self: *Response, page: *Page) !js.Promise {
if (self.body_used) {
return error.TypeError;
}

View File

@@ -19,7 +19,7 @@
const std = @import("std");
const log = @import("../../log.zig");
const Env = @import("../env.zig").Env;
const js = @import("../js/js.zig");
const Page = @import("../page.zig").Page;
const Http = @import("../../http/Http.zig");
@@ -45,7 +45,7 @@ pub const Interfaces = .{
pub const FetchContext = struct {
page: *Page,
arena: std.mem.Allocator,
promise_resolver: Env.PersistentPromiseResolver,
promise_resolver: js.PersistentPromiseResolver,
method: Http.Method,
url: []const u8,
@@ -111,7 +111,7 @@ pub const FetchContext = struct {
};
// https://developer.mozilla.org/en-US/docs/Web/API/Window/fetch
pub fn fetch(input: RequestInput, options: ?RequestInit, page: *Page) !Env.Promise {
pub fn fetch(input: RequestInput, options: ?RequestInit, page: *Page) !js.Promise {
const arena = page.arena;
const req = try Request.constructor(input, options, page);

View File

@@ -17,9 +17,9 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>.
const std = @import("std");
const js = @import("../js/js.zig");
const log = @import("../../log.zig");
const parser = @import("../netsurf.zig");
const Env = @import("../env.zig").Env;
const Page = @import("../page.zig").Page;
const EventTarget = @import("../dom/event_target.zig").EventTarget;
@@ -113,7 +113,7 @@ pub const AbortSignal = struct {
}
const ThrowIfAborted = union(enum) {
exception: Env.Exception,
exception: js.Exception,
undefined: void,
};
pub fn _throwIfAborted(self: *const AbortSignal, page: *Page) ThrowIfAborted {

View File

@@ -17,7 +17,8 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>.
const std = @import("std");
const parser = @import("../netsurf.zig");
const Env = @import("../env.zig").Env;
const js = @import("../js/js.zig");
const Page = @import("../page.zig").Page;
const Allocator = std.mem.Allocator;
@@ -26,7 +27,7 @@ const DataSet = @This();
element: *parser.Element,
pub fn named_get(self: *const DataSet, name: []const u8, _: *bool, page: *Page) !Env.UndefinedOr([]const u8) {
pub fn named_get(self: *const DataSet, name: []const u8, _: *bool, page: *Page) !js.UndefinedOr([]const u8) {
const normalized_name = try normalize(page.call_arena, name);
if (try parser.elementGetAttribute(self.element, normalized_name)) |value| {
return .{ .value = value };

View File

@@ -19,7 +19,7 @@
const std = @import("std");
const log = @import("../../log.zig");
const Env = @import("../env.zig").Env;
const js = @import("../js/js.zig");
const Page = @import("../page.zig").Page;
// https://html.spec.whatwg.org/multipage/nav-history-apis.html#the-history-interface
@@ -67,11 +67,11 @@ pub fn set_scrollRestoration(self: *History, mode: []const u8) void {
self.scroll_restoration = ScrollRestorationMode.fromString(mode) orelse self.scroll_restoration;
}
pub fn get_state(self: *History, page: *Page) !?Env.Value {
pub fn get_state(self: *History, page: *Page) !?js.Value {
if (self.current) |curr| {
const entry = self.stack.items[curr];
if (entry.state) |state| {
const value = try Env.Value.fromJson(page.main_context, state);
const value = try js.Value.fromJson(page.main_context, state);
return value;
} else {
return null;
@@ -113,7 +113,7 @@ fn _dispatchPopStateEvent(state: ?[]const u8, page: *Page) !void {
);
}
pub fn _pushState(self: *History, state: Env.JsObject, _: ?[]const u8, _url: ?[]const u8, page: *Page) !void {
pub fn _pushState(self: *History, state: js.JsObject, _: ?[]const u8, _url: ?[]const u8, page: *Page) !void {
const arena = page.session.arena;
const json = try state.toJson(arena);
@@ -123,7 +123,7 @@ pub fn _pushState(self: *History, state: Env.JsObject, _: ?[]const u8, _url: ?[]
self.current = self.stack.items.len - 1;
}
pub fn _replaceState(self: *History, state: Env.JsObject, _: ?[]const u8, _url: ?[]const u8, page: *Page) !void {
pub fn _replaceState(self: *History, state: js.JsObject, _: ?[]const u8, _url: ?[]const u8, page: *Page) !void {
const arena = page.session.arena;
if (self.current) |curr| {
@@ -199,9 +199,9 @@ pub const PopStateEvent = struct {
// `hasUAVisualTransition` is not implemented. It isn't baseline so this is okay.
pub fn get_state(self: *const PopStateEvent, page: *Page) !?Env.Value {
pub fn get_state(self: *const PopStateEvent, page: *Page) !?js.Value {
if (self.state) |state| {
const value = try Env.Value.fromJson(page.main_context, state);
const value = try js.Value.fromJson(page.main_context, state);
return value;
} else {
return null;

View File

@@ -18,9 +18,9 @@
const std = @import("std");
const log = @import("../../log.zig");
const js = @import("../js/js.zig");
const parser = @import("../netsurf.zig");
const generate = @import("../../runtime/generate.zig");
const Env = @import("../env.zig").Env;
const generate = @import("../js/generate.zig");
const Page = @import("../page.zig").Page;
const urlStitch = @import("../../url.zig").URL.stitch;
@@ -1000,22 +1000,22 @@ pub const HTMLScriptElement = struct {
);
}
pub fn get_onload(self: *parser.Script, page: *Page) !?Env.Function {
pub fn get_onload(self: *parser.Script, page: *Page) !?js.Function {
const state = page.getNodeState(@ptrCast(@alignCast(self))) orelse return null;
return state.onload;
}
pub fn set_onload(self: *parser.Script, function: ?Env.Function, page: *Page) !void {
pub fn set_onload(self: *parser.Script, function: ?js.Function, page: *Page) !void {
const state = try page.getOrCreateNodeState(@ptrCast(@alignCast(self)));
state.onload = function;
}
pub fn get_onerror(self: *parser.Script, page: *Page) !?Env.Function {
pub fn get_onerror(self: *parser.Script, page: *Page) !?js.Function {
const state = page.getNodeState(@ptrCast(@alignCast(self))) orelse return null;
return state.onerror;
}
pub fn set_onerror(self: *parser.Script, function: ?Env.Function, page: *Page) !void {
pub fn set_onerror(self: *parser.Script, function: ?js.Function, page: *Page) !void {
const state = try page.getOrCreateNodeState(@ptrCast(@alignCast(self)));
state.onerror = function;
}

View File

@@ -15,7 +15,7 @@
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
const Env = @import("../env.zig").Env;
const js = @import("../js/js.zig");
const parser = @import("../netsurf.zig");
// https://developer.mozilla.org/en-US/docs/Web/API/ErrorEvent
@@ -28,14 +28,14 @@ pub const ErrorEvent = struct {
filename: []const u8,
lineno: i32,
colno: i32,
@"error": ?Env.JsObject,
@"error": ?js.JsObject,
const ErrorEventInit = struct {
message: []const u8 = "",
filename: []const u8 = "",
lineno: i32 = 0,
colno: i32 = 0,
@"error": ?Env.JsObject = null,
@"error": ?js.JsObject = null,
};
pub fn constructor(event_type: []const u8, opts: ?ErrorEventInit) !ErrorEvent {
@@ -72,7 +72,7 @@ pub const ErrorEvent = struct {
return self.colno;
}
pub fn get_error(self: *const ErrorEvent) Env.UndefinedOr(Env.JsObject) {
pub fn get_error(self: *const ErrorEvent) js.UndefinedOr(js.JsObject) {
if (self.@"error") |e| {
return .{ .value = e };
}

View File

@@ -16,8 +16,8 @@
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
const js = @import("../js/js.zig");
const parser = @import("../netsurf.zig");
const Function = @import("../env.zig").Function;
const EventTarget = @import("../dom/event_target.zig").EventTarget;
// https://drafts.csswg.org/cssom-view/#the-mediaquerylist-interface
@@ -39,7 +39,7 @@ pub const MediaQueryList = struct {
return self.media;
}
pub fn _addListener(_: *const MediaQueryList, _: Function) void {}
pub fn _addListener(_: *const MediaQueryList, _: js.Function) void {}
pub fn _removeListener(_: *const MediaQueryList, _: Function) void {}
pub fn _removeListener(_: *const MediaQueryList, _: js.Function) void {}
};

View File

@@ -18,9 +18,9 @@
const std = @import("std");
const js = @import("../js/js.zig");
const log = @import("../../log.zig");
const parser = @import("../netsurf.zig");
const Env = @import("../env.zig").Env;
const Page = @import("../page.zig").Page;
const Navigator = @import("navigator.zig").Navigator;
@@ -37,8 +37,6 @@ const domcss = @import("../dom/css.zig");
const Css = @import("../css/css.zig").Css;
const EventHandler = @import("../events/event.zig").EventHandler;
const Function = Env.Function;
const v8 = @import("v8");
const Request = @import("../fetch/Request.zig");
const fetchFn = @import("../fetch/fetch.zig").fetch;
@@ -70,7 +68,7 @@ pub const Window = struct {
css: Css = .{},
scroll_x: u32 = 0,
scroll_y: u32 = 0,
onload_callback: ?Function = null,
onload_callback: ?js.Function = null,
pub fn create(target: ?[]const u8, navigator: ?Navigator) !Window {
var fbs = std.io.fixedBufferStream("");
@@ -101,12 +99,12 @@ pub const Window = struct {
self.storage_shelf = shelf;
}
pub fn _fetch(_: *Window, input: Request.RequestInput, options: ?Request.RequestInit, page: *Page) !Env.Promise {
pub fn _fetch(_: *Window, input: Request.RequestInput, options: ?Request.RequestInit, page: *Page) !js.Promise {
return fetchFn(input, options, page);
}
/// Returns `onload_callback`.
pub fn get_onload(self: *const Window) ?Function {
pub fn get_onload(self: *const Window) ?js.Function {
return self.onload_callback;
}
@@ -258,7 +256,7 @@ pub const Window = struct {
return &self.css;
}
pub fn _requestAnimationFrame(self: *Window, cbk: Function, page: *Page) !u32 {
pub fn _requestAnimationFrame(self: *Window, cbk: js.Function, page: *Page) !u32 {
return self.createTimeout(cbk, 5, page, .{
.animation_frame = true,
.name = "animationFrame",
@@ -270,11 +268,11 @@ pub const Window = struct {
_ = self.timers.remove(id);
}
pub fn _setTimeout(self: *Window, cbk: Function, delay: ?u32, params: []Env.JsObject, page: *Page) !u32 {
pub fn _setTimeout(self: *Window, cbk: js.Function, delay: ?u32, params: []js.JsObject, page: *Page) !u32 {
return self.createTimeout(cbk, delay, page, .{ .args = params, .name = "setTimeout" });
}
pub fn _setInterval(self: *Window, cbk: Function, delay: ?u32, params: []Env.JsObject, page: *Page) !u32 {
pub fn _setInterval(self: *Window, cbk: js.Function, delay: ?u32, params: []js.JsObject, page: *Page) !u32 {
return self.createTimeout(cbk, delay, page, .{ .repeat = true, .args = params, .name = "setInterval" });
}
@@ -286,11 +284,11 @@ pub const Window = struct {
_ = self.timers.remove(id);
}
pub fn _queueMicrotask(self: *Window, cbk: Function, page: *Page) !u32 {
pub fn _queueMicrotask(self: *Window, cbk: js.Function, page: *Page) !u32 {
return self.createTimeout(cbk, 0, page, .{ .name = "queueMicrotask" });
}
pub fn _setImmediate(self: *Window, cbk: Function, page: *Page) !u32 {
pub fn _setImmediate(self: *Window, cbk: js.Function, page: *Page) !u32 {
return self.createTimeout(cbk, 0, page, .{ .name = "setImmediate" });
}
@@ -298,7 +296,7 @@ pub const Window = struct {
_ = self.timers.remove(id);
}
pub fn _matchMedia(_: *const Window, media: Env.String) !MediaQueryList {
pub fn _matchMedia(_: *const Window, media: js.String) !MediaQueryList {
return .{
.matches = false, // TODO?
.media = media.string,
@@ -322,12 +320,12 @@ pub const Window = struct {
const CreateTimeoutOpts = struct {
name: []const u8,
args: []Env.JsObject = &.{},
args: []js.JsObject = &.{},
repeat: bool = false,
animation_frame: bool = false,
low_priority: bool = false,
};
fn createTimeout(self: *Window, cbk: Function, delay_: ?u32, page: *Page, opts: CreateTimeoutOpts) !u32 {
fn createTimeout(self: *Window, cbk: js.Function, delay_: ?u32, page: *Page, opts: CreateTimeoutOpts) !u32 {
const delay = delay_ orelse 0;
if (self.timers.count() > 512) {
return error.TooManyTimeout;
@@ -347,9 +345,9 @@ pub const Window = struct {
errdefer _ = self.timers.remove(timer_id);
const args = opts.args;
var persisted_args: []Env.JsObject = &.{};
var persisted_args: []js.JsObject = &.{};
if (args.len > 0) {
persisted_args = try page.arena.alloc(Env.JsObject, args.len);
persisted_args = try page.arena.alloc(js.JsObject, args.len);
for (args, persisted_args) |a, *ca| {
ca.* = try a.persist();
}
@@ -476,13 +474,13 @@ const TimerCallback = struct {
repeat: ?u32,
// The JavaScript callback to execute
cbk: Function,
cbk: js.Function,
animation_frame: bool = false,
window: *Window,
args: []Env.JsObject = &.{},
args: []js.JsObject = &.{},
fn run(ctx: *anyopaque) ?u32 {
const self: *TimerCallback = @ptrCast(@alignCast(ctx));
@@ -496,7 +494,7 @@ const TimerCallback = struct {
return null;
}
var result: Function.Result = undefined;
var result: js.Function.Result = undefined;
var call: anyerror!void = undefined;
if (self.animation_frame) {

View File

@@ -190,7 +190,7 @@ test "generate: Union" {
const value = Union(.{ Astruct, Bstruct, .{Cstruct} });
const ti = @typeInfo(value).@"union";
try std.testing.expectEqual(3, ti.fields.len);
try std.testing.expectEqualStrings("*runtime.generate.test.generate: Union.Astruct.Other", @typeName(ti.fields[0].type));
try std.testing.expectEqualStrings("*browser.js.generate.test.generate: Union.Astruct.Other", @typeName(ti.fields[0].type));
try std.testing.expectEqualStrings(ti.fields[0].name, "Astruct");
try std.testing.expectEqual(*Bstruct, ti.fields[1].type);
try std.testing.expectEqualStrings(ti.fields[1].name, "Bstruct");

4331
src/browser/js/js.zig Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -23,7 +23,6 @@ const Allocator = std.mem.Allocator;
const Dump = @import("dump.zig");
const State = @import("State.zig");
const Env = @import("env.zig").Env;
const Mime = @import("mime.zig").Mime;
const Session = @import("session.zig").Session;
const Renderer = @import("renderer.zig").Renderer;
@@ -35,6 +34,7 @@ const ScriptManager = @import("ScriptManager.zig");
const SlotChangeMonitor = @import("SlotChangeMonitor.zig");
const HTMLDocument = @import("html/document.zig").HTMLDocument;
const js = @import("js/js.zig");
const URL = @import("../url.zig").URL;
const log = @import("../log.zig");
@@ -74,7 +74,7 @@ pub const Page = struct {
// Our JavaScript context for this specific page. This is what we use to
// execute any JavaScript
main_context: *Env.JsContext,
main_context: *js.JsContext,
// indicates intention to navigate to another page on the next loop execution.
delayed_navigation: bool = false,
@@ -143,7 +143,7 @@ pub const Page = struct {
.main_context = undefined,
};
self.main_context = try session.executor.createJsContext(&self.window, self, &self.script_manager, true, Env.GlobalMissingCallback.init(&self.polyfill_loader));
self.main_context = try session.executor.createJsContext(&self.window, self, &self.script_manager, true, js.GlobalMissingCallback.init(&self.polyfill_loader));
try polyfill.preload(self.arena, self.main_context);
try self.scheduler.add(self, runMicrotasks, 5, .{ .name = "page.microtasks" });
@@ -276,7 +276,7 @@ pub const Page = struct {
var timer = try std.time.Timer.start();
var ms_remaining = wait_ms;
var try_catch: Env.TryCatch = undefined;
var try_catch: js.TryCatch = undefined;
try_catch.init(self.main_context);
defer try_catch.deinit();

View File

@@ -19,9 +19,9 @@
const std = @import("std");
const builtin = @import("builtin");
const js = @import("../js/js.zig");
const log = @import("../../log.zig");
const Allocator = std.mem.Allocator;
const Env = @import("../env.zig").Env;
pub const Loader = struct {
state: enum { empty, loading } = .empty,
@@ -30,8 +30,8 @@ pub const Loader = struct {
webcomponents: bool = false,
} = .{},
fn load(self: *Loader, comptime name: []const u8, source: []const u8, js_context: *Env.JsContext) void {
var try_catch: Env.TryCatch = undefined;
fn load(self: *Loader, comptime name: []const u8, source: []const u8, js_context: *js.JsContext) void {
var try_catch: js.TryCatch = undefined;
try_catch.init(js_context);
defer try_catch.deinit();
@@ -49,7 +49,7 @@ pub const Loader = struct {
@field(self.done, name) = true;
}
pub fn missing(self: *Loader, name: []const u8, js_context: *Env.JsContext) bool {
pub fn missing(self: *Loader, name: []const u8, js_context: *js.JsContext) bool {
// Avoid recursive calls during polyfill loading.
if (self.state == .loading) {
return false;
@@ -82,8 +82,8 @@ pub const Loader = struct {
}
};
pub fn preload(allocator: Allocator, js_context: *Env.JsContext) !void {
var try_catch: Env.TryCatch = undefined;
pub fn preload(allocator: Allocator, js_context: *js.JsContext) !void {
var try_catch: js.TryCatch = undefined;
try_catch.init(js_context);
defer try_catch.deinit();

View File

@@ -20,7 +20,7 @@ const std = @import("std");
const Allocator = std.mem.Allocator;
const Env = @import("env.zig").Env;
const js = @import("js/js.zig");
const Page = @import("page.zig").Page;
const Browser = @import("browser.zig").Browser;
const NavigateOpts = @import("page.zig").NavigateOpts;
@@ -50,7 +50,7 @@ pub const Session = struct {
// page and start another.
transfer_arena: Allocator,
executor: Env.ExecutionWorld,
executor: js.ExecutionWorld,
storage_shed: storage.Shed,
cookie_jar: storage.CookieJar,

View File

@@ -17,10 +17,10 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>.
const std = @import("std");
const js = @import("../js/js.zig");
const log = @import("../../log.zig");
const Allocator = std.mem.Allocator;
const Env = @import("../env.zig").Env;
const Page = @import("../page.zig").Page;
const ReadableStream = @This();
@@ -31,26 +31,26 @@ const State = union(enum) {
readable,
closed: ?[]const u8,
cancelled: ?[]const u8,
errored: Env.JsObject,
errored: js.JsObject,
};
// This promise resolves when a stream is canceled.
cancel_resolver: Env.PersistentPromiseResolver,
closed_resolver: Env.PersistentPromiseResolver,
reader_resolver: ?Env.PersistentPromiseResolver = null,
cancel_resolver: js.PersistentPromiseResolver,
closed_resolver: js.PersistentPromiseResolver,
reader_resolver: ?js.PersistentPromiseResolver = null,
locked: bool = false,
state: State = .readable,
cancel_fn: ?Env.Function = null,
pull_fn: ?Env.Function = null,
cancel_fn: ?js.Function = null,
pull_fn: ?js.Function = null,
strategy: QueueingStrategy,
queue: std.ArrayListUnmanaged(Chunk) = .empty,
pub const Chunk = union(enum) {
// the order matters, sorry.
uint8array: Env.TypedArray(u8),
uint8array: js.TypedArray(u8),
string: []const u8,
pub fn dupe(self: Chunk, allocator: Allocator) !Chunk {
@@ -91,14 +91,14 @@ pub const ReadableStreamReadResult = struct {
};
const UnderlyingSource = struct {
start: ?Env.Function = null,
pull: ?Env.Function = null,
cancel: ?Env.Function = null,
start: ?js.Function = null,
pull: ?js.Function = null,
cancel: ?js.Function = null,
type: ?[]const u8 = null,
};
const QueueingStrategy = struct {
size: ?Env.Function = null,
size: ?js.Function = null,
high_water_mark: u32 = 1,
};
@@ -146,7 +146,7 @@ pub fn get_locked(self: *const ReadableStream) bool {
return self.locked;
}
pub fn _cancel(self: *ReadableStream, reason: ?[]const u8, page: *Page) !Env.Promise {
pub fn _cancel(self: *ReadableStream, reason: ?[]const u8, page: *Page) !js.Promise {
if (self.locked) {
return error.TypeError;
}

View File

@@ -17,10 +17,10 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>.
const std = @import("std");
const js = @import("../js/js.zig");
const log = @import("../../log.zig");
const Page = @import("../page.zig").Page;
const Env = @import("../env.zig").Env;
const ReadableStream = @import("./ReadableStream.zig");
const ReadableStreamReadResult = @import("./ReadableStream.zig").ReadableStreamReadResult;
@@ -69,7 +69,7 @@ pub fn _enqueue(self: *ReadableStreamDefaultController, chunk: ReadableStream.Ch
try self.stream.pullIf();
}
pub fn _error(self: *ReadableStreamDefaultController, err: Env.JsObject) !void {
pub fn _error(self: *ReadableStreamDefaultController, err: js.JsObject) !void {
self.stream.state = .{ .errored = err };
if (self.stream.reader_resolver) |*rr| {

View File

@@ -18,8 +18,8 @@
const std = @import("std");
const js = @import("../js/js.zig");
const log = @import("../../log.zig");
const Env = @import("../env.zig").Env;
const Page = @import("../page.zig").Page;
const ReadableStream = @import("./ReadableStream.zig");
const ReadableStreamReadResult = @import("./ReadableStream.zig").ReadableStreamReadResult;
@@ -32,15 +32,15 @@ pub fn constructor(stream: *ReadableStream) ReadableStreamDefaultReader {
return .{ .stream = stream };
}
pub fn get_closed(self: *const ReadableStreamDefaultReader) Env.Promise {
pub fn get_closed(self: *const ReadableStreamDefaultReader) js.Promise {
return self.stream.closed_resolver.promise();
}
pub fn _cancel(self: *ReadableStreamDefaultReader, reason: ?[]const u8, page: *Page) !Env.Promise {
pub fn _cancel(self: *ReadableStreamDefaultReader, reason: ?[]const u8, page: *Page) !js.Promise {
return try self.stream._cancel(reason, page);
}
pub fn _read(self: *const ReadableStreamDefaultReader, page: *Page) !Env.Promise {
pub fn _read(self: *const ReadableStreamDefaultReader, page: *Page) !js.Promise {
const stream = self.stream;
switch (stream.state) {

View File

@@ -19,8 +19,8 @@
const std = @import("std");
const Allocator = std.mem.Allocator;
const js = @import("../js/js.zig");
const parser = @import("../netsurf.zig");
const Env = @import("../env.zig").Env;
const Page = @import("../page.zig").Page;
const FormData = @import("../xhr/form_data.zig").FormData;
@@ -261,7 +261,7 @@ pub const URLSearchParams = struct {
const URLSearchParamsOpts = union(enum) {
qs: []const u8,
form_data: *const FormData,
js_obj: Env.JsObject,
js_obj: js.JsObject,
};
pub fn constructor(opts_: ?URLSearchParamsOpts, page: *Page) !URLSearchParams {
const opts = opts_ orelse return .{ .entries = .{} };

View File

@@ -17,9 +17,7 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>.
const std = @import("std");
const Env = @import("../env.zig").Env;
const Function = Env.Function;
const js = @import("../js/js.zig");
const EventTarget = @import("../dom/event_target.zig").EventTarget;
const EventHandler = @import("../events/event.zig").EventHandler;
@@ -33,20 +31,20 @@ pub const XMLHttpRequestEventTarget = struct {
// Extend libdom event target for pure zig struct.
base: parser.EventTargetTBase = parser.EventTargetTBase{ .internal_target_type = .xhr },
onloadstart_cbk: ?Function = null,
onprogress_cbk: ?Function = null,
onabort_cbk: ?Function = null,
onload_cbk: ?Function = null,
ontimeout_cbk: ?Function = null,
onloadend_cbk: ?Function = null,
onreadystatechange_cbk: ?Function = null,
onloadstart_cbk: ?js.Function = null,
onprogress_cbk: ?js.Function = null,
onabort_cbk: ?js.Function = null,
onload_cbk: ?js.Function = null,
ontimeout_cbk: ?js.Function = null,
onloadend_cbk: ?js.Function = null,
onreadystatechange_cbk: ?js.Function = null,
fn register(
self: *XMLHttpRequestEventTarget,
alloc: std.mem.Allocator,
typ: []const u8,
listener: EventHandler.Listener,
) !?Function {
) !?js.Function {
const target = @as(*parser.EventTarget, @ptrCast(self));
// The only time this can return null if the listener is already
@@ -69,25 +67,25 @@ pub const XMLHttpRequestEventTarget = struct {
try parser.eventTargetRemoveEventListener(et, typ, lst.?, false);
}
pub fn get_onloadstart(self: *XMLHttpRequestEventTarget) ?Function {
pub fn get_onloadstart(self: *XMLHttpRequestEventTarget) ?js.Function {
return self.onloadstart_cbk;
}
pub fn get_onprogress(self: *XMLHttpRequestEventTarget) ?Function {
pub fn get_onprogress(self: *XMLHttpRequestEventTarget) ?js.Function {
return self.onprogress_cbk;
}
pub fn get_onabort(self: *XMLHttpRequestEventTarget) ?Function {
pub fn get_onabort(self: *XMLHttpRequestEventTarget) ?js.Function {
return self.onabort_cbk;
}
pub fn get_onload(self: *XMLHttpRequestEventTarget) ?Function {
pub fn get_onload(self: *XMLHttpRequestEventTarget) ?js.Function {
return self.onload_cbk;
}
pub fn get_ontimeout(self: *XMLHttpRequestEventTarget) ?Function {
pub fn get_ontimeout(self: *XMLHttpRequestEventTarget) ?js.Function {
return self.ontimeout_cbk;
}
pub fn get_onloadend(self: *XMLHttpRequestEventTarget) ?Function {
pub fn get_onloadend(self: *XMLHttpRequestEventTarget) ?js.Function {
return self.onloadend_cbk;
}
pub fn get_onreadystatechange(self: *XMLHttpRequestEventTarget) ?Function {
pub fn get_onreadystatechange(self: *XMLHttpRequestEventTarget) ?js.Function {
return self.onreadystatechange_cbk;
}

View File

@@ -21,18 +21,17 @@ const Allocator = std.mem.Allocator;
const json = std.json;
const log = @import("../log.zig");
const js = @import("../browser/js/js.zig");
const polyfill = @import("../browser/polyfill/polyfill.zig");
const App = @import("../app.zig").App;
const Env = @import("../browser/env.zig").Env;
const Browser = @import("../browser/browser.zig").Browser;
const Session = @import("../browser/session.zig").Session;
const Page = @import("../browser/page.zig").Page;
const Inspector = @import("../browser/env.zig").Env.Inspector;
const Incrementing = @import("../id.zig").Incrementing;
const Notification = @import("../notification.zig").Notification;
const InterceptState = @import("domains/fetch.zig").InterceptState;
const polyfill = @import("../browser/polyfill/polyfill.zig");
pub const URL_BASE = "chrome://newtab/";
pub const LOADER_ID = "LOADERID24DD2FD56CF1EF33C965C79C";
@@ -329,7 +328,7 @@ pub fn BrowserContext(comptime CDP_T: type) type {
node_registry: Node.Registry,
node_search_list: Node.Search.List,
inspector: Inspector,
inspector: js.Inspector,
isolated_worlds: std.ArrayListUnmanaged(IsolatedWorld),
http_proxy_changed: bool = false,
@@ -661,7 +660,7 @@ pub fn BrowserContext(comptime CDP_T: type) type {
/// An object id is unique across all contexts, different object ids can refer to the same Node in different contexts.
const IsolatedWorld = struct {
name: []const u8,
executor: Env.ExecutionWorld,
executor: js.ExecutionWorld,
grant_universal_access: bool,
// Polyfill loader for the isolated world.
@@ -695,7 +694,7 @@ const IsolatedWorld = struct {
page,
null,
false,
Env.GlobalMissingCallback.init(&self.polyfill_loader),
js.GlobalMissingCallback.init(&self.polyfill_loader),
);
}

View File

@@ -19,11 +19,12 @@
const std = @import("std");
const log = @import("log.zig");
const js = @import("browser/js/js.zig");
const Allocator = std.mem.Allocator;
const ArenaAllocator = std.heap.ArenaAllocator;
const App = @import("app.zig").App;
const Env = @import("browser/env.zig").Env;
const Browser = @import("browser/browser.zig").Browser;
const TestHTTPServer = @import("TestHTTPServer.zig");
@@ -123,7 +124,7 @@ fn run(
_ = page.wait(2000);
const js_context = page.main_context;
var try_catch: Env.TryCatch = undefined;
var try_catch: js.TryCatch = undefined;
try_catch.init(js_context);
defer try_catch.deinit();

File diff suppressed because it is too large Load Diff

View File

@@ -1,295 +0,0 @@
// Copyright (C) 2023-2024 Lightpanda (Selecy SAS)
//
// Francis Bouvier <francis@lightpanda.io>
// Pierre Tachoire <pierre@lightpanda.io>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
const std = @import("std");
const Allocator = std.mem.Allocator;
const MyList = struct {
items: []u8,
pub fn constructor(elem1: u8, elem2: u8, elem3: u8, state: State) MyList {
var items = state.arena.alloc(u8, 3) catch unreachable;
items[0] = elem1;
items[1] = elem2;
items[2] = elem3;
return .{ .items = items };
}
pub fn _first(self: *const MyList) u8 {
return self.items[0];
}
pub fn _symbol_iterator(self: *const MyList) IterableU8 {
return IterableU8.init(self.items);
}
};
const MyVariadic = struct {
member: u8,
pub fn constructor() MyVariadic {
return .{ .member = 0 };
}
pub fn _len(_: *const MyVariadic, variadic: []bool) u64 {
return @as(u64, variadic.len);
}
pub fn _first(_: *const MyVariadic, _: []const u8, variadic: []bool) bool {
return variadic[0];
}
pub fn _last(_: *const MyVariadic, variadic: []bool) bool {
return variadic[variadic.len - 1];
}
pub fn _empty(_: *const MyVariadic, _: []bool) bool {
return true;
}
pub fn _myListLen(_: *const MyVariadic, variadic: []*const MyList) u8 {
return @as(u8, @intCast(variadic.len));
}
pub fn _myListFirst(_: *const MyVariadic, variadic: []*const MyList) ?u8 {
if (variadic.len == 0) return null;
return variadic[0]._first();
}
};
const MyErrorUnion = struct {
pub fn constructor(is_err: bool) !MyErrorUnion {
if (is_err) return error.MyError;
return .{};
}
pub fn get_withoutError(_: *const MyErrorUnion) !u8 {
return 0;
}
pub fn get_withError(_: *const MyErrorUnion) !u8 {
return error.MyError;
}
pub fn set_withoutError(_: *const MyErrorUnion, _: bool) !void {}
pub fn set_withError(_: *const MyErrorUnion, _: bool) !void {
return error.MyError;
}
pub fn _funcWithoutError(_: *const MyErrorUnion) !void {}
pub fn _funcWithError(_: *const MyErrorUnion) !void {
return error.MyError;
}
};
pub const MyException = struct {
err: ErrorSet,
const errorNames = [_][]const u8{
"MyCustomError",
};
const errorMsgs = [_][]const u8{
"Some custom message.",
};
fn errorStrings(comptime i: usize) []const u8 {
return errorNames[0] ++ ": " ++ errorMsgs[i];
}
// interface definition
pub const ErrorSet = error{
MyCustomError,
};
pub fn init(_: Allocator, err: anyerror, _: []const u8) !MyException {
return .{ .err = @as(ErrorSet, @errorCast(err)) };
}
pub fn get_name(self: *const MyException) []const u8 {
return switch (self.err) {
ErrorSet.MyCustomError => errorNames[0],
};
}
pub fn get_message(self: *const MyException) []const u8 {
return switch (self.err) {
ErrorSet.MyCustomError => errorMsgs[0],
};
}
pub fn _toString(self: *const MyException) []const u8 {
return switch (self.err) {
ErrorSet.MyCustomError => errorStrings(0),
};
}
};
const MyTypeWithException = struct {
pub const Exception = MyException;
pub fn constructor() MyTypeWithException {
return .{};
}
pub fn _withoutError(_: *const MyTypeWithException) MyException.ErrorSet!void {}
pub fn _withError(_: *const MyTypeWithException) MyException.ErrorSet!void {
return MyException.ErrorSet.MyCustomError;
}
pub fn _superSetError(_: *const MyTypeWithException) !void {
return MyException.ErrorSet.MyCustomError;
}
pub fn _outOfMemory(_: *const MyTypeWithException) !void {
return error.OutOfMemory;
}
};
const MyUnionType = struct {
pub const Choices = union(enum) {
color: []const u8,
number: usize,
boolean: bool,
obj1: *MyList,
obj2: MyUnionType,
};
pub fn constructor() MyUnionType {
return .{};
}
pub fn _choices(_: *const MyUnionType, u: Choices) Choices {
return switch (u) {
.color => .{ .color = "nice" },
.number => |n| .{ .number = n + 10 },
.boolean => |b| .{ .boolean = !b },
.obj1 => |l| .{ .number = l.items.len },
.obj2 => .{ .color = "meta" },
};
}
};
const IterableU8 = Iterable(u8);
pub fn Iterable(comptime T: type) type {
return struct {
const Self = @This();
items: []T,
index: usize = 0,
pub fn init(items: []T) Self {
return .{ .items = items };
}
pub const Return = struct {
value: ?T,
done: bool,
};
pub fn _next(self: *Self) Return {
if (self.items.len > self.index) {
const val = self.items[self.index];
self.index += 1;
return .{ .value = val, .done = false };
} else {
return .{ .value = null, .done = true };
}
}
};
}
const State = struct {
arena: Allocator,
};
const testing = @import("testing.zig");
test "JS: complex types" {
var arena = std.heap.ArenaAllocator.init(testing.allocator);
defer arena.deinit();
var runner = try testing.Runner(State, void, .{
MyList,
IterableU8,
MyVariadic,
MyErrorUnion,
MyException,
MyTypeWithException,
MyUnionType,
}).init(.{ .arena = arena.allocator() }, {});
defer runner.deinit();
try runner.testCases(&.{
.{ "let myList = new MyList(1, 2, 3);", "undefined" },
.{ "myList.first();", "1" },
.{ "let iter = myList[Symbol.iterator]();", "undefined" },
.{ "iter.next().value;", "1" },
.{ "iter.next().value;", "2" },
.{ "iter.next().value;", "3" },
.{ "iter.next().done;", "true" },
.{ "let arr = Array.from(myList);", "undefined" },
.{ "arr.length;", "3" },
.{ "arr[0];", "1" },
}, .{});
try runner.testCases(&.{
.{ "let myVariadic = new MyVariadic();", "undefined" },
.{ "myVariadic.len(true, false, true)", "3" },
.{ "myVariadic.first('a_str', true, false, true, false)", "true" },
.{ "myVariadic.last(true, false)", "false" },
.{ "myVariadic.empty()", "true" },
.{ "myVariadic.myListLen(myList)", "1" },
.{ "myVariadic.myListFirst(myList)", "1" },
}, .{});
try runner.testCases(&.{
.{ "var myErrorCstr = ''; try {new MyErrorUnion(true)} catch (error) {myErrorCstr = error}; myErrorCstr", "Error: MyError" },
.{ "let myErrorUnion = new MyErrorUnion(false);", "undefined" },
.{ "myErrorUnion.withoutError", "0" },
.{ "var myErrorGetter = ''; try {myErrorUnion.withError} catch (error) {myErrorGetter = error}; myErrorGetter", "Error: MyError" },
.{ "myErrorUnion.withoutError = true", "true" },
.{ "var myErrorSetter = ''; try {myErrorUnion.withError = true} catch (error) {myErrorSetter = error}; myErrorSetter", "Error: MyError" },
.{ "myErrorUnion.funcWithoutError()", "undefined" },
.{ "var myErrorFunc = ''; try {myErrorUnion.funcWithError()} catch (error) {myErrorFunc = error}; myErrorFunc", "Error: MyError" },
}, .{});
try runner.testCases(&.{
.{ "MyException.prototype.__proto__ === Error.prototype", "true" },
.{ "let myTypeWithException = new MyTypeWithException();", "undefined" },
.{ "myTypeWithException.withoutError()", "undefined" },
.{ "var myCustomError = ''; try {myTypeWithException.withError()} catch (error) {myCustomError = error}", "MyCustomError: Some custom message." },
.{ "myCustomError instanceof MyException", "true" },
.{ "myCustomError instanceof Error", "true" },
.{ "var mySuperError = ''; try {myTypeWithException.superSetError()} catch (error) {mySuperError = error}", "MyCustomError: Some custom message." },
.{ "var oomError = ''; try {myTypeWithException.outOfMemory()} catch (error) {oomError = error}; oomError", "Error: out of memory" },
}, .{});
try runner.testCases(&.{
.{ "var mut = new MyUnionType()", "undefined" },
.{ "mut.choices(3)", "13" },
.{ "mut.choices('blue')", "nice" },
.{ "mut.choices(true)", "false" },
.{ "mut.choices(false)", "true" },
.{ "mut.choices(mut)", "meta" },
.{ "mut.choices(myList)", "3" },
}, .{});
}

View File

@@ -1,275 +0,0 @@
// Copyright (C) 2023-2024 Lightpanda (Selecy SAS)
//
// Francis Bouvier <francis@lightpanda.io>
// Pierre Tachoire <pierre@lightpanda.io>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
const std = @import("std");
const Allocator = std.mem.Allocator;
pub const Other = struct {
val: u8,
fn init(val: u8) Other {
return .{ .val = val };
}
pub fn _val(self: *const Other) u8 {
return self.val;
}
};
pub const OtherUnion = union(enum) {
Other: Other,
Bool: bool,
};
pub const MyObject = struct {
val: bool,
pub fn constructor(do_set: bool) MyObject {
return .{
.val = do_set,
};
}
pub fn named_get(_: *const MyObject, name: []const u8, has_value: *bool) ?OtherUnion {
if (std.mem.eql(u8, name, "a")) {
has_value.* = true;
return .{ .Other = .{ .val = 4 } };
}
if (std.mem.eql(u8, name, "c")) {
has_value.* = true;
return .{ .Bool = true };
}
has_value.* = false;
return null;
}
pub fn get_val(self: *const MyObject) bool {
return self.val;
}
pub fn set_val(self: *MyObject, val: bool) void {
self.val = val;
}
};
pub const MyAPI = struct {
pub fn constructor() MyAPI {
return .{};
}
pub fn _obj(_: *const MyAPI) !MyObject {
return MyObject.constructor(true);
}
};
pub const Parent = packed struct {
parent_id: i32 = 0,
pub fn get_parent(self: *const Parent) i32 {
return self.parent_id;
}
pub fn set_parent(self: *Parent, id: i32) void {
self.parent_id = id;
}
};
pub const Middle = struct {
pub const prototype = *Parent;
middle_id: i32 = 0,
_padding_1: u8 = 0,
_padding_2: u8 = 1,
_padding_3: u8 = 2,
proto: Parent,
pub fn constructor() Middle {
return .{
.middle_id = 0,
.proto = .{ .parent_id = 0 },
};
}
pub fn get_middle(self: *const Middle) i32 {
return self.middle_id;
}
pub fn set_middle(self: *Middle, id: i32) void {
self.middle_id = id;
}
};
pub const Child = struct {
pub const prototype = *Middle;
child_id: i32 = 0,
_padding_1: u8 = 0,
proto: Middle,
pub fn constructor() Child {
return .{
.child_id = 0,
.proto = .{ .middle_id = 0, .proto = .{ .parent_id = 0 } },
};
}
pub fn get_child(self: *const Child) i32 {
return self.child_id;
}
pub fn set_child(self: *Child, id: i32) void {
self.child_id = id;
}
};
pub const MiddlePtr = packed struct {
pub const prototype = *Parent;
middle_id: i32 = 0,
_padding_1: u8 = 0,
_padding_2: u8 = 1,
_padding_3: u8 = 2,
proto: *Parent,
pub fn constructor(state: State) !MiddlePtr {
const parent = try state.arena.create(Parent);
parent.* = .{ .parent_id = 0 };
return .{
.middle_id = 0,
.proto = parent,
};
}
pub fn get_middle(self: *const MiddlePtr) i32 {
return self.middle_id;
}
pub fn set_middle(self: *MiddlePtr, id: i32) void {
self.middle_id = id;
}
};
pub const ChildPtr = packed struct {
pub const prototype = *MiddlePtr;
child_id: i32 = 0,
_padding_1: u8 = 0,
_padding_2: u8 = 1,
proto: *MiddlePtr,
pub fn constructor(state: State) !ChildPtr {
const parent = try state.arena.create(Parent);
const middle = try state.arena.create(MiddlePtr);
parent.* = .{ .parent_id = 0 };
middle.* = .{ .middle_id = 0, .proto = parent };
return .{
.child_id = 0,
.proto = middle,
};
}
pub fn get_child(self: *const ChildPtr) i32 {
return self.child_id;
}
pub fn set_child(self: *ChildPtr, id: i32) void {
self.child_id = id;
}
};
const State = struct {
arena: Allocator,
};
const testing = @import("testing.zig");
test "JS: object types" {
var arena = std.heap.ArenaAllocator.init(testing.allocator);
defer arena.deinit();
var runner = try testing.Runner(State, void, .{
Other,
MyObject,
MyAPI,
Parent,
Middle,
Child,
MiddlePtr,
ChildPtr,
}).init(.{ .arena = arena.allocator() }, {});
defer runner.deinit();
// v8 has 3 default "own" properties
const own_base = "3";
try runner.testCases(&.{
.{ "Object.getOwnPropertyNames(MyObject).length;", own_base },
.{ "let myObj = new MyObject(true);", "undefined" },
// check object property
.{ "myObj.a.val()", "4" },
.{ "myObj.b", "undefined" },
.{ "Object.getOwnPropertyNames(myObj).length;", "0" },
// check if setter (pointer) still works
.{ "myObj.val", "true" },
.{ "myObj.val = false", "false" },
.{ "myObj.val", "false" },
.{ "let myObj2 = new MyObject(false);", "undefined" },
.{ "myObj2.c", "true" },
}, .{});
try runner.testCases(&.{
.{ "let myAPI = new MyAPI();", "undefined" },
.{ "let myObjIndirect = myAPI.obj();", "undefined" },
// check object property
.{ "myObjIndirect.a.val()", "4" },
}, .{});
try runner.testCases(&.{
.{ "let m1 = new Middle();", null },
.{ "m1.middle = 2", null },
.{ "m1.parent = 3", null },
.{ "m1.middle", "2" },
.{ "m1.parent", "3" },
}, .{});
try runner.testCases(&.{
.{ "let c1 = new Child();", null },
.{ "c1.child = 1", null },
.{ "c1.middle = 2", null },
.{ "c1.parent = 3", null },
.{ "c1.child", "1" },
.{ "c1.middle", "2" },
.{ "c1.parent", "3" },
}, .{});
try runner.testCases(&.{
.{ "let m2 = new MiddlePtr();", null },
.{ "m2.middle = 2", null },
.{ "m2.parent = 3", null },
.{ "m2.middle", "2" },
.{ "m2.parent", "3" },
}, .{});
try runner.testCases(&.{
.{ "let c2 = new ChildPtr();", null },
.{ "c2.child = 1", null },
.{ "c2.middle = 2", null },
.{ "c2.parent = 3", null },
.{ "c2.child", "1" },
.{ "c2.middle", "2" },
.{ "c2.parent", "3" },
}, .{});
}

View File

@@ -1,352 +0,0 @@
// Copyright (C) 2023-2024 Lightpanda (Selecy SAS)
//
// Francis Bouvier <francis@lightpanda.io>
// Pierre Tachoire <pierre@lightpanda.io>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
// TODO: use functions instead of "fake" struct once we handle function API generation
const Runner = testing.Runner(void, void, .{Primitives});
const Env = Runner.Env;
const Primitives = struct {
pub fn constructor() Primitives {
return .{};
}
// List of bytes (string)
pub fn _checkString(_: *const Primitives, v: []u8) []u8 {
return v;
}
// Integers signed
pub fn _checkI32(_: *const Primitives, v: i32) i32 {
return v;
}
pub fn _checkI64(_: *const Primitives, v: i64) i64 {
return v;
}
// Integers unsigned
pub fn _checkU32(_: *const Primitives, v: u32) u32 {
return v;
}
pub fn _checkU64(_: *const Primitives, v: u64) u64 {
return v;
}
// Floats
pub fn _checkF32(_: *const Primitives, v: f32) f32 {
return v;
}
pub fn _checkF64(_: *const Primitives, v: f64) f64 {
return v;
}
// Bool
pub fn _checkBool(_: *const Primitives, v: bool) bool {
return v;
}
// Undefined
// TODO: there is a bug with this function
// void paramater does not work => avoid for now
// pub fn _checkUndefined(_: *const Primitives, v: void) void {
// return v;
// }
// Null
pub fn _checkNullEmpty(_: *const Primitives, v: ?u32) bool {
return (v == null);
}
pub fn _checkNullNotEmpty(_: *const Primitives, v: ?u32) bool {
return (v != null);
}
// Optionals
pub fn _checkOptional(_: *const Primitives, _: ?u8, v: u8, _: ?u8, _: ?u8) u8 {
return v;
}
pub fn _checkNonOptional(_: *const Primitives, v: u8) u8 {
return v;
}
pub fn _checkOptionalReturn(_: *const Primitives) ?bool {
return true;
}
pub fn _checkOptionalReturnNull(_: *const Primitives) ?bool {
return null;
}
pub fn _checkOptionalReturnString(_: *const Primitives) ?[]const u8 {
return "ok";
}
pub fn _echoString(_: *const Primitives, a: []const u8) []const u8 {
return a;
}
pub fn _echoStringZ(_: *const Primitives, a: [:0]const u8) []const u8 {
return a;
}
pub fn _int8(_: *const Primitives, arr: []i8) void {
for (arr) |*a| {
a.* -= @intCast(arr.len);
}
}
pub fn _uint8(_: *const Primitives, arr: []u8) void {
for (arr) |*a| {
a.* += @intCast(arr.len);
}
}
pub fn _returnEmptyUint8(_: *const Primitives) Env.TypedArray(u8) {
return .{ .values = &.{} };
}
pub fn _returnUint8(_: *const Primitives) Env.TypedArray(u8) {
return .{ .values = &.{ 10, 20, 250 } };
}
pub fn _returnInt8(_: *const Primitives) Env.TypedArray(i8) {
return .{ .values = &.{ 10, -20, -120 } };
}
pub fn _returnUint16(_: *const Primitives) Env.TypedArray(u16) {
return .{ .values = &.{ 10, 200, 2050 } };
}
pub fn _returnInt16(_: *const Primitives) Env.TypedArray(i16) {
return .{ .values = &.{ 10, -420, 0 } };
}
pub fn _returnUint32(_: *const Primitives) Env.TypedArray(u32) {
return .{ .values = &.{ 10, 2444343, 43432432 } };
}
pub fn _returnInt32(_: *const Primitives) Env.TypedArray(i32) {
return .{ .values = &.{ 10, -20, -495929123 } };
}
pub fn _returnUint64(_: *const Primitives) Env.TypedArray(u64) {
return .{ .values = &.{ 10, 495812375924, 0 } };
}
pub fn _returnInt64(_: *const Primitives) Env.TypedArray(i64) {
return .{ .values = &.{ 10, -49283838122, -2 } };
}
pub fn _returnFloat32(_: *const Primitives) Env.TypedArray(f32) {
return .{ .values = &.{ 1.1, -200.035, 0.0003 } };
}
pub fn _returnFloat64(_: *const Primitives) Env.TypedArray(f64) {
return .{ .values = &.{ 8881.22284, -4928.3838122, -0.00004 } };
}
pub fn _int16(_: *const Primitives, arr: []i16) void {
for (arr) |*a| {
a.* -= @intCast(arr.len);
}
}
pub fn _uint16(_: *const Primitives, arr: []u16) void {
for (arr) |*a| {
a.* += @intCast(arr.len);
}
}
pub fn _int32(_: *const Primitives, arr: []i32) void {
for (arr) |*a| {
a.* -= @intCast(arr.len);
}
}
pub fn _uint32(_: *const Primitives, arr: []u32) void {
for (arr) |*a| {
a.* += @intCast(arr.len);
}
}
pub fn _int64(_: *const Primitives, arr: []i64) void {
for (arr) |*a| {
a.* -= @intCast(arr.len);
}
}
pub fn _uint64(_: *const Primitives, arr: []u64) void {
for (arr) |*a| {
a.* += @intCast(arr.len);
}
}
};
const testing = @import("testing.zig");
test "JS: primitive types" {
var runner = try Runner.init({}, {});
defer runner.deinit();
// constructor
try runner.testCases(&.{
.{ "let p = new Primitives();", "undefined" },
}, .{});
// JS <> Native translation of primitive types
try runner.testCases(&.{
.{ "p.checkString('ok ascii') === 'ok ascii';", "true" },
.{ "p.checkString('ok emoji 🚀') === 'ok emoji 🚀';", "true" },
.{ "p.checkString('ok chinese 鿍') === 'ok chinese 鿍';", "true" },
// String (JS liberal cases)
.{ "p.checkString(1) === '1';", "true" },
.{ "p.checkString(null) === 'null';", "true" },
.{ "p.checkString(undefined) === 'undefined';", "true" },
// Integers
// signed
.{ "const min_i32 = -2147483648", "undefined" },
.{ "p.checkI32(min_i32) === min_i32;", "true" },
.{ "p.checkI32(min_i32-1) === min_i32-1;", "false" },
.{ "try { p.checkI32(9007199254740995n) } catch(e) { e instanceof TypeError; }", "true" },
// unsigned
.{ "const max_u32 = 4294967295", "undefined" },
.{ "p.checkU32(max_u32) === max_u32;", "true" },
.{ "p.checkU32(max_u32+1) === max_u32+1;", "false" },
// int64 (with BigInt)
.{ "const big_int = 9007199254740995n", "undefined" },
.{ "p.checkI64(big_int) === big_int", "true" },
.{ "p.checkU64(big_int) === big_int;", "true" },
.{ "p.checkI64(0) === 0;", "true" },
.{ "p.checkI64(-1) === -1;", "true" },
.{ "p.checkU64(0) === 0;", "true" },
// Floats
// use round 2 decimals for float to ensure equality
.{ "const r = function(x) {return Math.round(x * 100) / 100};", "undefined" },
.{ "const double = 10.02;", "undefined" },
.{ "r(p.checkF32(double)) === double;", "true" },
.{ "r(p.checkF64(double)) === double;", "true" },
// Bool
.{ "p.checkBool(true);", "true" },
.{ "p.checkBool(false);", "false" },
.{ "p.checkBool(0);", "false" },
.{ "p.checkBool(1);", "true" },
// Bool (JS liberal cases)
.{ "p.checkBool(null);", "false" },
.{ "p.checkBool(undefined);", "false" },
// Undefined
// see TODO on Primitives.checkUndefined
// .{ "p.checkUndefined(undefined) === undefined;", "true" },
// Null
.{ "p.checkNullEmpty(null);", "true" },
.{ "p.checkNullEmpty(undefined);", "true" },
.{ "p.checkNullNotEmpty(1);", "true" },
// Optional
.{ "p.checkOptional(null, 3);", "3" },
.{ "p.checkNonOptional();", "TypeError" },
.{ "p.checkOptionalReturn() === true;", "true" },
.{ "p.checkOptionalReturnNull() === null;", "true" },
.{ "p.checkOptionalReturnString() === 'ok';", "true" },
// strings
.{ "p.echoString('over 9000!');", "over 9000!" },
.{ "p.echoStringZ('Teg');", "Teg" },
}, .{});
// typed arrays
try runner.testCases(&.{
.{ "let empty_arr = new Int8Array([]);", "undefined" },
.{ "p.int8(empty_arr)", "undefined" },
.{ "empty_arr;", "" },
.{ "let arr_i8 = new Int8Array([-10, -20, -30]);", "undefined" },
.{ "p.int8(arr_i8)", "undefined" },
.{ "arr_i8;", "-13,-23,-33" },
.{ "let arr_u8 = new Uint8Array([10, 20, 30]);", "undefined" },
.{ "p.uint8(arr_u8)", "undefined" },
.{ "arr_u8;", "13,23,33" },
.{ "let arr_i16 = new Int16Array([-1000, -2000, -3000]);", "undefined" },
.{ "p.int16(arr_i16)", "undefined" },
.{ "arr_i16;", "-1003,-2003,-3003" },
.{ "let arr_u16 = new Uint16Array([1000, 2000, 3000]);", "undefined" },
.{ "p.uint16(arr_u16)", "undefined" },
.{ "arr_u16;", "1003,2003,3003" },
.{ "let arr_i32 = new Int32Array([-1000000, -2000000, -3000000]);", "undefined" },
.{ "p.int32(arr_i32)", "undefined" },
.{ "arr_i32;", "-1000003,-2000003,-3000003" },
.{ "let arr_u32 = new Uint32Array([1000000, 2000000, 3000000]);", "undefined" },
.{ "p.uint32(arr_u32)", "undefined" },
.{ "arr_u32;", "1000003,2000003,3000003" },
.{ "let arr_i64 = new BigInt64Array([-1000000000n, -2000000000n, -3000000000n]);", "undefined" },
.{ "p.int64(arr_i64)", "undefined" },
.{ "arr_i64;", "-1000000003,-2000000003,-3000000003" },
.{ "let arr_u64 = new BigUint64Array([1000000000n, 2000000000n, 3000000000n]);", "undefined" },
.{ "p.uint64(arr_u64)", "undefined" },
.{ "arr_u64;", "1000000003,2000000003,3000000003" },
.{ "try { p.int8(arr_u8) } catch(e) { e instanceof TypeError; }", "true" },
.{ "try { p.intu8(arr_i8) } catch(e) { e instanceof TypeError; }", "true" },
.{ "try { p.intu8(arr_u32) } catch(e) { e instanceof TypeError; }", "true" },
.{ "try { p.int16(arr_u8) } catch(e) { e instanceof TypeError; }", "true" },
.{ "try { p.intu16(arr_i16) } catch(e) { e instanceof TypeError; }", "true" },
.{ "try { p.int16(arr_i64) } catch(e) { e instanceof TypeError; }", "true" },
.{ "try { p.int32(arr_u32) } catch(e) { e instanceof TypeError; }", "true" },
.{ "try { p.intu32(arr_i32) } catch(e) { e instanceof TypeError; }", "true" },
.{ "try { p.intu32(arr_u32) } catch(e) { e instanceof TypeError; }", "true" },
.{ "try { p.int64(arr_u64) } catch(e) { e instanceof TypeError; }", "true" },
.{ "try { p.intu64(arr_i64) } catch(e) { e instanceof TypeError; }", "true" },
.{ "try { p.intu64(arr_u32) } catch(e) { e instanceof TypeError; }", "true" },
.{ "p.returnEmptyUint8()", "" },
.{ "p.returnUint8()", "10,20,250" },
.{ "p.returnInt8()", "10,-20,-120" },
.{ "p.returnUint16()", "10,200,2050" },
.{ "p.returnInt16()", "10,-420,0" },
.{ "p.returnUint32()", "10,2444343,43432432" },
.{ "p.returnInt32()", "10,-20,-495929123" },
.{ "p.returnUint64()", "10,495812375924,0" },
.{ "p.returnInt64()", "10,-49283838122,-2" },
.{ "p.returnFloat32()", "1.100000023841858,-200.03500366210938,0.0003000000142492354" },
.{ "p.returnFloat64()", "8881.22284,-4928.3838122,-0.00004" },
}, .{});
try runner.testCases(&.{
.{ "'foo\\\\:bar'", "foo\\:bar" },
}, .{});
}

View File

@@ -1,110 +0,0 @@
// Copyright (C) 2023-2024 Lightpanda (Selecy SAS)
//
// Francis Bouvier <francis@lightpanda.io>
// Pierre Tachoire <pierre@lightpanda.io>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
const std = @import("std");
const js = @import("js.zig");
const base = @import("../testing.zig");
const generate = @import("generate.zig");
pub const allocator = std.testing.allocator;
// Very similar to the JSRunner in src/testing.zig, but it isn't tied to the
// browser.Env or the *Page state
pub fn Runner(comptime State: type, comptime Global: type, comptime types: anytype) type {
const AdjustedTypes = if (Global == void) generate.Tuple(.{ types, DefaultGlobal }) else types;
return struct {
env: *Env,
js_context: *Env.JsContext,
executor: Env.ExecutionWorld,
pub const Env = js.Env(State, struct {
pub const Interfaces = AdjustedTypes;
});
const Self = @This();
pub fn init(state: State, global: Global) !*Self {
const self = try allocator.create(Self);
errdefer allocator.destroy(self);
self.env = try Env.init(allocator, &base.test_app.platform, .{});
errdefer self.env.deinit();
self.executor = try self.env.newExecutionWorld();
errdefer self.executor.deinit();
self.js_context = try self.executor.createJsContext(
if (Global == void) &default_global else global,
state,
null,
true,
null,
);
return self;
}
pub fn deinit(self: *Self) void {
self.executor.deinit();
self.env.deinit();
allocator.destroy(self);
}
const RunOpts = struct {};
pub const Case = std.meta.Tuple(&.{ []const u8, ?[]const u8 });
pub fn testCases(self: *Self, cases: []const Case, _: RunOpts) !void {
for (cases, 0..) |case, i| {
var try_catch: Env.TryCatch = undefined;
try_catch.init(self.js_context);
defer try_catch.deinit();
const value = self.js_context.exec(case.@"0", null) catch |err| {
if (try try_catch.err(allocator)) |msg| {
defer allocator.free(msg);
if (isExpectedTypeError(case.@"1", msg)) {
continue;
}
std.debug.print("{s}\n\nCase: {d}\n{s}\n", .{ msg, i + 1, case.@"0" });
}
return err;
};
if (case.@"1") |expected| {
const actual = try value.toString(allocator);
defer allocator.free(actual);
if (std.mem.eql(u8, expected, actual) == false) {
std.debug.print("Expected:\n{s}\n\nGot:\n{s}\n\nCase: {d}\n{s}\n", .{ expected, actual, i + 1, case.@"0" });
return error.UnexpectedResult;
}
}
}
}
};
}
fn isExpectedTypeError(expected_: ?[]const u8, msg: []const u8) bool {
const expected = expected_ orelse return false;
if (!std.mem.eql(u8, expected, "TypeError")) {
return false;
}
return std.mem.startsWith(u8, msg, "TypeError: ");
}
var default_global = DefaultGlobal{};
const DefaultGlobal = struct {};

View File

@@ -37,7 +37,7 @@ pub fn reset() void {
}
const App = @import("app.zig").App;
const Env = @import("browser/env.zig").Env;
const js = @import("browser/js/js.zig");
const Browser = @import("browser/browser.zig").Browser;
const Session = @import("browser/session.zig").Session;
const parser = @import("browser/netsurf.zig");
@@ -396,7 +396,7 @@ pub fn htmlRunner(file: []const u8) !void {
page.arena = @import("root").tracking_allocator;
const js_context = page.main_context;
var try_catch: Env.TryCatch = undefined;
var try_catch: js.TryCatch = undefined;
try_catch.init(js_context);
defer try_catch.deinit();