add Range.getBoundingClientRect and getClientRects

headless stubs returning zero-valued DOMRect / empty list per CSSOM
View spec. fixes "getBoundingClientRect is not a function" errors on
sites where layout code calls this on Range objects (e.g. airbnb).
This commit is contained in:
egrs
2026-03-09 08:23:19 +01:00
parent 0227afffc8
commit 4586fb1d13
2 changed files with 32 additions and 0 deletions

View File

@@ -1022,3 +1022,23 @@
testing.expectEqual('Stnd', div.textContent); testing.expectEqual('Stnd', div.textContent);
} }
</script> </script>
<script id=getBoundingClientRect>
{
const range = new Range();
const rect = range.getBoundingClientRect();
testing.expectTrue(rect instanceof DOMRect);
testing.expectEqual(0, rect.x);
testing.expectEqual(0, rect.y);
testing.expectEqual(0, rect.width);
testing.expectEqual(0, rect.height);
}
</script>
<script id=getClientRects>
{
const range = new Range();
const rects = range.getClientRects();
testing.expectEqual(0, rects.length);
}
</script>

View File

@@ -25,6 +25,7 @@ const Page = @import("../Page.zig");
const Node = @import("Node.zig"); const Node = @import("Node.zig");
const DocumentFragment = @import("DocumentFragment.zig"); const DocumentFragment = @import("DocumentFragment.zig");
const AbstractRange = @import("AbstractRange.zig"); const AbstractRange = @import("AbstractRange.zig");
const DOMRect = @import("DOMRect.zig");
const Range = @This(); const Range = @This();
@@ -643,6 +644,15 @@ fn nextAfterSubtree(node: *Node, root: *Node) ?*Node {
return null; return null;
} }
// Headless browser has no layout — return a zero-valued DOMRect.
pub fn getBoundingClientRect(_: *const Range) DOMRect {
return .{ ._x = 0, ._y = 0, ._width = 0, ._height = 0 };
}
pub fn getClientRects(_: *const Range) []DOMRect {
return &.{};
}
pub const JsApi = struct { pub const JsApi = struct {
pub const bridge = js.Bridge(Range); pub const bridge = js.Bridge(Range);
@@ -681,6 +691,8 @@ pub const JsApi = struct {
pub const surroundContents = bridge.function(Range.surroundContents, .{ .dom_exception = true }); pub const surroundContents = bridge.function(Range.surroundContents, .{ .dom_exception = true });
pub const createContextualFragment = bridge.function(Range.createContextualFragment, .{ .dom_exception = true }); pub const createContextualFragment = bridge.function(Range.createContextualFragment, .{ .dom_exception = true });
pub const toString = bridge.function(Range.toString, .{ .dom_exception = true }); pub const toString = bridge.function(Range.toString, .{ .dom_exception = true });
pub const getBoundingClientRect = bridge.function(Range.getBoundingClientRect, .{});
pub const getClientRects = bridge.function(Range.getClientRects, .{});
}; };
const testing = @import("../../testing.zig"); const testing = @import("../../testing.zig");