Skip to content

Commit c5d49a9

Browse files
committed
Add dummy PerformanceObserver
Adds a dummy PerformanceObserver. Only the supportedEntryTypes static attribute is supported, and it currently returns an empty array. This hopefully prevents code from trying to use it. For example, before using it, reddit checks if specific types are supported and, if not, doesn't use it. This introduced complexity in the js runtime. Our current approach to attributes only works with primitive types. Non-primitive types can't be attached to a FunctionTemplate (v8 will crash saying only primitive types can be set). Plus, all non primitive types require a context to create anyways. We now detect "primitive" attributes and "complex" attributes. Primitive attributes are setup as before. Complex attributes are setup per-context, requiring another loop through our types to detect & setup on each context creation.
1 parent ef9f828 commit c5d49a9

File tree

6 files changed

+90
-6
lines changed

6 files changed

+90
-6
lines changed

src/browser/dom/dom.zig

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ const IntersectionObserver = @import("intersection_observer.zig");
2828
const DOMParser = @import("dom_parser.zig").DOMParser;
2929
const TreeWalker = @import("tree_walker.zig").TreeWalker;
3030
const NodeFilter = @import("node_filter.zig").NodeFilter;
31+
const Performance = @import("performance.zig").Performance;
32+
const PerformanceObserver = @import("performance_observer.zig").PerformanceObserver;
3133

