mirror of
https://github.com/lightpanda-io/browser.git
synced 2025-12-14 23:38:57 +00:00
Element.checkVisibility and Element.checkVisibility
This commit is contained in:
@@ -488,6 +488,7 @@ pub const JsApis = flattenTypes(&.{
|
|||||||
@import("../webapi/DOMImplementation.zig"),
|
@import("../webapi/DOMImplementation.zig"),
|
||||||
@import("../webapi/DOMTreeWalker.zig"),
|
@import("../webapi/DOMTreeWalker.zig"),
|
||||||
@import("../webapi/DOMNodeIterator.zig"),
|
@import("../webapi/DOMNodeIterator.zig"),
|
||||||
|
@import("../webapi/DOMRect.zig"),
|
||||||
@import("../webapi/NodeFilter.zig"),
|
@import("../webapi/NodeFilter.zig"),
|
||||||
@import("../webapi/Element.zig"),
|
@import("../webapi/Element.zig"),
|
||||||
@import("../webapi/element/DOMStringMap.zig"),
|
@import("../webapi/element/DOMStringMap.zig"),
|
||||||
|
|||||||
64
src/browser/webapi/DOMRect.zig
Normal file
64
src/browser/webapi/DOMRect.zig
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
const DOMRect = @This();
|
||||||
|
|
||||||
|
const js = @import("../js/js.zig");
|
||||||
|
const Page = @import("../Page.zig");
|
||||||
|
|
||||||
|
_x: f64,
|
||||||
|
_y: f64,
|
||||||
|
_width: f64,
|
||||||
|
_height: f64,
|
||||||
|
_top: f64,
|
||||||
|
_right: f64,
|
||||||
|
_bottom: f64,
|
||||||
|
_left: f64,
|
||||||
|
|
||||||
|
pub fn getX(self: *DOMRect) f64 {
|
||||||
|
return self._x;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn getY(self: *DOMRect) f64 {
|
||||||
|
return self._y;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn getWidth(self: *DOMRect) f64 {
|
||||||
|
return self._width;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn getHeight(self: *DOMRect) f64 {
|
||||||
|
return self._height;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn getTop(self: *DOMRect) f64 {
|
||||||
|
return self._top;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn getRight(self: *DOMRect) f64 {
|
||||||
|
return self._right;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn getBottom(self: *DOMRect) f64 {
|
||||||
|
return self._bottom;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn getLeft(self: *DOMRect) f64 {
|
||||||
|
return self._left;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const JsApi = struct {
|
||||||
|
pub const bridge = js.Bridge(DOMRect);
|
||||||
|
|
||||||
|
pub const Meta = struct {
|
||||||
|
pub const name = "DOMRect";
|
||||||
|
pub const prototype_chain = bridge.prototypeChain();
|
||||||
|
pub var class_id: bridge.ClassId = undefined;
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const x = bridge.accessor(DOMRect.getX, null, .{});
|
||||||
|
pub const y = bridge.accessor(DOMRect.getY, null, .{});
|
||||||
|
pub const width = bridge.accessor(DOMRect.getWidth, null, .{});
|
||||||
|
pub const height = bridge.accessor(DOMRect.getHeight, null, .{});
|
||||||
|
pub const top = bridge.accessor(DOMRect.getTop, null, .{});
|
||||||
|
pub const right = bridge.accessor(DOMRect.getRight, null, .{});
|
||||||
|
pub const bottom = bridge.accessor(DOMRect.getBottom, null, .{});
|
||||||
|
pub const left = bridge.accessor(DOMRect.getLeft, null, .{});
|
||||||
|
};
|
||||||
@@ -13,6 +13,8 @@ const Selector = @import("selector/Selector.zig");
|
|||||||
pub const Attribute = @import("element/Attribute.zig");
|
pub const Attribute = @import("element/Attribute.zig");
|
||||||
const CSSStyleProperties = @import("css/CSSStyleProperties.zig");
|
const CSSStyleProperties = @import("css/CSSStyleProperties.zig");
|
||||||
pub const DOMStringMap = @import("element/DOMStringMap.zig");
|
pub const DOMStringMap = @import("element/DOMStringMap.zig");
|
||||||
|
const DOMRect = @import("DOMRect.zig");
|
||||||
|
const css = @import("css.zig");
|
||||||
|
|
||||||
pub const Svg = @import("element/Svg.zig");
|
pub const Svg = @import("element/Svg.zig");
|
||||||
pub const Html = @import("element/Html.zig");
|
pub const Html = @import("element/Html.zig");
|
||||||
@@ -467,6 +469,126 @@ pub fn querySelectorAll(self: *Element, input: []const u8, page: *Page) !*Select
|
|||||||
return Selector.querySelectorAll(self.asNode(), input, page);
|
return Selector.querySelectorAll(self.asNode(), input, page);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn parentElement(self: *Element) ?*Element {
|
||||||
|
return self._proto.parentElement();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn checkVisibility(self: *Element, page: *Page) !bool {
|
||||||
|
var current: ?*Element = self;
|
||||||
|
|
||||||
|
while (current) |el| {
|
||||||
|
const style = try el.getStyle(page);
|
||||||
|
const display = style.asCSSStyleDeclaration().getPropertyValue("display", page);
|
||||||
|
if (std.mem.eql(u8, display, "none")) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
current = el.parentElement();
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn getBoundingClientRect(self: *Element, page: *Page) !*DOMRect {
|
||||||
|
const is_visible = try self.checkVisibility(page);
|
||||||
|
if (!is_visible) {
|
||||||
|
return page._factory.create(DOMRect{
|
||||||
|
._x = 0.0,
|
||||||
|
._y = 0.0,
|
||||||
|
._width = 0.0,
|
||||||
|
._height = 0.0,
|
||||||
|
._top = 0.0,
|
||||||
|
._right = 0.0,
|
||||||
|
._bottom = 0.0,
|
||||||
|
._left = 0.0,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const y = calculateDocumentPosition(self.asNode());
|
||||||
|
|
||||||
|
var width: f64 = 1.0;
|
||||||
|
var height: f64 = 1.0;
|
||||||
|
|
||||||
|
const style = try self.getStyle(page);
|
||||||
|
const decl = style.asCSSStyleDeclaration();
|
||||||
|
width = css.parseDimension(decl.getPropertyValue("width", page)) orelse 1.0;
|
||||||
|
height = css.parseDimension(decl.getPropertyValue("height", page)) orelse 1.0;
|
||||||
|
|
||||||
|
if (width == 1.0 or height == 1.0) {
|
||||||
|
const tag = self.getTag();
|
||||||
|
if (tag == .img or tag == .iframe) {
|
||||||
|
if (self.getAttributeSafe("width")) |w| {
|
||||||
|
width = std.fmt.parseFloat(f64, w) catch width;
|
||||||
|
}
|
||||||
|
if (self.getAttributeSafe("height")) |h| {
|
||||||
|
height = std.fmt.parseFloat(f64, h) catch height;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const x: f64 = 0.0;
|
||||||
|
const top = y;
|
||||||
|
const left = x;
|
||||||
|
const right = x + width;
|
||||||
|
const bottom = y + height;
|
||||||
|
|
||||||
|
return page._factory.create(DOMRect{
|
||||||
|
._x = x,
|
||||||
|
._y = y,
|
||||||
|
._width = width,
|
||||||
|
._height = height,
|
||||||
|
._top = top,
|
||||||
|
._right = right,
|
||||||
|
._bottom = bottom,
|
||||||
|
._left = left,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculates a pseudo-position in the document using an efficient heuristic.
|
||||||
|
//
|
||||||
|
// Instead of walking the entire DOM tree (which would be O(total_nodes)), this
|
||||||
|
// function walks UP the tree counting previous siblings at each level. Each level
|
||||||
|
// uses exponential weighting (1000x per depth level) to preserve document order.
|
||||||
|
//
|
||||||
|
// This gives O(depth * avg_siblings) complexity while maintaining relative positioning
|
||||||
|
// that's useful for scraping and understanding element flow in the document.
|
||||||
|
//
|
||||||
|
// Example:
|
||||||
|
// <body> → position 0
|
||||||
|
// <div> → position 0 (0 siblings at level 1)
|
||||||
|
// <span></span> → position 0 (0 siblings at level 2)
|
||||||
|
// <span></span> → position 1 (1 sibling at level 2)
|
||||||
|
// </div>
|
||||||
|
// <div> → position 1000 (1 sibling at level 1, weighted by 1000)
|
||||||
|
// <p></p> → position 1000 (0 siblings at level 2, parent has 1000)
|
||||||
|
// </div>
|
||||||
|
// </body>
|
||||||
|
//
|
||||||
|
// Trade-offs:
|
||||||
|
// - Much faster than full tree-walking for deep/large DOMs
|
||||||
|
// - Positions reflect document order and parent-child relationships
|
||||||
|
// - Not pixel-accurate, but sufficient for 1x1 layout heuristics
|
||||||
|
fn calculateDocumentPosition(node: *Node) f64 {
|
||||||
|
var position: f64 = 0.0;
|
||||||
|
var multiplier: f64 = 1.0;
|
||||||
|
var current = node;
|
||||||
|
|
||||||
|
while (current.parentNode()) |parent| {
|
||||||
|
var count: f64 = 0.0;
|
||||||
|
var sibling = parent.firstChild();
|
||||||
|
while (sibling) |s| {
|
||||||
|
if (s == current) break;
|
||||||
|
count += 1.0;
|
||||||
|
sibling = s.nextSibling();
|
||||||
|
}
|
||||||
|
|
||||||
|
position += count * multiplier;
|
||||||
|
multiplier *= 1000.0;
|
||||||
|
current = parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
return position;
|
||||||
|
}
|
||||||
|
|
||||||
const GetElementsByTagNameResult = union(enum) {
|
const GetElementsByTagNameResult = union(enum) {
|
||||||
tag: collections.NodeLive(.tag),
|
tag: collections.NodeLive(.tag),
|
||||||
tag_name: collections.NodeLive(.tag_name),
|
tag_name: collections.NodeLive(.tag_name),
|
||||||
@@ -702,6 +824,8 @@ pub const JsApi = struct {
|
|||||||
pub const matches = bridge.function(Element.matches, .{ .dom_exception = true });
|
pub const matches = bridge.function(Element.matches, .{ .dom_exception = true });
|
||||||
pub const querySelector = bridge.function(Element.querySelector, .{ .dom_exception = true });
|
pub const querySelector = bridge.function(Element.querySelector, .{ .dom_exception = true });
|
||||||
pub const querySelectorAll = bridge.function(Element.querySelectorAll, .{ .dom_exception = true });
|
pub const querySelectorAll = bridge.function(Element.querySelectorAll, .{ .dom_exception = true });
|
||||||
|
pub const checkVisibility = bridge.function(Element.checkVisibility, .{});
|
||||||
|
pub const getBoundingClientRect = bridge.function(Element.getBoundingClientRect, .{});
|
||||||
pub const getElementsByTagName = bridge.function(Element.getElementsByTagName, .{});
|
pub const getElementsByTagName = bridge.function(Element.getElementsByTagName, .{});
|
||||||
pub const getElementsByClassName = bridge.function(Element.getElementsByClassName, .{});
|
pub const getElementsByClassName = bridge.function(Element.getElementsByClassName, .{});
|
||||||
pub const children = bridge.accessor(Element.getChildren, null, .{});
|
pub const children = bridge.accessor(Element.getChildren, null, .{});
|
||||||
|
|||||||
14
src/browser/webapi/css.zig
Normal file
14
src/browser/webapi/css.zig
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
const std = @import("std");
|
||||||
|
|
||||||
|
pub fn parseDimension(value: []const u8) ?f64 {
|
||||||
|
if (value.len == 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var num_str = value;
|
||||||
|
if (std.mem.endsWith(u8, value, "px")) {
|
||||||
|
num_str = value[0 .. value.len - 2];
|
||||||
|
}
|
||||||
|
|
||||||
|
return std.fmt.parseFloat(f64, num_str) catch null;
|
||||||
|
}
|
||||||
@@ -57,13 +57,13 @@ pub fn item(self: *const CSSStyleDeclaration, index: u32) []const u8 {
|
|||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn getPropertyValue(self: *const CSSStyleDeclaration, property_name: []const u8, page: *Page) ![]const u8 {
|
pub fn getPropertyValue(self: *const CSSStyleDeclaration, property_name: []const u8, page: *Page) []const u8 {
|
||||||
const normalized = normalizePropertyName(property_name, &page.buf);
|
const normalized = normalizePropertyName(property_name, &page.buf);
|
||||||
const prop = self.findProperty(normalized) orelse return "";
|
const prop = self.findProperty(normalized) orelse return "";
|
||||||
return prop._value.str();
|
return prop._value.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn getPropertyPriority(self: *const CSSStyleDeclaration, property_name: []const u8, page: *Page) ![]const u8 {
|
pub fn getPropertyPriority(self: *const CSSStyleDeclaration, property_name: []const u8, page: *Page) []const u8 {
|
||||||
const normalized = normalizePropertyName(property_name, &page.buf);
|
const normalized = normalizePropertyName(property_name, &page.buf);
|
||||||
const prop = self.findProperty(normalized) orelse return "";
|
const prop = self.findProperty(normalized) orelse return "";
|
||||||
return if (prop._important) "important" else "";
|
return if (prop._important) "important" else "";
|
||||||
|
|||||||
@@ -154,7 +154,7 @@ pub const JsApi = struct {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const value = try self._proto.getPropertyValue(dash_case, page);
|
const value = self._proto.getPropertyValue(dash_case, page);
|
||||||
|
|
||||||
// Property accessors have special handling for empty values:
|
// Property accessors have special handling for empty values:
|
||||||
// - Known CSS properties return '' when not set
|
// - Known CSS properties return '' when not set
|
||||||
|
|||||||
Reference in New Issue
Block a user