render: preserve inline block inner flow
This commit is contained in:
1 parent
5b602259c6
commit
2d1f84a29d
3 files changed
+63
No files matched your search
@@ -1912,6 +1912,33 @@ fn build(
|
|||||||
|
|
||||||
let mut taffy_style = to_taffy_style(style);
|
let mut taffy_style = to_taffy_style(style);
|
||||||
|
|
||||||
|
// CSS has separate outer and inner display types, while taffy exposes one
|
||||||
|
// display value. An inline-block normally needs our wrapping inline
|
||||||
|
// approximation so it remains atomic in an inline formatting context.
|
||||||
|
// As a direct flex/grid item, however, its outer display is blockified and
|
||||||
|
// its flow-root inner display must stack block children. Apply the block
|
||||||
|
// inner mode only in that formatting context; doing it globally turns
|
||||||
|
// inline-block lists into one full-width item per line.
|
||||||
|
if style.is_inline_block {
|
||||||
|
let mut parent = node.parent;
|
||||||
|
let flex_or_grid_item = loop {
|
||||||
|
let Some(parent_id) = parent else { break false };
|
||||||
|
let Some(parent_style) = styles.get(&parent_id) else { break false };
|
||||||
|
if parent_style.display_contents {
|
||||||
|
parent = tree.get_node(parent_id).and_then(|node| node.parent);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
break matches!(
|
||||||
|
parent_style.display,
|
||||||
|
crate::Display::Flex | crate::Display::Grid
|
||||||
|
) && !parent_style.internal_flex_container;
|
||||||
|
};
|
||||||
|
if flex_or_grid_item {
|
||||||
|
taffy_style.display = taffy::style::Display::Block;
|
||||||
|
taffy_style.flex_wrap = taffy::FlexWrap::NoWrap;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// A replaced image is a measured leaf, even when CSS gives it a percentage
|
// A replaced image is a measured leaf, even when CSS gives it a percentage
|
||||||
// width. Its intrinsic dimensions participate in an auto-sized ancestor's
|
// width. Its intrinsic dimensions participate in an auto-sized ancestor's
|
||||||
// max-content measurement; once the percentage axis becomes definite, the
|
// max-content measurement; once the percentage axis becomes definite, the
|
||||||
|
|||||||
@@ -165,6 +165,10 @@ impl Dimension {
|
|||||||
#[derive(Debug, Clone, Default)]
|
#[derive(Debug, Clone, Default)]
|
||||||
pub struct LayoutStyle {
|
pub struct LayoutStyle {
|
||||||
pub display: Display,
|
pub display: Display,
|
||||||
|
/// True when `display:flex` is only an internal stand-in for native HTML
|
||||||
|
/// layout such as table cells or `<center>`, rather than the computed CSS
|
||||||
|
/// display. Descendants are not CSS flex items in these containers.
|
||||||
|
pub internal_flex_container: bool,
|
||||||
pub width: Dimension,
|
pub width: Dimension,
|
||||||
pub height: Dimension,
|
pub height: Dimension,
|
||||||
/// Which box edge `width`/`height` and min/max sizes describe. CSS starts
|
/// Which box edge `width`/`height` and min/max sizes describe. CSS starts
|
||||||
|
|||||||
@@ -45,6 +45,7 @@ pub fn ua_style(tag: &str) -> LayoutStyle {
|
|||||||
};
|
};
|
||||||
if tag == "center" {
|
if tag == "center" {
|
||||||
style.display = Display::Flex;
|
style.display = Display::Flex;
|
||||||
|
style.internal_flex_container = true;
|
||||||
style.flex_direction = Some(taffy::FlexDirection::Column);
|
style.flex_direction = Some(taffy::FlexDirection::Column);
|
||||||
style.align_items = Some(taffy::AlignItems::CENTER);
|
style.align_items = Some(taffy::AlignItems::CENTER);
|
||||||
} else if tag == "head" || tag == "script" || tag == "style" || tag == "title" || tag == "meta" || tag == "link" || tag == "noscript" || tag == "template"
|
} else if tag == "head" || tag == "script" || tag == "style" || tag == "title" || tag == "meta" || tag == "link" || tag == "noscript" || tag == "template"
|
||||||
@@ -106,6 +107,7 @@ pub fn ua_style(tag: &str) -> LayoutStyle {
|
|||||||
style.underline = Some(true); // UA default: links are underlined
|
style.underline = Some(true); // UA default: links are underlined
|
||||||
} else if tag == "table" || tag == "tbody" {
|
} else if tag == "table" || tag == "tbody" {
|
||||||
style.display = Display::Flex;
|
style.display = Display::Flex;
|
||||||
|
style.internal_flex_container = true;
|
||||||
style.flex_direction = Some(taffy::FlexDirection::Column);
|
style.flex_direction = Some(taffy::FlexDirection::Column);
|
||||||
style.align_items = Some(taffy::AlignItems::STRETCH); // stretch rows to fill table width
|
style.align_items = Some(taffy::AlignItems::STRETCH); // stretch rows to fill table width
|
||||||
// Rows fill the table width and may shrink below their content's
|
// Rows fill the table width and may shrink below their content's
|
||||||
@@ -118,6 +120,7 @@ pub fn ua_style(tag: &str) -> LayoutStyle {
|
|||||||
style.width = crate::Dimension::Percent(1.0);
|
style.width = crate::Dimension::Percent(1.0);
|
||||||
}
|
}
|
||||||
} else if tag == "tr" {
|
} else if tag == "tr" {
|
||||||
|
style.internal_flex_container = true;
|
||||||
// Rows fill the table width and can shrink below content min-content;
|
// Rows fill the table width and can shrink below content min-content;
|
||||||
// this is exactly why Wikipedia's own responsive CSS uses
|
// this is exactly why Wikipedia's own responsive CSS uses
|
||||||
// `tr{min-width:100%}`. `align-items:stretch` alone did not pin them
|
// `tr{min-width:100%}`. `align-items:stretch` alone did not pin them
|
||||||
@@ -126,6 +129,7 @@ pub fn ua_style(tag: &str) -> LayoutStyle {
|
|||||||
style.width = crate::Dimension::Percent(1.0);
|
style.width = crate::Dimension::Percent(1.0);
|
||||||
} else if tag == "td" || tag == "th" {
|
} else if tag == "td" || tag == "th" {
|
||||||
style.display = Display::Flex;
|
style.display = Display::Flex;
|
||||||
|
style.internal_flex_container = true;
|
||||||
style.flex_direction = Some(taffy::FlexDirection::Column);
|
style.flex_direction = Some(taffy::FlexDirection::Column);
|
||||||
style.align_items = Some(taffy::AlignItems::FLEX_START);
|
style.align_items = Some(taffy::AlignItems::FLEX_START);
|
||||||
style.padding = Edges { top: 0.0, right: 5.0, bottom: 0.0, left: 0.0 };
|
style.padding = Edges { top: 0.0, right: 5.0, bottom: 0.0, left: 0.0 };
|
||||||
@@ -241,6 +245,20 @@ fn apply_value(style: &mut LayoutStyle, name: &str, value: &str) {
|
|||||||
if value != "contents" {
|
if value != "contents" {
|
||||||
style.display_contents = false;
|
style.display_contents = false;
|
||||||
}
|
}
|
||||||
|
if matches!(
|
||||||
|
value,
|
||||||
|
"none"
|
||||||
|
| "flex"
|
||||||
|
| "inline-flex"
|
||||||
|
| "inline"
|
||||||
|
| "inline-block"
|
||||||
|
| "grid"
|
||||||
|
| "inline-grid"
|
||||||
|
| "block"
|
||||||
|
| "contents"
|
||||||
|
) {
|
||||||
|
style.internal_flex_container = false;
|
||||||
|
}
|
||||||
match value {
|
match value {
|
||||||
"none" => style.display = crate::Display::None,
|
"none" => style.display = crate::Display::None,
|
||||||
"flex" => style.display = crate::Display::Flex,
|
"flex" => style.display = crate::Display::Flex,
|
||||||
@@ -2577,6 +2595,20 @@ mod tests {
|
|||||||
assert_eq!(s.height, crate::Dimension::Px(50.0));
|
assert_eq!(s.height, crate::Dimension::Px(50.0));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn authored_display_replaces_internal_flex_provenance() {
|
||||||
|
let native_cell = compute_style("td", None);
|
||||||
|
assert_eq!(native_cell.display, Display::Flex);
|
||||||
|
assert!(native_cell.internal_flex_container);
|
||||||
|
|
||||||
|
let authored_cell = compute_style("td", Some("display:flex"));
|
||||||
|
assert_eq!(authored_cell.display, Display::Flex);
|
||||||
|
assert!(!authored_cell.internal_flex_container);
|
||||||
|
|
||||||
|
let invalid = compute_style("td", Some("display:bogus"));
|
||||||
|
assert!(invalid.internal_flex_container);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn font_shorthand_expands_layout_fields_and_resets_omissions() {
|
fn font_shorthand_expands_layout_fields_and_resets_omissions() {
|
||||||
let s = compute_style(
|
let s = compute_style(
|
||||||
|
|||||||
Reference in new issue
Block a user