mirror of
https://github.com/lightpanda-io/browser.git
synced 2026-07-31 01:36:15 -04:00
Detect Node-types that don't have an asNode()
Don't set null_as_undefined = false..it's the default.
This commit is contained in:
@@ -510,31 +510,24 @@ fn errorLocal(comptime T: type, local: *const Local, info: anytype) Local {
|
||||
return local.*;
|
||||
}
|
||||
|
||||
const frame = switch (local.ctx.global) {
|
||||
.frame => |f| f,
|
||||
.worker => return local.*,
|
||||
};
|
||||
|
||||
const Node = @import("../webapi/Node.zig");
|
||||
const Document = @import("../webapi/Document.zig");
|
||||
|
||||
const is_node_type = comptime blk: {
|
||||
if (@typeInfo(T) != .@"struct") break :blk false;
|
||||
if (T == Node) break :blk true;
|
||||
break :blk @hasDecl(T, "JsApi") and @hasDecl(T, "asNode") and @TypeOf(T.asNode) == fn (*T) *Node;
|
||||
if (@typeInfo(T) != .@"struct" or !@hasDecl(T, "JsApi")) break :blk false;
|
||||
break :blk @import("bridge.zig").inheritsOrIs(T.JsApi, Node.JsApi);
|
||||
};
|
||||
if (comptime !is_node_type) {
|
||||
return local.*;
|
||||
}
|
||||
|
||||
const node: *Node = blk: {
|
||||
const instance = TaggedOpaque.fromJS(*T, info.getThis()) catch return local.*;
|
||||
if (comptime T == Node) {
|
||||
break :blk instance;
|
||||
} else {
|
||||
break :blk instance.asNode();
|
||||
}
|
||||
};
|
||||
|
||||
const frame = switch (local.ctx.global) {
|
||||
.frame => |f| f,
|
||||
.worker => return local.*,
|
||||
};
|
||||
const instance = TaggedOpaque.fromJS(*T, info.getThis()) catch return local.*;
|
||||
const node = protoNode(T, instance);
|
||||
|
||||
const doc: *Document = node.ownerDocument(frame) orelse switch (node._type) {
|
||||
.document => |d| d,
|
||||
@@ -546,7 +539,7 @@ fn errorLocal(comptime T: type, local: *const Local, info: anytype) Local {
|
||||
}
|
||||
|
||||
const ctx = doc_frame.js;
|
||||
const local_v8_context: *const v8.Context = @ptrCast(v8.v8__Global__Get(&ctx.handle, ctx.isolate.handle));
|
||||
const local_v8_context: *const v8.Context = @ptrCast(v8.v8__Global__Get(&ctx.handle, ctx.isolate.handle) orelse return local.*);
|
||||
return .{
|
||||
.ctx = ctx,
|
||||
.handle = local_v8_context,
|
||||
@@ -555,6 +548,17 @@ fn errorLocal(comptime T: type, local: *const Local, info: anytype) Local {
|
||||
};
|
||||
}
|
||||
|
||||
// Upcast a Node-descendant instance to *Node by walking the _proto chain.
|
||||
// Not every node type defines an asNode() helper (e.g. Comment, Text), but
|
||||
// inheritsOrIs guarantees Node is in the chain
|
||||
fn protoNode(comptime T: type, instance: *T) *@import("../webapi/Node.zig") {
|
||||
if (T == @import("../webapi/Node.zig")) {
|
||||
return instance;
|
||||
}
|
||||
const Proto = @typeInfo(std.meta.fieldInfo(T, ._proto).type).pointer.child;
|
||||
return protoNode(Proto, instance._proto);
|
||||
}
|
||||
|
||||
fn handleError(comptime T: type, comptime F: type, local: *const Local, err: anyerror, info: anytype) void {
|
||||
const isolate = local.isolate;
|
||||
|
||||
@@ -568,26 +572,32 @@ fn handleError(comptime T: type, comptime F: type, local: *const Local, err: any
|
||||
}
|
||||
}
|
||||
|
||||
// early exit
|
||||
switch (err) {
|
||||
error.TryCatchRethrow => return,
|
||||
// A JS exception is already pending in the isolate (e.g. a value's
|
||||
// toString threw during argument conversion); throwing anything here
|
||||
// would replace the original exception the script expects to see.
|
||||
error.JsException => return,
|
||||
else => {},
|
||||
}
|
||||
|
||||
const err_local = errorLocal(T, local, info);
|
||||
|
||||
const js_err: *const v8.Value = blk: {
|
||||
// Error constructors use the isolate's current context: enter the
|
||||
// receiver's realm so the exception gets its prototypes.
|
||||
const entered = err_local.handle != local.handle;
|
||||
const entered = err_local.ctx != local.ctx;
|
||||
if (entered) v8.v8__Context__Enter(err_local.handle);
|
||||
defer if (entered) v8.v8__Context__Exit(err_local.handle);
|
||||
|
||||
break :blk switch (err) {
|
||||
error.TryCatchRethrow => return,
|
||||
// A JS exception is already pending in the isolate (e.g. a value's
|
||||
// toString threw during argument conversion); throwing anything here
|
||||
// would replace the original exception the script expects to see.
|
||||
error.JsException => return,
|
||||
error.InvalidArgument => isolate.createTypeError("invalid argument"),
|
||||
error.TypeError => isolate.createTypeError(""),
|
||||
error.RangeError => isolate.createRangeError(""),
|
||||
error.OutOfMemory => isolate.createError("out of memory"),
|
||||
error.IllegalConstructor => isolate.createError("Illegal Constructor"),
|
||||
error.TryCatchRethrow, error.JsException => unreachable, // early exited a few lines up
|
||||
else => domExceptionToJs(&err_local, err) orelse isolate.createError(@errorName(err)),
|
||||
};
|
||||
};
|
||||
|
||||
@@ -737,7 +737,7 @@ fn inheritsFromHtmlElement(comptime JsApi: type) bool {
|
||||
if (JsApi.bridge.type == HtmlElement) {
|
||||
return false;
|
||||
}
|
||||
return inheritsOrIs(JsApi, HtmlElement.JsApi);
|
||||
return bridge.inheritsOrIs(JsApi, HtmlElement.JsApi);
|
||||
}
|
||||
|
||||
const Unforgeable = struct {
|
||||
@@ -874,7 +874,7 @@ fn attachClass(comptime JsApi: type, comptime flatten: bool, isolate: *v8.Isolat
|
||||
|
||||
if (comptime flatten == false) {
|
||||
inline for (unforgeables) |u| {
|
||||
if (comptime inheritsOrIs(JsApi, u.Owner)) {
|
||||
if (comptime bridge.inheritsOrIs(JsApi, u.Owner)) {
|
||||
// unforgeables attributes aren't only applied directly on the
|
||||
// instance, they're also applied directly on every child instance
|
||||
attachAccessorProperty(u.name, u.accessor, isolate, template, signature, instance);
|
||||
@@ -949,23 +949,6 @@ const unforgeables: []const Unforgeable = blk: {
|
||||
break :blk list;
|
||||
};
|
||||
|
||||
// Whether Child is Ancestor or inherits from it, following the _proto chain.
|
||||
fn inheritsOrIs(comptime Child: type, comptime Ancestor: type) bool {
|
||||
comptime {
|
||||
const target = Ancestor.bridge.type;
|
||||
var T = Child.bridge.type;
|
||||
while (true) {
|
||||
if (T == target) {
|
||||
return true;
|
||||
}
|
||||
if (!@hasField(T, "_proto")) {
|
||||
return false;
|
||||
}
|
||||
T = @typeInfo(std.meta.fieldInfo(T, ._proto).type).pointer.child;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Attach JsApi members to a template (public for reuse). This is called on all
|
||||
// types. But, for globals (window, WGS) it's called twice. The first time, it's
|
||||
// called like any other interface. The 2nd time, it's called with flatten == true
|
||||
|
||||
@@ -1269,3 +1269,20 @@ pub const JsApis = blk: {
|
||||
}
|
||||
break :blk base ++ [_]type{@import("../webapi/WebDriver.zig").JsApi};
|
||||
};
|
||||
|
||||
// Whether Child is Ancestor or inherits from it, following the _proto chain.
|
||||
pub fn inheritsOrIs(comptime Child: type, comptime Ancestor: type) bool {
|
||||
comptime {
|
||||
const target = Ancestor.bridge.type;
|
||||
var T = Child.bridge.type;
|
||||
while (true) {
|
||||
if (T == target) {
|
||||
return true;
|
||||
}
|
||||
if (!@hasField(T, "_proto")) {
|
||||
return false;
|
||||
}
|
||||
T = @typeInfo(std.meta.fieldInfo(T, ._proto).type).pointer.child;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -108,24 +108,6 @@ pub fn setOnClick(self: *Document, setter: ?Window.FunctionSetter, frame: *Frame
|
||||
}
|
||||
}
|
||||
|
||||
// Generates a getter/setter pair backed by the frame's attribute-listener
|
||||
// map, like onclick above, for other document event handler properties.
|
||||
fn handlerAccessor(comptime handler: @import("global_event_handlers.zig").Handler) type {
|
||||
return struct {
|
||||
pub fn get(self: *Document, frame: *Frame) ?js.Function.Global {
|
||||
return frame._event_target_attr_listeners.get(.{ .target = self.asEventTarget(), .handler = handler });
|
||||
}
|
||||
|
||||
pub fn set(self: *Document, setter: ?Window.FunctionSetter, frame: *Frame) !void {
|
||||
if (Window.getFunctionFromSetter(setter)) |cb| {
|
||||
try frame._event_target_attr_listeners.put(frame.arena, .{ .target = self.asEventTarget(), .handler = handler }, cb);
|
||||
} else {
|
||||
_ = frame._event_target_attr_listeners.remove(.{ .target = self.asEventTarget(), .handler = handler });
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub const Type = union(enum) {
|
||||
generic,
|
||||
html: *HTMLDocument,
|
||||
@@ -1530,6 +1512,26 @@ pub const JsApi = struct {
|
||||
return doc_frame.charset;
|
||||
}
|
||||
pub const referrer = bridge.property("", .{ .template = false });
|
||||
|
||||
// Generates a getter/setter pair backed by the frame's attribute-listener
|
||||
// map, like onclick above, for other document event handler properties.
|
||||
fn handlerAccessor(comptime handler: @import("global_event_handlers.zig").Handler) type {
|
||||
return struct {
|
||||
pub fn get(self: *Document, frame: *Frame) ?js.Function.Global {
|
||||
const owner = self._frame orelse frame;
|
||||
return owner._event_target_attr_listeners.get(.{ .target = self.asEventTarget(), .handler = handler });
|
||||
}
|
||||
|
||||
pub fn set(self: *Document, setter: ?Window.FunctionSetter, frame: *Frame) !void {
|
||||
const owner = self._frame orelse frame;
|
||||
if (Window.getFunctionFromSetter(setter)) |cb| {
|
||||
try owner._event_target_attr_listeners.put(owner.arena, .{ .target = self.asEventTarget(), .handler = handler }, cb);
|
||||
} else {
|
||||
_ = owner._event_target_attr_listeners.remove(.{ .target = self.asEventTarget(), .handler = handler });
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
const testing = @import("../../testing.zig");
|
||||
|
||||
@@ -89,8 +89,8 @@ pub const JsApi = struct {
|
||||
};
|
||||
|
||||
pub const constructor = bridge.constructor(DeviceMotionEvent.init, .{});
|
||||
pub const acceleration = bridge.accessor(DeviceMotionEvent.getAcceleration, null, .{ .null_as_undefined = false });
|
||||
pub const accelerationIncludingGravity = bridge.accessor(DeviceMotionEvent.getAccelerationIncludingGravity, null, .{ .null_as_undefined = false });
|
||||
pub const rotationRate = bridge.accessor(DeviceMotionEvent.getRotationRate, null, .{ .null_as_undefined = false });
|
||||
pub const acceleration = bridge.accessor(DeviceMotionEvent.getAcceleration, null, .{});
|
||||
pub const accelerationIncludingGravity = bridge.accessor(DeviceMotionEvent.getAccelerationIncludingGravity, null, .{});
|
||||
pub const rotationRate = bridge.accessor(DeviceMotionEvent.getRotationRate, null, .{});
|
||||
pub const interval = bridge.accessor(DeviceMotionEvent.getInterval, null, .{});
|
||||
};
|
||||
|
||||
@@ -97,8 +97,8 @@ pub const JsApi = struct {
|
||||
};
|
||||
|
||||
pub const constructor = bridge.constructor(DeviceOrientationEvent.init, .{});
|
||||
pub const alpha = bridge.accessor(DeviceOrientationEvent.getAlpha, null, .{ .null_as_undefined = false });
|
||||
pub const beta = bridge.accessor(DeviceOrientationEvent.getBeta, null, .{ .null_as_undefined = false });
|
||||
pub const gamma = bridge.accessor(DeviceOrientationEvent.getGamma, null, .{ .null_as_undefined = false });
|
||||
pub const alpha = bridge.accessor(DeviceOrientationEvent.getAlpha, null, .{});
|
||||
pub const beta = bridge.accessor(DeviceOrientationEvent.getBeta, null, .{});
|
||||
pub const gamma = bridge.accessor(DeviceOrientationEvent.getGamma, null, .{});
|
||||
pub const absolute = bridge.accessor(DeviceOrientationEvent.getAbsolute, null, .{});
|
||||
};
|
||||
|
||||
@@ -111,7 +111,6 @@ pub fn initStorageEvent(
|
||||
old_value: ?[]const u8,
|
||||
new_value: ?[]const u8,
|
||||
url: ?[]const u8,
|
||||
frame: *Frame,
|
||||
) !void {
|
||||
const event = self._proto;
|
||||
if (event._event_phase != .none) {
|
||||
@@ -127,7 +126,6 @@ pub fn initStorageEvent(
|
||||
self._old_value = if (old_value) |v| try arena.dupe(u8, v) else null;
|
||||
self._new_value = if (new_value) |v| try arena.dupe(u8, v) else null;
|
||||
self._url = if (url) |u| try arena.dupe(u8, u) else "";
|
||||
_ = frame;
|
||||
}
|
||||
|
||||
pub const JsApi = struct {
|
||||
@@ -140,10 +138,10 @@ pub const JsApi = struct {
|
||||
};
|
||||
|
||||
pub const constructor = bridge.constructor(StorageEvent.init, .{});
|
||||
pub const key = bridge.accessor(StorageEvent.getKey, null, .{ .null_as_undefined = false });
|
||||
pub const oldValue = bridge.accessor(StorageEvent.getOldValue, null, .{ .null_as_undefined = false });
|
||||
pub const newValue = bridge.accessor(StorageEvent.getNewValue, null, .{ .null_as_undefined = false });
|
||||
pub const key = bridge.accessor(StorageEvent.getKey, null, .{});
|
||||
pub const oldValue = bridge.accessor(StorageEvent.getOldValue, null, .{});
|
||||
pub const newValue = bridge.accessor(StorageEvent.getNewValue, null, .{});
|
||||
pub const url = bridge.accessor(StorageEvent.getUrl, null, .{});
|
||||
pub const storageArea = bridge.accessor(StorageEvent.getStorageArea, null, .{ .null_as_undefined = false });
|
||||
pub const storageArea = bridge.accessor(StorageEvent.getStorageArea, null, .{});
|
||||
pub const initStorageEvent = bridge.function(StorageEvent.initStorageEvent, .{});
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user