diff --git a/packages/blitz-dom/src/layout/inline.rs b/packages/blitz-dom/src/layout/inline.rs index e5d0118ad5..0ffeafb033 100644 --- a/packages/blitz-dom/src/layout/inline.rs +++ b/packages/blitz-dom/src/layout/inline.rs @@ -18,6 +18,8 @@ use parley::YieldData; #[cfg(feature = "floats")] use taffy::{BlockItemStyle as _, Clear, Float, prelude::TaffyMaxContent}; +use stylo_taffy::StyleFlags; + use super::resolve_calc_value; use crate::layout::LayoutPassState; use crate::stylo_to_parley; @@ -192,6 +194,39 @@ impl LayoutPassState<'_> { } } + /// The out-of-flow positions for which an inline span between the inline box `box_id` and its + /// inline root `root_id` is a containing block (as `SPAN_*_CB` flags) + fn inline_span_cb_flags(&self, root_id: NodeId, box_id: NodeId) -> StyleFlags { + let root = &self.nodes[root_id]; + // The spans of an anonymous inline root are descendants of its parent + let root_parent = root.is_anonymous().then_some(root.parent).flatten(); + + let mut flags = StyleFlags::empty(); + let mut current = self.nodes[box_id].parent; + while let Some(id) = current { + if id == root_id || Some(id) == root_parent { + return flags; + } + let ancestor = &self.nodes[id]; + if let Some(style) = ancestor.primary_styles() { + let display = style.clone_display(); + if display.outside() == DisplayOutside::Inline + && display.inside() == DisplayInside::Flow + { + let claims = stylo_taffy::convert::inline_containing_block_claims(&style); + if claims.absolute { + flags |= StyleFlags::SPAN_ABSOLUTE_CB; + } + if claims.fixed { + flags |= StyleFlags::SPAN_FIXED_CB; + } + } + } + current = ancestor.parent; + } + StyleFlags::empty() + } + fn compute_inline_layout_inner( &mut self, node_id: NodeId, @@ -870,11 +905,16 @@ impl LayoutPassState<'_> { // Store sizes and positions of inline boxes let mut ibox_order: u32 = 0; + let mut span_cb_flags = StyleFlags::empty(); for line in inline_layout.layout.lines() { for item in line.items() { if let parley::layout::PositionedLayoutItem::InlineBox(ibox) = item { let order = ibox_order; ibox_order += 1; + if inputs.run_mode == RunMode::PerformLayout { + span_cb_flags |= + self.inline_span_cb_flags(node_id, NodeId::from_u64(ibox.id)); + } let node = &self.nodes[NodeId::from_u64(ibox.id)]; let style = self.child_layout_style(node); let padding = style @@ -1059,6 +1099,10 @@ impl LayoutPassState<'_> { .flatten() .map(line_baseline); + if inputs.run_mode == RunMode::PerformLayout { + inline_layout.span_cb_flags = span_cb_flags; + } + // Put layout back self.nodes[node_id] .data diff --git a/packages/blitz-dom/src/node/node.rs b/packages/blitz-dom/src/node/node.rs index 4c57b35969..b088084ebd 100644 --- a/packages/blitz-dom/src/node/node.rs +++ b/packages/blitz-dom/src/node/node.rs @@ -1120,6 +1120,9 @@ impl Node { if crate::layout::replaced::is_replaced_element(&el.name.local) { flags |= stylo_taffy::StyleFlags::IS_REPLACED; } + if let Some(inline_layout) = el.inline_layout_data.as_deref() { + flags |= inline_layout.span_cb_flags; + } } #[cfg(feature = "writing-mode")] diff --git a/packages/blitz-dom/src/node/text.rs b/packages/blitz-dom/src/node/text.rs index d920b821e3..61906af145 100644 --- a/packages/blitz-dom/src/node/text.rs +++ b/packages/blitz-dom/src/node/text.rs @@ -29,6 +29,10 @@ pub struct TextLayout { /// Block-axis offset (in CSS px) of the line boxes from the top of the container's content /// box, as applied by `align-content`. pub block_offset: f32, + /// The out-of-flow positions for which some inline span with an inline box below it is a + /// containing block (as `SPAN_*_CB` flags). The inline root claims those boxes in its place. + /// Set by each `RunMode::PerformLayout` run of inline layout. + pub span_cb_flags: stylo_taffy::StyleFlags, } impl TextLayout { diff --git a/packages/stylo_taffy/src/convert.rs b/packages/stylo_taffy/src/convert.rs index c8e24e3960..0e6ddfe622 100644 --- a/packages/stylo_taffy/src/convert.rs +++ b/packages/stylo_taffy/src/convert.rs @@ -370,6 +370,29 @@ pub fn containing_block_claims(style: &stylo::ComputedValues) -> taffy::Containi } } +/// [`containing_block_claims`] for a non-atomic inline box, to which transforms and containment +/// do not apply. +pub fn inline_containing_block_claims( + style: &stylo::ComputedValues, +) -> taffy::ContainingBlockClaims { + use style::values::specified::box_::WillChangeBits; + + let box_style = style.get_box(); + let effects = style.get_effects(); + let fixed = !effects.filter.0.is_empty() + || !effects.backdrop_filter.0.is_empty() + || box_style + .will_change + .bits + .intersects(WillChangeBits::FIXPOS_CB_NON_SVG); + taffy::ContainingBlockClaims { + absolute: box_style.position != stylo::Position::Static + || fixed + || establishes_absolute_containing_block(style), + fixed, + } +} + #[inline] pub fn overflow(input: stylo::Overflow) -> taffy::Overflow { match input { diff --git a/packages/stylo_taffy/src/wrapper.rs b/packages/stylo_taffy/src/wrapper.rs index 5926baeb09..fe93789a62 100644 --- a/packages/stylo_taffy/src/wrapper.rs +++ b/packages/stylo_taffy/src/wrapper.rs @@ -23,6 +23,12 @@ bitflags! { pub struct StyleFlags: u8 { /// Whether the node is a replaced element (e.g. an image or form control) const IS_REPLACED = 1 << 0; + /// Whether an inline span laid out by the node (an inline root) is a containing block + /// for `position: absolute` boxes + const SPAN_ABSOLUTE_CB = 1 << 1; + /// Whether an inline span laid out by the node (an inline root) is a containing block + /// for `position: fixed` boxes + const SPAN_FIXED_CB = 1 << 2; } } @@ -212,7 +218,10 @@ impl> taffy::CoreStyle for TaffyStyloStyle #[inline] fn is_containing_block(&self) -> taffy::ContainingBlockClaims { - convert::containing_block_claims(&self.style) + let mut claims = convert::containing_block_claims(&self.style); + claims.absolute |= self.flags.contains(StyleFlags::SPAN_ABSOLUTE_CB); + claims.fixed |= self.flags.contains(StyleFlags::SPAN_FIXED_CB); + claims } #[inline]