Merge pull request #1495 from lightpanda-io/fast_getter
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
e2e-test / browser fetch (push) Has been cancelled
zig-test / zig test using v8 in debug mode (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
wpt / web platform tests json output (push) Has been cancelled
wpt / perf-fmt (push) Has been cancelled
e2e-integration-test / zig build release (push) Has been cancelled
e2e-integration-test / demo-integration-scripts (push) Has been cancelled

Adds Document.prerendering
This commit is contained in:
Karl Seguin
2026-02-08 21:45:06 +08:00
committed by GitHub
11 changed files with 132 additions and 76 deletions

View File

@@ -280,6 +280,7 @@ fn countExternalReferences() comptime_int {
// +1 for the illegal constructor callback
var count: comptime_int = 1;
var has_non_template_property: bool = false;
inline for (JsApis) |JsApi| {
// Constructor (only if explicit)
@@ -302,6 +303,10 @@ fn countExternalReferences() comptime_int {
if (value.setter != null) count += 1; // setter
} else if (T == bridge.Function) {
count += 1;
} else if (T == bridge.Property) {
if (value.template == false) {
has_non_template_property = true;
}
} else if (T == bridge.Iterator) {
count += 1;
} else if (T == bridge.Indexed) {
@@ -314,6 +319,10 @@ fn countExternalReferences() comptime_int {
}
}
if (has_non_template_property) {
count += 1;
}
// In debug mode, add unknown property callbacks for types without NamedIndexed
if (comptime IS_DEBUG) {
inline for (JsApis) |JsApi| {
@@ -333,6 +342,8 @@ fn collectExternalReferences() [countExternalReferences()]isize {
references[idx] = @bitCast(@intFromPtr(&illegalConstructorCallback));
idx += 1;
var has_non_template_property = false;
inline for (JsApis) |JsApi| {
if (@hasDecl(JsApi, "constructor")) {
references[idx] = @bitCast(@intFromPtr(JsApi.constructor.func));
@@ -358,6 +369,10 @@ fn collectExternalReferences() [countExternalReferences()]isize {
} else if (T == bridge.Function) {
references[idx] = @bitCast(@intFromPtr(value.func));
idx += 1;
} else if (T == bridge.Property) {
if (value.template == false) {
has_non_template_property = true;
}
} else if (T == bridge.Iterator) {
references[idx] = @bitCast(@intFromPtr(value.func));
idx += 1;
@@ -379,6 +394,11 @@ fn collectExternalReferences() [countExternalReferences()]isize {
}
}
if (has_non_template_property) {
references[idx] = @bitCast(@intFromPtr(&bridge.Property.getter));
idx += 1;
}
// In debug mode, collect unknown property callbacks for types without NamedIndexed
if (comptime IS_DEBUG) {
inline for (JsApis) |JsApi| {
@@ -497,17 +517,24 @@ fn attachClass(comptime JsApi: type, isolate: *v8.Isolate, template: *v8.Functio
v8.v8__Template__Set(@ptrCast(target), js_name, @ptrCast(function_template), v8.None);
},
bridge.Property => {
// simpleZigValueToJs now returns raw handle directly
const js_value = switch (value) {
.int => |v| js.simpleZigValueToJs(.{ .handle = isolate }, v, true, false),
const js_value = switch (value.value) {
inline .bool, .int => |v| js.simpleZigValueToJs(.{ .handle = isolate }, v, true, false),
};
const js_name = v8.v8__String__NewFromUtf8(isolate, name.ptr, v8.kNormal, @intCast(name.len));
if (value.template == false) {
// not defined on the template, only on the instance. This
// is like an Accessor, but because the value is known at
// compile time, we skip _a lot_ of code and quickly return
// the hard-coded value
const getter_callback = @constCast(v8.v8__FunctionTemplate__New__DEFAULT3(isolate, bridge.Property.getter, js_value));
v8.v8__ObjectTemplate__SetAccessorProperty__DEFAULT(target, js_name, getter_callback);
} else {
// apply it both to the type itself
v8.v8__Template__Set(@ptrCast(template), js_name, js_value, v8.ReadOnly + v8.DontDelete);
// and to instances of the type
v8.v8__Template__Set(@ptrCast(target), js_name, js_value, v8.ReadOnly + v8.DontDelete);
}
},
bridge.Constructor => {}, // already handled in generateConstructor
else => {},

View File

@@ -62,12 +62,13 @@ pub fn Builder(comptime T: type) type {
return Callable.init(T, func, opts);
}
pub fn property(value: anytype) Property {
switch (@typeInfo(@TypeOf(value))) {
.comptime_int, .int => return .{ .int = value },
else => {},
}
@compileError("Property for " ++ @typeName(@TypeOf(value)) ++ " hasn't been defined yet");
pub fn property(value: anytype, opts: Property.Opts) Property {
// If you add strings to this, they might need to be internalized!
return switch (@typeInfo(@TypeOf(value))) {
.bool => Property.init(.{ .bool = value }, opts),
.comptime_int, .int => Property.init(.{ .int = value }, opts),
else => @compileError("Property for " ++ @typeName(@TypeOf(value)) ++ " hasn't been defined yet"),
};
}
const PrototypeChainEntry = @import("TaggedOpaque.zig").PrototypeChainEntry;
@@ -398,8 +399,33 @@ pub const Callable = struct {
}
};
pub const Property = union(enum) {
pub const Property = struct {
value: Value,
template: bool,
// If you add strings to this, they might need to be internalized!
const Value = union(enum) {
int: i64,
bool: bool,
};
const Opts = struct {
template: bool,
};
fn init(value: Value, opts: Opts) Property {
return .{
.value = value,
.template = opts.template,
};
}
pub fn getter(handle: ?*const v8.FunctionCallbackInfo) callconv(.c) void {
const value = v8.v8__FunctionCallbackInfo__Data(handle.?);
var rv: v8.ReturnValue = undefined;
v8.v8__FunctionCallbackInfo__GetReturnValue(handle.?, &rv);
v8.v8__ReturnValue__Set(rv, value);
}
};
const Finalizer = struct {

View File

@@ -15,6 +15,8 @@
testing.expectEqual(window, document.defaultView);
testing.expectEqual(false, document.hidden);
testing.expectEqual("visible", document.visibilityState);
testing.expectEqual(false, document.prerendering);
testing.expectEqual(undefined, Document.prerendering);
</script>
<script id=headAndbody>

View File

@@ -1004,6 +1004,7 @@ pub const JsApi = struct {
pub const adoptedStyleSheets = bridge.accessor(Document.getAdoptedStyleSheets, Document.setAdoptedStyleSheets, .{});
pub const hidden = bridge.accessor(Document.getHidden, null, .{});
pub const visibilityState = bridge.accessor(Document.getVisibilityState, null, .{});
pub const prerendering = bridge.property(false, .{ .template = false });
pub const defaultView = bridge.accessor(struct {
fn defaultView(_: *const Document, page: *Page) *@import("Window.zig") {

View File

@@ -410,10 +410,10 @@ pub const JsApi = struct {
pub const cancelBubble = bridge.accessor(Event.getCancelBubble, Event.setCancelBubble, .{});
// Event phase constants
pub const NONE = bridge.property(@intFromEnum(EventPhase.none));
pub const CAPTURING_PHASE = bridge.property(@intFromEnum(EventPhase.capturing_phase));
pub const AT_TARGET = bridge.property(@intFromEnum(EventPhase.at_target));
pub const BUBBLING_PHASE = bridge.property(@intFromEnum(EventPhase.bubbling_phase));
pub const NONE = bridge.property(@intFromEnum(EventPhase.none), .{ .template = true });
pub const CAPTURING_PHASE = bridge.property(@intFromEnum(EventPhase.capturing_phase), .{ .template = true });
pub const AT_TARGET = bridge.property(@intFromEnum(EventPhase.at_target), .{ .template = true });
pub const BUBBLING_PHASE = bridge.property(@intFromEnum(EventPhase.bubbling_phase), .{ .template = true });
};
// tested in event_target

View File

@@ -874,25 +874,25 @@ pub const JsApi = struct {
pub var class_id: bridge.ClassId = undefined;
};
pub const ELEMENT_NODE = bridge.property(1);
pub const ATTRIBUTE_NODE = bridge.property(2);
pub const TEXT_NODE = bridge.property(3);
pub const CDATA_SECTION_NODE = bridge.property(4);
pub const ENTITY_REFERENCE_NODE = bridge.property(5);
pub const ENTITY_NODE = bridge.property(6);
pub const PROCESSING_INSTRUCTION_NODE = bridge.property(7);
pub const COMMENT_NODE = bridge.property(8);
pub const DOCUMENT_NODE = bridge.property(9);
pub const DOCUMENT_TYPE_NODE = bridge.property(10);
pub const DOCUMENT_FRAGMENT_NODE = bridge.property(11);
pub const NOTATION_NODE = bridge.property(12);
pub const ELEMENT_NODE = bridge.property(1, .{ .template = true });
pub const ATTRIBUTE_NODE = bridge.property(2, .{ .template = true });
pub const TEXT_NODE = bridge.property(3, .{ .template = true });
pub const CDATA_SECTION_NODE = bridge.property(4, .{ .template = true });
pub const ENTITY_REFERENCE_NODE = bridge.property(5, .{ .template = true });
pub const ENTITY_NODE = bridge.property(6, .{ .template = true });
pub const PROCESSING_INSTRUCTION_NODE = bridge.property(7, .{ .template = true });
pub const COMMENT_NODE = bridge.property(8, .{ .template = true });
pub const DOCUMENT_NODE = bridge.property(9, .{ .template = true });
pub const DOCUMENT_TYPE_NODE = bridge.property(10, .{ .template = true });
pub const DOCUMENT_FRAGMENT_NODE = bridge.property(11, .{ .template = true });
pub const NOTATION_NODE = bridge.property(12, .{ .template = true });
pub const DOCUMENT_POSITION_DISCONNECTED = bridge.property(0x01);
pub const DOCUMENT_POSITION_PRECEDING = bridge.property(0x02);
pub const DOCUMENT_POSITION_FOLLOWING = bridge.property(0x04);
pub const DOCUMENT_POSITION_CONTAINS = bridge.property(0x08);
pub const DOCUMENT_POSITION_CONTAINED_BY = bridge.property(0x10);
pub const DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC = bridge.property(0x20);
pub const DOCUMENT_POSITION_DISCONNECTED = bridge.property(0x01, .{ .template = true });
pub const DOCUMENT_POSITION_PRECEDING = bridge.property(0x02, .{ .template = true });
pub const DOCUMENT_POSITION_FOLLOWING = bridge.property(0x04, .{ .template = true });
pub const DOCUMENT_POSITION_CONTAINS = bridge.property(0x08, .{ .template = true });
pub const DOCUMENT_POSITION_CONTAINED_BY = bridge.property(0x10, .{ .template = true });
pub const DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC = bridge.property(0x20, .{ .template = true });
pub const nodeName = bridge.accessor(struct {
fn wrap(self: *const Node, page: *Page) []const u8 {

View File

@@ -90,21 +90,21 @@ pub const JsApi = struct {
pub const empty_with_no_proto = true;
};
pub const FILTER_ACCEPT = bridge.property(NodeFilter.FILTER_ACCEPT);
pub const FILTER_REJECT = bridge.property(NodeFilter.FILTER_REJECT);
pub const FILTER_SKIP = bridge.property(NodeFilter.FILTER_SKIP);
pub const FILTER_ACCEPT = bridge.property(NodeFilter.FILTER_ACCEPT, .{ .template = true });
pub const FILTER_REJECT = bridge.property(NodeFilter.FILTER_REJECT, .{ .template = true });
pub const FILTER_SKIP = bridge.property(NodeFilter.FILTER_SKIP, .{ .template = true });
pub const SHOW_ALL = bridge.property(NodeFilter.SHOW_ALL);
pub const SHOW_ELEMENT = bridge.property(NodeFilter.SHOW_ELEMENT);
pub const SHOW_ATTRIBUTE = bridge.property(NodeFilter.SHOW_ATTRIBUTE);
pub const SHOW_TEXT = bridge.property(NodeFilter.SHOW_TEXT);
pub const SHOW_CDATA_SECTION = bridge.property(NodeFilter.SHOW_CDATA_SECTION);
pub const SHOW_ENTITY_REFERENCE = bridge.property(NodeFilter.SHOW_ENTITY_REFERENCE);
pub const SHOW_ENTITY = bridge.property(NodeFilter.SHOW_ENTITY);
pub const SHOW_PROCESSING_INSTRUCTION = bridge.property(NodeFilter.SHOW_PROCESSING_INSTRUCTION);
pub const SHOW_COMMENT = bridge.property(NodeFilter.SHOW_COMMENT);
pub const SHOW_DOCUMENT = bridge.property(NodeFilter.SHOW_DOCUMENT);
pub const SHOW_DOCUMENT_TYPE = bridge.property(NodeFilter.SHOW_DOCUMENT_TYPE);
pub const SHOW_DOCUMENT_FRAGMENT = bridge.property(NodeFilter.SHOW_DOCUMENT_FRAGMENT);
pub const SHOW_NOTATION = bridge.property(NodeFilter.SHOW_NOTATION);
pub const SHOW_ALL = bridge.property(NodeFilter.SHOW_ALL, .{ .template = true });
pub const SHOW_ELEMENT = bridge.property(NodeFilter.SHOW_ELEMENT, .{ .template = true });
pub const SHOW_ATTRIBUTE = bridge.property(NodeFilter.SHOW_ATTRIBUTE, .{ .template = true });
pub const SHOW_TEXT = bridge.property(NodeFilter.SHOW_TEXT, .{ .template = true });
pub const SHOW_CDATA_SECTION = bridge.property(NodeFilter.SHOW_CDATA_SECTION, .{ .template = true });
pub const SHOW_ENTITY_REFERENCE = bridge.property(NodeFilter.SHOW_ENTITY_REFERENCE, .{ .template = true });
pub const SHOW_ENTITY = bridge.property(NodeFilter.SHOW_ENTITY, .{ .template = true });
pub const SHOW_PROCESSING_INSTRUCTION = bridge.property(NodeFilter.SHOW_PROCESSING_INSTRUCTION, .{ .template = true });
pub const SHOW_COMMENT = bridge.property(NodeFilter.SHOW_COMMENT, .{ .template = true });
pub const SHOW_DOCUMENT = bridge.property(NodeFilter.SHOW_DOCUMENT, .{ .template = true });
pub const SHOW_DOCUMENT_TYPE = bridge.property(NodeFilter.SHOW_DOCUMENT_TYPE, .{ .template = true });
pub const SHOW_DOCUMENT_FRAGMENT = bridge.property(NodeFilter.SHOW_DOCUMENT_FRAGMENT, .{ .template = true });
pub const SHOW_NOTATION = bridge.property(NodeFilter.SHOW_NOTATION, .{ .template = true });
};

View File

@@ -565,10 +565,10 @@ pub const JsApi = struct {
};
// Constants for compareBoundaryPoints
pub const START_TO_START = bridge.property(0);
pub const START_TO_END = bridge.property(1);
pub const END_TO_END = bridge.property(2);
pub const END_TO_START = bridge.property(3);
pub const START_TO_START = bridge.property(0, .{ .template = true });
pub const START_TO_END = bridge.property(1, .{ .template = true });
pub const END_TO_END = bridge.property(2, .{ .template = true });
pub const END_TO_START = bridge.property(3, .{ .template = true });
pub const constructor = bridge.constructor(Range.init, .{});
pub const setStart = bridge.function(Range.setStart, .{ .dom_exception = true });

View File

@@ -284,16 +284,16 @@ pub const JsApi = struct {
pub var class_id: bridge.ClassId = undefined;
};
pub const NETWORK_EMPTY = bridge.property(@intFromEnum(NetworkState.NETWORK_EMPTY));
pub const NETWORK_IDLE = bridge.property(@intFromEnum(NetworkState.NETWORK_IDLE));
pub const NETWORK_LOADING = bridge.property(@intFromEnum(NetworkState.NETWORK_LOADING));
pub const NETWORK_NO_SOURCE = bridge.property(@intFromEnum(NetworkState.NETWORK_NO_SOURCE));
pub const NETWORK_EMPTY = bridge.property(@intFromEnum(NetworkState.NETWORK_EMPTY), .{ .template = true });
pub const NETWORK_IDLE = bridge.property(@intFromEnum(NetworkState.NETWORK_IDLE), .{ .template = true });
pub const NETWORK_LOADING = bridge.property(@intFromEnum(NetworkState.NETWORK_LOADING), .{ .template = true });
pub const NETWORK_NO_SOURCE = bridge.property(@intFromEnum(NetworkState.NETWORK_NO_SOURCE), .{ .template = true });
pub const HAVE_NOTHING = bridge.property(@intFromEnum(ReadyState.HAVE_NOTHING));
pub const HAVE_METADATA = bridge.property(@intFromEnum(ReadyState.HAVE_METADATA));
pub const HAVE_CURRENT_DATA = bridge.property(@intFromEnum(ReadyState.HAVE_CURRENT_DATA));
pub const HAVE_FUTURE_DATA = bridge.property(@intFromEnum(ReadyState.HAVE_FUTURE_DATA));
pub const HAVE_ENOUGH_DATA = bridge.property(@intFromEnum(ReadyState.HAVE_ENOUGH_DATA));
pub const HAVE_NOTHING = bridge.property(@intFromEnum(ReadyState.HAVE_NOTHING), .{ .template = true });
pub const HAVE_METADATA = bridge.property(@intFromEnum(ReadyState.HAVE_METADATA), .{ .template = true });
pub const HAVE_CURRENT_DATA = bridge.property(@intFromEnum(ReadyState.HAVE_CURRENT_DATA), .{ .template = true });
pub const HAVE_FUTURE_DATA = bridge.property(@intFromEnum(ReadyState.HAVE_FUTURE_DATA), .{ .template = true });
pub const HAVE_ENOUGH_DATA = bridge.property(@intFromEnum(ReadyState.HAVE_ENOUGH_DATA), .{ .template = true });
pub const src = bridge.accessor(Media.getSrc, Media.setSrc, .{});
pub const autoplay = bridge.accessor(Media.getAutoplay, Media.setAutoplay, .{});

View File

@@ -49,10 +49,10 @@ pub const JsApi = struct {
};
// Error code constants
pub const MEDIA_ERR_ABORTED = bridge.property(1);
pub const MEDIA_ERR_NETWORK = bridge.property(2);
pub const MEDIA_ERR_DECODE = bridge.property(3);
pub const MEDIA_ERR_SRC_NOT_SUPPORTED = bridge.property(4);
pub const MEDIA_ERR_ABORTED = bridge.property(1, .{ .template = true });
pub const MEDIA_ERR_NETWORK = bridge.property(2, .{ .template = true });
pub const MEDIA_ERR_DECODE = bridge.property(3, .{ .template = true });
pub const MEDIA_ERR_SRC_NOT_SUPPORTED = bridge.property(4, .{ .template = true });
pub const code = bridge.accessor(MediaError.getCode, null, .{});
pub const message = bridge.accessor(MediaError.getMessage, null, .{});

View File

@@ -552,11 +552,11 @@ pub const JsApi = struct {
};
pub const constructor = bridge.constructor(XMLHttpRequest.init, .{});
pub const UNSENT = bridge.property(@intFromEnum(XMLHttpRequest.ReadyState.unsent));
pub const OPENED = bridge.property(@intFromEnum(XMLHttpRequest.ReadyState.opened));
pub const HEADERS_RECEIVED = bridge.property(@intFromEnum(XMLHttpRequest.ReadyState.headers_received));
pub const LOADING = bridge.property(@intFromEnum(XMLHttpRequest.ReadyState.loading));
pub const DONE = bridge.property(@intFromEnum(XMLHttpRequest.ReadyState.done));
pub const UNSENT = bridge.property(@intFromEnum(XMLHttpRequest.ReadyState.unsent), .{ .template = true });
pub const OPENED = bridge.property(@intFromEnum(XMLHttpRequest.ReadyState.opened), .{ .template = true });
pub const HEADERS_RECEIVED = bridge.property(@intFromEnum(XMLHttpRequest.ReadyState.headers_received), .{ .template = true });
pub const LOADING = bridge.property(@intFromEnum(XMLHttpRequest.ReadyState.loading), .{ .template = true });
pub const DONE = bridge.property(@intFromEnum(XMLHttpRequest.ReadyState.done), .{ .template = true });
pub const onreadystatechange = bridge.accessor(XMLHttpRequest.getOnReadyStateChange, XMLHttpRequest.setOnReadyStateChange, .{});
pub const withCredentials = bridge.accessor(XMLHttpRequest.getWithCredentials, XMLHttpRequest.setWithCredentials, .{ .dom_exception = true });