1use super::*;
4
5pub fn selftest() -> (usize, usize) {
8 fn box_id<'a>(b: &LayoutBox<'a>) -> Option<&'a str> {
9 let styled = match &b.box_type {
10 BoxType::BlockNode(s)
11 | BoxType::InlineNode(s)
12 | BoxType::InlineBlockNode(s)
13 | BoxType::FlexNode(s)
14 | BoxType::GridNode(s)
15 | BoxType::TableNode(s)
16 | BoxType::TableRowGroupNode(s)
17 | BoxType::TableRowNode(s)
18 | BoxType::TableCellNode(s) => *s,
19 BoxType::AnonymousBlock => return None,
20 };
21 match &styled.node.node_type {
22 NodeType::Element { id, .. } => id.as_deref(),
23 NodeType::Text(_) => None,
24 }
25 }
26 fn find<'a, 'b>(b: &'b LayoutBox<'a>, id: &str) -> Option<&'b LayoutBox<'a>> {
27 if box_id(b) == Some(id) {
28 return Some(b);
29 }
30 for c in &b.children {
31 if let Some(r) = find(c, id) {
32 return Some(r);
33 }
34 }
35 None
36 }
37 fn measure(html: &str, css: &str, vw: i32, id: &str) -> Option<(i32, i32, i32, i32)> {
38 set_viewport_hint(vw, 600);
42 let dom = super::super::dom::parse_html(html);
43 let sheet = super::super::css::parse_css(css);
44 let styled = super::super::css::style_tree(&dom, &sheet);
45 let icb = Dimensions {
46 content: Rect {
47 x: 0,
48 y: 0,
49 width: vw,
50 height: 0,
51 },
52 ..Default::default()
53 };
54 let tree = layout_tree(&styled, icb);
55 let b = find(&tree, id)?;
56 let c = &b.dimensions.content;
57 Some((c.x, c.y, c.width, c.height))
58 }
59
60 let cases: &[(&str, &str, i32, &str, (i32, i32, i32, i32))] = &[
62 ("<div id='f'><div id='a'></div><div id='b'></div></div>",
64 "#f{display:flex} #a{width:30px;height:10px} #b{width:40px;height:10px}", 200, "a", (0, 0, 30, 10)),
65 ("<div id='f'><div id='a'></div><div id='b'></div></div>",
66 "#f{display:flex} #a{width:30px;height:10px} #b{width:40px;height:10px}", 200, "b", (30, 0, 40, 10)),
67 ("<div id='f'><div id='a'></div></div>",
69 "#f{display:flex;justify-content:center} #a{width:50px;height:10px}", 200, "a", (75, 0, 50, 10)),
70 ("<div id='f'><div id='a'></div></div>",
72 "#f{display:flex;justify-content:flex-end} #a{width:50px;height:10px}", 200, "a", (150, 0, 50, 10)),
73 ("<div id='f'><div id='a'></div><div id='b'></div></div>",
75 "#f{display:flex;justify-content:space-between} #a{width:20px;height:10px} #b{width:20px;height:10px}", 200, "b", (180, 0, 20, 10)),
76 ("<div id='f'><div id='a'></div><div id='b'></div></div>",
80 "#f{display:flex;justify-content:space-evenly} #a{width:20px;height:10px} #b{width:20px;height:10px}", 200, "a", (53, 0, 20, 10)),
81 ("<div id='f'><div id='a'></div><div id='b'></div></div>",
82 "#f{display:flex;justify-content:space-evenly} #a{width:20px;height:10px} #b{width:20px;height:10px}", 200, "b", (126, 0, 20, 10)),
83 ("<div id='f'><div id='a'></div></div>",
86 "#f{display:flex;justify-content:end} #a{width:50px;height:10px}", 200, "a", (150, 0, 50, 10)),
87 ("<div id='f'><div id='a'></div></div>",
90 "#f{display:flex;height:100px;align-items:end} #a{width:30px;height:20px}", 200, "a", (0, 80, 30, 20)),
91 ("<div id='f'><div id='a'></div></div>",
93 "#f{display:flex;height:100px} #a{width:30px;height:20px;align-self:self-end}", 200, "a", (0, 80, 30, 20)),
94 ("<div id='f'><div id='a'></div></div>",
97 "#f{display:flex;flex-wrap:wrap;justify-content:end} #a{width:50px;height:10px}", 200, "a", (150, 0, 50, 10)),
98 ("<div id='f'><div id='a'></div><div id='b'></div></div>",
101 "#f{display:flex;flex-wrap:wrap;justify-content:space-evenly} #a{width:20px;height:10px} #b{width:20px;height:10px}", 200, "b", (126, 0, 20, 10)),
102 ("<div id='f'><div id='a'></div><div id='b'></div></div>",
104 "#f{display:flex;flex-direction:column} #a{width:30px;height:10px} #b{width:30px;height:15px}", 200, "b", (0, 10, 30, 15)),
105 ("<div id='f'><div id='a'></div></div>",
107 "#f{display:flex;align-items:center;height:100px} #a{width:30px;height:20px}", 200, "a", (0, 40, 30, 20)),
108 ("<div id='f'><div id='a'></div><div id='b'></div></div>",
110 "#f{display:flex;gap:10px} #a{width:20px;height:10px} #b{width:20px;height:10px}", 200, "b", (30, 0, 20, 10)),
111 ("<div id='t'></div>", "#t{height:10px}", 200, "t", (0, 0, 200, 10)),
113 ("<div id='f'><div id='a'></div></div>",
115 "#f{display:flex;height:100px} #a{width:30px}", 200, "a", (0, 0, 30, 100)),
116 ("<div id='f'><div id='a'></div></div>",
118 "#f{display:flex;height:100px} #a{width:30px;height:20px}", 200, "a", (0, 0, 30, 20)),
119 ("<div id='f'><div id='a'></div></div>",
121 "#f{display:flex;flex-direction:column} #a{height:10px}", 200, "a", (0, 0, 200, 10)),
122 ("<div id='f'><div id='a'></div><div id='b'></div></div>",
124 "#f{display:flex} #a{flex:1;height:10px} #b{flex:1;height:10px}", 200, "a", (0, 0, 100, 10)),
125 ("<div id='f'><div id='a'></div><div id='b'></div></div>",
126 "#f{display:flex} #a{flex:1;height:10px} #b{flex:1;height:10px}", 200, "b", (100, 0, 100, 10)),
127 ("<div id='f'><div id='a'></div><div id='b'></div></div>",
129 "#f{display:flex} #a{width:50px;height:10px} #b{flex:1;height:10px}", 200, "b", (50, 0, 150, 10)),
130 ("<div id='f'><div id='a'></div><div id='b'></div></div>",
132 "#f{display:flex} #a{flex-grow:1;height:10px} #b{flex-grow:2;height:10px}", 200, "b", (66, 0, 134, 10)),
133 ("<div id='f'><div id='a'></div><div id='b'></div></div>",
135 "#f{display:flex;flex-direction:column;height:100px} #a{flex:1;width:30px} #b{height:20px;width:30px}", 200, "b", (0, 80, 30, 20)),
136 ("<div id='p'><div id='a'></div><div id='b'></div></div>",
138 "#a{height:10px} #b{height:10px;position:relative;left:20px;top:5px}", 200, "b", (20, 15, 200, 10)),
139 ("<div id='p'><div id='a'></div><div id='b'></div></div>",
141 "#a{height:10px} #b{height:10px;position:relative;left:20px;top:5px}", 200, "a", (0, 0, 200, 10)),
142 ("<div id='p'><div id='a'></div></div>",
144 "#p{position:relative;height:100px} #a{position:absolute;top:10px;left:30px;width:40px;height:20px}", 200, "a", (30, 10, 40, 20)),
145 ("<div id='p'><div id='a'></div></div>",
148 "#p{position:relative;height:100px} #a{position:absolute;inset:10px 0 0 30px;width:40px;height:20px}", 200, "a", (30, 10, 40, 20)),
149 ("<div id='p'><div id='a'></div></div>",
151 "#p{position:relative;height:100px} #a{position:absolute;inset:0;width:40px;height:20px}", 200, "a", (0, 0, 40, 20)),
152 ("<div id='p'><div id='a'></div></div>",
154 "#p{position:relative;height:100px} #a{position:absolute;inset:0;left:15px;width:40px;height:20px}", 200, "a", (15, 0, 40, 20)),
155 ("<div id='p'><div id='a'></div></div>",
157 "#p{position:relative;width:200px;height:100px} #a{position:absolute;right:10px;bottom:5px;width:40px;height:20px}", 200, "a", (150, 75, 40, 20)),
158 ("<div id='p'><div id='a'></div><div id='b'></div></div>",
160 "#p{position:relative} #a{position:absolute;top:0;left:0;width:50px;height:50px} #b{height:10px}", 200, "b", (0, 0, 200, 10)),
161 ("<div id='gp'><div id='sp'></div><div id='wrap'><div id='a'></div></div></div>",
164 "#gp{position:relative;height:100px} #sp{height:30px} #a{position:absolute;top:10px;left:20px;width:30px;height:15px}", 200, "a", (20, 10, 30, 15)),
165 ("<div id='h'></div>",
167 "#h{position:fixed;top:5px;left:8px;width:100px;height:20px}", 200, "h", (8, 5, 100, 20)),
168 ("<div id='p'><div id='a'></div><div id='b'></div></div>",
170 "#a{height:10px} #b{height:10px;transform:translate(10px,5px)}", 200, "b", (10, 15, 200, 10)),
171 ("<div id='p'><div id='a'></div><div id='b'></div></div>",
173 "#a{height:10px} #b{height:10px;transform:translate(10px,5px)}", 200, "a", (0, 0, 200, 10)),
174 ("<div id='p'><div id='a'></div><div id='b'></div></div>",
176 "#a{height:10px} #b{height:10px;transform:translateX(20px)}", 200, "b", (20, 10, 200, 10)),
177 ("<div id='p'><div id='a'></div><div id='b'></div></div>",
179 "#a{height:10px} #b{width:40px;height:10px;transform:translate(50%,100%)}", 200, "b", (20, 20, 40, 10)),
180 ("<div id='p'><div id='a'></div><div id='b'></div></div>",
182 "#a{display:none;height:10px} #b{height:10px}", 200, "b", (0, 0, 200, 10)),
183 ("<div id='t'></div>",
185 "#t{width:100px;padding:0 10px;box-sizing:border-box;height:30px}", 200, "t", (10, 0, 80, 30)),
186 ("<div id='p'><div id='a'></div></div>",
188 "#a{width:100px;margin:0 auto;height:10px}", 200, "a", (50, 0, 100, 10)),
189 ("<div id='t'></div>",
191 "#t{width:50px;height:50px;padding:10px 0;box-sizing:border-box}", 200, "t", (0, 10, 50, 30)),
192 ("<div id='t'></div>", "#t{height:100vh}", 200, "t", (0, 0, 200, 600)),
194 ("<div id='t'></div>", "#t{height:2rem}", 200, "t", (0, 0, 200, 32)),
196 ("<div id='g'><div id='a'></div><div id='b'></div><div id='c'></div></div>",
198 "#g{display:grid;grid-template-columns:repeat(3,1fr)} #a,#b,#c{height:10px}", 198, "b", (66, 0, 66, 10)),
199 ("<div id='m'><div id='a'></div><div id='b'></div></div>",
203 "#m{column-count:2} #a,#b{height:10px}", 216, "b", (116, 0, 100, 10)),
204 ("<div id='m'><div id='a'></div><div id='b'></div><div id='c'></div></div>",
206 "#m{column-count:2} #a,#b,#c{height:10px}", 216, "c", (0, 10, 100, 10)),
207 ("<div id='m'><div id='a'></div><div id='b'></div></div>",
209 "#m{columns:100px 2;column-gap:0px} #a,#b{height:10px}", 200, "b", (100, 0, 100, 10)),
210 ("<div id='m'><div id='a'></div><div id='b'></div><div id='s'></div><div id='c'></div></div>",
212 "#m{column-count:2} #a,#b,#c{height:10px} #s{column-span:all;height:20px}", 216, "s", (0, 10, 216, 20)),
213 ("<div id='m'><div id='a'></div><div id='b'></div><div id='s'></div><div id='c'></div></div>",
215 "#m{column-count:2} #a,#b,#c{height:10px} #s{column-span:all;height:20px}", 216, "c", (0, 30, 100, 10)),
216 ("<div id='m'><div id='a'></div><div id='b'></div></div>",
218 "#m{column-count:2;column-gap:2rem} #a,#b{height:10px}", 232, "b", (132, 0, 100, 10)),
219
220 ("<div id='g'><div id='a'></div><div id='b'></div></div>",
222 "#g{display:grid;grid-template-columns:1fr 2fr} #a,#b{height:10px}", 210, "b", (70, 0, 140, 10)),
223 ("<div id='g'><div id='a'></div><div id='b'></div></div>",
225 "#g{display:grid;grid-template-columns:repeat(auto-fit,minmax(80px,1fr))} #a,#b{height:10px}", 200, "b", (100, 0, 100, 10)),
226 ("<div id='g'><div id='a'></div><div id='b'></div></div>",
230 "#g{display:grid;grid-template-columns:50% 1fr} #a,#b{height:10px}", 200, "a", (0, 0, 100, 10)),
231 ("<div id='g'><div id='a'></div><div id='b'></div></div>",
232 "#g{display:grid;grid-template-columns:50% 1fr} #a,#b{height:10px}", 200, "b", (100, 0, 100, 10)),
233 ("<div id='g'><div id='a'></div><div id='b'></div></div>",
238 "#g{display:grid;grid-template-columns:repeat(auto-fit,minmax(50%,1fr))} #a,#b{height:10px}", 200, "b", (100, 0, 100, 10)),
239 ("<div id='t'></div>", "#t{width:160px;aspect-ratio:16/9}", 200, "t", (0, 0, 160, 90)),
241 ("<div id='t'></div>", "#t{width:80px;aspect-ratio:1/1}", 200, "t", (0, 0, 80, 80)),
243 ("<div id='t'></div>", "@media (max-width:300px){#t{height:42px}}", 200, "t", (0, 0, 200, 42)),
245 ("<div id='f'><div id='a'></div><div id='b'></div></div>",
247 "#f{display:flex;flex-direction:row-reverse} #a{width:30px;height:10px} #b{width:40px;height:10px}", 200, "a", (170, 0, 30, 10)),
248 ("<div id='f'><div id='a'></div><div id='b'></div></div>",
249 "#f{display:flex;flex-direction:row-reverse} #a{width:30px;height:10px} #b{width:40px;height:10px}", 200, "b", (130, 0, 40, 10)),
250 ("<div id='f'><div id='a'></div></div>",
252 "#f{display:flex;height:100px} #a{width:30px;height:20px;align-self:flex-end}", 200, "a", (0, 80, 30, 20)),
253 ("<div id='f'><div id='a'></div></div>",
255 "#f{display:flex;height:100px;align-items:flex-start} #a{width:30px;height:20px;align-self:center}", 200, "a", (0, 40, 30, 20)),
256 ("<div id='f'><div id='a'></div><div id='b'></div><div id='c'></div></div>",
258 "#f{display:flex;flex-wrap:wrap} #a{width:100px;height:10px} #b{width:100px;height:10px} #c{width:100px;height:10px}", 200, "c", (0, 10, 100, 10)),
259 ("<div id='f'><div id='a'></div><div id='b'></div><div id='c'></div></div>",
261 "#f{display:flex;flex-wrap:wrap} #a{width:100px;height:10px} #b{width:100px;height:10px} #c{width:100px;height:10px}", 200, "b", (100, 0, 100, 10)),
262 ("<div id='f'><div id='x'></div><div id='a'></div></div>",
265 "#f{display:flex;position:relative;height:100px} #x{position:absolute;top:10px;left:20px;width:30px;height:15px} #a{width:40px;height:10px}", 200, "x", (20, 10, 30, 15)),
266 ("<div id='f'><div id='x'></div><div id='a'></div></div>",
268 "#f{display:flex;position:relative;height:100px} #x{position:absolute;top:10px;left:20px;width:30px;height:15px} #a{width:40px;height:10px}", 200, "a", (0, 0, 40, 10)),
269 ("<div id='f'><div id='a'></div><div id='b'></div></div>",
271 "#f{display:flex} #a{width:40px;height:10px} #b{width:30px;height:10px;position:relative;left:5px;top:3px}", 200, "b", (45, 3, 30, 10)),
272 ("<div id='g'><div id='x'></div><div id='a'></div><div id='b'></div></div>",
274 "#g{display:grid;position:relative;grid-template-columns:repeat(2,1fr)} #x{position:absolute;top:8px;left:12px;width:20px;height:10px} #a,#b{height:10px}", 200, "x", (12, 8, 20, 10)),
275 ("<div id='g'><div id='x'></div><div id='a'></div><div id='b'></div></div>",
277 "#g{display:grid;position:relative;grid-template-columns:repeat(2,1fr)} #x{position:absolute;top:8px;left:12px;width:20px;height:10px} #a,#b{height:10px}", 200, "b", (100, 0, 100, 10)),
278 ("<table id='t'><tr><td id='c1'></td><td id='c2'></td></tr></table>",
281 "#t{position:relative} #c1{width:50px;height:10px} #c2{width:50px;height:10px;position:relative;left:7px;top:4px}", 200, "c2", (107, 4, 100, 10)),
282 ("<div id='g'><div id='a'></div><div id='b'></div><div id='c'></div></div>",
284 "#g{display:grid;grid-template-columns:repeat(2,1fr);grid-template-rows:20px 30px} #a,#b,#c{height:10px}", 200, "c", (0, 20, 100, 10)),
285 ("<div id='g'><div id='a'></div><div id='b'></div></div>",
287 "#g{display:grid;grid-template-columns:repeat(2,1fr)} #a{height:10px} #b{height:10px;grid-column:2}", 200, "b", (100, 0, 100, 10)),
288 ("<div id='g'><div id='a'></div><div id='b'></div></div>",
290 "#g{display:grid;grid-template-columns:1fr} #a{height:10px} #b{height:10px;grid-row:2}", 200, "b", (0, 10, 200, 10)),
291 ("<div id='g'><div id='a'></div><div id='b'></div></div>",
293 "#g{display:grid;grid-template-columns:repeat(3,100px)} #a{height:10px;grid-column:span 2} #b{height:10px}", 400, "a", (0, 0, 200, 10)),
294 ("<div id='g'><div id='a'></div><div id='b'></div></div>",
296 "#g{display:grid;grid-template-columns:repeat(3,100px)} #a{height:10px;grid-column:span 2} #b{height:10px}", 400, "b", (200, 0, 100, 10)),
297 ("<div id='g'><div id='a'></div><div id='b'></div><div id='c'></div><div id='d'></div></div>",
299 "#g{display:grid;grid-template-columns:repeat(2,1fr);grid-template-rows:1fr 1fr;height:100px} #a,#b,#c,#d{height:10px}", 200, "c", (0, 50, 100, 10)),
300 ("<div id='f'><div id='a'></div><div id='b'></div></div>",
303 "#f{display:flex;flex-wrap:wrap;width:100px;height:100px;align-content:flex-start} #a{width:60px;height:10px} #b{width:60px;height:10px}", 200, "b", (0, 10, 60, 10)),
304 ("<div id='f'><div id='a'></div><div id='b'></div></div>",
306 "#f{display:flex;flex-wrap:wrap;width:100px;height:100px;align-content:flex-end} #a{width:60px;height:10px} #b{width:60px;height:10px}", 200, "b", (0, 90, 60, 10)),
307 ("<div id='f'><div id='a'></div><div id='b'></div></div>",
309 "#f{display:flex;flex-wrap:wrap;width:100px;height:100px;align-content:center} #a{width:60px;height:10px} #b{width:60px;height:10px}", 200, "a", (0, 40, 60, 10)),
310 ("<div id='f'><div id='a'></div><div id='b'></div></div>",
312 "#f{display:flex;flex-wrap:wrap;width:100px;height:100px;align-content:space-between} #a{width:60px;height:10px} #b{width:60px;height:10px}", 200, "b", (0, 90, 60, 10)),
313 ("<div id='f'><div id='a'></div><div id='b'></div></div>",
315 "#f{display:flex;flex-wrap:wrap;width:100px;height:100px;align-content:stretch} #a{width:60px;height:10px} #b{width:60px;height:10px}", 200, "b", (0, 50, 60, 10)),
316 ("<div id='f'><div id='a'></div><div id='b'></div></div>",
318 "#f{display:flex;flex-wrap:wrap;width:100px;height:100px;align-content:space-around} #a{width:60px;height:10px} #b{width:60px;height:10px}", 200, "b", (0, 70, 60, 10)),
319 ("<div id='g'><div id='a'></div></div>",
322 "#g{display:grid;grid-template-columns:100px;width:200px} #a{width:40px;height:20px;justify-self:center}", 200, "a", (30, 0, 40, 20)),
323 ("<div id='g'><div id='a'></div></div>",
325 "#g{display:grid;grid-template-columns:100px;width:200px} #a{width:40px;height:20px;justify-self:end}", 200, "a", (60, 0, 40, 20)),
326 ("<div id='g'><div id='a'></div></div>",
328 "#g{display:grid;grid-template-columns:100px;grid-template-rows:60px;width:200px} #a{width:40px;height:20px;align-self:center}", 200, "a", (0, 20, 40, 20)),
329 ("<div id='g'><div id='a'></div></div>",
331 "#g{display:grid;grid-template-columns:100px;grid-template-rows:60px;width:200px} #a{width:40px;height:20px;align-self:end}", 200, "a", (0, 40, 40, 20)),
332 ("<div id='g'><div id='a'></div></div>",
334 "#g{display:grid;grid-template-columns:100px;width:200px;justify-items:center} #a{width:40px;height:20px}", 200, "a", (30, 0, 40, 20)),
335 ("<div id='g'><div id='a'></div></div>",
337 "#g{display:grid;grid-template-columns:100px;grid-template-rows:60px;width:200px;align-items:end} #a{width:40px;height:20px}", 200, "a", (0, 40, 40, 20)),
338 ("<div id='g'><div id='a'></div></div>",
342 "#g{display:grid;grid-template-columns:100px;width:200px;justify-content:center} #a{width:100px;height:20px}", 200, "a", (50, 0, 100, 20)),
343 ("<div id='g'><div id='a'></div></div>",
345 "#g{display:grid;grid-template-columns:100px;width:200px;justify-content:end} #a{width:100px;height:20px}", 200, "a", (100, 0, 100, 20)),
346 ("<div id='g'><div id='a'></div><div id='b'></div></div>",
349 "#g{display:grid;grid-template-columns:50px 50px;width:200px;justify-content:space-between} #a{width:50px;height:20px} #b{width:50px;height:20px}", 200, "b", (150, 0, 50, 20)),
350 ("<div id='g'><div id='a'></div></div>",
354 "#g{display:grid;grid-template-rows:50px;height:150px;width:200px;align-content:center} #a{width:40px;height:50px}", 200, "a", (0, 50, 40, 50)),
355 ("<div id='g'><div id='a'></div><div id='b'></div></div>",
358 "#g{display:grid;grid-template-rows:20px 20px;height:150px;width:200px;align-content:space-between} #a{width:40px;height:20px} #b{width:40px;height:20px}", 200, "b", (0, 130, 40, 20)),
359 ("<div id='f'><div id='a'></div><div id='b'></div></div>",
362 "#f{display:flex;width:100px} #a{width:80px;height:10px} #b{width:80px;height:10px}", 200, "a", (0, 0, 50, 10)),
363 ("<div id='f'><div id='a'></div><div id='b'></div></div>",
365 "#f{display:flex;width:100px} #a{width:80px;height:10px} #b{width:80px;height:10px}", 200, "b", (50, 0, 50, 10)),
366 ("<div id='f'><div id='a'></div><div id='b'></div></div>",
368 "#f{display:flex;width:100px} #a{width:80px;height:10px;flex-shrink:0} #b{width:80px;height:10px}", 200, "b", (80, 0, 20, 10)),
369 ("<div id='f'><div id='a'></div><div id='b'></div><div id='c'></div></div>",
371 "#f{display:flex} #a{width:30px;height:10px} #b{width:30px;height:10px} #c{width:30px;height:10px;order:-1}", 200, "b", (60, 0, 30, 10)),
372 ("<div id='f'><div id='a'></div><div id='b'></div><div id='c'></div></div>",
374 "#f{display:flex} #a{width:30px;height:10px} #b{width:30px;height:10px} #c{width:30px;height:10px;order:-1}", 200, "c", (0, 0, 30, 10)),
375 ("<div id='f'><div id='a'></div><div id='b'></div></div>",
378 "#f{display:flex} #a{width:20px;height:10px;flex-basis:60px} #b{width:30px;height:10px}", 200, "b", (60, 0, 30, 10)),
379 ("<div id='f'><div id='a'></div></div>",
381 "#f{display:flex;width:200px} #a{height:10px;flex-basis:50%}", 200, "a", (0, 0, 100, 10)),
382 ("<div id='f'><div id='a'></div><div id='b'></div></div>",
384 "#f{display:flex;width:200px} #a{height:10px;flex-basis:40px} #b{height:10px;flex-basis:40px;flex-grow:1}", 200, "b", (40, 0, 160, 10)),
385 ("<div id='f'><div id='a'></div><div id='b'></div></div>",
390 "#f{display:flex;width:200px} #a{height:10px;flex:0 0 40vw} #b{width:10px;height:10px}", 200, "b", (80, 0, 10, 10)),
391 ("<div id='t'><span id='a'></span><span id='b'></span></div>",
394 "#t{width:max-content} #a{display:inline-block;width:50px;height:10px} #b{display:inline-block;width:50px;height:10px}", 400, "t", (0, 0, 100, 10)),
395 ("<div id='t'><span id='a'></span><span id='b'></span></div>",
397 "#t{width:min-content} #a{display:inline-block;width:50px;height:10px} #b{display:inline-block;width:50px;height:10px}", 400, "t", (0, 0, 50, 20)),
398 ("<div id='t'><span id='a'></span><span id='b'></span></div>",
400 "#t{width:fit-content} #a{display:inline-block;width:50px;height:10px} #b{display:inline-block;width:50px;height:10px}", 400, "t", (0, 0, 100, 10)),
401 ("<div id='outer'><div id='t'><span id='a'></span><span id='b'></span></div></div>",
403 "#outer{width:70px} #t{width:fit-content} #a{display:inline-block;width:50px;height:10px} #b{display:inline-block;width:50px;height:10px}", 400, "t", (0, 0, 70, 20)),
404 ("<div id='t'></div>", "#t{height:90px;width:auto;aspect-ratio:16/9}", 400, "t", (0, 0, 160, 90)),
407 ("<div id='t'></div>", "#t{height:50px;width:auto;aspect-ratio:2/1}", 400, "t", (0, 0, 100, 50)),
409 ("<div id='t'></div>", "#t{height:60px;width:auto;aspect-ratio:1/1;max-width:40px}", 400, "t", (0, 0, 40, 60)),
411 ("<div id='f'><div id='a'></div><div id='b'></div></div>",
414 "#f{display:flex;width:200px} #a{width:30px;height:10px} #b{width:40px;height:10px;margin-left:auto}", 200, "b", (160, 0, 40, 10)),
415 ("<div id='f'><div id='a'></div></div>",
417 "#f{display:flex;width:200px} #a{width:50px;height:10px;margin:0 auto}", 200, "a", (75, 0, 50, 10)),
418 ("<div id='f'><div id='a'></div><div id='b'></div></div>",
420 "#f{display:flex;flex-direction:column;height:100px} #a{width:10px;height:10px} #b{width:10px;height:20px;margin-top:auto}", 200, "b", (0, 80, 10, 20)),
421 ("<div id='f'><div id='a'></div><div id='b'></div></div>",
424 "#f{display:flex;width:200px;column-gap:10%} #a{width:30px;height:10px} #b{width:40px;height:10px}", 200, "b", (50, 0, 40, 10)),
425 ("<div id='f'><div id='a'></div><div id='b'></div></div>",
427 "#f{display:flex;width:200px;gap:25%} #a{width:20px;height:10px} #b{width:20px;height:10px}", 200, "b", (70, 0, 20, 10)),
428 ("<div id='g'><div id='a'></div><div id='b'></div></div>",
430 "#g{display:grid;width:200px;grid-template-columns:repeat(2,80px);column-gap:10%} #a{height:10px} #b{height:10px}", 200, "b", (100, 0, 80, 10)),
431 ("<div id='t'><span id='a'></span><span id='b'></span></div>",
434 "#t{max-width:min-content} #a{display:inline-block;width:50px;height:10px} #b{display:inline-block;width:50px;height:10px}", 400, "t", (0, 0, 50, 20)),
435 ("<div id='t'><span id='a'></span><span id='b'></span></div>",
437 "#t{width:20px;min-width:max-content} #a{display:inline-block;width:50px;height:10px} #b{display:inline-block;width:50px;height:10px}", 400, "t", (0, 0, 100, 10)),
438 ("<div id='g'><div id='a'></div><div id='b'></div></div>",
441 "#g{display:grid;grid-template-rows:minmax(50px,auto)} #a{height:10px} #b{height:10px}", 200, "b", (0, 50, 200, 10)),
442 ("<div id='g'><div id='a'></div><div id='b'></div></div>",
444 "#g{display:grid;grid-template-rows:minmax(10px,30px)} #a{height:60px} #b{height:10px}", 200, "b", (0, 30, 200, 10)),
445 ("<div id='g'><div id='a'></div><div id='b'></div></div>",
447 "#g{display:grid;grid-template-rows:minmax(20px,1fr) 1fr;height:100px} #a{height:10px} #b{height:10px}", 200, "b", (0, 50, 200, 10)),
448 ("<div id='g'><div id='a'></div><div id='b'></div></div>",
453 "#g{display:grid;height:100px;grid-template-rows:50% 50%} #a{height:10px} #b{height:10px}", 200, "b", (0, 50, 200, 10)),
454 ("<div id='g'><div id='a'></div><div id='b'></div><div id='c'></div></div>",
457 "#g{display:grid;grid-template-columns:200px;grid-auto-rows:40px} #a{height:10px} #b{height:10px} #c{height:10px}", 200, "c", (0, 80, 200, 10)),
458 ("<div id='g'><div id='a'></div><div id='b'></div><div id='c'></div></div>",
460 "#g{display:grid;grid-template-columns:200px;grid-auto-rows:30px 60px} #a{height:10px} #b{height:10px} #c{height:10px}", 200, "c", (0, 90, 200, 10)),
461 ("<div id='g'><div id='a'></div><div id='b'></div><div id='c'></div><div id='d'></div></div>",
464 "#g{display:grid;grid-template-columns:repeat(2,100px);grid-template-rows:50px 50px;grid-auto-flow:column} #a{height:10px} #b{height:10px} #c{height:10px} #d{height:10px}", 200, "c", (100, 0, 100, 10)),
465 ("<div id='g'><div id='a'></div><div id='b'></div><div id='c'></div><div id='d'></div></div>",
467 "#g{display:grid;grid-template-columns:repeat(2,100px);grid-template-rows:50px 50px;grid-auto-flow:column} #a{height:10px} #b{height:10px} #c{height:10px} #d{height:10px}", 200, "b", (0, 50, 100, 10)),
468 ("<div id='g'><div id='a'></div><div id='b'></div><div id='c'></div><div id='d'></div><div id='e'></div></div>",
472 "#g{display:grid;grid-template-columns:repeat(2,100px);grid-template-rows:50px 50px;grid-auto-flow:column;grid-auto-columns:80px} #a{height:10px} #b{height:10px} #c{height:10px} #d{height:10px} #e{height:10px}", 300, "e", (200, 0, 80, 10)),
473 ("<div id='p'><div id='a'></div><div id='b'></div></div>",
476 "#p{width:200px} #a{float:left;width:50px;height:30px} #b{width:100px;height:10px}", 200, "b", (50, 0, 100, 10)),
477 ("<div id='p'><div id='a'></div></div>",
479 "#p{width:200px} #a{float:right;width:50px;height:30px}", 200, "a", (150, 0, 50, 30)),
480 ("<div id='p'><div id='a'></div><div id='c'></div></div>",
482 "#p{width:200px} #a{float:left;width:50px;height:30px} #c{clear:left;width:50px;height:10px}", 200, "c", (0, 30, 50, 10)),
483 ("<div id='p'><div id='a'></div><div id='b'></div></div>",
485 "#p{width:200px} #a{float:left;width:50px;height:10px} #b{width:100px;height:20px}", 200, "p", (0, 0, 200, 20)),
486 ("<div id='p'><div id='a'></div><div id='b'></div></div>",
489 "#p{width:200px} #a{float:inline-start;width:50px;height:30px} #b{width:100px;height:10px}", 200, "b", (50, 0, 100, 10)),
490 ("<div id='p'><div id='a'></div></div>",
492 "#p{width:200px;direction:rtl} #a{float:inline-start;width:50px;height:30px}", 200, "a", (150, 0, 50, 30)),
493 ("<div id='p'><div id='a'></div><div id='b'></div></div>",
495 "#p{width:200px;direction:rtl} #a{float:inline-end;width:50px;height:30px} #b{width:100px;height:10px}", 200, "b", (50, 0, 100, 10)),
496 ("<div id='g'><div id='h'></div><div id='s'></div><div id='c'></div></div>",
499 "#g{display:grid;grid-template-columns:100px 100px;grid-template-rows:20px 30px;grid-template-areas:'header header' 'sidebar content'} #h{grid-area:header} #s{grid-area:sidebar} #c{grid-area:content}",
500 200, "h", (0, 0, 200, 20)),
501 ("<div id='g'><div id='h'></div><div id='s'></div><div id='c'></div></div>",
503 "#g{display:grid;grid-template-columns:100px 100px;grid-template-rows:20px 30px;grid-template-areas:'header header' 'sidebar content'} #h{grid-area:header} #s{grid-area:sidebar} #c{grid-area:content}",
504 200, "s", (0, 20, 100, 30)),
505 ("<div id='g'><div id='h'></div><div id='s'></div><div id='c'></div></div>",
507 "#g{display:grid;grid-template-columns:100px 100px;grid-template-rows:20px 30px;grid-template-areas:'header header' 'sidebar content'} #h{grid-area:header} #s{grid-area:sidebar} #c{grid-area:content}",
508 200, "c", (100, 20, 100, 30)),
509 ("<div id='t'></div>", "#t{width:calc(100px + 50px);height:10px}", 400, "t", (0, 0, 150, 10)),
512 ("<div id='t'></div>", "#t{width:calc(50% - 20px);height:10px}", 400, "t", (0, 0, 180, 10)),
514 ("<div id='t'></div>", "#t{width:min(300px, 50%);height:10px}", 400, "t", (0, 0, 200, 10)),
516 ("<div id='t'></div>", "#t{width:max(300px, 50%);height:10px}", 400, "t", (0, 0, 300, 10)),
518 ("<div id='t'></div>", "#t{width:clamp(100px, 50%, 300px);height:10px}", 400, "t", (0, 0, 200, 10)),
520 ("<div id='t'></div>", "#t{width:clamp(100px, 10%, 300px);height:10px}", 400, "t", (0, 0, 100, 10)),
522 ("<div id='t'></div>", "#t{width:clamp(100px, 90%, 300px);height:10px}", 400, "t", (0, 0, 300, 10)),
524 ("<div id='a'></div><div id='b'></div>",
527 "#a{width:20px;height:10px} #b{width:20px;height:10px;margin-left:calc(10px * 3)}", 400, "b", (30, 10, 20, 10)),
528 ("<table><tr><td id='a'></td><td class='c'><div id='t'></div></td></tr></table>",
531 "#a{width:50px;height:100px} .c{vertical-align:middle} #t{height:10px}", 400, "t", (200, 45, 200, 10)),
532 ("<table><tr><td id='a'></td><td class='c'><div id='t'></div></td></tr></table>",
534 "#a{width:50px;height:100px} .c{vertical-align:bottom} #t{height:10px}", 400, "t", (200, 90, 200, 10)),
535 ("<table><tr><td id='a'></td><td><div id='t'></div></td></tr></table>",
537 "#a{width:50px;height:100px} #t{height:10px}", 400, "t", (200, 0, 200, 10)),
538 ("<table><tr><td id='a'></td><td id='t' class='c'></td></tr></table>",
540 "#a{width:50px;height:100px} .c{vertical-align:middle}", 400, "t", (200, 0, 200, 100)),
541 ("<table id='tbl'><tr><td id='a'></td><td id='t'></td></tr></table>",
544 "#tbl{border-spacing:10px} #a{width:50px;height:20px} #t{width:50px;height:20px}", 400, "t", (205, 0, 195, 20)),
545 ("<table id='tbl'><tr><td id='a'></td></tr><tr><td id='t'></td></tr></table>",
547 "#tbl{border-spacing:10px 5px} #a{height:20px} #t{height:20px}", 400, "t", (0, 25, 400, 20)),
548 ("<table id='tbl'><tr><td id='a'></td><td id='t'></td></tr></table>",
550 "#tbl{border-collapse:collapse;border-spacing:10px} #a{width:50px;height:20px} #t{width:50px;height:20px}", 400, "t", (199, 0, 200, 20)),
551 ("<div id='wrap'><table id='tbl'><tr><td id='a'></td><td id='t'></td></tr></table></div>",
556 "#wrap{border-collapse:collapse} #tbl{border-spacing:10px} #a{width:50px;height:20px} #t{width:50px;height:20px}", 400, "t", (199, 0, 200, 20)),
557 ("<table><caption id='cap' style='height:15px'></caption><tr><td id='t' style='height:20px'></td></tr></table>",
559 "", 400, "t", (0, 15, 400, 20)),
560 ("<table><caption id='cap' style='height:15px'></caption><tr><td id='t' style='height:20px'></td></tr></table>",
562 "caption{caption-side:bottom}", 400, "t", (0, 0, 400, 20)),
563 ("<table><caption id='cap' style='height:15px'></caption><tr><td id='t' style='height:20px'></td></tr></table>",
565 "caption{caption-side:bottom}", 400, "cap", (0, 20, 400, 15)),
566 ("<div id='d'><span id='t'>x</span></div>",
568 "#d{direction:rtl;width:100px} #t{width:30px;height:10px}", 400, "t", (70, 0, 30, 10)),
569 ("<div id='d'><span id='t'>x</span></div>",
571 "#d{direction:rtl;text-align:left;width:100px} #t{width:30px;height:10px}", 400, "t", (0, 0, 30, 10)),
572 ("<table><tr><td style='height:10px'></td></tr><tr class='c'><td style='height:20px'></td></tr><tr><td id='t' style='height:15px'></td></tr></table>",
575 ".c{visibility:collapse}", 400, "t", (0, 10, 400, 15)),
576 ("<table id='tbl'><tr><td style='width:100px;height:20px'></td><td id='t' style='height:20px'></td></tr></table>",
579 "#tbl{table-layout:fixed}", 400, "t", (100, 0, 300, 20)),
580 ("<table id='tbl'><tr><td style='width:50px;height:10px'></td></tr><tr><td id='t' style='width:200px;height:20px'></td></tr></table>",
583 "#tbl{table-layout:fixed}", 400, "t", (0, 10, 50, 20)),
584 ("<span id='wrap'><span id='a' style='display:inline-block;width:10px;height:30px'></span><span id='t' style='display:inline-block;width:10px;height:10px'></span></span>",
586 "", 400, "t", (10, 0, 10, 10)),
587 ("<span id='wrap'><span id='a' style='display:inline-block;width:10px;height:30px'></span><span id='t' style='display:inline-block;width:10px;height:10px;vertical-align:middle'></span></span>",
589 "", 400, "t", (10, 10, 10, 10)),
590 ("<span id='wrap'><span id='a' style='display:inline-block;width:10px;height:30px'></span><span id='t' style='display:inline-block;width:10px;height:10px;vertical-align:bottom'></span></span>",
592 "", 400, "t", (10, 20, 10, 10)),
593 ("<span id='wrap'><span id='a' style='display:inline-block;width:10px;height:30px'></span><span id='t' style='display:inline-block;width:10px;height:10px;vertical-align:middle'></span></span>",
595 "", 400, "a", (0, 0, 10, 30)),
596 ("<div id='c'><span id='a' style='display:inline-block;width:200px;height:10px'></span><span id='b' style='display:inline-block;width:200px;height:10px'></span></div>",
600 "#c{width:250px;text-align-last:right}", 400, "a", (0, 0, 200, 10)),
601 ("<div id='c'><span id='a' style='display:inline-block;width:200px;height:10px'></span><span id='b' style='display:inline-block;width:200px;height:10px'></span></div>",
602 "#c{width:250px;text-align-last:right}", 400, "b", (50, 10, 200, 10)),
603 ("<div id='c'><span id='t' style='display:inline-block;width:30px;height:10px'></span></div>",
606 "#c{width:100px;text-align:end}", 400, "t", (70, 0, 30, 10)),
607 ("<div id='c'><span id='t' style='display:inline-block;width:30px;height:10px'></span></div>",
609 "#c{width:100px;direction:rtl;text-align:start}", 400, "t", (70, 0, 30, 10)),
610 ("<div id='c'><span id='t' style='display:inline-block;width:30px;height:10px'></span></div>",
612 "#c{width:100px;direction:rtl;text-align:end}", 400, "t", (0, 0, 30, 10)),
613 ("<div id='a' hidden style='height:10px'></div><div id='b' style='height:10px'></div>",
617 "", 200, "b", (0, 0, 200, 10)),
618 ("<div id='a' hidden style='display:block;height:10px'></div><div id='b' style='height:10px'></div>",
621 "", 200, "b", (0, 10, 200, 10)),
622 ("<div id='d' dir='rtl'><span id='t' style='width:30px;height:10px'></span></div>",
626 "#d{width:100px}", 400, "t", (70, 0, 30, 10)),
627 ("<div id='d' dir='rtl'><span id='t' style='width:30px;height:10px'></span></div>",
630 "#d{width:100px;direction:ltr}", 400, "t", (0, 0, 30, 10)),
631 ("<div id='c'><span id='a' style='display:inline-block;width:100px;height:10px'></span><span id='b' style='display:inline-block;width:10px;height:10px'></span><span id='c2' style='display:inline-block;width:150px;height:10px'></span><span id='d' style='display:inline-block;width:50px;height:10px'></span></div>",
634 "#c{width:200px;text-align:justify}", 400, "a", (0, 0, 100, 10)),
635 ("<div id='c'><span id='a' style='display:inline-block;width:100px;height:10px'></span><span id='b' style='display:inline-block;width:10px;height:10px'></span><span id='c2' style='display:inline-block;width:150px;height:10px'></span><span id='d' style='display:inline-block;width:50px;height:10px'></span></div>",
636 "#c{width:200px;text-align:justify}", 400, "b", (190, 0, 10, 10)),
637 ("<div id='c'><span id='a' style='display:inline-block;width:100px;height:10px'></span><span id='b' style='display:inline-block;width:10px;height:10px'></span><span id='c2' style='display:inline-block;width:150px;height:10px'></span><span id='d' style='display:inline-block;width:50px;height:10px'></span></div>",
640 "#c{width:200px;text-align:justify}", 400, "c2", (0, 10, 150, 10)),
641 ("<div id='c'><span id='a' style='display:inline-block;width:100px;height:10px'></span><span id='b' style='display:inline-block;width:10px;height:10px'></span><span id='c2' style='display:inline-block;width:150px;height:10px'></span><span id='d' style='display:inline-block;width:50px;height:10px'></span></div>",
642 "#c{width:200px;text-align:justify}", 400, "d", (150, 10, 50, 10)),
643 ("<template id='a'><div style='height:10px'></div></template><div id='b' style='height:10px'></div>",
649 "", 200, "b", (0, 0, 200, 10)),
650 ("<noscript>plain text fallback</noscript><div id='b' style='height:10px'></div>",
653 "", 200, "b", (0, 0, 200, 10)),
654 ("<article id='a' style='height:10px'></article><div id='b' style='height:10px'></div>",
658 "", 200, "b", (0, 10, 200, 10)),
659 ("<figure id='a' style='height:10px'></figure><div id='b' style='height:10px'></div>",
661 "", 200, "b", (0, 10, 200, 10)),
662 ("<details id='d'><summary style='height:10px'></summary><div id='c' style='height:20px'></div></details><div id='b' style='height:10px'></div>",
670 "", 200, "b", (0, 10, 200, 10)),
671 ("<details id='d' open><summary style='height:10px'></summary><div id='c' style='height:20px'></div></details><div id='b' style='height:10px'></div>",
674 "", 200, "b", (0, 30, 200, 10)),
675 ("<details id='d' open><summary style='height:10px'></summary><div id='c' style='height:20px'></div></details>",
677 "", 200, "c", (0, 10, 200, 20)),
678 ("<iframe id='f' width='300' height='150'></iframe>",
684 "", 400, "f", (0, 0, 300, 150)),
685 ("<div id='p' style='width:400px'><iframe id='f' width='50%' height='100'></iframe></div>",
687 "", 400, "f", (0, 0, 200, 100)),
688 ("<iframe id='f' width='300' height='150' style='width:50px;height:20px'></iframe>",
691 "", 400, "f", (0, 0, 50, 20)),
692 ];
693 let mut passed = 0;
694 let total = cases.len();
695 for (html, css, vw, id, exp) in cases {
696 match measure(html, css, *vw, id) {
697 Some(d) if d == *exp => passed += 1,
698 Some(d) => crate::println!(
699 "LAYOUT_SELFTEST FAIL: #{} => ({},{},{},{}) want ({},{},{},{})",
700 id,
701 d.0,
702 d.1,
703 d.2,
704 d.3,
705 exp.0,
706 exp.1,
707 exp.2,
708 exp.3
709 ),
710 None => crate::println!("LAYOUT_SELFTEST FAIL: #{} NOT FOUND", id),
711 }
712 }
713
714 let mut extra_passed = 0usize;
721 let mut extra_total = 0usize;
722
723 let det_html = "<div id='c'><div id='a'></div><div id='b'></div></div>";
725 let det_css = "#c{display:flex} #a{width:30px;height:10px} #b{width:40px;height:10px}";
726 extra_total += 1;
727 let r1 = measure(det_html, det_css, 200, "b");
728 let r2 = measure(det_html, det_css, 200, "b");
729 if r1.is_some() && r1 == r2 {
730 extra_passed += 1;
731 } else {
732 crate::println!("LAYOUT_SELFTEST FAIL: determinism r1={:?} r2={:?}", r1, r2);
733 }
734
735 extra_total += 1;
737 {
738 let dom = super::super::dom::parse_html(det_html);
739 let sheet_fresh = super::super::css::parse_css(det_css);
740 let sheet_cached = sheet_fresh.clone(); set_viewport_hint(200, 600);
742 let icb = || Dimensions {
743 content: Rect {
744 x: 0,
745 y: 0,
746 width: 200,
747 height: 0,
748 },
749 ..Default::default()
750 };
751 let styled_a = super::super::css::style_tree(&dom, &sheet_fresh);
752 let styled_b = super::super::css::style_tree(&dom, &sheet_cached);
753 let ta = layout_tree(&styled_a, icb());
754 let tb = layout_tree(&styled_b, icb());
755 let da = find(&ta, "b").map(|b| {
756 let c = &b.dimensions.content;
757 (c.x, c.y, c.width, c.height)
758 });
759 let db = find(&tb, "b").map(|b| {
760 let c = &b.dimensions.content;
761 (c.x, c.y, c.width, c.height)
762 });
763 if da.is_some() && da == db {
764 extra_passed += 1;
765 } else {
766 crate::println!(
767 "LAYOUT_SELFTEST FAIL: css cache equiv da={:?} db={:?}",
768 da,
769 db
770 );
771 }
772 }
773
774 extra_total += 1;
776 {
777 let root = super::super::dom::Node::empty_root();
778 let is_html = matches!(
779 &root.node_type,
780 super::super::dom::NodeType::Element { tag_name, .. } if tag_name == "html"
781 );
782 if is_html && root.children.is_empty() {
783 extra_passed += 1;
784 } else {
785 crate::println!("LAYOUT_SELFTEST FAIL: empty_root shape");
786 }
787 }
788
789 {
793 use crate::kernel::vector_font::{get_vector_string_width, get_vector_string_width_ls};
794 let s = "ABCDE"; let nchars = s.chars().count() as i32;
796 let base = get_vector_string_width(s, 16) as i32;
797
798 extra_total += 1;
800 if get_vector_string_width_ls(s, 16, 0) as i32 == base {
801 extra_passed += 1;
802 } else {
803 crate::println!("LAYOUT_SELFTEST FAIL: letter-spacing ls=0 != base");
804 }
805
806 extra_total += 1;
808 if get_vector_string_width_ls(s, 16, 2) as i32 == base + nchars * 2 {
809 extra_passed += 1;
810 } else {
811 crate::println!("LAYOUT_SELFTEST FAIL: letter-spacing ls=2 width");
812 }
813
814 extra_total += 1;
816 if get_vector_string_width_ls(s, 16, -1) as i32 == (base - nchars).max(0) {
817 extra_passed += 1;
818 } else {
819 crate::println!("LAYOUT_SELFTEST FAIL: letter-spacing ls=-1 width");
820 }
821
822 extra_total += 1;
824 if parse_letter_spacing(Some("normal")) == 0
825 && parse_letter_spacing(Some("3px")) == 3
826 && parse_letter_spacing(Some("-2px")) == -2
827 && parse_letter_spacing(None) == 0
828 && parse_letter_spacing(Some("4")) == 4
829 {
830 extra_passed += 1;
831 } else {
832 crate::println!("LAYOUT_SELFTEST FAIL: parse_letter_spacing");
833 }
834
835 extra_total += 1;
838 if parse_letter_spacing_ctx(Some("calc(1px + 1px)"), 0) == 2
839 && parse_letter_spacing_ctx(Some("min(3px, 5px)"), 0) == 3
840 && parse_letter_spacing_ctx(Some("max(3px, 5px)"), 0) == 5
841 && parse_letter_spacing_ctx(Some("clamp(1px, 10px, 5px)"), 0) == 5
842 {
843 extra_passed += 1;
844 } else {
845 crate::println!("LAYOUT_SELFTEST FAIL: parse_letter_spacing_ctx calc/min/max/clamp");
846 }
847 }
848
849 {
851 use crate::kernel::vector_font::{get_vector_string_width, get_vector_string_width_ls_ws};
852 let s = "AB CD EF"; let base = get_vector_string_width(s, 16) as i32;
854
855 extra_total += 1;
856 if get_vector_string_width_ls_ws(s, 16, 0, 0) as i32 == base {
857 extra_passed += 1;
858 } else {
859 crate::println!("LAYOUT_SELFTEST FAIL: word-spacing ws=0 != base");
860 }
861
862 extra_total += 1;
863 if get_vector_string_width_ls_ws(s, 16, 0, 5) as i32 == base + 2 * 5 {
864 extra_passed += 1;
865 } else {
866 crate::println!("LAYOUT_SELFTEST FAIL: word-spacing ws=5 width");
867 }
868
869 extra_total += 1;
870 if parse_word_spacing(Some("normal")) == 0
871 && parse_word_spacing(Some("3px")) == 3
872 && parse_word_spacing(Some("-2px")) == -2
873 && parse_word_spacing(None) == 0
874 {
875 extra_passed += 1;
876 } else {
877 crate::println!("LAYOUT_SELFTEST FAIL: parse_word_spacing");
878 }
879 }
880
881 {
886 let orig_w = VIEWPORT_HINT_W.load(AtomicOrdering::Relaxed);
891 let orig_h = VIEWPORT_HINT_H.load(AtomicOrdering::Relaxed);
892 set_viewport_hint(1000, 500);
893 let translate_cases: &[(&str, i32, i32)] = &[
894 ("10px", 0, 10),
895 ("50%", 200, 100),
896 ("2rem", 0, 32),
897 ("50vh", 0, 250),
898 ("50vw", 0, 500),
899 ];
900 for (tok, base, want) in translate_cases {
901 extra_total += 1;
902 let got = resolve_translate_len(tok, *base);
903 if got == *want {
904 extra_passed += 1;
905 } else {
906 crate::println!(
907 "LAYOUT_SELFTEST FAIL: resolve_translate_len(`{}`, {}) => {} (want {})",
908 tok, base, got, want
909 );
910 }
911 }
912 VIEWPORT_HINT_W.store(orig_w, AtomicOrdering::Relaxed);
913 VIEWPORT_HINT_H.store(orig_h, AtomicOrdering::Relaxed);
914 }
915
916 (passed + extra_passed, total + extra_total)
917}