diff --git a/crates/browser/src/main.rs b/crates/browser/src/main.rs
index 2fe4ad6..a3ae9bd 100644
--- a/crates/browser/src/main.rs
+++ b/crates/browser/src/main.rs
@@ -78,7 +78,11 @@ fn render_page(page: &PageState, font: &Font, bitmap: &mut BitmapContext) {
}
// Resolve computed styles from DOM + stylesheet.
- let styled = match resolve_styles(&page.doc, std::slice::from_ref(&page.stylesheet)) {
+ let styled = match resolve_styles(
+ &page.doc,
+ std::slice::from_ref(&page.stylesheet),
+ (width as f32, height as f32),
+ ) {
Some(s) => s,
None => return,
};
diff --git a/crates/layout/src/lib.rs b/crates/layout/src/lib.rs
index 2b72121..50144a3 100644
--- a/crates/layout/src/lib.rs
+++ b/crates/layout/src/lib.rs
@@ -97,10 +97,16 @@ pub struct LayoutBox {
pub overflow: Overflow,
/// CSS `box-sizing` property.
pub box_sizing: BoxSizing,
- /// CSS `width` property (explicit or auto).
+ /// CSS `width` property (explicit or auto, may contain percentage).
pub css_width: LengthOrAuto,
- /// CSS `height` property (explicit or auto).
+ /// CSS `height` property (explicit or auto, may contain percentage).
pub css_height: LengthOrAuto,
+ /// CSS margin values (may contain percentages for layout resolution).
+ pub css_margin: [LengthOrAuto; 4],
+ /// CSS padding values (may contain percentages for layout resolution).
+ pub css_padding: [LengthOrAuto; 4],
+ /// CSS position offset values (top, right, bottom, left) for relative positioning.
+ pub css_offsets: [LengthOrAuto; 4],
/// CSS `visibility` property.
pub visibility: Visibility,
}
@@ -140,6 +146,19 @@ impl LayoutBox {
box_sizing: style.box_sizing,
css_width: style.width,
css_height: style.height,
+ css_margin: [
+ style.margin_top,
+ style.margin_right,
+ style.margin_bottom,
+ style.margin_left,
+ ],
+ css_padding: [
+ style.padding_top,
+ style.padding_right,
+ style.padding_bottom,
+ style.padding_left,
+ ],
+ css_offsets: [style.top, style.right, style.bottom, style.left],
visibility: style.visibility,
}
}
@@ -197,20 +216,25 @@ impl LayoutTree {
// Resolve LengthOrAuto to f32
// ---------------------------------------------------------------------------
-fn resolve_length(value: LengthOrAuto) -> f32 {
+/// Resolve a `LengthOrAuto` to px. Percentages are resolved against
+/// `reference` (typically the containing block width). Auto resolves to 0.
+fn resolve_length_against(value: LengthOrAuto, reference: f32) -> f32 {
match value {
LengthOrAuto::Length(px) => px,
+ LengthOrAuto::Percentage(p) => p / 100.0 * reference,
LengthOrAuto::Auto => 0.0,
}
}
/// Resolve horizontal offset for `position: relative`.
/// If both `left` and `right` are specified, `left` wins (CSS2 §9.4.3, ltr).
-fn resolve_relative_horizontal(left: LengthOrAuto, right: LengthOrAuto) -> f32 {
+fn resolve_relative_horizontal(left: LengthOrAuto, right: LengthOrAuto, cb_width: f32) -> f32 {
match left {
LengthOrAuto::Length(px) => px,
+ LengthOrAuto::Percentage(p) => p / 100.0 * cb_width,
LengthOrAuto::Auto => match right {
LengthOrAuto::Length(px) => -px,
+ LengthOrAuto::Percentage(p) => -(p / 100.0 * cb_width),
LengthOrAuto::Auto => 0.0,
},
}
@@ -218,11 +242,13 @@ fn resolve_relative_horizontal(left: LengthOrAuto, right: LengthOrAuto) -> f32 {
/// Resolve vertical offset for `position: relative`.
/// If both `top` and `bottom` are specified, `top` wins (CSS2 §9.4.3).
-fn resolve_relative_vertical(top: LengthOrAuto, bottom: LengthOrAuto) -> f32 {
+fn resolve_relative_vertical(top: LengthOrAuto, bottom: LengthOrAuto, cb_height: f32) -> f32 {
match top {
LengthOrAuto::Length(px) => px,
+ LengthOrAuto::Percentage(p) => p / 100.0 * cb_height,
LengthOrAuto::Auto => match bottom {
LengthOrAuto::Length(px) => -px,
+ LengthOrAuto::Percentage(p) => -(p / 100.0 * cb_height),
LengthOrAuto::Auto => 0.0,
},
}
@@ -263,17 +289,21 @@ fn build_box(
return None;
}
+ // Margin and padding: resolve absolute lengths now; percentages
+ // will be re-resolved in compute_layout against containing block.
+ // Use 0.0 as a placeholder reference for percentages — they'll be
+ // resolved properly in compute_layout.
let margin = EdgeSizes {
- top: resolve_length(style.margin_top),
- right: resolve_length(style.margin_right),
- bottom: resolve_length(style.margin_bottom),
- left: resolve_length(style.margin_left),
+ top: resolve_length_against(style.margin_top, 0.0),
+ right: resolve_length_against(style.margin_right, 0.0),
+ bottom: resolve_length_against(style.margin_bottom, 0.0),
+ left: resolve_length_against(style.margin_left, 0.0),
};
let padding = EdgeSizes {
- top: style.padding_top,
- right: style.padding_right,
- bottom: style.padding_bottom,
- left: style.padding_left,
+ top: resolve_length_against(style.padding_top, 0.0),
+ right: resolve_length_against(style.padding_right, 0.0),
+ bottom: resolve_length_against(style.padding_bottom, 0.0),
+ left: resolve_length_against(style.padding_left, 0.0),
};
let border = EdgeSizes {
top: if style.border_top_style != BorderStyle::None {
@@ -326,12 +356,8 @@ fn build_box(
b.replaced_size = Some((w, h));
}
- // Compute relative position offset.
- if style.position == Position::Relative {
- let dx = resolve_relative_horizontal(style.left, style.right);
- let dy = resolve_relative_vertical(style.top, style.bottom);
- b.relative_offset = (dx, dy);
- }
+ // Relative position offsets are resolved in compute_layout
+ // where the containing block dimensions are known.
Some(b)
}
@@ -421,14 +447,49 @@ fn is_block_level(b: &LayoutBox) -> bool {
// ---------------------------------------------------------------------------
/// Position and size a layout box within `available_width` at position (`x`, `y`).
+///
+/// `available_width` is the containing block width — used as the reference for
+/// percentage widths, margins, and paddings (per CSS spec, even vertical margins/
+/// padding resolve against the containing block width).
fn compute_layout(
b: &mut LayoutBox,
x: f32,
y: f32,
available_width: f32,
+ viewport_height: f32,
font: &Font,
doc: &Document,
) {
+ // Resolve percentage margins against containing block width.
+ // Only re-resolve percentages — absolute margins may have been modified
+ // by margin collapsing and must not be overwritten.
+ if matches!(b.css_margin[0], LengthOrAuto::Percentage(_)) {
+ b.margin.top = resolve_length_against(b.css_margin[0], available_width);
+ }
+ if matches!(b.css_margin[1], LengthOrAuto::Percentage(_)) {
+ b.margin.right = resolve_length_against(b.css_margin[1], available_width);
+ }
+ if matches!(b.css_margin[2], LengthOrAuto::Percentage(_)) {
+ b.margin.bottom = resolve_length_against(b.css_margin[2], available_width);
+ }
+ if matches!(b.css_margin[3], LengthOrAuto::Percentage(_)) {
+ b.margin.left = resolve_length_against(b.css_margin[3], available_width);
+ }
+
+ // Resolve percentage padding against containing block width.
+ if matches!(b.css_padding[0], LengthOrAuto::Percentage(_)) {
+ b.padding.top = resolve_length_against(b.css_padding[0], available_width);
+ }
+ if matches!(b.css_padding[1], LengthOrAuto::Percentage(_)) {
+ b.padding.right = resolve_length_against(b.css_padding[1], available_width);
+ }
+ if matches!(b.css_padding[2], LengthOrAuto::Percentage(_)) {
+ b.padding.bottom = resolve_length_against(b.css_padding[2], available_width);
+ }
+ if matches!(b.css_padding[3], LengthOrAuto::Percentage(_)) {
+ b.padding.left = resolve_length_against(b.css_padding[3], available_width);
+ }
+
let content_x = x + b.margin.left + b.border.left + b.padding.left;
let content_y = y + b.margin.top + b.border.top + b.padding.top;
@@ -440,6 +501,13 @@ fn compute_layout(
BoxSizing::ContentBox => w.max(0.0),
BoxSizing::BorderBox => (w - horizontal_extra).max(0.0),
},
+ LengthOrAuto::Percentage(p) => {
+ let resolved = p / 100.0 * available_width;
+ match b.box_sizing {
+ BoxSizing::ContentBox => resolved.max(0.0),
+ BoxSizing::BorderBox => (resolved - horizontal_extra).max(0.0),
+ }
+ }
LengthOrAuto::Auto => {
(available_width - b.margin.left - b.margin.right - horizontal_extra).max(0.0)
}
@@ -451,18 +519,16 @@ fn compute_layout(
// Replaced elements (e.g.,
) have intrinsic dimensions.
if let Some((rw, rh)) = b.replaced_size {
- // Use CSS width/height if specified, otherwise use replaced dimensions.
- // Content width is the minimum of replaced width and available width.
b.rect.width = rw.min(content_width);
b.rect.height = rh;
- apply_relative_offset(b);
+ apply_relative_offset(b, available_width, viewport_height);
return;
}
match &b.box_type {
BoxType::Block(_) | BoxType::Anonymous => {
if has_block_children(b) {
- layout_block_children(b, font, doc);
+ layout_block_children(b, viewport_height, font, doc);
} else {
layout_inline_children(b, font, doc);
}
@@ -473,23 +539,43 @@ fn compute_layout(
}
// Apply explicit CSS height (adjusted for box-sizing), overriding auto height.
- if let LengthOrAuto::Length(h) = b.css_height {
- let vertical_extra = b.border.top + b.border.bottom + b.padding.top + b.padding.bottom;
- b.rect.height = match b.box_sizing {
- BoxSizing::ContentBox => h.max(0.0),
- BoxSizing::BorderBox => (h - vertical_extra).max(0.0),
- };
+ match b.css_height {
+ LengthOrAuto::Length(h) => {
+ let vertical_extra = b.border.top + b.border.bottom + b.padding.top + b.padding.bottom;
+ b.rect.height = match b.box_sizing {
+ BoxSizing::ContentBox => h.max(0.0),
+ BoxSizing::BorderBox => (h - vertical_extra).max(0.0),
+ };
+ }
+ LengthOrAuto::Percentage(p) => {
+ // Height percentage resolves against containing block height.
+ // For the root element, use viewport height.
+ let cb_height = viewport_height;
+ let resolved = p / 100.0 * cb_height;
+ let vertical_extra = b.border.top + b.border.bottom + b.padding.top + b.padding.bottom;
+ b.rect.height = match b.box_sizing {
+ BoxSizing::ContentBox => resolved.max(0.0),
+ BoxSizing::BorderBox => (resolved - vertical_extra).max(0.0),
+ };
+ }
+ LengthOrAuto::Auto => {}
}
- apply_relative_offset(b);
+ apply_relative_offset(b, available_width, viewport_height);
}
/// Apply `position: relative` offset to a box and all its descendants.
///
-/// This shifts the visual position without affecting the normal-flow layout
-/// of surrounding elements (the original space is preserved).
-fn apply_relative_offset(b: &mut LayoutBox) {
- let (dx, dy) = b.relative_offset;
+/// Resolves the CSS position offsets (which may contain percentages) and
+/// shifts the visual position without affecting the normal-flow layout.
+fn apply_relative_offset(b: &mut LayoutBox, cb_width: f32, cb_height: f32) {
+ if b.position != Position::Relative {
+ return;
+ }
+ let [top, right, bottom, left] = b.css_offsets;
+ let dx = resolve_relative_horizontal(left, right, cb_width);
+ let dy = resolve_relative_vertical(top, bottom, cb_height);
+ b.relative_offset = (dx, dy);
if dx == 0.0 && dy == 0.0 {
return;
}
@@ -627,7 +713,12 @@ fn last_block_bottom_margin(children: &[LayoutBox]) -> Option {
/// Handles adjacent-sibling collapsing, empty-block collapsing, and
/// parent-child internal spacing (the parent's external margins were already
/// updated by `pre_collapse_margins`).
-fn layout_block_children(parent: &mut LayoutBox, font: &Font, doc: &Document) {
+fn layout_block_children(
+ parent: &mut LayoutBox,
+ viewport_height: f32,
+ font: &Font,
+ doc: &Document,
+) {
let content_x = parent.rect.x;
let content_width = parent.rect.width;
let mut cursor_y = parent.rect.y;
@@ -685,6 +776,7 @@ fn layout_block_children(parent: &mut LayoutBox, font: &Font, doc: &Document) {
content_x,
y_for_child,
content_width,
+ viewport_height,
font,
doc,
);
@@ -1015,7 +1107,7 @@ pub fn layout(
styled_root: &StyledNode,
doc: &Document,
viewport_width: f32,
- _viewport_height: f32,
+ viewport_height: f32,
font: &Font,
image_sizes: &HashMap,
) -> LayoutTree {
@@ -1033,7 +1125,15 @@ pub fn layout(
// Pre-collapse parent-child margins before positioning.
pre_collapse_margins(&mut root);
- compute_layout(&mut root, 0.0, 0.0, viewport_width, font, doc);
+ compute_layout(
+ &mut root,
+ 0.0,
+ 0.0,
+ viewport_width,
+ viewport_height,
+ font,
+ doc,
+ );
let height = root.margin_box_height();
LayoutTree {
@@ -1066,7 +1166,7 @@ mod tests {
fn layout_doc(doc: &Document) -> LayoutTree {
let font = test_font();
let sheets = extract_stylesheets(doc);
- let styled = resolve_styles(doc, &sheets).unwrap();
+ let styled = resolve_styles(doc, &sheets, (800.0, 600.0)).unwrap();
layout(&styled, doc, 800.0, 600.0, &font, &HashMap::new())
}
@@ -1075,7 +1175,7 @@ mod tests {
let doc = Document::new();
let font = test_font();
let sheets = extract_stylesheets(&doc);
- let styled = resolve_styles(&doc, &sheets);
+ let styled = resolve_styles(&doc, &sheets, (800.0, 600.0));
if let Some(styled) = styled {
let tree = layout(&styled, &doc, 800.0, 600.0, &font, &HashMap::new());
assert_eq!(tree.width, 800.0);
@@ -1237,7 +1337,7 @@ mod tests {
let font = test_font();
let sheets = extract_stylesheets(&doc);
- let styled = resolve_styles(&doc, &sheets).unwrap();
+ let styled = resolve_styles(&doc, &sheets, (800.0, 600.0)).unwrap();
let tree = layout(&styled, &doc, 100.0, 600.0, &font, &HashMap::new());
let body_box = &tree.root.children[0];
let p_box = &body_box.children[0];
@@ -1403,7 +1503,7 @@ mod tests {
let font = test_font();
let sheets = extract_stylesheets(&doc);
- let styled = resolve_styles(&doc, &sheets).unwrap();
+ let styled = resolve_styles(&doc, &sheets, (800.0, 600.0)).unwrap();
let tree = layout(&styled, &doc, 800.0, 600.0, &font, &HashMap::new());
let body_box = &tree.root.children[0];
@@ -1478,7 +1578,7 @@ p { margin-top: 50px; margin-bottom: 50px; }
let doc = we_html::parse_html(html_str);
let font = test_font();
let sheets = extract_stylesheets(&doc);
- let styled = resolve_styles(&doc, &sheets).unwrap();
+ let styled = resolve_styles(&doc, &sheets, (800.0, 600.0)).unwrap();
let tree = layout(&styled, &doc, 800.0, 600.0, &font, &HashMap::new());
let body_box = &tree.root.children[0];
@@ -1509,7 +1609,7 @@ p { margin-top: 50px; margin-bottom: 50px; }
let doc = we_html::parse_html(html_str);
let font = test_font();
let sheets = extract_stylesheets(&doc);
- let styled = resolve_styles(&doc, &sheets).unwrap();
+ let styled = resolve_styles(&doc, &sheets, (800.0, 600.0)).unwrap();
let tree = layout(&styled, &doc, 800.0, 600.0, &font, &HashMap::new());
let body_box = &tree.root.children[0];
@@ -1529,7 +1629,7 @@ p { margin-top: 50px; margin-bottom: 50px; }
let doc = we_html::parse_html(html_str);
let font = test_font();
let sheets = extract_stylesheets(&doc);
- let styled = resolve_styles(&doc, &sheets).unwrap();
+ let styled = resolve_styles(&doc, &sheets, (800.0, 600.0)).unwrap();
let tree = layout(&styled, &doc, 800.0, 600.0, &font, &HashMap::new());
let body_box = &tree.root.children[0];
@@ -1551,7 +1651,7 @@ p { margin-top: 50px; margin-bottom: 50px; }
let doc = we_html::parse_html(html_str);
let font = test_font();
let sheets = extract_stylesheets(&doc);
- let styled = resolve_styles(&doc, &sheets).unwrap();
+ let styled = resolve_styles(&doc, &sheets, (800.0, 600.0)).unwrap();
let tree = layout(&styled, &doc, 800.0, 600.0, &font, &HashMap::new());
let body_box = &tree.root.children[0];
@@ -1577,7 +1677,7 @@ p { margin-top: 50px; margin-bottom: 50px; }
let doc = we_html::parse_html(html_str);
let font = test_font();
let sheets = extract_stylesheets(&doc);
- let styled = resolve_styles(&doc, &sheets).unwrap();
+ let styled = resolve_styles(&doc, &sheets, (800.0, 600.0)).unwrap();
let tree = layout(&styled, &doc, 800.0, 600.0, &font, &HashMap::new());
let body_box = &tree.root.children[0];
@@ -1604,7 +1704,7 @@ p { margin-top: 50px; margin-bottom: 50px; }
let doc = we_html::parse_html(html_str);
let font = test_font();
let sheets = extract_stylesheets(&doc);
- let styled = resolve_styles(&doc, &sheets).unwrap();
+ let styled = resolve_styles(&doc, &sheets, (800.0, 600.0)).unwrap();
let tree = layout(&styled, &doc, 800.0, 600.0, &font, &HashMap::new());
let body_box = &tree.root.children[0];
@@ -1631,7 +1731,7 @@ p { margin-top: 50px; margin-bottom: 50px; }
let doc = we_html::parse_html(html_str);
let font = test_font();
let sheets = extract_stylesheets(&doc);
- let styled = resolve_styles(&doc, &sheets).unwrap();
+ let styled = resolve_styles(&doc, &sheets, (800.0, 600.0)).unwrap();
let tree = layout(&styled, &doc, 800.0, 600.0, &font, &HashMap::new());
let body_box = &tree.root.children[0];
@@ -1658,7 +1758,7 @@ p { margin-top: 50px; margin-bottom: 50px; }
let doc = we_html::parse_html(html_str);
let font = test_font();
let sheets = extract_stylesheets(&doc);
- let styled = resolve_styles(&doc, &sheets).unwrap();
+ let styled = resolve_styles(&doc, &sheets, (800.0, 600.0)).unwrap();
let tree = layout(&styled, &doc, 800.0, 600.0, &font, &HashMap::new());
let body_box = &tree.root.children[0];
@@ -1691,7 +1791,7 @@ p { margin-top: 50px; margin-bottom: 50px; }
let doc = we_html::parse_html(html_str);
let font = test_font();
let sheets = extract_stylesheets(&doc);
- let styled = resolve_styles(&doc, &sheets).unwrap();
+ let styled = resolve_styles(&doc, &sheets, (800.0, 600.0)).unwrap();
let tree = layout(&styled, &doc, 800.0, 600.0, &font, &HashMap::new());
let body_box = &tree.root.children[0];
@@ -1714,7 +1814,7 @@ p { margin-top: 50px; margin-bottom: 50px; }
let doc = we_html::parse_html(html_str);
let font = test_font();
let sheets = extract_stylesheets(&doc);
- let styled = resolve_styles(&doc, &sheets).unwrap();
+ let styled = resolve_styles(&doc, &sheets, (800.0, 600.0)).unwrap();
// Narrow viewport to force wrapping.
let tree = layout(&styled, &doc, 100.0, 600.0, &font, &HashMap::new());
@@ -1747,7 +1847,7 @@ p { margin-top: 50px; margin-bottom: 50px; }
let doc = we_html::parse_html(html_str);
let font = test_font();
let sheets = extract_stylesheets(&doc);
- let styled = resolve_styles(&doc, &sheets).unwrap();
+ let styled = resolve_styles(&doc, &sheets, (800.0, 600.0)).unwrap();
let tree = layout(&styled, &doc, 800.0, 600.0, &font, &HashMap::new());
let body_box = &tree.root.children[0];
@@ -1787,7 +1887,7 @@ p { margin: 0; }
let doc = we_html::parse_html(html_str);
let font = test_font();
let sheets = extract_stylesheets(&doc);
- let styled = resolve_styles(&doc, &sheets).unwrap();
+ let styled = resolve_styles(&doc, &sheets, (800.0, 600.0)).unwrap();
let tree = layout(&styled, &doc, 800.0, 600.0, &font, &HashMap::new());
let body_box = &tree.root.children[0];
@@ -1825,7 +1925,7 @@ p { margin: 0; }
let doc = we_html::parse_html(html_str);
let font = test_font();
let sheets = extract_stylesheets(&doc);
- let styled = resolve_styles(&doc, &sheets).unwrap();
+ let styled = resolve_styles(&doc, &sheets, (800.0, 600.0)).unwrap();
let tree = layout(&styled, &doc, 800.0, 600.0, &font, &HashMap::new());
let body_box = &tree.root.children[0];
@@ -1848,7 +1948,7 @@ p { margin: 0; }
let doc = we_html::parse_html(html_str);
let font = test_font();
let sheets = extract_stylesheets(&doc);
- let styled = resolve_styles(&doc, &sheets).unwrap();
+ let styled = resolve_styles(&doc, &sheets, (800.0, 600.0)).unwrap();
let tree = layout(&styled, &doc, 800.0, 600.0, &font, &HashMap::new());
let body_box = &tree.root.children[0];
@@ -1871,7 +1971,7 @@ p { margin: 0; }
let doc = we_html::parse_html(html_str);
let font = test_font();
let sheets = extract_stylesheets(&doc);
- let styled = resolve_styles(&doc, &sheets).unwrap();
+ let styled = resolve_styles(&doc, &sheets, (800.0, 600.0)).unwrap();
let tree = layout(&styled, &doc, 800.0, 600.0, &font, &HashMap::new());
let body_box = &tree.root.children[0];
@@ -1892,7 +1992,7 @@ p { margin: 0; }
let doc = we_html::parse_html(html_str);
let font = test_font();
let sheets = extract_stylesheets(&doc);
- let styled = resolve_styles(&doc, &sheets).unwrap();
+ let styled = resolve_styles(&doc, &sheets, (800.0, 600.0)).unwrap();
let tree = layout(&styled, &doc, 800.0, 600.0, &font, &HashMap::new());
let body_box = &tree.root.children[0];
@@ -1926,7 +2026,7 @@ p { margin: 0; }
let doc = we_html::parse_html(html_str);
let font = test_font();
let sheets = extract_stylesheets(&doc);
- let styled = resolve_styles(&doc, &sheets).unwrap();
+ let styled = resolve_styles(&doc, &sheets, (800.0, 600.0)).unwrap();
let tree = layout(&styled, &doc, 800.0, 600.0, &font, &HashMap::new());
let body_box = &tree.root.children[0];
@@ -1955,7 +2055,7 @@ p { margin-top: 16px; margin-bottom: 16px; }
let doc = we_html::parse_html(html_str);
let font = test_font();
let sheets = extract_stylesheets(&doc);
- let styled = resolve_styles(&doc, &sheets).unwrap();
+ let styled = resolve_styles(&doc, &sheets, (800.0, 600.0)).unwrap();
let tree = layout(&styled, &doc, 800.0, 600.0, &font, &HashMap::new());
let body_box = &tree.root.children[0];
@@ -1991,7 +2091,7 @@ body { margin: 0; border-top: 1px solid black; }
let doc = we_html::parse_html(html_str);
let font = test_font();
let sheets = extract_stylesheets(&doc);
- let styled = resolve_styles(&doc, &sheets).unwrap();
+ let styled = resolve_styles(&doc, &sheets, (800.0, 600.0)).unwrap();
let tree = layout(&styled, &doc, 800.0, 600.0, &font, &HashMap::new());
let body_box = &tree.root.children[0];
@@ -2024,7 +2124,7 @@ body { margin: 0; border-top: 1px solid black; }
let doc = we_html::parse_html(html_str);
let font = test_font();
let sheets = extract_stylesheets(&doc);
- let styled = resolve_styles(&doc, &sheets).unwrap();
+ let styled = resolve_styles(&doc, &sheets, (800.0, 600.0)).unwrap();
let tree = layout(&styled, &doc, 800.0, 600.0, &font, &HashMap::new());
let body_box = &tree.root.children[0];
@@ -2052,7 +2152,7 @@ body { margin: 0; border-top: 1px solid black; }
let doc = we_html::parse_html(html_str);
let font = test_font();
let sheets = extract_stylesheets(&doc);
- let styled = resolve_styles(&doc, &sheets).unwrap();
+ let styled = resolve_styles(&doc, &sheets, (800.0, 600.0)).unwrap();
let tree = layout(&styled, &doc, 800.0, 600.0, &font, &HashMap::new());
let body_box = &tree.root.children[0];
@@ -2087,7 +2187,7 @@ body { margin: 0; border-top: 1px solid black; }
let doc = we_html::parse_html(html_str);
let font = test_font();
let sheets = extract_stylesheets(&doc);
- let styled = resolve_styles(&doc, &sheets).unwrap();
+ let styled = resolve_styles(&doc, &sheets, (800.0, 600.0)).unwrap();
let tree = layout(&styled, &doc, 800.0, 600.0, &font, &HashMap::new());
let body_box = &tree.root.children[0];
@@ -2122,7 +2222,7 @@ body { margin: 0; border-top: 1px solid black; }
let doc = we_html::parse_html(html_str);
let font = test_font();
let sheets = extract_stylesheets(&doc);
- let styled = resolve_styles(&doc, &sheets).unwrap();
+ let styled = resolve_styles(&doc, &sheets, (800.0, 600.0)).unwrap();
let tree = layout(&styled, &doc, 800.0, 600.0, &font, &HashMap::new());
let body_box = &tree.root.children[0];
@@ -2158,7 +2258,7 @@ body { margin: 0; border-top: 1px solid black; }
let doc = we_html::parse_html(html_str);
let font = test_font();
let sheets = extract_stylesheets(&doc);
- let styled = resolve_styles(&doc, &sheets).unwrap();
+ let styled = resolve_styles(&doc, &sheets, (800.0, 600.0)).unwrap();
let tree = layout(&styled, &doc, 800.0, 600.0, &font, &HashMap::new());
let body_box = &tree.root.children[0];
@@ -2187,7 +2287,7 @@ p { margin-top: 5px; margin-bottom: 5px; }
let doc = we_html::parse_html(html_str);
let font = test_font();
let sheets = extract_stylesheets(&doc);
- let styled = resolve_styles(&doc, &sheets).unwrap();
+ let styled = resolve_styles(&doc, &sheets, (800.0, 600.0)).unwrap();
let tree = layout(&styled, &doc, 800.0, 600.0, &font, &HashMap::new());
let body_box = &tree.root.children[0];
@@ -2238,7 +2338,7 @@ div { width: 200px; padding: 10px; border: 5px solid black; }
let doc = we_html::parse_html(html_str);
let font = test_font();
let sheets = extract_stylesheets(&doc);
- let styled = resolve_styles(&doc, &sheets).unwrap();
+ let styled = resolve_styles(&doc, &sheets, (800.0, 600.0)).unwrap();
let tree = layout(&styled, &doc, 800.0, 600.0, &font, &HashMap::new());
let body_box = &tree.root.children[0];
@@ -2268,7 +2368,7 @@ div { box-sizing: border-box; width: 200px; padding: 10px; border: 5px solid bla
let doc = we_html::parse_html(html_str);
let font = test_font();
let sheets = extract_stylesheets(&doc);
- let styled = resolve_styles(&doc, &sheets).unwrap();
+ let styled = resolve_styles(&doc, &sheets, (800.0, 600.0)).unwrap();
let tree = layout(&styled, &doc, 800.0, 600.0, &font, &HashMap::new());
let body_box = &tree.root.children[0];
@@ -2298,7 +2398,7 @@ div { box-sizing: border-box; width: 20px; padding: 15px; border: 5px solid blac
let doc = we_html::parse_html(html_str);
let font = test_font();
let sheets = extract_stylesheets(&doc);
- let styled = resolve_styles(&doc, &sheets).unwrap();
+ let styled = resolve_styles(&doc, &sheets, (800.0, 600.0)).unwrap();
let tree = layout(&styled, &doc, 800.0, 600.0, &font, &HashMap::new());
let body_box = &tree.root.children[0];
@@ -2325,7 +2425,7 @@ body { margin: 0; }
let doc = we_html::parse_html(html_str);
let font = test_font();
let sheets = extract_stylesheets(&doc);
- let styled = resolve_styles(&doc, &sheets).unwrap();
+ let styled = resolve_styles(&doc, &sheets, (800.0, 600.0)).unwrap();
let tree = layout(&styled, &doc, 800.0, 600.0, &font, &HashMap::new());
let body_box = &tree.root.children[0];
@@ -2354,7 +2454,7 @@ div { box-sizing: border-box; height: 100px; padding: 10px; border: 5px solid bl
let doc = we_html::parse_html(html_str);
let font = test_font();
let sheets = extract_stylesheets(&doc);
- let styled = resolve_styles(&doc, &sheets).unwrap();
+ let styled = resolve_styles(&doc, &sheets, (800.0, 600.0)).unwrap();
let tree = layout(&styled, &doc, 800.0, 600.0, &font, &HashMap::new());
let body_box = &tree.root.children[0];
@@ -2380,7 +2480,7 @@ div { height: 100px; padding: 10px; border: 5px solid black; }
let doc = we_html::parse_html(html_str);
let font = test_font();
let sheets = extract_stylesheets(&doc);
- let styled = resolve_styles(&doc, &sheets).unwrap();
+ let styled = resolve_styles(&doc, &sheets, (800.0, 600.0)).unwrap();
let tree = layout(&styled, &doc, 800.0, 600.0, &font, &HashMap::new());
let body_box = &tree.root.children[0];
@@ -2410,7 +2510,7 @@ p { margin: 0; }
let doc = we_html::parse_html(html_str);
let font = test_font();
let sheets = extract_stylesheets(&doc);
- let styled = resolve_styles(&doc, &sheets).unwrap();
+ let styled = resolve_styles(&doc, &sheets, (800.0, 600.0)).unwrap();
let tree = layout(&styled, &doc, 800.0, 600.0, &font, &HashMap::new());
let body_box = &tree.root.children[0];
@@ -2444,7 +2544,7 @@ p { margin: 0; }
let doc = we_html::parse_html(html_str);
let font = test_font();
let sheets = extract_stylesheets(&doc);
- let styled = resolve_styles(&doc, &sheets).unwrap();
+ let styled = resolve_styles(&doc, &sheets, (800.0, 600.0)).unwrap();
let tree = layout(&styled, &doc, 800.0, 600.0, &font, &HashMap::new());
let body_box = &tree.root.children[0];
@@ -2478,7 +2578,7 @@ body { margin: 0; }
let doc = we_html::parse_html(html_str);
let font = test_font();
let sheets = extract_stylesheets(&doc);
- let styled = resolve_styles(&doc, &sheets).unwrap();
+ let styled = resolve_styles(&doc, &sheets, (800.0, 600.0)).unwrap();
let tree = layout(&styled, &doc, 800.0, 600.0, &font, &HashMap::new());
let body_box = &tree.root.children[0];
@@ -2504,7 +2604,7 @@ body { margin: 0; }
let doc = we_html::parse_html(html_str);
let font = test_font();
let sheets = extract_stylesheets(&doc);
- let styled = resolve_styles(&doc, &sheets).unwrap();
+ let styled = resolve_styles(&doc, &sheets, (800.0, 600.0)).unwrap();
let tree = layout(&styled, &doc, 800.0, 600.0, &font, &HashMap::new());
let body_box = &tree.root.children[0];
@@ -2529,7 +2629,7 @@ div { visibility: collapse; height: 50px; }
let doc = we_html::parse_html(html_str);
let font = test_font();
let sheets = extract_stylesheets(&doc);
- let styled = resolve_styles(&doc, &sheets).unwrap();
+ let styled = resolve_styles(&doc, &sheets, (800.0, 600.0)).unwrap();
let tree = layout(&styled, &doc, 800.0, 600.0, &font, &HashMap::new());
let body_box = &tree.root.children[0];
@@ -2538,4 +2638,259 @@ div { visibility: collapse; height: 50px; }
// Still occupies space (non-table collapse = hidden behavior).
assert_eq!(div_box.rect.height, 50.0);
}
+
+ // --- Viewport units and percentage resolution tests ---
+
+ #[test]
+ fn width_50_percent_resolves_to_half_containing_block() {
+ let html_str = r#"
+
+
+Half width
+"#;
+ let doc = we_html::parse_html(html_str);
+ let font = test_font();
+ let sheets = extract_stylesheets(&doc);
+ let styled = resolve_styles(&doc, &sheets, (800.0, 600.0)).unwrap();
+ let tree = layout(&styled, &doc, 800.0, 600.0, &font, &HashMap::new());
+
+ let body_box = &tree.root.children[0];
+ let div_box = &body_box.children[0];
+ assert!(
+ (div_box.rect.width - 400.0).abs() < 0.01,
+ "width: 50% should be 400px on 800px viewport, got {}",
+ div_box.rect.width
+ );
+ }
+
+ #[test]
+ fn margin_10_percent_resolves_against_containing_block_width() {
+ let html_str = r#"
+
+
+Box
+"#;
+ let doc = we_html::parse_html(html_str);
+ let font = test_font();
+ let sheets = extract_stylesheets(&doc);
+ let styled = resolve_styles(&doc, &sheets, (800.0, 600.0)).unwrap();
+ let tree = layout(&styled, &doc, 800.0, 600.0, &font, &HashMap::new());
+
+ let body_box = &tree.root.children[0];
+ let div_box = &body_box.children[0];
+ // All margins (including top/bottom) resolve against containing block WIDTH.
+ assert!(
+ (div_box.margin.left - 80.0).abs() < 0.01,
+ "margin-left: 10% should be 80px on 800px viewport, got {}",
+ div_box.margin.left
+ );
+ assert!(
+ (div_box.margin.top - 80.0).abs() < 0.01,
+ "margin-top: 10% should be 80px (against width, not height), got {}",
+ div_box.margin.top
+ );
+ }
+
+ #[test]
+ fn width_50vw_resolves_to_half_viewport() {
+ let html_str = r#"
+
+
+Half viewport
+"#;
+ let doc = we_html::parse_html(html_str);
+ let font = test_font();
+ let sheets = extract_stylesheets(&doc);
+ let styled = resolve_styles(&doc, &sheets, (800.0, 600.0)).unwrap();
+ let tree = layout(&styled, &doc, 800.0, 600.0, &font, &HashMap::new());
+
+ let body_box = &tree.root.children[0];
+ let div_box = &body_box.children[0];
+ assert!(
+ (div_box.rect.width - 400.0).abs() < 0.01,
+ "width: 50vw should be 400px on 800px viewport, got {}",
+ div_box.rect.width
+ );
+ }
+
+ #[test]
+ fn height_100vh_resolves_to_full_viewport() {
+ let html_str = r#"
+
+
+Full height
+"#;
+ let doc = we_html::parse_html(html_str);
+ let font = test_font();
+ let sheets = extract_stylesheets(&doc);
+ let styled = resolve_styles(&doc, &sheets, (800.0, 600.0)).unwrap();
+ let tree = layout(&styled, &doc, 800.0, 600.0, &font, &HashMap::new());
+
+ let body_box = &tree.root.children[0];
+ let div_box = &body_box.children[0];
+ assert!(
+ (div_box.rect.height - 600.0).abs() < 0.01,
+ "height: 100vh should be 600px on 600px viewport, got {}",
+ div_box.rect.height
+ );
+ }
+
+ #[test]
+ fn font_size_5vmin_resolves_to_smaller_dimension() {
+ let html_str = r#"
+
+
+Text
+"#;
+ let doc = we_html::parse_html(html_str);
+ let font = test_font();
+ let sheets = extract_stylesheets(&doc);
+ // viewport 800x600 → vmin = 600
+ let styled = resolve_styles(&doc, &sheets, (800.0, 600.0)).unwrap();
+ let tree = layout(&styled, &doc, 800.0, 600.0, &font, &HashMap::new());
+
+ let body_box = &tree.root.children[0];
+ let p_box = &body_box.children[0];
+ // 5vmin = 5% of min(800, 600) = 5% of 600 = 30px
+ assert!(
+ (p_box.font_size - 30.0).abs() < 0.01,
+ "font-size: 5vmin should be 30px, got {}",
+ p_box.font_size
+ );
+ }
+
+ #[test]
+ fn nested_percentage_widths_compound() {
+ let html_str = r#"
+
+
+
+
+
+"#;
+ let doc = we_html::parse_html(html_str);
+ let font = test_font();
+ let sheets = extract_stylesheets(&doc);
+ let styled = resolve_styles(&doc, &sheets, (800.0, 600.0)).unwrap();
+ let tree = layout(&styled, &doc, 800.0, 600.0, &font, &HashMap::new());
+
+ let body_box = &tree.root.children[0];
+ let outer_box = &body_box.children[0];
+ let inner_box = &outer_box.children[0];
+ // outer = 50% of 800 = 400
+ assert!(
+ (outer_box.rect.width - 400.0).abs() < 0.01,
+ "outer width should be 400px, got {}",
+ outer_box.rect.width
+ );
+ // inner = 50% of 400 = 200
+ assert!(
+ (inner_box.rect.width - 200.0).abs() < 0.01,
+ "inner width should be 200px (50% of 400), got {}",
+ inner_box.rect.width
+ );
+ }
+
+ #[test]
+ fn padding_percent_resolves_against_width() {
+ let html_str = r#"
+
+
+Content
+"#;
+ let doc = we_html::parse_html(html_str);
+ let font = test_font();
+ let sheets = extract_stylesheets(&doc);
+ let styled = resolve_styles(&doc, &sheets, (800.0, 600.0)).unwrap();
+ let tree = layout(&styled, &doc, 800.0, 600.0, &font, &HashMap::new());
+
+ let body_box = &tree.root.children[0];
+ let div_box = &body_box.children[0];
+ // padding: 5% resolves against containing block width (800px)
+ assert!(
+ (div_box.padding.top - 40.0).abs() < 0.01,
+ "padding-top: 5% should be 40px (5% of 800), got {}",
+ div_box.padding.top
+ );
+ assert!(
+ (div_box.padding.left - 40.0).abs() < 0.01,
+ "padding-left: 5% should be 40px (5% of 800), got {}",
+ div_box.padding.left
+ );
+ }
+
+ #[test]
+ fn vmax_uses_larger_dimension() {
+ let html_str = r#"
+
+
+Content
+"#;
+ let doc = we_html::parse_html(html_str);
+ let font = test_font();
+ let sheets = extract_stylesheets(&doc);
+ // viewport 800x600 → vmax = 800
+ let styled = resolve_styles(&doc, &sheets, (800.0, 600.0)).unwrap();
+ let tree = layout(&styled, &doc, 800.0, 600.0, &font, &HashMap::new());
+
+ let body_box = &tree.root.children[0];
+ let div_box = &body_box.children[0];
+ // 10vmax = 10% of max(800, 600) = 10% of 800 = 80px
+ assert!(
+ (div_box.rect.width - 80.0).abs() < 0.01,
+ "width: 10vmax should be 80px, got {}",
+ div_box.rect.width
+ );
+ }
+
+ #[test]
+ fn height_50_percent_resolves_against_viewport() {
+ let html_str = r#"
+
+
+Half height
+"#;
+ let doc = we_html::parse_html(html_str);
+ let font = test_font();
+ let sheets = extract_stylesheets(&doc);
+ let styled = resolve_styles(&doc, &sheets, (800.0, 600.0)).unwrap();
+ let tree = layout(&styled, &doc, 800.0, 600.0, &font, &HashMap::new());
+
+ let body_box = &tree.root.children[0];
+ let div_box = &body_box.children[0];
+ // height: 50% resolves against viewport height (600)
+ assert!(
+ (div_box.rect.height - 300.0).abs() < 0.01,
+ "height: 50% should be 300px on 600px viewport, got {}",
+ div_box.rect.height
+ );
+ }
}
diff --git a/crates/render/src/lib.rs b/crates/render/src/lib.rs
index 8641992..9101f51 100644
--- a/crates/render/src/lib.rs
+++ b/crates/render/src/lib.rs
@@ -490,7 +490,7 @@ mod tests {
fn layout_doc(doc: &Document) -> we_layout::LayoutTree {
let font = test_font();
let sheets = extract_stylesheets(doc);
- let styled = resolve_styles(doc, &sheets).unwrap();
+ let styled = resolve_styles(doc, &sheets, (800.0, 600.0)).unwrap();
we_layout::layout(
&styled,
doc,
@@ -551,7 +551,7 @@ mod tests {
let doc = Document::new();
let font = test_font();
let sheets = extract_stylesheets(&doc);
- let styled = resolve_styles(&doc, &sheets);
+ let styled = resolve_styles(&doc, &sheets, (800.0, 600.0));
if let Some(styled) = styled {
let tree = we_layout::layout(
&styled,
@@ -726,7 +726,7 @@ mod tests {
let doc = we_html::parse_html(html_str);
let font = test_font();
let sheets = extract_stylesheets(&doc);
- let styled = resolve_styles(&doc, &sheets).unwrap();
+ let styled = resolve_styles(&doc, &sheets, (800.0, 600.0)).unwrap();
let tree = we_layout::layout(
&styled,
&doc,
@@ -760,7 +760,7 @@ mod tests {
let doc = we_html::parse_html(html_str);
let font = test_font();
let sheets = extract_stylesheets(&doc);
- let styled = resolve_styles(&doc, &sheets).unwrap();
+ let styled = resolve_styles(&doc, &sheets, (800.0, 600.0)).unwrap();
let tree = we_layout::layout(
&styled,
&doc,
@@ -793,7 +793,7 @@ mod tests {
let doc = we_html::parse_html(html_str);
let font = test_font();
let sheets = extract_stylesheets(&doc);
- let styled = resolve_styles(&doc, &sheets).unwrap();
+ let styled = resolve_styles(&doc, &sheets, (800.0, 600.0)).unwrap();
let tree = we_layout::layout(
&styled,
&doc,
diff --git a/crates/style/src/computed.rs b/crates/style/src/computed.rs
index c34ffe2..1997b9a 100644
--- a/crates/style/src/computed.rs
+++ b/crates/style/src/computed.rs
@@ -149,10 +149,12 @@ pub enum BorderStyle {
// LengthOrAuto
// ---------------------------------------------------------------------------
-/// A computed length (resolved to px) or `auto`.
+/// A computed length (resolved to px), a percentage (unresolved), or `auto`.
#[derive(Debug, Clone, Copy, PartialEq, Default)]
pub enum LengthOrAuto {
Length(f32),
+ /// Percentage value (0.0–100.0), resolved during layout against the containing block.
+ Percentage(f32),
#[default]
Auto,
}
@@ -173,11 +175,11 @@ pub struct ComputedStyle {
pub margin_bottom: LengthOrAuto,
pub margin_left: LengthOrAuto,
- // Box model: padding (no auto for padding)
- pub padding_top: f32,
- pub padding_right: f32,
- pub padding_bottom: f32,
- pub padding_left: f32,
+ // Box model: padding (percentages resolve against containing block width)
+ pub padding_top: LengthOrAuto,
+ pub padding_right: LengthOrAuto,
+ pub padding_bottom: LengthOrAuto,
+ pub padding_left: LengthOrAuto,
// Box model: border width
pub border_top_width: f32,
@@ -241,10 +243,10 @@ impl Default for ComputedStyle {
margin_bottom: LengthOrAuto::Length(0.0),
margin_left: LengthOrAuto::Length(0.0),
- padding_top: 0.0,
- padding_right: 0.0,
- padding_bottom: 0.0,
- padding_left: 0.0,
+ padding_top: LengthOrAuto::Length(0.0),
+ padding_right: LengthOrAuto::Length(0.0),
+ padding_bottom: LengthOrAuto::Length(0.0),
+ padding_left: LengthOrAuto::Length(0.0),
border_top_width: 0.0,
border_right_width: 0.0,
@@ -406,20 +408,9 @@ u {
// Resolve a CssValue to f32 px given context
// ---------------------------------------------------------------------------
-fn resolve_length(value: &CssValue, _parent_font_size: f32, current_font_size: f32) -> Option {
- match value {
- // Em units resolve relative to the element's own computed font-size
- // (for properties other than font-size, which has its own handling).
- CssValue::Length(n, unit) => Some(resolve_length_unit(*n, *unit, current_font_size)),
- CssValue::Percentage(p) => Some((*p / 100.0) as f32 * current_font_size),
- CssValue::Zero => Some(0.0),
- CssValue::Number(n) if *n == 0.0 => Some(0.0),
- _ => None,
- }
-}
-
-fn resolve_length_unit(value: f64, unit: LengthUnit, em_base: f32) -> f32 {
+fn resolve_length_unit(value: f64, unit: LengthUnit, em_base: f32, viewport: (f32, f32)) -> f32 {
let v = value as f32;
+ let (vw, vh) = viewport;
match unit {
LengthUnit::Px => v,
LengthUnit::Em => v * em_base,
@@ -429,21 +420,31 @@ fn resolve_length_unit(value: f64, unit: LengthUnit, em_base: f32) -> f32 {
LengthUnit::Mm => v * (96.0 / 25.4),
LengthUnit::In => v * 96.0,
LengthUnit::Pc => v * 16.0,
- // Viewport units: use a reasonable default (can be parameterized later)
- LengthUnit::Vw | LengthUnit::Vh | LengthUnit::Vmin | LengthUnit::Vmax => v,
+ LengthUnit::Vw => v * vw / 100.0,
+ LengthUnit::Vh => v * vh / 100.0,
+ LengthUnit::Vmin => v * vw.min(vh) / 100.0,
+ LengthUnit::Vmax => v * vw.max(vh) / 100.0,
}
}
-fn resolve_length_or_auto(
+/// Resolve a CSS value to `LengthOrAuto` for layout properties (width, height,
+/// margin, padding, top/right/bottom/left). Percentages are preserved as
+/// `LengthOrAuto::Percentage` for later resolution during layout against the
+/// containing block.
+fn resolve_layout_length_or_auto(
value: &CssValue,
- parent_font_size: f32,
current_font_size: f32,
+ viewport: (f32, f32),
) -> LengthOrAuto {
match value {
CssValue::Auto => LengthOrAuto::Auto,
- _ => resolve_length(value, parent_font_size, current_font_size)
- .map(LengthOrAuto::Length)
- .unwrap_or(LengthOrAuto::Auto),
+ CssValue::Percentage(p) => LengthOrAuto::Percentage(*p as f32),
+ CssValue::Length(n, unit) => {
+ LengthOrAuto::Length(resolve_length_unit(*n, *unit, current_font_size, viewport))
+ }
+ CssValue::Zero => LengthOrAuto::Length(0.0),
+ CssValue::Number(n) if *n == 0.0 => LengthOrAuto::Length(0.0),
+ _ => LengthOrAuto::Auto,
}
}
@@ -465,6 +466,7 @@ fn apply_property(
property: &str,
value: &CssValue,
parent: &ComputedStyle,
+ viewport: (f32, f32),
) {
// Handle inherit/initial/unset
match value {
@@ -503,45 +505,37 @@ fn apply_property(
};
}
- // Margin
+ // Margin (percentages preserved for layout resolution)
"margin-top" => {
- style.margin_top = resolve_length_or_auto(value, parent_fs, current_fs);
+ style.margin_top = resolve_layout_length_or_auto(value, current_fs, viewport);
}
"margin-right" => {
- style.margin_right = resolve_length_or_auto(value, parent_fs, current_fs);
+ style.margin_right = resolve_layout_length_or_auto(value, current_fs, viewport);
}
"margin-bottom" => {
- style.margin_bottom = resolve_length_or_auto(value, parent_fs, current_fs);
+ style.margin_bottom = resolve_layout_length_or_auto(value, current_fs, viewport);
}
"margin-left" => {
- style.margin_left = resolve_length_or_auto(value, parent_fs, current_fs);
+ style.margin_left = resolve_layout_length_or_auto(value, current_fs, viewport);
}
- // Padding
+ // Padding (percentages preserved for layout resolution)
"padding-top" => {
- if let Some(v) = resolve_length(value, parent_fs, current_fs) {
- style.padding_top = v;
- }
+ style.padding_top = resolve_layout_length_or_auto(value, current_fs, viewport);
}
"padding-right" => {
- if let Some(v) = resolve_length(value, parent_fs, current_fs) {
- style.padding_right = v;
- }
+ style.padding_right = resolve_layout_length_or_auto(value, current_fs, viewport);
}
"padding-bottom" => {
- if let Some(v) = resolve_length(value, parent_fs, current_fs) {
- style.padding_bottom = v;
- }
+ style.padding_bottom = resolve_layout_length_or_auto(value, current_fs, viewport);
}
"padding-left" => {
- if let Some(v) = resolve_length(value, parent_fs, current_fs) {
- style.padding_left = v;
- }
+ style.padding_left = resolve_layout_length_or_auto(value, current_fs, viewport);
}
// Border width
"border-top-width" | "border-right-width" | "border-bottom-width" | "border-left-width" => {
- let w = resolve_border_width(value, parent_fs);
+ let w = resolve_border_width(value, parent_fs, viewport);
match property {
"border-top-width" => style.border_top_width = w,
"border-right-width" => style.border_right_width = w,
@@ -553,7 +547,7 @@ fn apply_property(
// Border width shorthand (single value applied to all sides)
"border-width" => {
- let w = resolve_border_width(value, parent_fs);
+ let w = resolve_border_width(value, parent_fs, viewport);
style.border_top_width = w;
style.border_right_width = w;
style.border_bottom_width = w;
@@ -602,12 +596,12 @@ fn apply_property(
}
}
- // Dimensions
+ // Dimensions (percentages preserved for layout resolution)
"width" => {
- style.width = resolve_length_or_auto(value, parent_fs, current_fs);
+ style.width = resolve_layout_length_or_auto(value, current_fs, viewport);
}
"height" => {
- style.height = resolve_length_or_auto(value, parent_fs, current_fs);
+ style.height = resolve_layout_length_or_auto(value, current_fs, viewport);
}
// Box sizing
@@ -634,7 +628,7 @@ fn apply_property(
"font-size" => {
match value {
CssValue::Length(n, unit) => {
- style.font_size = resolve_length_unit(*n, *unit, parent_fs);
+ style.font_size = resolve_length_unit(*n, *unit, parent_fs, viewport);
}
CssValue::Percentage(p) => {
style.font_size = (*p / 100.0) as f32 * parent_fs;
@@ -734,7 +728,7 @@ fn apply_property(
style.line_height = *n as f32 * style.font_size;
}
CssValue::Length(n, unit) => {
- style.line_height = resolve_length_unit(*n, *unit, style.font_size);
+ style.line_height = resolve_length_unit(*n, *unit, style.font_size, viewport);
}
CssValue::Percentage(p) => {
style.line_height = (*p / 100.0) as f32 * style.font_size;
@@ -763,11 +757,11 @@ fn apply_property(
};
}
- // Position offsets
- "top" => style.top = resolve_length_or_auto(value, parent_fs, current_fs),
- "right" => style.right = resolve_length_or_auto(value, parent_fs, current_fs),
- "bottom" => style.bottom = resolve_length_or_auto(value, parent_fs, current_fs),
- "left" => style.left = resolve_length_or_auto(value, parent_fs, current_fs),
+ // Position offsets (percentages preserved for layout resolution)
+ "top" => style.top = resolve_layout_length_or_auto(value, current_fs, viewport),
+ "right" => style.right = resolve_layout_length_or_auto(value, current_fs, viewport),
+ "bottom" => style.bottom = resolve_layout_length_or_auto(value, current_fs, viewport),
+ "left" => style.left = resolve_layout_length_or_auto(value, current_fs, viewport),
// Overflow
"overflow" => {
@@ -800,9 +794,9 @@ fn apply_property(
}
}
-fn resolve_border_width(value: &CssValue, em_base: f32) -> f32 {
+fn resolve_border_width(value: &CssValue, em_base: f32, viewport: (f32, f32)) -> f32 {
match value {
- CssValue::Length(n, unit) => resolve_length_unit(*n, *unit, em_base),
+ CssValue::Length(n, unit) => resolve_length_unit(*n, *unit, em_base, viewport),
CssValue::Zero => 0.0,
CssValue::Number(n) if *n == 0.0 => 0.0,
CssValue::Keyword(k) => match k.as_str() {
@@ -959,7 +953,11 @@ fn collect_style_elements(doc: &Document, node: NodeId, stylesheets: &mut Vec Option {
+pub fn resolve_styles(
+ doc: &Document,
+ author_stylesheets: &[Stylesheet],
+ viewport: (f32, f32),
+) -> Option {
let ua = ua_stylesheet();
// Combine UA + author stylesheets into a single list for rule collection.
@@ -972,7 +970,7 @@ pub fn resolve_styles(doc: &Document, author_stylesheets: &[Stylesheet]) -> Opti
}
let root = doc.root();
- resolve_node(doc, root, &combined, &ComputedStyle::default())
+ resolve_node(doc, root, &combined, &ComputedStyle::default(), viewport)
}
fn resolve_node(
@@ -980,13 +978,14 @@ fn resolve_node(
node: NodeId,
stylesheet: &Stylesheet,
parent_style: &ComputedStyle,
+ viewport: (f32, f32),
) -> Option {
match doc.node_data(node) {
NodeData::Document => {
// Document node: resolve children, return first element child or wrapper.
let mut children = Vec::new();
for child in doc.children(node) {
- if let Some(styled) = resolve_node(doc, child, stylesheet, parent_style) {
+ if let Some(styled) = resolve_node(doc, child, stylesheet, parent_style, viewport) {
children.push(styled);
}
}
@@ -1003,7 +1002,7 @@ fn resolve_node(
}
}
NodeData::Element { .. } => {
- let style = compute_style_for_element(doc, node, stylesheet, parent_style);
+ let style = compute_style_for_element(doc, node, stylesheet, parent_style, viewport);
if style.display == Display::None {
return None;
@@ -1011,7 +1010,7 @@ fn resolve_node(
let mut children = Vec::new();
for child in doc.children(node) {
- if let Some(styled) = resolve_node(doc, child, stylesheet, &style) {
+ if let Some(styled) = resolve_node(doc, child, stylesheet, &style, viewport) {
children.push(styled);
}
}
@@ -1043,6 +1042,7 @@ fn compute_style_for_element(
node: NodeId,
stylesheet: &Stylesheet,
parent_style: &ComputedStyle,
+ viewport: (f32, f32),
) -> ComputedStyle {
// Start from initial values, inheriting inherited properties from parent
let mut style = ComputedStyle {
@@ -1097,7 +1097,7 @@ fn compute_style_for_element(
// Step 5: Apply normal declarations (already in specificity order)
for (prop, value) in &normal_decls {
- apply_property(&mut style, prop, value, parent_style);
+ apply_property(&mut style, prop, value, parent_style, viewport);
}
// Step 6: Apply inline style normal declarations (override stylesheet normals)
@@ -1106,18 +1106,18 @@ fn compute_style_for_element(
let property = decl.property.as_str();
if let Some(longhands) = expand_shorthand(property, &decl.value, false) {
for lh in &longhands {
- apply_property(&mut style, &lh.property, &lh.value, parent_style);
+ apply_property(&mut style, &lh.property, &lh.value, parent_style, viewport);
}
} else {
let value = parse_value(&decl.value);
- apply_property(&mut style, property, &value, parent_style);
+ apply_property(&mut style, property, &value, parent_style, viewport);
}
}
}
// Step 7: Apply !important declarations (override everything normal)
for (prop, value) in &important_decls {
- apply_property(&mut style, prop, value, parent_style);
+ apply_property(&mut style, prop, value, parent_style, viewport);
}
// Step 8: Apply inline style !important declarations (highest priority)
@@ -1126,11 +1126,11 @@ fn compute_style_for_element(
let property = decl.property.as_str();
if let Some(longhands) = expand_shorthand(property, &decl.value, true) {
for lh in &longhands {
- apply_property(&mut style, &lh.property, &lh.value, parent_style);
+ apply_property(&mut style, &lh.property, &lh.value, parent_style, viewport);
}
} else {
let value = parse_value(&decl.value);
- apply_property(&mut style, property, &value, parent_style);
+ apply_property(&mut style, property, &value, parent_style, viewport);
}
}
}
@@ -1180,7 +1180,7 @@ mod tests {
#[test]
fn ua_body_has_8px_margin() {
let (doc, _, _, _) = make_doc_with_body();
- let styled = resolve_styles(&doc, &[]).unwrap();
+ let styled = resolve_styles(&doc, &[], (800.0, 600.0)).unwrap();
// styled is , first child is
let body = &styled.children[0];
assert_eq!(body.style.margin_top, LengthOrAuto::Length(8.0));
@@ -1197,7 +1197,7 @@ mod tests {
doc.append_child(body, h1);
doc.append_child(h1, text);
- let styled = resolve_styles(&doc, &[]).unwrap();
+ let styled = resolve_styles(&doc, &[], (800.0, 600.0)).unwrap();
let body_node = &styled.children[0];
let h1_node = &body_node.children[0];
@@ -1212,7 +1212,7 @@ mod tests {
let h2 = doc.create_element("h2");
doc.append_child(body, h2);
- let styled = resolve_styles(&doc, &[]).unwrap();
+ let styled = resolve_styles(&doc, &[], (800.0, 600.0)).unwrap();
let body_node = &styled.children[0];
let h2_node = &body_node.children[0];
@@ -1228,7 +1228,7 @@ mod tests {
doc.append_child(body, p);
doc.append_child(p, text);
- let styled = resolve_styles(&doc, &[]).unwrap();
+ let styled = resolve_styles(&doc, &[], (800.0, 600.0)).unwrap();
let body_node = &styled.children[0];
let p_node = &body_node.children[0];
@@ -1243,7 +1243,7 @@ mod tests {
let div = doc.create_element("div");
doc.append_child(body, div);
- let styled = resolve_styles(&doc, &[]).unwrap();
+ let styled = resolve_styles(&doc, &[], (800.0, 600.0)).unwrap();
let body_node = &styled.children[0];
let div_node = &body_node.children[0];
assert_eq!(div_node.style.display, Display::Block);
@@ -1259,7 +1259,7 @@ mod tests {
doc.append_child(p, span);
doc.append_child(span, text);
- let styled = resolve_styles(&doc, &[]).unwrap();
+ let styled = resolve_styles(&doc, &[], (800.0, 600.0)).unwrap();
let body_node = &styled.children[0];
let p_node = &body_node.children[0];
let span_node = &p_node.children[0];
@@ -1274,7 +1274,7 @@ mod tests {
doc.append_child(html, head);
doc.append_child(head, title);
- let styled = resolve_styles(&doc, &[]).unwrap();
+ let styled = resolve_styles(&doc, &[], (800.0, 600.0)).unwrap();
// head should not appear in styled tree (display: none)
for child in &styled.children {
if let NodeData::Element { tag_name, .. } = doc.node_data(child.node) {
@@ -1296,7 +1296,7 @@ mod tests {
doc.append_child(p, text);
let ss = Parser::parse("p { color: red; }");
- let styled = resolve_styles(&doc, &[ss]).unwrap();
+ let styled = resolve_styles(&doc, &[ss], (800.0, 600.0)).unwrap();
let body_node = &styled.children[0];
let p_node = &body_node.children[0];
assert_eq!(p_node.style.color, Color::rgb(255, 0, 0));
@@ -1309,7 +1309,7 @@ mod tests {
doc.append_child(body, div);
let ss = Parser::parse("div { background-color: blue; }");
- let styled = resolve_styles(&doc, &[ss]).unwrap();
+ let styled = resolve_styles(&doc, &[ss], (800.0, 600.0)).unwrap();
let body_node = &styled.children[0];
let div_node = &body_node.children[0];
assert_eq!(div_node.style.background_color, Color::rgb(0, 0, 255));
@@ -1322,7 +1322,7 @@ mod tests {
doc.append_child(body, p);
let ss = Parser::parse("p { font-size: 24px; }");
- let styled = resolve_styles(&doc, &[ss]).unwrap();
+ let styled = resolve_styles(&doc, &[ss], (800.0, 600.0)).unwrap();
let body_node = &styled.children[0];
let p_node = &body_node.children[0];
assert_eq!(p_node.style.font_size, 24.0);
@@ -1335,7 +1335,7 @@ mod tests {
doc.append_child(body, div);
let ss = Parser::parse("div { margin-top: 20px; margin-bottom: 10px; }");
- let styled = resolve_styles(&doc, &[ss]).unwrap();
+ let styled = resolve_styles(&doc, &[ss], (800.0, 600.0)).unwrap();
let body_node = &styled.children[0];
let div_node = &body_node.children[0];
assert_eq!(div_node.style.margin_top, LengthOrAuto::Length(20.0));
@@ -1357,7 +1357,7 @@ mod tests {
// .highlight (0,1,0) > p (0,0,1)
let ss = Parser::parse("p { color: red; } .highlight { color: green; }");
- let styled = resolve_styles(&doc, &[ss]).unwrap();
+ let styled = resolve_styles(&doc, &[ss], (800.0, 600.0)).unwrap();
let body_node = &styled.children[0];
let p_node = &body_node.children[0];
assert_eq!(p_node.style.color, Color::rgb(0, 128, 0));
@@ -1373,7 +1373,7 @@ mod tests {
// Same specificity: later wins
let ss = Parser::parse("p { color: red; } p { color: blue; }");
- let styled = resolve_styles(&doc, &[ss]).unwrap();
+ let styled = resolve_styles(&doc, &[ss], (800.0, 600.0)).unwrap();
let body_node = &styled.children[0];
let p_node = &body_node.children[0];
assert_eq!(p_node.style.color, Color::rgb(0, 0, 255));
@@ -1390,7 +1390,7 @@ mod tests {
// #main (1,0,0) has higher specificity, but p has !important
let ss = Parser::parse("#main { color: blue; } p { color: red !important; }");
- let styled = resolve_styles(&doc, &[ss]).unwrap();
+ let styled = resolve_styles(&doc, &[ss], (800.0, 600.0)).unwrap();
let body_node = &styled.children[0];
let p_node = &body_node.children[0];
assert_eq!(p_node.style.color, Color::rgb(255, 0, 0));
@@ -1411,7 +1411,7 @@ mod tests {
doc.append_child(p, text);
let ss = Parser::parse("div { color: green; }");
- let styled = resolve_styles(&doc, &[ss]).unwrap();
+ let styled = resolve_styles(&doc, &[ss], (800.0, 600.0)).unwrap();
let body_node = &styled.children[0];
let div_node = &body_node.children[0];
let p_node = &div_node.children[0];
@@ -1432,7 +1432,7 @@ mod tests {
doc.append_child(p, text);
let ss = Parser::parse("div { font-size: 20px; }");
- let styled = resolve_styles(&doc, &[ss]).unwrap();
+ let styled = resolve_styles(&doc, &[ss], (800.0, 600.0)).unwrap();
let body_node = &styled.children[0];
let div_node = &body_node.children[0];
let p_node = &div_node.children[0];
@@ -1452,7 +1452,7 @@ mod tests {
doc.append_child(span, text);
let ss = Parser::parse("div { margin-top: 50px; }");
- let styled = resolve_styles(&doc, &[ss]).unwrap();
+ let styled = resolve_styles(&doc, &[ss], (800.0, 600.0)).unwrap();
let body_node = &styled.children[0];
let div_node = &body_node.children[0];
let span_node = &div_node.children[0];
@@ -1477,7 +1477,7 @@ mod tests {
doc.append_child(p, text);
let ss = Parser::parse("div { background-color: red; } p { background-color: inherit; }");
- let styled = resolve_styles(&doc, &[ss]).unwrap();
+ let styled = resolve_styles(&doc, &[ss], (800.0, 600.0)).unwrap();
let body_node = &styled.children[0];
let div_node = &body_node.children[0];
let p_node = &div_node.children[0];
@@ -1498,7 +1498,7 @@ mod tests {
// div sets color to red, p resets to initial (black)
let ss = Parser::parse("div { color: red; } p { color: initial; }");
- let styled = resolve_styles(&doc, &[ss]).unwrap();
+ let styled = resolve_styles(&doc, &[ss], (800.0, 600.0)).unwrap();
let body_node = &styled.children[0];
let div_node = &body_node.children[0];
let p_node = &div_node.children[0];
@@ -1519,7 +1519,7 @@ mod tests {
// color is inherited, so unset => inherit
let ss = Parser::parse("div { color: green; } p { color: unset; }");
- let styled = resolve_styles(&doc, &[ss]).unwrap();
+ let styled = resolve_styles(&doc, &[ss], (800.0, 600.0)).unwrap();
let body_node = &styled.children[0];
let div_node = &body_node.children[0];
let p_node = &div_node.children[0];
@@ -1539,7 +1539,7 @@ mod tests {
// margin is non-inherited, so unset => initial (0)
let ss = Parser::parse("p { margin-top: unset; }");
- let styled = resolve_styles(&doc, &[ss]).unwrap();
+ let styled = resolve_styles(&doc, &[ss], (800.0, 600.0)).unwrap();
let body_node = &styled.children[0];
let p_node = &body_node.children[0];
@@ -1560,7 +1560,7 @@ mod tests {
doc.append_child(div, text);
let ss = Parser::parse("div { font-size: 20px; margin-top: 2em; }");
- let styled = resolve_styles(&doc, &[ss]).unwrap();
+ let styled = resolve_styles(&doc, &[ss], (800.0, 600.0)).unwrap();
let body_node = &styled.children[0];
let div_node = &body_node.children[0];
@@ -1580,7 +1580,7 @@ mod tests {
doc.append_child(p, text);
let ss = Parser::parse("div { font-size: 20px; } p { font-size: 1.5em; }");
- let styled = resolve_styles(&doc, &[ss]).unwrap();
+ let styled = resolve_styles(&doc, &[ss], (800.0, 600.0)).unwrap();
let body_node = &styled.children[0];
let div_node = &body_node.children[0];
let p_node = &div_node.children[0];
@@ -1604,7 +1604,7 @@ mod tests {
doc.append_child(p, text);
let ss = Parser::parse("p { color: red; }");
- let styled = resolve_styles(&doc, &[ss]).unwrap();
+ let styled = resolve_styles(&doc, &[ss], (800.0, 600.0)).unwrap();
let body_node = &styled.children[0];
let p_node = &body_node.children[0];
@@ -1622,7 +1622,7 @@ mod tests {
doc.append_child(p, text);
let ss = Parser::parse("p { color: red !important; }");
- let styled = resolve_styles(&doc, &[ss]).unwrap();
+ let styled = resolve_styles(&doc, &[ss], (800.0, 600.0)).unwrap();
let body_node = &styled.children[0];
let p_node = &body_node.children[0];
@@ -1643,7 +1643,7 @@ mod tests {
doc.append_child(div, text);
let ss = Parser::parse("div { margin: 10px 20px; }");
- let styled = resolve_styles(&doc, &[ss]).unwrap();
+ let styled = resolve_styles(&doc, &[ss], (800.0, 600.0)).unwrap();
let body_node = &styled.children[0];
let div_node = &body_node.children[0];
@@ -1662,14 +1662,14 @@ mod tests {
doc.append_child(div, text);
let ss = Parser::parse("div { padding: 5px; }");
- let styled = resolve_styles(&doc, &[ss]).unwrap();
+ let styled = resolve_styles(&doc, &[ss], (800.0, 600.0)).unwrap();
let body_node = &styled.children[0];
let div_node = &body_node.children[0];
- assert_eq!(div_node.style.padding_top, 5.0);
- assert_eq!(div_node.style.padding_right, 5.0);
- assert_eq!(div_node.style.padding_bottom, 5.0);
- assert_eq!(div_node.style.padding_left, 5.0);
+ assert_eq!(div_node.style.padding_top, LengthOrAuto::Length(5.0));
+ assert_eq!(div_node.style.padding_right, LengthOrAuto::Length(5.0));
+ assert_eq!(div_node.style.padding_bottom, LengthOrAuto::Length(5.0));
+ assert_eq!(div_node.style.padding_left, LengthOrAuto::Length(5.0));
}
// -----------------------------------------------------------------------
@@ -1686,7 +1686,7 @@ mod tests {
// UA gives p margin-top=1em=16px. Author overrides to 0.
let ss = Parser::parse("p { margin-top: 0; margin-bottom: 0; }");
- let styled = resolve_styles(&doc, &[ss]).unwrap();
+ let styled = resolve_styles(&doc, &[ss], (800.0, 600.0)).unwrap();
let body_node = &styled.children[0];
let p_node = &body_node.children[0];
@@ -1707,7 +1707,7 @@ mod tests {
doc.append_child(p, text);
let ss = Parser::parse("p { color: red; font-size: 20px; }");
- let styled = resolve_styles(&doc, &[ss]).unwrap();
+ let styled = resolve_styles(&doc, &[ss], (800.0, 600.0)).unwrap();
let body_node = &styled.children[0];
let p_node = &body_node.children[0];
let text_node = &p_node.children[0];
@@ -1730,7 +1730,7 @@ mod tests {
let ss1 = Parser::parse("p { color: red; }");
let ss2 = Parser::parse("p { color: blue; }");
- let styled = resolve_styles(&doc, &[ss1, ss2]).unwrap();
+ let styled = resolve_styles(&doc, &[ss1, ss2], (800.0, 600.0)).unwrap();
let body_node = &styled.children[0];
let p_node = &body_node.children[0];
@@ -1751,7 +1751,7 @@ mod tests {
doc.append_child(div, text);
let ss = Parser::parse("div { border: 2px solid red; }");
- let styled = resolve_styles(&doc, &[ss]).unwrap();
+ let styled = resolve_styles(&doc, &[ss], (800.0, 600.0)).unwrap();
let body_node = &styled.children[0];
let div_node = &body_node.children[0];
@@ -1773,7 +1773,7 @@ mod tests {
doc.append_child(div, text);
let ss = Parser::parse("div { position: relative; top: 10px; }");
- let styled = resolve_styles(&doc, &[ss]).unwrap();
+ let styled = resolve_styles(&doc, &[ss], (800.0, 600.0)).unwrap();
let body_node = &styled.children[0];
let div_node = &body_node.children[0];
@@ -1800,7 +1800,7 @@ mod tests {
doc.append_child(div2, t2);
let ss = Parser::parse(".hidden { display: none; }");
- let styled = resolve_styles(&doc, &[ss]).unwrap();
+ let styled = resolve_styles(&doc, &[ss], (800.0, 600.0)).unwrap();
let body_node = &styled.children[0];
// Only div1 should appear
@@ -1822,7 +1822,7 @@ mod tests {
doc.append_child(p, text);
let ss = Parser::parse("div { visibility: hidden; }");
- let styled = resolve_styles(&doc, &[ss]).unwrap();
+ let styled = resolve_styles(&doc, &[ss], (800.0, 600.0)).unwrap();
let body_node = &styled.children[0];
let div_node = &body_node.children[0];
let p_node = &div_node.children[0];
@@ -1920,7 +1920,7 @@ mod tests {
doc.append_child(p, text);
let sheets = extract_stylesheets(&doc);
- let styled = resolve_styles(&doc, &sheets).unwrap();
+ let styled = resolve_styles(&doc, &sheets, (800.0, 600.0)).unwrap();
let body_node = &styled.children[0];
let p_node = &body_node.children[0];
@@ -1941,7 +1941,7 @@ mod tests {
let sheets = extract_stylesheets(&doc);
assert!(sheets.is_empty());
- let styled = resolve_styles(&doc, &sheets).unwrap();
+ let styled = resolve_styles(&doc, &sheets, (800.0, 600.0)).unwrap();
let body_node = &styled.children[0];
let div_node = &body_node.children[0];
@@ -1967,7 +1967,7 @@ mod tests {
doc.append_child(p, text);
let sheets = extract_stylesheets(&doc);
- let styled = resolve_styles(&doc, &sheets).unwrap();
+ let styled = resolve_styles(&doc, &sheets, (800.0, 600.0)).unwrap();
let body_node = &styled.children[0];
let p_node = &body_node.children[0];
@@ -2003,7 +2003,7 @@ mod tests {
let sheets = extract_stylesheets(&doc);
assert_eq!(sheets.len(), 2);
- let styled = resolve_styles(&doc, &sheets).unwrap();
+ let styled = resolve_styles(&doc, &sheets, (800.0, 600.0)).unwrap();
let body_node = &styled.children[0];
let p_node = &body_node.children[0];
@@ -2017,7 +2017,7 @@ mod tests {
Test
"#;
let doc = we_html::parse_html(html_str);
let sheets = extract_stylesheets(&doc);
- let styled = resolve_styles(&doc, &sheets).unwrap();
+ let styled = resolve_styles(&doc, &sheets, (800.0, 600.0)).unwrap();
let body = &styled.children[0];
let div = &body.children[0];
assert_eq!(div.style.box_sizing, BoxSizing::ContentBox);
@@ -2030,7 +2030,7 @@ mod tests {
Test
"#;
let doc = we_html::parse_html(html_str);
let sheets = extract_stylesheets(&doc);
- let styled = resolve_styles(&doc, &sheets).unwrap();
+ let styled = resolve_styles(&doc, &sheets, (800.0, 600.0)).unwrap();
let body = &styled.children[0];
let div = &body.children[0];
assert_eq!(div.style.box_sizing, BoxSizing::BorderBox);
@@ -2043,7 +2043,7 @@ mod tests {
"#;
let doc = we_html::parse_html(html_str);
let sheets = extract_stylesheets(&doc);
- let styled = resolve_styles(&doc, &sheets).unwrap();
+ let styled = resolve_styles(&doc, &sheets, (800.0, 600.0)).unwrap();
let body = &styled.children[0];
let parent = &body.children[0];
let child = &parent.children[0];
diff --git a/crates/style/tests/style_extraction.rs b/crates/style/tests/style_extraction.rs
index 41d450e..6e01623 100644
--- a/crates/style/tests/style_extraction.rs
+++ b/crates/style/tests/style_extraction.rs
@@ -23,7 +23,7 @@ h1 { color: blue; }
let sheets = extract_stylesheets(&doc);
assert_eq!(sheets.len(), 1);
- let styled = resolve_styles(&doc, &sheets).unwrap();
+ let styled = resolve_styles(&doc, &sheets, (800.0, 600.0)).unwrap();
// styled root is
// Find body (first visible child)
let body = &styled.children[0];
@@ -52,7 +52,7 @@ fn html_with_multiple_style_elements() {
let sheets = extract_stylesheets(&doc);
assert_eq!(sheets.len(), 2);
- let styled = resolve_styles(&doc, &sheets).unwrap();
+ let styled = resolve_styles(&doc, &sheets, (800.0, 600.0)).unwrap();
let body = &styled.children[0];
let p = &body.children[0];
@@ -75,7 +75,7 @@ fn html_with_inline_style_attribute() {
let doc = we_html::parse_html(html);
let sheets = extract_stylesheets(&doc);
- let styled = resolve_styles(&doc, &sheets).unwrap();
+ let styled = resolve_styles(&doc, &sheets, (800.0, 600.0)).unwrap();
let body = &styled.children[0];
let p = &body.children[0];
@@ -97,7 +97,7 @@ fn html_no_style_uses_ua_defaults() {
let sheets = extract_stylesheets(&doc);
assert!(sheets.is_empty());
- let styled = resolve_styles(&doc, &sheets).unwrap();
+ let styled = resolve_styles(&doc, &sheets, (800.0, 600.0)).unwrap();
let body = &styled.children[0];
// Body should have 8px margin from UA stylesheet.
@@ -122,7 +122,7 @@ fn style_element_display_none_in_styled_tree() {
let doc = we_html::parse_html(html);
let sheets = extract_stylesheets(&doc);
- let styled = resolve_styles(&doc, &sheets).unwrap();
+ let styled = resolve_styles(&doc, &sheets, (800.0, 600.0)).unwrap();
// The and