From 6000ab404dd90c3c054fcfcb28f0b4b4ff619a36 Mon Sep 17 00:00:00 2001 From: Pierre Le Fevre Date: Sat, 16 May 2026 22:55:25 +0200 Subject: [PATCH] Set form to display:block in UA stylesheet MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `
` was missing from the UA stylesheet's block-display list, so it defaulted to display:inline. With author CSS like `form > * { display: block; margin: 8px 0; }`, the form's element children became block-level inside an inline parent — body's layout took the inline-children path and never called compute_layout on the form's controls, so labels and inputs collapsed at (0, 0) with zero height. Add `form` to the UA block list so the form itself is a block container and lays out its children via layout_block_children. Form controls then honour author display:block and margins, and retain their intrinsic painting (text-input border, button chrome, checkbox glyph, etc.). Adds layout regression test asserting the stacked-vertical behaviour and a UA-stylesheet test asserting form's default display. Fixes: e2e-smoke "Form controls with author display: block lay out incorrectly". Co-Authored-By: Claude Opus 4.7 --- crates/layout/src/lib.rs | 70 ++++++++++++++++++++++++++++++++++++ crates/style/src/computed.rs | 17 ++++++++- 2 files changed, 86 insertions(+), 1 deletion(-) diff --git a/crates/layout/src/lib.rs b/crates/layout/src/lib.rs index abd1c9f..773ff5e 100644 --- a/crates/layout/src/lib.rs +++ b/crates/layout/src/lib.rs @@ -8462,6 +8462,76 @@ body { margin: 0; } ); } + #[test] + fn form_with_block_children_stacks_vertically() { + // Regression: author CSS forcing `form > * { display: block }` must + // produce form controls that stack vertically (one per line) with + // their author margins honoured. Previously `` defaulted to + // display:inline because the UA stylesheet was missing the `form` + // rule, which broke layout for block-display children. + let mut doc = Document::new(); + let root = doc.root(); + let html = doc.create_element("html"); + let head = doc.create_element("head"); + let style = doc.create_element("style"); + let css = doc.create_text("form > * { display: block; margin: 8px 0; }"); + let body = doc.create_element("body"); + let form = doc.create_element("form"); + let label1 = doc.create_element("label"); + let label1_text = doc.create_text("Name"); + let input1 = doc.create_element("input"); + doc.set_attribute(input1, "type", "text"); + let input2 = doc.create_element("input"); + doc.set_attribute(input2, "type", "text"); + doc.append_child(root, html); + doc.append_child(html, head); + doc.append_child(head, style); + doc.append_child(style, css); + doc.append_child(html, body); + doc.append_child(body, form); + doc.append_child(form, label1); + doc.append_child(label1, label1_text); + doc.append_child(form, input1); + doc.append_child(form, input2); + + let tree = layout_doc(&doc); + let body_box = &tree.root.children[0]; + let form_box = &body_box.children[0]; + + // form must lay out as a block (not be split as inline) and contain + // three stacked children. + assert!( + matches!(form_box.box_type, BoxType::Block(_)), + " must be a block-level box (UA default); got {:?}", + form_box.box_type + ); + assert!( + form_box.children.len() >= 3, + "form should contain its three element children, got {}", + form_box.children.len() + ); + + // Each in-flow child must have a positive height (no zero-sized + // collapse) and a distinct y-position from its predecessors. + let mut prev_bottom = f32::NEG_INFINITY; + for (i, child) in form_box.children.iter().enumerate() { + assert!( + child.rect.height > 0.0, + "form child {i} ({:?}) should have positive height, got {}", + child.box_type, + child.rect.height, + ); + assert!( + child.rect.y >= prev_bottom, + "form child {i} ({:?}) at y={} should not overlap previous (bottom={})", + child.box_type, + child.rect.y, + prev_bottom, + ); + prev_bottom = child.rect.y + child.rect.height; + } + } + #[test] fn form_controls_are_atomic_inline() { // When multiple form controls are in a p, they should lay out side by side. diff --git a/crates/style/src/computed.rs b/crates/style/src/computed.rs index 6a10933..f9ec3d0 100644 --- a/crates/style/src/computed.rs +++ b/crates/style/src/computed.rs @@ -741,7 +741,7 @@ pub fn ua_stylesheet() -> Stylesheet { const UA_CSS: &str = r#" html, body, div, p, pre, h1, h2, h3, h4, h5, h6, ul, ol, li, blockquote, section, article, nav, -header, footer, main, hr { +header, footer, main, hr, form { display: block; } @@ -3116,6 +3116,21 @@ mod tests { assert_eq!(div_node.style.display, Display::Block); } + #[test] + fn ua_form_is_block() { + // `` defaults to display: block per HTML spec. Without this, + // author CSS like `form > * { display: block }` produces a + // block-in-inline situation that breaks layout for form controls. + let (mut doc, _, _, body) = make_doc_with_body(); + let form = doc.create_element("form"); + doc.append_child(body, form); + + let styled = resolve_styles(&doc, &[], (800.0, 600.0)).unwrap(); + let body_node = &styled.children[0]; + let form_node = &body_node.children[0]; + assert_eq!(form_node.style.display, Display::Block); + } + #[test] fn ua_display_inline_elements() { let (mut doc, _, _, body) = make_doc_with_body(); -- 2.51.2