3234
pub const Interfaces = .{
3335
DOMException,
@@ -44,4 +46,6 @@ pub const Interfaces = .{
4446
DOMParser,
4547
TreeWalker,
4648
NodeFilter,
49+
Performance,
50+
PerformanceObserver,
4751
};
File renamed without changes.
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
// Copyright (C) 2023-2025 Lightpanda (Selecy SAS)
2+
//
3+
// Francis Bouvier <[email protected]>
4+
// Pierre Tachoire <[email protected]>
5+
//
6+
// This program is free software: you can redistribute it and/or modify
7+
// it under the terms of the GNU Affero General Public License as
8+
// published by the Free Software Foundation, either version 3 of the
9+
// License, or (at your option) any later version.
10+
//
11+
// This program is distributed in the hope that it will be useful,
12+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+
// GNU Affero General Public License for more details.
15+
//
16+
// You should have received a copy of the GNU Affero General Public License
17+
// along with this program. If not, see <https://www.gnu.org/licenses/>.
18+
19+
const std = @import("std");
20+
21+
// https://developer.mozilla.org/en-US/docs/Web/API/PerformanceObserver
22+
pub const PerformanceObserver = struct {
23+
pub const _supportedEntryTypes = [0][]const u8{};
24+
};
25+
26+
const testing = @import("../../testing.zig");
27+
test "Browser.DOM.PerformanceObserver" {
28+
var runner = try testing.jsRunner(testing.tracking_allocator, .{});
29+
defer runner.deinit();
30+
31+
try runner.testCases(&.{
32+
.{ "PerformanceObserver.supportedEntryTypes.length", "0" },
33+
}, .{});
34+
}

src/browser/html/html.zig

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@ const Navigator = @import("navigator.zig").Navigator;
2424
const History = @import("history.zig").History;
2525
const Location = @import("location.zig").Location;
2626
const MediaQueryList = @import("media_query_list.zig").MediaQueryList;
27-
const Performance = @import("performance.zig").Performance;
2827

2928
pub const Interfaces = .{
3029
HTMLDocument,
@@ -37,6 +36,5 @@ pub const Interfaces = .{
3736
History,
3837
Location,
3938
MediaQueryList,
40-
Performance,
4139
@import("screen.zig").Interfaces,
4240
};

src/browser/html/window.zig

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ const Crypto = @import("../crypto/crypto.zig").Crypto;
3131
const Console = @import("../console/console.zig").Console;
3232
const EventTarget = @import("../dom/event_target.zig").EventTarget;
3333
const MediaQueryList = @import("media_query_list.zig").MediaQueryList;
34-
const Performance = @import("performance.zig").Performance;
34+
const Performance = @import("../dom/performance.zig").Performance;
3535
const CSSStyleDeclaration = @import("../cssom/css_style_declaration.zig").CSSStyleDeclaration;
3636
const CustomElementRegistry = @import("../webcomponents/custom_element_registry.zig").CustomElementRegistry;
3737
const Screen = @import("screen.zig").Screen;

src/runtime/js.zig

Lines changed: 51 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -337,6 +337,7 @@ pub fn Env(comptime State: type, comptime WebApis: type) type {
337337
const env = self.env;
338338
const isolate = env.isolate;
339339
const Global = @TypeOf(global.*);
340+
const templates = &self.env.templates;
340341

341342
var v8_context: v8.Context = blk: {
342343
var temp_scope: v8.HandleScope = undefined;
@@ -351,7 +352,6 @@ pub fn Env(comptime State: type, comptime WebApis: type) type {
351352

352353
// All the FunctionTemplates that we created and setup in Env.init
353354
// are now going to get associated with our global instance.
354-
const templates = &self.env.templates;
355355
inline for (Types, 0..) |s, i| {
356356
const Struct = s.defaultValue().?;
357357
const class_name = v8.String.initUtf8(isolate, comptime classNameForStruct(Struct));
@@ -463,6 +463,38 @@ pub fn Env(comptime State: type, comptime WebApis: type) type {
463463
}
464464
}
465465

466+
// Primitive attributes are set directly on the FunctionTemplate
467+
// when we setup the environment. But we cannot set more complex
468+
// types (v8 will crash).
469+
//
470+
// Plus, just to create more complex types, we always need a
471+
// context, i.e. an Array has to have a Context to exist.
472+
//
473+
// As far as I can tell, getting the FunctionTemplate's object
474+
// and setting values directly on it, for each context, is the
475+
// way to do this.
476+
inline for (Types, 0..) |s, i| {
477+
const Struct = s.defaultValue().?;
478+
inline for (@typeInfo(Struct).@"struct".decls) |declaration| {
479+
const name = declaration.name;
480+
if (comptime name[0] == '_') {
481+
const value = @field(Struct, name);
482+
483+
if (comptime isComplexAttributeType(@typeInfo(@TypeOf(value)))) {
484+
const js_obj = templates[i].getFunction(v8_context).toObject();
485+
const js_name = v8.String.initUtf8(isolate, name[1..]).toName();
486+
const js_val = try js_context.zigValueToJs(value);
487+
if (!js_obj.setValue(v8_context, js_name, js_val)) {
488+
log.fatal(.app, "set class attribute", .{
489+
.@"struct" = @typeName(Struct),
490+
.name = name,
491+
});
492+
}
493+
}
494+
}
495+
}
496+
}
497+
466498
_ = try js_context._mapZigInstanceToJs(v8_context.getGlobal(), global);
467499
return js_context;
468500
}
@@ -1809,7 +1841,9 @@ pub fn Env(comptime State: type, comptime WebApis: type) type {
18091841
if (comptime name[0] == '_') {
18101842
switch (@typeInfo(@TypeOf(@field(Struct, name)))) {
18111843
.@"fn" => generateMethod(Struct, name, isolate, template_proto),
1812-
else => generateAttribute(Struct, name, isolate, template, template_proto),
1844+
else => |ti| if (!comptime isComplexAttributeType(ti)) {
1845+
generateAttribute(Struct, name, isolate, template, template_proto);
1846+
},
18131847
}
18141848
} else if (comptime std.mem.startsWith(u8, name, "get_")) {
18151849
generateProperty(Struct, name[4..], isolate, template_proto);
@@ -1930,7 +1964,7 @@ pub fn Env(comptime State: type, comptime WebApis: type) type {
19301964
// apply it both to the type itself
19311965
template.set(js_name, js_value, v8.PropertyAttribute.ReadOnly + v8.PropertyAttribute.DontDelete);
19321966

1933-
// andto instances of the type
1967+
// and to instances of the type
19341968
template_proto.set(js_name, js_value, v8.PropertyAttribute.ReadOnly + v8.PropertyAttribute.DontDelete);
19351969
}
19361970

@@ -2396,6 +2430,20 @@ fn isEmpty(comptime T: type) bool {
23962430
return @typeInfo(T) != .@"opaque" and @sizeOf(T) == 0 and @hasDecl(T, "js_legacy_factory") == false;
23972431
}
23982432

2433+
// Attributes that return a primitive type are setup directly on the
2434+
// FunctionTemplate when the Env is setup. More complex types need a v8.Context
2435+
// and cannot be set directly on the FunctionTemplate.
2436+
// We default to saying types are primitives because that's mostly what
2437+
// we have. If we add a new complex type that isn't explictly handled here,
2438+
// we'll get a compiler error in simpleZigValueToJs, and can then explicitly
2439+
// add the type here.
2440+
fn isComplexAttributeType(ti: std.builtin.Type) bool {
2441+
return switch (ti) {
2442+
.array => true,
2443+
else => false,
2444+
};
2445+
}
2446+
23992447
// Responsible for calling Zig functions from JS invokations. This could
24002448
// probably just contained in ExecutionWorld, but having this specific logic, which
24012449
// is somewhat repetitive between constructors, functions, getters, etc contained

0 commit comments

Comments
 (0)