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