layout: Incrementalize reflow of block formatting contexts impacted by

floats, and make float placement idempotent.

This moves float placement outside sequential block size computation.

Improves the maze solver.
This commit is contained in:
Patrick Walton 2014-11-05 10:08:31 -08:00
parent be36fcd3b1
commit 55da2c97d5
11 changed files with 157 additions and 48 deletions

View file

@ -32,8 +32,9 @@ use context::LayoutContext;
use css::node_style::StyledNode;
use display_list_builder::{BlockFlowDisplayListBuilding, BlockLevel, FragmentDisplayListBuilding};
use floats::{ClearBoth, ClearLeft, ClearRight, FloatKind, FloatLeft, Floats, PlacementInfo};
use flow::{AbsolutePositionInfo, BaseFlow, BlockFlowClass, FlowClass, Flow, ImmutableFlowUtils};
use flow::{MutableFlowUtils, PreorderFlowTraversal, PostorderFlowTraversal, mut_base};
use flow::{AbsolutePositionInfo, BaseFlow, BlockFlowClass, FloatIfNecessary, FlowClass, Flow};
use flow::{ForceNonfloated, ImmutableFlowUtils, MutableFlowUtils, PreorderFlowTraversal};
use flow::{PostorderFlowTraversal, mut_base};
use flow::{HAS_LEFT_FLOATED_DESCENDANTS, HAS_RIGHT_FLOATED_DESCENDANTS};
use flow::{IMPACTED_BY_LEFT_FLOATS, IMPACTED_BY_RIGHT_FLOATS};
use flow::{LAYERS_NEEDED_FOR_DESCENDANTS, NEEDS_LAYER};
@ -569,7 +570,7 @@ impl BlockFlow {
pub fn from_node(constructor: &mut FlowConstructor, node: &ThreadSafeLayoutNode) -> BlockFlow {
let writing_mode = node.style().writing_mode;
BlockFlow {
base: BaseFlow::new(Some((*node).clone()), writing_mode),
base: BaseFlow::new(Some((*node).clone()), writing_mode, ForceNonfloated),
fragment: Fragment::new(constructor, node),
static_b_offset: Au::new(0),
inline_size_of_preceding_left_floats: Au(0),
@ -583,7 +584,7 @@ impl BlockFlow {
pub fn from_node_and_fragment(node: &ThreadSafeLayoutNode, fragment: Fragment) -> BlockFlow {
let writing_mode = node.style().writing_mode;
BlockFlow {
base: BaseFlow::new(Some((*node).clone()), writing_mode),
base: BaseFlow::new(Some((*node).clone()), writing_mode, ForceNonfloated),
fragment: fragment,
static_b_offset: Au::new(0),
inline_size_of_preceding_left_floats: Au(0),
@ -600,7 +601,7 @@ impl BlockFlow {
-> BlockFlow {
let writing_mode = node.style().writing_mode;
BlockFlow {
base: BaseFlow::new(Some((*node).clone()), writing_mode),
base: BaseFlow::new(Some((*node).clone()), writing_mode, FloatIfNecessary),
fragment: Fragment::new(constructor, node),
static_b_offset: Au::new(0),
inline_size_of_preceding_left_floats: Au(0),
@ -617,7 +618,7 @@ impl BlockFlow {
-> BlockFlow {
let writing_mode = node.style().writing_mode;
BlockFlow {
base: BaseFlow::new(Some((*node).clone()), writing_mode),
base: BaseFlow::new(Some((*node).clone()), writing_mode, FloatIfNecessary),
fragment: fragment,
static_b_offset: Au::new(0),
inline_size_of_preceding_left_floats: Au(0),
@ -866,7 +867,10 @@ impl BlockFlow {
// Assume that the *hypothetical box* for an absolute flow starts immediately
// after the block-end border edge of the previous flow.
kid.as_block().hypothetical_position.b = cur_b;
kid.place_float_if_applicable(layout_context);
if !flow::base(kid).flags.is_float() {
kid.assign_block_size_for_inorder_child_if_necessary(layout_context);
}
propagate_layer_flag_from_child(&mut layers_needed_for_descendants, kid);
// Skip the collapsing and float processing for absolute flow kids and continue
@ -885,10 +889,7 @@ impl BlockFlow {
margin_collapse_info.current_float_ceiling();
}
propagate_layer_flag_from_child(&mut layers_needed_for_descendants, kid);
let need_to_process_child_floats =
kid.assign_block_size_for_inorder_child_if_necessary(layout_context);
assert!(need_to_process_child_floats); // As it was a float itself...
kid.place_float_if_applicable(layout_context);
let kid_base = flow::mut_base(kid);
floats = kid_base.floats.clone();
@ -907,8 +908,12 @@ impl BlockFlow {
}
// Lay the child out if this was an in-order traversal.
let need_to_process_child_floats =
kid.assign_block_size_for_inorder_child_if_necessary(layout_context);
let need_to_process_child_floats = if flow::base(kid).flags.is_float() {
kid.place_float_if_applicable(layout_context);
true
} else {
kid.assign_block_size_for_inorder_child_if_necessary(layout_context)
};
// Mark flows for layerization if necessary to handle painting order correctly.
propagate_layer_flag_from_child(&mut layers_needed_for_descendants, kid);
@ -1066,8 +1071,9 @@ impl BlockFlow {
// has not been calculated yet. (See `calculate_absolute_block_size_and_margins` for that.)
// Also don't remove the dirty bits if we're a block formatting context since our inline
// size has not yet been computed. (See `assign_inline_position_for_formatting_context()`.)
if !self.base.flags.contains(IS_ABSOLUTELY_POSITIONED) &&
self.formatting_context_type() == NonformattingContext {
if (self.base.flags.is_float() ||
self.formatting_context_type() == NonformattingContext) &&
!self.base.flags.contains(IS_ABSOLUTELY_POSITIONED) {
self.base.restyle_damage.remove(REFLOW_OUT_OF_FLOW | REFLOW);
}
}
@ -1113,8 +1119,13 @@ impl BlockFlow {
self.fragment.margin.block_start);
if !self.base.flags.contains(IS_ABSOLUTELY_POSITIONED) {
self.base.position = self.base.position.translate(&float_offset)
.translate(&margin_offset);
let mut origin = LogicalPoint::new(self.base.writing_mode,
self.hypothetical_position.i,
self.base.position.start.b);
origin = origin.add_point(&float_offset).add_point(&margin_offset);
self.base.position = LogicalRect::from_point_size(self.base.writing_mode,
origin,
self.base.position.size);
}
}
@ -1321,7 +1332,8 @@ impl BlockFlow {
// and its inline-size is our content inline-size.
{
let kid_base = flow::mut_base(kid);
if !kid_base.flags.contains(IS_ABSOLUTELY_POSITIONED) {
if !kid_base.flags.contains(IS_ABSOLUTELY_POSITIONED) ||
!kid_base.flags.is_float() {
kid_base.position.start.i = inline_start_content_edge
}
kid_base.block_container_inline_size = content_inline_size;
@ -1414,9 +1426,9 @@ impl BlockFlow {
}
let info = PlacementInfo {
size: LogicalSize::new(
self.fragment.style.writing_mode,
self.base.position.size.inline + self.fragment.margin.inline_start_end() +
size: LogicalSize::new(self.fragment.style.writing_mode,
self.base.position.size.inline +
self.fragment.margin.inline_start_end() +
self.fragment.border_padding.inline_start_end(),
self.fragment.border_box.size.block),
ceiling: self.base.position.start.b,
@ -1426,10 +1438,12 @@ impl BlockFlow {
// Offset our position by whatever displacement is needed to not impact the floats.
let rect = self.base.floats.place_between_floats(&info);
self.base.position.start.i = self.base.position.start.i + rect.start.i;
self.base.position.start.i = self.hypothetical_position.i + rect.start.i;
// TODO(pcwalton): If the inline-size of this flow is different from the size we estimated
// earlier, lay it out again.
self.base.restyle_damage.remove(REFLOW_OUT_OF_FLOW | REFLOW);
}
fn is_inline_block(&self) -> bool {
@ -1602,19 +1616,17 @@ impl Flow for BlockFlow {
None);
}
/// Assigns block-sizes in-order; or, if this is a float, places the float. The default
/// implementation simply assigns block-sizes if this flow is impacted by floats. Returns true
/// if this child affected the floats in the flow somehow or false otherwise; thus, if true,
/// then the parent flow is expected to take the `floats` member of this flow into account.
///
/// This is called on child flows by the parent. Hence, we can assume that `assign_block_size`
/// has already been called on the child (because of the bottom-up traversal).
fn place_float_if_applicable<'a>(&mut self, _: &'a LayoutContext<'a>) {
if self.base.flags.is_float() {
self.place_float();
}
}
fn assign_block_size_for_inorder_child_if_necessary<'a>(&mut self,
layout_context: &'a LayoutContext<'a>)
-> bool {
if self.base.flags.is_float() {
self.place_float();
return true
return false
}
let is_formatting_context = self.formatting_context_type() != NonformattingContext;
@ -2010,7 +2022,6 @@ pub trait ISizeAndMarginsComputer {
// We also resize the block itself, to ensure that overflow is not calculated
// as the inline-size of our parent. We might be smaller and we might be larger if we
// overflow.
flow::mut_base(block).position.size.inline = inline_size + extra_inline_size_from_margin;
}

View file

@ -191,6 +191,9 @@ pub trait Flow: fmt::Show + ToString + Sync {
panic!("assign_block_size not yet implemented")
}
/// If this is a float, places it. The default implementation does nothing.
fn place_float_if_applicable<'a>(&mut self, _: &'a LayoutContext<'a>) {}
/// Assigns block-sizes in-order; or, if this is a float, places the float. The default
/// implementation simply assigns block-sizes if this flow is impacted by floats. Returns true
/// if this child was impacted by floats or false otherwise.
@ -568,7 +571,7 @@ impl FlowFlags {
#[inline]
pub fn is_float(&self) -> bool {
self.floats_left() || self.floats_right()
self.contains(FLOATS_LEFT) || self.contains(FLOATS_RIGHT)
}
#[inline]
@ -835,9 +838,22 @@ impl Drop for BaseFlow {
}
}
/// Whether a base flow should be forced to be nonfloated. This can affect e.g. `TableFlow`, which
/// is never floated because the table wrapper flow is the floated one.
#[deriving(Clone, PartialEq)]
pub enum ForceNonfloatedFlag {
/// The flow should be floated if the node has a `float` property.
FloatIfNecessary,
/// The flow should be forced to be nonfloated.
ForceNonfloated,
}
impl BaseFlow {
#[inline]
pub fn new(node: Option<ThreadSafeLayoutNode>, writing_mode: WritingMode) -> BaseFlow {
pub fn new(node: Option<ThreadSafeLayoutNode>,
writing_mode: WritingMode,
force_nonfloated: ForceNonfloatedFlag)
-> BaseFlow {
let mut flags = FlowFlags::empty();
match node {
None => {}
@ -849,11 +865,15 @@ impl BaseFlow {
}
_ => {}
}
if force_nonfloated == FloatIfNecessary {
match node_style.get_box().float {
float::none => {}
float::left => flags.insert(FLOATS_LEFT),
float::right => flags.insert(FLOATS_RIGHT),
}
}
match node_style.get_box().clear {
clear::none => {}
clear::left => flags.insert(CLEARS_LEFT),

View file

@ -8,7 +8,7 @@ use css::node_style::StyledNode;
use context::LayoutContext;
use display_list_builder::{ContentLevel, DisplayListResult, FragmentDisplayListBuilding};
use floats::{FloatLeft, Floats, PlacementInfo};
use flow::{BaseFlow, FlowClass, Flow, InlineFlowClass, MutableFlowUtils};
use flow::{BaseFlow, FlowClass, Flow, ForceNonfloated, InlineFlowClass, MutableFlowUtils};
use flow::{IS_ABSOLUTELY_POSITIONED};
use flow;
use fragment::{Fragment, InlineAbsoluteHypotheticalFragment, InlineBlockFragment};
@ -706,7 +706,7 @@ pub struct InlineFlow {
impl InlineFlow {
pub fn from_fragments(fragments: InlineFragments, writing_mode: WritingMode) -> InlineFlow {
InlineFlow {
base: BaseFlow::new(None, writing_mode),
base: BaseFlow::new(None, writing_mode, ForceNonfloated),
fragments: fragments,
lines: Vec::new(),
minimum_block_size_above_baseline: Au(0),

View file

@ -11,8 +11,8 @@ use block::{ISizeConstraintInput, ISizeConstraintSolution};
use construct::FlowConstructor;
use context::LayoutContext;
use floats::FloatKind;
use flow::{TableFlowClass, FlowClass, Flow, ImmutableFlowUtils};
use flow::{IMPACTED_BY_LEFT_FLOATS, IMPACTED_BY_RIGHT_FLOATS};
use flow::{Flow, FlowClass, IMPACTED_BY_LEFT_FLOATS, IMPACTED_BY_RIGHT_FLOATS, ImmutableFlowUtils};
use flow::{TableFlowClass};
use fragment::{Fragment, FragmentBoundsIterator};
use layout_debug;
use model::{IntrinsicISizes, IntrinsicISizesContribution};

View file

@ -8,7 +8,7 @@
use context::LayoutContext;
use css::node_style::StyledNode;
use flow::{BaseFlow, TableColGroupFlowClass, FlowClass, Flow};
use flow::{BaseFlow, ForceNonfloated, TableColGroupFlowClass, FlowClass, Flow};
use fragment::{Fragment, FragmentBoundsIterator, TableColumnFragment};
use layout_debug;
use wrapper::ThreadSafeLayoutNode;
@ -44,7 +44,7 @@ impl TableColGroupFlow {
-> TableColGroupFlow {
let writing_mode = node.style().writing_mode;
TableColGroupFlow {
base: BaseFlow::new(Some((*node).clone()), writing_mode),
base: BaseFlow::new(Some((*node).clone()), writing_mode, ForceNonfloated),
fragment: Some(fragment),
cols: fragments,
inline_sizes: vec!(),

View file

@ -79,7 +79,10 @@ impl TableRowFlow {
// cells).
let mut max_y = Au(0);
for kid in self.block_flow.base.child_iter() {
kid.place_float_if_applicable(layout_context);
if !flow::base(kid).flags.is_float() {
kid.assign_block_size_for_inorder_child_if_necessary(layout_context);
}
{
let child_fragment = kid.as_table_cell().fragment();

View file

@ -74,7 +74,10 @@ impl TableRowGroupFlow {
let mut cur_y = block_start_offset;
for kid in self.block_flow.base.child_iter() {
kid.place_float_if_applicable(layout_context);
if !flow::base(kid).flags.is_float() {
kid.assign_block_size_for_inorder_child_if_necessary(layout_context);
}
let child_node = flow::mut_base(kid);
child_node.position.start.b = cur_y;

View file

@ -301,6 +301,10 @@ impl Flow for TableWrapperFlow {
self.block_flow.compute_absolute_position()
}
fn place_float_if_applicable<'a>(&mut self, layout_context: &'a LayoutContext<'a>) {
self.block_flow.place_float_if_applicable(layout_context)
}
fn assign_block_size_for_inorder_child_if_necessary<'a>(&mut self,
layout_context: &'a LayoutContext<'a>)
-> bool {

View file

@ -184,3 +184,4 @@ fragment=top != ../html/acid2.html acid2_ref.html
== linear_gradients_reverse_a.html linear_gradients_reverse_ref.html
!= linear_gradients_corners_a.html linear_gradients_corners_ref.html
== linear_gradients_lengths_a.html linear_gradients_lengths_ref.html
== incremental_float_a.html incremental_float_ref.html

View file

@ -0,0 +1,36 @@
<!DOCTYPE html>
<html>
<head>
<style>
nav.floaty {
float: left;
}
section {
clear: both;
position: relative;
}
</style>
</head>
<body>
<section>
<nav>This floats</nav>
<div>This doesn't</div>
</section>
<section>
<nav>This floats</nav>
<div style="overflow: hidden;">This is a block formatting context</div>
</section>
<section>
<nav>This floats</nav>
<div style="position: absolute; top: 0; left: 0; width: 100px; height: 100px;">
This is abspos
</div>
</section>
<script>
var elements = document.getElementsByTagName('nav');
for (var i = 0; i < elements.length; i++)
elements[i].setAttribute('class', 'floaty');
</script>
</body>
</html>

View file

@ -0,0 +1,31 @@
<!DOCTYPE html>
<html>
<head>
<style>
nav {
float: left;
}
section {
clear: both;
position: relative;
}
</style>
</head>
<body>
<section>
<nav>This floats</nav>
<div>This doesn't</div>
</section>
<section>
<nav>This floats</nav>
<div style="overflow: hidden;">This is a block formatting context</div>
</section>
<section>
<nav>This floats</nav>
<div style="position: absolute; top: 0; left: 0; width: 100px; height: 100px;">
This is abspos
</div>
</section>
</body>
</html>