aboutsummaryrefslogtreecommitdiffstats
path: root/components/layout
diff options
context:
space:
mode:
Diffstat (limited to 'components/layout')
-rw-r--r--components/layout/construct_modern.rs2
-rw-r--r--components/layout/display_list/mod.rs66
-rw-r--r--components/layout/dom.rs79
-rw-r--r--components/layout/dom_traversal.rs33
-rw-r--r--components/layout/flexbox/layout.rs92
-rw-r--r--components/layout/flexbox/mod.rs23
-rw-r--r--components/layout/flow/construct.rs173
-rw-r--r--components/layout/flow/inline/construct.rs71
-rw-r--r--components/layout/flow/inline/inline_box.rs26
-rw-r--r--components/layout/flow/inline/line.rs21
-rw-r--r--components/layout/flow/inline/mod.rs85
-rw-r--r--components/layout/flow/inline/text_run.rs72
-rw-r--r--components/layout/flow/mod.rs124
-rw-r--r--components/layout/flow/root.rs8
-rw-r--r--components/layout/formatting_contexts.rs34
-rw-r--r--components/layout/fragment_tree/box_fragment.rs11
-rw-r--r--components/layout/fragment_tree/fragment.rs27
-rw-r--r--components/layout/fragment_tree/positioning_fragment.rs17
-rw-r--r--components/layout/geom.rs86
-rw-r--r--components/layout/layout_box_base.rs8
-rw-r--r--components/layout/layout_impl.rs10
-rw-r--r--components/layout/lib.rs11
-rw-r--r--components/layout/positioned.rs300
-rw-r--r--components/layout/table/construct.rs22
-rw-r--r--components/layout/table/layout.rs1
-rw-r--r--components/layout/table/mod.rs59
-rw-r--r--components/layout/taffy/layout.rs15
-rw-r--r--components/layout/taffy/mod.rs24
-rw-r--r--components/layout/traversal.rs82
29 files changed, 1097 insertions, 485 deletions
diff --git a/components/layout/construct_modern.rs b/components/layout/construct_modern.rs
index 1489d82635b..8f1282ec9f6 100644
--- a/components/layout/construct_modern.rs
+++ b/components/layout/construct_modern.rs
@@ -142,7 +142,7 @@ impl<'a, 'dom> ModernContainerBuilder<'a, 'dom> {
.filter_map(|job| match job {
ModernContainerJob::TextRuns(runs) => {
let mut inline_formatting_context_builder =
- InlineFormattingContextBuilder::new();
+ InlineFormattingContextBuilder::new(self.info);
for flex_text_run in runs.into_iter() {
inline_formatting_context_builder
.push_text(flex_text_run.text, &flex_text_run.info);
diff --git a/components/layout/display_list/mod.rs b/components/layout/display_list/mod.rs
index d6cbb50e4a1..f017642908d 100644
--- a/components/layout/display_list/mod.rs
+++ b/components/layout/display_list/mod.rs
@@ -14,6 +14,7 @@ use euclid::{Point2D, SideOffsets2D, Size2D, UnknownUnit};
use fonts::GlyphStore;
use gradient::WebRenderGradient;
use range::Range as ServoRange;
+use servo_arc::Arc as ServoArc;
use servo_geometry::MaxRect;
use style::Zero;
use style::color::{AbsoluteColor, ColorSpace};
@@ -470,18 +471,16 @@ impl Fragment {
Fragment::AbsoluteOrFixedPositioned(_) => {},
Fragment::Positioning(positioning_fragment) => {
let positioning_fragment = positioning_fragment.borrow();
- if let Some(style) = positioning_fragment.style.as_ref() {
- let rect = positioning_fragment
- .rect
- .translate(containing_block.origin.to_vector());
- self.maybe_push_hit_test_for_style_and_tag(
- builder,
- style,
- positioning_fragment.base.tag,
- rect,
- Cursor::Default,
- );
- }
+ let rect = positioning_fragment
+ .rect
+ .translate(containing_block.origin.to_vector());
+ self.maybe_push_hit_test_for_style_and_tag(
+ builder,
+ &positioning_fragment.style,
+ positioning_fragment.base.tag,
+ rect,
+ Cursor::Default,
+ );
},
Fragment::Image(image) => {
let image = image.borrow();
@@ -544,7 +543,13 @@ impl Fragment {
},
Fragment::Text(text) => {
let text = &*text.borrow();
- match text.parent_style.get_inherited_box().visibility {
+ match text
+ .inline_styles
+ .style
+ .borrow()
+ .get_inherited_box()
+ .visibility
+ {
Visibility::Visible => {
self.build_display_list_for_text_fragment(text, builder, containing_block)
},
@@ -604,22 +609,23 @@ impl Fragment {
return;
}
+ let parent_style = fragment.inline_styles.style.borrow();
self.maybe_push_hit_test_for_style_and_tag(
builder,
- &fragment.parent_style,
+ &parent_style,
fragment.base.tag,
rect,
Cursor::Text,
);
- let color = fragment.parent_style.clone_color();
+ let color = parent_style.clone_color();
let font_metrics = &fragment.font_metrics;
let dppx = builder.context.style_context.device_pixel_ratio().get();
- let common = builder.common_properties(rect.to_webrender(), &fragment.parent_style);
+ let common = builder.common_properties(rect.to_webrender(), &parent_style);
// Shadows. According to CSS-BACKGROUNDS, text shadows render in *reverse* order (front to
// back).
- let shadows = &fragment.parent_style.get_inherited_text().text_shadow;
+ let shadows = &parent_style.get_inherited_text().text_shadow;
for shadow in shadows.0.iter().rev() {
builder.wr().push_shadow(
&wr::SpaceAndClipInfo {
@@ -642,7 +648,7 @@ impl Fragment {
let mut rect = rect;
rect.origin.y += font_metrics.ascent - font_metrics.underline_offset;
rect.size.height = Au::from_f32_px(font_metrics.underline_size.to_nearest_pixel(dppx));
- self.build_display_list_for_text_decoration(fragment, builder, &rect, &color);
+ self.build_display_list_for_text_decoration(&parent_style, builder, &rect, &color);
}
if fragment
@@ -651,7 +657,7 @@ impl Fragment {
{
let mut rect = rect;
rect.size.height = Au::from_f32_px(font_metrics.underline_size.to_nearest_pixel(dppx));
- self.build_display_list_for_text_decoration(fragment, builder, &rect, &color);
+ self.build_display_list_for_text_decoration(&parent_style, builder, &rect, &color);
}
// TODO: This caret/text selection implementation currently does not account for vertical text
@@ -678,12 +684,13 @@ impl Fragment {
Point2D::new(end.x.to_f32_px(), containing_block.max_y().to_f32_px()),
);
if let Some(selection_color) = fragment
- .selected_style
+ .inline_styles
+ .selected
+ .borrow()
.clone_background_color()
.as_absolute()
{
- let selection_common =
- builder.common_properties(selection_rect, &fragment.parent_style);
+ let selection_common = builder.common_properties(selection_rect, &parent_style);
builder.wr().push_rect(
&selection_common,
selection_rect,
@@ -709,7 +716,7 @@ impl Fragment {
),
);
let insertion_point_common =
- builder.common_properties(insertion_point_rect, &fragment.parent_style);
+ builder.common_properties(insertion_point_rect, &parent_style);
// TODO: The color of the caret is currently hardcoded to the text color.
// We should be retrieving the caret color from the style properly.
builder
@@ -734,7 +741,7 @@ impl Fragment {
let mut rect = rect;
rect.origin.y += font_metrics.ascent - font_metrics.strikeout_offset;
rect.size.height = Au::from_f32_px(font_metrics.strikeout_size.to_nearest_pixel(dppx));
- self.build_display_list_for_text_decoration(fragment, builder, &rect, &color);
+ self.build_display_list_for_text_decoration(&parent_style, builder, &rect, &color);
}
if !shadows.0.is_empty() {
@@ -744,23 +751,22 @@ impl Fragment {
fn build_display_list_for_text_decoration(
&self,
- fragment: &TextFragment,
+ parent_style: &ServoArc<ComputedValues>,
builder: &mut DisplayListBuilder,
rect: &PhysicalRect<Au>,
color: &AbsoluteColor,
) {
let rect = rect.to_webrender();
let wavy_line_thickness = (0.33 * rect.size().height).ceil();
- let text_decoration_color = fragment
- .parent_style
+ let text_decoration_color = parent_style
.clone_text_decoration_color()
.resolve_to_absolute(color);
- let text_decoration_style = fragment.parent_style.clone_text_decoration_style();
+ let text_decoration_style = parent_style.clone_text_decoration_style();
if text_decoration_style == ComputedTextDecorationStyle::MozNone {
return;
}
builder.display_list.wr.push_line(
- &builder.common_properties(rect, &fragment.parent_style),
+ &builder.common_properties(rect, parent_style),
&rect,
wavy_line_thickness,
wr::LineOrientation::Horizontal,
@@ -1026,7 +1032,7 @@ impl<'a> BuilderForBoxFragment<'a> {
for extra_background in extra_backgrounds {
let positioning_area = extra_background.rect;
let painter = BackgroundPainter {
- style: &extra_background.style.borrow_mut().0,
+ style: &extra_background.style.borrow_mut(),
painting_area_override: None,
positioning_area_override: Some(
positioning_area
diff --git a/components/layout/dom.rs b/components/layout/dom.rs
index 4400d78feb1..e3a22eb5197 100644
--- a/components/layout/dom.rs
+++ b/components/layout/dom.rs
@@ -19,14 +19,14 @@ use script_layout_interface::{
GenericLayoutDataTrait, LayoutElementType, LayoutNodeType as ScriptLayoutNodeType,
};
use servo_arc::Arc as ServoArc;
+use style::context::SharedStyleContext;
use style::properties::ComputedValues;
use style::selector_parser::PseudoElement;
use crate::cell::ArcRefCell;
-use crate::context::LayoutContext;
use crate::flexbox::FlexLevelBox;
use crate::flow::BlockLevelBox;
-use crate::flow::inline::InlineItem;
+use crate::flow::inline::{InlineItem, SharedInlineStyles};
use crate::fragment_tree::Fragment;
use crate::geom::PhysicalSize;
use crate::replaced::CanvasInfo;
@@ -59,7 +59,7 @@ impl InnerDOMLayoutData {
/// A box that is stored in one of the `DOMLayoutData` slots.
#[derive(MallocSizeOf)]
pub(super) enum LayoutBox {
- DisplayContents,
+ DisplayContents(SharedInlineStyles),
BlockLevel(ArcRefCell<BlockLevelBox>),
InlineLevel(Vec<ArcRefCell<InlineItem>>),
FlexLevel(ArcRefCell<FlexLevelBox>),
@@ -70,7 +70,7 @@ pub(super) enum LayoutBox {
impl LayoutBox {
fn invalidate_cached_fragment(&self) {
match self {
- LayoutBox::DisplayContents => {},
+ LayoutBox::DisplayContents(..) => {},
LayoutBox::BlockLevel(block_level_box) => {
block_level_box.borrow().invalidate_cached_fragment()
},
@@ -91,7 +91,7 @@ impl LayoutBox {
pub(crate) fn fragments(&self) -> Vec<Fragment> {
match self {
- LayoutBox::DisplayContents => vec![],
+ LayoutBox::DisplayContents(..) => vec![],
LayoutBox::BlockLevel(block_level_box) => block_level_box.borrow().fragments(),
LayoutBox::InlineLevel(inline_items) => inline_items
.iter()
@@ -102,6 +102,41 @@ impl LayoutBox {
LayoutBox::TableLevelBox(table_box) => table_box.fragments(),
}
}
+
+ fn repair_style(
+ &self,
+ context: &SharedStyleContext,
+ node: &ServoLayoutNode,
+ new_style: &ServoArc<ComputedValues>,
+ ) {
+ match self {
+ LayoutBox::DisplayContents(inline_shared_styles) => {
+ *inline_shared_styles.style.borrow_mut() = new_style.clone();
+ *inline_shared_styles.selected.borrow_mut() = node.to_threadsafe().selected_style();
+ },
+ LayoutBox::BlockLevel(block_level_box) => {
+ block_level_box
+ .borrow_mut()
+ .repair_style(context, node, new_style);
+ },
+ LayoutBox::InlineLevel(inline_items) => {
+ for inline_item in inline_items {
+ inline_item
+ .borrow_mut()
+ .repair_style(context, node, new_style);
+ }
+ },
+ LayoutBox::FlexLevel(flex_level_box) => {
+ flex_level_box.borrow_mut().repair_style(context, new_style)
+ },
+ LayoutBox::TableLevelBox(table_level_box) => {
+ table_level_box.repair_style(context, new_style)
+ },
+ LayoutBox::TaffyItemBox(taffy_item_box) => {
+ taffy_item_box.borrow_mut().repair_style(context, new_style)
+ },
+ }
+ }
}
/// A wrapper for [`InnerDOMLayoutData`]. This is necessary to give the entire data
@@ -167,7 +202,7 @@ pub(crate) trait NodeExt<'dom> {
fn as_iframe(&self) -> Option<(PipelineId, BrowsingContextId)>;
fn as_video(&self) -> Option<(Option<webrender_api::ImageKey>, Option<PhysicalSize<f64>>)>;
fn as_typeless_object_with_data_attribute(&self) -> Option<String>;
- fn style(&self, context: &LayoutContext) -> ServoArc<ComputedValues>;
+ fn style(&self, context: &SharedStyleContext) -> ServoArc<ComputedValues>;
fn layout_data_mut(&self) -> AtomicRefMut<'dom, InnerDOMLayoutData>;
fn layout_data(&self) -> Option<AtomicRef<'dom, InnerDOMLayoutData>>;
@@ -180,6 +215,8 @@ pub(crate) trait NodeExt<'dom> {
fn fragments_for_pseudo(&self, pseudo_element: Option<PseudoElement>) -> Vec<Fragment>;
fn invalidate_cached_fragment(&self);
+
+ fn repair_style(&self, context: &SharedStyleContext);
}
impl<'dom> NodeExt<'dom> for ServoLayoutNode<'dom> {
@@ -253,8 +290,8 @@ impl<'dom> NodeExt<'dom> for ServoLayoutNode<'dom> {
.map(|string| string.to_owned())
}
- fn style(&self, context: &LayoutContext) -> ServoArc<ComputedValues> {
- self.to_threadsafe().style(context.shared_context())
+ fn style(&self, context: &SharedStyleContext) -> ServoArc<ComputedValues> {
+ self.to_threadsafe().style(context)
}
fn layout_data_mut(&self) -> AtomicRefMut<'dom, InnerDOMLayoutData> {
@@ -339,4 +376,30 @@ impl<'dom> NodeExt<'dom> for ServoLayoutNode<'dom> {
})
.unwrap_or_default()
}
+
+ fn repair_style(&self, context: &SharedStyleContext) {
+ let data = self.layout_data_mut();
+ if let Some(layout_object) = &*data.self_box.borrow() {
+ let style = self.to_threadsafe().style(context);
+ layout_object.repair_style(context, self, &style);
+ }
+
+ if let Some(layout_object) = &*data.pseudo_before_box.borrow() {
+ if let Some(node) = self.to_threadsafe().with_pseudo(PseudoElement::Before) {
+ layout_object.repair_style(context, self, &node.style(context));
+ }
+ }
+
+ if let Some(layout_object) = &*data.pseudo_after_box.borrow() {
+ if let Some(node) = self.to_threadsafe().with_pseudo(PseudoElement::After) {
+ layout_object.repair_style(context, self, &node.style(context));
+ }
+ }
+
+ if let Some(layout_object) = &*data.pseudo_marker_box.borrow() {
+ if let Some(node) = self.to_threadsafe().with_pseudo(PseudoElement::Marker) {
+ layout_object.repair_style(context, self, &node.style(context));
+ }
+ }
+ }
}
diff --git a/components/layout/dom_traversal.rs b/components/layout/dom_traversal.rs
index a75d699a1b3..0201d72dbe2 100644
--- a/components/layout/dom_traversal.rs
+++ b/components/layout/dom_traversal.rs
@@ -23,6 +23,7 @@ use style::values::specified::Quotes;
use crate::context::LayoutContext;
use crate::dom::{BoxSlot, LayoutBox, NodeExt};
+use crate::flow::inline::SharedInlineStyles;
use crate::fragment_tree::{BaseFragmentInfo, FragmentFlags, Tag};
use crate::quotes::quotes_for_lang;
use crate::replaced::ReplacedContents;
@@ -185,6 +186,12 @@ pub(super) trait TraversalHandler<'dom> {
contents: Contents,
box_slot: BoxSlot<'dom>,
);
+
+ /// Notify the handler that we are about to recurse into a `display: contents` element.
+ fn enter_display_contents(&mut self, _: SharedInlineStyles) {}
+
+ /// Notify the handler that we have finished a `display: contents` element.
+ fn leave_display_contents(&mut self) {}
}
fn traverse_children_of<'dom>(
@@ -205,7 +212,10 @@ fn traverse_children_of<'dom>(
);
if is_text_input_element || is_textarea_element {
- let info = NodeAndStyleInfo::new(parent_element, parent_element.style(context));
+ let info = NodeAndStyleInfo::new(
+ parent_element,
+ parent_element.style(context.shared_context()),
+ );
let node_text_content = parent_element.to_threadsafe().node_text_content();
if node_text_content.is_empty() {
// The addition of zero-width space here forces the text input to have an inline formatting
@@ -224,7 +234,7 @@ fn traverse_children_of<'dom>(
if !is_text_input_element && !is_textarea_element {
for child in iter_child_nodes(parent_element) {
if child.is_text_node() {
- let info = NodeAndStyleInfo::new(child, child.style(context));
+ let info = NodeAndStyleInfo::new(child, child.style(context.shared_context()));
handler.handle_text(&info, child.to_threadsafe().node_text_content());
} else if child.is_element() {
traverse_element(child, context, handler);
@@ -245,7 +255,7 @@ fn traverse_element<'dom>(
element.unset_pseudo_element_box(PseudoElement::Marker);
let replaced = ReplacedContents::for_element(element, context);
- let style = element.style(context);
+ let style = element.style(context.shared_context());
match Display::from(style.get_box().display) {
Display::None => element.unset_all_boxes(),
Display::Contents => {
@@ -254,8 +264,15 @@ fn traverse_element<'dom>(
// <https://drafts.csswg.org/css-display-3/#valdef-display-contents>
element.unset_all_boxes()
} else {
- element.element_box_slot().set(LayoutBox::DisplayContents);
- traverse_children_of(element, context, handler)
+ let shared_inline_styles: SharedInlineStyles =
+ (&NodeAndStyleInfo::new(element, style)).into();
+ element
+ .element_box_slot()
+ .set(LayoutBox::DisplayContents(shared_inline_styles.clone()));
+
+ handler.enter_display_contents(shared_inline_styles);
+ traverse_children_of(element, context, handler);
+ handler.leave_display_contents();
}
},
Display::GeneratingBox(display) => {
@@ -308,8 +325,12 @@ fn traverse_eager_pseudo_element<'dom>(
Display::Contents => {
let items = generate_pseudo_element_content(&info.style, node, context);
let box_slot = node.pseudo_element_box_slot(pseudo_element_type);
- box_slot.set(LayoutBox::DisplayContents);
+ let shared_inline_styles: SharedInlineStyles = (&info).into();
+ box_slot.set(LayoutBox::DisplayContents(shared_inline_styles.clone()));
+
+ handler.enter_display_contents(shared_inline_styles);
traverse_pseudo_element_contents(&info, context, handler, items);
+ handler.leave_display_contents();
},
Display::GeneratingBox(display) => {
let items = generate_pseudo_element_content(&info.style, node, context);
diff --git a/components/layout/flexbox/layout.rs b/components/layout/flexbox/layout.rs
index e69b792e272..80057af2b34 100644
--- a/components/layout/flexbox/layout.rs
+++ b/components/layout/flexbox/layout.rs
@@ -29,8 +29,10 @@ use super::{FlexContainer, FlexContainerConfig, FlexItemBox, FlexLevelBox};
use crate::cell::ArcRefCell;
use crate::context::LayoutContext;
use crate::formatting_contexts::{Baselines, IndependentFormattingContextContents};
-use crate::fragment_tree::{BoxFragment, CollapsedBlockMargins, Fragment, FragmentFlags};
-use crate::geom::{AuOrAuto, LogicalRect, LogicalSides, LogicalVec2, Size, Sizes};
+use crate::fragment_tree::{
+ BoxFragment, CollapsedBlockMargins, Fragment, FragmentFlags, SpecificLayoutInfo,
+};
+use crate::geom::{AuOrAuto, LazySize, LogicalRect, LogicalSides, LogicalVec2, Size, Sizes};
use crate::layout_box_base::CacheableLayoutResult;
use crate::positioned::{
AbsolutelyPositionedBox, PositioningContext, PositioningContextLength, relative_adjustement,
@@ -142,6 +144,9 @@ struct FlexItemLayoutResult {
// Whether or not this layout had a child that dependeded on block constraints.
has_child_which_depends_on_block_constraints: bool,
+
+ // The specific layout info that this flex item had.
+ specific_layout_info: Option<SpecificLayoutInfo>,
}
impl FlexItemLayoutResult {
@@ -295,7 +300,8 @@ impl FlexLineItem<'_> {
.sides_to_flow_relative(item_margin)
.to_physical(container_writing_mode),
None, /* clearance */
- );
+ )
+ .with_specific_layout_info(self.layout_result.specific_layout_info);
// If this flex item establishes a containing block for absolutely-positioned
// descendants, then lay out any relevant absolutely-positioned children. This
@@ -649,6 +655,7 @@ impl FlexContainer {
positioning_context: &mut PositioningContext,
containing_block: &ContainingBlock,
depends_on_block_constraints: bool,
+ lazy_block_size: &LazySize,
) -> CacheableLayoutResult {
let depends_on_block_constraints =
depends_on_block_constraints || self.config.flex_direction == FlexDirection::Column;
@@ -670,14 +677,13 @@ impl FlexContainer {
// https://drafts.csswg.org/css-flexbox/#algo-main-container
let container_main_size = match self.config.flex_axis {
FlexAxis::Row => containing_block.size.inline,
- FlexAxis::Column => match containing_block.size.block {
- SizeConstraint::Definite(size) => size,
- SizeConstraint::MinMax(min, max) => self
- .main_content_sizes(layout_context, &containing_block.into(), || &flex_context)
+ FlexAxis::Column => lazy_block_size.resolve(|| {
+ let mut containing_block = IndefiniteContainingBlock::from(containing_block);
+ containing_block.size.block = None;
+ self.main_content_sizes(layout_context, &containing_block, || &flex_context)
.sizes
.max_content
- .clamp_between_extremums(min, max),
- },
+ }),
};
// Actual length may be less, but we guess that usually not by a lot
@@ -760,30 +766,23 @@ impl FlexContainer {
.map(|layout| layout.line_size.cross)
.sum::<Au>() +
cross_gap * (line_count as i32 - 1);
+ let content_block_size = match self.config.flex_axis {
+ FlexAxis::Row => content_cross_size,
+ FlexAxis::Column => container_main_size,
+ };
// https://drafts.csswg.org/css-flexbox/#algo-cross-container
- let container_cross_size = match flex_context.container_inner_size_constraint.cross {
- SizeConstraint::Definite(cross_size) => cross_size,
- SizeConstraint::MinMax(min, max) => {
- content_cross_size.clamp_between_extremums(min, max)
- },
+ let container_cross_size = match self.config.flex_axis {
+ FlexAxis::Row => lazy_block_size.resolve(|| content_cross_size),
+ FlexAxis::Column => containing_block.size.inline,
};
let container_size = FlexRelativeVec2 {
main: container_main_size,
cross: container_cross_size,
};
- let content_block_size = flex_context
- .config
- .flex_axis
- .vec2_to_flow_relative(container_size)
- .block;
-
- let mut remaining_free_cross_space =
- match flex_context.container_inner_size_constraint.cross {
- SizeConstraint::Definite(cross_size) => cross_size - content_cross_size,
- _ => Au::zero(),
- };
+
+ let mut remaining_free_cross_space = container_cross_size - content_cross_size;
// Implement fallback alignment.
//
@@ -1910,6 +1909,7 @@ impl FlexItem<'_> {
// size can differ from the hypothetical cross size, we should defer
// synthesizing until needed.
baseline_relative_to_margin_box: None,
+ specific_layout_info: None,
})
},
IndependentFormattingContextContents::NonReplaced(non_replaced) => {
@@ -1929,6 +1929,27 @@ impl FlexItem<'_> {
}
}
+ let lazy_block_size = if cross_axis_is_item_block_axis {
+ // This means that an auto size with stretch alignment will behave different than
+ // a stretch size. That's not what the spec says, but matches other browsers.
+ // To be discussed in https://github.com/w3c/csswg-drafts/issues/11784.
+ let stretch_size = containing_block
+ .size
+ .block
+ .to_definite()
+ .map(|size| Au::zero().max(size - self.pbm_auto_is_zero.cross));
+ LazySize::new(
+ &self.content_cross_sizes,
+ Direction::Block,
+ Size::FitContent,
+ Au::zero,
+ stretch_size,
+ self.is_table(),
+ )
+ } else {
+ used_main_size.into()
+ };
+
let layout = non_replaced.layout(
flex_context.layout_context,
&mut positioning_context,
@@ -1938,12 +1959,14 @@ impl FlexItem<'_> {
flex_axis == FlexAxis::Column ||
self.cross_size_stretches_to_line ||
self.depends_on_block_constraints,
+ &lazy_block_size,
);
let CacheableLayoutResult {
fragments,
content_block_size,
baselines: content_box_baselines,
depends_on_block_constraints,
+ specific_layout_info,
..
} = layout;
@@ -1954,22 +1977,7 @@ impl FlexItem<'_> {
});
let hypothetical_cross_size = if cross_axis_is_item_block_axis {
- // This means that an auto size with stretch alignment will behave different than
- // a stretch size. That's not what the spec says, but matches other browsers.
- // To be discussed in https://github.com/w3c/csswg-drafts/issues/11784.
- let stretch_size = containing_block
- .size
- .block
- .to_definite()
- .map(|size| Au::zero().max(size - self.pbm_auto_is_zero.cross));
- self.content_cross_sizes.resolve(
- Direction::Block,
- Size::FitContent,
- Au::zero,
- stretch_size,
- || content_block_size.into(),
- self.is_table(),
- )
+ lazy_block_size.resolve(|| content_block_size)
} else {
inline_size
};
@@ -2012,6 +2020,7 @@ impl FlexItem<'_> {
containing_block_block_size: item_as_containing_block.size.block,
depends_on_block_constraints,
has_child_which_depends_on_block_constraints,
+ specific_layout_info,
})
},
}
@@ -2678,6 +2687,7 @@ impl FlexItemBox {
flex_context.containing_block,
&self.independent_formatting_context.base,
false, /* depends_on_block_constraints */
+ &LazySize::intrinsic(),
)
.content_block_size
};
diff --git a/components/layout/flexbox/mod.rs b/components/layout/flexbox/mod.rs
index 27b69bf289f..91a12b31812 100644
--- a/components/layout/flexbox/mod.rs
+++ b/components/layout/flexbox/mod.rs
@@ -5,6 +5,7 @@
use geom::{FlexAxis, MainStartCrossStart};
use malloc_size_of_derive::MallocSizeOf;
use servo_arc::Arc as ServoArc;
+use style::context::SharedStyleContext;
use style::logical_geometry::WritingMode;
use style::properties::ComputedValues;
use style::properties::longhands::align_items::computed_value::T as AlignItems;
@@ -90,7 +91,6 @@ impl FlexContainerConfig {
pub(crate) struct FlexContainer {
children: Vec<ArcRefCell<FlexLevelBox>>,
- #[conditional_malloc_size_of]
style: ServoArc<ComputedValues>,
/// The configuration of this [`FlexContainer`].
@@ -137,6 +137,11 @@ impl FlexContainer {
config: FlexContainerConfig::new(&info.style),
}
}
+
+ pub(crate) fn repair_style(&mut self, new_style: &ServoArc<ComputedValues>) {
+ self.config = FlexContainerConfig::new(new_style);
+ self.style = new_style.clone();
+ }
}
#[derive(Debug, MallocSizeOf)]
@@ -146,6 +151,22 @@ pub(crate) enum FlexLevelBox {
}
impl FlexLevelBox {
+ pub(crate) fn repair_style(
+ &mut self,
+ context: &SharedStyleContext,
+ new_style: &ServoArc<ComputedValues>,
+ ) {
+ match self {
+ FlexLevelBox::FlexItem(flex_item_box) => flex_item_box
+ .independent_formatting_context
+ .repair_style(context, new_style),
+ FlexLevelBox::OutOfFlowAbsolutelyPositionedBox(positioned_box) => positioned_box
+ .borrow_mut()
+ .context
+ .repair_style(context, new_style),
+ }
+ }
+
pub(crate) fn invalidate_cached_fragment(&self) {
match self {
FlexLevelBox::FlexItem(flex_item_box) => flex_item_box
diff --git a/components/layout/flow/construct.rs b/components/layout/flow/construct.rs
index a50464123bc..334da8ae2b0 100644
--- a/components/layout/flow/construct.rs
+++ b/components/layout/flow/construct.rs
@@ -13,9 +13,9 @@ use style::selector_parser::PseudoElement;
use style::str::char_is_whitespace;
use super::OutsideMarker;
-use super::inline::InlineFormattingContext;
use super::inline::construct::InlineFormattingContextBuilder;
use super::inline::inline_box::InlineBox;
+use super::inline::{InlineFormattingContext, SharedInlineStyles};
use crate::PropagatedBoxTreeData;
use crate::cell::ArcRefCell;
use crate::context::LayoutContext;
@@ -137,16 +137,25 @@ pub(crate) struct BlockContainerBuilder<'dom, 'style> {
/// The propagated data to use for BoxTree construction.
propagated_data: PropagatedBoxTreeData,
- inline_formatting_context_builder: InlineFormattingContextBuilder,
+ /// The [`InlineFormattingContextBuilder`] if we have encountered any inline items,
+ /// otherwise None.
+ ///
+ /// TODO: This can be `OnceCell` once `OnceCell::get_mut_or_init` is stabilized.
+ inline_formatting_context_builder: Option<InlineFormattingContextBuilder>,
/// The [`NodeAndStyleInfo`] to use for anonymous block boxes pushed to the list of
- /// block-level boxes, lazily initialized (see `end_ongoing_inline_formatting_context`).
+ /// block-level boxes, lazily initialized.
anonymous_box_info: Option<NodeAndStyleInfo<'dom>>,
/// A collection of content that is being added to an anonymous table. This is
/// composed of any sequence of internal table elements or table captions that
/// are found outside of a table.
anonymous_table_content: Vec<AnonymousTableContent<'dom>>,
+
+ /// Any [`InlineFormattingContexts`] created need to know about the ongoing `display: contents`
+ /// ancestors that have been processed. This `Vec` allows passing those into new
+ /// [`InlineFormattingContext`]s that we create.
+ display_contents_shared_styles: Vec<SharedInlineStyles>,
}
impl BlockContainer {
@@ -194,26 +203,44 @@ impl<'dom, 'style> BlockContainerBuilder<'dom, 'style> {
have_already_seen_first_line_for_text_indent: false,
anonymous_box_info: None,
anonymous_table_content: Vec::new(),
- inline_formatting_context_builder: InlineFormattingContextBuilder::new(),
+ inline_formatting_context_builder: None,
+ display_contents_shared_styles: Vec::new(),
}
}
- pub(crate) fn finish(mut self) -> BlockContainer {
- debug_assert!(
- !self
- .inline_formatting_context_builder
- .currently_processing_inline_box()
- );
+ fn currently_processing_inline_box(&self) -> bool {
+ self.inline_formatting_context_builder
+ .as_ref()
+ .is_some_and(InlineFormattingContextBuilder::currently_processing_inline_box)
+ }
- self.finish_anonymous_table_if_needed();
+ fn ensure_inline_formatting_context_builder(&mut self) -> &mut InlineFormattingContextBuilder {
+ self.inline_formatting_context_builder
+ .get_or_insert_with(|| {
+ let mut builder = InlineFormattingContextBuilder::new(self.info);
+ for shared_inline_styles in self.display_contents_shared_styles.iter() {
+ builder.enter_display_contents(shared_inline_styles.clone());
+ }
+ builder
+ })
+ }
- if let Some(inline_formatting_context) = self.inline_formatting_context_builder.finish(
+ fn finish_ongoing_inline_formatting_context(&mut self) -> Option<InlineFormattingContext> {
+ self.inline_formatting_context_builder.take()?.finish(
self.context,
self.propagated_data,
!self.have_already_seen_first_line_for_text_indent,
self.info.is_single_line_text_input(),
self.info.style.writing_mode.to_bidi_level(),
- ) {
+ )
+ }
+
+ pub(crate) fn finish(mut self) -> BlockContainer {
+ debug_assert!(!self.currently_processing_inline_box());
+
+ self.finish_anonymous_table_if_needed();
+
+ if let Some(inline_formatting_context) = self.finish_ongoing_inline_formatting_context() {
// There are two options here. This block was composed of both one or more inline formatting contexts
// and child blocks OR this block was a single inline formatting context. In the latter case, we
// just return the inline formatting context as the block itself.
@@ -251,9 +278,7 @@ impl<'dom, 'style> BlockContainerBuilder<'dom, 'style> {
//
// Note that text content in the inline formatting context isn't enough to force the
// creation of an inline table. It requires the parent to be an inline box.
- let inline_table = self
- .inline_formatting_context_builder
- .currently_processing_inline_box();
+ let inline_table = self.currently_processing_inline_box();
// Text decorations are not propagated to atomic inline-level descendants.
// From https://drafts.csswg.org/css2/#lining-striking-props:
@@ -276,10 +301,16 @@ impl<'dom, 'style> BlockContainerBuilder<'dom, 'style> {
Table::construct_anonymous(self.context, self.info, contents, propagated_data);
if inline_table {
- self.inline_formatting_context_builder.push_atomic(ifc);
+ self.ensure_inline_formatting_context_builder()
+ .push_atomic(ifc);
} else {
let table_block = ArcRefCell::new(BlockLevelBox::Independent(ifc));
- self.end_ongoing_inline_formatting_context();
+
+ if let Some(inline_formatting_context) = self.finish_ongoing_inline_formatting_context()
+ {
+ self.push_block_level_job_for_inline_formatting_context(inline_formatting_context);
+ }
+
self.block_level_boxes.push(BlockLevelJob {
info: table_info,
box_slot: BoxSlot::dummy(),
@@ -363,7 +394,22 @@ impl<'dom> TraversalHandler<'dom> for BlockContainerBuilder<'dom, '_> {
self.finish_anonymous_table_if_needed();
}
- self.inline_formatting_context_builder.push_text(text, info);
+ self.ensure_inline_formatting_context_builder()
+ .push_text(text, info);
+ }
+
+ fn enter_display_contents(&mut self, styles: SharedInlineStyles) {
+ self.display_contents_shared_styles.push(styles.clone());
+ if let Some(builder) = self.inline_formatting_context_builder.as_mut() {
+ builder.enter_display_contents(styles);
+ }
+ }
+
+ fn leave_display_contents(&mut self) {
+ self.display_contents_shared_styles.pop();
+ if let Some(builder) = self.inline_formatting_context_builder.as_mut() {
+ builder.leave_display_contents();
+ }
}
}
@@ -433,14 +479,16 @@ impl<'dom> BlockContainerBuilder<'dom, '_> {
(display_inside, contents.is_replaced())
else {
// If this inline element is an atomic, handle it and return.
- let atomic = self.inline_formatting_context_builder.push_atomic(
+ let context = self.context;
+ let propagaged_data = self.propagated_data.without_text_decorations();
+ let atomic = self.ensure_inline_formatting_context_builder().push_atomic(
IndependentFormattingContext::construct(
- self.context,
+ context,
info,
display_inside,
contents,
// Text decorations are not propagated to atomic inline-level descendants.
- self.propagated_data.without_text_decorations(),
+ propagaged_data,
),
);
box_slot.set(LayoutBox::InlineLevel(vec![atomic]));
@@ -449,7 +497,7 @@ impl<'dom> BlockContainerBuilder<'dom, '_> {
// Otherwise, this is just a normal inline box. Whatever happened before, all we need to do
// before recurring is to remember this ongoing inline level box.
- self.inline_formatting_context_builder
+ self.ensure_inline_formatting_context_builder()
.start_inline_box(InlineBox::new(info), None);
if is_list_item {
@@ -476,7 +524,10 @@ impl<'dom> BlockContainerBuilder<'dom, '_> {
// `InlineFormattingContextBuilder::end_inline_box()` is returning all of those box tree
// items.
box_slot.set(LayoutBox::InlineLevel(
- self.inline_formatting_context_builder.end_inline_box(),
+ self.inline_formatting_context_builder
+ .as_mut()
+ .expect("Should be building an InlineFormattingContext")
+ .end_inline_box(),
));
}
@@ -495,12 +546,15 @@ impl<'dom> BlockContainerBuilder<'dom, '_> {
// that we want to have after we push the block below.
if let Some(inline_formatting_context) = self
.inline_formatting_context_builder
- .split_around_block_and_finish(
- self.context,
- self.propagated_data,
- !self.have_already_seen_first_line_for_text_indent,
- self.info.style.writing_mode.to_bidi_level(),
- )
+ .as_mut()
+ .and_then(|builder| {
+ builder.split_around_block_and_finish(
+ self.context,
+ self.propagated_data,
+ !self.have_already_seen_first_line_for_text_indent,
+ self.info.style.writing_mode.to_bidi_level(),
+ )
+ })
{
self.push_block_level_job_for_inline_formatting_context(inline_formatting_context);
}
@@ -555,17 +609,18 @@ impl<'dom> BlockContainerBuilder<'dom, '_> {
contents: Contents,
box_slot: BoxSlot<'dom>,
) {
- if !self.inline_formatting_context_builder.is_empty() {
- let inline_level_box = self
- .inline_formatting_context_builder
- .push_absolutely_positioned_box(AbsolutelyPositionedBox::construct(
- self.context,
- info,
- display_inside,
- contents,
- ));
- box_slot.set(LayoutBox::InlineLevel(vec![inline_level_box]));
- return;
+ if let Some(builder) = self.inline_formatting_context_builder.as_mut() {
+ if !builder.is_empty() {
+ let inline_level_box =
+ builder.push_absolutely_positioned_box(AbsolutelyPositionedBox::construct(
+ self.context,
+ info,
+ display_inside,
+ contents,
+ ));
+ box_slot.set(LayoutBox::InlineLevel(vec![inline_level_box]));
+ return;
+ }
}
let kind = BlockLevelCreator::OutOfFlowAbsolutelyPositionedBox {
@@ -587,18 +642,18 @@ impl<'dom> BlockContainerBuilder<'dom, '_> {
contents: Contents,
box_slot: BoxSlot<'dom>,
) {
- if !self.inline_formatting_context_builder.is_empty() {
- let inline_level_box =
- self.inline_formatting_context_builder
- .push_float_box(FloatBox::construct(
- self.context,
- info,
- display_inside,
- contents,
- self.propagated_data,
- ));
- box_slot.set(LayoutBox::InlineLevel(vec![inline_level_box]));
- return;
+ if let Some(builder) = self.inline_formatting_context_builder.as_mut() {
+ if !builder.is_empty() {
+ let inline_level_box = builder.push_float_box(FloatBox::construct(
+ self.context,
+ info,
+ display_inside,
+ contents,
+ self.propagated_data,
+ ));
+ box_slot.set(LayoutBox::InlineLevel(vec![inline_level_box]));
+ return;
+ }
}
let kind = BlockLevelCreator::OutOfFlowFloatBox {
@@ -613,18 +668,6 @@ impl<'dom> BlockContainerBuilder<'dom, '_> {
});
}
- fn end_ongoing_inline_formatting_context(&mut self) {
- if let Some(inline_formatting_context) = self.inline_formatting_context_builder.finish(
- self.context,
- self.propagated_data,
- !self.have_already_seen_first_line_for_text_indent,
- self.info.is_single_line_text_input(),
- self.info.style.writing_mode.to_bidi_level(),
- ) {
- self.push_block_level_job_for_inline_formatting_context(inline_formatting_context);
- }
- }
-
fn push_block_level_job_for_inline_formatting_context(
&mut self,
inline_formatting_context: InlineFormattingContext,
diff --git a/components/layout/flow/inline/construct.rs b/components/layout/flow/inline/construct.rs
index 74b0cf4ea7d..a99de1679a4 100644
--- a/components/layout/flow/inline/construct.rs
+++ b/components/layout/flow/inline/construct.rs
@@ -7,13 +7,15 @@ use std::char::{ToLowercase, ToUppercase};
use icu_segmenter::WordSegmenter;
use itertools::izip;
-use servo_arc::Arc;
use style::computed_values::white_space_collapse::T as WhiteSpaceCollapse;
use style::values::specified::text::TextTransformCase;
use unicode_bidi::Level;
use super::text_run::TextRun;
-use super::{InlineBox, InlineBoxIdentifier, InlineBoxes, InlineFormattingContext, InlineItem};
+use super::{
+ InlineBox, InlineBoxIdentifier, InlineBoxes, InlineFormattingContext, InlineItem,
+ SharedInlineStyles,
+};
use crate::PropagatedBoxTreeData;
use crate::cell::ArcRefCell;
use crate::context::LayoutContext;
@@ -25,6 +27,12 @@ use crate::style_ext::ComputedValuesExt;
#[derive(Default)]
pub(crate) struct InlineFormattingContextBuilder {
+ /// A stack of [`SharedInlineStyles`] including one for the root, one for each inline box on the
+ /// inline box stack, and importantly, one for every `display: contents` element that we are
+ /// currently processing. Normally `display: contents` elements don't affect the structure of
+ /// the [`InlineFormattingContext`], but the styles they provide do style their children.
+ shared_inline_styles_stack: Vec<SharedInlineStyles>,
+
/// The collection of text strings that make up this [`InlineFormattingContext`] under
/// construction.
pub text_segments: Vec<String>,
@@ -63,7 +71,7 @@ pub(crate) struct InlineFormattingContextBuilder {
/// The traversal is at all times as deep in the tree as this stack is,
/// which is why the code doesn't need to keep track of the actual
/// container root (see `handle_inline_level_element`).
- ///
+ //_
/// When an inline box ends, it's removed from this stack.
inline_box_stack: Vec<InlineBoxIdentifier>,
@@ -83,10 +91,17 @@ pub(crate) struct InlineFormattingContextBuilder {
}
impl InlineFormattingContextBuilder {
- pub(crate) fn new() -> Self {
- // For the purposes of `text-transform: capitalize` the start of the IFC is a word boundary.
+ pub(crate) fn new(info: &NodeAndStyleInfo) -> Self {
+ Self::new_for_shared_styles(vec![info.into()])
+ }
+
+ pub(crate) fn new_for_shared_styles(
+ shared_inline_styles_stack: Vec<SharedInlineStyles>,
+ ) -> Self {
Self {
+ // For the purposes of `text-transform: capitalize` the start of the IFC is a word boundary.
on_word_boundary: true,
+ shared_inline_styles_stack,
..Default::default()
}
}
@@ -100,6 +115,13 @@ impl InlineFormattingContextBuilder {
self.current_text_offset += string_to_push.len();
}
+ fn shared_inline_styles(&self) -> SharedInlineStyles {
+ self.shared_inline_styles_stack
+ .last()
+ .expect("Should always have at least one SharedInlineStyles")
+ .clone()
+ }
+
/// Return true if this [`InlineFormattingContextBuilder`] is empty for the purposes of ignoring
/// during box tree construction. An IFC is empty if it only contains TextRuns with
/// completely collapsible whitespace. When that happens it can be ignored completely.
@@ -135,7 +157,7 @@ impl InlineFormattingContextBuilder {
independent_formatting_context: IndependentFormattingContext,
) -> ArcRefCell<InlineItem> {
let inline_level_box = ArcRefCell::new(InlineItem::Atomic(
- Arc::new(independent_formatting_context),
+ ArcRefCell::new(independent_formatting_context),
self.current_text_offset,
Level::ltr(), /* This will be assigned later if necessary. */
));
@@ -166,7 +188,8 @@ impl InlineFormattingContextBuilder {
}
pub(crate) fn push_float_box(&mut self, float_box: FloatBox) -> ArcRefCell<InlineItem> {
- let inline_level_box = ArcRefCell::new(InlineItem::OutOfFlowFloatBox(Arc::new(float_box)));
+ let inline_level_box =
+ ArcRefCell::new(InlineItem::OutOfFlowFloatBox(ArcRefCell::new(float_box)));
self.inline_items.push(inline_level_box.clone());
self.contains_floats = true;
inline_level_box
@@ -179,6 +202,14 @@ impl InlineFormattingContextBuilder {
) {
self.push_control_character_string(inline_box.base.style.bidi_control_chars().0);
+ // Don't push a `SharedInlineStyles` if we are pushing this box when splitting
+ // an IFC for a block-in-inline split. Shared styles are pushed as part of setting
+ // up the second split of the IFC.
+ if inline_box.is_first_split {
+ self.shared_inline_styles_stack
+ .push(inline_box.shared_inline_styles.clone());
+ }
+
let (identifier, inline_box) = self.inline_boxes.start_inline_box(inline_box);
let inline_level_box = ArcRefCell::new(InlineItem::StartInlineBox(inline_box));
self.inline_items.push(inline_level_box.clone());
@@ -194,6 +225,8 @@ impl InlineFormattingContextBuilder {
/// a single box tree items may be produced for a single inline box when that inline
/// box is split around a block-level element.
pub(crate) fn end_inline_box(&mut self) -> Vec<ArcRefCell<InlineItem>> {
+ self.shared_inline_styles_stack.pop();
+
let (identifier, block_in_inline_splits) = self.end_inline_box_internal();
let inline_level_box = self.inline_boxes.get(&identifier);
{
@@ -272,8 +305,6 @@ impl InlineFormattingContextBuilder {
}
let selection_range = info.get_selection_range();
- let selected_style = info.get_selected_style();
-
if let Some(last_character) = new_text.chars().next_back() {
self.on_word_boundary = last_character.is_whitespace();
self.last_inline_box_ended_with_collapsible_white_space =
@@ -295,14 +326,21 @@ impl InlineFormattingContextBuilder {
.push(ArcRefCell::new(InlineItem::TextRun(ArcRefCell::new(
TextRun::new(
info.into(),
- info.style.clone(),
+ self.shared_inline_styles(),
new_range,
selection_range,
- selected_style,
),
))));
}
+ pub(crate) fn enter_display_contents(&mut self, shared_inline_styles: SharedInlineStyles) {
+ self.shared_inline_styles_stack.push(shared_inline_styles);
+ }
+
+ pub(crate) fn leave_display_contents(&mut self) {
+ self.shared_inline_styles_stack.pop();
+ }
+
pub(crate) fn split_around_block_and_finish(
&mut self,
layout_context: &LayoutContext,
@@ -318,7 +356,8 @@ impl InlineFormattingContextBuilder {
// context. It has the same inline box structure as this builder, except the boxes are
// marked as not being the first fragment. No inline content is carried over to this new
// builder.
- let mut new_builder = InlineFormattingContextBuilder::new();
+ let mut new_builder = Self::new_for_shared_styles(self.shared_inline_styles_stack.clone());
+
let block_in_inline_splits = std::mem::take(&mut self.block_in_inline_splits);
for (identifier, historical_inline_boxes) in
izip!(self.inline_box_stack.iter(), block_in_inline_splits)
@@ -356,7 +395,7 @@ impl InlineFormattingContextBuilder {
/// Finish the current inline formatting context, returning [`None`] if the context was empty.
pub(crate) fn finish(
- &mut self,
+ self,
layout_context: &LayoutContext,
propagated_data: PropagatedBoxTreeData,
has_first_formatted_line: bool,
@@ -367,11 +406,9 @@ impl InlineFormattingContextBuilder {
return None;
}
- let old_builder = std::mem::replace(self, InlineFormattingContextBuilder::new());
- assert!(old_builder.inline_box_stack.is_empty());
-
+ assert!(self.inline_box_stack.is_empty());
Some(InlineFormattingContext::new_with_builder(
- old_builder,
+ self,
layout_context,
propagated_data,
has_first_formatted_line,
diff --git a/components/layout/flow/inline/inline_box.rs b/components/layout/flow/inline/inline_box.rs
index 1c953c13074..b547f3b5935 100644
--- a/components/layout/flow/inline/inline_box.rs
+++ b/components/layout/flow/inline/inline_box.rs
@@ -7,8 +7,15 @@ use std::vec::IntoIter;
use app_units::Au;
use fonts::FontMetrics;
use malloc_size_of_derive::MallocSizeOf;
-
-use super::{InlineContainerState, InlineContainerStateFlags, inline_container_needs_strut};
+use script::layout_dom::ServoLayoutNode;
+use script_layout_interface::wrapper_traits::{LayoutNode, ThreadSafeLayoutNode};
+use servo_arc::Arc as ServoArc;
+use style::properties::ComputedValues;
+
+use super::{
+ InlineContainerState, InlineContainerStateFlags, SharedInlineStyles,
+ inline_container_needs_strut,
+};
use crate::ContainingBlock;
use crate::cell::ArcRefCell;
use crate::context::LayoutContext;
@@ -20,6 +27,9 @@ use crate::style_ext::{LayoutStyle, PaddingBorderMargin};
#[derive(Debug, MallocSizeOf)]
pub(crate) struct InlineBox {
pub base: LayoutBoxBase,
+ /// The [`SharedInlineStyles`] for this [`InlineBox`] that are used to share styles
+ /// with all [`super::TextRun`] children.
+ pub(super) shared_inline_styles: SharedInlineStyles,
/// The identifier of this inline box in the containing [`super::InlineFormattingContext`].
pub(super) identifier: InlineBoxIdentifier,
/// Whether or not this is the first instance of an [`InlineBox`] before a possible
@@ -37,6 +47,7 @@ impl InlineBox {
pub(crate) fn new(info: &NodeAndStyleInfo) -> Self {
Self {
base: LayoutBoxBase::new(info.into(), info.style.clone()),
+ shared_inline_styles: info.into(),
// This will be assigned later, when the box is actually added to the IFC.
identifier: InlineBoxIdentifier::default(),
is_first_split: true,
@@ -48,6 +59,7 @@ impl InlineBox {
pub(crate) fn split_around_block(&self) -> Self {
Self {
base: LayoutBoxBase::new(self.base.base_fragment_info, self.base.style.clone()),
+ shared_inline_styles: self.shared_inline_styles.clone(),
is_first_split: false,
is_last_split: false,
..*self
@@ -58,6 +70,16 @@ impl InlineBox {
pub(crate) fn layout_style(&self) -> LayoutStyle {
LayoutStyle::Default(&self.base.style)
}
+
+ pub(crate) fn repair_style(
+ &mut self,
+ node: &ServoLayoutNode,
+ new_style: &ServoArc<ComputedValues>,
+ ) {
+ self.base.repair_style(new_style);
+ *self.shared_inline_styles.style.borrow_mut() = new_style.clone();
+ *self.shared_inline_styles.selected.borrow_mut() = node.to_threadsafe().selected_style();
+ }
}
#[derive(Debug, Default, MallocSizeOf)]
diff --git a/components/layout/flow/inline/line.rs b/components/layout/flow/inline/line.rs
index 80bab1080ed..3b92078d67d 100644
--- a/components/layout/flow/inline/line.rs
+++ b/components/layout/flow/inline/line.rs
@@ -7,7 +7,6 @@ use bitflags::bitflags;
use fonts::{ByteIndex, FontMetrics, GlyphStore};
use itertools::Either;
use range::Range;
-use servo_arc::Arc;
use style::Zero;
use style::computed_values::position::T as Position;
use style::computed_values::white_space_collapse::T as WhiteSpaceCollapse;
@@ -21,7 +20,7 @@ use unicode_bidi::{BidiInfo, Level};
use webrender_api::FontInstanceKey;
use super::inline_box::{InlineBoxContainerState, InlineBoxIdentifier, InlineBoxTreePathToken};
-use super::{InlineFormattingContextLayout, LineBlockSizes};
+use super::{InlineFormattingContextLayout, LineBlockSizes, SharedInlineStyles};
use crate::cell::ArcRefCell;
use crate::fragment_tree::{BaseFragmentInfo, BoxFragment, Fragment, TextFragment};
use crate::geom::{LogicalRect, LogicalVec2, PhysicalRect, ToLogical};
@@ -568,7 +567,7 @@ impl LineItemLayout<'_, '_> {
self.current_state.fragments.push((
Fragment::Text(ArcRefCell::new(TextFragment {
base: text_item.base_fragment_info.into(),
- parent_style: text_item.parent_style,
+ inline_styles: text_item.inline_styles.clone(),
rect: PhysicalRect::zero(),
font_metrics: text_item.font_metrics,
font_key: text_item.font_key,
@@ -576,7 +575,6 @@ impl LineItemLayout<'_, '_> {
text_decoration_line: text_item.text_decoration_line,
justification_adjustment: self.justification_adjustment,
selection_range: text_item.selection_range,
- selected_style: text_item.selected_style,
})),
content_rect,
));
@@ -763,7 +761,7 @@ impl LineItem {
pub(super) struct TextRunLineItem {
pub base_fragment_info: BaseFragmentInfo,
- pub parent_style: Arc<ComputedValues>,
+ pub inline_styles: SharedInlineStyles,
pub text: Vec<std::sync::Arc<GlyphStore>>,
pub font_metrics: FontMetrics,
pub font_key: FontInstanceKey,
@@ -771,13 +769,16 @@ pub(super) struct TextRunLineItem {
/// The BiDi level of this [`TextRunLineItem`] to enable reordering.
pub bidi_level: Level,
pub selection_range: Option<Range<ByteIndex>>,
- pub selected_style: Arc<ComputedValues>,
}
impl TextRunLineItem {
fn trim_whitespace_at_end(&mut self, whitespace_trimmed: &mut Au) -> bool {
if matches!(
- self.parent_style.get_inherited_text().white_space_collapse,
+ self.inline_styles
+ .style
+ .borrow()
+ .get_inherited_text()
+ .white_space_collapse,
WhiteSpaceCollapse::Preserve | WhiteSpaceCollapse::BreakSpaces
) {
return false;
@@ -803,7 +804,11 @@ impl TextRunLineItem {
fn trim_whitespace_at_start(&mut self, whitespace_trimmed: &mut Au) -> bool {
if matches!(
- self.parent_style.get_inherited_text().white_space_collapse,
+ self.inline_styles
+ .style
+ .borrow()
+ .get_inherited_text()
+ .white_space_collapse,
WhiteSpaceCollapse::Preserve | WhiteSpaceCollapse::BreakSpaces
) {
return false;
diff --git a/components/layout/flow/inline/mod.rs b/components/layout/flow/inline/mod.rs
index 2023f4e7174..7e69aa1aaae 100644
--- a/components/layout/flow/inline/mod.rs
+++ b/components/layout/flow/inline/mod.rs
@@ -90,12 +90,13 @@ use line::{
use line_breaker::LineBreaker;
use malloc_size_of_derive::MallocSizeOf;
use range::Range;
+use script::layout_dom::ServoLayoutNode;
use servo_arc::Arc;
use style::Zero;
use style::computed_values::text_wrap_mode::T as TextWrapMode;
use style::computed_values::vertical_align::T as VerticalAlign;
use style::computed_values::white_space_collapse::T as WhiteSpaceCollapse;
-use style::context::QuirksMode;
+use style::context::{QuirksMode, SharedStyleContext};
use style::properties::ComputedValues;
use style::properties::style_structs::InheritedText;
use style::values::generics::box_::VerticalAlignKeyword;
@@ -118,6 +119,7 @@ use super::{
};
use crate::cell::ArcRefCell;
use crate::context::LayoutContext;
+use crate::dom_traversal::NodeAndStyleInfo;
use crate::flow::CollapsibleWithParentStartMargin;
use crate::flow::float::{FloatBox, SequentialLayoutState};
use crate::formatting_contexts::{
@@ -131,7 +133,7 @@ use crate::geom::{LogicalRect, LogicalVec2, ToLogical};
use crate::positioned::{AbsolutelyPositionedBox, PositioningContext};
use crate::sizing::{ComputeInlineContentSizes, ContentSizes, InlineContentSizesResult};
use crate::style_ext::{ComputedValuesExt, PaddingBorderMargin};
-use crate::{ConstraintSpace, ContainingBlock, PropagatedBoxTreeData};
+use crate::{ConstraintSpace, ContainingBlock, PropagatedBoxTreeData, SharedStyle};
// From gfxFontConstants.h in Firefox.
static FONT_SUBSCRIPT_OFFSET_RATIO: f32 = 0.20;
@@ -173,6 +175,25 @@ pub(crate) struct InlineFormattingContext {
pub(super) has_right_to_left_content: bool,
}
+/// [`TextRun`] and `TextFragment`s need a handle on their parent inline box (or inline
+/// formatting context root)'s style. In order to implement incremental layout, these are
+/// wrapped in [`SharedStyle`]. This allows updating the parent box tree element without
+/// updating every single descendant box tree node and fragment.
+#[derive(Clone, Debug, MallocSizeOf)]
+pub(crate) struct SharedInlineStyles {
+ pub style: SharedStyle,
+ pub selected: SharedStyle,
+}
+
+impl From<&NodeAndStyleInfo<'_>> for SharedInlineStyles {
+ fn from(info: &NodeAndStyleInfo) -> Self {
+ Self {
+ style: SharedStyle::new(info.style.clone()),
+ selected: SharedStyle::new(info.get_selected_style()),
+ }
+ }
+}
+
/// A collection of data used to cache [`FontMetrics`] in the [`InlineFormattingContext`]
#[derive(Debug, MallocSizeOf)]
pub(crate) struct FontKeyAndMetrics {
@@ -190,15 +211,41 @@ pub(crate) enum InlineItem {
ArcRefCell<AbsolutelyPositionedBox>,
usize, /* offset_in_text */
),
- OutOfFlowFloatBox(#[conditional_malloc_size_of] Arc<FloatBox>),
+ OutOfFlowFloatBox(ArcRefCell<FloatBox>),
Atomic(
- #[conditional_malloc_size_of] Arc<IndependentFormattingContext>,
+ ArcRefCell<IndependentFormattingContext>,
usize, /* offset_in_text */
Level, /* bidi_level */
),
}
impl InlineItem {
+ pub(crate) fn repair_style(
+ &self,
+ context: &SharedStyleContext,
+ node: &ServoLayoutNode,
+ new_style: &Arc<ComputedValues>,
+ ) {
+ match self {
+ InlineItem::StartInlineBox(inline_box) => {
+ inline_box.borrow_mut().repair_style(node, new_style);
+ },
+ InlineItem::EndInlineBox => {},
+ // TextRun holds a handle the `InlineSharedStyles` which is updated when repairing inline box
+ // and `display: contents` styles.
+ InlineItem::TextRun(..) => {},
+ InlineItem::OutOfFlowAbsolutelyPositionedBox(positioned_box, ..) => positioned_box
+ .borrow_mut()
+ .context
+ .repair_style(context, new_style),
+ InlineItem::OutOfFlowFloatBox(float_box) => float_box
+ .borrow_mut()
+ .contents
+ .repair_style(context, new_style),
+ InlineItem::Atomic(atomic, ..) => atomic.borrow_mut().repair_style(context, new_style),
+ }
+ }
+
pub(crate) fn invalidate_cached_fragment(&self) {
match self {
InlineItem::StartInlineBox(inline_box) => {
@@ -212,11 +259,14 @@ impl InlineItem {
.base
.invalidate_cached_fragment();
},
- InlineItem::OutOfFlowFloatBox(float_box) => {
- float_box.contents.base.invalidate_cached_fragment()
- },
+ InlineItem::OutOfFlowFloatBox(float_box) => float_box
+ .borrow()
+ .contents
+ .base
+ .invalidate_cached_fragment(),
InlineItem::Atomic(independent_formatting_context, ..) => {
independent_formatting_context
+ .borrow()
.base
.invalidate_cached_fragment();
},
@@ -232,9 +282,11 @@ impl InlineItem {
InlineItem::OutOfFlowAbsolutelyPositionedBox(positioned_box, ..) => {
positioned_box.borrow().context.base.fragments()
},
- InlineItem::OutOfFlowFloatBox(float_box) => float_box.contents.base.fragments(),
+ InlineItem::OutOfFlowFloatBox(float_box) => {
+ float_box.borrow().contents.base.fragments()
+ },
InlineItem::Atomic(independent_formatting_context, ..) => {
- independent_formatting_context.base.fragments()
+ independent_formatting_context.borrow().base.fragments()
},
}
}
@@ -958,6 +1010,7 @@ impl InlineFormattingContextLayout<'_> {
.as_physical(Some(self.containing_block));
self.fragments
.push(Fragment::Positioning(PositioningFragment::new_anonymous(
+ self.root_nesting_level.style.clone(),
physical_line_rect,
fragments,
)));
@@ -1313,7 +1366,7 @@ impl InlineFormattingContextLayout<'_> {
) {
let inline_advance = glyph_store.total_advance();
let flags = if glyph_store.is_whitespace() {
- SegmentContentFlags::from(text_run.parent_style.get_inherited_text())
+ SegmentContentFlags::from(text_run.inline_styles.style.borrow().get_inherited_text())
} else {
SegmentContentFlags::empty()
};
@@ -1398,13 +1451,12 @@ impl InlineFormattingContextLayout<'_> {
TextRunLineItem {
text: vec![glyph_store],
base_fragment_info: text_run.base_fragment_info,
- parent_style: text_run.parent_style.clone(),
+ inline_styles: text_run.inline_styles.clone(),
font_metrics,
font_key: ifc_font_info.key,
text_decoration_line: self.current_inline_container_state().text_decoration_line,
bidi_level,
selection_range,
- selected_style: text_run.selected_style.clone(),
},
));
}
@@ -1751,7 +1803,7 @@ impl InlineFormattingContext {
InlineItem::EndInlineBox => layout.finish_inline_box(),
InlineItem::TextRun(run) => run.borrow().layout_into_line_items(&mut layout),
InlineItem::Atomic(atomic_formatting_context, offset_in_text, bidi_level) => {
- atomic_formatting_context.layout_into_line_items(
+ atomic_formatting_context.borrow().layout_into_line_items(
&mut layout,
*offset_in_text,
*bidi_level,
@@ -1766,7 +1818,7 @@ impl InlineFormattingContext {
));
},
InlineItem::OutOfFlowFloatBox(float_box) => {
- float_box.layout_into_line_items(&mut layout);
+ float_box.borrow().layout_into_line_items(&mut layout);
},
}
}
@@ -2363,8 +2415,9 @@ impl<'layout_data> ContentSizesComputation<'layout_data> {
},
InlineItem::TextRun(text_run) => {
let text_run = &*text_run.borrow();
+ let parent_style = text_run.inline_styles.style.borrow();
for segment in text_run.shaped_text.iter() {
- let style_text = text_run.parent_style.get_inherited_text();
+ let style_text = parent_style.get_inherited_text();
let can_wrap = style_text.text_wrap_mode == TextWrapMode::Wrap;
// TODO: This should take account whether or not the first and last character prevent
@@ -2428,7 +2481,7 @@ impl<'layout_data> ContentSizesComputation<'layout_data> {
let InlineContentSizesResult {
sizes: outer,
depends_on_block_constraints,
- } = atomic.outer_inline_content_sizes(
+ } = atomic.borrow().outer_inline_content_sizes(
self.layout_context,
&self.constraint_space.into(),
&LogicalVec2::zero(),
diff --git a/components/layout/flow/inline/text_run.rs b/components/layout/flow/inline/text_run.rs
index 0d0c6398017..591c7b9b5e2 100644
--- a/components/layout/flow/inline/text_run.rs
+++ b/components/layout/flow/inline/text_run.rs
@@ -26,7 +26,7 @@ use unicode_script::Script;
use xi_unicode::linebreak_property;
use super::line_breaker::LineBreaker;
-use super::{FontKeyAndMetrics, InlineFormattingContextLayout};
+use super::{FontKeyAndMetrics, InlineFormattingContextLayout, SharedInlineStyles};
use crate::fragment_tree::BaseFragmentInfo;
// These constants are the xi-unicode line breaking classes that are defined in
@@ -37,22 +37,6 @@ pub(crate) const XI_LINE_BREAKING_CLASS_ZW: u8 = 28;
pub(crate) const XI_LINE_BREAKING_CLASS_WJ: u8 = 30;
pub(crate) const XI_LINE_BREAKING_CLASS_ZWJ: u8 = 42;
-/// <https://www.w3.org/TR/css-display-3/#css-text-run>
-#[derive(Debug, MallocSizeOf)]
-pub(crate) struct TextRun {
- pub base_fragment_info: BaseFragmentInfo,
- #[conditional_malloc_size_of]
- pub parent_style: Arc<ComputedValues>,
- pub text_range: Range<usize>,
-
- /// The text of this [`TextRun`] with a font selected, broken into unbreakable
- /// segments, and shaped.
- pub shaped_text: Vec<TextRunSegment>,
- pub selection_range: Option<ServoRange<ByteIndex>>,
- #[conditional_malloc_size_of]
- pub selected_style: Arc<ComputedValues>,
-}
-
// There are two reasons why we might want to break at the start:
//
// 1. The line breaker told us that a break was necessary between two separate
@@ -334,21 +318,49 @@ impl TextRunSegment {
}
}
+/// A single [`TextRun`] for the box tree. These are all descendants of
+/// [`super::InlineBox`] or the root of the [`super::InlineFormattingContext`]. During
+/// box tree construction, text is split into [`TextRun`]s based on their font, script,
+/// etc. When these are created text is already shaped.
+///
+/// <https://www.w3.org/TR/css-display-3/#css-text-run>
+#[derive(Debug, MallocSizeOf)]
+pub(crate) struct TextRun {
+ /// The [`BaseFragmentInfo`] for this [`TextRun`]. Usually this comes from the
+ /// original text node in the DOM for the text.
+ pub base_fragment_info: BaseFragmentInfo,
+
+ /// The [`crate::SharedStyle`] from this [`TextRun`]s parent element. This is
+ /// shared so that incremental layout can simply update the parent element and
+ /// this [`TextRun`] will be updated automatically.
+ pub inline_styles: SharedInlineStyles,
+
+ /// The range of text in [`super::InlineFormattingContext::text_content`] of the
+ /// [`super::InlineFormattingContext`] that owns this [`TextRun`]. These are UTF-8 offsets.
+ pub text_range: Range<usize>,
+
+ /// The text of this [`TextRun`] with a font selected, broken into unbreakable
+ /// segments, and shaped.
+ pub shaped_text: Vec<TextRunSegment>,
+
+ /// The selection range for the DOM text node that originated this [`TextRun`]. This
+ /// comes directly from the DOM.
+ pub selection_range: Option<ServoRange<ByteIndex>>,
+}
+
impl TextRun {
pub(crate) fn new(
base_fragment_info: BaseFragmentInfo,
- parent_style: Arc<ComputedValues>,
+ inline_styles: SharedInlineStyles,
text_range: Range<usize>,
selection_range: Option<ServoRange<ByteIndex>>,
- selected_style: Arc<ComputedValues>,
) -> Self {
Self {
base_fragment_info,
- parent_style,
+ inline_styles,
text_range,
shaped_text: Vec::new(),
selection_range,
- selected_style,
}
}
@@ -360,11 +372,12 @@ impl TextRun {
font_cache: &mut Vec<FontKeyAndMetrics>,
bidi_info: &BidiInfo,
) {
- let inherited_text_style = self.parent_style.get_inherited_text().clone();
+ let parent_style = self.inline_styles.style.borrow().clone();
+ let inherited_text_style = parent_style.get_inherited_text().clone();
let letter_spacing = inherited_text_style
.letter_spacing
.0
- .resolve(self.parent_style.clone_font().font_size.computed_size());
+ .resolve(parent_style.clone_font().font_size.computed_size());
let letter_spacing = if letter_spacing.px() != 0. {
Some(app_units::Au::from(letter_spacing))
} else {
@@ -384,7 +397,13 @@ impl TextRun {
let style_word_spacing: Option<Au> = specified_word_spacing.to_length().map(|l| l.into());
let segments = self
- .segment_text_by_font(formatting_context_text, font_context, font_cache, bidi_info)
+ .segment_text_by_font(
+ formatting_context_text,
+ font_context,
+ font_cache,
+ bidi_info,
+ &parent_style,
+ )
.into_iter()
.map(|(mut segment, font)| {
let word_spacing = style_word_spacing.unwrap_or_else(|| {
@@ -407,7 +426,7 @@ impl TextRun {
};
segment.shape_text(
- &self.parent_style,
+ &parent_style,
formatting_context_text,
linebreaker,
&shaping_options,
@@ -430,8 +449,9 @@ impl TextRun {
font_context: &FontContext,
font_cache: &mut Vec<FontKeyAndMetrics>,
bidi_info: &BidiInfo,
+ parent_style: &Arc<ComputedValues>,
) -> Vec<(TextRunSegment, FontRef)> {
- let font_group = font_context.font_group(self.parent_style.clone_font());
+ let font_group = font_context.font_group(parent_style.clone_font());
let mut current: Option<(TextRunSegment, FontRef)> = None;
let mut results = Vec::new();
diff --git a/components/layout/flow/mod.rs b/components/layout/flow/mod.rs
index e23193f3904..99b84d088e5 100644
--- a/components/layout/flow/mod.rs
+++ b/components/layout/flow/mod.rs
@@ -9,9 +9,11 @@ use app_units::{Au, MAX_AU};
use inline::InlineFormattingContext;
use malloc_size_of_derive::MallocSizeOf;
use rayon::iter::{IndexedParallelIterator, IntoParallelRefIterator, ParallelIterator};
+use script::layout_dom::ServoLayoutNode;
use servo_arc::Arc;
use style::Zero;
use style::computed_values::clear::T as StyleClear;
+use style::context::SharedStyleContext;
use style::logical_geometry::Direction;
use style::properties::ComputedValues;
use style::servo::selector_parser::PseudoElement;
@@ -21,6 +23,7 @@ use style::values::specified::{Display, TextAlignKeyword};
use crate::cell::ArcRefCell;
use crate::context::LayoutContext;
+use crate::dom::NodeExt;
use crate::flow::float::{
Clear, ContainingBlockPositionInfo, FloatBox, FloatSide, PlacementAmongFloats,
SequentialLayoutState,
@@ -33,8 +36,8 @@ use crate::fragment_tree::{
BaseFragmentInfo, BoxFragment, CollapsedBlockMargins, CollapsedMargin, Fragment, FragmentFlags,
};
use crate::geom::{
- AuOrAuto, LogicalRect, LogicalSides, LogicalSides1D, LogicalVec2, PhysicalPoint, PhysicalRect,
- PhysicalSides, Size, Sizes, ToLogical, ToLogicalWithContainingBlock,
+ AuOrAuto, LazySize, LogicalRect, LogicalSides, LogicalSides1D, LogicalVec2, PhysicalPoint,
+ PhysicalRect, PhysicalSides, Size, Sizes, ToLogical, ToLogicalWithContainingBlock,
};
use crate::layout_box_base::{CacheableLayoutResult, LayoutBoxBase};
use crate::positioned::{AbsolutelyPositionedBox, PositioningContext, PositioningContextLength};
@@ -91,6 +94,36 @@ pub(crate) enum BlockLevelBox {
}
impl BlockLevelBox {
+ pub(crate) fn repair_style(
+ &mut self,
+ context: &SharedStyleContext,
+ node: &ServoLayoutNode,
+ new_style: &Arc<ComputedValues>,
+ ) {
+ self.with_base_mut(|base| {
+ base.repair_style(new_style);
+ });
+
+ match self {
+ BlockLevelBox::Independent(independent_formatting_context) => {
+ independent_formatting_context.repair_style(context, new_style)
+ },
+ BlockLevelBox::OutOfFlowAbsolutelyPositionedBox(positioned_box) => positioned_box
+ .borrow_mut()
+ .context
+ .repair_style(context, new_style),
+ BlockLevelBox::OutOfFlowFloatBox(float_box) => {
+ float_box.contents.repair_style(context, new_style)
+ },
+ BlockLevelBox::OutsideMarker(outside_marker) => {
+ outside_marker.repair_style(context, node, new_style)
+ },
+ BlockLevelBox::SameFormattingContextBlock { base, .. } => {
+ base.repair_style(new_style);
+ },
+ }
+ }
+
pub(crate) fn invalidate_cached_fragment(&self) {
self.with_base(LayoutBoxBase::invalidate_cached_fragment);
}
@@ -113,6 +146,20 @@ impl BlockLevelBox {
}
}
+ pub(crate) fn with_base_mut<T>(&mut self, callback: impl Fn(&mut LayoutBoxBase) -> T) -> T {
+ match self {
+ BlockLevelBox::Independent(independent_formatting_context) => {
+ callback(&mut independent_formatting_context.base)
+ },
+ BlockLevelBox::OutOfFlowAbsolutelyPositionedBox(positioned_box) => {
+ callback(&mut positioned_box.borrow_mut().context.base)
+ },
+ BlockLevelBox::OutOfFlowFloatBox(float_box) => callback(&mut float_box.contents.base),
+ BlockLevelBox::OutsideMarker(outside_marker) => callback(&mut outside_marker.base),
+ BlockLevelBox::SameFormattingContextBlock { base, .. } => callback(base),
+ }
+ }
+
fn contains_floats(&self) -> bool {
match self {
BlockLevelBox::SameFormattingContextBlock {
@@ -249,7 +296,6 @@ pub(crate) struct CollapsibleWithParentStartMargin(bool);
/// for a list that has `list-style-position: outside`.
#[derive(Debug, MallocSizeOf)]
pub(crate) struct OutsideMarker {
- #[conditional_malloc_size_of]
pub list_item_style: Arc<ComputedValues>,
pub base: LayoutBoxBase,
pub block_container: BlockContainer,
@@ -361,6 +407,16 @@ impl OutsideMarker {
None,
)))
}
+
+ fn repair_style(
+ &mut self,
+ context: &SharedStyleContext,
+ node: &ServoLayoutNode,
+ new_style: &Arc<ComputedValues>,
+ ) {
+ self.list_item_style = node.style(context);
+ self.base.repair_style(new_style);
+ }
}
impl BlockFormattingContext {
@@ -1161,6 +1217,15 @@ impl IndependentNonReplacedContents {
ignore_block_margins_for_stretch,
);
+ let lazy_block_size = LazySize::new(
+ &block_sizes,
+ Direction::Block,
+ Size::FitContent,
+ Au::zero,
+ available_block_size,
+ layout_style.is_table(),
+ );
+
let layout = self.layout(
layout_context,
positioning_context,
@@ -1168,19 +1233,13 @@ impl IndependentNonReplacedContents {
containing_block,
base,
false, /* depends_on_block_constraints */
+ &lazy_block_size,
);
let inline_size = layout
.content_inline_size_for_table
.unwrap_or(containing_block_for_children.size.inline);
- let block_size = block_sizes.resolve(
- Direction::Block,
- Size::FitContent,
- Au::zero,
- available_block_size,
- || layout.content_block_size.into(),
- layout_style.is_table(),
- );
+ let block_size = lazy_block_size.resolve(|| layout.content_block_size);
let ResolvedMargins {
margin,
@@ -1313,16 +1372,14 @@ impl IndependentNonReplacedContents {
)
};
- let compute_block_size = |layout: &CacheableLayoutResult| {
- content_box_sizes.block.resolve(
- Direction::Block,
- Size::FitContent,
- Au::zero,
- available_block_size,
- || layout.content_block_size.into(),
- is_table,
- )
- };
+ let lazy_block_size = LazySize::new(
+ &content_box_sizes.block,
+ Direction::Block,
+ Size::FitContent,
+ Au::zero,
+ available_block_size,
+ is_table,
+ );
// The final inline size can depend on the available space, which depends on where
// we are placing the box, since floats reduce the available space.
@@ -1351,10 +1408,11 @@ impl IndependentNonReplacedContents {
containing_block,
base,
false, /* depends_on_block_constraints */
+ &lazy_block_size,
);
content_size = LogicalVec2 {
- block: compute_block_size(&layout),
+ block: lazy_block_size.resolve(|| layout.content_block_size),
inline: layout.content_inline_size_for_table.unwrap_or(inline_size),
};
@@ -1416,6 +1474,7 @@ impl IndependentNonReplacedContents {
containing_block,
base,
false, /* depends_on_block_constraints */
+ &lazy_block_size,
);
let inline_size = if let Some(inline_size) = layout.content_inline_size_for_table {
@@ -1429,7 +1488,7 @@ impl IndependentNonReplacedContents {
proposed_inline_size
};
content_size = LogicalVec2 {
- block: compute_block_size(&layout),
+ block: lazy_block_size.resolve(|| layout.content_block_size),
inline: inline_size,
};
@@ -2363,6 +2422,15 @@ impl IndependentFormattingContext {
"Mixed horizontal and vertical writing modes are not supported yet"
);
+ let lazy_block_size = LazySize::new(
+ &content_box_sizes_and_pbm.content_box_sizes.block,
+ Direction::Block,
+ Size::FitContent,
+ Au::zero,
+ available_block_size,
+ is_table,
+ );
+
let independent_layout = non_replaced.layout(
layout_context,
child_positioning_context,
@@ -2370,18 +2438,12 @@ impl IndependentFormattingContext {
containing_block,
&self.base,
false, /* depends_on_block_constraints */
+ &lazy_block_size,
);
let inline_size = independent_layout
.content_inline_size_for_table
.unwrap_or(inline_size);
- let block_size = content_box_sizes_and_pbm.content_box_sizes.block.resolve(
- Direction::Block,
- Size::FitContent,
- Au::zero,
- available_block_size,
- || independent_layout.content_block_size.into(),
- is_table,
- );
+ let block_size = lazy_block_size.resolve(|| independent_layout.content_block_size);
let content_size = LogicalVec2 {
block: block_size,
diff --git a/components/layout/flow/root.rs b/components/layout/flow/root.rs
index ec85f3574dc..a37db54065d 100644
--- a/components/layout/flow/root.rs
+++ b/components/layout/flow/root.rs
@@ -59,7 +59,7 @@ impl BoxTree {
// > none, user agents must instead apply the overflow-* values of the first such child
// > element to the viewport. The element from which the value is propagated must then have a
// > used overflow value of visible.
- let root_style = root_element.style(context);
+ let root_style = root_element.style(context.shared_context());
let mut viewport_overflow_x = root_style.clone_overflow_x();
let mut viewport_overflow_y = root_style.clone_overflow_y();
@@ -76,7 +76,7 @@ impl BoxTree {
continue;
}
- let style = child.style(context);
+ let style = child.style(context.shared_context());
if !style.get_box().display.is_none() {
viewport_overflow_x = style.clone_overflow_x();
viewport_overflow_y = style.clone_overflow_y();
@@ -174,7 +174,7 @@ impl BoxTree {
let update_point =
match &*AtomicRef::filter_map(layout_data.self_box.borrow(), Option::as_ref)? {
- LayoutBox::DisplayContents => return None,
+ LayoutBox::DisplayContents(..) => return None,
LayoutBox::BlockLevel(block_level_box) => match &*block_level_box.borrow() {
BlockLevelBox::OutOfFlowAbsolutelyPositionedBox(_)
if box_style.position.is_absolutely_positioned() =>
@@ -293,7 +293,7 @@ fn construct_for_root_element(
context: &LayoutContext,
root_element: ServoLayoutNode<'_>,
) -> Vec<ArcRefCell<BlockLevelBox>> {
- let info = NodeAndStyleInfo::new(root_element, root_element.style(context));
+ let info = NodeAndStyleInfo::new(root_element, root_element.style(context.shared_context()));
let box_style = info.style.get_box();
let display_inside = match Display::from(box_style.display) {
diff --git a/components/layout/formatting_contexts.rs b/components/layout/formatting_contexts.rs
index 04a8c60f692..a489df2b663 100644
--- a/components/layout/formatting_contexts.rs
+++ b/components/layout/formatting_contexts.rs
@@ -6,6 +6,7 @@ use app_units::Au;
use malloc_size_of_derive::MallocSizeOf;
use script::layout_dom::ServoLayoutElement;
use servo_arc::Arc;
+use style::context::SharedStyleContext;
use style::properties::ComputedValues;
use style::selector_parser::PseudoElement;
@@ -14,6 +15,7 @@ use crate::dom_traversal::{Contents, NodeAndStyleInfo};
use crate::flexbox::FlexContainer;
use crate::flow::BlockFormattingContext;
use crate::fragment_tree::{BaseFragmentInfo, FragmentFlags};
+use crate::geom::LazySize;
use crate::layout_box_base::{
CacheableLayoutResult, CacheableLayoutResultAndInputs, LayoutBoxBase,
};
@@ -217,6 +219,20 @@ impl IndependentFormattingContext {
},
}
}
+
+ pub(crate) fn repair_style(
+ &mut self,
+ context: &SharedStyleContext,
+ new_style: &Arc<ComputedValues>,
+ ) {
+ self.base.repair_style(new_style);
+ match &mut self.contents {
+ IndependentFormattingContextContents::NonReplaced(content) => {
+ content.repair_style(context, new_style);
+ },
+ IndependentFormattingContextContents::Replaced(..) => {},
+ }
+ }
}
impl IndependentNonReplacedContents {
@@ -227,6 +243,7 @@ impl IndependentNonReplacedContents {
containing_block_for_children: &ContainingBlock,
containing_block: &ContainingBlock,
depends_on_block_constraints: bool,
+ lazy_block_size: &LazySize,
) -> CacheableLayoutResult {
match self {
IndependentNonReplacedContents::Flow(bfc) => bfc.layout(
@@ -240,6 +257,7 @@ impl IndependentNonReplacedContents {
positioning_context,
containing_block_for_children,
depends_on_block_constraints,
+ lazy_block_size,
),
IndependentNonReplacedContents::Grid(fc) => fc.layout(
layout_context,
@@ -266,6 +284,7 @@ impl IndependentNonReplacedContents {
level = "trace",
)
)]
+ #[allow(clippy::too_many_arguments)]
pub fn layout(
&self,
layout_context: &LayoutContext,
@@ -274,6 +293,7 @@ impl IndependentNonReplacedContents {
containing_block: &ContainingBlock,
base: &LayoutBoxBase,
depends_on_block_constraints: bool,
+ lazy_block_size: &LazySize,
) -> CacheableLayoutResult {
if let Some(cache) = base.cached_layout_result.borrow().as_ref() {
let cache = &**cache;
@@ -302,6 +322,7 @@ impl IndependentNonReplacedContents {
containing_block_for_children,
containing_block,
depends_on_block_constraints,
+ lazy_block_size,
);
*base.cached_layout_result.borrow_mut() = Some(Box::new(CacheableLayoutResultAndInputs {
@@ -334,6 +355,19 @@ impl IndependentNonReplacedContents {
pub(crate) fn is_table(&self) -> bool {
matches!(self, Self::Table(_))
}
+
+ fn repair_style(&mut self, context: &SharedStyleContext, new_style: &Arc<ComputedValues>) {
+ match self {
+ IndependentNonReplacedContents::Flow(..) => {},
+ IndependentNonReplacedContents::Flex(flex_container) => {
+ flex_container.repair_style(new_style)
+ },
+ IndependentNonReplacedContents::Grid(taffy_container) => {
+ taffy_container.repair_style(new_style)
+ },
+ IndependentNonReplacedContents::Table(table) => table.repair_style(context, new_style),
+ }
+ }
}
impl ComputeInlineContentSizes for IndependentNonReplacedContents {
diff --git a/components/layout/fragment_tree/box_fragment.rs b/components/layout/fragment_tree/box_fragment.rs
index 596556b296c..9b96b1c4fb4 100644
--- a/components/layout/fragment_tree/box_fragment.rs
+++ b/components/layout/fragment_tree/box_fragment.rs
@@ -17,7 +17,7 @@ use style::properties::ComputedValues;
use style::values::specified::box_::DisplayOutside;
use super::{BaseFragment, BaseFragmentInfo, CollapsedBlockMargins, Fragment, FragmentFlags};
-use crate::ArcRefCell;
+use crate::SharedStyle;
use crate::display_list::ToWebRender;
use crate::formatting_contexts::Baselines;
use crate::geom::{
@@ -40,15 +40,9 @@ pub(crate) enum BackgroundMode {
/// Draw the background normally, getting information from the Fragment style.
Normal,
}
-
-#[derive(Debug, MallocSizeOf)]
-pub(crate) struct BackgroundStyle(#[conditional_malloc_size_of] pub ServoArc<ComputedValues>);
-
-pub(crate) type SharedBackgroundStyle = ArcRefCell<BackgroundStyle>;
-
#[derive(MallocSizeOf)]
pub(crate) struct ExtraBackground {
- pub style: SharedBackgroundStyle,
+ pub style: SharedStyle,
pub rect: PhysicalRect<Au>,
}
@@ -64,7 +58,6 @@ pub(crate) enum SpecificLayoutInfo {
pub(crate) struct BoxFragment {
pub base: BaseFragment,
- #[conditional_malloc_size_of]
pub style: ServoArc<ComputedValues>,
pub children: Vec<Fragment>,
diff --git a/components/layout/fragment_tree/fragment.rs b/components/layout/fragment_tree/fragment.rs
index 1c5324fa1c4..1ebc7b3c989 100644
--- a/components/layout/fragment_tree/fragment.rs
+++ b/components/layout/fragment_tree/fragment.rs
@@ -22,6 +22,7 @@ use super::{
Tag,
};
use crate::cell::ArcRefCell;
+use crate::flow::inline::SharedInlineStyles;
use crate::geom::{LogicalSides, PhysicalPoint, PhysicalRect};
use crate::style_ext::ComputedValuesExt;
@@ -64,8 +65,7 @@ pub(crate) struct CollapsedMargin {
#[derive(MallocSizeOf)]
pub(crate) struct TextFragment {
pub base: BaseFragment,
- #[conditional_malloc_size_of]
- pub parent_style: ServoArc<ComputedValues>,
+ pub inline_styles: SharedInlineStyles,
pub rect: PhysicalRect<Au>,
pub font_metrics: FontMetrics,
pub font_key: FontInstanceKey,
@@ -78,14 +78,11 @@ pub(crate) struct TextFragment {
/// Extra space to add for each justification opportunity.
pub justification_adjustment: Au,
pub selection_range: Option<ServoRange<ByteIndex>>,
- #[conditional_malloc_size_of]
- pub selected_style: ServoArc<ComputedValues>,
}
#[derive(MallocSizeOf)]
pub(crate) struct ImageFragment {
pub base: BaseFragment,
- #[conditional_malloc_size_of]
pub style: ServoArc<ComputedValues>,
pub rect: PhysicalRect<Au>,
pub clip: PhysicalRect<Au>,
@@ -97,7 +94,6 @@ pub(crate) struct IFrameFragment {
pub base: BaseFragment,
pub pipeline_id: PipelineId,
pub rect: PhysicalRect<Au>,
- #[conditional_malloc_size_of]
pub style: ServoArc<ComputedValues>,
}
@@ -308,6 +304,25 @@ impl Fragment {
_ => None,
}
}
+
+ pub(crate) fn repair_style(&self, style: &ServoArc<ComputedValues>) {
+ match self {
+ Fragment::Box(box_fragment) | Fragment::Float(box_fragment) => {
+ box_fragment.borrow_mut().style = style.clone()
+ },
+ Fragment::Positioning(positioning_fragment) => {
+ positioning_fragment.borrow_mut().style = style.clone();
+ },
+ Fragment::AbsoluteOrFixedPositioned(positioned_fragment) => {
+ if let Some(ref fragment) = positioned_fragment.borrow().fragment {
+ fragment.repair_style(style);
+ }
+ },
+ Fragment::Text(..) => unreachable!("Should never try to repair style of TextFragment"),
+ Fragment::Image(image_fragment) => image_fragment.borrow_mut().style = style.clone(),
+ Fragment::IFrame(iframe_fragment) => iframe_fragment.borrow_mut().style = style.clone(),
+ }
+ }
}
impl TextFragment {
diff --git a/components/layout/fragment_tree/positioning_fragment.rs b/components/layout/fragment_tree/positioning_fragment.rs
index 0cf525a3479..e45a6137bff 100644
--- a/components/layout/fragment_tree/positioning_fragment.rs
+++ b/components/layout/fragment_tree/positioning_fragment.rs
@@ -24,9 +24,8 @@ pub(crate) struct PositioningFragment {
/// The scrollable overflow of this anonymous fragment's children.
pub scrollable_overflow: PhysicalRect<Au>,
- /// If this fragment was created with a style, the style of the fragment.
- #[conditional_malloc_size_of]
- pub style: Option<ServoArc<ComputedValues>>,
+ /// The style of the fragment.
+ pub style: ServoArc<ComputedValues>,
/// This [`PositioningFragment`]'s containing block rectangle in coordinates relative to
/// the initial containing block, but not taking into account any transforms.
@@ -34,8 +33,12 @@ pub(crate) struct PositioningFragment {
}
impl PositioningFragment {
- pub fn new_anonymous(rect: PhysicalRect<Au>, children: Vec<Fragment>) -> ArcRefCell<Self> {
- Self::new_with_base_fragment(BaseFragment::anonymous(), None, rect, children)
+ pub fn new_anonymous(
+ style: ServoArc<ComputedValues>,
+ rect: PhysicalRect<Au>,
+ children: Vec<Fragment>,
+ ) -> ArcRefCell<Self> {
+ Self::new_with_base_fragment(BaseFragment::anonymous(), style, rect, children)
}
pub fn new_empty(
@@ -43,12 +46,12 @@ impl PositioningFragment {
rect: PhysicalRect<Au>,
style: ServoArc<ComputedValues>,
) -> ArcRefCell<Self> {
- Self::new_with_base_fragment(base_fragment_info.into(), Some(style), rect, Vec::new())
+ Self::new_with_base_fragment(base_fragment_info.into(), style, rect, Vec::new())
}
fn new_with_base_fragment(
base: BaseFragment,
- style: Option<ServoArc<ComputedValues>>,
+ style: ServoArc<ComputedValues>,
rect: PhysicalRect<Au>,
children: Vec<Fragment>,
) -> ArcRefCell<Self> {
diff --git a/components/layout/geom.rs b/components/layout/geom.rs
index 6a09519b7ed..4065b785832 100644
--- a/components/layout/geom.rs
+++ b/components/layout/geom.rs
@@ -2,7 +2,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-use std::cell::LazyCell;
+use std::cell::{LazyCell, OnceCell};
use std::convert::From;
use std::fmt;
use std::ops::{Add, AddAssign, Neg, Sub, SubAssign};
@@ -1102,3 +1102,87 @@ impl Sizes {
)
}
}
+
+struct LazySizeData<'a> {
+ sizes: &'a Sizes,
+ axis: Direction,
+ automatic_size: Size<Au>,
+ get_automatic_minimum_size: fn() -> Au,
+ stretch_size: Option<Au>,
+ is_table: bool,
+}
+
+/// Represents a size that can't be fully resolved until the intrinsic size
+/// is known. This is useful in the block axis, since the intrinsic size
+/// depends on layout, but the other inputs are known beforehand.
+pub(crate) struct LazySize<'a> {
+ result: OnceCell<Au>,
+ data: Option<LazySizeData<'a>>,
+}
+
+impl<'a> LazySize<'a> {
+ pub(crate) fn new(
+ sizes: &'a Sizes,
+ axis: Direction,
+ automatic_size: Size<Au>,
+ get_automatic_minimum_size: fn() -> Au,
+ stretch_size: Option<Au>,
+ is_table: bool,
+ ) -> Self {
+ Self {
+ result: OnceCell::new(),
+ data: Some(LazySizeData {
+ sizes,
+ axis,
+ automatic_size,
+ get_automatic_minimum_size,
+ stretch_size,
+ is_table,
+ }),
+ }
+ }
+
+ /// Creates a [`LazySize`] that will resolve to the intrinsic size.
+ /// Should be equivalent to [`LazySize::new()`] with default parameters,
+ /// but avoiding the trouble of getting a reference to a [`Sizes::default()`]
+ /// which lives long enough.
+ ///
+ /// TODO: It's not clear what this should do if/when [`LazySize::resolve()`]
+ /// is changed to accept a [`ContentSizes`] as the intrinsic size.
+ pub(crate) fn intrinsic() -> Self {
+ Self {
+ result: OnceCell::new(),
+ data: None,
+ }
+ }
+
+ /// Resolves the [`LazySize`] into [`Au`], caching the result.
+ /// The argument is a callback that computes the intrinsic size lazily.
+ ///
+ /// TODO: The intrinsic size should probably be a [`ContentSizes`] instead of [`Au`].
+ pub(crate) fn resolve(&self, get_content_size: impl FnOnce() -> Au) -> Au {
+ *self.result.get_or_init(|| {
+ let Some(ref data) = self.data else {
+ return get_content_size();
+ };
+ data.sizes.resolve(
+ data.axis,
+ data.automatic_size,
+ data.get_automatic_minimum_size,
+ data.stretch_size,
+ || get_content_size().into(),
+ data.is_table,
+ )
+ })
+ }
+}
+
+impl From<Au> for LazySize<'_> {
+ /// Creates a [`LazySize`] that will resolve to the given [`Au`],
+ /// ignoring the intrinsic size.
+ fn from(value: Au) -> Self {
+ let result = OnceCell::new();
+ result.set(value).unwrap();
+ LazySize { result, data: None }
+ }
+}
diff --git a/components/layout/layout_box_base.rs b/components/layout/layout_box_base.rs
index 71fbfdeced1..161aee0f9bb 100644
--- a/components/layout/layout_box_base.rs
+++ b/components/layout/layout_box_base.rs
@@ -27,7 +27,6 @@ use crate::{ConstraintSpace, ContainingBlockSize};
#[derive(MallocSizeOf)]
pub(crate) struct LayoutBoxBase {
pub base_fragment_info: BaseFragmentInfo,
- #[conditional_malloc_size_of]
pub style: Arc<ComputedValues>,
pub cached_inline_content_size:
AtomicRefCell<Option<Box<(SizeConstraint, InlineContentSizesResult)>>>,
@@ -90,6 +89,13 @@ impl LayoutBoxBase {
pub(crate) fn clear_fragments(&self) {
self.fragments.borrow_mut().clear();
}
+
+ pub(crate) fn repair_style(&mut self, new_style: &Arc<ComputedValues>) {
+ self.style = new_style.clone();
+ for fragment in self.fragments.borrow_mut().iter_mut() {
+ fragment.repair_style(new_style);
+ }
+ }
}
impl Debug for LayoutBoxBase {
diff --git a/components/layout/layout_impl.rs b/components/layout/layout_impl.rs
index cdf76d3fed0..fcf658036b2 100644
--- a/components/layout/layout_impl.rs
+++ b/components/layout/layout_impl.rs
@@ -56,7 +56,7 @@ use style::media_queries::{Device, MediaList, MediaType};
use style::properties::style_structs::Font;
use style::properties::{ComputedValues, PropertyId};
use style::queries::values::PrefersColorScheme;
-use style::selector_parser::{PseudoElement, SnapshotMap};
+use style::selector_parser::{PseudoElement, RestyleDamage, SnapshotMap};
use style::servo::media_queries::FontMetricsProvider;
use style::shared_lock::{SharedRwLock, SharedRwLockReadGuard, StylesheetGuards};
use style::stylesheets::{
@@ -83,7 +83,7 @@ use crate::query::{
process_content_boxes_request, process_node_scroll_area_request, process_offset_parent_query,
process_resolved_font_style_query, process_resolved_style_request, process_text_index_request,
};
-use crate::traversal::RecalcStyle;
+use crate::traversal::{RecalcStyle, compute_damage_and_repair_style};
use crate::{BoxTree, FragmentTree};
// This mutex is necessary due to syncronisation issues between two different types of thread-local storage
@@ -765,6 +765,12 @@ impl LayoutThread {
driver::traverse_dom(&recalc_style_traversal, token, rayon_pool).as_node();
let root_node = root_element.as_node();
+ let damage = compute_damage_and_repair_style(layout_context.shared_context(), root_node);
+ if damage == RestyleDamage::REPAINT {
+ layout_context.style_context.stylist.rule_tree().maybe_gc();
+ return;
+ }
+
let mut box_tree = self.box_tree.borrow_mut();
let box_tree = &mut *box_tree;
let mut build_box_tree = || {
diff --git a/components/layout/lib.rs b/components/layout/lib.rs
index af7d432c4d8..cd992387277 100644
--- a/components/layout/lib.rs
+++ b/components/layout/lib.rs
@@ -38,6 +38,7 @@ pub use flow::BoxTree;
pub use fragment_tree::FragmentTree;
pub use layout_impl::LayoutFactoryImpl;
use malloc_size_of_derive::MallocSizeOf;
+use servo_arc::Arc as ServoArc;
use style::logical_geometry::WritingMode;
use style::properties::ComputedValues;
use style::values::computed::TextDecorationLine;
@@ -45,6 +46,16 @@ use style::values::computed::TextDecorationLine;
use crate::geom::{LogicalVec2, SizeConstraint};
use crate::style_ext::AspectRatio;
+/// At times, a style is "owned" by more than one layout object. For example, text
+/// fragments need a handle on their parent inline box's style. In order to make
+/// incremental layout easier to implement, another layer of shared ownership is added via
+/// [`SharedStyle`]. This allows updating the style in originating layout object and
+/// having all "depdendent" objects update automatically.
+///
+/// Note that this is not a cost-free data structure, so should only be
+/// used when necessary.
+pub(crate) type SharedStyle = ArcRefCell<ServoArc<ComputedValues>>;
+
/// Represents the set of constraints that we use when computing the min-content
/// and max-content inline sizes of an element.
pub(crate) struct ConstraintSpace {
diff --git a/components/layout/positioned.rs b/components/layout/positioned.rs
index bb5386dc696..6280864d533 100644
--- a/components/layout/positioned.rs
+++ b/components/layout/positioned.rs
@@ -24,12 +24,11 @@ use crate::fragment_tree::{
BoxFragment, Fragment, FragmentFlags, HoistedSharedFragment, SpecificLayoutInfo,
};
use crate::geom::{
- AuOrAuto, LengthPercentageOrAuto, LogicalRect, LogicalSides, LogicalSides1D, LogicalVec2,
- PhysicalPoint, PhysicalRect, PhysicalSides, PhysicalSize, PhysicalVec, Size, Sizes, ToLogical,
- ToLogicalWithContainingBlock,
+ AuOrAuto, LazySize, LengthPercentageOrAuto, LogicalRect, LogicalSides, LogicalSides1D,
+ LogicalVec2, PhysicalPoint, PhysicalRect, PhysicalSides, PhysicalSize, PhysicalVec, Size,
+ Sizes, ToLogical, ToLogicalWithContainingBlock,
};
use crate::layout_box_base::LayoutBoxBase;
-use crate::sizing::ContentSizes;
use crate::style_ext::{Clamp, ComputedValuesExt, ContentBoxSizesAndPBM, DisplayInside};
use crate::{
ConstraintSpace, ContainingBlock, ContainingBlockSize, DefiniteContainingBlock,
@@ -473,7 +472,7 @@ impl HoistedAbsolutelyPositionedBox {
false => shared_fragment.resolved_alignment.inline,
};
- let mut inline_axis_solver = AbsoluteAxisSolver {
+ let inline_axis_solver = AbsoluteAxisSolver {
axis: Direction::Inline,
containing_size: cbis,
padding_border_sum: pbm.padding_border_sums.inline,
@@ -496,7 +495,7 @@ impl HoistedAbsolutelyPositionedBox {
true => style.clone_align_self().0.0,
false => shared_fragment.resolved_alignment.block,
};
- let mut block_axis_solver = AbsoluteAxisSolver {
+ let block_axis_solver = AbsoluteAxisSolver {
axis: Direction::Block,
containing_size: cbbs,
padding_border_sum: pbm.padding_border_sums.block,
@@ -511,52 +510,6 @@ impl HoistedAbsolutelyPositionedBox {
is_table,
};
- if let IndependentFormattingContextContents::Replaced(replaced) = &context.contents {
- // https://drafts.csswg.org/css2/visudet.html#abs-replaced-width
- // https://drafts.csswg.org/css2/visudet.html#abs-replaced-height
- let inset_sums = LogicalVec2 {
- inline: inline_axis_solver.inset_sum(),
- block: block_axis_solver.inset_sum(),
- };
- let automatic_size = |alignment: AlignFlags, offsets: &LogicalSides1D<_>| {
- if alignment.value() == AlignFlags::STRETCH && !offsets.either_auto() {
- Size::Stretch
- } else {
- Size::FitContent
- }
- };
- let used_size = replaced.used_size_as_if_inline_element_from_content_box_sizes(
- containing_block,
- &style,
- context.preferred_aspect_ratio(&pbm.padding_border_sums),
- LogicalVec2 {
- inline: &inline_axis_solver.computed_sizes,
- block: &block_axis_solver.computed_sizes,
- },
- LogicalVec2 {
- inline: automatic_size(inline_alignment, &inline_axis_solver.box_offsets),
- block: automatic_size(block_alignment, &block_axis_solver.box_offsets),
- },
- pbm.padding_border_sums + pbm.margin.auto_is(Au::zero).sum() + inset_sums,
- );
- inline_axis_solver.override_size(used_size.inline);
- block_axis_solver.override_size(used_size.block);
- }
-
- // The block axis can depend on layout results, so we only solve it tentatively,
- // we may have to resolve it properly later on.
- let mut block_axis = block_axis_solver.solve_tentatively();
-
- // The inline axis can be fully resolved, computing intrinsic sizes using the
- // tentative block size.
- let mut inline_axis = inline_axis_solver.solve(Some(|| {
- let ratio = context.preferred_aspect_ratio(&pbm.padding_border_sums);
- let constraint_space = ConstraintSpace::new(block_axis.size, style.writing_mode, ratio);
- context
- .inline_content_sizes(layout_context, &constraint_space)
- .sizes
- }));
-
let mut positioning_context = PositioningContext::default();
let mut new_fragment = {
let content_size: LogicalVec2<Au>;
@@ -566,10 +519,34 @@ impl HoistedAbsolutelyPositionedBox {
IndependentFormattingContextContents::Replaced(replaced) => {
// https://drafts.csswg.org/css2/visudet.html#abs-replaced-width
// https://drafts.csswg.org/css2/visudet.html#abs-replaced-height
- content_size = LogicalVec2 {
- inline: inline_axis.size.to_definite().unwrap(),
- block: block_axis.size.to_definite().unwrap(),
+ let inset_sums = LogicalVec2 {
+ inline: inline_axis_solver.inset_sum(),
+ block: block_axis_solver.inset_sum(),
+ };
+ let automatic_size = |alignment: AlignFlags, offsets: &LogicalSides1D<_>| {
+ if alignment.value() == AlignFlags::STRETCH && !offsets.either_auto() {
+ Size::Stretch
+ } else {
+ Size::FitContent
+ }
};
+ content_size = replaced.used_size_as_if_inline_element_from_content_box_sizes(
+ containing_block,
+ &style,
+ context.preferred_aspect_ratio(&pbm.padding_border_sums),
+ LogicalVec2 {
+ inline: &inline_axis_solver.computed_sizes,
+ block: &block_axis_solver.computed_sizes,
+ },
+ LogicalVec2 {
+ inline: automatic_size(
+ inline_alignment,
+ &inline_axis_solver.box_offsets,
+ ),
+ block: automatic_size(block_alignment, &block_axis_solver.box_offsets),
+ },
+ pbm.padding_border_sums + pbm.margin.auto_is(Au::zero).sum() + inset_sums,
+ );
fragments = replaced.make_fragments(
layout_context,
&style,
@@ -579,11 +556,40 @@ impl HoistedAbsolutelyPositionedBox {
IndependentFormattingContextContents::NonReplaced(non_replaced) => {
// https://drafts.csswg.org/css2/visudet.html#abs-non-replaced-width
// https://drafts.csswg.org/css2/visudet.html#abs-non-replaced-height
- let inline_size = inline_axis.size.to_definite().unwrap();
+
+ // The block size can depend on layout results, so we only solve it extrinsically,
+ // we may have to resolve it properly later on.
+ let block_automatic_size = block_axis_solver.automatic_size();
+ let block_stretch_size = Some(block_axis_solver.stretch_size());
+ let extrinsic_block_size = block_axis_solver.computed_sizes.resolve_extrinsic(
+ block_automatic_size,
+ Au::zero(),
+ block_stretch_size,
+ );
+
+ // The inline axis can be fully resolved, computing intrinsic sizes using the
+ // extrinsic block size.
+ let get_inline_content_size = || {
+ let ratio = context.preferred_aspect_ratio(&pbm.padding_border_sums);
+ let constraint_space =
+ ConstraintSpace::new(extrinsic_block_size, style.writing_mode, ratio);
+ context
+ .inline_content_sizes(layout_context, &constraint_space)
+ .sizes
+ };
+ let inline_size = inline_axis_solver.computed_sizes.resolve(
+ Direction::Inline,
+ inline_axis_solver.automatic_size(),
+ Au::zero,
+ Some(inline_axis_solver.stretch_size()),
+ get_inline_content_size,
+ is_table,
+ );
+
let containing_block_for_children = ContainingBlock {
size: ContainingBlockSize {
inline: inline_size,
- block: block_axis.size,
+ block: extrinsic_block_size,
},
style: &style,
};
@@ -594,6 +600,14 @@ impl HoistedAbsolutelyPositionedBox {
"Mixed horizontal and vertical writing modes are not supported yet"
);
+ let lazy_block_size = LazySize::new(
+ &block_axis_solver.computed_sizes,
+ Direction::Block,
+ block_automatic_size,
+ Au::zero,
+ block_stretch_size,
+ is_table,
+ );
let independent_layout = non_replaced.layout(
layout_context,
&mut positioning_context,
@@ -601,24 +615,17 @@ impl HoistedAbsolutelyPositionedBox {
containing_block,
&context.base,
false, /* depends_on_block_constraints */
+ &lazy_block_size,
);
- let inline_size = if let Some(inline_size) =
- independent_layout.content_inline_size_for_table
- {
- // Tables can become narrower than predicted due to collapsed columns,
- // so we need to solve again to update margins.
- inline_axis_solver.override_size(inline_size);
- inline_axis = inline_axis_solver.solve_tentatively();
- inline_size
- } else {
- inline_size
- };
+ // Tables can become narrower than predicted due to collapsed columns
+ let inline_size = independent_layout
+ .content_inline_size_for_table
+ .unwrap_or(inline_size);
// Now we can properly solve the block size.
- block_axis = block_axis_solver
- .solve(Some(|| independent_layout.content_block_size.into()));
- let block_size = block_axis.size.to_definite().unwrap();
+ let block_size =
+ lazy_block_size.resolve(|| independent_layout.content_block_size);
content_size = LogicalVec2 {
inline: inline_size,
@@ -629,11 +636,13 @@ impl HoistedAbsolutelyPositionedBox {
},
};
+ let inline_margins = inline_axis_solver.solve_margins(content_size.inline);
+ let block_margins = block_axis_solver.solve_margins(content_size.block);
let margin = LogicalSides {
- inline_start: inline_axis.margin_start,
- inline_end: inline_axis.margin_end,
- block_start: block_axis.margin_start,
- block_end: block_axis.margin_end,
+ inline_start: inline_margins.start,
+ inline_end: inline_margins.end,
+ block_start: block_margins.start,
+ block_end: block_margins.end,
};
let pb = pbm.padding + pbm.border;
@@ -715,12 +724,6 @@ impl LogicalRect<Au> {
}
}
-struct AxisResult {
- size: SizeConstraint,
- margin_start: Au,
- margin_end: Au,
-}
-
struct AbsoluteAxisSolver<'a> {
axis: Direction,
containing_size: Au,
@@ -763,101 +766,56 @@ impl AbsoluteAxisSolver<'_> {
}
}
- /// This unifies some of the parts in common in:
- ///
- /// * <https://drafts.csswg.org/css2/visudet.html#abs-non-replaced-width>
- /// * <https://drafts.csswg.org/css2/visudet.html#abs-non-replaced-height>
- ///
- /// … and:
- ///
- /// * <https://drafts.csswg.org/css2/visudet.html#abs-replaced-width>
- /// * <https://drafts.csswg.org/css2/visudet.html#abs-replaced-height>
- ///
- /// In the replaced case, `size` is never `Auto`.
- fn solve(&self, get_content_size: Option<impl FnOnce() -> ContentSizes>) -> AxisResult {
- let solve_size = |initial_behavior, stretch_size: Au| -> SizeConstraint {
- let stretch_size = stretch_size.max(Au::zero());
- if let Some(get_content_size) = get_content_size {
- SizeConstraint::Definite(self.computed_sizes.resolve(
- self.axis,
- initial_behavior,
- Au::zero,
- Some(stretch_size),
- get_content_size,
- self.is_table,
- ))
- } else {
- self.computed_sizes.resolve_extrinsic(
- initial_behavior,
- Au::zero(),
- Some(stretch_size),
- )
- }
- };
- if self.box_offsets.either_auto() {
- let margin_start = self.computed_margin_start.auto_is(Au::zero);
- let margin_end = self.computed_margin_end.auto_is(Au::zero);
- let stretch_size = self.containing_size -
- self.inset_sum() -
- self.padding_border_sum -
- margin_start -
- margin_end;
- let size = solve_size(Size::FitContent, stretch_size);
- AxisResult {
- size,
- margin_start,
- margin_end,
- }
- } else {
- let mut free_space = self.containing_size - self.inset_sum() - self.padding_border_sum;
- let stretch_size = free_space -
- self.computed_margin_start.auto_is(Au::zero) -
- self.computed_margin_end.auto_is(Au::zero);
- let initial_behavior = match self.alignment.value() {
- AlignFlags::NORMAL | AlignFlags::AUTO if !self.is_table => Size::Stretch,
- AlignFlags::STRETCH => Size::Stretch,
- _ => Size::FitContent,
- };
- let size = solve_size(initial_behavior, stretch_size);
- if let Some(used_size) = size.to_definite() {
- free_space -= used_size;
- } else {
- free_space = Au::zero();
- }
- let (margin_start, margin_end) =
- match (self.computed_margin_start, self.computed_margin_end) {
- (AuOrAuto::Auto, AuOrAuto::Auto) => {
- if self.avoid_negative_margin_start && free_space < Au::zero() {
- (Au::zero(), free_space)
- } else {
- let margin_start = free_space / 2;
- (margin_start, free_space - margin_start)
- }
- },
- (AuOrAuto::Auto, AuOrAuto::LengthPercentage(end)) => (free_space - end, end),
- (AuOrAuto::LengthPercentage(start), AuOrAuto::Auto) => {
- (start, free_space - start)
- },
- (AuOrAuto::LengthPercentage(start), AuOrAuto::LengthPercentage(end)) => {
- (start, end)
- },
- };
- AxisResult {
- size,
- margin_start,
- margin_end,
- }
+ #[inline]
+ fn automatic_size(&self) -> Size<Au> {
+ match self.alignment.value() {
+ _ if self.box_offsets.either_auto() => Size::FitContent,
+ AlignFlags::NORMAL | AlignFlags::AUTO if !self.is_table => Size::Stretch,
+ AlignFlags::STRETCH => Size::Stretch,
+ _ => Size::FitContent,
}
}
- fn solve_tentatively(&mut self) -> AxisResult {
- self.solve(None::<fn() -> ContentSizes>)
+ #[inline]
+ fn stretch_size(&self) -> Au {
+ Au::zero().max(
+ self.containing_size -
+ self.inset_sum() -
+ self.padding_border_sum -
+ self.computed_margin_start.auto_is(Au::zero) -
+ self.computed_margin_end.auto_is(Au::zero),
+ )
}
- fn override_size(&mut self, size: Au) {
- self.computed_sizes.preferred = Size::Numeric(size);
- self.computed_sizes.min = Size::default();
- self.computed_sizes.max = Size::default();
+ fn solve_margins(&self, size: Au) -> LogicalSides1D<Au> {
+ if self.box_offsets.either_auto() {
+ LogicalSides1D::new(
+ self.computed_margin_start.auto_is(Au::zero),
+ self.computed_margin_end.auto_is(Au::zero),
+ )
+ } else {
+ let free_space =
+ self.containing_size - self.inset_sum() - self.padding_border_sum - size;
+ match (self.computed_margin_start, self.computed_margin_end) {
+ (AuOrAuto::Auto, AuOrAuto::Auto) => {
+ if self.avoid_negative_margin_start && free_space < Au::zero() {
+ LogicalSides1D::new(Au::zero(), free_space)
+ } else {
+ let margin_start = free_space / 2;
+ LogicalSides1D::new(margin_start, free_space - margin_start)
+ }
+ },
+ (AuOrAuto::Auto, AuOrAuto::LengthPercentage(end)) => {
+ LogicalSides1D::new(free_space - end, end)
+ },
+ (AuOrAuto::LengthPercentage(start), AuOrAuto::Auto) => {
+ LogicalSides1D::new(start, free_space - start)
+ },
+ (AuOrAuto::LengthPercentage(start), AuOrAuto::LengthPercentage(end)) => {
+ LogicalSides1D::new(start, end)
+ },
+ }
+ }
}
fn origin_for_margin_box(
diff --git a/components/layout/table/construct.rs b/components/layout/table/construct.rs
index 133904db7ae..0c238073df2 100644
--- a/components/layout/table/construct.rs
+++ b/components/layout/table/construct.rs
@@ -19,7 +19,6 @@ use super::{
Table, TableCaption, TableLevelBox, TableSlot, TableSlotCell, TableSlotCoordinates,
TableSlotOffset, TableTrack, TableTrackGroup, TableTrackGroupType,
};
-use crate::PropagatedBoxTreeData;
use crate::cell::ArcRefCell;
use crate::context::LayoutContext;
use crate::dom::{BoxSlot, LayoutBox};
@@ -29,9 +28,10 @@ use crate::formatting_contexts::{
IndependentFormattingContext, IndependentFormattingContextContents,
IndependentNonReplacedContents,
};
-use crate::fragment_tree::{BackgroundStyle, BaseFragmentInfo, SharedBackgroundStyle};
+use crate::fragment_tree::BaseFragmentInfo;
use crate::layout_box_base::LayoutBoxBase;
use crate::style_ext::{DisplayGeneratingBox, DisplayLayoutInternal};
+use crate::{PropagatedBoxTreeData, SharedStyle};
/// A reference to a slot and its coordinates in the table
#[derive(Debug)]
@@ -725,7 +725,7 @@ impl<'style, 'dom> TableBuilderTraversal<'style, 'dom> {
base: LayoutBoxBase::new((&anonymous_info).into(), style.clone()),
group_index: self.current_row_group_index,
is_anonymous: true,
- shared_background_style: SharedBackgroundStyle::new(BackgroundStyle(style)),
+ shared_background_style: SharedStyle::new(style),
}));
}
@@ -767,9 +767,7 @@ impl<'dom> TraversalHandler<'dom> for TableBuilderTraversal<'_, 'dom> {
base: LayoutBoxBase::new(info.into(), info.style.clone()),
group_type: internal.into(),
track_range: next_row_index..next_row_index,
- shared_background_style: SharedBackgroundStyle::new(BackgroundStyle(
- info.style.clone(),
- )),
+ shared_background_style: SharedStyle::new(info.style.clone()),
});
self.builder.table.row_groups.push(row_group.clone());
@@ -812,9 +810,7 @@ impl<'dom> TraversalHandler<'dom> for TableBuilderTraversal<'_, 'dom> {
base: LayoutBoxBase::new(info.into(), info.style.clone()),
group_index: self.current_row_group_index,
is_anonymous: false,
- shared_background_style: SharedBackgroundStyle::new(BackgroundStyle(
- info.style.clone(),
- )),
+ shared_background_style: SharedStyle::new(info.style.clone()),
});
self.push_table_row(row.clone());
box_slot.set(LayoutBox::TableLevelBox(TableLevelBox::Track(row)));
@@ -860,9 +856,7 @@ impl<'dom> TraversalHandler<'dom> for TableBuilderTraversal<'_, 'dom> {
base: LayoutBoxBase::new(info.into(), info.style.clone()),
group_type: internal.into(),
track_range: first_column..self.builder.table.columns.len(),
- shared_background_style: SharedBackgroundStyle::new(BackgroundStyle(
- info.style.clone(),
- )),
+ shared_background_style: SharedStyle::new(info.style.clone()),
});
self.builder.table.column_groups.push(column_group.clone());
box_slot.set(LayoutBox::TableLevelBox(TableLevelBox::TrackGroup(
@@ -1145,9 +1139,7 @@ fn add_column(
base: LayoutBoxBase::new(column_info.into(), column_info.style.clone()),
group_index,
is_anonymous,
- shared_background_style: SharedBackgroundStyle::new(BackgroundStyle(
- column_info.style.clone(),
- )),
+ shared_background_style: SharedStyle::new(column_info.style.clone()),
});
collection.extend(repeat(column.clone()).take(span as usize));
column
diff --git a/components/layout/table/layout.rs b/components/layout/table/layout.rs
index 00dac210625..5b7e79d7fb0 100644
--- a/components/layout/table/layout.rs
+++ b/components/layout/table/layout.rs
@@ -2867,6 +2867,7 @@ impl TableSlotCell {
block: vertical_align_offset,
};
let vertical_align_fragment = PositioningFragment::new_anonymous(
+ self.base.style.clone(),
vertical_align_fragment_rect.as_physical(None),
layout.layout.fragments,
);
diff --git a/components/layout/table/mod.rs b/components/layout/table/mod.rs
index 8e2783e2919..72b67863e7d 100644
--- a/components/layout/table/mod.rs
+++ b/components/layout/table/mod.rs
@@ -76,16 +76,20 @@ pub(crate) use construct::AnonymousTableContent;
pub use construct::TableBuilder;
use euclid::{Point2D, Size2D, UnknownUnit, Vector2D};
use malloc_size_of_derive::MallocSizeOf;
+use script::layout_dom::ServoLayoutElement;
use servo_arc::Arc;
+use style::context::SharedStyleContext;
use style::properties::ComputedValues;
use style::properties::style_structs::Font;
+use style::selector_parser::PseudoElement;
use style_traits::dom::OpaqueNode;
use super::flow::BlockFormattingContext;
+use crate::SharedStyle;
use crate::cell::ArcRefCell;
use crate::flow::BlockContainer;
use crate::formatting_contexts::IndependentFormattingContext;
-use crate::fragment_tree::{BaseFragmentInfo, Fragment, SharedBackgroundStyle};
+use crate::fragment_tree::{BaseFragmentInfo, Fragment};
use crate::geom::PhysicalVec;
use crate::layout_box_base::LayoutBoxBase;
use crate::style_ext::BorderStyleColor;
@@ -98,12 +102,10 @@ pub struct Table {
/// The style of this table. These are the properties that apply to the "wrapper" ie the element
/// that contains both the grid and the captions. Not all properties are actually used on the
/// wrapper though, such as background and borders, which apply to the grid.
- #[conditional_malloc_size_of]
style: Arc<ComputedValues>,
/// The style of this table's grid. This is an anonymous style based on the table's style, but
/// eliminating all the properties handled by the "wrapper."
- #[conditional_malloc_size_of]
grid_style: Arc<ComputedValues>,
/// The [`BaseFragmentInfo`] for this table's grid. This is necessary so that when the
@@ -192,6 +194,19 @@ impl Table {
),
}
}
+
+ pub(crate) fn repair_style(
+ &mut self,
+ context: &SharedStyleContext,
+ new_style: &Arc<ComputedValues>,
+ ) {
+ self.style = new_style.clone();
+ self.grid_style = context.stylist.style_for_anonymous::<ServoLayoutElement>(
+ &context.guards,
+ &PseudoElement::ServoTableGrid,
+ new_style,
+ );
+ }
}
type TableSlotCoordinates = Point2D<usize, UnknownUnit>;
@@ -233,6 +248,10 @@ impl TableSlotCell {
pub fn node_id(&self) -> usize {
self.base.base_fragment_info.tag.map_or(0, |tag| tag.node.0)
}
+
+ fn repair_style(&mut self, new_style: &Arc<ComputedValues>) {
+ self.base.repair_style(new_style);
+ }
}
/// A single table slot. It may be an actual cell, or a reference
@@ -292,7 +311,14 @@ pub struct TableTrack {
/// A shared container for this track's style, used to share the style for the purposes
/// of drawing backgrounds in individual cells. This allows updating the style in a
/// single place and having it affect all cell `Fragment`s.
- shared_background_style: SharedBackgroundStyle,
+ shared_background_style: SharedStyle,
+}
+
+impl TableTrack {
+ fn repair_style(&mut self, new_style: &Arc<ComputedValues>) {
+ self.base.repair_style(new_style);
+ self.shared_background_style = SharedStyle::new(new_style.clone());
+ }
}
#[derive(Debug, MallocSizeOf, PartialEq)]
@@ -317,13 +343,18 @@ pub struct TableTrackGroup {
/// A shared container for this track's style, used to share the style for the purposes
/// of drawing backgrounds in individual cells. This allows updating the style in a
/// single place and having it affect all cell `Fragment`s.
- shared_background_style: SharedBackgroundStyle,
+ shared_background_style: SharedStyle,
}
impl TableTrackGroup {
pub(super) fn is_empty(&self) -> bool {
self.track_range.is_empty()
}
+
+ fn repair_style(&mut self, new_style: &Arc<ComputedValues>) {
+ self.base.repair_style(new_style);
+ self.shared_background_style = SharedStyle::new(new_style.clone());
+ }
}
#[derive(Debug, MallocSizeOf)]
@@ -390,4 +421,22 @@ impl TableLevelBox {
TableLevelBox::Track(track) => track.borrow().base.fragments(),
}
}
+
+ pub(crate) fn repair_style(
+ &self,
+ context: &SharedStyleContext<'_>,
+ new_style: &Arc<ComputedValues>,
+ ) {
+ match self {
+ TableLevelBox::Caption(caption) => caption
+ .borrow_mut()
+ .context
+ .repair_style(context, new_style),
+ TableLevelBox::Cell(cell) => cell.borrow_mut().repair_style(new_style),
+ TableLevelBox::TrackGroup(track_group) => {
+ track_group.borrow_mut().repair_style(new_style);
+ },
+ TableLevelBox::Track(track) => track.borrow_mut().repair_style(new_style),
+ }
+ }
}
diff --git a/components/layout/taffy/layout.rs b/components/layout/taffy/layout.rs
index a5838c1bd65..61c4a0508e9 100644
--- a/components/layout/taffy/layout.rs
+++ b/components/layout/taffy/layout.rs
@@ -23,8 +23,8 @@ use crate::fragment_tree::{
BoxFragment, CollapsedBlockMargins, Fragment, FragmentFlags, SpecificLayoutInfo,
};
use crate::geom::{
- LogicalVec2, PhysicalPoint, PhysicalRect, PhysicalSides, PhysicalSize, Size, SizeConstraint,
- Sizes,
+ LazySize, LogicalVec2, PhysicalPoint, PhysicalRect, PhysicalSides, PhysicalSize, Size,
+ SizeConstraint, Sizes,
};
use crate::layout_box_base::CacheableLayoutResult;
use crate::positioned::{AbsolutelyPositionedBox, PositioningContext, PositioningContextLength};
@@ -251,6 +251,12 @@ impl taffy::LayoutPartialTree for TaffyContainerContext<'_> {
style,
};
+ let lazy_block_size = match content_box_known_dimensions.height {
+ // FIXME: use the correct min/max sizes.
+ None => LazySize::intrinsic(),
+ Some(height) => Au::from_f32_px(height).into(),
+ };
+
child.positioning_context = PositioningContext::default();
let layout = non_replaced.layout_without_caching(
self.layout_context,
@@ -258,13 +264,16 @@ impl taffy::LayoutPartialTree for TaffyContainerContext<'_> {
&content_box_size_override,
containing_block,
false, /* depends_on_block_constraints */
+ &lazy_block_size,
);
child.child_fragments = layout.fragments;
self.child_specific_layout_infos[usize::from(node_id)] =
layout.specific_layout_info;
- let block_size = layout.content_block_size.to_f32_px();
+ let block_size = lazy_block_size
+ .resolve(|| layout.content_block_size)
+ .to_f32_px();
let computed_size = taffy::Size {
width: inline_size + pb_sum.inline,
diff --git a/components/layout/taffy/mod.rs b/components/layout/taffy/mod.rs
index 50873fc3e66..ba80824fa99 100644
--- a/components/layout/taffy/mod.rs
+++ b/components/layout/taffy/mod.rs
@@ -8,6 +8,7 @@ use std::fmt;
use app_units::Au;
use malloc_size_of_derive::MallocSizeOf;
use servo_arc::Arc;
+use style::context::SharedStyleContext;
use style::properties::ComputedValues;
use stylo_taffy::TaffyStyloStyle;
@@ -24,7 +25,6 @@ use crate::positioned::{AbsolutelyPositionedBox, PositioningContext};
#[derive(Debug, MallocSizeOf)]
pub(crate) struct TaffyContainer {
children: Vec<ArcRefCell<TaffyItemBox>>,
- #[conditional_malloc_size_of]
style: Arc<ComputedValues>,
}
@@ -69,6 +69,10 @@ impl TaffyContainer {
style: info.style.clone(),
}
}
+
+ pub(crate) fn repair_style(&mut self, new_style: &Arc<ComputedValues>) {
+ self.style = new_style.clone();
+ }
}
#[derive(MallocSizeOf)]
@@ -76,7 +80,6 @@ pub(crate) struct TaffyItemBox {
pub(crate) taffy_layout: taffy::Layout,
pub(crate) child_fragments: Vec<Fragment>,
pub(crate) positioning_context: PositioningContext,
- #[conditional_malloc_size_of]
pub(crate) style: Arc<ComputedValues>,
pub(crate) taffy_level_box: TaffyItemBoxInner,
}
@@ -145,6 +148,23 @@ impl TaffyItemBox {
},
}
}
+
+ pub(crate) fn repair_style(
+ &mut self,
+ context: &SharedStyleContext,
+ new_style: &Arc<ComputedValues>,
+ ) {
+ self.style = new_style.clone();
+ match &mut self.taffy_level_box {
+ TaffyItemBoxInner::InFlowBox(independent_formatting_context) => {
+ independent_formatting_context.repair_style(context, new_style)
+ },
+ TaffyItemBoxInner::OutOfFlowAbsolutelyPositionedBox(positioned_box) => positioned_box
+ .borrow_mut()
+ .context
+ .repair_style(context, new_style),
+ }
+ }
}
/// Details from Taffy grid layout that will be stored
diff --git a/components/layout/traversal.rs b/components/layout/traversal.rs
index bf60c41d6ba..17c3d0b1c20 100644
--- a/components/layout/traversal.rs
+++ b/components/layout/traversal.rs
@@ -2,14 +2,18 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
+use script::layout_dom::ServoLayoutNode;
use script_layout_interface::wrapper_traits::LayoutNode;
use style::context::{SharedStyleContext, StyleContext};
use style::data::ElementData;
use style::dom::{NodeInfo, TElement, TNode};
+use style::selector_parser::RestyleDamage;
use style::traversal::{DomTraversal, PerLevelTraversalData, recalc_style_at};
+use style::values::computed::Display;
use crate::context::LayoutContext;
-use crate::dom::DOMLayoutData;
+use crate::dom::{DOMLayoutData, NodeExt};
+use crate::dom_traversal::iter_child_nodes;
pub struct RecalcStyle<'a> {
context: &'a LayoutContext<'a>,
@@ -40,14 +44,33 @@ where
) where
F: FnMut(E::ConcreteNode),
{
+ if node.is_text_node() {
+ return;
+ }
+
+ let had_style_data = node.style_data().is_some();
unsafe {
node.initialize_style_and_layout_data::<DOMLayoutData>();
- if !node.is_text_node() {
- let el = node.as_element().unwrap();
- let mut data = el.mutate_data().unwrap();
- recalc_style_at(self, traversal_data, context, el, &mut data, note_child);
- el.unset_dirty_descendants();
- }
+ }
+
+ let element = node.as_element().unwrap();
+ let mut element_data = element.mutate_data().unwrap();
+
+ if !had_style_data {
+ element_data.damage = RestyleDamage::reconstruct();
+ }
+
+ recalc_style_at(
+ self,
+ traversal_data,
+ context,
+ element,
+ &mut element_data,
+ note_child,
+ );
+
+ unsafe {
+ element.unset_dirty_descendants();
}
}
@@ -68,3 +91,48 @@ where
&self.context.style_context
}
}
+
+pub(crate) fn compute_damage_and_repair_style(
+ context: &SharedStyleContext,
+ node: ServoLayoutNode<'_>,
+) -> RestyleDamage {
+ compute_damage_and_repair_style_inner(context, node, RestyleDamage::empty())
+}
+
+pub(crate) fn compute_damage_and_repair_style_inner(
+ context: &SharedStyleContext,
+ node: ServoLayoutNode<'_>,
+ parent_restyle_damage: RestyleDamage,
+) -> RestyleDamage {
+ let original_damage;
+ let damage = {
+ let mut element_data = node
+ .style_data()
+ .expect("Should not run `compute_damage` before styling.")
+ .element_data
+ .borrow_mut();
+
+ if let Some(ref style) = element_data.styles.primary {
+ if style.get_box().display == Display::None {
+ return parent_restyle_damage;
+ }
+ }
+
+ original_damage = std::mem::take(&mut element_data.damage);
+ element_data.damage |= parent_restyle_damage;
+ element_data.damage
+ };
+
+ let mut propagated_damage = damage;
+ for child in iter_child_nodes(node) {
+ if child.is_element() {
+ propagated_damage |= compute_damage_and_repair_style_inner(context, child, damage);
+ }
+ }
+
+ if propagated_damage == RestyleDamage::REPAINT && original_damage == RestyleDamage::REPAINT {
+ node.repair_style(context);
+ }
+
+ propagated_damage
+}