From 21f7b95db99292a404cb3eefff287103136b1f0a Mon Sep 17 00:00:00 2001 From: Karl Seguin Date: Tue, 17 Mar 2026 19:54:21 +0800 Subject: [PATCH] disable observer weak ref https://github.com/lightpanda-io/browser/pull/1870 doesn't work. I think there are ways for the inspector to move objects into a context that skips our reference count (those remote objects?). This disables weak references for MutationObserver and IntersectionObserver. The issue is probably more widespread but these are two types CDP drivers us _a lot_ via inspector, so this should fix a number of immediate crashes. I believe the correct fix is to remove Origin and store things at the Session- level. --- src/browser/js/Origin.zig | 2 +- src/browser/webapi/IntersectionObserver.zig | 31 ++------------------- src/browser/webapi/MutationObserver.zig | 26 ++--------------- 3 files changed, 7 insertions(+), 52 deletions(-) diff --git a/src/browser/js/Origin.zig b/src/browser/js/Origin.zig index 180cfd84..9dc5857b 100644 --- a/src/browser/js/Origin.zig +++ b/src/browser/js/Origin.zig @@ -206,7 +206,7 @@ pub fn createFinalizerCallback( pub fn takeover(self: *Origin, original: *Origin) !void { const arena = self.arena; - try self.globals.ensureUnusedCapacity(arena, self.globals.items.len); + try self.globals.ensureUnusedCapacity(arena, original.globals.items.len); for (original.globals.items) |obj| { self.globals.appendAssumeCapacity(obj); } diff --git a/src/browser/webapi/IntersectionObserver.zig b/src/browser/webapi/IntersectionObserver.zig index b4c07e77..8586a11d 100644 --- a/src/browser/webapi/IntersectionObserver.zig +++ b/src/browser/webapi/IntersectionObserver.zig @@ -37,7 +37,6 @@ pub fn registerTypes() []const type { const IntersectionObserver = @This(); -_rc: u8 = 0, _arena: Allocator, _callback: js.Function.Temp, _observing: std.ArrayList(*Element) = .{}, @@ -94,26 +93,14 @@ pub fn init(callback: js.Function.Temp, options: ?ObserverInit, page: *Page) !*I } pub fn deinit(self: *IntersectionObserver, shutdown: bool, session: *Session) void { - const rc = self._rc; - if (comptime IS_DEBUG) { - std.debug.assert(rc != 0); - } - - if (rc == 1 or shutdown) { + if (shutdown) { self._callback.release(); - if ((comptime IS_DEBUG) and !shutdown) { - std.debug.assert(self._observing.items.len == 0); - } session.releaseArena(self._arena); - } else { - self._rc = rc - 1; + } else if (comptime IS_DEBUG) { + std.debug.assert(false); } } -pub fn acquireRef(self: *IntersectionObserver) void { - self._rc += 1; -} - pub fn observe(self: *IntersectionObserver, target: *Element, page: *Page) !void { // Check if already observing this target for (self._observing.items) |elem| { @@ -124,7 +111,6 @@ pub fn observe(self: *IntersectionObserver, target: *Element, page: *Page) !void // Register with page if this is our first observation if (self._observing.items.len == 0) { - self._rc += 1; try page.registerIntersectionObserver(self); } @@ -159,10 +145,6 @@ pub fn unobserve(self: *IntersectionObserver, target: *Element, page: *Page) voi break; } } - - if (self._observing.items.len == 0) { - self.deinit(false, page._session); - } } pub fn disconnect(self: *IntersectionObserver, page: *Page) void { @@ -173,13 +155,7 @@ pub fn disconnect(self: *IntersectionObserver, page: *Page) void { } self._pending_entries.clearRetainingCapacity(); - const observing_count = self._observing.items.len; self._observing.clearRetainingCapacity(); - - if (observing_count > 0) { - self.deinit(false, page._session); - } - page.unregisterIntersectionObserver(self); } @@ -382,7 +358,6 @@ pub const JsApi = struct { pub const name = "IntersectionObserver"; pub const prototype_chain = bridge.prototypeChain(); pub var class_id: bridge.ClassId = undefined; - pub const weak = true; pub const finalizer = bridge.finalizer(IntersectionObserver.deinit); }; diff --git a/src/browser/webapi/MutationObserver.zig b/src/browser/webapi/MutationObserver.zig index 8b625fa8..002547e5 100644 --- a/src/browser/webapi/MutationObserver.zig +++ b/src/browser/webapi/MutationObserver.zig @@ -39,7 +39,6 @@ pub fn registerTypes() []const type { const MutationObserver = @This(); -_rc: u8 = 0, _arena: Allocator, _callback: js.Function.Temp, _observing: std.ArrayList(Observing) = .{}, @@ -87,26 +86,14 @@ pub fn init(callback: js.Function.Temp, page: *Page) !*MutationObserver { } pub fn deinit(self: *MutationObserver, shutdown: bool, session: *Session) void { - const rc = self._rc; - if (comptime IS_DEBUG) { - std.debug.assert(rc != 0); - } - - if (rc == 1 or shutdown) { + if (shutdown) { self._callback.release(); - if ((comptime IS_DEBUG) and !shutdown) { - std.debug.assert(self._observing.items.len == 0); - } session.releaseArena(self._arena); - } else { - self._rc = rc - 1; + } else if (comptime IS_DEBUG) { + std.debug.assert(false); } } -pub fn acquireRef(self: *MutationObserver) void { - self._rc += 1; -} - pub fn observe(self: *MutationObserver, target: *Node, options: ObserveOptions, page: *Page) !void { const arena = self._arena; @@ -171,7 +158,6 @@ pub fn observe(self: *MutationObserver, target: *Node, options: ObserveOptions, // Register with page if this is our first observation if (self._observing.items.len == 0) { - self._rc += 1; try page.registerMutationObserver(self); } @@ -187,12 +173,7 @@ pub fn disconnect(self: *MutationObserver, page: *Page) void { } self._pending_records.clearRetainingCapacity(); - const observing_count = self._observing.items.len; self._observing.clearRetainingCapacity(); - - if (observing_count > 0) { - self.deinit(false, page._session); - } page.unregisterMutationObserver(self); } @@ -459,7 +440,6 @@ pub const JsApi = struct { pub const name = "MutationObserver"; pub const prototype_chain = bridge.prototypeChain(); pub var class_id: bridge.ClassId = undefined; - pub const weak = true; pub const finalizer = bridge.finalizer(MutationObserver.deinit); };