aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSimon Sapin <simon.sapin@exyr.org>2015-12-30 23:50:24 +0000
committerSimon Sapin <simon.sapin@exyr.org>2016-01-28 09:43:14 +0100
commit5498b5434716ac2a464532c0455aac68c8ed06b2 (patch)
tree3d30387104f8b401f0a14f5d8339cac75cb0b28e
parent359b98434843feab752a1f0268339babd9759633 (diff)
downloadservo-5498b5434716ac2a464532c0455aac68c8ed06b2.tar.gz
servo-5498b5434716ac2a464532c0455aac68c8ed06b2.zip
Add Multicolumn support block fragmentation.
-rw-r--r--components/layout/block.rs79
-rw-r--r--components/layout/construct.rs67
-rw-r--r--components/layout/display_list_builder.rs2
-rw-r--r--components/layout/flow.rs45
-rw-r--r--components/layout/flow_list.rs18
-rw-r--r--components/layout/fragment.rs26
-rw-r--r--components/layout/model.rs2
-rw-r--r--components/layout/multicol.rs167
-rw-r--r--components/layout/table_cell.rs6
-rw-r--r--components/layout/table_wrapper.rs8
10 files changed, 371 insertions, 49 deletions
diff --git a/components/layout/block.rs b/components/layout/block.rs
index 40e2042ba67..2ec619607a3 100644
--- a/components/layout/block.rs
+++ b/components/layout/block.rs
@@ -39,8 +39,10 @@ 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::{IS_ABSOLUTELY_POSITIONED};
use flow::{ImmutableFlowUtils, LateAbsolutePositionInfo, MutableFlowUtils, OpaqueFlow};
-use flow::{NEEDS_LAYER, PostorderFlowTraversal, PreorderFlowTraversal};
+use flow::{NEEDS_LAYER, PostorderFlowTraversal, PreorderFlowTraversal, FragmentationContext};
use flow::{self, BaseFlow, EarlyAbsolutePositionInfo, Flow, FlowClass, ForceNonfloatedFlag};
+use flow_list::FlowList;
+use flow_ref::FlowRef;
use fragment::{CoordinateSystem, Fragment, FragmentBorderBoxIterator, HAS_LAYER};
use fragment::{SpecificFragmentInfo};
use gfx::display_list::{ClippingRegion, DisplayList};
@@ -726,6 +728,18 @@ impl BlockFlow {
block_end_margin_value;
}
+ // FIXME: Record enough info to deal with fragmented decorations.
+ // See https://drafts.csswg.org/css-break/#break-decoration
+ // For borders, this might be `enum FragmentPosition { First, Middle, Last }`
+ fn clone_with_children(&self, new_children: FlowList) -> BlockFlow {
+ BlockFlow {
+ base: self.base.clone_with_children(new_children),
+ fragment: self.fragment.clone(),
+ float: self.float.clone(),
+ ..*self
+ }
+ }
+
/// Assign block-size for current flow.
///
/// * Collapse margins for flow's children and set in-flow child flows' block offsets now that
@@ -742,10 +756,13 @@ impl BlockFlow {
#[inline(always)]
pub fn assign_block_size_block_base<'a>(&mut self,
layout_context: &'a LayoutContext<'a>,
- margins_may_collapse: MarginsMayCollapseFlag) {
+ mut fragmentation_context: Option<FragmentationContext>,
+ margins_may_collapse: MarginsMayCollapseFlag)
+ -> Option<FlowRef> {
let _scope = layout_debug_scope!("assign_block_size_block_base {:x}",
self.base.debug_id());
+ let mut break_at = None;
if self.base.restyle_damage.contains(REFLOW) {
self.determine_if_layer_needed();
@@ -779,7 +796,7 @@ impl BlockFlow {
// At this point, `cur_b` is at the content edge of our box. Now iterate over children.
let mut floats = self.base.floats.clone();
let thread_id = self.base.thread_id;
- for kid in self.base.child_iter() {
+ for (child_index, kid) in self.base.child_iter().enumerate() {
if flow::base(kid).flags.contains(IS_ABSOLUTELY_POSITIONED) {
// Assume that the *hypothetical box* for an absolute flow starts immediately
// after the block-end border edge of the previous flow.
@@ -799,6 +816,17 @@ impl BlockFlow {
continue
}
+ let previous_b = cur_b;
+ if let Some(ctx) = fragmentation_context {
+ let child_ctx = FragmentationContext {
+ available_block_size: ctx.available_block_size - cur_b,
+ this_fragment_is_empty: ctx.this_fragment_is_empty,
+ };
+ if let Some(remaining) = kid.fragment(layout_context, Some(child_ctx)) {
+ break_at = Some((child_index + 1, Some(remaining)));
+ }
+ }
+
// Assign block-size now for the child if it was impacted by floats and we couldn't
// before.
flow::mut_base(kid).floats = floats.clone();
@@ -867,6 +895,19 @@ impl BlockFlow {
let delta =
margin_collapse_info.advance_block_end_margin(&kid_base.collapsible_margins);
translate_including_floats(&mut cur_b, delta, &mut floats);
+
+ if break_at.is_some() {
+ break
+ }
+
+ if let Some(ref mut ctx) = fragmentation_context {
+ if cur_b > ctx.available_block_size && !ctx.this_fragment_is_empty {
+ break_at = Some((child_index, None));
+ cur_b = previous_b;
+ break
+ }
+ ctx.this_fragment_is_empty = false
+ }
}
// Add in our block-end margin and compute our collapsible margins.
@@ -920,7 +961,7 @@ impl BlockFlow {
}
if self.base.flags.contains(IS_ABSOLUTELY_POSITIONED) {
- return
+ return None
}
// Compute any explicitly-specified block size.
@@ -985,6 +1026,18 @@ impl BlockFlow {
self.base.restyle_damage.remove(REFLOW_OUT_OF_FLOW | REFLOW);
self.fragment.restyle_damage.remove(REFLOW_OUT_OF_FLOW | REFLOW);
}
+
+ break_at.and_then(|(i, child_remaining)| {
+ if i == self.base.children.len() && child_remaining.is_none() {
+ None
+ } else {
+ let mut children = self.base.children.split_off(i);
+ if let Some(child) = child_remaining {
+ children.push_front(child);
+ }
+ Some(Arc::new(self.clone_with_children(children)) as FlowRef)
+ }
+ })
}
/// Add placement information about current float flow for use by the parent.
@@ -1711,6 +1764,13 @@ impl Flow for BlockFlow {
}
fn assign_block_size<'a>(&mut self, ctx: &'a LayoutContext<'a>) {
+ let remaining = Flow::fragment(self, ctx, None);
+ debug_assert!(remaining.is_none());
+ }
+
+ fn fragment(&mut self, layout_context: &LayoutContext,
+ fragmentation_context: Option<FragmentationContext>)
+ -> Option<FlowRef> {
if self.is_replaced_content() {
let _scope = layout_debug_scope!("assign_replaced_block_size_if_necessary {:x}",
self.base.debug_id());
@@ -1722,15 +1782,22 @@ impl Flow for BlockFlow {
if !self.base.flags.contains(IS_ABSOLUTELY_POSITIONED) {
self.base.position.size.block = self.fragment.border_box.size.block;
}
+ None
} else if self.is_root() || self.base.flags.is_float() || self.is_inline_block() {
// Root element margins should never be collapsed according to CSS § 8.3.1.
debug!("assign_block_size: assigning block_size for root flow {:?}",
flow::base(self).debug_id());
- self.assign_block_size_block_base(ctx, MarginsMayCollapseFlag::MarginsMayNotCollapse);
+ self.assign_block_size_block_base(
+ layout_context,
+ fragmentation_context,
+ MarginsMayCollapseFlag::MarginsMayNotCollapse)
} else {
debug!("assign_block_size: assigning block_size for block {:?}",
flow::base(self).debug_id());
- self.assign_block_size_block_base(ctx, MarginsMayCollapseFlag::MarginsMayCollapse);
+ self.assign_block_size_block_base(
+ layout_context,
+ fragmentation_context,
+ MarginsMayCollapseFlag::MarginsMayCollapse)
}
}
diff --git a/components/layout/construct.rs b/components/layout/construct.rs
index 53ddc2bed04..36c5742e053 100644
--- a/components/layout/construct.rs
+++ b/components/layout/construct.rs
@@ -31,7 +31,7 @@ use incremental::{BUBBLE_ISIZES, RECONSTRUCT_FLOW, RestyleDamage};
use inline::{FIRST_FRAGMENT_OF_ELEMENT, InlineFlow, InlineFragmentNodeFlags};
use inline::{InlineFragmentNodeInfo, LAST_FRAGMENT_OF_ELEMENT};
use list_item::{ListItemFlow, ListStyleTypeContent};
-use multicol::MulticolFlow;
+use multicol::{MulticolFlow, MulticolColumnFlow};
use parallel;
use script::dom::bindings::inheritance::{CharacterDataTypeId, ElementTypeId};
use script::dom::bindings::inheritance::{HTMLElementTypeId, NodeTypeId};
@@ -754,12 +754,12 @@ impl<'a, 'ln, ConcreteThreadSafeLayoutNode: ThreadSafeLayoutNode<'ln>>
/// to happen.
fn build_flow_for_block(&mut self, node: &ConcreteThreadSafeLayoutNode, float_kind: Option<FloatKind>)
-> ConstructionResult {
- let fragment = self.build_fragment_for_block(node);
- let flow: FlowRef = if node.style().is_multicol() {
- Arc::new(MulticolFlow::from_fragment(fragment, float_kind))
- } else {
- Arc::new(BlockFlow::from_fragment(fragment, float_kind))
- };
+ if node.style().is_multicol() {
+ return self.build_flow_for_multicol(node, float_kind)
+ }
+
+ let flow: FlowRef = Arc::new(
+ BlockFlow::from_fragment(self.build_fragment_for_block(node), float_kind));
self.build_flow_for_block_like(flow, node)
}
@@ -1078,6 +1078,53 @@ impl<'a, 'ln, ConcreteThreadSafeLayoutNode: ThreadSafeLayoutNode<'ln>>
flow.add_new_child(anonymous_flow);
}
+ /// Builds a flow for a node with `column-count` or `column-width` non-`auto`.
+ /// This yields a `MulticolFlow` with a single `MulticolColumnFlow` underneath it.
+ fn build_flow_for_multicol(&mut self, node: &ConcreteThreadSafeLayoutNode,
+ float_kind: Option<FloatKind>)
+ -> ConstructionResult {
+ let fragment = Fragment::new(node, SpecificFragmentInfo::Multicol);
+ let mut flow: FlowRef = Arc::new(MulticolFlow::from_fragment(fragment, float_kind));
+
+ let column_fragment = Fragment::new(node, SpecificFragmentInfo::MulticolColumn);
+ let column_flow = Arc::new(MulticolColumnFlow::from_fragment(column_fragment));
+
+ // First populate the column flow with its children.
+ let construction_result = self.build_flow_for_block_like(column_flow, node);
+
+ let mut abs_descendants = AbsoluteDescendants::new();
+ let mut fixed_descendants = AbsoluteDescendants::new();
+
+ if let ConstructionResult::Flow(column_flow, column_abs_descendants) = construction_result {
+ flow.add_new_child(column_flow);
+ abs_descendants.push_descendants(column_abs_descendants);
+ }
+
+ // The flow is done.
+ flow.finish();
+ let contains_positioned_fragments = flow.contains_positioned_fragments();
+ if contains_positioned_fragments {
+ // This is the containing block for all the absolute descendants.
+ flow.set_absolute_descendants(abs_descendants);
+
+ abs_descendants = AbsoluteDescendants::new();
+
+ let is_fixed_positioned = flow.as_block().is_fixed();
+ let is_absolutely_positioned =
+ flow::base(&*flow).flags.contains(IS_ABSOLUTELY_POSITIONED);
+ if is_fixed_positioned {
+ // Send itself along with the other fixed descendants.
+ fixed_descendants.push(flow.clone());
+ } else if is_absolutely_positioned {
+ // This is now the only absolute flow in the subtree which hasn't yet
+ // reached its containing block.
+ abs_descendants.push(flow.clone());
+ }
+ }
+
+ ConstructionResult::Flow(flow, abs_descendants)
+ }
+
/// Builds a flow for a node with `display: table`. This yields a `TableWrapperFlow` with
/// possibly other `TableCaptionFlow`s or `TableFlow`s underneath it.
fn build_flow_for_table_wrapper(&mut self, node: &ConcreteThreadSafeLayoutNode, float_value: float::T)
@@ -1115,15 +1162,15 @@ impl<'a, 'ln, ConcreteThreadSafeLayoutNode: ThreadSafeLayoutNode<'ln>>
// The flow is done.
wrapper_flow.finish();
let contains_positioned_fragments = wrapper_flow.contains_positioned_fragments();
- let is_fixed_positioned = wrapper_flow.as_block().is_fixed();
- let is_absolutely_positioned =
- flow::base(&*wrapper_flow).flags.contains(IS_ABSOLUTELY_POSITIONED);
if contains_positioned_fragments {
// This is the containing block for all the absolute descendants.
wrapper_flow.set_absolute_descendants(abs_descendants);
abs_descendants = AbsoluteDescendants::new();
+ let is_fixed_positioned = wrapper_flow.as_block().is_fixed();
+ let is_absolutely_positioned =
+ flow::base(&*wrapper_flow).flags.contains(IS_ABSOLUTELY_POSITIONED);
if is_fixed_positioned {
// Send itself along with the other fixed descendants.
fixed_descendants.push(wrapper_flow.clone());
diff --git a/components/layout/display_list_builder.rs b/components/layout/display_list_builder.rs
index 82293921bbb..bab234d8b6b 100644
--- a/components/layout/display_list_builder.rs
+++ b/components/layout/display_list_builder.rs
@@ -1082,6 +1082,8 @@ impl FragmentDisplayListBuilding for Fragment {
SpecificFragmentInfo::TableCell |
SpecificFragmentInfo::TableRow |
SpecificFragmentInfo::TableWrapper |
+ SpecificFragmentInfo::Multicol |
+ SpecificFragmentInfo::MulticolColumn |
SpecificFragmentInfo::InlineBlock(_) |
SpecificFragmentInfo::InlineAbsoluteHypothetical(_) |
SpecificFragmentInfo::InlineAbsolute(_) => {
diff --git a/components/layout/flow.rs b/components/layout/flow.rs
index 4a8b206193a..215e25bd731 100644
--- a/components/layout/flow.rs
+++ b/components/layout/flow.rs
@@ -35,7 +35,7 @@ use flow_ref::{self, FlowRef, WeakFlowRef};
use fragment::{Fragment, FragmentBorderBoxIterator, SpecificFragmentInfo};
use gfx::display_list::{ClippingRegion, DisplayList};
use gfx_traits::{LayerId, LayerType};
-use incremental::{RECONSTRUCT_FLOW, REFLOW, REFLOW_OUT_OF_FLOW, RestyleDamage};
+use incremental::{RECONSTRUCT_FLOW, REFLOW, REFLOW_OUT_OF_FLOW, REPAINT, RestyleDamage};
use inline::InlineFlow;
use model::{CollapsibleMargins, IntrinsicISizes, MarginCollapseInfo};
use multicol::MulticolFlow;
@@ -203,22 +203,21 @@ pub trait Flow: fmt::Debug + Sync + Send + 'static {
/// Like `assign_block_size`, but is recurses explicitly into descendants.
/// Fit as much content as possible within `available_block_size`.
- /// If that’s not all of it,
- /// return an indication of where in the tree to break and start the next fragment.
- ///
- /// FIXME: replace `()` in the return value with something meaningful.
+ /// If that’s not all of it, truncate the contents of `self`
+ /// and return a new flow similar to `self` with the rest of the content.
///
/// The default is to make a flow "atomic": it can not be fragmented.
- fn fragment<'a>(&mut self, ctx: &'a LayoutContext<'a>, _available_block_size: Au)
- -> Option<()> {
- fn recursive_assign_block_size<'a, F: ?Sized + Flow>
- (flow: &mut F, ctx: &'a LayoutContext<'a>) {
+ fn fragment(&mut self,
+ layout_context: &LayoutContext,
+ _fragmentation_context: Option<FragmentationContext>)
+ -> Option<FlowRef> {
+ fn recursive_assign_block_size<F: ?Sized + Flow>(flow: &mut F, ctx: &LayoutContext) {
for child in mut_base(flow).children.iter_mut() {
recursive_assign_block_size(child, ctx)
}
flow.assign_block_size(ctx);
}
- recursive_assign_block_size(self, ctx);
+ recursive_assign_block_size(self, layout_context);
None
}
@@ -538,6 +537,7 @@ pub enum FlowClass {
TableCaption,
TableCell,
Multicol,
+ MulticolColumn,
Flex,
}
@@ -799,6 +799,7 @@ pub type AbsoluteDescendantOffsetIter<'a> = Zip<AbsoluteDescendantIter<'a>, Iter
/// Information needed to compute absolute (i.e. viewport-relative) flow positions (not to be
/// confused with absolutely-positioned flows) that is computed during block-size assignment.
+#[derive(Copy, Clone)]
pub struct EarlyAbsolutePositionInfo {
/// The size of the containing block for relatively-positioned descendants.
pub relative_containing_block_size: LogicalSize<Au>,
@@ -836,6 +837,12 @@ impl LateAbsolutePositionInfo {
}
}
+#[derive(Copy, Clone, Debug)]
+pub struct FragmentationContext {
+ pub available_block_size: Au,
+ pub this_fragment_is_empty: bool,
+}
+
/// Data common to all flows.
pub struct BaseFlow {
pub restyle_damage: RestyleDamage,
@@ -1092,6 +1099,23 @@ impl BaseFlow {
}
}
+ /// Return a new BaseFlow like this one but with the given children list
+ pub fn clone_with_children(&self, children: FlowList) -> BaseFlow {
+ BaseFlow {
+ children: children,
+ restyle_damage: self.restyle_damage | REPAINT | REFLOW_OUT_OF_FLOW | REFLOW,
+ parallel: FlowParallelInfo::new(),
+ display_list_building_result: None,
+
+ floats: self.floats.clone(),
+ abs_descendants: self.abs_descendants.clone(),
+ absolute_cb: self.absolute_cb.clone(),
+ clip: self.clip.clone(),
+
+ ..*self
+ }
+ }
+
pub fn child_iter(&mut self) -> MutFlowListIterator {
self.children.iter_mut()
}
@@ -1454,6 +1478,7 @@ impl MutableOwnedFlowUtils for FlowRef {
/// invalidation and use-after-free.
///
/// FIXME(pcwalton): I think this would be better with a borrow flag instead of `unsafe`.
+#[derive(Clone)]
pub struct ContainingBlockLink {
/// The pointer up to the containing block.
link: Option<WeakFlowRef>,
diff --git a/components/layout/flow_list.rs b/components/layout/flow_list.rs
index 94045f21d3d..37044008f7f 100644
--- a/components/layout/flow_list.rs
+++ b/components/layout/flow_list.rs
@@ -29,6 +29,17 @@ impl FlowList {
self.flows.push_back(new_tail);
}
+ /// Add an element first in the list
+ ///
+ /// O(1)
+ pub fn push_front(&mut self, new_head: FlowRef) {
+ self.flows.push_front(new_head);
+ }
+
+ pub fn pop_front(&mut self) -> Option<FlowRef> {
+ self.flows.pop_front()
+ }
+
/// Create an empty list
#[inline]
pub fn new() -> FlowList {
@@ -64,6 +75,13 @@ impl FlowList {
pub fn len(&self) -> usize {
self.flows.len()
}
+
+ #[inline]
+ pub fn split_off(&mut self, i: usize) -> Self {
+ FlowList {
+ flows: self.flows.split_off(i)
+ }
+ }
}
impl<'a> Iterator for FlowListIterator<'a> {
diff --git a/components/layout/fragment.rs b/components/layout/fragment.rs
index 004e8ce0543..9fa563ce20d 100644
--- a/components/layout/fragment.rs
+++ b/components/layout/fragment.rs
@@ -161,6 +161,8 @@ pub enum SpecificFragmentInfo {
TableColumn(TableColumnFragmentInfo),
TableRow,
TableWrapper,
+ Multicol,
+ MulticolColumn,
UnscannedText(UnscannedTextFragmentInfo),
}
@@ -178,6 +180,8 @@ impl SpecificFragmentInfo {
SpecificFragmentInfo::TableColumn(_) |
SpecificFragmentInfo::TableRow |
SpecificFragmentInfo::TableWrapper |
+ SpecificFragmentInfo::Multicol |
+ SpecificFragmentInfo::MulticolColumn |
SpecificFragmentInfo::UnscannedText(_) |
SpecificFragmentInfo::Generic => return RestyleDamage::empty(),
SpecificFragmentInfo::InlineAbsoluteHypothetical(ref info) => &info.flow_ref,
@@ -206,6 +210,8 @@ impl SpecificFragmentInfo {
SpecificFragmentInfo::TableColumn(_) => "SpecificFragmentInfo::TableColumn",
SpecificFragmentInfo::TableRow => "SpecificFragmentInfo::TableRow",
SpecificFragmentInfo::TableWrapper => "SpecificFragmentInfo::TableWrapper",
+ SpecificFragmentInfo::Multicol => "SpecificFragmentInfo::Multicol",
+ SpecificFragmentInfo::MulticolColumn => "SpecificFragmentInfo::MulticolColumn",
SpecificFragmentInfo::UnscannedText(_) => "SpecificFragmentInfo::UnscannedText",
}
}
@@ -912,7 +918,8 @@ impl Fragment {
SpecificFragmentInfo::GeneratedContent(_) |
SpecificFragmentInfo::Iframe(_) |
SpecificFragmentInfo::Image(_) |
- SpecificFragmentInfo::InlineAbsolute(_) => {
+ SpecificFragmentInfo::InlineAbsolute(_) |
+ SpecificFragmentInfo::Multicol => {
QuantitiesIncludedInIntrinsicInlineSizes::all()
}
SpecificFragmentInfo::Table | SpecificFragmentInfo::TableCell => {
@@ -948,7 +955,8 @@ impl Fragment {
SpecificFragmentInfo::TableColumn(_) |
SpecificFragmentInfo::UnscannedText(_) |
SpecificFragmentInfo::InlineAbsoluteHypothetical(_) |
- SpecificFragmentInfo::InlineBlock(_) => {
+ SpecificFragmentInfo::InlineBlock(_) |
+ SpecificFragmentInfo::MulticolColumn => {
QuantitiesIncludedInIntrinsicInlineSizes::empty()
}
}
@@ -1308,6 +1316,8 @@ impl Fragment {
SpecificFragmentInfo::TableColumn(_) |
SpecificFragmentInfo::TableRow |
SpecificFragmentInfo::TableWrapper |
+ SpecificFragmentInfo::Multicol |
+ SpecificFragmentInfo::MulticolColumn |
SpecificFragmentInfo::InlineAbsoluteHypothetical(_) => {}
SpecificFragmentInfo::InlineBlock(ref info) => {
let block_flow = info.flow_ref.as_block();
@@ -1409,6 +1419,8 @@ impl Fragment {
SpecificFragmentInfo::TableCell |
SpecificFragmentInfo::TableRow |
SpecificFragmentInfo::TableWrapper |
+ SpecificFragmentInfo::Multicol |
+ SpecificFragmentInfo::MulticolColumn |
SpecificFragmentInfo::InlineBlock(_) |
SpecificFragmentInfo::InlineAbsoluteHypothetical(_) |
SpecificFragmentInfo::InlineAbsolute(_) => Au(0),
@@ -1667,7 +1679,9 @@ impl Fragment {
SpecificFragmentInfo::Table |
SpecificFragmentInfo::TableCell |
SpecificFragmentInfo::TableRow |
- SpecificFragmentInfo::TableWrapper => return,
+ SpecificFragmentInfo::TableWrapper |
+ SpecificFragmentInfo::Multicol |
+ SpecificFragmentInfo::MulticolColumn => return,
SpecificFragmentInfo::TableColumn(_) => {
panic!("Table column fragments do not have inline size")
}
@@ -1759,7 +1773,9 @@ impl Fragment {
SpecificFragmentInfo::Table |
SpecificFragmentInfo::TableCell |
SpecificFragmentInfo::TableRow |
- SpecificFragmentInfo::TableWrapper => return,
+ SpecificFragmentInfo::TableWrapper |
+ SpecificFragmentInfo::Multicol |
+ SpecificFragmentInfo::MulticolColumn => return,
SpecificFragmentInfo::TableColumn(_) => {
panic!("Table column fragments do not have block size")
}
@@ -1977,6 +1993,7 @@ impl Fragment {
SpecificFragmentInfo::InlineBlock(_) |
SpecificFragmentInfo::InlineAbsoluteHypothetical(_) |
SpecificFragmentInfo::InlineAbsolute(_) |
+ SpecificFragmentInfo::MulticolColumn |
SpecificFragmentInfo::TableWrapper => false,
SpecificFragmentInfo::Canvas(_) |
SpecificFragmentInfo::Generic |
@@ -1988,6 +2005,7 @@ impl Fragment {
SpecificFragmentInfo::TableCell |
SpecificFragmentInfo::TableColumn(_) |
SpecificFragmentInfo::TableRow |
+ SpecificFragmentInfo::Multicol |
SpecificFragmentInfo::UnscannedText(_) => true,
}
}
diff --git a/components/layout/model.rs b/components/layout/model.rs
index f49ac51ad21..b32a9c48bf9 100644
--- a/components/layout/model.rs
+++ b/components/layout/model.rs
@@ -272,7 +272,7 @@ pub enum MarginCollapseState {
}
/// Intrinsic inline-sizes, which consist of minimum and preferred.
-#[derive(RustcEncodable)]
+#[derive(RustcEncodable, Copy, Clone)]
pub struct IntrinsicISizes {
/// The *minimum inline-size* of the content.
pub minimum_inline_size: Au,
diff --git a/components/layout/multicol.rs b/components/layout/multicol.rs
index 3ac17412a76..196a65715d6 100644
--- a/components/layout/multicol.rs
+++ b/components/layout/multicol.rs
@@ -11,23 +11,43 @@ use block::BlockFlow;
use context::LayoutContext;
use euclid::{Point2D, Rect};
use floats::FloatKind;
-use flow::{Flow, FlowClass, OpaqueFlow, mut_base};
+use flow::{Flow, FlowClass, OpaqueFlow, mut_base, FragmentationContext};
+use flow_ref::{self, FlowRef};
use fragment::{Fragment, FragmentBorderBoxIterator};
use std::cmp::{min, max};
use std::fmt;
use std::sync::Arc;
+use style::context::StyleContext;
use style::properties::ComputedValues;
+use style::values::computed::{LengthOrPercentageOrAuto, LengthOrPercentageOrNone};
use util::logical_geometry::LogicalSize;
use util::print_tree::PrintTree;
pub struct MulticolFlow {
pub block_flow: BlockFlow,
+
+ /// Length between the inline-start edge of a column and that of the next.
+ /// That is, the used column-width + used column-gap.
+ pub column_pitch: Au,
+}
+
+pub struct MulticolColumnFlow {
+ pub block_flow: BlockFlow,
}
impl MulticolFlow {
pub fn from_fragment(fragment: Fragment, float_kind: Option<FloatKind>) -> MulticolFlow {
MulticolFlow {
- block_flow: BlockFlow::from_fragment(fragment, float_kind)
+ block_flow: BlockFlow::from_fragment(fragment, float_kind),
+ column_pitch: Au(0),
+ }
+ }
+}
+
+impl MulticolColumnFlow {
+ pub fn from_fragment(fragment: Fragment) -> MulticolColumnFlow {
+ MulticolColumnFlow {
+ block_flow: BlockFlow::from_fragment(fragment, None),
}
}
}
@@ -37,10 +57,6 @@ impl Flow for MulticolFlow {
FlowClass::Multicol
}
- fn as_mut_multicol(&mut self) -> &mut MulticolFlow {
- self
- }
-
fn as_mut_block(&mut self) -> &mut BlockFlow {
&mut self.block_flow
}
@@ -49,6 +65,10 @@ impl Flow for MulticolFlow {
&self.block_flow
}
+ fn as_mut_multicol(&mut self) -> &mut MulticolFlow {
+ self
+ }
+
fn bubble_inline_sizes(&mut self) {
// FIXME(SimonSapin) http://dev.w3.org/csswg/css-sizing/#multicol-intrinsic
self.block_flow.bubble_inline_sizes();
@@ -90,6 +110,7 @@ impl Flow for MulticolFlow {
}
column_width =
max(Au(0), (content_inline_size + column_gap) / column_count - column_gap);
+ self.column_pitch = column_width + column_gap;
}
self.block_flow.fragment.border_box.size.inline = content_inline_size + padding_and_borders;
@@ -101,13 +122,127 @@ impl Flow for MulticolFlow {
fn assign_block_size<'a>(&mut self, ctx: &'a LayoutContext<'a>) {
debug!("assign_block_size: assigning block_size for multicol");
- let available_block_size = Au(0);
- for child in mut_base(self).children.iter_mut() {
- child.fragment(ctx, available_block_size);
+
+ let fragmentation_context = Some(FragmentationContext {
+ this_fragment_is_empty: true,
+ available_block_size: {
+ let style = &self.block_flow.fragment.style;
+ if let LengthOrPercentageOrAuto::Length(length) = style.content_block_size() {
+ length
+ } else if let LengthOrPercentageOrNone::Length(length) = style.max_block_size() {
+ length
+ } else {
+ // FIXME: do column balancing instead
+ // FIXME: (until column balancing) substract margins/borders/padding
+ LogicalSize::from_physical(
+ self.block_flow.base.writing_mode,
+ ctx.shared_context().viewport_size,
+ ).block
+ }
+ }
+ });
+
+ // Before layout, everything is in a single "column"
+ assert!(self.block_flow.base.children.len() == 1);
+ let mut column = self.block_flow.base.children.pop_front().unwrap();
+
+ // Pretend there is no children for this:
+ self.block_flow.assign_block_size(ctx);
+
+ loop {
+ let remaining = flow_ref::deref_mut(&mut column).fragment(ctx, fragmentation_context);
+ self.block_flow.base.children.push_back(column);
+ column = match remaining {
+ Some(remaining) => remaining,
+ None => break
+ };
}
+ }
+
+ fn compute_absolute_position(&mut self, layout_context: &LayoutContext) {
+ self.block_flow.compute_absolute_position(layout_context);
+ let pitch = LogicalSize::new(self.block_flow.base.writing_mode, self.column_pitch, Au(0));
+ let pitch = pitch.to_physical(self.block_flow.base.writing_mode);
+ for (i, child) in self.block_flow.base.children.iter_mut().enumerate() {
+ let point = &mut mut_base(child).stacking_relative_position;
+ *point = *point + pitch * i as i32;
+ }
+ }
+
+ fn update_late_computed_inline_position_if_necessary(&mut self, inline_position: Au) {
+ self.block_flow.update_late_computed_inline_position_if_necessary(inline_position)
+ }
+
+ fn update_late_computed_block_position_if_necessary(&mut self, block_position: Au) {
+ self.block_flow.update_late_computed_block_position_if_necessary(block_position)
+ }
+
+ fn build_display_list(&mut self, layout_context: &LayoutContext) {
+ debug!("build_display_list_multicol");
+ self.block_flow.build_display_list(layout_context);
+ }
+
+ fn repair_style(&mut self, new_style: &Arc<ComputedValues>) {
+ self.block_flow.repair_style(new_style)
+ }
+
+ fn compute_overflow(&self) -> Rect<Au> {
+ self.block_flow.compute_overflow()
+ }
+
+ fn generated_containing_block_size(&self, flow: OpaqueFlow) -> LogicalSize<Au> {
+ self.block_flow.generated_containing_block_size(flow)
+ }
+
+ fn iterate_through_fragment_border_boxes(&self,
+ iterator: &mut FragmentBorderBoxIterator,
+ level: i32,
+ stacking_context_position: &Point2D<Au>) {
+ self.block_flow.iterate_through_fragment_border_boxes(iterator, level, stacking_context_position);
+ }
+
+ fn mutate_fragments(&mut self, mutator: &mut FnMut(&mut Fragment)) {
+ self.block_flow.mutate_fragments(mutator);
+ }
+
+ fn print_extra_flow_children(&self, print_tree: &mut PrintTree) {
+ self.block_flow.print_extra_flow_children(print_tree);
+ }
+}
+
+impl Flow for MulticolColumnFlow {
+ fn class(&self) -> FlowClass {
+ FlowClass::MulticolColumn
+ }
+
+ fn as_mut_block(&mut self) -> &mut BlockFlow {
+ &mut self.block_flow
+ }
+
+ fn as_block(&self) -> &BlockFlow {
+ &self.block_flow
+ }
+
+ fn bubble_inline_sizes(&mut self) {
+ self.block_flow.bubble_inline_sizes();
+ }
+
+ fn assign_inline_sizes(&mut self, layout_context: &LayoutContext) {
+ debug!("assign_inline_sizes({}): assigning inline_size for flow", "multicol column");
+ self.block_flow.assign_inline_sizes(layout_context);
+ }
+
+ fn assign_block_size<'a>(&mut self, ctx: &'a LayoutContext<'a>) {
+ debug!("assign_block_size: assigning block_size for multicol column");
self.block_flow.assign_block_size(ctx);
}
+ fn fragment(&mut self, layout_context: &LayoutContext,
+ fragmentation_context: Option<FragmentationContext>)
+ -> Option<FlowRef> {
+ Flow::fragment(&mut self.block_flow, layout_context, fragmentation_context)
+ }
+
fn compute_absolute_position(&mut self, layout_context: &LayoutContext) {
self.block_flow.compute_absolute_position(layout_context)
}
@@ -121,8 +256,8 @@ impl Flow for MulticolFlow {
}
fn build_display_list(&mut self, layout_context: &LayoutContext) {
- debug!("build_display_list_multicol: same process as block flow");
- self.block_flow.build_display_list(layout_context)
+ debug!("build_display_list_multicol column");
+ self.block_flow.build_display_list(layout_context);
}
fn repair_style(&mut self, new_style: &Arc<ComputedValues>) {
@@ -141,11 +276,11 @@ impl Flow for MulticolFlow {
iterator: &mut FragmentBorderBoxIterator,
level: i32,
stacking_context_position: &Point2D<Au>) {
- self.block_flow.iterate_through_fragment_border_boxes(iterator, level, stacking_context_position)
+ self.block_flow.iterate_through_fragment_border_boxes(iterator, level, stacking_context_position);
}
fn mutate_fragments(&mut self, mutator: &mut FnMut(&mut Fragment)) {
- self.block_flow.mutate_fragments(mutator)
+ self.block_flow.mutate_fragments(mutator);
}
fn print_extra_flow_children(&self, print_tree: &mut PrintTree) {
@@ -158,3 +293,9 @@ impl fmt::Debug for MulticolFlow {
write!(f, "MulticolFlow: {:?}", self.block_flow)
}
}
+
+impl fmt::Debug for MulticolColumnFlow {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(f, "MulticolColumnFlow: {:?}", self.block_flow)
+ }
+}
diff --git a/components/layout/table_cell.rs b/components/layout/table_cell.rs
index ad6d2ca1804..cfd66e26bef 100644
--- a/components/layout/table_cell.rs
+++ b/components/layout/table_cell.rs
@@ -69,9 +69,11 @@ impl TableCellFlow {
/// methods.
#[inline(always)]
fn assign_block_size_table_cell_base<'a>(&mut self, layout_context: &'a LayoutContext<'a>) {
- self.block_flow.assign_block_size_block_base(
+ let remaining = self.block_flow.assign_block_size_block_base(
layout_context,
- MarginsMayCollapseFlag::MarginsMayNotCollapse)
+ None,
+ MarginsMayCollapseFlag::MarginsMayNotCollapse);
+ debug_assert!(remaining.is_none());
}
}
diff --git a/components/layout/table_wrapper.rs b/components/layout/table_wrapper.rs
index b41ee8ed119..02cc1b768fb 100644
--- a/components/layout/table_wrapper.rs
+++ b/components/layout/table_wrapper.rs
@@ -408,9 +408,11 @@ impl Flow for TableWrapperFlow {
fn assign_block_size<'a>(&mut self, layout_context: &'a LayoutContext<'a>) {
debug!("assign_block_size: assigning block_size for table_wrapper");
- self.block_flow
- .assign_block_size_block_base(layout_context,
- MarginsMayCollapseFlag::MarginsMayNotCollapse);
+ let remaining = self.block_flow.assign_block_size_block_base(
+ layout_context,
+ None,
+ MarginsMayCollapseFlag::MarginsMayNotCollapse);
+ debug_assert!(remaining.is_none());
}
fn compute_absolute_position(&mut self, layout_context: &LayoutContext) {