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 //! Layout for CSS block-level elements.
6 //!
7 //! As a terminology note, the term *absolute positioning* here refers to elements with position
8 //! `absolute` or `fixed`. The term *positioned element* refers to elements with position
9 //! `relative`, `absolute`, and `fixed`. The term *containing block* (occasionally abbreviated as
10 //! *CB*) is the containing block for the current flow, which differs from the static containing
11 //! block if the flow is absolutely-positioned.
12 //!
13 //! "CSS 2.1" or "CSS 2.2" refers to the editor's draft of the W3C "Cascading Style Sheets Level 2
14 //! Revision 2 (CSS 2.2) Specification" available here:
15 //!
16 //!   http://dev.w3.org/csswg/css2/
17 //!
18 //! "INTRINSIC" refers to L. David Baron's "More Precise Definitions of Inline Layout and Table
19 //! Layout" available here:
20 //!
21 //!   http://dbaron.org/css/intrinsic/
22 //!
23 //! "CSS-SIZING" refers to the W3C "CSS Intrinsic & Extrinsic Sizing Module Level 3" document
24 //! available here:
25 //!
26 //!   http://dev.w3.org/csswg/css-sizing/
27 
28 #![deny(unsafe_code)]
29 
30 use app_units::{Au, MAX_AU};
31 use context::LayoutContext;
32 use display_list::{BlockFlowDisplayListBuilding, BorderPaintingMode};
33 use display_list::{DisplayListBuildState, StackingContextCollectionFlags};
34 use display_list::StackingContextCollectionState;
35 use euclid::{Point2D, Rect, SideOffsets2D, Size2D};
36 use floats::{ClearType, FloatKind, Floats, PlacementInfo};
37 use flow::{BaseFlow, EarlyAbsolutePositionInfo, Flow, FlowClass, ForceNonfloatedFlag, GetBaseFlow};
38 use flow::{ImmutableFlowUtils, LateAbsolutePositionInfo, OpaqueFlow, FragmentationContext, FlowFlags};
39 use flow_list::FlowList;
40 use fragment::{CoordinateSystem, Fragment, FragmentBorderBoxIterator, Overflow, FragmentFlags};
41 use gfx::display_list::DisplayListSection;
42 use gfx_traits::print_tree::PrintTree;
43 use incremental::RelayoutMode;
44 use layout_debug;
45 use model::{AdjoiningMargins, CollapsibleMargins, IntrinsicISizes, MarginCollapseInfo, MaybeAuto};
46 use sequential;
47 use serde::{Serialize, Serializer};
48 use servo_geometry::MaxRect;
49 use std::cmp::{max, min};
50 use std::fmt;
51 use std::sync::Arc;
52 use style::computed_values::box_sizing::T as BoxSizing;
53 use style::computed_values::display::T as Display;
54 use style::computed_values::float::T as Float;
55 use style::computed_values::overflow_x::T as StyleOverflow;
56 use style::computed_values::position::T as Position;
57 use style::computed_values::text_align::T as TextAlign;
58 use style::context::SharedStyleContext;
59 use style::logical_geometry::{LogicalMargin, LogicalPoint, LogicalRect, LogicalSize, WritingMode};
60 use style::properties::ComputedValues;
61 use style::servo::restyle_damage::ServoRestyleDamage;
62 use style::values::computed::{LengthOrPercentageOrNone, LengthOrPercentage};
63 use style::values::computed::LengthOrPercentageOrAuto;
64 use traversal::PreorderFlowTraversal;
65 
66 /// Information specific to floated blocks.
67 #[derive(Clone, Serialize)]
68 pub struct FloatedBlockInfo {
69     /// The amount of inline size that is available for the float.
70     pub containing_inline_size: Au,
71 
72     /// The float ceiling, relative to `BaseFlow::position::cur_b` (i.e. the top part of the border
73     /// box).
74     pub float_ceiling: Au,
75 
76     /// Left or right?
77     pub float_kind: FloatKind,
78 }
79 
80 impl FloatedBlockInfo {
new(float_kind: FloatKind) -> FloatedBlockInfo81     pub fn new(float_kind: FloatKind) -> FloatedBlockInfo {
82         FloatedBlockInfo {
83             containing_inline_size: Au(0),
84             float_ceiling: Au(0),
85             float_kind: float_kind,
86         }
87     }
88 }
89 
90 /// The solutions for the block-size-and-margins constraint equation.
91 #[derive(Clone, Copy)]
92 struct BSizeConstraintSolution {
93     block_start: Au,
94     block_size: Au,
95     margin_block_start: Au,
96     margin_block_end: Au
97 }
98 
99 impl BSizeConstraintSolution {
new(block_start: Au, block_size: Au, margin_block_start: Au, margin_block_end: Au) -> BSizeConstraintSolution100     fn new(block_start: Au,
101            block_size: Au,
102            margin_block_start: Au,
103            margin_block_end: Au)
104            -> BSizeConstraintSolution {
105         BSizeConstraintSolution {
106             block_start: block_start,
107             block_size: block_size,
108             margin_block_start: margin_block_start,
109             margin_block_end: margin_block_end,
110         }
111     }
112 
113     /// Solve the vertical constraint equation for absolute non-replaced elements.
114     ///
115     /// CSS Section 10.6.4
116     /// Constraint equation:
117     /// block-start + block-end + block-size + margin-block-start + margin-block-end
118     /// = absolute containing block block-size - (vertical padding and border)
119     /// [aka available_block-size]
120     ///
121     /// Return the solution for the equation.
solve_vertical_constraints_abs_nonreplaced(block_size: MaybeAuto, block_start_margin: MaybeAuto, block_end_margin: MaybeAuto, block_start: MaybeAuto, block_end: MaybeAuto, content_block_size: Au, available_block_size: Au) -> BSizeConstraintSolution122     fn solve_vertical_constraints_abs_nonreplaced(block_size: MaybeAuto,
123                                                   block_start_margin: MaybeAuto,
124                                                   block_end_margin: MaybeAuto,
125                                                   block_start: MaybeAuto,
126                                                   block_end: MaybeAuto,
127                                                   content_block_size: Au,
128                                                   available_block_size: Au)
129                                                   -> BSizeConstraintSolution {
130         let (block_start, block_size, margin_block_start, margin_block_end) =
131             match (block_start, block_end, block_size) {
132                 (MaybeAuto::Auto, MaybeAuto::Auto, MaybeAuto::Auto) => {
133                     let margin_block_start = block_start_margin.specified_or_zero();
134                     let margin_block_end = block_end_margin.specified_or_zero();
135                     // Now it is the same situation as block-start Specified and block-end
136                     // and block-size Auto.
137                     let block_size = content_block_size;
138                     // Use a dummy value for `block_start`, since it has the static position.
139                     (Au(0), block_size, margin_block_start, margin_block_end)
140                 }
141                 (MaybeAuto::Specified(block_start),
142                  MaybeAuto::Specified(block_end),
143                  MaybeAuto::Specified(block_size)) => {
144                     match (block_start_margin, block_end_margin) {
145                         (MaybeAuto::Auto, MaybeAuto::Auto) => {
146                             let total_margin_val =
147                                 available_block_size - block_start - block_end - block_size;
148                             (block_start,
149                              block_size,
150                              total_margin_val.scale_by(0.5),
151                              total_margin_val.scale_by(0.5))
152                         }
153                         (MaybeAuto::Specified(margin_block_start), MaybeAuto::Auto) => {
154                             let sum = block_start + block_end + block_size + margin_block_start;
155                             (block_start,
156                              block_size,
157                              margin_block_start,
158                              available_block_size - sum)
159                         }
160                         (MaybeAuto::Auto, MaybeAuto::Specified(margin_block_end)) => {
161                             let sum = block_start + block_end + block_size + margin_block_end;
162                             (block_start, block_size, available_block_size - sum, margin_block_end)
163                         }
164                         (MaybeAuto::Specified(margin_block_start),
165                          MaybeAuto::Specified(margin_block_end)) => {
166                             // Values are over-constrained. Ignore value for 'block-end'.
167                             (block_start, block_size, margin_block_start, margin_block_end)
168                         }
169                     }
170                 }
171 
172                 // For the rest of the cases, auto values for margin are set to 0
173 
174                 // If only one is Auto, solve for it
175                 (MaybeAuto::Auto,
176                  MaybeAuto::Specified(block_end),
177                  MaybeAuto::Specified(block_size)) => {
178                     let margin_block_start = block_start_margin.specified_or_zero();
179                     let margin_block_end = block_end_margin.specified_or_zero();
180                     let sum = block_end + block_size + margin_block_start + margin_block_end;
181                     (available_block_size - sum, block_size, margin_block_start, margin_block_end)
182                 }
183                 (MaybeAuto::Specified(block_start),
184                  MaybeAuto::Auto,
185                  MaybeAuto::Specified(block_size)) => {
186                     let margin_block_start = block_start_margin.specified_or_zero();
187                     let margin_block_end = block_end_margin.specified_or_zero();
188                     (block_start, block_size, margin_block_start, margin_block_end)
189                 }
190                 (MaybeAuto::Specified(block_start),
191                  MaybeAuto::Specified(block_end),
192                  MaybeAuto::Auto) => {
193                     let margin_block_start = block_start_margin.specified_or_zero();
194                     let margin_block_end = block_end_margin.specified_or_zero();
195                     let sum = block_start + block_end + margin_block_start + margin_block_end;
196                     (block_start, available_block_size - sum, margin_block_start, margin_block_end)
197                 }
198 
199                 // If block-size is auto, then block-size is content block-size. Solve for the
200                 // non-auto value.
201                 (MaybeAuto::Specified(block_start), MaybeAuto::Auto, MaybeAuto::Auto) => {
202                     let margin_block_start = block_start_margin.specified_or_zero();
203                     let margin_block_end = block_end_margin.specified_or_zero();
204                     let block_size = content_block_size;
205                     (block_start, block_size, margin_block_start, margin_block_end)
206                 }
207                 (MaybeAuto::Auto, MaybeAuto::Specified(block_end), MaybeAuto::Auto) => {
208                     let margin_block_start = block_start_margin.specified_or_zero();
209                     let margin_block_end = block_end_margin.specified_or_zero();
210                     let block_size = content_block_size;
211                     let sum = block_end + block_size + margin_block_start + margin_block_end;
212                     (available_block_size - sum, block_size, margin_block_start, margin_block_end)
213                 }
214 
215                 (MaybeAuto::Auto, MaybeAuto::Auto, MaybeAuto::Specified(block_size)) => {
216                     let margin_block_start = block_start_margin.specified_or_zero();
217                     let margin_block_end = block_end_margin.specified_or_zero();
218                     // Use a dummy value for `block_start`, since it has the static position.
219                     (Au(0), block_size, margin_block_start, margin_block_end)
220                 }
221             };
222 
223         BSizeConstraintSolution::new(block_start, block_size, margin_block_start, margin_block_end)
224     }
225 
226     /// Solve the vertical constraint equation for absolute replaced elements.
227     ///
228     /// Assumption: The used value for block-size has already been calculated.
229     ///
230     /// CSS Section 10.6.5
231     /// Constraint equation:
232     /// block-start + block-end + block-size + margin-block-start + margin-block-end
233     /// = absolute containing block block-size - (vertical padding and border)
234     /// [aka available block-size]
235     ///
236     /// Return the solution for the equation.
solve_vertical_constraints_abs_replaced(block_size: Au, block_start_margin: MaybeAuto, block_end_margin: MaybeAuto, block_start: MaybeAuto, block_end: MaybeAuto, _: Au, available_block_size: Au) -> BSizeConstraintSolution237     fn solve_vertical_constraints_abs_replaced(block_size: Au,
238                                                block_start_margin: MaybeAuto,
239                                                block_end_margin: MaybeAuto,
240                                                block_start: MaybeAuto,
241                                                block_end: MaybeAuto,
242                                                _: Au,
243                                                available_block_size: Au)
244                                                -> BSizeConstraintSolution {
245         let (block_start, block_size, margin_block_start, margin_block_end) =
246             match (block_start, block_end) {
247                 (MaybeAuto::Auto, MaybeAuto::Auto) => {
248                     let margin_block_start = block_start_margin.specified_or_zero();
249                     let margin_block_end = block_end_margin.specified_or_zero();
250                     // Use a dummy value for `block_start`, since it has the static position.
251                     (Au(0), block_size, margin_block_start, margin_block_end)
252                 }
253                 (MaybeAuto::Specified(block_start), MaybeAuto::Specified(block_end)) => {
254                     match (block_start_margin, block_end_margin) {
255                         (MaybeAuto::Auto, MaybeAuto::Auto) => {
256                             let total_margin_val = available_block_size - block_start - block_end -
257                                 block_size;
258                             (block_start,
259                              block_size,
260                              total_margin_val.scale_by(0.5),
261                              total_margin_val.scale_by(0.5))
262                         }
263                         (MaybeAuto::Specified(margin_block_start), MaybeAuto::Auto) => {
264                             let sum = block_start + block_end + block_size + margin_block_start;
265                             (block_start,
266                              block_size,
267                              margin_block_start,
268                              available_block_size - sum)
269                         }
270                         (MaybeAuto::Auto, MaybeAuto::Specified(margin_block_end)) => {
271                             let sum = block_start + block_end + block_size + margin_block_end;
272                             (block_start, block_size, available_block_size - sum, margin_block_end)
273                         }
274                         (MaybeAuto::Specified(margin_block_start),
275                          MaybeAuto::Specified(margin_block_end)) => {
276                             // Values are over-constrained. Ignore value for 'block-end'.
277                             (block_start, block_size, margin_block_start, margin_block_end)
278                         }
279                     }
280                 }
281 
282                 // If only one is Auto, solve for it
283                 (MaybeAuto::Auto, MaybeAuto::Specified(block_end)) => {
284                     let margin_block_start = block_start_margin.specified_or_zero();
285                     let margin_block_end = block_end_margin.specified_or_zero();
286                     let sum = block_end + block_size + margin_block_start + margin_block_end;
287                     (available_block_size - sum, block_size, margin_block_start, margin_block_end)
288                 }
289                 (MaybeAuto::Specified(block_start), MaybeAuto::Auto) => {
290                     let margin_block_start = block_start_margin.specified_or_zero();
291                     let margin_block_end = block_end_margin.specified_or_zero();
292                     (block_start, block_size, margin_block_start, margin_block_end)
293                 }
294             };
295         BSizeConstraintSolution::new(block_start, block_size, margin_block_start, margin_block_end)
296     }
297 }
298 
299 /// Performs block-size calculations potentially multiple times, taking
300 /// (assuming an horizontal writing mode) `height`, `min-height`, and `max-height`
301 /// into account. After each call to `next()`, the caller must call `.try()` with the
302 /// current calculated value of `height`.
303 ///
304 /// See CSS 2.1 § 10.7.
305 pub struct CandidateBSizeIterator {
306     block_size: MaybeAuto,
307     max_block_size: Option<Au>,
308     min_block_size: Au,
309     pub candidate_value: Au,
310     status: CandidateBSizeIteratorStatus,
311 }
312 
313 impl CandidateBSizeIterator {
314     /// Creates a new candidate block-size iterator. `block_container_block-size` is `None` if the block-size
315     /// of the block container has not been determined yet. It will always be `Some` in the case of
316     /// absolutely-positioned containing blocks.
new(fragment: &Fragment, block_container_block_size: Option<Au>) -> CandidateBSizeIterator317     pub fn new(fragment: &Fragment, block_container_block_size: Option<Au>)
318                -> CandidateBSizeIterator {
319         // Per CSS 2.1 § 10.7, (assuming an horizontal writing mode,)
320         // percentages in `min-height` and `max-height` refer to the height of
321         // the containing block.
322         // If that is not determined yet by the time we need to resolve
323         // `min-height` and `max-height`, percentage values are ignored.
324 
325         let block_size = match (fragment.style.content_block_size(), block_container_block_size) {
326             (LengthOrPercentageOrAuto::Percentage(percent), Some(block_container_block_size)) => {
327                 MaybeAuto::Specified(block_container_block_size.scale_by(percent.0))
328             }
329             (LengthOrPercentageOrAuto::Calc(calc), _) => {
330                 MaybeAuto::from_option(calc.to_used_value(block_container_block_size))
331             }
332             (LengthOrPercentageOrAuto::Percentage(_), None) |
333             (LengthOrPercentageOrAuto::Auto, _) => MaybeAuto::Auto,
334             (LengthOrPercentageOrAuto::Length(length), _) => MaybeAuto::Specified(Au::from(length)),
335         };
336         let max_block_size = match (fragment.style.max_block_size(), block_container_block_size) {
337             (LengthOrPercentageOrNone::Percentage(percent), Some(block_container_block_size)) => {
338                 Some(block_container_block_size.scale_by(percent.0))
339             }
340             (LengthOrPercentageOrNone::Calc(calc), _) => {
341                 calc.to_used_value(block_container_block_size)
342             }
343             (LengthOrPercentageOrNone::Percentage(_), None) |
344             (LengthOrPercentageOrNone::None, _) => None,
345             (LengthOrPercentageOrNone::Length(length), _) => Some(Au::from(length)),
346         };
347         let min_block_size = match (fragment.style.min_block_size(), block_container_block_size) {
348             (LengthOrPercentage::Percentage(percent), Some(block_container_block_size)) => {
349                 block_container_block_size.scale_by(percent.0)
350             }
351             (LengthOrPercentage::Calc(calc), _) => {
352                 calc.to_used_value(block_container_block_size).unwrap_or(Au(0))
353             }
354             (LengthOrPercentage::Percentage(_), None) => Au(0),
355             (LengthOrPercentage::Length(length), _) => Au::from(length),
356         };
357 
358         // If the style includes `box-sizing: border-box`, subtract the border and padding.
359         let adjustment_for_box_sizing = match fragment.style.get_position().box_sizing {
360             BoxSizing::BorderBox => fragment.border_padding.block_start_end(),
361             BoxSizing::ContentBox => Au(0),
362         };
363 
364         return CandidateBSizeIterator {
365             block_size: block_size.map(|size| adjust(size, adjustment_for_box_sizing)),
366             max_block_size: max_block_size.map(|size| adjust(size, adjustment_for_box_sizing)),
367             min_block_size: adjust(min_block_size, adjustment_for_box_sizing),
368             candidate_value: Au(0),
369             status: CandidateBSizeIteratorStatus::Initial,
370         };
371 
372         fn adjust(size: Au, delta: Au) -> Au {
373             max(size - delta, Au(0))
374         }
375     }
376 }
377 
378 impl Iterator for CandidateBSizeIterator {
379     type Item = MaybeAuto;
next(&mut self) -> Option<MaybeAuto>380     fn next(&mut self) -> Option<MaybeAuto> {
381         self.status = match self.status {
382             CandidateBSizeIteratorStatus::Initial => CandidateBSizeIteratorStatus::Trying,
383             CandidateBSizeIteratorStatus::Trying => {
384                 match self.max_block_size {
385                     Some(max_block_size) if self.candidate_value > max_block_size => {
386                         CandidateBSizeIteratorStatus::TryingMax
387                     }
388                     _ if self.candidate_value < self.min_block_size => {
389                         CandidateBSizeIteratorStatus::TryingMin
390                     }
391                     _ => CandidateBSizeIteratorStatus::Found,
392                 }
393             }
394             CandidateBSizeIteratorStatus::TryingMax => {
395                 if self.candidate_value < self.min_block_size {
396                     CandidateBSizeIteratorStatus::TryingMin
397                 } else {
398                     CandidateBSizeIteratorStatus::Found
399                 }
400             }
401             CandidateBSizeIteratorStatus::TryingMin | CandidateBSizeIteratorStatus::Found => {
402                 CandidateBSizeIteratorStatus::Found
403             }
404         };
405 
406         match self.status {
407             CandidateBSizeIteratorStatus::Trying => Some(self.block_size),
408             CandidateBSizeIteratorStatus::TryingMax => {
409                 Some(MaybeAuto::Specified(self.max_block_size.unwrap()))
410             }
411             CandidateBSizeIteratorStatus::TryingMin => {
412                 Some(MaybeAuto::Specified(self.min_block_size))
413             }
414             CandidateBSizeIteratorStatus::Found => None,
415             CandidateBSizeIteratorStatus::Initial => panic!(),
416         }
417     }
418 }
419 
420 enum CandidateBSizeIteratorStatus {
421     Initial,
422     Trying,
423     TryingMax,
424     TryingMin,
425     Found,
426 }
427 
428 // A helper function used in block-size calculation.
translate_including_floats(cur_b: &mut Au, delta: Au, floats: &mut Floats)429 fn translate_including_floats(cur_b: &mut Au, delta: Au, floats: &mut Floats) {
430     *cur_b = *cur_b + delta;
431     let writing_mode = floats.writing_mode;
432     floats.translate(LogicalSize::new(writing_mode, Au(0), -delta));
433 }
434 
435 /// The real assign-block-sizes traversal for flows with position 'absolute'.
436 ///
437 /// This is a traversal of an Absolute Flow tree.
438 /// - Relatively positioned flows and the Root flow start new Absolute flow trees.
439 /// - The kids of a flow in this tree will be the flows for which it is the
440 /// absolute Containing Block.
441 /// - Thus, leaf nodes and inner non-root nodes are all Absolute Flows.
442 ///
443 /// A Flow tree can have several Absolute Flow trees (depending on the number
444 /// of relatively positioned flows it has).
445 ///
446 /// Note that flows with position 'fixed' just form a flat list as they all
447 /// have the Root flow as their CB.
448 pub struct AbsoluteAssignBSizesTraversal<'a>(pub &'a SharedStyleContext<'a>);
449 
450 impl<'a> PreorderFlowTraversal for AbsoluteAssignBSizesTraversal<'a> {
451     #[inline]
process(&self, flow: &mut Flow)452     fn process(&self, flow: &mut Flow) {
453         if !flow.is_block_like() {
454             return
455         }
456 
457         // This flow might not be an absolutely positioned flow if it is the root of the tree.
458         let block = flow.as_mut_block();
459         if !block.base.flags.contains(FlowFlags::IS_ABSOLUTELY_POSITIONED) {
460             return;
461         }
462 
463         if !block.base.restyle_damage.intersects(ServoRestyleDamage::REFLOW_OUT_OF_FLOW | ServoRestyleDamage::REFLOW) {
464             return
465         }
466 
467         block.calculate_absolute_block_size_and_margins(self.0);
468     }
469 }
470 
471 pub enum BlockType {
472     Replaced,
473     NonReplaced,
474     AbsoluteReplaced,
475     AbsoluteNonReplaced,
476     FloatReplaced,
477     FloatNonReplaced,
478     InlineBlockReplaced,
479     InlineBlockNonReplaced,
480     InlineFlexItem,
481 }
482 
483 #[derive(Clone, PartialEq)]
484 pub enum MarginsMayCollapseFlag {
485     MarginsMayCollapse,
486     MarginsMayNotCollapse,
487 }
488 
489 #[derive(Debug, PartialEq)]
490 pub enum FormattingContextType {
491     None,
492     Block,
493     Other,
494 }
495 
496 #[allow(unsafe_code)]
497 unsafe impl ::flow::HasBaseFlow for BlockFlow {}
498 
499 // A block formatting context.
500 #[derive(Serialize)]
501 #[repr(C)]
502 pub struct BlockFlow {
503     /// Data common to all flows.
504     pub base: BaseFlow,
505 
506     /// The associated fragment.
507     pub fragment: Fragment,
508 
509     /// Additional floating flow members.
510     pub float: Option<Box<FloatedBlockInfo>>,
511 
512     /// Various flags.
513     flags: BlockFlowFlags,
514 }
515 
516 bitflags! {
517     struct BlockFlowFlags: u8 {
518         #[doc = "If this is set, then this block flow is the root flow."]
519         const IS_ROOT = 0b0000_0001;
520         #[doc = "If this is set, then this block flow has overflow and it will scroll."]
521         const HAS_SCROLLING_OVERFLOW = 0b0000_0010;
522     }
523 }
524 
525 impl Serialize for BlockFlowFlags {
serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error>526     fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
527         self.bits().serialize(serializer)
528     }
529 }
530 
531 impl BlockFlow {
from_fragment(fragment: Fragment) -> BlockFlow532     pub fn from_fragment(fragment: Fragment) -> BlockFlow {
533         BlockFlow::from_fragment_and_float_kind(fragment, None)
534     }
535 
from_fragment_and_float_kind(fragment: Fragment, float_kind: Option<FloatKind>) -> BlockFlow536     pub fn from_fragment_and_float_kind(fragment: Fragment, float_kind: Option<FloatKind>)
537                                         -> BlockFlow {
538         let writing_mode = fragment.style().writing_mode;
539         BlockFlow {
540             base: BaseFlow::new(Some(fragment.style()), writing_mode, match float_kind {
541                 Some(_) => ForceNonfloatedFlag::FloatIfNecessary,
542                 None => ForceNonfloatedFlag::ForceNonfloated,
543             }),
544             fragment: fragment,
545             float: float_kind.map(|kind| Box::new(FloatedBlockInfo::new(kind))),
546             flags: BlockFlowFlags::empty(),
547         }
548     }
549 
550     /// Return the type of this block.
551     ///
552     /// This determines the algorithm used to calculate inline-size, block-size, and the
553     /// relevant margins for this Block.
block_type(&self) -> BlockType554     pub fn block_type(&self) -> BlockType {
555         if self.base.flags.contains(FlowFlags::IS_ABSOLUTELY_POSITIONED) {
556             if self.fragment.is_replaced() {
557                 BlockType::AbsoluteReplaced
558             } else {
559                 BlockType::AbsoluteNonReplaced
560             }
561         } else if self.is_inline_flex_item() {
562             BlockType::InlineFlexItem
563         } else if self.base.flags.is_float() {
564             if self.fragment.is_replaced() {
565                 BlockType::FloatReplaced
566             } else {
567                 BlockType::FloatNonReplaced
568             }
569         } else if self.is_inline_block_or_inline_flex() {
570             if self.fragment.is_replaced() {
571                 BlockType::InlineBlockReplaced
572             } else {
573                 BlockType::InlineBlockNonReplaced
574             }
575         } else {
576             if self.fragment.is_replaced() {
577                 BlockType::Replaced
578             } else {
579                 BlockType::NonReplaced
580             }
581         }
582     }
583 
584     /// Compute the actual inline size and position for this block.
compute_used_inline_size(&mut self, shared_context: &SharedStyleContext, containing_block_inline_size: Au)585     pub fn compute_used_inline_size(&mut self,
586                                     shared_context: &SharedStyleContext,
587                                     containing_block_inline_size: Au) {
588         let block_type = self.block_type();
589         match block_type {
590             BlockType::AbsoluteReplaced => {
591                 let inline_size_computer = AbsoluteReplaced;
592                 inline_size_computer.compute_used_inline_size(self,
593                                                               shared_context,
594                                                               containing_block_inline_size);
595             }
596             BlockType::AbsoluteNonReplaced => {
597                 let inline_size_computer = AbsoluteNonReplaced;
598                 inline_size_computer.compute_used_inline_size(self,
599                                                               shared_context,
600                                                               containing_block_inline_size);
601             }
602             BlockType::FloatReplaced => {
603                 let inline_size_computer = FloatReplaced;
604                 inline_size_computer.compute_used_inline_size(self,
605                                                               shared_context,
606                                                               containing_block_inline_size);
607             }
608             BlockType::FloatNonReplaced => {
609                 let inline_size_computer = FloatNonReplaced;
610                 inline_size_computer.compute_used_inline_size(self,
611                                                               shared_context,
612                                                               containing_block_inline_size);
613             }
614             BlockType::InlineBlockReplaced => {
615                 let inline_size_computer = InlineBlockReplaced;
616                 inline_size_computer.compute_used_inline_size(self,
617                                                               shared_context,
618                                                               containing_block_inline_size);
619             }
620             BlockType::InlineBlockNonReplaced => {
621                 let inline_size_computer = InlineBlockNonReplaced;
622                 inline_size_computer.compute_used_inline_size(self,
623                                                               shared_context,
624                                                               containing_block_inline_size);
625             }
626             BlockType::Replaced => {
627                 let inline_size_computer = BlockReplaced;
628                 inline_size_computer.compute_used_inline_size(self,
629                                                               shared_context,
630                                                               containing_block_inline_size);
631             }
632             BlockType::NonReplaced => {
633                 let inline_size_computer = BlockNonReplaced;
634                 inline_size_computer.compute_used_inline_size(self,
635                                                               shared_context,
636                                                               containing_block_inline_size);
637             }
638             BlockType::InlineFlexItem => {
639                 let inline_size_computer = InlineFlexItem;
640                 inline_size_computer.compute_used_inline_size(self,
641                                                               shared_context,
642                                                               containing_block_inline_size);
643             }
644         }
645     }
646 
647     /// Return this flow's fragment.
fragment(&mut self) -> &mut Fragment648     pub fn fragment(&mut self) -> &mut Fragment {
649         &mut self.fragment
650     }
651 
stacking_relative_border_box(&self, coor: CoordinateSystem) -> Rect<Au>652     pub fn stacking_relative_border_box(&self, coor: CoordinateSystem) -> Rect<Au> {
653         return self.fragment.stacking_relative_border_box(
654             &self.base.stacking_relative_position,
655             &self.base.early_absolute_position_info.relative_containing_block_size,
656             self.base.early_absolute_position_info.relative_containing_block_mode,
657             coor);
658     }
659 
660     /// Return the size of the containing block for the given immediate absolute descendant of this
661     /// flow.
662     ///
663     /// Right now, this only gets the containing block size for absolutely positioned elements.
664     /// Note: We assume this is called in a top-down traversal, so it is ok to reference the CB.
665     #[inline]
containing_block_size(&self, viewport_size: &Size2D<Au>, descendant: OpaqueFlow) -> LogicalSize<Au>666     pub fn containing_block_size(&self, viewport_size: &Size2D<Au>, descendant: OpaqueFlow)
667                                  -> LogicalSize<Au> {
668         debug_assert!(self.base.flags.contains(FlowFlags::IS_ABSOLUTELY_POSITIONED));
669         if self.is_fixed() || self.is_root() {
670             // Initial containing block is the CB for the root
671             LogicalSize::from_physical(self.base.writing_mode, *viewport_size)
672         } else {
673             self.base.absolute_cb.generated_containing_block_size(descendant)
674         }
675     }
676 
677     /// Return shrink-to-fit inline-size.
678     ///
679     /// This is where we use the preferred inline-sizes and minimum inline-sizes
680     /// calculated in the bubble-inline-sizes traversal.
get_shrink_to_fit_inline_size(&self, available_inline_size: Au) -> Au681     pub fn get_shrink_to_fit_inline_size(&self, available_inline_size: Au) -> Au {
682         let content_intrinsic_inline_sizes = self.content_intrinsic_inline_sizes();
683         min(content_intrinsic_inline_sizes.preferred_inline_size,
684             max(content_intrinsic_inline_sizes.minimum_inline_size, available_inline_size))
685     }
686 
687     /// If this is the root flow, shifts all kids down and adjusts our size to account for
688     /// root flow margins, which should never be collapsed according to CSS § 8.3.1.
689     ///
690     /// TODO(#2017, pcwalton): This is somewhat inefficient (traverses kids twice); can we do
691     /// better?
adjust_fragments_for_collapsed_margins_if_root(&mut self, shared_context: &SharedStyleContext)692     fn adjust_fragments_for_collapsed_margins_if_root(&mut self,
693                                                       shared_context: &SharedStyleContext) {
694         if !self.is_root() {
695             return
696         }
697 
698         let (block_start_margin_value, block_end_margin_value) =
699             match self.base.collapsible_margins {
700                 CollapsibleMargins::CollapseThrough(_) => {
701                     panic!("Margins unexpectedly collapsed through root flow.")
702                 }
703                 CollapsibleMargins::Collapse(block_start_margin, block_end_margin) => {
704                     (block_start_margin.collapse(), block_end_margin.collapse())
705                 }
706                 CollapsibleMargins::None(block_start, block_end) => (block_start, block_end),
707             };
708 
709         // Shift all kids down (or up, if margins are negative) if necessary.
710         if block_start_margin_value != Au(0) {
711             for kid in self.base.child_iter_mut() {
712                 let kid_base = kid.mut_base();
713                 kid_base.position.start.b = kid_base.position.start.b + block_start_margin_value
714             }
715         }
716 
717         // FIXME(#2003, pcwalton): The max is taken here so that you can scroll the page, but this
718         // is not correct behavior according to CSS 2.1 § 10.5. Instead I think we should treat the
719         // root element as having `overflow: scroll` and use the layers-based scrolling
720         // infrastructure to make it scrollable.
721         let viewport_size =
722             LogicalSize::from_physical(self.fragment.style.writing_mode,
723                                        shared_context.viewport_size());
724         let block_size = max(viewport_size.block,
725                              self.fragment.border_box.size.block + block_start_margin_value +
726                              block_end_margin_value);
727 
728         self.base.position.size.block = block_size;
729         self.fragment.border_box.size.block = block_size;
730     }
731 
732     // FIXME: Record enough info to deal with fragmented decorations.
733     // See https://drafts.csswg.org/css-break/#break-decoration
734     // For borders, this might be `enum FragmentPosition { First, Middle, Last }`
clone_with_children(&self, new_children: FlowList) -> BlockFlow735     fn clone_with_children(&self, new_children: FlowList) -> BlockFlow {
736         BlockFlow {
737             base: self.base.clone_with_children(new_children),
738             fragment: self.fragment.clone(),
739             float: self.float.clone(),
740             ..*self
741         }
742     }
743 
744     /// Writes in the size of the relative containing block for children. (This information
745     /// is also needed to handle RTL.)
propagate_early_absolute_position_info_to_children(&mut self)746     fn propagate_early_absolute_position_info_to_children(&mut self) {
747         for kid in self.base.child_iter_mut() {
748             kid.mut_base().early_absolute_position_info = EarlyAbsolutePositionInfo {
749                 relative_containing_block_size: self.fragment.content_box().size,
750                 relative_containing_block_mode: self.fragment.style().writing_mode,
751             }
752         }
753     }
754 
755     /// Assign block-size for current flow.
756     ///
757     /// * Collapse margins for flow's children and set in-flow child flows' block offsets now that
758     ///   we know their block-sizes.
759     /// * Calculate and set the block-size of the current flow.
760     /// * Calculate block-size, vertical margins, and block offset for the flow's box using CSS §
761     ///   10.6.7.
762     ///
763     /// For absolute flows, we store the calculated content block-size for the flow. We defer the
764     /// calculation of the other values until a later traversal.
765     ///
766     /// When `fragmentation_context` is given (not `None`), this should fit as much of the content
767     /// as possible within the available block size.
768     /// If there is more content (that doesn’t fit), this flow is *fragmented*
769     /// with the extra content moved to another fragment (a flow like this one) which is returned.
770     /// See `Flow::fragment`.
771     ///
772     /// The return value is always `None` when `fragmentation_context` is `None`.
773     ///
774     /// `inline(always)` because this is only ever called by in-order or non-in-order top-level
775     /// methods.
776     #[inline(always)]
assign_block_size_block_base(&mut self, layout_context: &LayoutContext, mut fragmentation_context: Option<FragmentationContext>, margins_may_collapse: MarginsMayCollapseFlag) -> Option<Arc<Flow>>777     pub fn assign_block_size_block_base(&mut self,
778                                         layout_context: &LayoutContext,
779                                         mut fragmentation_context: Option<FragmentationContext>,
780                                         margins_may_collapse: MarginsMayCollapseFlag)
781                                         -> Option<Arc<Flow>> {
782         let _scope = layout_debug_scope!("assign_block_size_block_base {:x}",
783                                          self.base.debug_id());
784 
785         let mut break_at = None;
786         let content_box = self.fragment.content_box();
787         if self.base.restyle_damage.contains(ServoRestyleDamage::REFLOW) {
788             // Our current border-box position.
789             let mut cur_b = Au(0);
790 
791             // Absolute positioning establishes a block formatting context. Don't propagate floats
792             // in or out. (But do propagate them between kids.)
793             if self.base.flags.contains(FlowFlags::IS_ABSOLUTELY_POSITIONED) ||
794                     margins_may_collapse != MarginsMayCollapseFlag::MarginsMayCollapse {
795                 self.base.floats = Floats::new(self.fragment.style.writing_mode);
796             }
797 
798             let writing_mode = self.base.floats.writing_mode;
799             self.base.floats.translate(LogicalSize::new(
800                 writing_mode, -self.fragment.inline_start_offset(), Au(0)));
801 
802             // The sum of our block-start border and block-start padding.
803             let block_start_offset = self.fragment.border_padding.block_start;
804             translate_including_floats(&mut cur_b, block_start_offset, &mut self.base.floats);
805 
806             let can_collapse_block_start_margin_with_kids =
807                 margins_may_collapse == MarginsMayCollapseFlag::MarginsMayCollapse &&
808                 !self.base.flags.contains(FlowFlags::IS_ABSOLUTELY_POSITIONED) &&
809                 self.fragment.border_padding.block_start == Au(0);
810             let mut margin_collapse_info = MarginCollapseInfo::initialize_block_start_margin(
811                 &self.fragment,
812                 can_collapse_block_start_margin_with_kids);
813 
814             // At this point, `cur_b` is at the content edge of our box. Now iterate over children.
815             let mut floats = self.base.floats.clone();
816             let thread_id = self.base.thread_id;
817             let (mut had_floated_children, mut had_children_with_clearance) = (false, false);
818             for (child_index, kid) in self.base.child_iter_mut().enumerate() {
819                 if kid.base().flags.contains(FlowFlags::IS_ABSOLUTELY_POSITIONED) {
820                     // Assume that the *hypothetical box* for an absolute flow starts immediately
821                     // after the margin-end border edge of the previous flow.
822                     if kid.base().flags.contains(FlowFlags::BLOCK_POSITION_IS_STATIC) {
823                         let previous_bottom_margin = margin_collapse_info.current_float_ceiling();
824 
825                         kid.mut_base().position.start.b = cur_b +
826                             kid.base().collapsible_margins
827                                            .block_start_margin_for_noncollapsible_context() +
828                             previous_bottom_margin
829                     }
830                     kid.place_float_if_applicable();
831                     if !kid.base().flags.is_float() {
832                         kid.assign_block_size_for_inorder_child_if_necessary(layout_context,
833                                                                              thread_id,
834                                                                              content_box);
835                     }
836 
837                     // Skip the collapsing and float processing for absolute flow kids and continue
838                     // with the next flow.
839                     continue
840                 }
841 
842                 let previous_b = cur_b;
843                 if let Some(ctx) = fragmentation_context {
844                     let child_ctx = FragmentationContext {
845                         available_block_size: ctx.available_block_size - cur_b,
846                         this_fragment_is_empty: ctx.this_fragment_is_empty,
847                     };
848                     if let Some(remaining) = kid.fragment(layout_context, Some(child_ctx)) {
849                         break_at = Some((child_index + 1, Some(remaining)));
850                     }
851                 }
852 
853                 // Assign block-size now for the child if it might have floats in and we couldn't
854                 // before.
855                 kid.mut_base().floats = floats.clone();
856                 if kid.base().flags.is_float() {
857                     had_floated_children = true;
858                     kid.mut_base().position.start.b = cur_b;
859                     {
860                         let kid_block = kid.as_mut_block();
861                         let float_ceiling = margin_collapse_info.current_float_ceiling();
862                         kid_block.float.as_mut().unwrap().float_ceiling = float_ceiling
863                     }
864                     kid.place_float_if_applicable();
865 
866                     let kid_base = kid.mut_base();
867                     floats = kid_base.floats.clone();
868                     continue
869                 }
870 
871                 // If we have clearance, assume there are no floats in.
872                 //
873                 // FIXME(#2008, pcwalton): This could be wrong if we have `clear: left` or `clear:
874                 // right` and there are still floats to impact, of course. But this gets
875                 // complicated with margin collapse. Possibly the right thing to do is to lay out
876                 // the block again in this rare case. (Note that WebKit can lay blocks out twice;
877                 // this may be related, although I haven't looked into it closely.)
878                 if kid.base().flags.clears_floats() {
879                     kid.mut_base().floats = Floats::new(self.fragment.style.writing_mode)
880                 }
881 
882                 // Lay the child out if this was an in-order traversal.
883                 let need_to_process_child_floats =
884                     kid.assign_block_size_for_inorder_child_if_necessary(layout_context,
885                                                                          thread_id,
886                                                                          content_box);
887 
888                 if !had_children_with_clearance &&
889                         floats.is_present() &&
890                         (kid.base().flags.contains(FlowFlags::CLEARS_LEFT) ||
891                          kid.base().flags.contains(FlowFlags::CLEARS_RIGHT)) {
892                     had_children_with_clearance = true
893                 }
894 
895                 // Handle any (possibly collapsed) top margin.
896                 let delta = margin_collapse_info.advance_block_start_margin(
897                     &kid.base().collapsible_margins,
898                     !had_children_with_clearance);
899                 translate_including_floats(&mut cur_b, delta, &mut floats);
900 
901                 // Collapse-through margins should be placed at the top edge,
902                 // so we'll handle the delta after the bottom margin is processed
903                 if let CollapsibleMargins::CollapseThrough(_) = kid.base().collapsible_margins {
904                     cur_b = cur_b - delta;
905                 }
906 
907                 // Clear past the floats that came in, if necessary.
908                 let clearance = match (kid.base().flags.contains(FlowFlags::CLEARS_LEFT),
909                                        kid.base().flags.contains(FlowFlags::CLEARS_RIGHT)) {
910                     (false, false) => Au(0),
911                     (true, false) => floats.clearance(ClearType::Left),
912                     (false, true) => floats.clearance(ClearType::Right),
913                     (true, true) => floats.clearance(ClearType::Both),
914                 };
915                 translate_including_floats(&mut cur_b, clearance, &mut floats);
916 
917                 // At this point, `cur_b` is at the border edge of the child.
918                 kid.mut_base().position.start.b = cur_b;
919 
920                 // Now pull out the child's outgoing floats. We didn't do this immediately after
921                 // the `assign_block_size_for_inorder_child_if_necessary` call because clearance on
922                 // a block operates on the floats that come *in*, not the floats that go *out*.
923                 if need_to_process_child_floats {
924                     floats = kid.mut_base().floats.clone()
925                 }
926 
927                 // Move past the child's border box. Do not use the `translate_including_floats`
928                 // function here because the child has already translated floats past its border
929                 // box.
930                 let kid_base = kid.mut_base();
931                 cur_b = cur_b + kid_base.position.size.block;
932 
933                 // Handle any (possibly collapsed) block-end margin.
934                 let delta =
935                     margin_collapse_info.advance_block_end_margin(&kid_base.collapsible_margins);
936                 translate_including_floats(&mut cur_b, delta, &mut floats);
937 
938                 // Collapse-through margin should be placed at the top edge of the flow.
939                 let collapse_delta = match kid_base.collapsible_margins {
940                     CollapsibleMargins::CollapseThrough(_) => {
941                         let delta = margin_collapse_info.current_float_ceiling();
942                         cur_b = cur_b + delta;
943                         kid_base.position.start.b = kid_base.position.start.b + delta;
944                         delta
945                     }
946                     _ => Au(0)
947                 };
948 
949                 if break_at.is_some() {
950                     break
951                 }
952 
953                 if let Some(ref mut ctx) = fragmentation_context {
954                     if cur_b > ctx.available_block_size && !ctx.this_fragment_is_empty {
955                         break_at = Some((child_index, None));
956                         cur_b = previous_b;
957                         break
958                     }
959                     ctx.this_fragment_is_empty = false
960                 }
961 
962                 // For consecutive collapse-through flows, their top margin should be calculated
963                 // from the same baseline.
964                 cur_b = cur_b - collapse_delta;
965             }
966 
967             // Add in our block-end margin and compute our collapsible margins.
968             let can_collapse_block_end_margin_with_kids =
969                 margins_may_collapse == MarginsMayCollapseFlag::MarginsMayCollapse &&
970                 !self.base.flags.contains(FlowFlags::IS_ABSOLUTELY_POSITIONED) &&
971                 self.fragment.border_padding.block_end == Au(0);
972             let (collapsible_margins, delta) =
973                 margin_collapse_info.finish_and_compute_collapsible_margins(
974                 &self.fragment,
975                 self.base.block_container_explicit_block_size,
976                 can_collapse_block_end_margin_with_kids,
977                 !had_floated_children);
978             self.base.collapsible_margins = collapsible_margins;
979             translate_including_floats(&mut cur_b, delta, &mut floats);
980 
981             let mut block_size = cur_b - block_start_offset;
982             let is_root = self.is_root();
983 
984             if is_root || self.formatting_context_type() != FormattingContextType::None ||
985                     self.base.flags.contains(FlowFlags::IS_ABSOLUTELY_POSITIONED) {
986                 // The content block-size includes all the floats per CSS 2.1 § 10.6.7. The easiest
987                 // way to handle this is to just treat it as clearance.
988                 block_size = block_size + floats.clearance(ClearType::Both);
989             }
990 
991             if self.base.flags.contains(FlowFlags::IS_ABSOLUTELY_POSITIONED) {
992                 // FIXME(#2003, pcwalton): The max is taken here so that you can scroll the page,
993                 // but this is not correct behavior according to CSS 2.1 § 10.5. Instead I think we
994                 // should treat the root element as having `overflow: scroll` and use the layers-
995                 // based scrolling infrastructure to make it scrollable.
996                 if is_root {
997                     let viewport_size =
998                         LogicalSize::from_physical(self.fragment.style.writing_mode,
999                                                    layout_context.shared_context().viewport_size());
1000                     block_size = max(viewport_size.block, block_size)
1001                 }
1002 
1003                 // Store the content block-size for use in calculating the absolute flow's
1004                 // dimensions later.
1005                 //
1006                 // FIXME(pcwalton): This looks not idempotent. Is it?
1007                 self.fragment.border_box.size.block = block_size;
1008             }
1009 
1010 
1011             if self.base.flags.contains(FlowFlags::IS_ABSOLUTELY_POSITIONED) {
1012                 self.propagate_early_absolute_position_info_to_children();
1013                 return None
1014             }
1015 
1016             // Compute any explicitly-specified block size.
1017             // Can't use `for` because we assign to `candidate_block_size_iterator.candidate_value`.
1018             let mut candidate_block_size_iterator = CandidateBSizeIterator::new(
1019                 &self.fragment,
1020                 self.base.block_container_explicit_block_size);
1021             while let Some(candidate_block_size) = candidate_block_size_iterator.next() {
1022                 candidate_block_size_iterator.candidate_value =
1023                     match candidate_block_size {
1024                         MaybeAuto::Auto => block_size,
1025                         MaybeAuto::Specified(value) => value
1026                     }
1027             }
1028 
1029             // Adjust `cur_b` as necessary to account for the explicitly-specified block-size.
1030             block_size = candidate_block_size_iterator.candidate_value;
1031             let delta = block_size - (cur_b - block_start_offset);
1032             translate_including_floats(&mut cur_b, delta, &mut floats);
1033 
1034             // Take border and padding into account.
1035             let block_end_offset = self.fragment.border_padding.block_end;
1036             translate_including_floats(&mut cur_b, block_end_offset, &mut floats);
1037 
1038             // Now that `cur_b` is at the block-end of the border box, compute the final border box
1039             // position.
1040             self.fragment.border_box.size.block = cur_b;
1041             self.fragment.border_box.start.b = Au(0);
1042             self.base.position.size.block = cur_b;
1043 
1044             self.propagate_early_absolute_position_info_to_children();
1045 
1046             // Translate the current set of floats back into the parent coordinate system in the
1047             // inline direction, and store them in the flow so that flows that come later in the
1048             // document can access them.
1049             floats.translate(LogicalSize::new(writing_mode,
1050                                               self.fragment.inline_start_offset(),
1051                                               Au(0)));
1052             self.base.floats = floats.clone();
1053             self.adjust_fragments_for_collapsed_margins_if_root(layout_context.shared_context());
1054         } else {
1055             // We don't need to reflow, but we still need to perform in-order traversals if
1056             // necessary.
1057             let thread_id = self.base.thread_id;
1058             for kid in self.base.child_iter_mut() {
1059                 kid.assign_block_size_for_inorder_child_if_necessary(layout_context,
1060                                                                      thread_id,
1061                                                                      content_box);
1062             }
1063         }
1064 
1065         if (&*self as &Flow).contains_roots_of_absolute_flow_tree() {
1066             // Assign block-sizes for all flows in this absolute flow tree.
1067             // This is preorder because the block-size of an absolute flow may depend on
1068             // the block-size of its containing block, which may also be an absolute flow.
1069             let assign_abs_b_sizes = AbsoluteAssignBSizesTraversal(layout_context.shared_context());
1070             assign_abs_b_sizes.traverse_absolute_flows(&mut *self);
1071         }
1072 
1073         // Don't remove the dirty bits yet if we're absolutely-positioned, since our final size
1074         // has not been calculated yet. (See `calculate_absolute_block_size_and_margins` for that.)
1075         // Also don't remove the dirty bits if we're a block formatting context since our inline
1076         // size has not yet been computed. (See `assign_inline_position_for_formatting_context()`.)
1077         if (self.base.flags.is_float() ||
1078                 self.formatting_context_type() == FormattingContextType::None) &&
1079                 !self.base.flags.contains(FlowFlags::IS_ABSOLUTELY_POSITIONED) {
1080             self.base.restyle_damage.remove(ServoRestyleDamage::REFLOW_OUT_OF_FLOW | ServoRestyleDamage::REFLOW);
1081             self.fragment.restyle_damage.remove(ServoRestyleDamage::REFLOW_OUT_OF_FLOW | ServoRestyleDamage::REFLOW);
1082         }
1083 
1084         break_at.and_then(|(i, child_remaining)| {
1085             if i == self.base.children.len() && child_remaining.is_none() {
1086                 None
1087             } else {
1088                 let mut children = self.base.children.split_off(i);
1089                 if let Some(child) = child_remaining {
1090                     children.push_front_arc(child);
1091                 }
1092                 Some(Arc::new(self.clone_with_children(children)) as Arc<Flow>)
1093             }
1094         })
1095     }
1096 
1097     /// Add placement information about current float flow for use by the parent.
1098     ///
1099     /// Also, use information given by parent about other floats to find out our relative position.
1100     ///
1101     /// This does not give any information about any float descendants because they do not affect
1102     /// elements outside of the subtree rooted at this float.
1103     ///
1104     /// This function is called on a kid flow by a parent. Therefore, `assign_block_size_float` was
1105     /// already called on this kid flow by the traversal function. So, the values used are
1106     /// well-defined.
place_float(&mut self)1107     pub fn place_float(&mut self) {
1108         let block_size = self.fragment.border_box.size.block;
1109         let clearance = match self.fragment.clear() {
1110             None => Au(0),
1111             Some(clear) => self.base.floats.clearance(clear),
1112         };
1113 
1114         let float_info: FloatedBlockInfo = (**self.float.as_ref().unwrap()).clone();
1115 
1116         // Our `position` field accounts for positive margins, but not negative margins. (See
1117         // calculation of `extra_inline_size_from_margin` below.) Negative margins must be taken
1118         // into account for float placement, however. So we add them in here.
1119         let inline_size_for_float_placement = self.base.position.size.inline +
1120             min(Au(0), self.fragment.margin.inline_start_end());
1121 
1122         let info = PlacementInfo {
1123             size: LogicalSize::new(
1124                 self.fragment.style.writing_mode,
1125                 inline_size_for_float_placement,
1126                 block_size + self.fragment.margin.block_start_end())
1127                       .convert(self.fragment.style.writing_mode, self.base.floats.writing_mode),
1128             ceiling: clearance + float_info.float_ceiling,
1129             max_inline_size: float_info.containing_inline_size,
1130             kind: float_info.float_kind,
1131         };
1132 
1133         // Place the float and return the `Floats` back to the parent flow.
1134         // After, grab the position and use that to set our position.
1135         self.base.floats.add_float(&info);
1136 
1137         // FIXME (mbrubeck) Get the correct container size for self.base.floats;
1138         let container_size = Size2D::new(self.base.block_container_inline_size, Au(0));
1139 
1140         // Move in from the margin edge, as per CSS 2.1 § 9.5, floats may not overlap anything on
1141         // their margin edges.
1142         let float_offset = self.base.floats.last_float_pos().unwrap()
1143                                            .convert(self.base.floats.writing_mode,
1144                                                     self.base.writing_mode,
1145                                                     container_size)
1146                                            .start;
1147         let margin_offset = LogicalPoint::new(self.base.writing_mode,
1148                                               Au(0),
1149                                               self.fragment.margin.block_start);
1150 
1151         let mut origin = LogicalPoint::new(self.base.writing_mode,
1152                                            self.base.position.start.i,
1153                                            self.base.position.start.b);
1154         origin = origin.add_point(&float_offset).add_point(&margin_offset);
1155         self.base.position = LogicalRect::from_point_size(self.base.writing_mode,
1156                                                           origin,
1157                                                           self.base.position.size);
1158     }
1159 
explicit_block_containing_size(&self, shared_context: &SharedStyleContext) -> Option<Au>1160     pub fn explicit_block_containing_size(&self, shared_context: &SharedStyleContext) -> Option<Au> {
1161         if self.is_root() || self.is_fixed() {
1162             let viewport_size = LogicalSize::from_physical(self.fragment.style.writing_mode,
1163                                                            shared_context.viewport_size());
1164             Some(viewport_size.block)
1165         } else if self.base.flags.contains(FlowFlags::IS_ABSOLUTELY_POSITIONED) &&
1166                   self.base.block_container_explicit_block_size.is_none() {
1167             self.base.absolute_cb.explicit_block_containing_size(shared_context)
1168         } else {
1169             self.base.block_container_explicit_block_size
1170         }
1171     }
1172 
explicit_block_size(&self, containing_block_size: Option<Au>) -> Option<Au>1173     pub fn explicit_block_size(&self, containing_block_size: Option<Au>) -> Option<Au> {
1174         let content_block_size = self.fragment.style().content_block_size();
1175 
1176         match (content_block_size, containing_block_size) {
1177             (LengthOrPercentageOrAuto::Calc(calc), _) => {
1178                 calc.to_used_value(containing_block_size)
1179             }
1180             (LengthOrPercentageOrAuto::Length(length), _) => Some(Au::from(length)),
1181             (LengthOrPercentageOrAuto::Percentage(percent), Some(container_size)) => {
1182                 Some(container_size.scale_by(percent.0))
1183             }
1184             (LengthOrPercentageOrAuto::Percentage(_), None) |
1185             (LengthOrPercentageOrAuto::Auto, None) => {
1186                 None
1187             }
1188             (LengthOrPercentageOrAuto::Auto, Some(container_size)) => {
1189                 let (block_start, block_end) = {
1190                     let position = self.fragment.style().logical_position();
1191                     (MaybeAuto::from_style(position.block_start, container_size),
1192                      MaybeAuto::from_style(position.block_end, container_size))
1193                 };
1194 
1195                 match (block_start, block_end) {
1196                     (MaybeAuto::Specified(block_start), MaybeAuto::Specified(block_end)) => {
1197                         let available_block_size = container_size - self.fragment.border_padding.block_start_end();
1198 
1199                         // Non-auto margin-block-start and margin-block-end values have already been
1200                         // calculated during assign-inline-size.
1201                         let margin = self.fragment.style().logical_margin();
1202                         let margin_block_start = match margin.block_start {
1203                             LengthOrPercentageOrAuto::Auto => MaybeAuto::Auto,
1204                             _ => MaybeAuto::Specified(self.fragment.margin.block_start)
1205                         };
1206                         let margin_block_end = match margin.block_end {
1207                             LengthOrPercentageOrAuto::Auto => MaybeAuto::Auto,
1208                             _ => MaybeAuto::Specified(self.fragment.margin.block_end)
1209                         };
1210 
1211                         let margin_block_start = margin_block_start.specified_or_zero();
1212                         let margin_block_end = margin_block_end.specified_or_zero();
1213                         let sum = block_start + block_end + margin_block_start + margin_block_end;
1214                         Some(available_block_size - sum)
1215                     }
1216 
1217                     (_, _) => {
1218                         None
1219                     }
1220                 }
1221             }
1222         }
1223     }
1224 
calculate_absolute_block_size_and_margins(&mut self, shared_context: &SharedStyleContext)1225     fn calculate_absolute_block_size_and_margins(&mut self, shared_context: &SharedStyleContext) {
1226         let opaque_self = OpaqueFlow::from_flow(self);
1227         let containing_block_block_size =
1228             self.containing_block_size(&shared_context.viewport_size(), opaque_self).block;
1229 
1230         // This is the stored content block-size value from assign-block-size
1231         let content_block_size = self.fragment.border_box.size.block;
1232 
1233         let mut solution = None;
1234         {
1235             // Non-auto margin-block-start and margin-block-end values have already been
1236             // calculated during assign-inline-size.
1237             let margin = self.fragment.style().logical_margin();
1238             let margin_block_start = match margin.block_start {
1239                 LengthOrPercentageOrAuto::Auto => MaybeAuto::Auto,
1240                 _ => MaybeAuto::Specified(self.fragment.margin.block_start)
1241             };
1242             let margin_block_end = match margin.block_end {
1243                 LengthOrPercentageOrAuto::Auto => MaybeAuto::Auto,
1244                 _ => MaybeAuto::Specified(self.fragment.margin.block_end)
1245             };
1246 
1247             let block_start;
1248             let block_end;
1249             {
1250                 let position = self.fragment.style().logical_position();
1251                 block_start = MaybeAuto::from_style(position.block_start,
1252                                                     containing_block_block_size);
1253                 block_end = MaybeAuto::from_style(position.block_end, containing_block_block_size);
1254             }
1255 
1256             let available_block_size = containing_block_block_size -
1257                 self.fragment.border_padding.block_start_end();
1258             if self.fragment.is_replaced() {
1259                 // Calculate used value of block-size just like we do for inline replaced elements.
1260                 // TODO: Pass in the containing block block-size when Fragment's
1261                 // assign-block-size can handle it correctly.
1262                 self.fragment.assign_replaced_block_size_if_necessary();
1263                 // TODO: Right now, this content block-size value includes the
1264                 // margin because of erroneous block-size calculation in fragment.
1265                 // Check this when that has been fixed.
1266                 let block_size_used_val = self.fragment.border_box.size.block;
1267                 solution = Some(BSizeConstraintSolution::solve_vertical_constraints_abs_replaced(
1268                         block_size_used_val,
1269                         margin_block_start,
1270                         margin_block_end,
1271                         block_start,
1272                         block_end,
1273                         content_block_size,
1274                         available_block_size))
1275             } else {
1276                 let mut candidate_block_size_iterator =
1277                     CandidateBSizeIterator::new(&self.fragment, Some(containing_block_block_size));
1278 
1279                 // Can't use `for` because we assign to
1280                 // `candidate_block_size_iterator.candidate_value`.
1281                 while let Some(block_size_used_val) =  candidate_block_size_iterator.next() {
1282                     solution = Some(
1283                         BSizeConstraintSolution::solve_vertical_constraints_abs_nonreplaced(
1284                             block_size_used_val,
1285                             margin_block_start,
1286                             margin_block_end,
1287                             block_start,
1288                             block_end,
1289                             content_block_size,
1290                             available_block_size));
1291 
1292                     candidate_block_size_iterator.candidate_value =
1293                         solution.unwrap().block_size;
1294                 }
1295             }
1296         }
1297 
1298         let solution = solution.unwrap();
1299         self.fragment.margin.block_start = solution.margin_block_start;
1300         self.fragment.margin.block_end = solution.margin_block_end;
1301         self.fragment.border_box.start.b = Au(0);
1302 
1303         if !self.base.flags.contains(FlowFlags::BLOCK_POSITION_IS_STATIC) {
1304             self.base.position.start.b = solution.block_start + self.fragment.margin.block_start
1305         }
1306 
1307         let block_size = solution.block_size + self.fragment.border_padding.block_start_end();
1308         self.fragment.border_box.size.block = block_size;
1309         self.base.position.size.block = block_size;
1310 
1311         self.base.restyle_damage.remove(ServoRestyleDamage::REFLOW_OUT_OF_FLOW | ServoRestyleDamage::REFLOW);
1312         self.fragment.restyle_damage.remove(ServoRestyleDamage::REFLOW_OUT_OF_FLOW | ServoRestyleDamage::REFLOW);
1313     }
1314 
1315     /// Compute inline size based using the `block_container_inline_size` set by the parent flow.
1316     ///
1317     /// This is run in the `AssignISizes` traversal.
propagate_and_compute_used_inline_size(&mut self, shared_context: &SharedStyleContext)1318     fn propagate_and_compute_used_inline_size(&mut self, shared_context: &SharedStyleContext) {
1319         let containing_block_inline_size = self.base.block_container_inline_size;
1320         self.compute_used_inline_size(shared_context, containing_block_inline_size);
1321         if self.base.flags.is_float() {
1322             self.float.as_mut().unwrap().containing_inline_size = containing_block_inline_size
1323         }
1324     }
1325 
1326     /// Assigns the computed inline-start content edge and inline-size to all the children of this
1327     /// block flow. The given `callback`, if supplied, will be called once per child; it is
1328     /// currently used to push down column sizes for tables.
1329     ///
1330     /// `#[inline(always)]` because this is called only from block or table inline-size assignment
1331     /// and the code for block layout is significantly simpler.
1332     #[inline(always)]
propagate_assigned_inline_size_to_children<F>(&mut self, shared_context: &SharedStyleContext, inline_start_content_edge: Au, inline_end_content_edge: Au, content_inline_size: Au, mut callback: F) where F: FnMut(&mut Flow, usize, Au, WritingMode, &mut Au, &mut Au)1333     pub fn propagate_assigned_inline_size_to_children<F>(&mut self,
1334                                                          shared_context: &SharedStyleContext,
1335                                                          inline_start_content_edge: Au,
1336                                                          inline_end_content_edge: Au,
1337                                                          content_inline_size: Au,
1338                                                          mut callback: F)
1339                                                          where F: FnMut(&mut Flow,
1340                                                                         usize,
1341                                                                         Au,
1342                                                                         WritingMode,
1343                                                                         &mut Au,
1344                                                                         &mut Au) {
1345         let flags = self.base.flags.clone();
1346 
1347         let opaque_self = OpaqueFlow::from_flow(self);
1348 
1349         // Calculate non-auto block size to pass to children.
1350         let box_border = match self.fragment.style().get_position().box_sizing {
1351             BoxSizing::BorderBox => self.fragment.border_padding.block_start_end(),
1352             BoxSizing::ContentBox => Au(0),
1353         };
1354         let parent_container_size = self.explicit_block_containing_size(shared_context);
1355         // https://drafts.csswg.org/css-ui-3/#box-sizing
1356         let mut explicit_content_size = self
1357                                     .explicit_block_size(parent_container_size)
1358                                     .map(|x| if x < box_border { Au(0) } else { x - box_border });
1359         if self.is_root() { explicit_content_size = max(parent_container_size, explicit_content_size); }
1360         // Calculate containing block inline size.
1361         let containing_block_size = if flags.contains(FlowFlags::IS_ABSOLUTELY_POSITIONED) {
1362             self.containing_block_size(&shared_context.viewport_size(), opaque_self).inline
1363         } else {
1364             content_inline_size
1365         };
1366         // FIXME (mbrubeck): Get correct mode for absolute containing block
1367         let containing_block_mode = self.base.writing_mode;
1368 
1369         let mut inline_start_margin_edge = inline_start_content_edge;
1370         let mut inline_end_margin_edge = inline_end_content_edge;
1371 
1372         let mut iterator = self.base.child_iter_mut().enumerate().peekable();
1373         while let Some((i, kid)) = iterator.next() {
1374             kid.mut_base().block_container_explicit_block_size = explicit_content_size;
1375 
1376             // The inline-start margin edge of the child flow is at our inline-start content edge,
1377             // and its inline-size is our content inline-size.
1378             let kid_mode = kid.base().writing_mode;
1379             {
1380                 // Don't assign positions to children unless they're going to be reflowed.
1381                 // Otherwise, the position we assign might be incorrect and never fixed up. (Issue
1382                 // #13704.)
1383                 //
1384                 // For instance, floats have their true inline position calculated in
1385                 // `assign_block_size()`, which won't do anything unless `REFLOW` is set. So, if a
1386                 // float child does not have `REFLOW` set, we must be careful to avoid touching its
1387                 // inline position, as no logic will run afterward to set its true value.
1388                 let kid_base = kid.mut_base();
1389                 let reflow_damage = if kid_base.flags.contains(FlowFlags::IS_ABSOLUTELY_POSITIONED) {
1390                     ServoRestyleDamage::REFLOW_OUT_OF_FLOW
1391                 } else {
1392                     ServoRestyleDamage::REFLOW
1393                 };
1394                 if kid_base.flags.contains(FlowFlags::INLINE_POSITION_IS_STATIC) &&
1395                         kid_base.restyle_damage.contains(reflow_damage) {
1396                     kid_base.position.start.i =
1397                         if kid_mode.is_bidi_ltr() == containing_block_mode.is_bidi_ltr() {
1398                             inline_start_content_edge
1399                         } else {
1400                             // The kid's inline 'start' is at the parent's 'end'
1401                             inline_end_content_edge
1402                         };
1403                 }
1404                 kid_base.block_container_inline_size = content_inline_size;
1405                 kid_base.block_container_writing_mode = containing_block_mode;
1406             }
1407 
1408             // Call the callback to propagate extra inline size information down to the child. This
1409             // is currently used for tables.
1410             callback(kid,
1411                      i,
1412                      content_inline_size,
1413                      containing_block_mode,
1414                      &mut inline_start_margin_edge,
1415                      &mut inline_end_margin_edge);
1416 
1417             // Per CSS 2.1 § 16.3.1, text alignment propagates to all children in flow.
1418             //
1419             // TODO(#2265, pcwalton): Do this in the cascade instead.
1420             let containing_block_text_align = self.fragment.style().get_inheritedtext().text_align;
1421             kid.mut_base().flags.set_text_align(containing_block_text_align);
1422 
1423             // Handle `text-indent` on behalf of any inline children that we have. This is
1424             // necessary because any percentages are relative to the containing block, which only
1425             // we know.
1426             if kid.is_inline_flow() {
1427                 kid.as_mut_inline().first_line_indentation =
1428                     self.fragment.style().get_inheritedtext().text_indent
1429                         .to_used_value(containing_block_size);
1430             }
1431         }
1432     }
1433 
1434     /// Determines the type of formatting context this is. See the definition of
1435     /// `FormattingContextType`.
formatting_context_type(&self) -> FormattingContextType1436     pub fn formatting_context_type(&self) -> FormattingContextType {
1437         if self.is_inline_flex_item() || self.is_block_flex_item() {
1438             return FormattingContextType::Other
1439         }
1440         let style = self.fragment.style();
1441         if style.get_box().float != Float::None {
1442             return FormattingContextType::Other
1443         }
1444         match style.get_box().display {
1445             Display::TableCell |
1446             Display::TableCaption |
1447             Display::TableRowGroup |
1448             Display::Table |
1449             Display::InlineBlock |
1450             Display::Flex => {
1451                 FormattingContextType::Other
1452             }
1453             _ if style.get_box().overflow_x != StyleOverflow::Visible ||
1454                     style.get_box().overflow_y != StyleOverflow::Visible ||
1455                     style.is_multicol() => {
1456                 FormattingContextType::Block
1457             }
1458             _ => FormattingContextType::None,
1459         }
1460     }
1461 
1462     /// Per CSS 2.1 § 9.5, block formatting contexts' inline widths and positions are affected by
1463     /// the presence of floats. This is the part of the assign-heights traversal that computes
1464     /// the final inline position and width for such flows.
1465     ///
1466     /// Note that this is part of the assign-block-sizes traversal, not the assign-inline-sizes
1467     /// traversal as one might expect. That is because, in general, float placement cannot occur
1468     /// until heights are assigned. To work around this unfortunate circular dependency, by the
1469     /// time we get here we have already estimated the width of the block formatting context based
1470     /// on the floats we could see at the time of inline-size assignment. The job of this function,
1471     /// therefore, is not only to assign the final size but also to perform the layout again for
1472     /// this block formatting context if our speculation was wrong.
assign_inline_position_for_formatting_context(&mut self, layout_context: &LayoutContext, content_box: LogicalRect<Au>)1473     fn assign_inline_position_for_formatting_context(&mut self,
1474                                                      layout_context: &LayoutContext,
1475                                                      content_box: LogicalRect<Au>) {
1476         debug_assert_ne!(self.formatting_context_type(), FormattingContextType::None);
1477 
1478         if !self.base.restyle_damage.intersects(ServoRestyleDamage::REFLOW_OUT_OF_FLOW | ServoRestyleDamage::REFLOW) {
1479             return
1480         }
1481 
1482         // We do this first to avoid recomputing our inline size when we propagate it.
1483         self.base.restyle_damage.remove(ServoRestyleDamage::REFLOW_OUT_OF_FLOW | ServoRestyleDamage::REFLOW);
1484         self.fragment.restyle_damage.remove(ServoRestyleDamage::REFLOW_OUT_OF_FLOW | ServoRestyleDamage::REFLOW);
1485 
1486         // The code below would completely wreck the layout if run on a flex item, however:
1487         //   * Flex items are always the children of flex containers.
1488         //   * Flex containers only contain flex items.
1489         //   * Floats cannot intrude into flex containers.
1490         //   * Floats cannot escape flex items.
1491         //   * Flex items cannot also be floats.
1492         // Therefore, a flex item cannot be impacted by a float.
1493         // See also: https://www.w3.org/TR/css-flexbox-1/#flex-containers
1494         if !self.base.might_have_floats_in() {
1495             return
1496         }
1497 
1498         // If you remove the might_have_floats_in conditional, this will go off.
1499         debug_assert!(!self.is_inline_flex_item());
1500 
1501         // Compute the available space for us, based on the actual floats.
1502         let rect = self.base.floats.available_rect(Au(0),
1503                                                    self.fragment.border_box.size.block,
1504                                                    content_box.size.inline);
1505         let available_inline_size = if let Some(rect) = rect {
1506             // Offset our position by whatever displacement is needed to not impact the floats.
1507             // Also, account for margins sliding behind floats.
1508             let inline_offset = if self.fragment.margin.inline_start < rect.start.i {
1509                 // Do not do anything for negative margins; those are handled separately.
1510                 rect.start.i - max(Au(0), self.fragment.margin.inline_start)
1511             } else {
1512                 Au(0)
1513             };
1514             self.base.position.start.i = content_box.start.i + inline_offset;
1515             // Handle the end margin sliding behind the float.
1516             let end = content_box.size.inline - rect.start.i - rect.size.inline;
1517             let inline_end_offset = if self.fragment.margin.inline_end < end {
1518                 end - max(Au(0), self.fragment.margin.inline_end)
1519             } else {
1520                 Au(0)
1521             };
1522             content_box.size.inline - inline_offset - inline_end_offset
1523         } else {
1524             content_box.size.inline
1525         } - self.fragment.margin.inline_start_end();
1526         let max_inline_size =
1527             self.fragment.style().max_inline_size()
1528                 .to_used_value(self.base.block_container_inline_size)
1529                 .unwrap_or(MAX_AU);
1530         let min_inline_size =
1531             self.fragment.style().min_inline_size().to_used_value(self.base.block_container_inline_size);
1532         let specified_inline_size = self.fragment.style().content_inline_size();
1533         let container_size = self.base.block_container_inline_size;
1534         let inline_size =
1535             if let MaybeAuto::Specified(size) = MaybeAuto::from_style(specified_inline_size,
1536                                                                       container_size) {
1537                 match self.fragment.style().get_position().box_sizing {
1538                     BoxSizing::BorderBox => size,
1539                     BoxSizing::ContentBox =>
1540                         size + self.fragment.border_padding.inline_start_end(),
1541                 }
1542             } else {
1543                 max(min_inline_size, min(available_inline_size, max_inline_size))
1544             };
1545         self.base.position.size.inline = inline_size + self.fragment.margin.inline_start_end();
1546 
1547         // If float speculation failed, fixup our layout, and re-layout all the children.
1548         if self.fragment.margin_box_inline_size() != self.base.position.size.inline {
1549             debug!("assign_inline_position_for_formatting_context: float speculation failed");
1550             // Fix-up our own layout.
1551             // We can't just traverse_flow_tree_preorder ourself, because that would re-run
1552             // float speculation, instead of acting on the actual results.
1553             self.fragment.border_box.size.inline = inline_size;
1554             // Assign final-final inline sizes on all our children.
1555             self.assign_inline_sizes(layout_context);
1556             // Re-run layout on our children.
1557             for child in self.base.child_iter_mut() {
1558                 sequential::reflow(child, layout_context, RelayoutMode::Force);
1559             }
1560             // Assign our final-final block size.
1561             self.assign_block_size(layout_context);
1562         }
1563 
1564         debug_assert_eq!(self.fragment.margin_box_inline_size(), self.base.position.size.inline);
1565     }
1566 
is_inline_block_or_inline_flex(&self) -> bool1567     fn is_inline_block_or_inline_flex(&self) -> bool {
1568         self.fragment.style().get_box().display == Display::InlineBlock ||
1569         self.fragment.style().get_box().display == Display::InlineFlex
1570     }
1571 
1572     /// Computes the content portion (only) of the intrinsic inline sizes of this flow. This is
1573     /// used for calculating shrink-to-fit width. Assumes that intrinsic sizes have already been
1574     /// computed for this flow.
content_intrinsic_inline_sizes(&self) -> IntrinsicISizes1575     fn content_intrinsic_inline_sizes(&self) -> IntrinsicISizes {
1576         let (border_padding, margin) = self.fragment.surrounding_intrinsic_inline_size();
1577         IntrinsicISizes {
1578             minimum_inline_size: self.base.intrinsic_inline_sizes.minimum_inline_size -
1579                                     border_padding - margin,
1580             preferred_inline_size: self.base.intrinsic_inline_sizes.preferred_inline_size -
1581                                     border_padding - margin,
1582         }
1583     }
1584 
1585     /// Computes intrinsic inline sizes for a block.
bubble_inline_sizes_for_block(&mut self, consult_children: bool)1586     pub fn bubble_inline_sizes_for_block(&mut self, consult_children: bool) {
1587         let _scope = layout_debug_scope!("block::bubble_inline_sizes {:x}", self.base.debug_id());
1588 
1589         let mut flags = self.base.flags;
1590         if self.definitely_has_zero_block_size() {
1591             // This is kind of a hack for Acid2. But it's a harmless one, because (a) this behavior
1592             // is unspecified; (b) it matches the behavior one would intuitively expect, since
1593             // floats don't flow around blocks that take up no space in the block direction.
1594             flags.remove(FlowFlags::CONTAINS_TEXT_OR_REPLACED_FRAGMENTS);
1595         } else if self.fragment.is_text_or_replaced() {
1596             flags.insert(FlowFlags::CONTAINS_TEXT_OR_REPLACED_FRAGMENTS);
1597         } else {
1598             flags.remove(FlowFlags::CONTAINS_TEXT_OR_REPLACED_FRAGMENTS);
1599             for kid in self.base.children.iter() {
1600                 if kid.base().flags.contains(FlowFlags::CONTAINS_TEXT_OR_REPLACED_FRAGMENTS) {
1601                     flags.insert(FlowFlags::CONTAINS_TEXT_OR_REPLACED_FRAGMENTS);
1602                     break
1603                 }
1604             }
1605         }
1606 
1607         // Find the maximum inline-size from children.
1608         //
1609         // See: https://lists.w3.org/Archives/Public/www-style/2014Nov/0085.html
1610         //
1611         // FIXME(pcwalton): This doesn't exactly follow that algorithm at the moment.
1612         // FIXME(pcwalton): This should consider all float descendants, not just children.
1613         let mut computation = self.fragment.compute_intrinsic_inline_sizes();
1614         let (mut left_float_width, mut right_float_width) = (Au(0), Au(0));
1615         let (mut left_float_width_accumulator, mut right_float_width_accumulator) = (Au(0), Au(0));
1616         let mut preferred_inline_size_of_children_without_text_or_replaced_fragments = Au(0);
1617         for kid in self.base.child_iter_mut() {
1618             if kid.base().flags.contains(FlowFlags::IS_ABSOLUTELY_POSITIONED) || !consult_children {
1619                 continue
1620             }
1621 
1622             let child_base = kid.mut_base();
1623             let float_kind = child_base.flags.float_kind();
1624             computation.content_intrinsic_sizes.minimum_inline_size =
1625                 max(computation.content_intrinsic_sizes.minimum_inline_size,
1626                     child_base.intrinsic_inline_sizes.minimum_inline_size);
1627 
1628             if child_base.flags.contains(FlowFlags::CLEARS_LEFT) {
1629                 left_float_width = max(left_float_width, left_float_width_accumulator);
1630                 left_float_width_accumulator = Au(0)
1631             }
1632             if child_base.flags.contains(FlowFlags::CLEARS_RIGHT) {
1633                 right_float_width = max(right_float_width, right_float_width_accumulator);
1634                 right_float_width_accumulator = Au(0)
1635             }
1636 
1637             match (float_kind, child_base.flags.contains(FlowFlags::CONTAINS_TEXT_OR_REPLACED_FRAGMENTS)) {
1638                 (Float::None, true) => {
1639                     computation.content_intrinsic_sizes.preferred_inline_size =
1640                         max(computation.content_intrinsic_sizes.preferred_inline_size,
1641                             child_base.intrinsic_inline_sizes.preferred_inline_size);
1642                 }
1643                 (Float::None, false) => {
1644                     preferred_inline_size_of_children_without_text_or_replaced_fragments = max(
1645                         preferred_inline_size_of_children_without_text_or_replaced_fragments,
1646                         child_base.intrinsic_inline_sizes.preferred_inline_size)
1647                 }
1648                 (Float::Left, _) => {
1649                     left_float_width_accumulator = left_float_width_accumulator +
1650                         child_base.intrinsic_inline_sizes.preferred_inline_size;
1651                 }
1652                 (Float::Right, _) => {
1653                     right_float_width_accumulator = right_float_width_accumulator +
1654                         child_base.intrinsic_inline_sizes.preferred_inline_size;
1655                 }
1656             }
1657         }
1658 
1659         left_float_width = max(left_float_width, left_float_width_accumulator);
1660         right_float_width = max(right_float_width, right_float_width_accumulator);
1661 
1662         computation.content_intrinsic_sizes.preferred_inline_size =
1663             computation.content_intrinsic_sizes.preferred_inline_size + left_float_width +
1664             right_float_width;
1665         computation.content_intrinsic_sizes.preferred_inline_size =
1666             max(computation.content_intrinsic_sizes.preferred_inline_size,
1667                 preferred_inline_size_of_children_without_text_or_replaced_fragments);
1668 
1669         self.base.intrinsic_inline_sizes = computation.finish();
1670         self.base.flags = flags
1671     }
1672 
overflow_style_may_require_clip_scroll_node(&self) -> bool1673     pub fn overflow_style_may_require_clip_scroll_node(&self) -> bool {
1674         match (self.fragment.style().get_box().overflow_x,
1675                self.fragment.style().get_box().overflow_y) {
1676             (StyleOverflow::Auto, _) | (StyleOverflow::Scroll, _) | (StyleOverflow::Hidden, _) |
1677             (_, StyleOverflow::Auto) | (_, StyleOverflow::Scroll) | (_, StyleOverflow::Hidden) =>
1678                 true,
1679             (_, _) => false,
1680         }
1681     }
1682 
compute_inline_sizes(&mut self, shared_context: &SharedStyleContext)1683     pub fn compute_inline_sizes(&mut self, shared_context: &SharedStyleContext) {
1684         if !self.base.restyle_damage.intersects(ServoRestyleDamage::REFLOW_OUT_OF_FLOW | ServoRestyleDamage::REFLOW) {
1685             return
1686         }
1687 
1688         debug!("assign_inline_sizes({}): assigning inline_size for flow",
1689                if self.base.flags.is_float() {
1690                    "float"
1691                } else {
1692                    "block"
1693                });
1694 
1695         self.base.floats = Floats::new(self.base.writing_mode);
1696 
1697         self.initialize_container_size_for_root(shared_context);
1698 
1699         // Our inline-size was set to the inline-size of the containing block by the flow's parent.
1700         // Now compute the real value.
1701         self.propagate_and_compute_used_inline_size(shared_context);
1702 
1703         self.guess_inline_size_for_block_formatting_context_if_necessary()
1704     }
1705 
1706     /// If this is the root flow, initialize values that would normally be set by the parent.
1707     ///
1708     /// Should be called during `assign_inline_sizes` for flows that may be the root.
initialize_container_size_for_root(&mut self, shared_context: &SharedStyleContext)1709     pub fn initialize_container_size_for_root(&mut self, shared_context: &SharedStyleContext) {
1710         if self.is_root() {
1711             debug!("Setting root position");
1712             self.base.position.start = LogicalPoint::zero(self.base.writing_mode);
1713             self.base.block_container_inline_size = LogicalSize::from_physical(
1714                 self.base.writing_mode, shared_context.viewport_size()).inline;
1715             self.base.block_container_writing_mode = self.base.writing_mode;
1716         }
1717     }
1718 
guess_inline_size_for_block_formatting_context_if_necessary(&mut self)1719     fn guess_inline_size_for_block_formatting_context_if_necessary(&mut self) {
1720         // We don't need to guess anything unless this is a block formatting context.
1721         if self.formatting_context_type() != FormattingContextType::Block {
1722             return
1723         }
1724 
1725         // If `max-width` is set, then don't perform this speculation. We guess that the
1726         // page set `max-width` in order to avoid hitting floats. The search box on Google
1727         // SERPs falls into this category.
1728         if self.fragment.style.max_inline_size() != LengthOrPercentageOrNone::None {
1729             return
1730         }
1731 
1732         // At this point, we know we can't precisely compute the inline-size of this block now,
1733         // because floats might affect it. Speculate that its inline-size is equal to the
1734         // inline-size computed above minus the inline-size of the previous left and/or right
1735         // floats.
1736         let speculated_left_float_size = if self.fragment.margin.inline_start >= Au(0) &&
1737                 self.base.speculated_float_placement_in.left > self.fragment.margin.inline_start {
1738             self.base.speculated_float_placement_in.left - self.fragment.margin.inline_start
1739         } else {
1740             Au(0)
1741         };
1742         let speculated_right_float_size = if self.fragment.margin.inline_end >= Au(0) &&
1743                 self.base.speculated_float_placement_in.right > self.fragment.margin.inline_end {
1744             self.base.speculated_float_placement_in.right - self.fragment.margin.inline_end
1745         } else {
1746             Au(0)
1747         };
1748         self.fragment.border_box.size.inline = self.fragment.border_box.size.inline -
1749             speculated_left_float_size - speculated_right_float_size
1750     }
1751 
definitely_has_zero_block_size(&self) -> bool1752     fn definitely_has_zero_block_size(&self) -> bool {
1753         if !self.fragment.style.content_block_size().is_definitely_zero() {
1754             return false
1755         }
1756         let border_width = self.fragment.border_width();
1757         if border_width.block_start != Au(0) || border_width.block_end != Au(0) {
1758             return false
1759         }
1760         let padding = self.fragment.style.logical_padding();
1761         padding.block_start.is_definitely_zero() && padding.block_end.is_definitely_zero()
1762     }
1763 
is_inline_flex_item(&self) -> bool1764     pub fn is_inline_flex_item(&self) -> bool {
1765         self.fragment.flags.contains(FragmentFlags::IS_INLINE_FLEX_ITEM)
1766     }
1767 
is_block_flex_item(&self) -> bool1768     pub fn is_block_flex_item(&self) -> bool {
1769         self.fragment.flags.contains(FragmentFlags::IS_BLOCK_FLEX_ITEM)
1770     }
1771 
mark_scrolling_overflow(&mut self, has_scrolling_overflow: bool)1772     pub fn mark_scrolling_overflow(&mut self, has_scrolling_overflow: bool) {
1773         if has_scrolling_overflow {
1774             self.flags.insert(BlockFlowFlags::HAS_SCROLLING_OVERFLOW);
1775         } else {
1776             self.flags.remove(BlockFlowFlags::HAS_SCROLLING_OVERFLOW);
1777         }
1778     }
1779 
has_scrolling_overflow(&self) -> bool1780     pub fn has_scrolling_overflow(&self) -> bool {
1781         self.flags.contains(BlockFlowFlags::HAS_SCROLLING_OVERFLOW)
1782     }
1783 
1784     // Return offset from original position because of `position: sticky`.
sticky_position(&self) -> SideOffsets2D<MaybeAuto>1785     pub fn sticky_position(&self) -> SideOffsets2D<MaybeAuto> {
1786         let containing_block_size = &self.base.early_absolute_position_info
1787                                               .relative_containing_block_size;
1788         let writing_mode = self.base.early_absolute_position_info.relative_containing_block_mode;
1789         let offsets = self.fragment.style().logical_position();
1790         let as_margins = LogicalMargin::new(writing_mode,
1791             MaybeAuto::from_style(offsets.block_start, containing_block_size.inline),
1792             MaybeAuto::from_style(offsets.inline_end, containing_block_size.inline),
1793             MaybeAuto::from_style(offsets.block_end, containing_block_size.inline),
1794             MaybeAuto::from_style(offsets.inline_start, containing_block_size.inline));
1795         as_margins.to_physical(writing_mode)
1796     }
1797 
background_border_section(&self) -> DisplayListSection1798     pub fn background_border_section(&self) -> DisplayListSection {
1799         if self.base.flags.is_float() {
1800             DisplayListSection::BackgroundAndBorders
1801         } else if self.base
1802             .flags
1803             .contains(FlowFlags::IS_ABSOLUTELY_POSITIONED)
1804         {
1805             if self.fragment.establishes_stacking_context() {
1806                 DisplayListSection::BackgroundAndBorders
1807             } else {
1808                 DisplayListSection::BlockBackgroundsAndBorders
1809             }
1810         } else {
1811             DisplayListSection::BlockBackgroundsAndBorders
1812         }
1813     }
1814 }
1815 
1816 impl Flow for BlockFlow {
class(&self) -> FlowClass1817     fn class(&self) -> FlowClass {
1818         FlowClass::Block
1819     }
1820 
as_mut_block(&mut self) -> &mut BlockFlow1821     fn as_mut_block(&mut self) -> &mut BlockFlow {
1822         self
1823     }
1824 
as_block(&self) -> &BlockFlow1825     fn as_block(&self) -> &BlockFlow {
1826         self
1827     }
1828 
1829     /// Pass 1 of reflow: computes minimum and preferred inline-sizes.
1830     ///
1831     /// Recursively (bottom-up) determine the flow's minimum and preferred inline-sizes. When
1832     /// called on this flow, all child flows have had their minimum and preferred inline-sizes set.
1833     /// This function must decide minimum/preferred inline-sizes based on its children's
1834     /// inline-sizes and the dimensions of any fragments it is responsible for flowing.
bubble_inline_sizes(&mut self)1835     fn bubble_inline_sizes(&mut self) {
1836         // If this block has a fixed width, just use that for the minimum and preferred width,
1837         // rather than bubbling up children inline width.
1838         let consult_children = match self.fragment.style().get_position().width {
1839             LengthOrPercentageOrAuto::Length(_) => false,
1840             _ => true,
1841         };
1842         self.bubble_inline_sizes_for_block(consult_children);
1843         self.fragment.restyle_damage.remove(ServoRestyleDamage::BUBBLE_ISIZES);
1844     }
1845 
1846     /// Recursively (top-down) determines the actual inline-size of child contexts and fragments.
1847     /// When called on this context, the context has had its inline-size set by the parent context.
1848     ///
1849     /// Dual fragments consume some inline-size first, and the remainder is assigned to all child
1850     /// (block) contexts.
assign_inline_sizes(&mut self, layout_context: &LayoutContext)1851     fn assign_inline_sizes(&mut self, layout_context: &LayoutContext) {
1852         let _scope = layout_debug_scope!("block::assign_inline_sizes {:x}", self.base.debug_id());
1853 
1854         let shared_context = layout_context.shared_context();
1855         self.compute_inline_sizes(shared_context);
1856 
1857         // Move in from the inline-start border edge.
1858         let inline_start_content_edge = self.fragment.border_box.start.i +
1859             self.fragment.border_padding.inline_start;
1860 
1861         let padding_and_borders = self.fragment.border_padding.inline_start_end();
1862 
1863         // Distance from the inline-end margin edge to the inline-end content edge.
1864         let inline_end_content_edge =
1865             self.fragment.margin.inline_end +
1866             self.fragment.border_padding.inline_end;
1867 
1868         let content_inline_size = self.fragment.border_box.size.inline - padding_and_borders;
1869 
1870         self.propagate_assigned_inline_size_to_children(shared_context,
1871                                                         inline_start_content_edge,
1872                                                         inline_end_content_edge,
1873                                                         content_inline_size,
1874                                                         |_, _, _, _, _, _| {});
1875     }
1876 
place_float_if_applicable<'a>(&mut self)1877     fn place_float_if_applicable<'a>(&mut self) {
1878         if self.base.flags.is_float() {
1879             self.place_float();
1880         }
1881     }
1882 
assign_block_size_for_inorder_child_if_necessary(&mut self, layout_context: &LayoutContext, parent_thread_id: u8, content_box: LogicalRect<Au>) -> bool1883     fn assign_block_size_for_inorder_child_if_necessary(&mut self,
1884                                                         layout_context: &LayoutContext,
1885                                                         parent_thread_id: u8,
1886                                                         content_box: LogicalRect<Au>)
1887                                                         -> bool {
1888         if self.base.flags.is_float() {
1889             return false
1890         }
1891 
1892         let is_formatting_context = self.formatting_context_type() != FormattingContextType::None;
1893         if !self.base.flags.contains(FlowFlags::IS_ABSOLUTELY_POSITIONED) && is_formatting_context {
1894             self.assign_inline_position_for_formatting_context(layout_context, content_box);
1895         }
1896 
1897         if (self as &Flow).floats_might_flow_through() {
1898             self.base.thread_id = parent_thread_id;
1899             if self.base.restyle_damage.intersects(ServoRestyleDamage::REFLOW_OUT_OF_FLOW |
1900                                                    ServoRestyleDamage::REFLOW) {
1901                 self.assign_block_size(layout_context);
1902                 // Don't remove the restyle damage; `assign_block_size` decides whether that is
1903                 // appropriate (which in the case of e.g. absolutely-positioned flows, it is not).
1904             }
1905             return true
1906         }
1907 
1908         if is_formatting_context {
1909             // If this is a formatting context and definitely did not have floats in, then we must
1910             // translate the floats past us.
1911             let writing_mode = self.base.floats.writing_mode;
1912             let delta = self.base.position.size.block;
1913             self.base.floats.translate(LogicalSize::new(writing_mode, Au(0), -delta));
1914             return true
1915         }
1916 
1917         false
1918     }
1919 
assign_block_size(&mut self, ctx: &LayoutContext)1920     fn assign_block_size(&mut self, ctx: &LayoutContext) {
1921         let remaining = Flow::fragment(self, ctx, None);
1922         debug_assert!(remaining.is_none());
1923     }
1924 
fragment(&mut self, layout_context: &LayoutContext, fragmentation_context: Option<FragmentationContext>) -> Option<Arc<Flow>>1925     fn fragment(&mut self, layout_context: &LayoutContext,
1926                 fragmentation_context: Option<FragmentationContext>)
1927                 -> Option<Arc<Flow>> {
1928         if self.fragment.is_replaced() {
1929             let _scope = layout_debug_scope!("assign_replaced_block_size_if_necessary {:x}",
1930                                              self.base.debug_id());
1931 
1932             // Assign block-size for fragment if it is an image fragment.
1933             self.fragment.assign_replaced_block_size_if_necessary();
1934             if !self.base.flags.contains(FlowFlags::IS_ABSOLUTELY_POSITIONED) {
1935                 self.base.position.size.block = self.fragment.border_box.size.block;
1936                 let mut block_start = AdjoiningMargins::from_margin(self.fragment.margin.block_start);
1937                 let block_end = AdjoiningMargins::from_margin(self.fragment.margin.block_end);
1938                 if self.fragment.border_box.size.block == Au(0) {
1939                     block_start.union(block_end);
1940                     self.base.collapsible_margins = CollapsibleMargins::CollapseThrough(block_start);
1941                 } else {
1942                     self.base.collapsible_margins = CollapsibleMargins::Collapse(block_start, block_end);
1943                 }
1944                 self.base.restyle_damage.remove(ServoRestyleDamage::REFLOW_OUT_OF_FLOW | ServoRestyleDamage::REFLOW);
1945                 self.fragment.restyle_damage.remove(ServoRestyleDamage::REFLOW_OUT_OF_FLOW |
1946                                                     ServoRestyleDamage::REFLOW);
1947             }
1948             None
1949         } else if self.is_root() ||
1950                 self.formatting_context_type() != FormattingContextType::None ||
1951                 self.base.flags.contains(FlowFlags::MARGINS_CANNOT_COLLAPSE) {
1952             // Root element margins should never be collapsed according to CSS § 8.3.1.
1953             debug!("assign_block_size: assigning block_size for root flow {:?}",
1954                    self.base().debug_id());
1955             self.assign_block_size_block_base(
1956                 layout_context,
1957                 fragmentation_context,
1958                 MarginsMayCollapseFlag::MarginsMayNotCollapse)
1959         } else {
1960             debug!("assign_block_size: assigning block_size for block {:?}",
1961                    self.base().debug_id());
1962             self.assign_block_size_block_base(
1963                 layout_context,
1964                 fragmentation_context,
1965                 MarginsMayCollapseFlag::MarginsMayCollapse)
1966         }
1967     }
1968 
compute_stacking_relative_position(&mut self, _layout_context: &LayoutContext)1969     fn compute_stacking_relative_position(&mut self, _layout_context: &LayoutContext) {
1970         // FIXME (mbrubeck): Get the real container size, taking the container writing mode into
1971         // account.  Must handle vertical writing modes.
1972         let container_size = Size2D::new(self.base.block_container_inline_size, Au(0));
1973 
1974         if self.is_root() {
1975             self.base.clip = Rect::max_rect();
1976         }
1977 
1978         if self.base.flags.contains(FlowFlags::IS_ABSOLUTELY_POSITIONED) {
1979             let position_start = self.base.position.start.to_physical(self.base.writing_mode,
1980                                                                       container_size);
1981 
1982             // Compute our position relative to the nearest ancestor stacking context. This will be
1983             // passed down later as part of containing block details for absolute descendants.
1984             let absolute_stacking_relative_position = if self.is_fixed() {
1985                 // The viewport is initially at (0, 0).
1986                 position_start
1987             } else {
1988                 // Absolute position of the containing block + position of absolute
1989                 // flow w.r.t. the containing block.
1990                 self.base
1991                     .late_absolute_position_info
1992                     .stacking_relative_position_of_absolute_containing_block + position_start.to_vector()
1993             };
1994 
1995             if !self.base.writing_mode.is_vertical() {
1996                 if !self.base.flags.contains(FlowFlags::INLINE_POSITION_IS_STATIC) {
1997                     self.base.stacking_relative_position.x = absolute_stacking_relative_position.x
1998                 }
1999                 if !self.base.flags.contains(FlowFlags::BLOCK_POSITION_IS_STATIC) {
2000                     self.base.stacking_relative_position.y = absolute_stacking_relative_position.y
2001                 }
2002             } else {
2003                 if !self.base.flags.contains(FlowFlags::INLINE_POSITION_IS_STATIC) {
2004                     self.base.stacking_relative_position.y = absolute_stacking_relative_position.y
2005                 }
2006                 if !self.base.flags.contains(FlowFlags::BLOCK_POSITION_IS_STATIC) {
2007                     self.base.stacking_relative_position.x = absolute_stacking_relative_position.x
2008                 }
2009             }
2010         }
2011 
2012         // For relatively-positioned descendants, the containing block formed by a block is just
2013         // the content box. The containing block for absolutely-positioned descendants, on the
2014         // other hand, is established in other circumstances (see `is_absolute_containing_block').
2015         let relative_offset =
2016             self.fragment.relative_position(&self.base
2017                                                  .early_absolute_position_info
2018                                                  .relative_containing_block_size);
2019         if self.is_absolute_containing_block() {
2020             let border_box_origin = (self.fragment.border_box -
2021                 self.fragment.style.logical_border_width()).start;
2022             self.base
2023                 .late_absolute_position_info
2024                 .stacking_relative_position_of_absolute_containing_block =
2025                     self.base.stacking_relative_position.to_point() +
2026                      (border_box_origin + relative_offset).to_physical(self.base.writing_mode,
2027                                                                        container_size).to_vector()
2028         }
2029 
2030         // Compute absolute position info for children.
2031         let stacking_relative_position_of_absolute_containing_block_for_children =
2032             if self.fragment.establishes_stacking_context() {
2033                 let logical_border_width = self.fragment.style().logical_border_width();
2034                 let position = LogicalPoint::new(self.base.writing_mode,
2035                                                  logical_border_width.inline_start,
2036                                                  logical_border_width.block_start);
2037                 let position = position.to_physical(self.base.writing_mode, container_size);
2038 
2039                 // Some blocks establish a stacking context, but not a containing block for
2040                 // absolutely positioned elements. An example of this might be a block that has
2041                 // `position: static` and `opacity` set. In these cases, absolutely-positioned
2042                 // children will not be positioned relative to us but will instead be positioned
2043                 // relative to our containing block.
2044                 if self.is_absolute_containing_block() {
2045                     position
2046                 } else {
2047                     position - self.base.stacking_relative_position
2048                 }
2049             } else {
2050                 self.base
2051                     .late_absolute_position_info
2052                     .stacking_relative_position_of_absolute_containing_block
2053             };
2054         let late_absolute_position_info_for_children = LateAbsolutePositionInfo {
2055             stacking_relative_position_of_absolute_containing_block:
2056                 stacking_relative_position_of_absolute_containing_block_for_children,
2057         };
2058         let container_size_for_children =
2059             self.base.position.size.to_physical(self.base.writing_mode);
2060 
2061         // Compute the origin and clipping rectangle for children.
2062         let relative_offset = relative_offset.to_physical(self.base.writing_mode).to_vector();
2063         let is_stacking_context = self.fragment.establishes_stacking_context();
2064         let origin_for_children = if is_stacking_context {
2065             // We establish a stacking context, so the position of our children is vertically
2066             // correct, but has to be adjusted to accommodate horizontal margins. (Note the
2067             // calculation involving `position` below and recall that inline-direction flow
2068             // positions are relative to the edges of the margin box.)
2069             //
2070             // FIXME(pcwalton): Is this vertical-writing-direction-safe?
2071             let margin = self.fragment.margin.to_physical(self.base.writing_mode);
2072             Point2D::new(-margin.left, Au(0))
2073         } else {
2074             self.base.stacking_relative_position.to_point() + relative_offset
2075         };
2076 
2077         // Process children.
2078         for kid in self.base.child_iter_mut() {
2079             if kid.base().flags.contains(FlowFlags::INLINE_POSITION_IS_STATIC) ||
2080                     kid.base().flags.contains(FlowFlags::BLOCK_POSITION_IS_STATIC) {
2081                 let kid_base = kid.mut_base();
2082                 let physical_position = kid_base.position.to_physical(kid_base.writing_mode,
2083                                                                       container_size_for_children);
2084 
2085                 // Set the inline and block positions as necessary.
2086                 if !kid_base.writing_mode.is_vertical() {
2087                     if kid_base.flags.contains(FlowFlags::INLINE_POSITION_IS_STATIC) {
2088                         kid_base.stacking_relative_position.x = origin_for_children.x +
2089                             physical_position.origin.x
2090                     }
2091                     if kid_base.flags.contains(FlowFlags::BLOCK_POSITION_IS_STATIC) {
2092                         kid_base.stacking_relative_position.y = origin_for_children.y +
2093                             physical_position.origin.y
2094                     }
2095                 } else {
2096                     if kid_base.flags.contains(FlowFlags::INLINE_POSITION_IS_STATIC) {
2097                         kid_base.stacking_relative_position.y = origin_for_children.y +
2098                             physical_position.origin.y
2099                     }
2100                     if kid_base.flags.contains(FlowFlags::BLOCK_POSITION_IS_STATIC) {
2101                         kid_base.stacking_relative_position.x = origin_for_children.x +
2102                             physical_position.origin.x
2103                     }
2104                 }
2105             }
2106 
2107             kid.mut_base().late_absolute_position_info =
2108                 late_absolute_position_info_for_children;
2109         }
2110     }
2111 
mark_as_root(&mut self)2112     fn mark_as_root(&mut self) {
2113         self.flags.insert(BlockFlowFlags::IS_ROOT)
2114     }
2115 
is_root(&self) -> bool2116     fn is_root(&self) -> bool {
2117         self.flags.contains(BlockFlowFlags::IS_ROOT)
2118     }
2119 
2120     /// The 'position' property of this flow.
positioning(&self) -> Position2121     fn positioning(&self) -> Position {
2122         self.fragment.style.get_box().position
2123     }
2124 
2125     /// Return the dimensions of the containing block generated by this flow for absolutely-
2126     /// positioned descendants. For block flows, this is the padding box.
generated_containing_block_size(&self, _: OpaqueFlow) -> LogicalSize<Au>2127     fn generated_containing_block_size(&self, _: OpaqueFlow) -> LogicalSize<Au> {
2128         (self.fragment.border_box - self.fragment.style().logical_border_width()).size
2129     }
2130 
2131     /// Returns true if this flow contains fragments that are roots of an absolute flow tree.
contains_roots_of_absolute_flow_tree(&self) -> bool2132     fn contains_roots_of_absolute_flow_tree(&self) -> bool {
2133         self.contains_relatively_positioned_fragments() || self.is_root() ||
2134         self.fragment.has_filter_transform_or_perspective()
2135     }
2136 
2137     /// Returns true if this is an absolute containing block.
is_absolute_containing_block(&self) -> bool2138     fn is_absolute_containing_block(&self) -> bool {
2139         self.contains_positioned_fragments() || self.fragment.has_filter_transform_or_perspective()
2140     }
2141 
update_late_computed_inline_position_if_necessary(&mut self, inline_position: Au)2142     fn update_late_computed_inline_position_if_necessary(&mut self, inline_position: Au) {
2143         if self.base.flags.contains(FlowFlags::IS_ABSOLUTELY_POSITIONED) &&
2144                 self.fragment.style().logical_position().inline_start ==
2145                     LengthOrPercentageOrAuto::Auto &&
2146                 self.fragment.style().logical_position().inline_end ==
2147                 LengthOrPercentageOrAuto::Auto {
2148             self.base.position.start.i = inline_position
2149         }
2150     }
2151 
update_late_computed_block_position_if_necessary(&mut self, block_position: Au)2152     fn update_late_computed_block_position_if_necessary(&mut self, block_position: Au) {
2153         if self.base.flags.contains(FlowFlags::IS_ABSOLUTELY_POSITIONED) &&
2154                 self.fragment.style().logical_position().block_start ==
2155                     LengthOrPercentageOrAuto::Auto &&
2156                 self.fragment.style().logical_position().block_end ==
2157                 LengthOrPercentageOrAuto::Auto {
2158             self.base.position.start.b = block_position
2159         }
2160     }
2161 
collect_stacking_contexts(&mut self, state: &mut StackingContextCollectionState)2162     fn collect_stacking_contexts(&mut self, state: &mut StackingContextCollectionState) {
2163         self.collect_stacking_contexts_for_block(state, StackingContextCollectionFlags::empty());
2164     }
2165 
build_display_list(&mut self, state: &mut DisplayListBuildState)2166     fn build_display_list(&mut self, state: &mut DisplayListBuildState) {
2167         self.build_display_list_for_block(state, BorderPaintingMode::Separate);
2168     }
2169 
repair_style(&mut self, new_style: &::ServoArc<ComputedValues>)2170     fn repair_style(&mut self, new_style: &::ServoArc<ComputedValues>) {
2171         self.fragment.repair_style(new_style)
2172     }
2173 
compute_overflow(&self) -> Overflow2174     fn compute_overflow(&self) -> Overflow {
2175         let flow_size = self.base.position.size.to_physical(self.base.writing_mode);
2176         let overflow = self.fragment.compute_overflow(&flow_size,
2177                                        &self.base
2178                                             .early_absolute_position_info
2179                                             .relative_containing_block_size);
2180         overflow
2181     }
2182 
iterate_through_fragment_border_boxes(&self, iterator: &mut FragmentBorderBoxIterator, level: i32, stacking_context_position: &Point2D<Au>)2183     fn iterate_through_fragment_border_boxes(&self,
2184                                              iterator: &mut FragmentBorderBoxIterator,
2185                                              level: i32,
2186                                              stacking_context_position: &Point2D<Au>) {
2187         if !iterator.should_process(&self.fragment) {
2188             return
2189         }
2190 
2191         iterator.process(&self.fragment,
2192                          level,
2193                          &self.fragment
2194                               .stacking_relative_border_box(&self.base.stacking_relative_position,
2195                                                             &self.base
2196                                                                  .early_absolute_position_info
2197                                                                  .relative_containing_block_size,
2198                                                             self.base
2199                                                                 .early_absolute_position_info
2200                                                                 .relative_containing_block_mode,
2201                                                             CoordinateSystem::Own)
2202                               .translate(&stacking_context_position.to_vector()));
2203     }
2204 
mutate_fragments(&mut self, mutator: &mut FnMut(&mut Fragment))2205     fn mutate_fragments(&mut self, mutator: &mut FnMut(&mut Fragment)) {
2206         (*mutator)(&mut self.fragment)
2207     }
2208 
print_extra_flow_children(&self, print_tree: &mut PrintTree)2209     fn print_extra_flow_children(&self, print_tree: &mut PrintTree) {
2210         print_tree.add_item(format!("↑↑ Fragment for block:{:?}", self.fragment));
2211     }
2212 }
2213 
2214 impl fmt::Debug for BlockFlow {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result2215     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
2216         write!(f,
2217                "{:?}({:x}) {:?}",
2218                self.class(),
2219                self.base.debug_id(),
2220                self.base)
2221     }
2222 }
2223 
2224 /// The inputs for the inline-sizes-and-margins constraint equation.
2225 #[derive(Clone, Copy, Debug)]
2226 pub struct ISizeConstraintInput {
2227     pub computed_inline_size: MaybeAuto,
2228     pub inline_start_margin: MaybeAuto,
2229     pub inline_end_margin: MaybeAuto,
2230     pub inline_start: MaybeAuto,
2231     pub inline_end: MaybeAuto,
2232     pub text_align: TextAlign,
2233     pub available_inline_size: Au,
2234 }
2235 
2236 impl ISizeConstraintInput {
new(computed_inline_size: MaybeAuto, inline_start_margin: MaybeAuto, inline_end_margin: MaybeAuto, inline_start: MaybeAuto, inline_end: MaybeAuto, text_align: TextAlign, available_inline_size: Au) -> ISizeConstraintInput2237     pub fn new(computed_inline_size: MaybeAuto,
2238                inline_start_margin: MaybeAuto,
2239                inline_end_margin: MaybeAuto,
2240                inline_start: MaybeAuto,
2241                inline_end: MaybeAuto,
2242                text_align: TextAlign,
2243                available_inline_size: Au)
2244            -> ISizeConstraintInput {
2245         ISizeConstraintInput {
2246             computed_inline_size: computed_inline_size,
2247             inline_start_margin: inline_start_margin,
2248             inline_end_margin: inline_end_margin,
2249             inline_start: inline_start,
2250             inline_end: inline_end,
2251             text_align: text_align,
2252             available_inline_size: available_inline_size,
2253         }
2254     }
2255 }
2256 
2257 /// The solutions for the inline-size-and-margins constraint equation.
2258 #[derive(Clone, Copy, Debug)]
2259 pub struct ISizeConstraintSolution {
2260     pub inline_start: Au,
2261     pub inline_size: Au,
2262     pub margin_inline_start: Au,
2263     pub margin_inline_end: Au
2264 }
2265 
2266 impl ISizeConstraintSolution {
new(inline_size: Au, margin_inline_start: Au, margin_inline_end: Au) -> ISizeConstraintSolution2267     pub fn new(inline_size: Au, margin_inline_start: Au, margin_inline_end: Au)
2268                -> ISizeConstraintSolution {
2269         ISizeConstraintSolution {
2270             inline_start: Au(0),
2271             inline_size: inline_size,
2272             margin_inline_start: margin_inline_start,
2273             margin_inline_end: margin_inline_end,
2274         }
2275     }
2276 
for_absolute_flow(inline_start: Au, inline_size: Au, margin_inline_start: Au, margin_inline_end: Au) -> ISizeConstraintSolution2277     fn for_absolute_flow(inline_start: Au,
2278                          inline_size: Au,
2279                          margin_inline_start: Au,
2280                          margin_inline_end: Au)
2281                          -> ISizeConstraintSolution {
2282         ISizeConstraintSolution {
2283             inline_start: inline_start,
2284             inline_size: inline_size,
2285             margin_inline_start: margin_inline_start,
2286             margin_inline_end: margin_inline_end,
2287         }
2288     }
2289 }
2290 
2291 // Trait to encapsulate the ISize and Margin calculation.
2292 //
2293 // CSS Section 10.3
2294 pub trait ISizeAndMarginsComputer {
2295     /// Instructs the fragment to compute its border and padding.
compute_border_and_padding(&self, block: &mut BlockFlow, containing_block_inline_size: Au)2296     fn compute_border_and_padding(&self, block: &mut BlockFlow, containing_block_inline_size: Au) {
2297         block.fragment.compute_border_and_padding(containing_block_inline_size);
2298     }
2299 
2300     /// Compute the inputs for the ISize constraint equation.
2301     ///
2302     /// This is called only once to compute the initial inputs. For calculations involving
2303     /// minimum and maximum inline-size, we don't need to recompute these.
compute_inline_size_constraint_inputs(&self, block: &mut BlockFlow, parent_flow_inline_size: Au, shared_context: &SharedStyleContext) -> ISizeConstraintInput2304     fn compute_inline_size_constraint_inputs(&self,
2305                                              block: &mut BlockFlow,
2306                                              parent_flow_inline_size: Au,
2307                                              shared_context: &SharedStyleContext)
2308                                              -> ISizeConstraintInput {
2309         let containing_block_inline_size =
2310             self.containing_block_inline_size(block, parent_flow_inline_size, shared_context);
2311 
2312         block.fragment.compute_block_direction_margins(containing_block_inline_size);
2313         block.fragment.compute_inline_direction_margins(containing_block_inline_size);
2314         self.compute_border_and_padding(block, containing_block_inline_size);
2315 
2316         let mut computed_inline_size = self.initial_computed_inline_size(block,
2317                                                                          parent_flow_inline_size,
2318                                                                          shared_context);
2319         let style = block.fragment.style();
2320         match (computed_inline_size, style.get_position().box_sizing) {
2321             (MaybeAuto::Specified(size), BoxSizing::BorderBox) => {
2322                 computed_inline_size =
2323                     MaybeAuto::Specified(size - block.fragment.border_padding.inline_start_end())
2324             }
2325             (MaybeAuto::Auto, BoxSizing::BorderBox) |
2326             (_, BoxSizing::ContentBox) => {}
2327         }
2328 
2329         let margin = style.logical_margin();
2330         let position = style.logical_position();
2331 
2332         let available_inline_size = containing_block_inline_size -
2333             block.fragment.border_padding.inline_start_end();
2334         ISizeConstraintInput::new(computed_inline_size,
2335                                   MaybeAuto::from_style(margin.inline_start,
2336                                                         containing_block_inline_size),
2337                                   MaybeAuto::from_style(margin.inline_end,
2338                                                         containing_block_inline_size),
2339                                   MaybeAuto::from_style(position.inline_start,
2340                                                         containing_block_inline_size),
2341                                   MaybeAuto::from_style(position.inline_end,
2342                                                         containing_block_inline_size),
2343                                   style.get_inheritedtext().text_align,
2344                                   available_inline_size)
2345     }
2346 
2347     /// Set the used values for inline-size and margins from the relevant constraint equation.
2348     /// This is called only once.
2349     ///
2350     /// Set:
2351     /// * Used values for content inline-size, inline-start margin, and inline-end margin for this
2352     ///   flow's box;
2353     /// * Inline-start coordinate of this flow's box;
2354     /// * Inline-start coordinate of the flow with respect to its containing block (if this is an
2355     ///   absolute flow).
set_inline_size_constraint_solutions(&self, block: &mut BlockFlow, solution: ISizeConstraintSolution)2356     fn set_inline_size_constraint_solutions(&self,
2357                                             block: &mut BlockFlow,
2358                                             solution: ISizeConstraintSolution) {
2359         let inline_size;
2360         let extra_inline_size_from_margin;
2361         {
2362             let block_mode = block.base.writing_mode;
2363 
2364             // FIXME (mbrubeck): Get correct containing block for positioned blocks?
2365             let container_mode = block.base.block_container_writing_mode;
2366             let container_size = block.base.block_container_inline_size;
2367 
2368             let fragment = block.fragment();
2369             fragment.margin.inline_start = solution.margin_inline_start;
2370             fragment.margin.inline_end = solution.margin_inline_end;
2371 
2372             // The associated fragment has the border box of this flow.
2373             inline_size = solution.inline_size + fragment.border_padding.inline_start_end();
2374             fragment.border_box.size.inline = inline_size;
2375 
2376             // Start border edge.
2377             // FIXME (mbrubeck): Handle vertical writing modes.
2378             fragment.border_box.start.i =
2379                 if container_mode.is_bidi_ltr() == block_mode.is_bidi_ltr() {
2380                     fragment.margin.inline_start
2381                 } else {
2382                     // The parent's "start" direction is the child's "end" direction.
2383                     container_size - inline_size - fragment.margin.inline_end
2384                 };
2385 
2386             // To calculate the total size of this block, we also need to account for any
2387             // additional size contribution from positive margins. Negative margins means the block
2388             // isn't made larger at all by the margin.
2389             extra_inline_size_from_margin = max(Au(0), fragment.margin.inline_start) +
2390                                             max(Au(0), fragment.margin.inline_end);
2391         }
2392 
2393         // We also resize the block itself, to ensure that overflow is not calculated
2394         // as the inline-size of our parent. We might be smaller and we might be larger if we
2395         // overflow.
2396         block.mut_base().position.size.inline = inline_size + extra_inline_size_from_margin;
2397     }
2398 
2399     /// Set the inline coordinate of the given flow if it is absolutely positioned.
set_inline_position_of_flow_if_necessary(&self, _: &mut BlockFlow, _: ISizeConstraintSolution)2400     fn set_inline_position_of_flow_if_necessary(&self,
2401                                                 _: &mut BlockFlow,
2402                                                 _: ISizeConstraintSolution) {}
2403 
2404     /// Solve the inline-size and margins constraints for this block flow.
solve_inline_size_constraints(&self, block: &mut BlockFlow, input: &ISizeConstraintInput) -> ISizeConstraintSolution2405     fn solve_inline_size_constraints(&self,
2406                                      block: &mut BlockFlow,
2407                                      input: &ISizeConstraintInput)
2408                                      -> ISizeConstraintSolution;
2409 
initial_computed_inline_size(&self, block: &mut BlockFlow, parent_flow_inline_size: Au, shared_context: &SharedStyleContext) -> MaybeAuto2410     fn initial_computed_inline_size(&self,
2411                                     block: &mut BlockFlow,
2412                                     parent_flow_inline_size: Au,
2413                                     shared_context: &SharedStyleContext)
2414                                     -> MaybeAuto {
2415         MaybeAuto::from_style(block.fragment().style().content_inline_size(),
2416                               self.containing_block_inline_size(block,
2417                                                                 parent_flow_inline_size,
2418                                                                 shared_context))
2419     }
2420 
containing_block_inline_size(&self, _: &mut BlockFlow, parent_flow_inline_size: Au, _: &SharedStyleContext) -> Au2421     fn containing_block_inline_size(&self,
2422                                     _: &mut BlockFlow,
2423                                     parent_flow_inline_size: Au,
2424                                     _: &SharedStyleContext)
2425                               -> Au {
2426         parent_flow_inline_size
2427     }
2428 
2429     /// Compute the used value of inline-size, taking care of min-inline-size and max-inline-size.
2430     ///
2431     /// CSS Section 10.4: Minimum and Maximum inline-sizes
compute_used_inline_size(&self, block: &mut BlockFlow, shared_context: &SharedStyleContext, parent_flow_inline_size: Au)2432     fn compute_used_inline_size(&self,
2433                                 block: &mut BlockFlow,
2434                                 shared_context: &SharedStyleContext,
2435                                 parent_flow_inline_size: Au) {
2436         let mut input = self.compute_inline_size_constraint_inputs(block,
2437                                                                    parent_flow_inline_size,
2438                                                                    shared_context);
2439 
2440         let containing_block_inline_size =
2441             self.containing_block_inline_size(block, parent_flow_inline_size, shared_context);
2442 
2443         let mut solution = self.solve_inline_size_constraints(block, &input);
2444 
2445         // If the tentative used inline-size is greater than 'max-inline-size', inline-size should
2446         // be recalculated, but this time using the computed value of 'max-inline-size' as the
2447         // computed value for 'inline-size'.
2448         match block.fragment().style().max_inline_size().to_used_value(containing_block_inline_size) {
2449             Some(max_inline_size) if max_inline_size < solution.inline_size => {
2450                 input.computed_inline_size = MaybeAuto::Specified(max_inline_size);
2451                 solution = self.solve_inline_size_constraints(block, &input);
2452             }
2453             _ => {}
2454         }
2455 
2456         // If the resulting inline-size is smaller than 'min-inline-size', inline-size should be
2457         // recalculated, but this time using the value of 'min-inline-size' as the computed value
2458         // for 'inline-size'.
2459         let computed_min_inline_size =
2460             block.fragment().style().min_inline_size().to_used_value(containing_block_inline_size);
2461         if computed_min_inline_size > solution.inline_size {
2462             input.computed_inline_size = MaybeAuto::Specified(computed_min_inline_size);
2463             solution = self.solve_inline_size_constraints(block, &input);
2464         }
2465 
2466         self.set_inline_size_constraint_solutions(block, solution);
2467         self.set_inline_position_of_flow_if_necessary(block, solution);
2468     }
2469 
2470     /// Computes inline-start and inline-end margins and inline-size.
2471     ///
2472     /// This is used by both replaced and non-replaced Blocks.
2473     ///
2474     /// CSS 2.1 Section 10.3.3.
2475     /// Constraint Equation: margin-inline-start + margin-inline-end + inline-size =
2476     /// available_inline-size
2477     /// where available_inline-size = CB inline-size - (horizontal border + padding)
solve_block_inline_size_constraints(&self, block: &mut BlockFlow, input: &ISizeConstraintInput) -> ISizeConstraintSolution2478     fn solve_block_inline_size_constraints(&self,
2479                                            block: &mut BlockFlow,
2480                                            input: &ISizeConstraintInput)
2481                                            -> ISizeConstraintSolution {
2482         let (computed_inline_size, inline_start_margin, inline_end_margin, available_inline_size) =
2483             (input.computed_inline_size,
2484              input.inline_start_margin,
2485              input.inline_end_margin,
2486              input.available_inline_size);
2487 
2488         // Check for direction of parent flow (NOT Containing Block)
2489         let block_mode = block.base.writing_mode;
2490         let container_mode = block.base.block_container_writing_mode;
2491         let block_align = block.base.flags.text_align();
2492 
2493         // FIXME (mbrubeck): Handle vertical writing modes.
2494         let parent_has_same_direction = container_mode.is_bidi_ltr() == block_mode.is_bidi_ltr();
2495 
2496         // If inline-size is not 'auto', and inline-size + margins > available_inline-size, all
2497         // 'auto' margins are treated as 0.
2498         let (inline_start_margin, inline_end_margin) = match computed_inline_size {
2499             MaybeAuto::Auto => (inline_start_margin, inline_end_margin),
2500             MaybeAuto::Specified(inline_size) => {
2501                 let inline_start = inline_start_margin.specified_or_zero();
2502                 let inline_end = inline_end_margin.specified_or_zero();
2503 
2504                 if (inline_start + inline_end + inline_size) > available_inline_size {
2505                     (MaybeAuto::Specified(inline_start), MaybeAuto::Specified(inline_end))
2506                 } else {
2507                     (inline_start_margin, inline_end_margin)
2508                 }
2509             }
2510         };
2511 
2512         // Invariant: inline-start_margin + inline-size + inline-end_margin ==
2513         // available_inline-size
2514         let (inline_start_margin, inline_size, inline_end_margin) =
2515             match (inline_start_margin, computed_inline_size, inline_end_margin) {
2516                 // If all have a computed value other than 'auto', the system is over-constrained.
2517                 (MaybeAuto::Specified(margin_start),
2518                  MaybeAuto::Specified(inline_size),
2519                  MaybeAuto::Specified(margin_end)) => {
2520                     // servo_left, servo_right, and servo_center are used to implement
2521                     // the "align descendants" rule in HTML5 § 14.2.
2522                     if block_align == TextAlign::ServoCenter {
2523                         // Ignore any existing margins, and make the inline-start and
2524                         // inline-end margins equal.
2525                         let margin = (available_inline_size - inline_size).scale_by(0.5);
2526                         (margin, inline_size, margin)
2527                     } else {
2528                         let ignore_end_margin = match block_align {
2529                             TextAlign::ServoLeft => block_mode.is_bidi_ltr(),
2530                             TextAlign::ServoRight => !block_mode.is_bidi_ltr(),
2531                             _ => parent_has_same_direction,
2532                         };
2533                         if ignore_end_margin {
2534                             (margin_start, inline_size, available_inline_size -
2535                              (margin_start + inline_size))
2536                         } else {
2537                             (available_inline_size - (margin_end + inline_size),
2538                              inline_size,
2539                              margin_end)
2540                         }
2541                     }
2542                 }
2543                 // If exactly one value is 'auto', solve for it
2544                 (MaybeAuto::Auto,
2545                  MaybeAuto::Specified(inline_size),
2546                  MaybeAuto::Specified(margin_end)) =>
2547                     (available_inline_size - (inline_size + margin_end), inline_size, margin_end),
2548                 (MaybeAuto::Specified(margin_start),
2549                  MaybeAuto::Auto,
2550                  MaybeAuto::Specified(margin_end)) => {
2551                     (margin_start,
2552                      available_inline_size - (margin_start + margin_end),
2553                      margin_end)
2554                 }
2555                 (MaybeAuto::Specified(margin_start),
2556                  MaybeAuto::Specified(inline_size),
2557                  MaybeAuto::Auto) => {
2558                     (margin_start,
2559                      inline_size,
2560                      available_inline_size - (margin_start + inline_size))
2561                 }
2562 
2563                 // If inline-size is set to 'auto', any other 'auto' value becomes '0',
2564                 // and inline-size is solved for
2565                 (MaybeAuto::Auto, MaybeAuto::Auto, MaybeAuto::Specified(margin_end)) => {
2566                     (Au(0), available_inline_size - margin_end, margin_end)
2567                 }
2568                 (MaybeAuto::Specified(margin_start), MaybeAuto::Auto, MaybeAuto::Auto) => {
2569                     (margin_start, available_inline_size - margin_start, Au(0))
2570                 }
2571                 (MaybeAuto::Auto, MaybeAuto::Auto, MaybeAuto::Auto) => {
2572                     (Au(0), available_inline_size, Au(0))
2573                 }
2574 
2575                 // If inline-start and inline-end margins are auto, they become equal
2576                 (MaybeAuto::Auto, MaybeAuto::Specified(inline_size), MaybeAuto::Auto) => {
2577                     let margin = (available_inline_size - inline_size).scale_by(0.5);
2578                     (margin, inline_size, margin)
2579                 }
2580             };
2581 
2582         ISizeConstraintSolution::new(inline_size, inline_start_margin, inline_end_margin)
2583     }
2584 }
2585 
2586 /// The different types of Blocks.
2587 ///
2588 /// They mainly differ in the way inline-size and block-sizes and margins are calculated
2589 /// for them.
2590 pub struct AbsoluteNonReplaced;
2591 pub struct AbsoluteReplaced;
2592 pub struct BlockNonReplaced;
2593 pub struct BlockReplaced;
2594 pub struct FloatNonReplaced;
2595 pub struct FloatReplaced;
2596 pub struct InlineBlockNonReplaced;
2597 pub struct InlineBlockReplaced;
2598 pub struct InlineFlexItem;
2599 
2600 impl ISizeAndMarginsComputer for AbsoluteNonReplaced {
2601     /// Solve the horizontal constraint equation for absolute non-replaced elements.
2602     ///
2603     /// CSS Section 10.3.7
2604     /// Constraint equation:
2605     /// inline-start + inline-end + inline-size + margin-inline-start + margin-inline-end
2606     /// = absolute containing block inline-size - (horizontal padding and border)
2607     /// [aka available inline-size]
2608     ///
2609     /// Return the solution for the equation.
solve_inline_size_constraints(&self, block: &mut BlockFlow, input: &ISizeConstraintInput) -> ISizeConstraintSolution2610     fn solve_inline_size_constraints(&self,
2611                                      block: &mut BlockFlow,
2612                                      input: &ISizeConstraintInput)
2613                                      -> ISizeConstraintSolution {
2614         let &ISizeConstraintInput {
2615             computed_inline_size,
2616             inline_start_margin,
2617             inline_end_margin,
2618             inline_start,
2619             inline_end,
2620             available_inline_size,
2621             ..
2622         } = input;
2623 
2624         // Check for direction of parent flow (NOT Containing Block)
2625         let block_mode = block.base.writing_mode;
2626         let container_mode = block.base.block_container_writing_mode;
2627 
2628         // FIXME (mbrubeck): Handle vertical writing modes.
2629         let parent_has_same_direction = container_mode.is_bidi_ltr() == block_mode.is_bidi_ltr();
2630 
2631         let (inline_start, inline_size, margin_inline_start, margin_inline_end) =
2632             match (inline_start, inline_end, computed_inline_size) {
2633                 (MaybeAuto::Auto, MaybeAuto::Auto, MaybeAuto::Auto) => {
2634                     let margin_start = inline_start_margin.specified_or_zero();
2635                     let margin_end = inline_end_margin.specified_or_zero();
2636                     // Now it is the same situation as inline-start Specified and inline-end
2637                     // and inline-size Auto.
2638 
2639                     // Set inline-end to zero to calculate inline-size.
2640                     let inline_size =
2641                         block.get_shrink_to_fit_inline_size(available_inline_size -
2642                                                             (margin_start + margin_end));
2643                     (Au(0), inline_size, margin_start, margin_end)
2644                 }
2645                 (MaybeAuto::Specified(inline_start),
2646                  MaybeAuto::Specified(inline_end),
2647                  MaybeAuto::Specified(inline_size)) => {
2648                     match (inline_start_margin, inline_end_margin) {
2649                         (MaybeAuto::Auto, MaybeAuto::Auto) => {
2650                             let total_margin_val =
2651                                 available_inline_size - inline_start - inline_end - inline_size;
2652                             if total_margin_val < Au(0) {
2653                                 if parent_has_same_direction {
2654                                     // margin-inline-start becomes 0
2655                                     (inline_start, inline_size, Au(0), total_margin_val)
2656                                 } else {
2657                                     // margin-inline-end becomes 0, because it's toward the parent's
2658                                     // inline-start edge.
2659                                     (inline_start, inline_size, total_margin_val, Au(0))
2660                                 }
2661                             } else {
2662                                 // Equal margins
2663                                 (inline_start,
2664                                  inline_size,
2665                                  total_margin_val.scale_by(0.5),
2666                                  total_margin_val.scale_by(0.5))
2667                             }
2668                         }
2669                         (MaybeAuto::Specified(margin_start), MaybeAuto::Auto) => {
2670                             let sum = inline_start + inline_end + inline_size + margin_start;
2671                             (inline_start, inline_size, margin_start, available_inline_size - sum)
2672                         }
2673                         (MaybeAuto::Auto, MaybeAuto::Specified(margin_end)) => {
2674                             let sum = inline_start + inline_end + inline_size + margin_end;
2675                             (inline_start, inline_size, available_inline_size - sum, margin_end)
2676                         }
2677                         (MaybeAuto::Specified(margin_start), MaybeAuto::Specified(margin_end)) => {
2678                             // Values are over-constrained.
2679                             let sum = inline_start + inline_size + margin_start + margin_end;
2680                             if parent_has_same_direction {
2681                                 // Ignore value for 'inline-end'
2682                                 (inline_start, inline_size, margin_start, margin_end)
2683                             } else {
2684                                 // Ignore value for 'inline-start'
2685                                 (available_inline_size - sum,
2686                                  inline_size,
2687                                  margin_start,
2688                                  margin_end)
2689                             }
2690                         }
2691                     }
2692                 }
2693                 // For the rest of the cases, auto values for margin are set to 0
2694 
2695                 // If only one is Auto, solve for it
2696                 (MaybeAuto::Auto,
2697                  MaybeAuto::Specified(inline_end),
2698                  MaybeAuto::Specified(inline_size)) => {
2699                     let margin_start = inline_start_margin.specified_or_zero();
2700                     let margin_end = inline_end_margin.specified_or_zero();
2701                     let sum = inline_end + inline_size + margin_start + margin_end;
2702                     (available_inline_size - sum, inline_size, margin_start, margin_end)
2703                 }
2704                 (MaybeAuto::Specified(inline_start),
2705                  MaybeAuto::Auto,
2706                  MaybeAuto::Specified(inline_size)) => {
2707                     let margin_start = inline_start_margin.specified_or_zero();
2708                     let margin_end = inline_end_margin.specified_or_zero();
2709                     (inline_start, inline_size, margin_start, margin_end)
2710                 }
2711                 (MaybeAuto::Specified(inline_start),
2712                  MaybeAuto::Specified(inline_end),
2713                  MaybeAuto::Auto) => {
2714                     let margin_start = inline_start_margin.specified_or_zero();
2715                     let margin_end = inline_end_margin.specified_or_zero();
2716                     let sum = inline_start + inline_end + margin_start + margin_end;
2717                     (inline_start, available_inline_size - sum, margin_start, margin_end)
2718                 }
2719 
2720                 // If inline-size is auto, then inline-size is shrink-to-fit. Solve for the
2721                 // non-auto value.
2722                 (MaybeAuto::Specified(inline_start), MaybeAuto::Auto, MaybeAuto::Auto) => {
2723                     let margin_start = inline_start_margin.specified_or_zero();
2724                     let margin_end = inline_end_margin.specified_or_zero();
2725                     // Set inline-end to zero to calculate inline-size
2726                     let inline_size =
2727                         block.get_shrink_to_fit_inline_size(available_inline_size -
2728                                                             (margin_start + margin_end));
2729                     (inline_start, inline_size, margin_start, margin_end)
2730                 }
2731                 (MaybeAuto::Auto, MaybeAuto::Specified(inline_end), MaybeAuto::Auto) => {
2732                     let margin_start = inline_start_margin.specified_or_zero();
2733                     let margin_end = inline_end_margin.specified_or_zero();
2734                     // Set inline-start to zero to calculate inline-size
2735                     let inline_size =
2736                         block.get_shrink_to_fit_inline_size(available_inline_size -
2737                                                             (margin_start + margin_end));
2738                     let sum = inline_end + inline_size + margin_start + margin_end;
2739                     (available_inline_size - sum, inline_size, margin_start, margin_end)
2740                 }
2741 
2742                 (MaybeAuto::Auto, MaybeAuto::Auto, MaybeAuto::Specified(inline_size)) => {
2743                     let margin_start = inline_start_margin.specified_or_zero();
2744                     let margin_end = inline_end_margin.specified_or_zero();
2745                     // Setting 'inline-start' to static position because direction is 'ltr'.
2746                     // TODO: Handle 'rtl' when it is implemented.
2747                     (Au(0), inline_size, margin_start, margin_end)
2748                 }
2749             };
2750         ISizeConstraintSolution::for_absolute_flow(inline_start,
2751                                                    inline_size,
2752                                                    margin_inline_start,
2753                                                    margin_inline_end)
2754     }
2755 
containing_block_inline_size(&self, block: &mut BlockFlow, _: Au, shared_context: &SharedStyleContext) -> Au2756     fn containing_block_inline_size(&self,
2757                                     block: &mut BlockFlow,
2758                                     _: Au,
2759                                     shared_context: &SharedStyleContext)
2760                                     -> Au {
2761         let opaque_block = OpaqueFlow::from_flow(block);
2762         block.containing_block_size(&shared_context.viewport_size(), opaque_block).inline
2763     }
2764 
set_inline_position_of_flow_if_necessary(&self, block: &mut BlockFlow, solution: ISizeConstraintSolution)2765     fn set_inline_position_of_flow_if_necessary(&self,
2766                                                 block: &mut BlockFlow,
2767                                                 solution: ISizeConstraintSolution) {
2768         // Set the inline position of the absolute flow wrt to its containing block.
2769         if !block.base.flags.contains(FlowFlags::INLINE_POSITION_IS_STATIC) {
2770             block.base.position.start.i = solution.inline_start;
2771         }
2772     }
2773 }
2774 
2775 impl ISizeAndMarginsComputer for AbsoluteReplaced {
2776     /// Solve the horizontal constraint equation for absolute replaced elements.
2777     ///
2778     /// CSS Section 10.3.8
2779     /// Constraint equation:
2780     /// inline-start + inline-end + inline-size + margin-inline-start + margin-inline-end
2781     /// = absolute containing block inline-size - (horizontal padding and border)
2782     /// [aka available_inline-size]
2783     ///
2784     /// Return the solution for the equation.
solve_inline_size_constraints(&self, _: &mut BlockFlow, input: &ISizeConstraintInput) -> ISizeConstraintSolution2785     fn solve_inline_size_constraints(&self, _: &mut BlockFlow, input: &ISizeConstraintInput)
2786                                      -> ISizeConstraintSolution {
2787         let &ISizeConstraintInput {
2788             computed_inline_size,
2789             inline_start_margin,
2790             inline_end_margin,
2791             inline_start,
2792             inline_end,
2793             available_inline_size,
2794             ..
2795         } = input;
2796         // TODO: Check for direction of static-position Containing Block (aka
2797         // parent flow, _not_ the actual Containing Block) when right-to-left
2798         // is implemented
2799         // Assume direction is 'ltr' for now
2800         // TODO: Handle all the cases for 'rtl' direction.
2801 
2802         let inline_size = match computed_inline_size {
2803             MaybeAuto::Specified(w) => w,
2804             _ => panic!("{} {}",
2805                        "The used value for inline_size for absolute replaced flow",
2806                        "should have already been calculated by now.")
2807         };
2808 
2809         let (inline_start, inline_size, margin_inline_start, margin_inline_end) =
2810             match (inline_start, inline_end) {
2811                 (MaybeAuto::Auto, MaybeAuto::Auto) => {
2812                     let margin_start = inline_start_margin.specified_or_zero();
2813                     let margin_end = inline_end_margin.specified_or_zero();
2814                     (Au(0), inline_size, margin_start, margin_end)
2815                 }
2816                 // If only one is Auto, solve for it
2817                 (MaybeAuto::Auto, MaybeAuto::Specified(inline_end)) => {
2818                     let margin_start = inline_start_margin.specified_or_zero();
2819                     let margin_end = inline_end_margin.specified_or_zero();
2820                     let sum = inline_end + inline_size + margin_start + margin_end;
2821                     (available_inline_size - sum, inline_size, margin_start, margin_end)
2822                 }
2823                 (MaybeAuto::Specified(inline_start), MaybeAuto::Auto) => {
2824                     let margin_start = inline_start_margin.specified_or_zero();
2825                     let margin_end = inline_end_margin.specified_or_zero();
2826                     (inline_start, inline_size, margin_start, margin_end)
2827                 }
2828                 (MaybeAuto::Specified(inline_start), MaybeAuto::Specified(inline_end)) => {
2829                     match (inline_start_margin, inline_end_margin) {
2830                         (MaybeAuto::Auto, MaybeAuto::Auto) => {
2831                             let total_margin_val = available_inline_size - inline_start -
2832                                 inline_end - inline_size;
2833                             if total_margin_val < Au(0) {
2834                                 // margin-inline-start becomes 0 because direction is 'ltr'.
2835                                 (inline_start, inline_size, Au(0), total_margin_val)
2836                             } else {
2837                                 // Equal margins
2838                                 (inline_start,
2839                                  inline_size,
2840                                  total_margin_val.scale_by(0.5),
2841                                  total_margin_val.scale_by(0.5))
2842                             }
2843                         }
2844                         (MaybeAuto::Specified(margin_start), MaybeAuto::Auto) => {
2845                             let sum = inline_start + inline_end + inline_size + margin_start;
2846                             (inline_start, inline_size, margin_start, available_inline_size - sum)
2847                         }
2848                         (MaybeAuto::Auto, MaybeAuto::Specified(margin_end)) => {
2849                             let sum = inline_start + inline_end + inline_size + margin_end;
2850                             (inline_start, inline_size, available_inline_size - sum, margin_end)
2851                         }
2852                         (MaybeAuto::Specified(margin_start), MaybeAuto::Specified(margin_end)) => {
2853                             // Values are over-constrained.
2854                             // Ignore value for 'inline-end' cos direction is 'ltr'.
2855                             (inline_start, inline_size, margin_start, margin_end)
2856                         }
2857                     }
2858                 }
2859             };
2860         ISizeConstraintSolution::for_absolute_flow(inline_start,
2861                                                    inline_size,
2862                                                    margin_inline_start,
2863                                                    margin_inline_end)
2864     }
2865 
2866     /// Calculate used value of inline-size just like we do for inline replaced elements.
initial_computed_inline_size(&self, block: &mut BlockFlow, _: Au, shared_context: &SharedStyleContext) -> MaybeAuto2867     fn initial_computed_inline_size(&self,
2868                                     block: &mut BlockFlow,
2869                                     _: Au,
2870                                     shared_context: &SharedStyleContext)
2871                                     -> MaybeAuto {
2872         let opaque_block = OpaqueFlow::from_flow(block);
2873         let containing_block_inline_size =
2874             block.containing_block_size(&shared_context.viewport_size(), opaque_block).inline;
2875         let container_block_size = block.explicit_block_containing_size(shared_context);
2876         let fragment = block.fragment();
2877         fragment.assign_replaced_inline_size_if_necessary(containing_block_inline_size, container_block_size);
2878         // For replaced absolute flow, the rest of the constraint solving will
2879         // take inline-size to be specified as the value computed here.
2880         MaybeAuto::Specified(fragment.content_box().size.inline)
2881     }
2882 
containing_block_inline_size(&self, block: &mut BlockFlow, _: Au, shared_context: &SharedStyleContext) -> Au2883     fn containing_block_inline_size(&self,
2884                                     block: &mut BlockFlow,
2885                                     _: Au,
2886                                     shared_context: &SharedStyleContext)
2887                                     -> Au {
2888         let opaque_block = OpaqueFlow::from_flow(block);
2889         block.containing_block_size(&shared_context.viewport_size(), opaque_block).inline
2890     }
2891 
set_inline_position_of_flow_if_necessary(&self, block: &mut BlockFlow, solution: ISizeConstraintSolution)2892     fn set_inline_position_of_flow_if_necessary(&self,
2893                                                 block: &mut BlockFlow,
2894                                                 solution: ISizeConstraintSolution) {
2895         // Set the x-coordinate of the absolute flow wrt to its containing block.
2896         block.base.position.start.i = solution.inline_start;
2897     }
2898 }
2899 
2900 impl ISizeAndMarginsComputer for BlockNonReplaced {
2901     /// Compute inline-start and inline-end margins and inline-size.
solve_inline_size_constraints(&self, block: &mut BlockFlow, input: &ISizeConstraintInput) -> ISizeConstraintSolution2902     fn solve_inline_size_constraints(&self,
2903                                      block: &mut BlockFlow,
2904                                      input: &ISizeConstraintInput)
2905                                      -> ISizeConstraintSolution {
2906         self.solve_block_inline_size_constraints(block, input)
2907     }
2908 }
2909 
2910 impl ISizeAndMarginsComputer for BlockReplaced {
2911     /// Compute inline-start and inline-end margins and inline-size.
2912     ///
2913     /// ISize has already been calculated. We now calculate the margins just
2914     /// like for non-replaced blocks.
solve_inline_size_constraints(&self, block: &mut BlockFlow, input: &ISizeConstraintInput) -> ISizeConstraintSolution2915     fn solve_inline_size_constraints(&self,
2916                                      block: &mut BlockFlow,
2917                                      input: &ISizeConstraintInput)
2918                                      -> ISizeConstraintSolution {
2919         match input.computed_inline_size {
2920             MaybeAuto::Specified(_) => {},
2921             MaybeAuto::Auto => {
2922                 panic!("BlockReplaced: inline_size should have been computed by now")
2923             }
2924         };
2925         self.solve_block_inline_size_constraints(block, input)
2926     }
2927 
2928     /// Calculate used value of inline-size just like we do for inline replaced elements.
initial_computed_inline_size(&self, block: &mut BlockFlow, parent_flow_inline_size: Au, shared_context: &SharedStyleContext) -> MaybeAuto2929     fn initial_computed_inline_size(&self,
2930                                     block: &mut BlockFlow,
2931                                     parent_flow_inline_size: Au,
2932                                     shared_context: &SharedStyleContext)
2933                                     -> MaybeAuto {
2934         let container_block_size = block.explicit_block_containing_size(shared_context);
2935         let fragment = block.fragment();
2936         fragment.assign_replaced_inline_size_if_necessary(parent_flow_inline_size, container_block_size);
2937         // For replaced block flow, the rest of the constraint solving will
2938         // take inline-size to be specified as the value computed here.
2939         MaybeAuto::Specified(fragment.content_box().size.inline)
2940     }
2941 
2942 }
2943 
2944 impl ISizeAndMarginsComputer for FloatNonReplaced {
2945     /// CSS Section 10.3.5
2946     ///
2947     /// If inline-size is computed as 'auto', the used value is the 'shrink-to-fit' inline-size.
solve_inline_size_constraints(&self, block: &mut BlockFlow, input: &ISizeConstraintInput) -> ISizeConstraintSolution2948     fn solve_inline_size_constraints(&self,
2949                                block: &mut BlockFlow,
2950                                input: &ISizeConstraintInput)
2951                                -> ISizeConstraintSolution {
2952         let (computed_inline_size, inline_start_margin, inline_end_margin, available_inline_size) =
2953             (input.computed_inline_size,
2954              input.inline_start_margin,
2955              input.inline_end_margin,
2956              input.available_inline_size);
2957         let margin_inline_start = inline_start_margin.specified_or_zero();
2958         let margin_inline_end = inline_end_margin.specified_or_zero();
2959         let available_inline_size_float = available_inline_size - margin_inline_start -
2960             margin_inline_end;
2961         let shrink_to_fit = block.get_shrink_to_fit_inline_size(available_inline_size_float);
2962         let inline_size = computed_inline_size.specified_or_default(shrink_to_fit);
2963         debug!("assign_inline_sizes_float -- inline_size: {:?}", inline_size);
2964         ISizeConstraintSolution::new(inline_size, margin_inline_start, margin_inline_end)
2965     }
2966 }
2967 
2968 impl ISizeAndMarginsComputer for FloatReplaced {
2969     /// CSS Section 10.3.5
2970     ///
2971     /// If inline-size is computed as 'auto', the used value is the 'shrink-to-fit' inline-size.
solve_inline_size_constraints(&self, _: &mut BlockFlow, input: &ISizeConstraintInput) -> ISizeConstraintSolution2972     fn solve_inline_size_constraints(&self, _: &mut BlockFlow, input: &ISizeConstraintInput)
2973                                -> ISizeConstraintSolution {
2974         let (computed_inline_size, inline_start_margin, inline_end_margin) =
2975             (input.computed_inline_size, input.inline_start_margin, input.inline_end_margin);
2976         let margin_inline_start = inline_start_margin.specified_or_zero();
2977         let margin_inline_end = inline_end_margin.specified_or_zero();
2978         let inline_size = match computed_inline_size {
2979             MaybeAuto::Specified(w) => w,
2980             MaybeAuto::Auto => panic!("FloatReplaced: inline_size should have been computed by now")
2981         };
2982         debug!("assign_inline_sizes_float -- inline_size: {:?}", inline_size);
2983         ISizeConstraintSolution::new(inline_size, margin_inline_start, margin_inline_end)
2984     }
2985 
2986     /// Calculate used value of inline-size just like we do for inline replaced elements.
initial_computed_inline_size(&self, block: &mut BlockFlow, parent_flow_inline_size: Au, shared_context: &SharedStyleContext) -> MaybeAuto2987     fn initial_computed_inline_size(&self,
2988                                     block: &mut BlockFlow,
2989                                     parent_flow_inline_size: Au,
2990                                     shared_context: &SharedStyleContext)
2991                                     -> MaybeAuto {
2992         let container_block_size = block.explicit_block_containing_size(shared_context);
2993         let fragment = block.fragment();
2994         fragment.assign_replaced_inline_size_if_necessary(parent_flow_inline_size, container_block_size);
2995         // For replaced block flow, the rest of the constraint solving will
2996         // take inline-size to be specified as the value computed here.
2997         MaybeAuto::Specified(fragment.content_box().size.inline)
2998     }
2999 }
3000 
3001 impl ISizeAndMarginsComputer for InlineBlockNonReplaced {
3002     /// Compute inline-start and inline-end margins and inline-size.
solve_inline_size_constraints(&self, block: &mut BlockFlow, input: &ISizeConstraintInput) -> ISizeConstraintSolution3003     fn solve_inline_size_constraints(&self,
3004                                      block: &mut BlockFlow,
3005                                      input: &ISizeConstraintInput)
3006                                      -> ISizeConstraintSolution {
3007         let (computed_inline_size,
3008              inline_start_margin,
3009              inline_end_margin,
3010              available_inline_size) =
3011             (input.computed_inline_size,
3012              input.inline_start_margin,
3013              input.inline_end_margin,
3014              input.available_inline_size);
3015 
3016         // For inline-blocks, `auto` margins compute to 0.
3017         let inline_start_margin = inline_start_margin.specified_or_zero();
3018         let inline_end_margin = inline_end_margin.specified_or_zero();
3019 
3020         // If inline-size is set to 'auto', and this is an inline block, use the
3021         // shrink to fit algorithm (see CSS 2.1 § 10.3.9)
3022         let inline_size = match computed_inline_size {
3023             MaybeAuto::Auto => {
3024                 block.get_shrink_to_fit_inline_size(available_inline_size - (inline_start_margin +
3025                                                                              inline_end_margin))
3026             }
3027             MaybeAuto::Specified(inline_size) => inline_size,
3028         };
3029 
3030         ISizeConstraintSolution::new(inline_size, inline_start_margin, inline_end_margin)
3031     }
3032 }
3033 
3034 impl ISizeAndMarginsComputer for InlineBlockReplaced {
3035     /// Compute inline-start and inline-end margins and inline-size.
3036     ///
3037     /// ISize has already been calculated. We now calculate the margins just
3038     /// like for non-replaced blocks.
solve_inline_size_constraints(&self, block: &mut BlockFlow, input: &ISizeConstraintInput) -> ISizeConstraintSolution3039     fn solve_inline_size_constraints(&self,
3040                                      block: &mut BlockFlow,
3041                                      input: &ISizeConstraintInput)
3042                                      -> ISizeConstraintSolution {
3043         debug_assert!(match input.computed_inline_size {
3044             MaybeAuto::Specified(_) => true,
3045             MaybeAuto::Auto => false,
3046         });
3047 
3048         let (computed_inline_size,
3049              inline_start_margin,
3050              inline_end_margin,
3051              available_inline_size) =
3052             (input.computed_inline_size,
3053              input.inline_start_margin,
3054              input.inline_end_margin,
3055              input.available_inline_size);
3056 
3057         // For inline-blocks, `auto` margins compute to 0.
3058         let inline_start_margin = inline_start_margin.specified_or_zero();
3059         let inline_end_margin = inline_end_margin.specified_or_zero();
3060 
3061         // If inline-size is set to 'auto', and this is an inline block, use the
3062         // shrink to fit algorithm (see CSS 2.1 § 10.3.9)
3063         let inline_size = match computed_inline_size {
3064             MaybeAuto::Auto => {
3065                 block.get_shrink_to_fit_inline_size(available_inline_size - (inline_start_margin +
3066                                                                              inline_end_margin))
3067             }
3068             MaybeAuto::Specified(inline_size) => inline_size,
3069         };
3070 
3071         ISizeConstraintSolution::new(inline_size, inline_start_margin, inline_end_margin)
3072     }
3073 
3074     /// Calculate used value of inline-size just like we do for inline replaced elements.
initial_computed_inline_size(&self, block: &mut BlockFlow, parent_flow_inline_size: Au, shared_context: &SharedStyleContext) -> MaybeAuto3075     fn initial_computed_inline_size(&self,
3076                                     block: &mut BlockFlow,
3077                                     parent_flow_inline_size: Au,
3078                                     shared_context: &SharedStyleContext)
3079                                     -> MaybeAuto {
3080         let container_block_size = block.explicit_block_containing_size(shared_context);
3081         let fragment = block.fragment();
3082         fragment.assign_replaced_inline_size_if_necessary(parent_flow_inline_size, container_block_size);
3083         // For replaced block flow, the rest of the constraint solving will
3084         // take inline-size to be specified as the value computed here.
3085         MaybeAuto::Specified(fragment.content_box().size.inline)
3086     }
3087 }
3088 
3089 impl ISizeAndMarginsComputer for InlineFlexItem {
3090     // Replace the default method directly to prevent recalculating and setting margins again
3091     // which has already been set by its parent.
compute_used_inline_size(&self, block: &mut BlockFlow, shared_context: &SharedStyleContext, parent_flow_inline_size: Au)3092     fn compute_used_inline_size(&self,
3093                                 block: &mut BlockFlow,
3094                                 shared_context: &SharedStyleContext,
3095                                 parent_flow_inline_size: Au) {
3096         let container_block_size = block.explicit_block_containing_size(shared_context);
3097         block.fragment.assign_replaced_inline_size_if_necessary(parent_flow_inline_size,
3098                                                                 container_block_size);
3099     }
3100 
3101     // The used inline size and margins are set by parent flex flow, do nothing here.
solve_inline_size_constraints(&self, block: &mut BlockFlow, _: &ISizeConstraintInput) -> ISizeConstraintSolution3102     fn solve_inline_size_constraints(&self,
3103                                      block: &mut BlockFlow,
3104                                      _: &ISizeConstraintInput)
3105                                      -> ISizeConstraintSolution {
3106         let fragment = block.fragment();
3107         ISizeConstraintSolution::new(fragment.border_box.size.inline,
3108                                      fragment.margin.inline_start,
3109                                      fragment.margin.inline_end)
3110     }
3111 }
3112