mirror of
https://github.com/servo/servo.git
synced 2025-08-03 12:40:06 +01:00
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:
parent
be36fcd3b1
commit
55da2c97d5
11 changed files with 157 additions and 48 deletions
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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};
|
||||
|
|
|
@ -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!(),
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
|
|
36
tests/ref/incremental_float_a.html
Normal file
36
tests/ref/incremental_float_a.html
Normal 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>
|
||||
|
31
tests/ref/incremental_float_ref.html
Normal file
31
tests/ref/incremental_float_ref.html
Normal 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>
|
||||
|
Loading…
Add table
Add a link
Reference in a new issue