mirror of
https://github.com/servo/servo.git
synced 2025-08-03 04:30:10 +01:00
layout: Rewrite the block formatting context/float inline-size
speculation code. The old code tried to do the speculation as a single bottom-up pass after intrinsic inline-size calculation, which was unable to handle cases like this: <div> <div style="float: left">Foo</div> </div> <div> <div style="overflow: hidden">Bar</div> </div> No single bottom-up pass could possibly handle this case, because the inline-size of the float flowing out of the "Foo" block could never make it down to the "Bar" block, where it is needed for speculation. On the pages I tried, this regresses layout performance by 1%-2%. I first noticed this breaking some pages, like the Google SERPs, several months ago.
This commit is contained in:
parent
9b2ae3a62f
commit
b29719e36b
15 changed files with 346 additions and 184 deletions
|
@ -33,10 +33,7 @@ use display_list_builder::BlockFlowDisplayListBuilding;
|
|||
use display_list_builder::{BorderPaintingMode, DisplayListBuildState, FragmentDisplayListBuilding};
|
||||
use euclid::{Point2D, Rect, Size2D};
|
||||
use floats::{ClearType, FloatKind, Floats, PlacementInfo};
|
||||
use flow::{BLOCK_POSITION_IS_STATIC};
|
||||
use flow::{CLEARS_LEFT, CLEARS_RIGHT};
|
||||
use flow::{HAS_LEFT_FLOATED_DESCENDANTS, HAS_RIGHT_FLOATED_DESCENDANTS};
|
||||
use flow::{IMPACTED_BY_LEFT_FLOATS, IMPACTED_BY_RIGHT_FLOATS, INLINE_POSITION_IS_STATIC};
|
||||
use flow::{BLOCK_POSITION_IS_STATIC, CLEARS_LEFT, CLEARS_RIGHT, INLINE_POSITION_IS_STATIC};
|
||||
use flow::{IS_ABSOLUTELY_POSITIONED};
|
||||
use flow::{ImmutableFlowUtils, LateAbsolutePositionInfo, MutableFlowUtils, OpaqueFlow};
|
||||
use flow::{NEEDS_LAYER, PostorderFlowTraversal, PreorderFlowTraversal, FragmentationContext};
|
||||
|
@ -497,7 +494,7 @@ pub enum MarginsMayCollapseFlag {
|
|||
}
|
||||
|
||||
#[derive(PartialEq)]
|
||||
enum FormattingContextType {
|
||||
pub enum FormattingContextType {
|
||||
None,
|
||||
Block,
|
||||
Other,
|
||||
|
@ -844,7 +841,7 @@ impl BlockFlow {
|
|||
}
|
||||
}
|
||||
|
||||
// Assign block-size now for the child if it was impacted by floats and we couldn't
|
||||
// Assign block-size now for the child if it might have floats in and we couldn't
|
||||
// before.
|
||||
flow::mut_base(kid).floats = floats.clone();
|
||||
if flow::base(kid).flags.is_float() {
|
||||
|
@ -1289,9 +1286,8 @@ impl BlockFlow {
|
|||
}
|
||||
|
||||
/// Assigns the computed inline-start content edge and inline-size to all the children of this
|
||||
/// block flow. Also computes whether each child will be impacted by floats. The given
|
||||
/// `callback`, if supplied, will be called once per child; it is currently used to push down
|
||||
/// column sizes for tables.
|
||||
/// block flow. The given `callback`, if supplied, will be called once per child; it is
|
||||
/// currently used to push down column sizes for tables.
|
||||
///
|
||||
/// `#[inline(always)]` because this is called only from block or table inline-size assignment
|
||||
/// and the code for block layout is significantly simpler.
|
||||
|
@ -1308,34 +1304,8 @@ impl BlockFlow {
|
|||
WritingMode,
|
||||
&mut Au,
|
||||
&mut Au) {
|
||||
// Keep track of whether floats could impact each child.
|
||||
let mut inline_start_floats_impact_child =
|
||||
self.base.flags.contains(IMPACTED_BY_LEFT_FLOATS);
|
||||
let mut inline_end_floats_impact_child =
|
||||
self.base.flags.contains(IMPACTED_BY_RIGHT_FLOATS);
|
||||
|
||||
let flags = self.base.flags.clone();
|
||||
|
||||
// Remember the inline-sizes of the last left and right floats, if there were any. These
|
||||
// are used for estimating the inline-sizes of block formatting contexts. (We estimate that
|
||||
// the inline-size of any block formatting context that we see will be based on the
|
||||
// inline-size of the containing block as well as the last float seen before it in each
|
||||
// direction.)
|
||||
let mut inline_size_of_preceding_left_floats = Au(0);
|
||||
let mut inline_size_of_preceding_right_floats = Au(0);
|
||||
if self.formatting_context_type() == FormattingContextType::None {
|
||||
if inline_start_content_edge > Au(0) {
|
||||
inline_size_of_preceding_left_floats =
|
||||
max(self.inline_size_of_preceding_left_floats - inline_start_content_edge,
|
||||
Au(0));
|
||||
}
|
||||
if inline_end_content_edge > Au(0) {
|
||||
inline_size_of_preceding_right_floats =
|
||||
max(self.inline_size_of_preceding_right_floats - inline_end_content_edge,
|
||||
Au(0));
|
||||
}
|
||||
}
|
||||
|
||||
let opaque_self = OpaqueFlow::from_flow(self);
|
||||
|
||||
// Calculate non-auto block size to pass to children.
|
||||
|
@ -1358,29 +1328,6 @@ impl BlockFlow {
|
|||
while let Some((i, kid)) = iterator.next() {
|
||||
flow::mut_base(kid).block_container_explicit_block_size = explicit_content_size;
|
||||
|
||||
// Determine float impaction, and update the inline size speculations if necessary.
|
||||
if flow::base(kid).flags.contains(CLEARS_LEFT) {
|
||||
inline_start_floats_impact_child = false;
|
||||
inline_size_of_preceding_left_floats = Au(0);
|
||||
}
|
||||
if flow::base(kid).flags.contains(CLEARS_RIGHT) {
|
||||
inline_end_floats_impact_child = false;
|
||||
inline_size_of_preceding_right_floats = Au(0);
|
||||
}
|
||||
|
||||
// Update the speculated inline size if this child is floated.
|
||||
match flow::base(kid).flags.float_kind() {
|
||||
float::T::none => {}
|
||||
float::T::left => {
|
||||
inline_size_of_preceding_left_floats = inline_size_of_preceding_left_floats +
|
||||
flow::base(kid).intrinsic_inline_sizes.preferred_inline_size;
|
||||
}
|
||||
float::T::right => {
|
||||
inline_size_of_preceding_right_floats = inline_size_of_preceding_right_floats +
|
||||
flow::base(kid).intrinsic_inline_sizes.preferred_inline_size;
|
||||
}
|
||||
}
|
||||
|
||||
// The inline-start margin edge of the child flow is at our inline-start content edge,
|
||||
// and its inline-size is our content inline-size.
|
||||
let kid_mode = flow::base(kid).writing_mode;
|
||||
|
@ -1399,24 +1346,6 @@ impl BlockFlow {
|
|||
kid_base.block_container_writing_mode = containing_block_mode;
|
||||
}
|
||||
|
||||
{
|
||||
let kid_base = flow::mut_base(kid);
|
||||
inline_start_floats_impact_child = inline_start_floats_impact_child ||
|
||||
kid_base.flags.contains(HAS_LEFT_FLOATED_DESCENDANTS);
|
||||
inline_end_floats_impact_child = inline_end_floats_impact_child ||
|
||||
kid_base.flags.contains(HAS_RIGHT_FLOATED_DESCENDANTS);
|
||||
kid_base.flags.set(IMPACTED_BY_LEFT_FLOATS, inline_start_floats_impact_child);
|
||||
kid_base.flags.set(IMPACTED_BY_RIGHT_FLOATS, inline_end_floats_impact_child);
|
||||
}
|
||||
|
||||
if kid.is_block_flow() {
|
||||
let kid_block = kid.as_mut_block();
|
||||
kid_block.inline_size_of_preceding_left_floats =
|
||||
inline_size_of_preceding_left_floats;
|
||||
kid_block.inline_size_of_preceding_right_floats =
|
||||
inline_size_of_preceding_right_floats;
|
||||
}
|
||||
|
||||
// Call the callback to propagate extra inline size information down to the child. This
|
||||
// is currently used for tables.
|
||||
callback(kid,
|
||||
|
@ -1445,7 +1374,7 @@ impl BlockFlow {
|
|||
|
||||
/// Determines the type of formatting context this is. See the definition of
|
||||
/// `FormattingContextType`.
|
||||
fn formatting_context_type(&self) -> FormattingContextType {
|
||||
pub fn formatting_context_type(&self) -> FormattingContextType {
|
||||
let style = self.fragment.style();
|
||||
if style.get_box().float != float::T::none {
|
||||
return FormattingContextType::Other
|
||||
|
@ -1528,8 +1457,6 @@ impl BlockFlow {
|
|||
let _scope = layout_debug_scope!("block::bubble_inline_sizes {:x}", self.base.debug_id());
|
||||
|
||||
let mut flags = self.base.flags;
|
||||
flags.remove(HAS_LEFT_FLOATED_DESCENDANTS);
|
||||
flags.remove(HAS_RIGHT_FLOATED_DESCENDANTS);
|
||||
|
||||
// Find the maximum inline-size from children.
|
||||
let mut computation = self.fragment.compute_intrinsic_inline_sizes();
|
||||
|
@ -1584,12 +1511,6 @@ impl BlockFlow {
|
|||
left_float_width + right_float_width);
|
||||
|
||||
self.base.intrinsic_inline_sizes = computation.finish();
|
||||
|
||||
match self.fragment.style().get_box().float {
|
||||
float::T::none => {}
|
||||
float::T::left => flags.insert(HAS_LEFT_FLOATED_DESCENDANTS),
|
||||
float::T::right => flags.insert(HAS_RIGHT_FLOATED_DESCENDANTS),
|
||||
}
|
||||
self.base.flags = flags
|
||||
}
|
||||
|
||||
|
@ -1668,23 +1589,15 @@ impl BlockFlow {
|
|||
self.base.block_container_inline_size = LogicalSize::from_physical(
|
||||
self.base.writing_mode, layout_context.shared_context().viewport_size).inline;
|
||||
self.base.block_container_writing_mode = self.base.writing_mode;
|
||||
|
||||
// The root element is never impacted by floats.
|
||||
self.base.flags.remove(IMPACTED_BY_LEFT_FLOATS);
|
||||
self.base.flags.remove(IMPACTED_BY_RIGHT_FLOATS);
|
||||
}
|
||||
|
||||
// Our inline-size was set to the inline-size of the containing block by the flow's parent.
|
||||
// Now compute the real value.
|
||||
self.propagate_and_compute_used_inline_size(layout_context);
|
||||
|
||||
// Formatting contexts are never impacted by floats.
|
||||
// Now for some speculation.
|
||||
match self.formatting_context_type() {
|
||||
FormattingContextType::None => {}
|
||||
FormattingContextType::Block => {
|
||||
self.base.flags.remove(IMPACTED_BY_LEFT_FLOATS);
|
||||
self.base.flags.remove(IMPACTED_BY_RIGHT_FLOATS);
|
||||
|
||||
// We can't actually compute the inline-size of this block now, because floats
|
||||
// might affect it. Speculate that its inline-size is equal to the inline-size
|
||||
// computed above minus the inline-size of the previous left and/or right floats.
|
||||
|
@ -1695,14 +1608,11 @@ impl BlockFlow {
|
|||
if self.fragment.style.max_inline_size() == LengthOrPercentageOrNone::None {
|
||||
self.fragment.border_box.size.inline =
|
||||
self.fragment.border_box.size.inline -
|
||||
self.inline_size_of_preceding_left_floats -
|
||||
self.inline_size_of_preceding_right_floats;
|
||||
self.base.speculated_float_placement_in.left -
|
||||
self.base.speculated_float_placement_in.right;
|
||||
}
|
||||
}
|
||||
FormattingContextType::Other => {
|
||||
self.base.flags.remove(IMPACTED_BY_LEFT_FLOATS);
|
||||
self.base.flags.remove(IMPACTED_BY_RIGHT_FLOATS);
|
||||
}
|
||||
FormattingContextType::None | FormattingContextType::Other => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1786,7 +1696,7 @@ impl Flow for BlockFlow {
|
|||
self.assign_inline_position_for_formatting_context();
|
||||
}
|
||||
|
||||
if self.base.flags.impacted_by_floats() {
|
||||
if (self as &Flow).floats_might_flow_through() {
|
||||
self.base.thread_id = parent_thread_id;
|
||||
if self.base.restyle_damage.intersects(REFLOW_OUT_OF_FLOW | REFLOW) {
|
||||
self.assign_block_size(layout_context);
|
||||
|
@ -1797,7 +1707,7 @@ impl Flow for BlockFlow {
|
|||
}
|
||||
|
||||
if is_formatting_context {
|
||||
// If this is a formatting context and was *not* impacted by floats, then we must
|
||||
// If this is a formatting context and definitely did not have floats in, then we must
|
||||
// translate the floats past us.
|
||||
let writing_mode = self.base.floats.writing_mode;
|
||||
let delta = self.base.position.size.block;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue