From e32d35b156b28f7ddad0603ca47dd44bc75981ab Mon Sep 17 00:00:00 2001 From: sjorsdonkers <72333389+sjorsdonkers@users.noreply.github.com> Date: Tue, 6 May 2025 11:27:35 +0200 Subject: [PATCH] no reobserve rootbounds for Window --- src/browser/browser.zig | 9 ++++++ src/browser/dom/element.zig | 6 ++-- src/browser/dom/intersection_observer.zig | 39 +++++++++++++++++------ 3 files changed, 41 insertions(+), 13 deletions(-) diff --git a/src/browser/browser.zig b/src/browser/browser.zig index 5bee4ac2..75592ba7 100644 --- a/src/browser/browser.zig +++ b/src/browser/browser.zig @@ -828,6 +828,15 @@ const FlatRenderer = struct { }; } + pub fn boundingRect(self: *const FlatRenderer) Element.DOMRect { + return .{ + .x = 0.0, + .y = 0.0, + .width = @floatFromInt(self.width()), + .height = @floatFromInt(self.height()), + }; + } + pub fn width(self: *const FlatRenderer) u32 { return @intCast(self.elements.items.len + 1); // +1 since x starts at 1 (use len after append) } diff --git a/src/browser/dom/element.zig b/src/browser/dom/element.zig index cd808a19..b349990e 100644 --- a/src/browser/dom/element.zig +++ b/src/browser/dom/element.zig @@ -348,10 +348,8 @@ pub const Element = struct { // returns a collection of DOMRect objects that indicate the bounding rectangles for each CSS border box in a client. // We do not render so just always return the element's rect. - pub fn _getClientRects(self: *parser.Element, state: *SessionState) ![]DOMRect { - var heap = try state.arena.create(DOMRect); - heap.* = try state.renderer.getRect(self); - return heap[0..1]; + pub fn _getClientRects(self: *parser.Element, state: *SessionState) ![1]DOMRect { + return [_]DOMRect{try state.renderer.getRect(self)}; } pub fn get_clientWidth(_: *parser.Element, state: *SessionState) u32 { diff --git a/src/browser/dom/intersection_observer.zig b/src/browser/dom/intersection_observer.zig index 3eed51ef..fb2937a7 100644 --- a/src/browser/dom/intersection_observer.zig +++ b/src/browser/dom/intersection_observer.zig @@ -54,7 +54,7 @@ pub const IntersectionObserver = struct { var options = IntersectionObserverOptions{ .root = parser.documentToNode(parser.documentHTMLToDocument(state.document.?)), .rootMargin = "0px 0px 0px 0px", - .threshold = &default_threshold, + .threshold = &.{0.0}, }; if (options_) |*o| { if (o.root) |root| { @@ -74,10 +74,16 @@ pub const IntersectionObserver = struct { self.observed_entries = .{}; // We don't free as it is on an arena } - pub fn _observe(self: *IntersectionObserver, targetElement: *parser.Element) !void { + pub fn _observe(self: *IntersectionObserver, target_element: *parser.Element) !void { + for (self.observed_entries.items) |*observer| { + if (observer.target == target_element) { + return; // Already observed + } + } + try self.observed_entries.append(self.state.arena, .{ .state = self.state, - .target = targetElement, + .target = target_element, .options = &self.options, }); @@ -107,7 +113,6 @@ const IntersectionObserverOptions = struct { rootMargin: ?[]const u8, threshold: ?[]const f32, }; -const default_threshold = [_]f32{0.0}; // https://developer.mozilla.org/en-US/docs/Web/API/IntersectionObserverEntry // https://w3c.github.io/IntersectionObserver/#intersection-observer-entry @@ -118,7 +123,7 @@ pub const IntersectionObserverEntry = struct { // Returns the bounds rectangle of the target element as a DOMRectReadOnly. The bounds are computed as described in the documentation for Element.getBoundingClientRect(). pub fn get_boundingClientRect(self: *const IntersectionObserverEntry) !Element.DOMRect { - return self.state.renderer.getRect(self.target); // Does this ever change? + return self.state.renderer.getRect(self.target); } // Returns the ratio of the intersectionRect to the boundingClientRect. @@ -139,6 +144,10 @@ pub const IntersectionObserverEntry = struct { // Returns a DOMRectReadOnly for the intersection observer's root. pub fn get_rootBounds(self: *const IntersectionObserverEntry) !Element.DOMRect { const root = self.options.root.?; + if (@intFromPtr(root) == @intFromPtr(self.state.document.?)) { + return self.state.renderer.boundingRect(); + } + const root_type = try parser.nodeType(root); var element: *parser.Element = undefined; @@ -179,7 +188,7 @@ test "Browser.DOM.IntersectionObserver" { }, .{}); // This test is documenting current behavior, not correct behavior. - // Currently every time observe is called, the callback is called with all entries. 1 + 2 = 3 + // Currently every time observe is called, the callback is called with all entries. try runner.testCases(&.{ .{ "let count_b = 0;", "undefined" }, .{ "let observer_b = new IntersectionObserver(entries => {count_b = entries.length;});", "undefined" }, @@ -191,6 +200,17 @@ test "Browser.DOM.IntersectionObserver" { .{ "count_b;", "2" }, }, .{}); + // Re-observing is a no-op + try runner.testCases(&.{ + .{ "let count_bb = 0;", "undefined" }, + .{ "let observer_bb = new IntersectionObserver(entries => {count_bb = entries.length;});", "undefined" }, + .{ "const bb1 = document.createElement('div');", "undefined" }, + .{ "observer_bb.observe(bb1);", "undefined" }, + .{ "count_bb;", "1" }, + .{ "observer_bb.observe(bb1);", "undefined" }, + .{ "count_bb;", "1" }, // Still 1, not 2 + }, .{}); + // Unobserve try runner.testCases(&.{ .{ "let count_c = 0;", "undefined" }, @@ -234,8 +254,9 @@ test "Browser.DOM.IntersectionObserver" { .{ "entry.intersectionRect.width;", "1" }, .{ "entry.intersectionRect.height;", "1" }, .{ "entry.isIntersecting;", "true" }, - .{ "entry.rootBounds.x;", "2" }, // This is not the prefered behaviour, the Window rect should wrap all elements so x -> 0 - .{ "entry.rootBounds.width;", "1" }, // width -> clientWidth + .{ "entry.rootBounds.x;", "0" }, + .{ "entry.rootBounds.y;", "0" }, + .{ "entry.rootBounds.width;", "2" }, .{ "entry.rootBounds.height;", "1" }, .{ "entry.target;", "[object HTMLDivElement]" }, }, .{}); @@ -252,6 +273,6 @@ test "Browser.DOM.IntersectionObserver" { "undefined", }, .{ "new_observer.observe(document.createElement('div'));", "undefined" }, - .{ "new_entry.rootBounds.x;", "3" }, + .{ "new_entry.rootBounds.x;", "2" }, }, .{}); }