1use crate::box_model::BoxModel;
6use crate::fragment::Fragment;
7use crate::geometry::{Point, Size};
8use crate::style::{ComputedStyle, Position};
9use crate::tree::BoxTree;
10
11pub fn resolve_positioned(tree: &BoxTree, mut root: Fragment, viewport: Size) -> Fragment {
18 resolve_fragment(tree, &mut root, viewport, viewport);
19 root
20}
21
22fn resolve_fragment(
23 tree: &BoxTree,
24 fragment: &mut Fragment,
25 containing_block_size: Size,
26 viewport: Size,
27) {
28 let style = tree.style(fragment.node);
29
30 match style.position {
31 Position::Relative => {
32 apply_relative_offset(fragment, style, containing_block_size);
33 }
34 Position::Absolute => {
35 resolve_absolute_position(fragment, style, containing_block_size);
36 }
37 Position::Fixed => {
38 resolve_absolute_position(fragment, style, viewport);
39 }
40 Position::Sticky => {
41 apply_relative_offset(fragment, style, containing_block_size);
43 }
44 Position::Static => {}
45 }
46
47 let child_cb = if style.position.is_positioned() || style.position == Position::Static {
49 Size::new(
52 fragment.size.width + fragment.padding.horizontal() + fragment.border.horizontal(),
53 fragment.size.height + fragment.padding.vertical() + fragment.border.vertical(),
54 )
55 } else {
56 containing_block_size
57 };
58
59 for child in &mut fragment.children {
61 resolve_fragment(tree, child, child_cb, viewport);
62 }
63}
64
65fn apply_relative_offset(fragment: &mut Fragment, style: &ComputedStyle, cb_size: Size) {
68 let dx = resolve_offset_pair(&style.left, &style.right, cb_size.width);
69 let dy = resolve_offset_pair(&style.top, &style.bottom, cb_size.height);
70
71 fragment.position.x += dx;
72 fragment.position.y += dy;
73}
74
75fn resolve_absolute_position(fragment: &mut Fragment, style: &ComputedStyle, cb_size: Size) {
78 let border = BoxModel::resolve_border(style);
79 let padding = BoxModel::resolve_padding(style, cb_size.width);
80
81 let (x, width) = resolve_absolute_axis(
83 &style.left,
84 &style.right,
85 &style.width,
86 &style.margin_left,
87 &style.margin_right,
88 border.left + padding.left,
89 border.right + padding.right,
90 cb_size.width,
91 fragment.size.width,
92 );
93
94 let (y, height) = resolve_absolute_axis(
96 &style.top,
97 &style.bottom,
98 &style.height,
99 &style.margin_top,
100 &style.margin_bottom,
101 border.top + padding.top,
102 border.bottom + padding.bottom,
103 cb_size.height,
104 fragment.size.height,
105 );
106
107 fragment.position = Point::new(x, y);
108 if width >= 0.0 {
109 fragment.size.width = width;
110 }
111 if height >= 0.0 {
112 fragment.size.height = height;
113 }
114 fragment.border = border;
115 fragment.padding = padding;
116}
117
118fn resolve_absolute_axis(
126 start: &crate::values::LengthPercentageAuto,
127 end: &crate::values::LengthPercentageAuto,
128 size: &crate::values::LengthPercentageAuto,
129 margin_start: &crate::values::LengthPercentageAuto,
130 margin_end: &crate::values::LengthPercentageAuto,
131 border_padding_start: f32,
132 border_padding_end: f32,
133 cb_size: f32,
134 intrinsic_size: f32,
135) -> (f32, f32) {
136 let start_val = start.resolve(cb_size);
137 let end_val = end.resolve(cb_size);
138 let size_val = size.resolve(cb_size);
139 let ms = margin_start.resolve(cb_size).unwrap_or(0.0);
140 let me = margin_end.resolve(cb_size).unwrap_or(0.0);
141
142 match (start_val, end_val, size_val) {
143 (Some(s), Some(_e), Some(w)) => {
145 let pos = s + ms;
146 (pos, w)
147 }
148 (Some(s), _, Some(w)) => {
150 let pos = s + ms;
151 (pos, w)
152 }
153 (_, Some(e), Some(w)) => {
155 let pos = cb_size - e - me - w - border_padding_start - border_padding_end;
156 (pos, w)
157 }
158 (Some(s), Some(e), None) => {
160 let pos = s + ms;
161 let w = cb_size - s - e - ms - me - border_padding_start - border_padding_end;
162 (pos, w.max(0.0))
163 }
164 (Some(s), None, None) => {
166 let pos = s + ms;
167 (pos, intrinsic_size)
168 }
169 (None, Some(e), None) => {
171 let pos = cb_size - e - me - intrinsic_size - border_padding_start - border_padding_end;
172 (pos, intrinsic_size)
173 }
174 (None, None, Some(w)) => (ms, w),
176 (None, None, None) => (ms, intrinsic_size),
178 }
179}
180
181fn resolve_offset_pair(
184 start: &crate::values::LengthPercentageAuto,
185 end: &crate::values::LengthPercentageAuto,
186 reference: f32,
187) -> f32 {
188 let s = start.resolve(reference);
189 let e = end.resolve(reference);
190
191 match (s, e) {
192 (Some(sv), _) => sv, (None, Some(ev)) => -ev, (None, None) => 0.0,
195 }
196}
197
198#[cfg(test)]
199mod tests {
200 use super::*;
201 use crate::values::LengthPercentageAuto;
202
203 #[test]
204 fn test_relative_offset_left() {
205 let left = LengthPercentageAuto::px(10.0);
206 let right = LengthPercentageAuto::Auto;
207 assert_eq!(resolve_offset_pair(&left, &right, 800.0), 10.0);
208 }
209
210 #[test]
211 fn test_relative_offset_right() {
212 let left = LengthPercentageAuto::Auto;
213 let right = LengthPercentageAuto::px(10.0);
214 assert_eq!(resolve_offset_pair(&left, &right, 800.0), -10.0);
215 }
216
217 #[test]
218 fn test_relative_offset_both_start_wins() {
219 let left = LengthPercentageAuto::px(20.0);
220 let right = LengthPercentageAuto::px(10.0);
221 assert_eq!(resolve_offset_pair(&left, &right, 800.0), 20.0);
222 }
223
224 #[test]
225 fn test_absolute_position_left_top() {
226 let (x, w) = resolve_absolute_axis(
227 &LengthPercentageAuto::px(10.0),
228 &LengthPercentageAuto::Auto,
229 &LengthPercentageAuto::px(200.0),
230 &LengthPercentageAuto::px(0.0),
231 &LengthPercentageAuto::px(0.0),
232 0.0,
233 0.0,
234 800.0,
235 0.0,
236 );
237 assert_eq!(x, 10.0);
238 assert_eq!(w, 200.0);
239 }
240
241 #[test]
242 fn test_absolute_position_stretch() {
243 let (x, w) = resolve_absolute_axis(
244 &LengthPercentageAuto::px(10.0),
245 &LengthPercentageAuto::px(10.0),
246 &LengthPercentageAuto::Auto,
247 &LengthPercentageAuto::px(0.0),
248 &LengthPercentageAuto::px(0.0),
249 0.0,
250 0.0,
251 800.0,
252 0.0,
253 );
254 assert_eq!(x, 10.0);
255 assert_eq!(w, 780.0); }
257}