diff --git a/src/browser/js/Object.zig b/src/browser/js/Object.zig index fbf036e4..13899782 100644 --- a/src/browser/js/Object.zig +++ b/src/browser/js/Object.zig @@ -152,7 +152,7 @@ pub fn getPropertyNames(self: Object) js.Array { } pub fn nameIterator(self: Object) !NameIterator { - const handle = v8.v8__Object__GetPropertyNames(self.handle, self.local.handle) orelse { + const handle = v8.v8__Object__GetOwnPropertyNames(self.handle, self.local.handle) orelse { // see getOwnPropertyNames above return error.TypeError; }; diff --git a/src/browser/js/Snapshot.zig b/src/browser/js/Snapshot.zig index 5562a73a..7da66e79 100644 --- a/src/browser/js/Snapshot.zig +++ b/src/browser/js/Snapshot.zig @@ -670,11 +670,12 @@ fn attachClass(comptime JsApi: type, isolate: *v8.Isolate, template: *const v8.F if (value.static) { v8.v8__Template__SetAccessorProperty(@ptrCast(template), js_name, getter_callback, setter_callback, attribute); } else { + const accessor_attr = if (own_properties) attribute else attribute | v8.DontEnum; v8.v8__ObjectTemplate__SetAccessorProperty__Config(prototype, &.{ .key = js_name, .getter = getter_callback, .setter = setter_callback, - .attribute = attribute, + .attribute = accessor_attr, }); } }, @@ -695,10 +696,8 @@ fn attachClass(comptime JsApi: type, isolate: *v8.Isolate, template: *const v8.F if (value.static and !own_properties) { v8.v8__Template__Set(@ptrCast(template), js_name, @ptrCast(function_template), v8.None); } else { - // For own_properties namespaces, static methods still belong - // on the instance — `CSS` is exposed as an instance via - // `window.CSS`, not as a constructor. - v8.v8__Template__Set(@ptrCast(member_template), js_name, @ptrCast(function_template), v8.None); + const fn_attr: v8.PropertyAttribute = if (own_properties) v8.None else v8.DontEnum; + v8.v8__Template__Set(@ptrCast(member_template), js_name, @ptrCast(function_template), fn_attr); } }, bridge.Indexed => { diff --git a/src/browser/tests/net/url_search_params.html b/src/browser/tests/net/url_search_params.html index a4213655..08d28ec0 100644 --- a/src/browser/tests/net/url_search_params.html +++ b/src/browser/tests/net/url_search_params.html @@ -416,6 +416,99 @@ } + + + + + + + + + + + + + + + diff --git a/src/browser/webapi/Window.zig b/src/browser/webapi/Window.zig index 5b4135fc..d602fc1b 100644 --- a/src/browser/webapi/Window.zig +++ b/src/browser/webapi/Window.zig @@ -206,6 +206,10 @@ pub fn getSelection(self: *const Window) *Selection { return &self._document._selection; } +pub fn getFrameElement(self: *const Window) ?*Element.Html.IFrame { + return self._frame.iframe; +} + pub fn getLocation(self: *const Window) *Location { return self._location; } @@ -898,6 +902,7 @@ pub const JsApi = struct { pub const structuredClone = bridge.function(Window.structuredClone, .{}); pub const getComputedStyle = bridge.function(Window.getComputedStyle, .{}); pub const getSelection = bridge.function(Window.getSelection, .{}); + pub const frameElement = bridge.accessor(Window.getFrameElement, null, .{}); pub const frames = bridge.accessor(Window.getWindow, null, .{}); pub const index = bridge.indexed(Window.getFrame, null, .{ .null_as_undefined = true }); diff --git a/src/browser/webapi/net/URLSearchParams.zig b/src/browser/webapi/net/URLSearchParams.zig index 747a8114..f0dc14ec 100644 --- a/src/browser/webapi/net/URLSearchParams.zig +++ b/src/browser/webapi/net/URLSearchParams.zig @@ -53,6 +53,15 @@ pub fn init(opts_: ?InitOpts, exec: *const Execution) !*URLSearchParams { break :blk try paramsFromArray(arena, js_val.toArray()); } if (js_val.isObject()) { + // Per the URL spec, an iterable init (URLSearchParams, + // Map, ...) should be walked via its @@iterator. We + // don't have a generic iterable path yet; cover the + // common case of `new URLSearchParams(otherUSP)` so + // the prototype-method-leak doesn't just turn into a + // silent empty querystring. + if (js_val.toZig(*URLSearchParams)) |other| { + break :blk try KeyValueList.copy(arena, other._params); + } else |_| {} // normalizer is null, so frame won't be used break :blk try KeyValueList.fromJsObject(arena, js_val.toObject(), null, exec.buf); } diff --git a/src/cdp/CDP.zig b/src/cdp/CDP.zig index fa73efd8..92a17aa7 100644 --- a/src/cdp/CDP.zig +++ b/src/cdp/CDP.zig @@ -379,6 +379,7 @@ pub fn disposeBrowserContext(self: *CDP, browser_context_id: []const u8) bool { bc.deinit(); self.browser.closeSession(); self.browser_context = null; + _ = self.browser_context_arena.reset(.{ .retain_with_limit = 1024 * 16 }); return true; } diff --git a/src/network/layer/RobotsLayer.zig b/src/network/layer/RobotsLayer.zig index 710884e9..da0d5312 100644 --- a/src/network/layer/RobotsLayer.zig +++ b/src/network/layer/RobotsLayer.zig @@ -96,6 +96,13 @@ fn fetchRobotsThenRequest( errdefer std.debug.assert(self.pending.remove(robots_url)); entry.value_ptr.* = .empty; + try entry.value_ptr.append(self.allocator, transfer); + transfer.loop_owned = true; + errdefer { + entry.value_ptr.deinit(self.allocator); + transfer.loop_owned = false; + } + const robots_ctx = try transfer.arena.create(RobotsContext); robots_ctx.* = .{ .layer = self, @@ -135,14 +142,16 @@ fn fetchRobotsThenRequest( .error_callback = RobotsContext.errorCallback, .shutdown_callback = RobotsContext.shutdownCallback, }, transfer.owner); - } + } else { + // Already one in flight, just queue behind. + try entry.value_ptr.append(self.allocator, transfer); - try entry.value_ptr.append(self.allocator, transfer); - // Parked: RobotsLayer owns destruction via flushPending / flushPendingShutdown - // until robots.txt resolves. Without this, Client.request's errdefer (or - // any caller's cleanup) would deinit a transfer that's still on the - // pending list, leaving flushPending with a dangling pointer. - transfer.loop_owned = true; + // Parked: RobotsLayer owns destruction via flushPending / flushPendingShutdown + // until robots.txt resolves. Without this, Client.request's errdefer (or + // any caller's cleanup) would deinit a transfer that's still on the + // pending list, leaving flushPending with a dangling pointer. + transfer.loop_owned = true; + } } fn flushPending(self: *RobotsLayer, robots_url: [:0]const u8, allowed: bool) void {