diff --git a/components/layout_2020/flexbox/construct.rs b/components/layout_2020/flexbox/construct.rs index 8d9dfbf75ce..d1d3580aace 100644 --- a/components/layout_2020/flexbox/construct.rs +++ b/components/layout_2020/flexbox/construct.rs @@ -181,6 +181,7 @@ where independent_formatting_context: IndependentFormattingContext::NonReplaced( non_replaced, ), + cached_layout: Default::default(), }))) }, FlexLevelJob::Element { @@ -212,6 +213,7 @@ where contents, self.text_decoration_line, ), + cached_layout: Default::default(), })) }; box_slot.set(LayoutBox::FlexLevel(box_.clone())); diff --git a/components/layout_2020/flexbox/layout.rs b/components/layout_2020/flexbox/layout.rs index db5517eb552..8950e9a0ce1 100644 --- a/components/layout_2020/flexbox/layout.rs +++ b/components/layout_2020/flexbox/layout.rs @@ -21,7 +21,10 @@ use style::values::specified::align::AlignFlags; use style::Zero; use super::geom::{FlexAxis, FlexRelativeRect, FlexRelativeSides, FlexRelativeVec2}; -use super::{FlexContainer, FlexContainerConfig, FlexItemBox, FlexLevelBox}; +use super::{ + FlexContainer, FlexContainerConfig, FlexItemBox, FlexItemLayoutCache, + FlexItemLayoutCacheDescriptor, FlexLevelBox, +}; use crate::cell::ArcRefCell; use crate::context::LayoutContext; use crate::formatting_contexts::{Baselines, IndependentFormattingContext, IndependentLayout}; @@ -1708,6 +1711,9 @@ impl FlexItem<'_> { flex_context: &FlexContext, used_cross_size_override: Option, ) -> FlexItemLayoutResult { + // Clear any layout cache information so that it doesn't persist until the next layout. + self.box_.cached_layout.borrow_mut().take(); + let containing_block = flex_context.containing_block; let mut positioning_context = PositioningContext::new_for_style(self.box_.style()) .unwrap_or_else(|| { @@ -2572,14 +2578,27 @@ impl FlexItemBox { style: &non_replaced.style, }; let mut content_block_size = || { - non_replaced - .layout( - flex_context.layout_context, - &mut positioning_context, - &item_as_containing_block, - flex_context.containing_block, - ) - .content_block_size + if let Some(cache) = &*self.cached_layout.borrow() { + if cache.descriptor.compatible_with_size(inline_size) { + return cache.descriptor.content_block_size; + } + } + + let layout = non_replaced.layout( + flex_context.layout_context, + &mut positioning_context, + &item_as_containing_block, + flex_context.containing_block, + ); + + let content_block_size = layout.content_block_size; + *self.cached_layout.borrow_mut() = Some(FlexItemLayoutCache { + descriptor: FlexItemLayoutCacheDescriptor { + containing_block_inline_size: item_as_containing_block.inline_size, + content_block_size: layout.content_block_size, + }, + }); + content_block_size }; match intrinsic_sizing_mode { IntrinsicSizingMode::Contribution => { diff --git a/components/layout_2020/flexbox/mod.rs b/components/layout_2020/flexbox/mod.rs index b0ef118b4e0..4eeac734feb 100644 --- a/components/layout_2020/flexbox/mod.rs +++ b/components/layout_2020/flexbox/mod.rs @@ -2,6 +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 app_units::Au; use geom::{FlexAxis, MainStartCrossStart}; use serde::Serialize; use servo_arc::Arc as ServoArc; @@ -112,9 +113,17 @@ pub(crate) enum FlexLevelBox { OutOfFlowAbsolutelyPositionedBox(ArcRefCell), } -#[derive(Debug, Serialize)] +#[derive(Serialize)] pub(crate) struct FlexItemBox { independent_formatting_context: IndependentFormattingContext, + #[serde(skip)] + cached_layout: ArcRefCell>, +} + +impl std::fmt::Debug for FlexItemBox { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_str("FlexItemBox") + } } impl FlexItemBox { @@ -126,3 +135,20 @@ impl FlexItemBox { self.independent_formatting_context.base_fragment_info() } } + +#[derive(Debug)] +struct FlexItemLayoutCacheDescriptor { + containing_block_inline_size: Au, + content_block_size: Au, +} + +impl FlexItemLayoutCacheDescriptor { + fn compatible_with_size(&self, inline: Au) -> bool { + inline == self.containing_block_inline_size + } +} + +/// A cache to avoid multiple layouts during flexbox layout. +struct FlexItemLayoutCache { + descriptor: FlexItemLayoutCacheDescriptor, +}