diff --git a/src/browser/js/ExecutionWorld.zig b/src/browser/js/ExecutionWorld.zig index 372709cb..b4a45f78 100644 --- a/src/browser/js/ExecutionWorld.zig +++ b/src/browser/js/ExecutionWorld.zig @@ -74,26 +74,23 @@ pub fn createContext(self: *ExecutionWorld, page: *Page, enter: bool) !*Context const env = self.env; const isolate = env.isolate; + const arena = self.context_arena.allocator(); var v8_context: v8.Context = blk: { var temp_scope: v8.HandleScope = undefined; v8.HandleScope.init(&temp_scope, isolate); defer temp_scope.deinit(); - if (comptime IS_DEBUG) { - // Getting this into the snapshot is tricky (anything involving the - // global is tricky). Easier to do here, and in debug mode, we're - // fine with paying the small perf hit. - const js_global = v8.FunctionTemplate.initDefault(isolate); - const global_template = js_global.getInstanceTemplate(); + // Creates a global template that inherits from Window. + const global_template = @import("Snapshot.zig").createGlobalTemplate(isolate, env.templates); - global_template.setNamedProperty(v8.NamedPropertyHandlerConfiguration{ - .getter = unknownPropertyCallback, - .flags = v8.PropertyHandlerFlags.NonMasking | v8.PropertyHandlerFlags.OnlyInterceptStrings, - }, null); - } + // Add the named property handler + global_template.setNamedProperty(v8.NamedPropertyHandlerConfiguration{ + .getter = unknownPropertyCallback, + .flags = v8.PropertyHandlerFlags.NonMasking | v8.PropertyHandlerFlags.OnlyInterceptStrings, + }, null); - const context_local = v8.Context.init(isolate, null, null); + const context_local = v8.Context.init(isolate, global_template, null); const v8_context = v8.Persistent(v8.Context).init(isolate, context_local).castToContext(); break :blk v8_context; }; @@ -124,7 +121,7 @@ pub fn createContext(self: *ExecutionWorld, page: *Page, enter: bool) !*Context .handle_scope = handle_scope, .script_manager = &page._script_manager, .call_arena = page.call_arena, - .arena = self.context_arena.allocator(), + .arena = arena, }; var context = &self.context.?; @@ -159,9 +156,9 @@ pub fn resumeExecution(self: *const ExecutionWorld) void { pub fn unknownPropertyCallback(c_name: ?*const v8.C_Name, raw_info: ?*const v8.C_PropertyCallbackInfo) callconv(.c) u8 { const info = v8.PropertyCallbackInfo.initFromV8(raw_info); - const context = Context.fromIsolate(info.getIsolate()); - const property = context.valueToString(.{ .handle = c_name.? }, .{}) catch "???"; + const context = Context.fromIsolate(info.getIsolate()); + const maybe_property: ?[]u8 = context.valueToString(.{ .handle = c_name.? }, .{}) catch null; const ignored = std.StaticStringMap(void).initComptime(.{ .{ "process", {} }, @@ -185,12 +182,25 @@ pub fn unknownPropertyCallback(c_name: ?*const v8.C_Name, raw_info: ?*const v8.C .{ "CLOSURE_FLAGS", {} }, }); - if (!ignored.has(property)) { - log.debug(.unknown_prop, "unkown global property", .{ - .info = "but the property can exist in pure JS", - .stack = context.stackTrace() catch "???", - .property = property, - }); + if (maybe_property) |prop| { + if (!ignored.has(prop)) { + const document = context.page.document; + + if (document.getElementById(prop)) |el| { + const js_value = context.zigValueToJs(el, .{}) catch { + return v8.Intercepted.No; + }; + + info.getReturnValue().set(js_value); + return v8.Intercepted.Yes; + } + + log.debug(.unknown_prop, "unknown global property", .{ + .info = "but the property can exist in pure JS", + .stack = context.stackTrace() catch "???", + .property = prop, + }); + } } return v8.Intercepted.No; diff --git a/src/browser/js/Function.zig b/src/browser/js/Function.zig index aa87f57a..7e88b926 100644 --- a/src/browser/js/Function.zig +++ b/src/browser/js/Function.zig @@ -116,6 +116,19 @@ pub fn tryCallWithThis(self: *const Function, comptime T: type, this: anytype, a pub fn callWithThis(self: *const Function, comptime T: type, this: anytype, args: anytype) !T { const context = self.context; + // When we're calling a function from within JavaScript itself, this isn't + // necessary. We're within a Caller instantiation, which will already have + // incremented the call_depth and it won't decrement it until the Caller is + // done. + // But some JS functions are initiated from Zig code, and not v8. For + // example, Observers, some event and window callbacks. In those cases, we + // need to increase the call_depth so that the call_arena remains valid for + // the duration of the function call. If we don't do this, the call_arena + // will be reset after each statement of the function which executes Zig code. + const call_depth = context.call_depth; + context.call_depth = call_depth + 1; + defer context.call_depth = call_depth; + const js_this = try context.valueToExistingObject(this); const aargs = if (comptime @typeInfo(@TypeOf(args)) == .null) struct {}{} else args; diff --git a/src/browser/js/Object.zig b/src/browser/js/Object.zig index f2bc3a4d..4520e396 100644 --- a/src/browser/js/Object.zig +++ b/src/browser/js/Object.zig @@ -54,7 +54,7 @@ pub fn set(self: Object, key: []const u8, value: anytype, opts: SetOpts) error{ const context = self.context; const js_key = v8.String.initUtf8(context.isolate, key); - const js_value = try context.zigValueToJs(value); + const js_value = try context.zigValueToJs(value, .{}); const res = self.js_obj.defineOwnProperty(context.v8_context, js_key.toName(), js_value, @bitCast(opts)) orelse false; if (!res) { diff --git a/src/browser/js/Snapshot.zig b/src/browser/js/Snapshot.zig index d93a0f97..6a89e5e5 100644 --- a/src/browser/js/Snapshot.zig +++ b/src/browser/js/Snapshot.zig @@ -113,6 +113,17 @@ fn isValid(self: Snapshot) bool { return v8.SnapshotCreator.startupDataIsValid(self.startup_data); } +pub fn createGlobalTemplate(isolate: v8.Isolate, templates: []const v8.FunctionTemplate) v8.ObjectTemplate { + // Set up the global template to inherit from Window's template + // This way the global object gets all Window properties through inheritance + const js_global = v8.FunctionTemplate.initDefault(isolate); + js_global.setClassName(v8.String.initUtf8(isolate, "Window")); + // Find Window in JsApis by name (avoids circular import) + const window_index = comptime bridge.JsApiLookup.getId(Window.JsApi); + js_global.inherit(templates[window_index]); + return js_global.getInstanceTemplate(); +} + pub fn create(allocator: Allocator) !Snapshot { var external_references = collectExternalReferences(); @@ -154,14 +165,7 @@ pub fn create(allocator: Allocator) !Snapshot { // Set up the global template to inherit from Window's template // This way the global object gets all Window properties through inheritance - const js_global = v8.FunctionTemplate.initDefault(isolate); - js_global.setClassName(v8.String.initUtf8(isolate, "Window")); - - // Find Window in JsApis by name (avoids circular import) - const window_index = comptime bridge.JsApiLookup.getId(Window.JsApi); - js_global.inherit(templates[window_index]); - - const global_template = js_global.getInstanceTemplate(); + const global_template = createGlobalTemplate(isolate, templates[0..]); const context = v8.Context.init(isolate, global_template, null); context.enter(); diff --git a/src/browser/tests/window/named_access.html b/src/browser/tests/window/named_access.html new file mode 100644 index 00000000..7d43eb8c --- /dev/null +++ b/src/browser/tests/window/named_access.html @@ -0,0 +1,26 @@ + + + +
+
+ +

+ + + + + +