mirror of
https://github.com/servo/servo.git
synced 2025-08-08 06:55:31 +01:00
Auto merge of #9170 - SimonSapin:multicol2, r=mbrubeck
Add CSS Multicolumn support with block fragmentation  Includes/supersedes #8763. r? @mbrubeck <!-- Reviewable:start --> [<img src="https://reviewable.io/review_button.png" height=40 alt="Review on Reviewable"/>](https://reviewable.io/reviews/servo/servo/9170) <!-- Reviewable:end -->
This commit is contained in:
commit
5e1f0495a9
17 changed files with 534 additions and 91 deletions
|
@ -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::{IMPACTED_BY_LEFT_FLOATS, IMPACTED_BY_RIGHT_FLOATS, INLINE_POSITION_IS_STATIC};
|
||||||
use flow::{IS_ABSOLUTELY_POSITIONED};
|
use flow::{IS_ABSOLUTELY_POSITIONED};
|
||||||
use flow::{ImmutableFlowUtils, LateAbsolutePositionInfo, MutableFlowUtils, OpaqueFlow};
|
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::{self, BaseFlow, EarlyAbsolutePositionInfo, Flow, FlowClass, ForceNonfloatedFlag};
|
||||||
|
use flow_list::FlowList;
|
||||||
|
use flow_ref::FlowRef;
|
||||||
use fragment::{CoordinateSystem, Fragment, FragmentBorderBoxIterator, HAS_LAYER};
|
use fragment::{CoordinateSystem, Fragment, FragmentBorderBoxIterator, HAS_LAYER};
|
||||||
use fragment::{SpecificFragmentInfo};
|
use fragment::{SpecificFragmentInfo};
|
||||||
use gfx::display_list::{ClippingRegion, DisplayList};
|
use gfx::display_list::{ClippingRegion, DisplayList};
|
||||||
|
@ -726,6 +728,18 @@ impl BlockFlow {
|
||||||
block_end_margin_value;
|
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.
|
/// Assign block-size for current flow.
|
||||||
///
|
///
|
||||||
/// * Collapse margins for flow's children and set in-flow child flows' block offsets now that
|
/// * Collapse margins for flow's children and set in-flow child flows' block offsets now that
|
||||||
|
@ -737,15 +751,26 @@ impl BlockFlow {
|
||||||
/// For absolute flows, we store the calculated content block-size for the flow. We defer the
|
/// For absolute flows, we store the calculated content block-size for the flow. We defer the
|
||||||
/// calculation of the other values until a later traversal.
|
/// calculation of the other values until a later traversal.
|
||||||
///
|
///
|
||||||
|
/// When `fragmentation_context` is given (not `None`), this should fit as much of the content
|
||||||
|
/// as possible within the available block size.
|
||||||
|
/// If there is more content (that doesn’t fit), this flow is *fragmented*
|
||||||
|
/// with the extra content moved to another fragment (a flow like this one) which is returrned.
|
||||||
|
/// See `Flow::fragment`.
|
||||||
|
///
|
||||||
|
/// The return value is always `None` when `fragmentation_context` is `None`.
|
||||||
|
///
|
||||||
/// `inline(always)` because this is only ever called by in-order or non-in-order top-level
|
/// `inline(always)` because this is only ever called by in-order or non-in-order top-level
|
||||||
/// methods.
|
/// methods.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn assign_block_size_block_base<'a>(&mut self,
|
pub fn assign_block_size_block_base<'a>(&mut self,
|
||||||
layout_context: &'a LayoutContext<'a>,
|
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}",
|
let _scope = layout_debug_scope!("assign_block_size_block_base {:x}",
|
||||||
self.base.debug_id());
|
self.base.debug_id());
|
||||||
|
|
||||||
|
let mut break_at = None;
|
||||||
if self.base.restyle_damage.contains(REFLOW) {
|
if self.base.restyle_damage.contains(REFLOW) {
|
||||||
self.determine_if_layer_needed();
|
self.determine_if_layer_needed();
|
||||||
|
|
||||||
|
@ -779,7 +804,7 @@ impl BlockFlow {
|
||||||
// At this point, `cur_b` is at the content edge of our box. Now iterate over children.
|
// 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 mut floats = self.base.floats.clone();
|
||||||
let thread_id = self.base.thread_id;
|
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) {
|
if flow::base(kid).flags.contains(IS_ABSOLUTELY_POSITIONED) {
|
||||||
// Assume that the *hypothetical box* for an absolute flow starts immediately
|
// Assume that the *hypothetical box* for an absolute flow starts immediately
|
||||||
// after the block-end border edge of the previous flow.
|
// after the block-end border edge of the previous flow.
|
||||||
|
@ -799,6 +824,17 @@ impl BlockFlow {
|
||||||
continue
|
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
|
// Assign block-size now for the child if it was impacted by floats and we couldn't
|
||||||
// before.
|
// before.
|
||||||
flow::mut_base(kid).floats = floats.clone();
|
flow::mut_base(kid).floats = floats.clone();
|
||||||
|
@ -867,6 +903,19 @@ impl BlockFlow {
|
||||||
let delta =
|
let delta =
|
||||||
margin_collapse_info.advance_block_end_margin(&kid_base.collapsible_margins);
|
margin_collapse_info.advance_block_end_margin(&kid_base.collapsible_margins);
|
||||||
translate_including_floats(&mut cur_b, delta, &mut floats);
|
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.
|
// Add in our block-end margin and compute our collapsible margins.
|
||||||
|
@ -920,7 +969,7 @@ impl BlockFlow {
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.base.flags.contains(IS_ABSOLUTELY_POSITIONED) {
|
if self.base.flags.contains(IS_ABSOLUTELY_POSITIONED) {
|
||||||
return
|
return None
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compute any explicitly-specified block size.
|
// Compute any explicitly-specified block size.
|
||||||
|
@ -985,6 +1034,18 @@ impl BlockFlow {
|
||||||
self.base.restyle_damage.remove(REFLOW_OUT_OF_FLOW | REFLOW);
|
self.base.restyle_damage.remove(REFLOW_OUT_OF_FLOW | REFLOW);
|
||||||
self.fragment.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.
|
/// Add placement information about current float flow for use by the parent.
|
||||||
|
@ -1549,46 +1610,8 @@ impl BlockFlow {
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl Flow for BlockFlow {
|
|
||||||
fn class(&self) -> FlowClass {
|
|
||||||
FlowClass::Block
|
|
||||||
}
|
|
||||||
|
|
||||||
fn as_mut_block(&mut self) -> &mut BlockFlow {
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
fn as_block(&self) -> &BlockFlow {
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Pass 1 of reflow: computes minimum and preferred inline-sizes.
|
|
||||||
///
|
|
||||||
/// Recursively (bottom-up) determine the flow's minimum and preferred inline-sizes. When
|
|
||||||
/// called on this flow, all child flows have had their minimum and preferred inline-sizes set.
|
|
||||||
/// This function must decide minimum/preferred inline-sizes based on its children's
|
|
||||||
/// inline-sizes and the dimensions of any fragments it is responsible for flowing.
|
|
||||||
fn bubble_inline_sizes(&mut self) {
|
|
||||||
// If this block has a fixed width, just use that for the minimum and preferred width,
|
|
||||||
// rather than bubbling up children inline width.
|
|
||||||
let consult_children = match self.fragment.style().get_box().width {
|
|
||||||
LengthOrPercentageOrAuto::Length(_) => false,
|
|
||||||
_ => true,
|
|
||||||
};
|
|
||||||
self.bubble_inline_sizes_for_block(consult_children);
|
|
||||||
self.fragment.restyle_damage.remove(BUBBLE_ISIZES);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Recursively (top-down) determines the actual inline-size of child contexts and fragments.
|
|
||||||
/// When called on this context, the context has had its inline-size set by the parent context.
|
|
||||||
///
|
|
||||||
/// Dual fragments consume some inline-size first, and the remainder is assigned to all child
|
|
||||||
/// (block) contexts.
|
|
||||||
fn assign_inline_sizes(&mut self, layout_context: &LayoutContext) {
|
|
||||||
let _scope = layout_debug_scope!("block::assign_inline_sizes {:x}", self.base.debug_id());
|
|
||||||
|
|
||||||
|
pub fn compute_inline_sizes(&mut self, layout_context: &LayoutContext) {
|
||||||
if !self.base.restyle_damage.intersects(REFLOW_OUT_OF_FLOW | REFLOW) {
|
if !self.base.restyle_damage.intersects(REFLOW_OUT_OF_FLOW | REFLOW) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -1644,6 +1667,49 @@ impl Flow for BlockFlow {
|
||||||
self.base.flags.remove(IMPACTED_BY_RIGHT_FLOATS);
|
self.base.flags.remove(IMPACTED_BY_RIGHT_FLOATS);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Flow for BlockFlow {
|
||||||
|
fn class(&self) -> FlowClass {
|
||||||
|
FlowClass::Block
|
||||||
|
}
|
||||||
|
|
||||||
|
fn as_mut_block(&mut self) -> &mut BlockFlow {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
fn as_block(&self) -> &BlockFlow {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Pass 1 of reflow: computes minimum and preferred inline-sizes.
|
||||||
|
///
|
||||||
|
/// Recursively (bottom-up) determine the flow's minimum and preferred inline-sizes. When
|
||||||
|
/// called on this flow, all child flows have had their minimum and preferred inline-sizes set.
|
||||||
|
/// This function must decide minimum/preferred inline-sizes based on its children's
|
||||||
|
/// inline-sizes and the dimensions of any fragments it is responsible for flowing.
|
||||||
|
fn bubble_inline_sizes(&mut self) {
|
||||||
|
// If this block has a fixed width, just use that for the minimum and preferred width,
|
||||||
|
// rather than bubbling up children inline width.
|
||||||
|
let consult_children = match self.fragment.style().get_box().width {
|
||||||
|
LengthOrPercentageOrAuto::Length(_) => false,
|
||||||
|
_ => true,
|
||||||
|
};
|
||||||
|
self.bubble_inline_sizes_for_block(consult_children);
|
||||||
|
self.fragment.restyle_damage.remove(BUBBLE_ISIZES);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Recursively (top-down) determines the actual inline-size of child contexts and fragments.
|
||||||
|
/// When called on this context, the context has had its inline-size set by the parent context.
|
||||||
|
///
|
||||||
|
/// Dual fragments consume some inline-size first, and the remainder is assigned to all child
|
||||||
|
/// (block) contexts.
|
||||||
|
fn assign_inline_sizes(&mut self, layout_context: &LayoutContext) {
|
||||||
|
let _scope = layout_debug_scope!("block::assign_inline_sizes {:x}", self.base.debug_id());
|
||||||
|
|
||||||
|
self.compute_inline_sizes(layout_context);
|
||||||
|
|
||||||
// Move in from the inline-start border edge.
|
// Move in from the inline-start border edge.
|
||||||
let inline_start_content_edge = self.fragment.border_box.start.i +
|
let inline_start_content_edge = self.fragment.border_box.start.i +
|
||||||
self.fragment.border_padding.inline_start;
|
self.fragment.border_padding.inline_start;
|
||||||
|
@ -1706,6 +1772,13 @@ impl Flow for BlockFlow {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn assign_block_size<'a>(&mut self, ctx: &'a LayoutContext<'a>) {
|
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() {
|
if self.is_replaced_content() {
|
||||||
let _scope = layout_debug_scope!("assign_replaced_block_size_if_necessary {:x}",
|
let _scope = layout_debug_scope!("assign_replaced_block_size_if_necessary {:x}",
|
||||||
self.base.debug_id());
|
self.base.debug_id());
|
||||||
|
@ -1717,15 +1790,22 @@ impl Flow for BlockFlow {
|
||||||
if !self.base.flags.contains(IS_ABSOLUTELY_POSITIONED) {
|
if !self.base.flags.contains(IS_ABSOLUTELY_POSITIONED) {
|
||||||
self.base.position.size.block = self.fragment.border_box.size.block;
|
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() {
|
} 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.
|
// Root element margins should never be collapsed according to CSS § 8.3.1.
|
||||||
debug!("assign_block_size: assigning block_size for root flow {:?}",
|
debug!("assign_block_size: assigning block_size for root flow {:?}",
|
||||||
flow::base(self).debug_id());
|
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 {
|
} else {
|
||||||
debug!("assign_block_size: assigning block_size for block {:?}",
|
debug!("assign_block_size: assigning block_size for block {:?}",
|
||||||
flow::base(self).debug_id());
|
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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -18,7 +18,7 @@ use context::LayoutContext;
|
||||||
use data::{HAS_NEWLY_CONSTRUCTED_FLOW, PrivateLayoutData};
|
use data::{HAS_NEWLY_CONSTRUCTED_FLOW, PrivateLayoutData};
|
||||||
use flex::FlexFlow;
|
use flex::FlexFlow;
|
||||||
use floats::FloatKind;
|
use floats::FloatKind;
|
||||||
use flow::{MutableFlowUtils, MutableOwnedFlowUtils};
|
use flow::{MutableFlowUtils, MutableOwnedFlowUtils, CAN_BE_FRAGMENTED};
|
||||||
use flow::{self, AbsoluteDescendants, Flow, IS_ABSOLUTELY_POSITIONED, ImmutableFlowUtils};
|
use flow::{self, AbsoluteDescendants, Flow, IS_ABSOLUTELY_POSITIONED, ImmutableFlowUtils};
|
||||||
use flow_ref::{self, FlowRef};
|
use flow_ref::{self, FlowRef};
|
||||||
use fragment::{CanvasFragmentInfo, ImageFragmentInfo, InlineAbsoluteFragmentInfo};
|
use fragment::{CanvasFragmentInfo, ImageFragmentInfo, InlineAbsoluteFragmentInfo};
|
||||||
|
@ -31,7 +31,7 @@ use incremental::{BUBBLE_ISIZES, RECONSTRUCT_FLOW, RestyleDamage};
|
||||||
use inline::{FIRST_FRAGMENT_OF_ELEMENT, InlineFlow, InlineFragmentNodeFlags};
|
use inline::{FIRST_FRAGMENT_OF_ELEMENT, InlineFlow, InlineFragmentNodeFlags};
|
||||||
use inline::{InlineFragmentNodeInfo, LAST_FRAGMENT_OF_ELEMENT};
|
use inline::{InlineFragmentNodeInfo, LAST_FRAGMENT_OF_ELEMENT};
|
||||||
use list_item::{ListItemFlow, ListStyleTypeContent};
|
use list_item::{ListItemFlow, ListStyleTypeContent};
|
||||||
use multicol::MulticolFlow;
|
use multicol::{MulticolFlow, MulticolColumnFlow};
|
||||||
use parallel;
|
use parallel;
|
||||||
use script::dom::bindings::inheritance::{CharacterDataTypeId, ElementTypeId};
|
use script::dom::bindings::inheritance::{CharacterDataTypeId, ElementTypeId};
|
||||||
use script::dom::bindings::inheritance::{HTMLElementTypeId, NodeTypeId};
|
use script::dom::bindings::inheritance::{HTMLElementTypeId, NodeTypeId};
|
||||||
|
@ -754,12 +754,12 @@ impl<'a, 'ln, ConcreteThreadSafeLayoutNode: ThreadSafeLayoutNode<'ln>>
|
||||||
/// to happen.
|
/// to happen.
|
||||||
fn build_flow_for_block(&mut self, node: &ConcreteThreadSafeLayoutNode, float_kind: Option<FloatKind>)
|
fn build_flow_for_block(&mut self, node: &ConcreteThreadSafeLayoutNode, float_kind: Option<FloatKind>)
|
||||||
-> ConstructionResult {
|
-> ConstructionResult {
|
||||||
let fragment = self.build_fragment_for_block(node);
|
if node.style().is_multicol() {
|
||||||
let flow: FlowRef = if node.style().is_multicol() {
|
return self.build_flow_for_multicol(node, float_kind)
|
||||||
Arc::new(MulticolFlow::from_fragment(fragment, float_kind))
|
}
|
||||||
} else {
|
|
||||||
Arc::new(BlockFlow::from_fragment(fragment, 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)
|
self.build_flow_for_block_like(flow, node)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1078,6 +1078,48 @@ impl<'a, 'ln, ConcreteThreadSafeLayoutNode: ThreadSafeLayoutNode<'ln>>
|
||||||
flow.add_new_child(anonymous_flow);
|
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();
|
||||||
|
|
||||||
|
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_absolutely_positioned =
|
||||||
|
flow::base(&*flow).flags.contains(IS_ABSOLUTELY_POSITIONED);
|
||||||
|
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
|
/// Builds a flow for a node with `display: table`. This yields a `TableWrapperFlow` with
|
||||||
/// possibly other `TableCaptionFlow`s or `TableFlow`s underneath it.
|
/// possibly other `TableCaptionFlow`s or `TableFlow`s underneath it.
|
||||||
fn build_flow_for_table_wrapper(&mut self, node: &ConcreteThreadSafeLayoutNode, float_value: float::T)
|
fn build_flow_for_table_wrapper(&mut self, node: &ConcreteThreadSafeLayoutNode, float_value: float::T)
|
||||||
|
@ -1093,7 +1135,6 @@ impl<'a, 'ln, ConcreteThreadSafeLayoutNode: ThreadSafeLayoutNode<'ln>>
|
||||||
let construction_result = self.build_flow_for_block_like(table_flow, node);
|
let construction_result = self.build_flow_for_block_like(table_flow, node);
|
||||||
|
|
||||||
let mut abs_descendants = AbsoluteDescendants::new();
|
let mut abs_descendants = AbsoluteDescendants::new();
|
||||||
let mut fixed_descendants = AbsoluteDescendants::new();
|
|
||||||
|
|
||||||
// The order of the caption and the table are not necessarily the same order as in the DOM
|
// The order of the caption and the table are not necessarily the same order as in the DOM
|
||||||
// tree. All caption blocks are placed before or after the table flow, depending on the
|
// tree. All caption blocks are placed before or after the table flow, depending on the
|
||||||
|
@ -1115,19 +1156,15 @@ impl<'a, 'ln, ConcreteThreadSafeLayoutNode: ThreadSafeLayoutNode<'ln>>
|
||||||
// The flow is done.
|
// The flow is done.
|
||||||
wrapper_flow.finish();
|
wrapper_flow.finish();
|
||||||
let contains_positioned_fragments = wrapper_flow.contains_positioned_fragments();
|
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 {
|
if contains_positioned_fragments {
|
||||||
// This is the containing block for all the absolute descendants.
|
// This is the containing block for all the absolute descendants.
|
||||||
wrapper_flow.set_absolute_descendants(abs_descendants);
|
wrapper_flow.set_absolute_descendants(abs_descendants);
|
||||||
|
|
||||||
abs_descendants = AbsoluteDescendants::new();
|
abs_descendants = AbsoluteDescendants::new();
|
||||||
|
|
||||||
if is_fixed_positioned {
|
let is_absolutely_positioned =
|
||||||
// Send itself along with the other fixed descendants.
|
flow::base(&*wrapper_flow).flags.contains(IS_ABSOLUTELY_POSITIONED);
|
||||||
fixed_descendants.push(wrapper_flow.clone());
|
if is_absolutely_positioned {
|
||||||
} else if is_absolutely_positioned {
|
|
||||||
// This is now the only absolute flow in the subtree which hasn't yet
|
// This is now the only absolute flow in the subtree which hasn't yet
|
||||||
// reached its containing block.
|
// reached its containing block.
|
||||||
abs_descendants.push(wrapper_flow.clone());
|
abs_descendants.push(wrapper_flow.clone());
|
||||||
|
@ -1318,6 +1355,9 @@ impl<'a, 'ln, ConcreteThreadSafeLayoutNode: ThreadSafeLayoutNode<'ln>>
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if node.can_be_fragmented() || node.style().is_multicol() {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
let mut style = node.style().clone();
|
let mut style = node.style().clone();
|
||||||
let mut data = node.mutate_layout_data().unwrap();
|
let mut data = node.mutate_layout_data().unwrap();
|
||||||
|
@ -1613,7 +1653,13 @@ impl<'ln, ConcreteThreadSafeLayoutNode> NodeUtils for ConcreteThreadSafeLayoutNo
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn set_flow_construction_result(self, result: ConstructionResult) {
|
fn set_flow_construction_result(self, mut result: ConstructionResult) {
|
||||||
|
if self.can_be_fragmented() {
|
||||||
|
if let ConstructionResult::Flow(ref mut flow, _) = result {
|
||||||
|
flow::mut_base(flow_ref::deref_mut(flow)).flags.insert(CAN_BE_FRAGMENTED);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let mut layout_data = self.mutate_layout_data().unwrap();
|
let mut layout_data = self.mutate_layout_data().unwrap();
|
||||||
let dst = self.construction_result_mut(&mut *layout_data);
|
let dst = self.construction_result_mut(&mut *layout_data);
|
||||||
|
|
||||||
|
|
|
@ -1099,6 +1099,8 @@ impl FragmentDisplayListBuilding for Fragment {
|
||||||
SpecificFragmentInfo::TableCell |
|
SpecificFragmentInfo::TableCell |
|
||||||
SpecificFragmentInfo::TableRow |
|
SpecificFragmentInfo::TableRow |
|
||||||
SpecificFragmentInfo::TableWrapper |
|
SpecificFragmentInfo::TableWrapper |
|
||||||
|
SpecificFragmentInfo::Multicol |
|
||||||
|
SpecificFragmentInfo::MulticolColumn |
|
||||||
SpecificFragmentInfo::InlineBlock(_) |
|
SpecificFragmentInfo::InlineBlock(_) |
|
||||||
SpecificFragmentInfo::InlineAbsoluteHypothetical(_) |
|
SpecificFragmentInfo::InlineAbsoluteHypothetical(_) |
|
||||||
SpecificFragmentInfo::InlineAbsolute(_) => {
|
SpecificFragmentInfo::InlineAbsolute(_) => {
|
||||||
|
|
|
@ -35,7 +35,7 @@ use flow_ref::{self, FlowRef, WeakFlowRef};
|
||||||
use fragment::{Fragment, FragmentBorderBoxIterator, SpecificFragmentInfo};
|
use fragment::{Fragment, FragmentBorderBoxIterator, SpecificFragmentInfo};
|
||||||
use gfx::display_list::{ClippingRegion, DisplayList};
|
use gfx::display_list::{ClippingRegion, DisplayList};
|
||||||
use gfx_traits::{LayerId, LayerType};
|
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 inline::InlineFlow;
|
||||||
use model::{CollapsibleMargins, IntrinsicISizes, MarginCollapseInfo};
|
use model::{CollapsibleMargins, IntrinsicISizes, MarginCollapseInfo};
|
||||||
use multicol::MulticolFlow;
|
use multicol::MulticolFlow;
|
||||||
|
@ -201,6 +201,26 @@ pub trait Flow: fmt::Debug + Sync + Send + 'static {
|
||||||
panic!("assign_block_size not yet implemented")
|
panic!("assign_block_size not yet implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 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, 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(&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, layout_context);
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
/// If this is a float, places it. The default implementation does nothing.
|
/// If this is a float, places it. The default implementation does nothing.
|
||||||
fn place_float_if_applicable<'a>(&mut self, _: &'a LayoutContext<'a>) {}
|
fn place_float_if_applicable<'a>(&mut self, _: &'a LayoutContext<'a>) {}
|
||||||
|
|
||||||
|
@ -517,6 +537,7 @@ pub enum FlowClass {
|
||||||
TableCaption,
|
TableCaption,
|
||||||
TableCell,
|
TableCell,
|
||||||
Multicol,
|
Multicol,
|
||||||
|
MulticolColumn,
|
||||||
Flex,
|
Flex,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -614,6 +635,9 @@ bitflags! {
|
||||||
static` and `position: relative` as well as absolutely-positioned flows with \
|
static` and `position: relative` as well as absolutely-positioned flows with \
|
||||||
unconstrained positions in the block direction."]
|
unconstrained positions in the block direction."]
|
||||||
const BLOCK_POSITION_IS_STATIC = 0b0100_0000_0000_0000_0000,
|
const BLOCK_POSITION_IS_STATIC = 0b0100_0000_0000_0000_0000,
|
||||||
|
|
||||||
|
/// Whether any ancestor is a fragmentation container
|
||||||
|
const CAN_BE_FRAGMENTED = 0b1000_0000_0000_0000_0000,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -775,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
|
/// 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.
|
/// confused with absolutely-positioned flows) that is computed during block-size assignment.
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
pub struct EarlyAbsolutePositionInfo {
|
pub struct EarlyAbsolutePositionInfo {
|
||||||
/// The size of the containing block for relatively-positioned descendants.
|
/// The size of the containing block for relatively-positioned descendants.
|
||||||
pub relative_containing_block_size: LogicalSize<Au>,
|
pub relative_containing_block_size: LogicalSize<Au>,
|
||||||
|
@ -812,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.
|
/// Data common to all flows.
|
||||||
pub struct BaseFlow {
|
pub struct BaseFlow {
|
||||||
pub restyle_damage: RestyleDamage,
|
pub restyle_damage: RestyleDamage,
|
||||||
|
@ -1068,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 {
|
pub fn child_iter(&mut self) -> MutFlowListIterator {
|
||||||
self.children.iter_mut()
|
self.children.iter_mut()
|
||||||
}
|
}
|
||||||
|
@ -1430,6 +1478,7 @@ impl MutableOwnedFlowUtils for FlowRef {
|
||||||
/// invalidation and use-after-free.
|
/// invalidation and use-after-free.
|
||||||
///
|
///
|
||||||
/// FIXME(pcwalton): I think this would be better with a borrow flag instead of `unsafe`.
|
/// FIXME(pcwalton): I think this would be better with a borrow flag instead of `unsafe`.
|
||||||
|
#[derive(Clone)]
|
||||||
pub struct ContainingBlockLink {
|
pub struct ContainingBlockLink {
|
||||||
/// The pointer up to the containing block.
|
/// The pointer up to the containing block.
|
||||||
link: Option<WeakFlowRef>,
|
link: Option<WeakFlowRef>,
|
||||||
|
|
|
@ -29,6 +29,17 @@ impl FlowList {
|
||||||
self.flows.push_back(new_tail);
|
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
|
/// Create an empty list
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn new() -> FlowList {
|
pub fn new() -> FlowList {
|
||||||
|
@ -64,6 +75,13 @@ impl FlowList {
|
||||||
pub fn len(&self) -> usize {
|
pub fn len(&self) -> usize {
|
||||||
self.flows.len()
|
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> {
|
impl<'a> Iterator for FlowListIterator<'a> {
|
||||||
|
|
|
@ -161,6 +161,8 @@ pub enum SpecificFragmentInfo {
|
||||||
TableColumn(TableColumnFragmentInfo),
|
TableColumn(TableColumnFragmentInfo),
|
||||||
TableRow,
|
TableRow,
|
||||||
TableWrapper,
|
TableWrapper,
|
||||||
|
Multicol,
|
||||||
|
MulticolColumn,
|
||||||
UnscannedText(UnscannedTextFragmentInfo),
|
UnscannedText(UnscannedTextFragmentInfo),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -178,6 +180,8 @@ impl SpecificFragmentInfo {
|
||||||
SpecificFragmentInfo::TableColumn(_) |
|
SpecificFragmentInfo::TableColumn(_) |
|
||||||
SpecificFragmentInfo::TableRow |
|
SpecificFragmentInfo::TableRow |
|
||||||
SpecificFragmentInfo::TableWrapper |
|
SpecificFragmentInfo::TableWrapper |
|
||||||
|
SpecificFragmentInfo::Multicol |
|
||||||
|
SpecificFragmentInfo::MulticolColumn |
|
||||||
SpecificFragmentInfo::UnscannedText(_) |
|
SpecificFragmentInfo::UnscannedText(_) |
|
||||||
SpecificFragmentInfo::Generic => return RestyleDamage::empty(),
|
SpecificFragmentInfo::Generic => return RestyleDamage::empty(),
|
||||||
SpecificFragmentInfo::InlineAbsoluteHypothetical(ref info) => &info.flow_ref,
|
SpecificFragmentInfo::InlineAbsoluteHypothetical(ref info) => &info.flow_ref,
|
||||||
|
@ -206,6 +210,8 @@ impl SpecificFragmentInfo {
|
||||||
SpecificFragmentInfo::TableColumn(_) => "SpecificFragmentInfo::TableColumn",
|
SpecificFragmentInfo::TableColumn(_) => "SpecificFragmentInfo::TableColumn",
|
||||||
SpecificFragmentInfo::TableRow => "SpecificFragmentInfo::TableRow",
|
SpecificFragmentInfo::TableRow => "SpecificFragmentInfo::TableRow",
|
||||||
SpecificFragmentInfo::TableWrapper => "SpecificFragmentInfo::TableWrapper",
|
SpecificFragmentInfo::TableWrapper => "SpecificFragmentInfo::TableWrapper",
|
||||||
|
SpecificFragmentInfo::Multicol => "SpecificFragmentInfo::Multicol",
|
||||||
|
SpecificFragmentInfo::MulticolColumn => "SpecificFragmentInfo::MulticolColumn",
|
||||||
SpecificFragmentInfo::UnscannedText(_) => "SpecificFragmentInfo::UnscannedText",
|
SpecificFragmentInfo::UnscannedText(_) => "SpecificFragmentInfo::UnscannedText",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -912,7 +918,8 @@ impl Fragment {
|
||||||
SpecificFragmentInfo::GeneratedContent(_) |
|
SpecificFragmentInfo::GeneratedContent(_) |
|
||||||
SpecificFragmentInfo::Iframe(_) |
|
SpecificFragmentInfo::Iframe(_) |
|
||||||
SpecificFragmentInfo::Image(_) |
|
SpecificFragmentInfo::Image(_) |
|
||||||
SpecificFragmentInfo::InlineAbsolute(_) => {
|
SpecificFragmentInfo::InlineAbsolute(_) |
|
||||||
|
SpecificFragmentInfo::Multicol => {
|
||||||
QuantitiesIncludedInIntrinsicInlineSizes::all()
|
QuantitiesIncludedInIntrinsicInlineSizes::all()
|
||||||
}
|
}
|
||||||
SpecificFragmentInfo::Table | SpecificFragmentInfo::TableCell => {
|
SpecificFragmentInfo::Table | SpecificFragmentInfo::TableCell => {
|
||||||
|
@ -948,7 +955,8 @@ impl Fragment {
|
||||||
SpecificFragmentInfo::TableColumn(_) |
|
SpecificFragmentInfo::TableColumn(_) |
|
||||||
SpecificFragmentInfo::UnscannedText(_) |
|
SpecificFragmentInfo::UnscannedText(_) |
|
||||||
SpecificFragmentInfo::InlineAbsoluteHypothetical(_) |
|
SpecificFragmentInfo::InlineAbsoluteHypothetical(_) |
|
||||||
SpecificFragmentInfo::InlineBlock(_) => {
|
SpecificFragmentInfo::InlineBlock(_) |
|
||||||
|
SpecificFragmentInfo::MulticolColumn => {
|
||||||
QuantitiesIncludedInIntrinsicInlineSizes::empty()
|
QuantitiesIncludedInIntrinsicInlineSizes::empty()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1308,6 +1316,8 @@ impl Fragment {
|
||||||
SpecificFragmentInfo::TableColumn(_) |
|
SpecificFragmentInfo::TableColumn(_) |
|
||||||
SpecificFragmentInfo::TableRow |
|
SpecificFragmentInfo::TableRow |
|
||||||
SpecificFragmentInfo::TableWrapper |
|
SpecificFragmentInfo::TableWrapper |
|
||||||
|
SpecificFragmentInfo::Multicol |
|
||||||
|
SpecificFragmentInfo::MulticolColumn |
|
||||||
SpecificFragmentInfo::InlineAbsoluteHypothetical(_) => {}
|
SpecificFragmentInfo::InlineAbsoluteHypothetical(_) => {}
|
||||||
SpecificFragmentInfo::InlineBlock(ref info) => {
|
SpecificFragmentInfo::InlineBlock(ref info) => {
|
||||||
let block_flow = info.flow_ref.as_block();
|
let block_flow = info.flow_ref.as_block();
|
||||||
|
@ -1409,6 +1419,8 @@ impl Fragment {
|
||||||
SpecificFragmentInfo::TableCell |
|
SpecificFragmentInfo::TableCell |
|
||||||
SpecificFragmentInfo::TableRow |
|
SpecificFragmentInfo::TableRow |
|
||||||
SpecificFragmentInfo::TableWrapper |
|
SpecificFragmentInfo::TableWrapper |
|
||||||
|
SpecificFragmentInfo::Multicol |
|
||||||
|
SpecificFragmentInfo::MulticolColumn |
|
||||||
SpecificFragmentInfo::InlineBlock(_) |
|
SpecificFragmentInfo::InlineBlock(_) |
|
||||||
SpecificFragmentInfo::InlineAbsoluteHypothetical(_) |
|
SpecificFragmentInfo::InlineAbsoluteHypothetical(_) |
|
||||||
SpecificFragmentInfo::InlineAbsolute(_) => Au(0),
|
SpecificFragmentInfo::InlineAbsolute(_) => Au(0),
|
||||||
|
@ -1667,7 +1679,9 @@ impl Fragment {
|
||||||
SpecificFragmentInfo::Table |
|
SpecificFragmentInfo::Table |
|
||||||
SpecificFragmentInfo::TableCell |
|
SpecificFragmentInfo::TableCell |
|
||||||
SpecificFragmentInfo::TableRow |
|
SpecificFragmentInfo::TableRow |
|
||||||
SpecificFragmentInfo::TableWrapper => return,
|
SpecificFragmentInfo::TableWrapper |
|
||||||
|
SpecificFragmentInfo::Multicol |
|
||||||
|
SpecificFragmentInfo::MulticolColumn => return,
|
||||||
SpecificFragmentInfo::TableColumn(_) => {
|
SpecificFragmentInfo::TableColumn(_) => {
|
||||||
panic!("Table column fragments do not have inline size")
|
panic!("Table column fragments do not have inline size")
|
||||||
}
|
}
|
||||||
|
@ -1759,7 +1773,9 @@ impl Fragment {
|
||||||
SpecificFragmentInfo::Table |
|
SpecificFragmentInfo::Table |
|
||||||
SpecificFragmentInfo::TableCell |
|
SpecificFragmentInfo::TableCell |
|
||||||
SpecificFragmentInfo::TableRow |
|
SpecificFragmentInfo::TableRow |
|
||||||
SpecificFragmentInfo::TableWrapper => return,
|
SpecificFragmentInfo::TableWrapper |
|
||||||
|
SpecificFragmentInfo::Multicol |
|
||||||
|
SpecificFragmentInfo::MulticolColumn => return,
|
||||||
SpecificFragmentInfo::TableColumn(_) => {
|
SpecificFragmentInfo::TableColumn(_) => {
|
||||||
panic!("Table column fragments do not have block size")
|
panic!("Table column fragments do not have block size")
|
||||||
}
|
}
|
||||||
|
@ -1977,6 +1993,7 @@ impl Fragment {
|
||||||
SpecificFragmentInfo::InlineBlock(_) |
|
SpecificFragmentInfo::InlineBlock(_) |
|
||||||
SpecificFragmentInfo::InlineAbsoluteHypothetical(_) |
|
SpecificFragmentInfo::InlineAbsoluteHypothetical(_) |
|
||||||
SpecificFragmentInfo::InlineAbsolute(_) |
|
SpecificFragmentInfo::InlineAbsolute(_) |
|
||||||
|
SpecificFragmentInfo::MulticolColumn |
|
||||||
SpecificFragmentInfo::TableWrapper => false,
|
SpecificFragmentInfo::TableWrapper => false,
|
||||||
SpecificFragmentInfo::Canvas(_) |
|
SpecificFragmentInfo::Canvas(_) |
|
||||||
SpecificFragmentInfo::Generic |
|
SpecificFragmentInfo::Generic |
|
||||||
|
@ -1988,6 +2005,7 @@ impl Fragment {
|
||||||
SpecificFragmentInfo::TableCell |
|
SpecificFragmentInfo::TableCell |
|
||||||
SpecificFragmentInfo::TableColumn(_) |
|
SpecificFragmentInfo::TableColumn(_) |
|
||||||
SpecificFragmentInfo::TableRow |
|
SpecificFragmentInfo::TableRow |
|
||||||
|
SpecificFragmentInfo::Multicol |
|
||||||
SpecificFragmentInfo::UnscannedText(_) => true,
|
SpecificFragmentInfo::UnscannedText(_) => true,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -55,7 +55,6 @@ use std::borrow::ToOwned;
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::collections::hash_state::DefaultState;
|
use std::collections::hash_state::DefaultState;
|
||||||
use std::mem::transmute;
|
|
||||||
use std::ops::{Deref, DerefMut};
|
use std::ops::{Deref, DerefMut};
|
||||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||||
use std::sync::mpsc::{channel, Sender, Receiver};
|
use std::sync::mpsc::{channel, Sender, Receiver};
|
||||||
|
@ -1313,7 +1312,8 @@ impl LayoutThread {
|
||||||
/// Handles a message to destroy layout data. Layout data must be destroyed on *this* thread
|
/// Handles a message to destroy layout data. Layout data must be destroyed on *this* thread
|
||||||
/// because the struct type is transmuted to a different type on the script side.
|
/// because the struct type is transmuted to a different type on the script side.
|
||||||
unsafe fn handle_reap_style_and_layout_data(&self, data: OpaqueStyleAndLayoutData) {
|
unsafe fn handle_reap_style_and_layout_data(&self, data: OpaqueStyleAndLayoutData) {
|
||||||
let non_opaque: NonOpaqueStyleAndLayoutData = transmute(data.ptr);
|
let ptr: *mut () = *data.ptr;
|
||||||
|
let non_opaque: NonOpaqueStyleAndLayoutData = ptr as *mut _;
|
||||||
let _ = Box::from_raw(non_opaque);
|
let _ = Box::from_raw(non_opaque);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -272,7 +272,7 @@ pub enum MarginCollapseState {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Intrinsic inline-sizes, which consist of minimum and preferred.
|
/// Intrinsic inline-sizes, which consist of minimum and preferred.
|
||||||
#[derive(RustcEncodable)]
|
#[derive(RustcEncodable, Copy, Clone)]
|
||||||
pub struct IntrinsicISizes {
|
pub struct IntrinsicISizes {
|
||||||
/// The *minimum inline-size* of the content.
|
/// The *minimum inline-size* of the content.
|
||||||
pub minimum_inline_size: Au,
|
pub minimum_inline_size: Au,
|
||||||
|
|
|
@ -11,22 +11,43 @@ use block::BlockFlow;
|
||||||
use context::LayoutContext;
|
use context::LayoutContext;
|
||||||
use euclid::{Point2D, Rect};
|
use euclid::{Point2D, Rect};
|
||||||
use floats::FloatKind;
|
use floats::FloatKind;
|
||||||
use flow::{Flow, FlowClass, OpaqueFlow};
|
use flow::{Flow, FlowClass, OpaqueFlow, mut_base, FragmentationContext};
|
||||||
|
use flow_ref::{self, FlowRef};
|
||||||
use fragment::{Fragment, FragmentBorderBoxIterator};
|
use fragment::{Fragment, FragmentBorderBoxIterator};
|
||||||
|
use std::cmp::{min, max};
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
use style::context::StyleContext;
|
||||||
use style::properties::ComputedValues;
|
use style::properties::ComputedValues;
|
||||||
|
use style::values::computed::{LengthOrPercentageOrAuto, LengthOrPercentageOrNone};
|
||||||
use util::logical_geometry::LogicalSize;
|
use util::logical_geometry::LogicalSize;
|
||||||
use util::print_tree::PrintTree;
|
use util::print_tree::PrintTree;
|
||||||
|
|
||||||
pub struct MulticolFlow {
|
pub struct MulticolFlow {
|
||||||
pub block_flow: BlockFlow,
|
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 {
|
impl MulticolFlow {
|
||||||
pub fn from_fragment(fragment: Fragment, float_kind: Option<FloatKind>) -> MulticolFlow {
|
pub fn from_fragment(fragment: Fragment, float_kind: Option<FloatKind>) -> MulticolFlow {
|
||||||
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),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -36,10 +57,6 @@ impl Flow for MulticolFlow {
|
||||||
FlowClass::Multicol
|
FlowClass::Multicol
|
||||||
}
|
}
|
||||||
|
|
||||||
fn as_mut_multicol(&mut self) -> &mut MulticolFlow {
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
fn as_mut_block(&mut self) -> &mut BlockFlow {
|
fn as_mut_block(&mut self) -> &mut BlockFlow {
|
||||||
&mut self.block_flow
|
&mut self.block_flow
|
||||||
}
|
}
|
||||||
|
@ -48,23 +65,108 @@ impl Flow for MulticolFlow {
|
||||||
&self.block_flow
|
&self.block_flow
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn as_mut_multicol(&mut self) -> &mut MulticolFlow {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
fn bubble_inline_sizes(&mut self) {
|
fn bubble_inline_sizes(&mut self) {
|
||||||
// FIXME(SimonSapin) http://dev.w3.org/csswg/css-sizing/#multicol-intrinsic
|
// FIXME(SimonSapin) http://dev.w3.org/csswg/css-sizing/#multicol-intrinsic
|
||||||
self.block_flow.bubble_inline_sizes();
|
self.block_flow.bubble_inline_sizes();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn assign_inline_sizes(&mut self, ctx: &LayoutContext) {
|
fn assign_inline_sizes(&mut self, layout_context: &LayoutContext) {
|
||||||
debug!("assign_inline_sizes({}): assigning inline_size for flow", "multicol");
|
debug!("assign_inline_sizes({}): assigning inline_size for flow", "multicol");
|
||||||
self.block_flow.assign_inline_sizes(ctx);
|
self.block_flow.compute_inline_sizes(layout_context);
|
||||||
|
|
||||||
|
// Move in from the inline-start border edge.
|
||||||
|
let inline_start_content_edge = self.block_flow.fragment.border_box.start.i +
|
||||||
|
self.block_flow.fragment.border_padding.inline_start;
|
||||||
|
|
||||||
|
// Distance from the inline-end margin edge to the inline-end content edge.
|
||||||
|
let inline_end_content_edge =
|
||||||
|
self.block_flow.fragment.margin.inline_end +
|
||||||
|
self.block_flow.fragment.border_padding.inline_end;
|
||||||
|
|
||||||
|
self.block_flow.assign_inline_sizes(layout_context);
|
||||||
|
let padding_and_borders = self.block_flow.fragment.border_padding.inline_start_end();
|
||||||
|
let content_inline_size =
|
||||||
|
self.block_flow.fragment.border_box.size.inline - padding_and_borders;
|
||||||
|
let column_width;
|
||||||
|
{
|
||||||
|
let column_style = self.block_flow.fragment.style.get_column();
|
||||||
|
|
||||||
|
// `None` is 'normal': "UA-specified length. A value of 1em is suggested."
|
||||||
|
let column_gap = column_style.column_gap.0.unwrap_or_else(||
|
||||||
|
self.block_flow.fragment.style.get_font().font_size);
|
||||||
|
let mut column_count;
|
||||||
|
if let Some(column_width) = column_style.column_width.0 {
|
||||||
|
column_count =
|
||||||
|
max(1, (content_inline_size + column_gap).0 / (column_width + column_gap).0);
|
||||||
|
if let Some(specified_column_count) = column_style.column_count.0 {
|
||||||
|
column_count = min(column_count, specified_column_count as i32);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
column_count = column_style.column_count.0.unwrap() as i32;
|
||||||
|
}
|
||||||
|
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;
|
||||||
|
|
||||||
|
self.block_flow.propagate_assigned_inline_size_to_children(
|
||||||
|
layout_context, inline_start_content_edge, inline_end_content_edge, column_width,
|
||||||
|
|_, _, _, _, _, _| {});
|
||||||
}
|
}
|
||||||
|
|
||||||
fn assign_block_size<'a>(&mut self, ctx: &'a LayoutContext<'a>) {
|
fn assign_block_size<'a>(&mut self, ctx: &'a LayoutContext<'a>) {
|
||||||
debug!("assign_block_size: assigning block_size for multicol");
|
debug!("assign_block_size: assigning block_size for multicol");
|
||||||
|
|
||||||
|
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);
|
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) {
|
fn compute_absolute_position(&mut self, layout_context: &LayoutContext) {
|
||||||
self.block_flow.compute_absolute_position(layout_context)
|
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) {
|
fn update_late_computed_inline_position_if_necessary(&mut self, inline_position: Au) {
|
||||||
|
@ -76,8 +178,8 @@ impl Flow for MulticolFlow {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build_display_list(&mut self, layout_context: &LayoutContext) {
|
fn build_display_list(&mut self, layout_context: &LayoutContext) {
|
||||||
debug!("build_display_list_multicol: same process as block flow");
|
debug!("build_display_list_multicol");
|
||||||
self.block_flow.build_display_list(layout_context)
|
self.block_flow.build_display_list(layout_context);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn repair_style(&mut self, new_style: &Arc<ComputedValues>) {
|
fn repair_style(&mut self, new_style: &Arc<ComputedValues>) {
|
||||||
|
@ -96,11 +198,89 @@ impl Flow for MulticolFlow {
|
||||||
iterator: &mut FragmentBorderBoxIterator,
|
iterator: &mut FragmentBorderBoxIterator,
|
||||||
level: i32,
|
level: i32,
|
||||||
stacking_context_position: &Point2D<Au>) {
|
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)) {
|
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) {
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
|
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 column");
|
||||||
|
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) {
|
fn print_extra_flow_children(&self, print_tree: &mut PrintTree) {
|
||||||
|
@ -113,3 +293,9 @@ impl fmt::Debug for MulticolFlow {
|
||||||
write!(f, "MulticolFlow: {:?}", self.block_flow)
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -69,9 +69,11 @@ impl TableCellFlow {
|
||||||
/// methods.
|
/// methods.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn assign_block_size_table_cell_base<'a>(&mut self, layout_context: &'a LayoutContext<'a>) {
|
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,
|
layout_context,
|
||||||
MarginsMayCollapseFlag::MarginsMayNotCollapse)
|
None,
|
||||||
|
MarginsMayCollapseFlag::MarginsMayNotCollapse);
|
||||||
|
debug_assert!(remaining.is_none());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -408,9 +408,11 @@ impl Flow for TableWrapperFlow {
|
||||||
|
|
||||||
fn assign_block_size<'a>(&mut self, layout_context: &'a LayoutContext<'a>) {
|
fn assign_block_size<'a>(&mut self, layout_context: &'a LayoutContext<'a>) {
|
||||||
debug!("assign_block_size: assigning block_size for table_wrapper");
|
debug!("assign_block_size: assigning block_size for table_wrapper");
|
||||||
self.block_flow
|
let remaining = self.block_flow.assign_block_size_block_base(
|
||||||
.assign_block_size_block_base(layout_context,
|
layout_context,
|
||||||
MarginsMayCollapseFlag::MarginsMayNotCollapse);
|
None,
|
||||||
|
MarginsMayCollapseFlag::MarginsMayNotCollapse);
|
||||||
|
debug_assert!(remaining.is_none());
|
||||||
}
|
}
|
||||||
|
|
||||||
fn compute_absolute_position(&mut self, layout_context: &LayoutContext) {
|
fn compute_absolute_position(&mut self, layout_context: &LayoutContext) {
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
use construct::FlowConstructor;
|
use construct::FlowConstructor;
|
||||||
use context::{LayoutContext, SharedLayoutContext};
|
use context::{LayoutContext, SharedLayoutContext};
|
||||||
use flow::{PostorderFlowTraversal, PreorderFlowTraversal};
|
use flow::{PostorderFlowTraversal, PreorderFlowTraversal};
|
||||||
use flow::{self, Flow};
|
use flow::{self, Flow, CAN_BE_FRAGMENTED};
|
||||||
use gfx::display_list::OpaqueNode;
|
use gfx::display_list::OpaqueNode;
|
||||||
use incremental::{BUBBLE_ISIZES, REFLOW, REFLOW_OUT_OF_FLOW, REPAINT, RestyleDamage};
|
use incremental::{BUBBLE_ISIZES, REFLOW, REFLOW_OUT_OF_FLOW, REPAINT, RestyleDamage};
|
||||||
use std::mem;
|
use std::mem;
|
||||||
|
@ -194,7 +194,10 @@ impl<'a> PostorderFlowTraversal for AssignBSizesAndStoreOverflow<'a> {
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn should_process(&self, flow: &mut Flow) -> bool {
|
fn should_process(&self, flow: &mut Flow) -> bool {
|
||||||
flow::base(flow).restyle_damage.intersects(REFLOW_OUT_OF_FLOW | REFLOW)
|
let base = flow::base(flow);
|
||||||
|
base.restyle_damage.intersects(REFLOW_OUT_OF_FLOW | REFLOW)
|
||||||
|
// The fragmentation countainer is responsible for calling Flow::fragment recursively
|
||||||
|
&& !base.flags.contains(CAN_BE_FRAGMENTED)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -49,7 +49,7 @@ use script::dom::htmliframeelement::HTMLIFrameElement;
|
||||||
use script::dom::htmlimageelement::LayoutHTMLImageElementHelpers;
|
use script::dom::htmlimageelement::LayoutHTMLImageElementHelpers;
|
||||||
use script::dom::htmlinputelement::{HTMLInputElement, LayoutHTMLInputElementHelpers};
|
use script::dom::htmlinputelement::{HTMLInputElement, LayoutHTMLInputElementHelpers};
|
||||||
use script::dom::htmltextareaelement::{HTMLTextAreaElement, LayoutHTMLTextAreaElementHelpers};
|
use script::dom::htmltextareaelement::{HTMLTextAreaElement, LayoutHTMLTextAreaElementHelpers};
|
||||||
use script::dom::node::{HAS_CHANGED, HAS_DIRTY_DESCENDANTS, IS_DIRTY};
|
use script::dom::node::{CAN_BE_FRAGMENTED, HAS_CHANGED, HAS_DIRTY_DESCENDANTS, IS_DIRTY};
|
||||||
use script::dom::node::{LayoutNodeHelpers, Node, OpaqueStyleAndLayoutData};
|
use script::dom::node::{LayoutNodeHelpers, Node, OpaqueStyleAndLayoutData};
|
||||||
use script::dom::text::Text;
|
use script::dom::text::Text;
|
||||||
use script::layout_interface::TrustedNodeAddress;
|
use script::layout_interface::TrustedNodeAddress;
|
||||||
|
@ -226,6 +226,14 @@ impl<'ln> TNode<'ln> for ServoLayoutNode<'ln> {
|
||||||
self.node.set_flag(HAS_DIRTY_DESCENDANTS, value)
|
self.node.set_flag(HAS_DIRTY_DESCENDANTS, value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn can_be_fragmented(&self) -> bool {
|
||||||
|
unsafe { self.node.get_flag(CAN_BE_FRAGMENTED) }
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn set_can_be_fragmented(&self, value: bool) {
|
||||||
|
self.node.set_flag(CAN_BE_FRAGMENTED, value)
|
||||||
|
}
|
||||||
|
|
||||||
unsafe fn borrow_data_unchecked(&self) -> Option<*const PrivateStyleData> {
|
unsafe fn borrow_data_unchecked(&self) -> Option<*const PrivateStyleData> {
|
||||||
self.borrow_layout_data_unchecked().map(|d| &(*d).style_data as *const PrivateStyleData)
|
self.borrow_layout_data_unchecked().map(|d| &(*d).style_data as *const PrivateStyleData)
|
||||||
}
|
}
|
||||||
|
@ -753,6 +761,8 @@ pub trait ThreadSafeLayoutNode<'ln> : Clone + Copy + Sized {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn can_be_fragmented(&self) -> bool;
|
||||||
|
|
||||||
/// If this is a text node, generated content, or a form element, copies out
|
/// If this is a text node, generated content, or a form element, copies out
|
||||||
/// its content. Otherwise, panics.
|
/// its content. Otherwise, panics.
|
||||||
///
|
///
|
||||||
|
@ -929,6 +939,10 @@ impl<'ln> ThreadSafeLayoutNode<'ln> for ServoThreadSafeLayoutNode<'ln> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn can_be_fragmented(&self) -> bool {
|
||||||
|
self.node.can_be_fragmented()
|
||||||
|
}
|
||||||
|
|
||||||
fn text_content(&self) -> TextContent {
|
fn text_content(&self) -> TextContent {
|
||||||
if self.pseudo != PseudoElementType::Normal {
|
if self.pseudo != PseudoElementType::Normal {
|
||||||
let data = &self.borrow_layout_data().unwrap().style_data;
|
let data = &self.borrow_layout_data().unwrap().style_data;
|
||||||
|
|
|
@ -144,6 +144,9 @@ bitflags! {
|
||||||
#[doc = "Specifies whether this node is focusable and whether it is supposed \
|
#[doc = "Specifies whether this node is focusable and whether it is supposed \
|
||||||
to be reachable with using sequential focus navigation."]
|
to be reachable with using sequential focus navigation."]
|
||||||
const SEQUENTIALLY_FOCUSABLE = 0x20,
|
const SEQUENTIALLY_FOCUSABLE = 0x20,
|
||||||
|
|
||||||
|
/// Whether any ancestor is a fragmentation container
|
||||||
|
const CAN_BE_FRAGMENTED = 0x40
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -129,6 +129,10 @@ pub trait TNode<'ln> : Sized + Copy + Clone {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn can_be_fragmented(&self) -> bool;
|
||||||
|
|
||||||
|
unsafe fn set_can_be_fragmented(&self, value: bool);
|
||||||
|
|
||||||
/// Borrows the PrivateStyleData without checks.
|
/// Borrows the PrivateStyleData without checks.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
unsafe fn borrow_data_unchecked(&self) -> Option<*const PrivateStyleData>;
|
unsafe fn borrow_data_unchecked(&self) -> Option<*const PrivateStyleData>;
|
||||||
|
|
|
@ -700,6 +700,11 @@ pub trait MatchMethods<'ln> : TNode<'ln> {
|
||||||
// This method needs to borrow the data as mutable, so make sure data_ref goes out of
|
// This method needs to borrow the data as mutable, so make sure data_ref goes out of
|
||||||
// scope first.
|
// scope first.
|
||||||
self.set_restyle_damage(damage);
|
self.set_restyle_damage(damage);
|
||||||
|
|
||||||
|
self.set_can_be_fragmented(parent.map_or(false, |p| {
|
||||||
|
p.can_be_fragmented() ||
|
||||||
|
parent_style.as_ref().unwrap().is_multicol()
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -182,6 +182,17 @@ impl<'ln> TNode<'ln> for GeckoNode<'ln> {
|
||||||
unimplemented!()
|
unimplemented!()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn can_be_fragmented(&self) -> bool {
|
||||||
|
// FIXME(SimonSapin): Servo uses this to implement CSS multicol / fragmentation
|
||||||
|
// Maybe this isn’t useful for Gecko?
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn set_can_be_fragmented(&self, _value: bool) {
|
||||||
|
// FIXME(SimonSapin): Servo uses this to implement CSS multicol / fragmentation
|
||||||
|
// Maybe this isn’t useful for Gecko?
|
||||||
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
unsafe fn borrow_data_unchecked(&self) -> Option<*const PrivateStyleData> {
|
unsafe fn borrow_data_unchecked(&self) -> Option<*const PrivateStyleData> {
|
||||||
self.get_node_data().as_ref().map(|d| d.as_unsafe_cell().get() as *const PrivateStyleData)
|
self.get_node_data().as_ref().map(|d| d.as_unsafe_cell().get() as *const PrivateStyleData)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue