1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 5 //! CSS Multi-column layout http://dev.w3.org/csswg/css-multicol/ 6 7 #![deny(unsafe_code)] 8 9 use ServoArc; 10 use app_units::Au; 11 use block::BlockFlow; 12 use context::LayoutContext; 13 use display_list::{DisplayListBuildState, StackingContextCollectionState}; 14 use euclid::{Point2D, Vector2D}; 15 use floats::FloatKind; 16 use flow::{Flow, FlowClass, OpaqueFlow, FragmentationContext, GetBaseFlow}; 17 use fragment::{Fragment, FragmentBorderBoxIterator, Overflow}; 18 use gfx_traits::print_tree::PrintTree; 19 use std::cmp::{min, max}; 20 use std::fmt; 21 use std::sync::Arc; 22 use style::logical_geometry::LogicalSize; 23 use style::properties::ComputedValues; 24 use style::values::Either; 25 use style::values::computed::{LengthOrPercentageOrAuto, LengthOrPercentageOrNone}; 26 use style::values::generics::column::ColumnCount; 27 28 #[allow(unsafe_code)] 29 unsafe impl ::flow::HasBaseFlow for MulticolFlow {} 30 31 #[repr(C)] 32 pub struct MulticolFlow { 33 pub block_flow: BlockFlow, 34 35 /// Length between the inline-start edge of a column and that of the next. 36 /// That is, the used column-width + used column-gap. 37 pub column_pitch: Au, 38 } 39 40 #[allow(unsafe_code)] 41 unsafe impl ::flow::HasBaseFlow for MulticolColumnFlow {} 42 43 #[repr(C)] 44 pub struct MulticolColumnFlow { 45 pub block_flow: BlockFlow, 46 } 47 48 impl MulticolFlow { from_fragment(fragment: Fragment, float_kind: Option<FloatKind>) -> MulticolFlow49 pub fn from_fragment(fragment: Fragment, float_kind: Option<FloatKind>) -> MulticolFlow { 50 MulticolFlow { 51 block_flow: BlockFlow::from_fragment_and_float_kind(fragment, float_kind), 52 column_pitch: Au(0), 53 } 54 } 55 } 56 57 impl MulticolColumnFlow { from_fragment(fragment: Fragment) -> MulticolColumnFlow58 pub fn from_fragment(fragment: Fragment) -> MulticolColumnFlow { 59 MulticolColumnFlow { 60 block_flow: BlockFlow::from_fragment(fragment), 61 } 62 } 63 } 64 65 impl Flow for MulticolFlow { class(&self) -> FlowClass66 fn class(&self) -> FlowClass { 67 FlowClass::Multicol 68 } 69 as_mut_block(&mut self) -> &mut BlockFlow70 fn as_mut_block(&mut self) -> &mut BlockFlow { 71 &mut self.block_flow 72 } 73 as_block(&self) -> &BlockFlow74 fn as_block(&self) -> &BlockFlow { 75 &self.block_flow 76 } 77 as_mut_multicol(&mut self) -> &mut MulticolFlow78 fn as_mut_multicol(&mut self) -> &mut MulticolFlow { 79 self 80 } 81 bubble_inline_sizes(&mut self)82 fn bubble_inline_sizes(&mut self) { 83 // FIXME(SimonSapin) http://dev.w3.org/csswg/css-sizing/#multicol-intrinsic 84 self.block_flow.bubble_inline_sizes(); 85 } 86 assign_inline_sizes(&mut self, layout_context: &LayoutContext)87 fn assign_inline_sizes(&mut self, layout_context: &LayoutContext) { 88 debug!("assign_inline_sizes({}): assigning inline_size for flow", "multicol"); 89 let shared_context = layout_context.shared_context(); 90 self.block_flow.compute_inline_sizes(shared_context); 91 92 // Move in from the inline-start border edge. 93 let inline_start_content_edge = self.block_flow.fragment.border_box.start.i + 94 self.block_flow.fragment.border_padding.inline_start; 95 96 // Distance from the inline-end margin edge to the inline-end content edge. 97 let inline_end_content_edge = 98 self.block_flow.fragment.margin.inline_end + 99 self.block_flow.fragment.border_padding.inline_end; 100 101 self.block_flow.assign_inline_sizes(layout_context); 102 let padding_and_borders = self.block_flow.fragment.border_padding.inline_start_end(); 103 let content_inline_size = 104 self.block_flow.fragment.border_box.size.inline - padding_and_borders; 105 let column_width; 106 { 107 let column_style = self.block_flow.fragment.style.get_column(); 108 109 let column_gap = match column_style.column_gap { 110 Either::First(len) => len.into(), 111 Either::Second(_normal) => self.block_flow.fragment.style.get_font().font_size.size(), 112 }; 113 114 let mut column_count; 115 if let Either::First(column_width) = column_style.column_width { 116 let column_width = Au::from(column_width); 117 column_count = 118 max(1, (content_inline_size + column_gap).0 / (column_width + column_gap).0); 119 if let ColumnCount::Integer(specified_column_count) = column_style.column_count { 120 column_count = min(column_count, specified_column_count.0 as i32); 121 } 122 } else { 123 column_count = match column_style.column_count { 124 ColumnCount::Integer(n) => n.0, 125 _ => unreachable!(), 126 } 127 } 128 column_width = 129 max(Au(0), (content_inline_size + column_gap) / column_count - column_gap); 130 self.column_pitch = column_width + column_gap; 131 } 132 133 self.block_flow.fragment.border_box.size.inline = content_inline_size + padding_and_borders; 134 135 self.block_flow.propagate_assigned_inline_size_to_children( 136 shared_context, inline_start_content_edge, inline_end_content_edge, column_width, 137 |_, _, _, _, _, _| {}); 138 } 139 assign_block_size(&mut self, ctx: &LayoutContext)140 fn assign_block_size(&mut self, ctx: &LayoutContext) { 141 debug!("assign_block_size: assigning block_size for multicol"); 142 143 let fragmentation_context = Some(FragmentationContext { 144 this_fragment_is_empty: true, 145 available_block_size: { 146 let style = &self.block_flow.fragment.style; 147 if let LengthOrPercentageOrAuto::Length(length) = style.content_block_size() { 148 Au::from(length) 149 } else if let LengthOrPercentageOrNone::Length(length) = style.max_block_size() { 150 Au::from(length) 151 } else { 152 // FIXME: do column balancing instead 153 // FIXME: (until column balancing) substract margins/borders/padding 154 LogicalSize::from_physical( 155 self.block_flow.base.writing_mode, 156 ctx.shared_context().viewport_size(), 157 ).block 158 } 159 } 160 }); 161 162 // Before layout, everything is in a single "column" 163 assert_eq!(self.block_flow.base.children.len(), 1); 164 let mut column = self.block_flow.base.children.pop_front_arc().unwrap(); 165 166 // Pretend there is no children for this: 167 self.block_flow.assign_block_size(ctx); 168 169 loop { 170 let remaining = Arc::get_mut(&mut column).unwrap().fragment(ctx, fragmentation_context); 171 self.block_flow.base.children.push_back_arc(column); 172 column = match remaining { 173 Some(remaining) => remaining, 174 None => break 175 }; 176 } 177 } 178 compute_stacking_relative_position(&mut self, layout_context: &LayoutContext)179 fn compute_stacking_relative_position(&mut self, layout_context: &LayoutContext) { 180 self.block_flow.compute_stacking_relative_position(layout_context); 181 let pitch = LogicalSize::new(self.block_flow.base.writing_mode, self.column_pitch, Au(0)); 182 let pitch = pitch.to_physical(self.block_flow.base.writing_mode); 183 for (i, child) in self.block_flow.base.children.iter_mut().enumerate() { 184 let point = &mut child.mut_base().stacking_relative_position; 185 *point = *point + Vector2D::new(pitch.width * i as i32, pitch.height * i as i32); 186 } 187 } 188 update_late_computed_inline_position_if_necessary(&mut self, inline_position: Au)189 fn update_late_computed_inline_position_if_necessary(&mut self, inline_position: Au) { 190 self.block_flow.update_late_computed_inline_position_if_necessary(inline_position) 191 } 192 update_late_computed_block_position_if_necessary(&mut self, block_position: Au)193 fn update_late_computed_block_position_if_necessary(&mut self, block_position: Au) { 194 self.block_flow.update_late_computed_block_position_if_necessary(block_position) 195 } 196 build_display_list(&mut self, state: &mut DisplayListBuildState)197 fn build_display_list(&mut self, state: &mut DisplayListBuildState) { 198 debug!("build_display_list_multicol"); 199 self.block_flow.build_display_list(state); 200 } 201 collect_stacking_contexts(&mut self, state: &mut StackingContextCollectionState)202 fn collect_stacking_contexts(&mut self, state: &mut StackingContextCollectionState) { 203 self.block_flow.collect_stacking_contexts(state); 204 } 205 repair_style(&mut self, new_style: &ServoArc<ComputedValues>)206 fn repair_style(&mut self, new_style: &ServoArc<ComputedValues>) { 207 self.block_flow.repair_style(new_style) 208 } 209 compute_overflow(&self) -> Overflow210 fn compute_overflow(&self) -> Overflow { 211 self.block_flow.compute_overflow() 212 } 213 contains_roots_of_absolute_flow_tree(&self) -> bool214 fn contains_roots_of_absolute_flow_tree(&self) -> bool { 215 self.block_flow.contains_roots_of_absolute_flow_tree() 216 } 217 is_absolute_containing_block(&self) -> bool218 fn is_absolute_containing_block(&self) -> bool { 219 self.block_flow.is_absolute_containing_block() 220 } 221 generated_containing_block_size(&self, flow: OpaqueFlow) -> LogicalSize<Au>222 fn generated_containing_block_size(&self, flow: OpaqueFlow) -> LogicalSize<Au> { 223 self.block_flow.generated_containing_block_size(flow) 224 } 225 iterate_through_fragment_border_boxes(&self, iterator: &mut FragmentBorderBoxIterator, level: i32, stacking_context_position: &Point2D<Au>)226 fn iterate_through_fragment_border_boxes(&self, 227 iterator: &mut FragmentBorderBoxIterator, 228 level: i32, 229 stacking_context_position: &Point2D<Au>) { 230 self.block_flow.iterate_through_fragment_border_boxes(iterator, level, stacking_context_position); 231 } 232 mutate_fragments(&mut self, mutator: &mut FnMut(&mut Fragment))233 fn mutate_fragments(&mut self, mutator: &mut FnMut(&mut Fragment)) { 234 self.block_flow.mutate_fragments(mutator); 235 } 236 print_extra_flow_children(&self, print_tree: &mut PrintTree)237 fn print_extra_flow_children(&self, print_tree: &mut PrintTree) { 238 self.block_flow.print_extra_flow_children(print_tree); 239 } 240 } 241 242 impl Flow for MulticolColumnFlow { class(&self) -> FlowClass243 fn class(&self) -> FlowClass { 244 FlowClass::MulticolColumn 245 } 246 as_mut_block(&mut self) -> &mut BlockFlow247 fn as_mut_block(&mut self) -> &mut BlockFlow { 248 &mut self.block_flow 249 } 250 as_block(&self) -> &BlockFlow251 fn as_block(&self) -> &BlockFlow { 252 &self.block_flow 253 } 254 bubble_inline_sizes(&mut self)255 fn bubble_inline_sizes(&mut self) { 256 self.block_flow.bubble_inline_sizes(); 257 } 258 assign_inline_sizes(&mut self, layout_context: &LayoutContext)259 fn assign_inline_sizes(&mut self, layout_context: &LayoutContext) { 260 debug!("assign_inline_sizes({}): assigning inline_size for flow", "multicol column"); 261 self.block_flow.assign_inline_sizes(layout_context); 262 } 263 assign_block_size(&mut self, ctx: &LayoutContext)264 fn assign_block_size(&mut self, ctx: &LayoutContext) { 265 debug!("assign_block_size: assigning block_size for multicol column"); 266 self.block_flow.assign_block_size(ctx); 267 } 268 fragment(&mut self, layout_context: &LayoutContext, fragmentation_context: Option<FragmentationContext>) -> Option<Arc<Flow>>269 fn fragment(&mut self, layout_context: &LayoutContext, 270 fragmentation_context: Option<FragmentationContext>) 271 -> Option<Arc<Flow>> { 272 Flow::fragment(&mut self.block_flow, layout_context, fragmentation_context) 273 } 274 compute_stacking_relative_position(&mut self, layout_context: &LayoutContext)275 fn compute_stacking_relative_position(&mut self, layout_context: &LayoutContext) { 276 self.block_flow.compute_stacking_relative_position(layout_context) 277 } 278 update_late_computed_inline_position_if_necessary(&mut self, inline_position: Au)279 fn update_late_computed_inline_position_if_necessary(&mut self, inline_position: Au) { 280 self.block_flow.update_late_computed_inline_position_if_necessary(inline_position) 281 } 282 update_late_computed_block_position_if_necessary(&mut self, block_position: Au)283 fn update_late_computed_block_position_if_necessary(&mut self, block_position: Au) { 284 self.block_flow.update_late_computed_block_position_if_necessary(block_position) 285 } 286 build_display_list(&mut self, state: &mut DisplayListBuildState)287 fn build_display_list(&mut self, state: &mut DisplayListBuildState) { 288 debug!("build_display_list_multicol column"); 289 self.block_flow.build_display_list(state); 290 } 291 collect_stacking_contexts(&mut self, state: &mut StackingContextCollectionState)292 fn collect_stacking_contexts(&mut self, state: &mut StackingContextCollectionState) { 293 self.block_flow.collect_stacking_contexts(state); 294 } 295 repair_style(&mut self, new_style: &ServoArc<ComputedValues>)296 fn repair_style(&mut self, new_style: &ServoArc<ComputedValues>) { 297 self.block_flow.repair_style(new_style) 298 } 299 compute_overflow(&self) -> Overflow300 fn compute_overflow(&self) -> Overflow { 301 self.block_flow.compute_overflow() 302 } 303 contains_roots_of_absolute_flow_tree(&self) -> bool304 fn contains_roots_of_absolute_flow_tree(&self) -> bool { 305 self.block_flow.contains_roots_of_absolute_flow_tree() 306 } 307 is_absolute_containing_block(&self) -> bool308 fn is_absolute_containing_block(&self) -> bool { 309 self.block_flow.is_absolute_containing_block() 310 } 311 generated_containing_block_size(&self, flow: OpaqueFlow) -> LogicalSize<Au>312 fn generated_containing_block_size(&self, flow: OpaqueFlow) -> LogicalSize<Au> { 313 self.block_flow.generated_containing_block_size(flow) 314 } 315 iterate_through_fragment_border_boxes(&self, iterator: &mut FragmentBorderBoxIterator, level: i32, stacking_context_position: &Point2D<Au>)316 fn iterate_through_fragment_border_boxes(&self, 317 iterator: &mut FragmentBorderBoxIterator, 318 level: i32, 319 stacking_context_position: &Point2D<Au>) { 320 self.block_flow.iterate_through_fragment_border_boxes(iterator, level, stacking_context_position); 321 } 322 mutate_fragments(&mut self, mutator: &mut FnMut(&mut Fragment))323 fn mutate_fragments(&mut self, mutator: &mut FnMut(&mut Fragment)) { 324 self.block_flow.mutate_fragments(mutator); 325 } 326 print_extra_flow_children(&self, print_tree: &mut PrintTree)327 fn print_extra_flow_children(&self, print_tree: &mut PrintTree) { 328 self.block_flow.print_extra_flow_children(print_tree); 329 } 330 } 331 332 impl fmt::Debug for MulticolFlow { fmt(&self, f: &mut fmt::Formatter) -> fmt::Result333 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 334 write!(f, "MulticolFlow: {:?}", self.block_flow) 335 } 336 } 337 338 impl fmt::Debug for MulticolColumnFlow { fmt(&self, f: &mut fmt::Formatter) -> fmt::Result339 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 340 write!(f, "MulticolColumnFlow: {:?}", self.block_flow) 341 } 342 } 343