From aa32dbeb1b0bf2ff8e9f5e1b034e1c75c1abae3d Mon Sep 17 00:00:00 2001 From: Karl Seguin Date: Tue, 9 Jun 2026 19:02:10 +0800 Subject: [PATCH] clean: Map -> Set since value isn't needed Swap `_previous_states: Map(*Element, Bool)` to `_tracked: Set(*Element)` since we only care about membership (the value was previous always bool). This better captures the intent. --- src/browser/webapi/IntersectionObserver.zig | 25 ++++++++++----------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/src/browser/webapi/IntersectionObserver.zig b/src/browser/webapi/IntersectionObserver.zig index fdc53de9..b2e10843 100644 --- a/src/browser/webapi/IntersectionObserver.zig +++ b/src/browser/webapi/IntersectionObserver.zig @@ -47,7 +47,8 @@ _root: ?*Element = null, _root_margin: []const u8 = "0px", _threshold: []const f64 = &.{0.0}, _pending_entries: std.ArrayList(*IntersectionObserverEntry) = .{}, -_previous_states: std.AutoHashMapUnmanaged(*Element, bool) = .{}, +// tracked targets that aren't reported yet +_tracked: std.AutoHashMapUnmanaged(*Element, void) = .{}, // Shared zero DOMRect to avoid repeated allocations for non-intersecting elements var zero_rect: DOMRect = .{ @@ -140,8 +141,7 @@ pub fn observe(self: *IntersectionObserver, target: *Element, frame: *Frame) !vo try frame.registerIntersectionObserver(self); } - // false = tracked but not yet reported - try self._previous_states.put(self._arena, target, false); + try self._tracked.put(self._arena, target, {}); // Check intersection for this new target and schedule delivery try self.checkIntersection(target, frame); @@ -155,7 +155,7 @@ pub fn unobserve(self: *IntersectionObserver, target: *Element, frame: *Frame) v for (self._observing.items, 0..) |elem, i| { if (elem == target) { _ = self._observing.swapRemove(i); - _ = self._previous_states.remove(target); + _ = self._tracked.remove(target); // Remove any pending entries for this target. // Entries will be cleaned up by V8 GC via the finalizer. @@ -182,7 +182,7 @@ pub fn disconnect(self: *IntersectionObserver, frame: *Frame) void { entry.deinit(frame._page); } self._pending_entries.clearRetainingCapacity(); - self._previous_states.clearRetainingCapacity(); + self._tracked.clearRetainingCapacity(); if (self._observing.items.len > 0) { frame.unregisterIntersectionObserver(self); @@ -252,22 +252,21 @@ fn meetsThreshold(self: *IntersectionObserver, ratio: f64) bool { } fn checkIntersection(self: *IntersectionObserver, target: *Element, frame: *Frame) !void { - const previous_state = self._previous_states.getEntry(target) orelse { - // If target isn't in `_previous_states` then it was already reported. - // This is an important optimization which lets us skip the heavy work. + const tracked = self._tracked.getEntry(target) orelse { + // If target isn't tracked then it was already reported. This is an + // important optimization which lets us skip the heavy work below. // This function can be called a lot. return; }; const has_parent = target.asNode().parentNode() != null; const is_now_intersecting = has_parent and self.meetsThreshold(1.0); - if (is_now_intersecting == previous_state.value_ptr.*) { - // Unchanged (e.g. observed while still orphaned) — leave the entry as-is - // so a later attach is reported. + if (!is_now_intersecting) { + // Not intersecting yet (e.g. observed while still orphaned) — keep + // tracking so a later attach is reported. return; } - // The only reachable transition is false → intersecting. // Building the entry is the expensive part — getBoundingClientRect walks the // document to fake a position (O(node count)) — so it only runs here. const data = try self.calculateIntersection(target, has_parent, frame); @@ -286,7 +285,7 @@ fn checkIntersection(self: *IntersectionObserver, target: *Element, frame: *Fram ._intersection_ratio = data.intersection_ratio, }; try self._pending_entries.append(self._arena, entry); - _ = self._previous_states.removeByPtr(previous_state.key_ptr); + _ = self._tracked.removeByPtr(tracked.key_ptr); } pub fn checkIntersections(self: *IntersectionObserver, frame: *Frame) !void {