mirror of
https://github.com/lightpanda-io/browser.git
synced 2026-03-22 04:34:44 +00:00
Merge pull request #1533 from lightpanda-io/element_positions
Some checks failed
e2e-test / zig build release (push) Has been cancelled
e2e-test / demo-scripts (push) Has been cancelled
e2e-test / cdp-and-hyperfine-bench (push) Has been cancelled
e2e-test / perf-fmt (push) Has been cancelled
e2e-test / browser fetch (push) Has been cancelled
zig-test / zig test using v8 in debug mode (push) Has been cancelled
zig-test / zig test (push) Has been cancelled
zig-test / perf-fmt (push) Has been cancelled
nightly build / build-linux-x86_64 (push) Has been cancelled
nightly build / build-linux-aarch64 (push) Has been cancelled
nightly build / build-macos-aarch64 (push) Has been cancelled
nightly build / build-macos-x86_64 (push) Has been cancelled
wpt / web platform tests json output (push) Has been cancelled
wpt / perf-fmt (push) Has been cancelled
Some checks failed
e2e-test / zig build release (push) Has been cancelled
e2e-test / demo-scripts (push) Has been cancelled
e2e-test / cdp-and-hyperfine-bench (push) Has been cancelled
e2e-test / perf-fmt (push) Has been cancelled
e2e-test / browser fetch (push) Has been cancelled
zig-test / zig test using v8 in debug mode (push) Has been cancelled
zig-test / zig test (push) Has been cancelled
zig-test / perf-fmt (push) Has been cancelled
nightly build / build-linux-x86_64 (push) Has been cancelled
nightly build / build-linux-aarch64 (push) Has been cancelled
nightly build / build-macos-aarch64 (push) Has been cancelled
nightly build / build-macos-x86_64 (push) Has been cancelled
wpt / web platform tests json output (push) Has been cancelled
wpt / perf-fmt (push) Has been cancelled
Add various element position properties
This commit is contained in:
@@ -106,6 +106,7 @@ _element_rel_lists: Element.RelListLookup = .empty,
|
|||||||
_element_shadow_roots: Element.ShadowRootLookup = .empty,
|
_element_shadow_roots: Element.ShadowRootLookup = .empty,
|
||||||
_node_owner_documents: Node.OwnerDocumentLookup = .empty,
|
_node_owner_documents: Node.OwnerDocumentLookup = .empty,
|
||||||
_element_assigned_slots: Element.AssignedSlotLookup = .empty,
|
_element_assigned_slots: Element.AssignedSlotLookup = .empty,
|
||||||
|
_element_scroll_positions: Element.ScrollPositionLookup = .empty,
|
||||||
|
|
||||||
/// Lazily-created inline event listeners (or listeners provided as attributes).
|
/// Lazily-created inline event listeners (or listeners provided as attributes).
|
||||||
/// Avoids bloating all elements with extra function fields for rare usage.
|
/// Avoids bloating all elements with extra function fields for rare usage.
|
||||||
|
|||||||
116
src/browser/tests/element/position.html
Normal file
116
src/browser/tests/element/position.html
Normal file
@@ -0,0 +1,116 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<script src="../testing.js"></script>
|
||||||
|
|
||||||
|
<div id="test1">Test Element</div>
|
||||||
|
<div id="test2">Another Element</div>
|
||||||
|
|
||||||
|
<script id="clientDimensions">
|
||||||
|
{
|
||||||
|
const test1 = $('#test1');
|
||||||
|
|
||||||
|
// clientWidth/Height - default is 5px in dummy layout
|
||||||
|
testing.expectEqual('number', typeof test1.clientWidth);
|
||||||
|
testing.expectEqual('number', typeof test1.clientHeight);
|
||||||
|
testing.expectTrue(test1.clientWidth >= 0);
|
||||||
|
testing.expectTrue(test1.clientHeight >= 0);
|
||||||
|
|
||||||
|
// clientTop/Left should be 0 (no borders in dummy layout)
|
||||||
|
testing.expectEqual(0, test1.clientTop);
|
||||||
|
testing.expectEqual(0, test1.clientLeft);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script id="scrollDimensions">
|
||||||
|
{
|
||||||
|
const test1 = $('#test1');
|
||||||
|
|
||||||
|
// In dummy layout, scroll dimensions equal client dimensions (no overflow)
|
||||||
|
testing.expectEqual(test1.clientWidth, test1.scrollWidth);
|
||||||
|
testing.expectEqual(test1.clientHeight, test1.scrollHeight);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script id="scrollPosition">
|
||||||
|
{
|
||||||
|
const test1 = $('#test1');
|
||||||
|
|
||||||
|
// Initial scroll position should be 0
|
||||||
|
testing.expectEqual(0, test1.scrollTop);
|
||||||
|
testing.expectEqual(0, test1.scrollLeft);
|
||||||
|
|
||||||
|
// Setting scroll position
|
||||||
|
test1.scrollTop = 50;
|
||||||
|
testing.expectEqual(50, test1.scrollTop);
|
||||||
|
|
||||||
|
test1.scrollLeft = 25;
|
||||||
|
testing.expectEqual(25, test1.scrollLeft);
|
||||||
|
|
||||||
|
// Negative values should be clamped to 0
|
||||||
|
test1.scrollTop = -10;
|
||||||
|
testing.expectEqual(0, test1.scrollTop);
|
||||||
|
|
||||||
|
test1.scrollLeft = -5;
|
||||||
|
testing.expectEqual(0, test1.scrollLeft);
|
||||||
|
|
||||||
|
// Each element has independent scroll position
|
||||||
|
const test2 = $('#test2');
|
||||||
|
testing.expectEqual(0, test2.scrollTop);
|
||||||
|
testing.expectEqual(0, test2.scrollLeft);
|
||||||
|
|
||||||
|
test2.scrollTop = 100;
|
||||||
|
testing.expectEqual(100, test2.scrollTop);
|
||||||
|
testing.expectEqual(0, test1.scrollTop); // test1 should still be 0
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script id="offsetDimensions">
|
||||||
|
{
|
||||||
|
const test1 = $('#test1');
|
||||||
|
|
||||||
|
// offsetWidth/Height should be numbers
|
||||||
|
testing.expectEqual('number', typeof test1.offsetWidth);
|
||||||
|
testing.expectEqual('number', typeof test1.offsetHeight);
|
||||||
|
testing.expectTrue(test1.offsetWidth >= 0);
|
||||||
|
testing.expectTrue(test1.offsetHeight >= 0);
|
||||||
|
|
||||||
|
// Should equal client dimensions
|
||||||
|
testing.expectEqual(test1.clientWidth, test1.offsetWidth);
|
||||||
|
testing.expectEqual(test1.clientHeight, test1.offsetHeight);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script id="offsetPosition">
|
||||||
|
{
|
||||||
|
const test1 = $('#test1');
|
||||||
|
const test2 = $('#test2');
|
||||||
|
|
||||||
|
// offsetTop/Left should be calculated from tree position
|
||||||
|
// These values are based on the heuristic layout engine
|
||||||
|
const top1 = test1.offsetTop;
|
||||||
|
const left1 = test1.offsetLeft;
|
||||||
|
const top2 = test2.offsetTop;
|
||||||
|
const left2 = test2.offsetLeft;
|
||||||
|
|
||||||
|
// Position values should be numbers
|
||||||
|
testing.expectEqual('number', typeof top1);
|
||||||
|
testing.expectEqual('number', typeof left1);
|
||||||
|
testing.expectEqual('number', typeof top2);
|
||||||
|
testing.expectEqual('number', typeof left2);
|
||||||
|
|
||||||
|
// Siblings should have different positions (either different x or y)
|
||||||
|
testing.expectTrue(top1 !== top2 || left1 !== left2);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script id="offsetVsBounding">
|
||||||
|
{
|
||||||
|
const test1 = $('#test1');
|
||||||
|
|
||||||
|
// offsetTop/Left should match getBoundingClientRect
|
||||||
|
const rect = test1.getBoundingClientRect();
|
||||||
|
testing.expectEqual(rect.y, test1.offsetTop);
|
||||||
|
testing.expectEqual(rect.x, test1.offsetLeft);
|
||||||
|
testing.expectEqual(rect.width, test1.offsetWidth);
|
||||||
|
testing.expectEqual(rect.height, test1.offsetHeight);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
@@ -49,6 +49,12 @@ pub const RelListLookup = std.AutoHashMapUnmanaged(*Element, *collections.DOMTok
|
|||||||
pub const ShadowRootLookup = std.AutoHashMapUnmanaged(*Element, *ShadowRoot);
|
pub const ShadowRootLookup = std.AutoHashMapUnmanaged(*Element, *ShadowRoot);
|
||||||
pub const AssignedSlotLookup = std.AutoHashMapUnmanaged(*Element, *Html.Slot);
|
pub const AssignedSlotLookup = std.AutoHashMapUnmanaged(*Element, *Html.Slot);
|
||||||
|
|
||||||
|
pub const ScrollPosition = struct {
|
||||||
|
x: u32 = 0,
|
||||||
|
y: u32 = 0,
|
||||||
|
};
|
||||||
|
pub const ScrollPositionLookup = std.AutoHashMapUnmanaged(*Element, ScrollPosition);
|
||||||
|
|
||||||
pub const Namespace = enum(u8) {
|
pub const Namespace = enum(u8) {
|
||||||
html,
|
html,
|
||||||
svg,
|
svg,
|
||||||
@@ -1027,6 +1033,82 @@ pub fn getClientRects(self: *Element, page: *Page) ![]DOMRect {
|
|||||||
return ptr[0..1];
|
return ptr[0..1];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn getScrollTop(self: *Element, page: *Page) u32 {
|
||||||
|
const pos = page._element_scroll_positions.get(self) orelse return 0;
|
||||||
|
return pos.y;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn setScrollTop(self: *Element, value: i32, page: *Page) !void {
|
||||||
|
const gop = try page._element_scroll_positions.getOrPut(page.arena, self);
|
||||||
|
if (!gop.found_existing) {
|
||||||
|
gop.value_ptr.* = .{};
|
||||||
|
}
|
||||||
|
gop.value_ptr.y = @intCast(@max(0, value));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn getScrollLeft(self: *Element, page: *Page) u32 {
|
||||||
|
const pos = page._element_scroll_positions.get(self) orelse return 0;
|
||||||
|
return pos.x;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn setScrollLeft(self: *Element, value: i32, page: *Page) !void {
|
||||||
|
const gop = try page._element_scroll_positions.getOrPut(page.arena, self);
|
||||||
|
if (!gop.found_existing) {
|
||||||
|
gop.value_ptr.* = .{};
|
||||||
|
}
|
||||||
|
gop.value_ptr.x = @intCast(@max(0, value));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn getScrollHeight(self: *Element, page: *Page) !f64 {
|
||||||
|
// In our dummy layout engine, content doesn't overflow
|
||||||
|
return self.getClientHeight(page);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn getScrollWidth(self: *Element, page: *Page) !f64 {
|
||||||
|
// In our dummy layout engine, content doesn't overflow
|
||||||
|
return self.getClientWidth(page);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn getOffsetHeight(self: *Element, page: *Page) !f64 {
|
||||||
|
if (!try self.checkVisibility(page)) {
|
||||||
|
return 0.0;
|
||||||
|
}
|
||||||
|
const dims = try self.getElementDimensions(page);
|
||||||
|
return dims.height;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn getOffsetWidth(self: *Element, page: *Page) !f64 {
|
||||||
|
if (!try self.checkVisibility(page)) {
|
||||||
|
return 0.0;
|
||||||
|
}
|
||||||
|
const dims = try self.getElementDimensions(page);
|
||||||
|
return dims.width;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn getOffsetTop(self: *Element, page: *Page) !f64 {
|
||||||
|
if (!try self.checkVisibility(page)) {
|
||||||
|
return 0.0;
|
||||||
|
}
|
||||||
|
return calculateDocumentPosition(self.asNode());
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn getOffsetLeft(self: *Element, page: *Page) !f64 {
|
||||||
|
if (!try self.checkVisibility(page)) {
|
||||||
|
return 0.0;
|
||||||
|
}
|
||||||
|
return calculateSiblingPosition(self.asNode());
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn getClientTop(_: *Element) f64 {
|
||||||
|
// Border width - in our dummy layout, we don't apply borders to layout
|
||||||
|
return 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn getClientLeft(_: *Element) f64 {
|
||||||
|
// Border width - in our dummy layout, we don't apply borders to layout
|
||||||
|
return 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
// Calculates document position by counting all nodes that appear before this one
|
// Calculates document position by counting all nodes that appear before this one
|
||||||
// in tree order, but only traversing the "left side" of the tree.
|
// in tree order, but only traversing the "left side" of the tree.
|
||||||
//
|
//
|
||||||
@@ -1502,6 +1584,16 @@ pub const JsApi = struct {
|
|||||||
pub const checkVisibility = bridge.function(Element.checkVisibility, .{});
|
pub const checkVisibility = bridge.function(Element.checkVisibility, .{});
|
||||||
pub const clientWidth = bridge.accessor(Element.getClientWidth, null, .{});
|
pub const clientWidth = bridge.accessor(Element.getClientWidth, null, .{});
|
||||||
pub const clientHeight = bridge.accessor(Element.getClientHeight, null, .{});
|
pub const clientHeight = bridge.accessor(Element.getClientHeight, null, .{});
|
||||||
|
pub const clientTop = bridge.accessor(Element.getClientTop, null, .{});
|
||||||
|
pub const clientLeft = bridge.accessor(Element.getClientLeft, null, .{});
|
||||||
|
pub const scrollTop = bridge.accessor(Element.getScrollTop, Element.setScrollTop, .{});
|
||||||
|
pub const scrollLeft = bridge.accessor(Element.getScrollLeft, Element.setScrollLeft, .{});
|
||||||
|
pub const scrollHeight = bridge.accessor(Element.getScrollHeight, null, .{});
|
||||||
|
pub const scrollWidth = bridge.accessor(Element.getScrollWidth, null, .{});
|
||||||
|
pub const offsetTop = bridge.accessor(Element.getOffsetTop, null, .{});
|
||||||
|
pub const offsetLeft = bridge.accessor(Element.getOffsetLeft, null, .{});
|
||||||
|
pub const offsetWidth = bridge.accessor(Element.getOffsetWidth, null, .{});
|
||||||
|
pub const offsetHeight = bridge.accessor(Element.getOffsetHeight, null, .{});
|
||||||
pub const getClientRects = bridge.function(Element.getClientRects, .{});
|
pub const getClientRects = bridge.function(Element.getClientRects, .{});
|
||||||
pub const getBoundingClientRect = bridge.function(Element.getBoundingClientRect, .{});
|
pub const getBoundingClientRect = bridge.function(Element.getBoundingClientRect, .{});
|
||||||
pub const getElementsByTagName = bridge.function(Element.getElementsByTagName, .{});
|
pub const getElementsByTagName = bridge.function(Element.getElementsByTagName, .{});
|
||||||
|
|||||||
Reference in New Issue
Block a user