diff --git a/components/layout/construct.rs b/components/layout/construct.rs index 2e05b1ed694..53ddc2bed04 100644 --- a/components/layout/construct.rs +++ b/components/layout/construct.rs @@ -18,7 +18,7 @@ use context::LayoutContext; use data::{HAS_NEWLY_CONSTRUCTED_FLOW, PrivateLayoutData}; use flex::FlexFlow; 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_ref::{self, FlowRef}; use fragment::{CanvasFragmentInfo, ImageFragmentInfo, InlineAbsoluteFragmentInfo}; @@ -1318,7 +1318,7 @@ impl<'a, 'ln, ConcreteThreadSafeLayoutNode: ThreadSafeLayoutNode<'ln>> return false } - if node.in_fragmentation_container() { + if node.can_be_fragmented() || node.style().is_multicol() { return false } @@ -1616,7 +1616,13 @@ impl<'ln, ConcreteThreadSafeLayoutNode> NodeUtils for ConcreteThreadSafeLayoutNo } #[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 dst = self.construction_result_mut(&mut *layout_data); diff --git a/components/layout/flow.rs b/components/layout/flow.rs index 2df03d43233..4a8b206193a 100644 --- a/components/layout/flow.rs +++ b/components/layout/flow.rs @@ -201,6 +201,27 @@ pub trait Flow: fmt::Debug + Sync + Send + 'static { 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, + /// return an indication of where in the tree to break and start the next fragment. + /// + /// FIXME: replace `()` in the return value with something meaningful. + /// + /// 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>) { + 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); + None + } + /// If this is a float, places it. The default implementation does nothing. fn place_float_if_applicable<'a>(&mut self, _: &'a LayoutContext<'a>) {} @@ -614,6 +635,9 @@ bitflags! { static` and `position: relative` as well as absolutely-positioned flows with \ unconstrained positions in the block direction."] 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, } } diff --git a/components/layout/multicol.rs b/components/layout/multicol.rs index 3db1479dc52..f0c4acd09ea 100644 --- a/components/layout/multicol.rs +++ b/components/layout/multicol.rs @@ -11,7 +11,7 @@ use block::BlockFlow; use context::LayoutContext; use euclid::{Point2D, Rect}; use floats::FloatKind; -use flow::{Flow, FlowClass, OpaqueFlow}; +use flow::{Flow, FlowClass, OpaqueFlow, mut_base}; use fragment::{Fragment, FragmentBorderBoxIterator}; use std::fmt; use std::sync::Arc; @@ -60,6 +60,10 @@ 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); + } self.block_flow.assign_block_size(ctx); } diff --git a/components/layout/traversal.rs b/components/layout/traversal.rs index b5f7505d834..69e85c39303 100644 --- a/components/layout/traversal.rs +++ b/components/layout/traversal.rs @@ -10,7 +10,7 @@ use construct::FlowConstructor; use context::{LayoutContext, SharedLayoutContext}; use flow::{PostorderFlowTraversal, PreorderFlowTraversal}; -use flow::{self, Flow}; +use flow::{self, Flow, CAN_BE_FRAGMENTED}; use gfx::display_list::OpaqueNode; use incremental::{BUBBLE_ISIZES, REFLOW, REFLOW_OUT_OF_FLOW, REPAINT, RestyleDamage}; use std::mem; @@ -194,7 +194,10 @@ impl<'a> PostorderFlowTraversal for AssignBSizesAndStoreOverflow<'a> { #[inline] 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) } } diff --git a/components/layout/wrapper.rs b/components/layout/wrapper.rs index 652dd8c6359..cf02c66ed63 100644 --- a/components/layout/wrapper.rs +++ b/components/layout/wrapper.rs @@ -49,8 +49,8 @@ use script::dom::htmliframeelement::HTMLIFrameElement; use script::dom::htmlimageelement::LayoutHTMLImageElementHelpers; use script::dom::htmlinputelement::{HTMLInputElement, LayoutHTMLInputElementHelpers}; use script::dom::htmltextareaelement::{HTMLTextAreaElement, LayoutHTMLTextAreaElementHelpers}; -use script::dom::node::{HAS_CHANGED, HAS_DIRTY_DESCENDANTS, IS_DIRTY}; -use script::dom::node::{IN_FRAGMENTATION_CONTAINER, LayoutNodeHelpers, Node, OpaqueStyleAndLayoutData}; +use script::dom::node::{CAN_BE_FRAGMENTED, HAS_CHANGED, HAS_DIRTY_DESCENDANTS, IS_DIRTY}; +use script::dom::node::{LayoutNodeHelpers, Node, OpaqueStyleAndLayoutData}; use script::dom::text::Text; use script::layout_interface::TrustedNodeAddress; use selectors::matching::DeclarationBlock; @@ -226,12 +226,12 @@ impl<'ln> TNode<'ln> for ServoLayoutNode<'ln> { self.node.set_flag(HAS_DIRTY_DESCENDANTS, value) } - fn in_fragmentation_container(&self) -> bool { - unsafe { self.node.get_flag(IN_FRAGMENTATION_CONTAINER) } + fn can_be_fragmented(&self) -> bool { + unsafe { self.node.get_flag(CAN_BE_FRAGMENTED) } } - unsafe fn set_in_fragmentation_container(&self, value: bool) { - self.node.set_flag(IN_FRAGMENTATION_CONTAINER, value) + 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> { @@ -761,7 +761,7 @@ pub trait ThreadSafeLayoutNode<'ln> : Clone + Copy + Sized { } } - fn in_fragmentation_container(&self) -> bool; + fn can_be_fragmented(&self) -> bool; /// If this is a text node, generated content, or a form element, copies out /// its content. Otherwise, panics. @@ -939,8 +939,8 @@ impl<'ln> ThreadSafeLayoutNode<'ln> for ServoThreadSafeLayoutNode<'ln> { } } - fn in_fragmentation_container(&self) -> bool { - self.node.in_fragmentation_container() + fn can_be_fragmented(&self) -> bool { + self.node.can_be_fragmented() } fn text_content(&self) -> TextContent { diff --git a/components/script/dom/node.rs b/components/script/dom/node.rs index 3f8bcefeef8..33dadfe527b 100644 --- a/components/script/dom/node.rs +++ b/components/script/dom/node.rs @@ -146,7 +146,7 @@ bitflags! { const SEQUENTIALLY_FOCUSABLE = 0x20, /// Whether any ancestor is a fragmentation container - const IN_FRAGMENTATION_CONTAINER = 0x40 + const CAN_BE_FRAGMENTED = 0x40 } } diff --git a/components/style/dom.rs b/components/style/dom.rs index 63c645901b3..ce538f1b21f 100644 --- a/components/style/dom.rs +++ b/components/style/dom.rs @@ -129,9 +129,9 @@ pub trait TNode<'ln> : Sized + Copy + Clone { } } - fn in_fragmentation_container(&self) -> bool; + fn can_be_fragmented(&self) -> bool; - unsafe fn set_in_fragmentation_container(&self, value: bool); + unsafe fn set_can_be_fragmented(&self, value: bool); /// Borrows the PrivateStyleData without checks. #[inline(always)] diff --git a/components/style/matching.rs b/components/style/matching.rs index f2a1515ef07..ea25d518a99 100644 --- a/components/style/matching.rs +++ b/components/style/matching.rs @@ -701,10 +701,10 @@ pub trait MatchMethods<'ln> : TNode<'ln> { // scope first. self.set_restyle_damage(damage); - self.set_in_fragmentation_container( - parent.as_ref().map_or(false, |p| p.in_fragmentation_container()) || - self.borrow_data().unwrap().style.as_ref().unwrap().is_multicol() - ); + self.set_can_be_fragmented(parent.map_or(false, |p| { + p.can_be_fragmented() || + parent_style.as_ref().unwrap().is_multicol() + })); } } } diff --git a/ports/geckolib/wrapper.rs b/ports/geckolib/wrapper.rs index 77563f91ee3..a4d97c8a17c 100644 --- a/ports/geckolib/wrapper.rs +++ b/ports/geckolib/wrapper.rs @@ -182,13 +182,13 @@ impl<'ln> TNode<'ln> for GeckoNode<'ln> { unimplemented!() } - fn in_fragmentation_container(&self) -> bool { + 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_in_fragmentation_container(&self, _value: bool) { + 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? }