headless_chrome/browser/tab/element/
box_model.rs

1use crate::protocol::cdp::Page;
2
3use crate::browser::tab::point::Point;
4
5#[derive(Debug, Copy, Clone)]
6pub struct ElementQuad {
7    pub top_left: Point,
8    pub top_right: Point,
9    pub bottom_left: Point,
10    pub bottom_right: Point,
11}
12
13impl ElementQuad {
14    pub fn from_raw_points(raw_quad: &[f64]) -> Self {
15        Self {
16            top_left: Point {
17                x: raw_quad[0],
18                y: raw_quad[1],
19            },
20            top_right: Point {
21                x: raw_quad[2],
22                y: raw_quad[3],
23            },
24            bottom_right: Point {
25                x: raw_quad[4],
26                y: raw_quad[5],
27            },
28            bottom_left: Point {
29                x: raw_quad[6],
30                y: raw_quad[7],
31            },
32        }
33    }
34
35    pub fn height(&self) -> f64 {
36        self.bottom_left.y - self.top_left.y
37    }
38
39    pub fn width(&self) -> f64 {
40        self.top_right.x - self.top_left.x
41    }
42
43    /// The width divided by the height
44    pub fn aspect_ratio(&self) -> f64 {
45        self.width() / self.height()
46    }
47
48    /// The most left (smallest) x-coordinate
49    pub fn most_left(&self) -> f64 {
50        self.top_right
51            .x
52            .min(self.top_left.x)
53            .min(self.bottom_right.x)
54            .min(self.bottom_left.x)
55    }
56
57    /// The most right (largest) x-coordinate
58    pub fn most_right(&self) -> f64 {
59        self.top_right
60            .x
61            .max(self.top_left.x)
62            .max(self.bottom_right.x)
63            .max(self.bottom_left.x)
64    }
65
66    /// The most top (smallest) y-coordinate
67    pub fn most_top(&self) -> f64 {
68        self.top_right
69            .y
70            .min(self.top_left.y)
71            .min(self.bottom_right.y)
72            .min(self.bottom_left.y)
73    }
74
75    /// The most bottom (largest) y-coordinate
76    fn most_bottom(&self) -> f64 {
77        self.top_right
78            .y
79            .max(self.top_left.y)
80            .max(self.bottom_right.y)
81            .max(self.bottom_left.y)
82    }
83
84    /// If the most bottom point of `self` is above the most top point of `other`
85    pub fn strictly_above(&self, other: &Self) -> bool {
86        self.most_bottom() < other.most_top()
87    }
88
89    /// If the most bottom point of `self` is above or on the same line as the
90    /// most top point of `other`
91    pub fn above(&self, other: &Self) -> bool {
92        self.most_bottom() <= other.most_top()
93    }
94
95    /// If the most top point of `self` is below the most bottom point of `other`
96    pub fn strictly_below(&self, other: &Self) -> bool {
97        self.most_top() > other.most_bottom()
98    }
99
100    /// If the most top point of `self` is below or on the same line as the
101    /// most bottom point of `other`
102    pub fn below(&self, other: &Self) -> bool {
103        self.most_top() >= other.most_bottom()
104    }
105
106    /// If the most right point of `self` is left of the most left point of `other`
107    pub fn strictly_left_of(&self, other: &Self) -> bool {
108        self.most_right() < other.most_left()
109    }
110
111    /// If the most right point of `self` is left or on the same line as the
112    /// most left point of `other`
113    pub fn left_of(&self, other: &Self) -> bool {
114        self.most_right() <= other.most_left()
115    }
116
117    /// If the most left point of `self` is right of the most right point of `other`
118    pub fn strictly_right_of(&self, other: &Self) -> bool {
119        self.most_left() > other.most_right()
120    }
121
122    /// If the most left point of `self` is right or on the same line as the
123    /// most right point of `other`
124    pub fn right_of(&self, other: &Self) -> bool {
125        self.most_left() >= other.most_right()
126    }
127
128    /// If `self` is within the left/right boundaries defined by `other`.
129    pub fn within_horizontal_bounds_of(&self, other: &Self) -> bool {
130        self.most_left() >= other.most_left() && self.most_right() <= other.most_right()
131    }
132
133    /// If `self` is within the top/bottom boundaries defined by `other`.
134    pub fn within_vertical_bounds_of(&self, other: &Self) -> bool {
135        self.most_top() >= other.most_top() && self.most_bottom() <= other.most_bottom()
136    }
137
138    /// If `self` is within the boundaries defined by `other`.
139    pub fn within_bounds_of(&self, other: &Self) -> bool {
140        self.within_horizontal_bounds_of(other) && self.within_vertical_bounds_of(other)
141    }
142}
143
144#[derive(Debug, Clone)]
145pub struct BoxModel {
146    pub content: ElementQuad,
147    pub padding: ElementQuad,
148    pub border: ElementQuad,
149    pub margin: ElementQuad,
150    pub width: f64,
151    pub height: f64,
152}
153
154impl BoxModel {
155    /// Create a `page::Viewport` equal to the content-box, using a scale of 1.0
156    pub fn content_viewport(&self) -> Page::Viewport {
157        Page::Viewport {
158            x: self.content.top_left.x,
159            y: self.content.top_left.y,
160            width: self.content.width(),
161            height: self.content.height(),
162            scale: 1.0,
163        }
164    }
165
166    /// Create a `page::Viewport` equal to the padding-box, using a scale of 1.0
167    pub fn padding_viewport(&self) -> Page::Viewport {
168        Page::Viewport {
169            x: self.padding.top_left.x,
170            y: self.padding.top_left.y,
171            width: self.padding.width(),
172            height: self.padding.height(),
173            scale: 1.0,
174        }
175    }
176
177    /// Create a `page::Viewport` equal to the border-box, using a scale of 1.0
178    pub fn border_viewport(&self) -> Page::Viewport {
179        Page::Viewport {
180            x: self.border.top_left.x,
181            y: self.border.top_left.y,
182            width: self.border.width(),
183            height: self.border.height(),
184            scale: 1.0,
185        }
186    }
187
188    /// Create a `page::Viewport` equal to the margin-box, using a scale of 1.0
189    pub fn margin_viewport(&self) -> Page::Viewport {
190        Page::Viewport {
191            x: self.margin.top_left.x,
192            y: self.margin.top_left.y,
193            width: self.margin.width(),
194            height: self.margin.height(),
195            scale: 1.0,
196        }
197    }
198}