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 //! Borders, padding, and margins.
6 
7 #![deny(unsafe_code)]
8 
9 use app_units::Au;
10 use euclid::{SideOffsets2D, Size2D};
11 use fragment::Fragment;
12 use std::cmp::{max, min};
13 use std::fmt;
14 use style::logical_geometry::{LogicalMargin, WritingMode};
15 use style::properties::ComputedValues;
16 use style::values::computed::{BorderCornerRadius, LengthOrPercentageOrAuto};
17 use style::values::computed::{LengthOrPercentage, LengthOrPercentageOrNone};
18 
19 /// A collapsible margin. See CSS 2.1 § 8.3.1.
20 #[derive(Clone, Copy, Debug)]
21 pub struct AdjoiningMargins {
22     /// The value of the greatest positive margin.
23     pub most_positive: Au,
24 
25     /// The actual value (not the absolute value) of the negative margin with the largest absolute
26     /// value. Since this is not the absolute value, this is always zero or negative.
27     pub most_negative: Au,
28 }
29 
30 impl AdjoiningMargins {
new() -> AdjoiningMargins31     pub fn new() -> AdjoiningMargins {
32         AdjoiningMargins {
33             most_positive: Au(0),
34             most_negative: Au(0),
35         }
36     }
37 
from_margin(margin_value: Au) -> AdjoiningMargins38     pub fn from_margin(margin_value: Au) -> AdjoiningMargins {
39         if margin_value >= Au(0) {
40             AdjoiningMargins {
41                 most_positive: margin_value,
42                 most_negative: Au(0),
43             }
44         } else {
45             AdjoiningMargins {
46                 most_positive: Au(0),
47                 most_negative: margin_value,
48             }
49         }
50     }
51 
union(&mut self, other: AdjoiningMargins)52     pub fn union(&mut self, other: AdjoiningMargins) {
53         self.most_positive = max(self.most_positive, other.most_positive);
54         self.most_negative = min(self.most_negative, other.most_negative)
55     }
56 
collapse(&self) -> Au57     pub fn collapse(&self) -> Au {
58         self.most_positive + self.most_negative
59     }
60 }
61 
62 /// Represents the block-start and block-end margins of a flow with collapsible margins. See CSS 2.1 § 8.3.1.
63 #[derive(Clone, Copy, Debug)]
64 pub enum CollapsibleMargins {
65     /// Margins may not collapse with this flow.
66     None(Au, Au),
67 
68     /// Both the block-start and block-end margins (specified here in that order) may collapse, but the
69     /// margins do not collapse through this flow.
70     Collapse(AdjoiningMargins, AdjoiningMargins),
71 
72     /// Margins collapse *through* this flow. This means, essentially, that the flow doesn’t
73     /// have any border, padding, or out-of-flow (floating or positioned) content
74     CollapseThrough(AdjoiningMargins),
75 }
76 
77 impl CollapsibleMargins {
new() -> CollapsibleMargins78     pub fn new() -> CollapsibleMargins {
79         CollapsibleMargins::None(Au(0), Au(0))
80     }
81 
82     /// Returns the amount of margin that should be applied in a noncollapsible context. This is
83     /// currently used to apply block-start margin for hypothetical boxes, since we do not collapse
84     /// margins of hypothetical boxes.
block_start_margin_for_noncollapsible_context(&self) -> Au85     pub fn block_start_margin_for_noncollapsible_context(&self) -> Au {
86         match *self {
87             CollapsibleMargins::None(block_start, _) => block_start,
88             CollapsibleMargins::Collapse(ref block_start, _) |
89             CollapsibleMargins::CollapseThrough(ref block_start) => block_start.collapse(),
90         }
91     }
92 
block_end_margin_for_noncollapsible_context(&self) -> Au93     pub fn block_end_margin_for_noncollapsible_context(&self) -> Au {
94         match *self {
95             CollapsibleMargins::None(_, block_end) => block_end,
96             CollapsibleMargins::Collapse(_, ref block_end) |
97             CollapsibleMargins::CollapseThrough(ref block_end) => block_end.collapse(),
98         }
99     }
100 }
101 
102 enum FinalMarginState {
103     MarginsCollapseThrough,
104     BottomMarginCollapses,
105 }
106 
107 pub struct MarginCollapseInfo {
108     pub state: MarginCollapseState,
109     pub block_start_margin: AdjoiningMargins,
110     pub margin_in: AdjoiningMargins,
111 }
112 
113 impl MarginCollapseInfo {
initialize_block_start_margin( fragment: &Fragment, can_collapse_block_start_margin_with_kids: bool, ) -> MarginCollapseInfo114     pub fn initialize_block_start_margin(
115         fragment: &Fragment,
116         can_collapse_block_start_margin_with_kids: bool,
117     ) -> MarginCollapseInfo {
118         MarginCollapseInfo {
119             state: if can_collapse_block_start_margin_with_kids {
120                 MarginCollapseState::AccumulatingCollapsibleTopMargin
121             } else {
122                 MarginCollapseState::AccumulatingMarginIn
123             },
124             block_start_margin: AdjoiningMargins::from_margin(fragment.margin.block_start),
125             margin_in: AdjoiningMargins::new(),
126         }
127     }
128 
finish_and_compute_collapsible_margins(mut self, fragment: &Fragment, containing_block_size: Option<Au>, can_collapse_block_end_margin_with_kids: bool, mut may_collapse_through: bool) -> (CollapsibleMargins, Au)129     pub fn finish_and_compute_collapsible_margins(mut self,
130                                                   fragment: &Fragment,
131                                                   containing_block_size: Option<Au>,
132                                                   can_collapse_block_end_margin_with_kids: bool,
133                                                   mut may_collapse_through: bool)
134                                                   -> (CollapsibleMargins, Au) {
135         let state = match self.state {
136             MarginCollapseState::AccumulatingCollapsibleTopMargin => {
137                 may_collapse_through = may_collapse_through &&
138                     match fragment.style().content_block_size() {
139                         LengthOrPercentageOrAuto::Auto => true,
140                         LengthOrPercentageOrAuto::Length(l) => l.px() == 0.,
141                         LengthOrPercentageOrAuto::Percentage(v) => {
142                             v.0 == 0. || containing_block_size.is_none()
143                         }
144                         LengthOrPercentageOrAuto::Calc(_) => false,
145                     };
146 
147                 if may_collapse_through {
148                     match fragment.style().min_block_size() {
149                         LengthOrPercentage::Length(l) if l.px() == 0. => {
150                             FinalMarginState::MarginsCollapseThrough
151                         },
152                         LengthOrPercentage::Percentage(v) if v.0 == 0. => {
153                             FinalMarginState::MarginsCollapseThrough
154                         },
155                         _ => {
156                             // If the fragment has non-zero min-block-size, margins may not
157                             // collapse through it.
158                             FinalMarginState::BottomMarginCollapses
159                         }
160                     }
161                 } else {
162                     // If the fragment has an explicitly specified block-size, margins may not
163                     // collapse through it.
164                     FinalMarginState::BottomMarginCollapses
165                 }
166             }
167             MarginCollapseState::AccumulatingMarginIn => FinalMarginState::BottomMarginCollapses,
168         };
169 
170         // Different logic is needed here depending on whether this flow can collapse its block-end
171         // margin with its children.
172         let block_end_margin = fragment.margin.block_end;
173         if !can_collapse_block_end_margin_with_kids {
174             match state {
175                 FinalMarginState::MarginsCollapseThrough => {
176                     let advance = self.block_start_margin.collapse();
177                     self.margin_in.union(AdjoiningMargins::from_margin(block_end_margin));
178                     (CollapsibleMargins::Collapse(self.block_start_margin, self.margin_in),
179                                                   advance)
180                 }
181                 FinalMarginState::BottomMarginCollapses => {
182                     let advance = self.margin_in.collapse();
183                     self.margin_in.union(AdjoiningMargins::from_margin(block_end_margin));
184                     (CollapsibleMargins::Collapse(self.block_start_margin, self.margin_in),
185                                                   advance)
186                 }
187             }
188         } else {
189             match state {
190                 FinalMarginState::MarginsCollapseThrough => {
191                     self.block_start_margin.union(AdjoiningMargins::from_margin(block_end_margin));
192                     (CollapsibleMargins::CollapseThrough(self.block_start_margin), Au(0))
193                 }
194                 FinalMarginState::BottomMarginCollapses => {
195                     self.margin_in.union(AdjoiningMargins::from_margin(block_end_margin));
196                     (CollapsibleMargins::Collapse(self.block_start_margin, self.margin_in), Au(0))
197                 }
198             }
199         }
200     }
201 
current_float_ceiling(&mut self) -> Au202     pub fn current_float_ceiling(&mut self) -> Au {
203         match self.state {
204             MarginCollapseState::AccumulatingCollapsibleTopMargin => {
205                 // We do not include the top margin in the float ceiling, because the float flow
206                 // needs to be positioned relative to our *border box*, not our margin box. See
207                 // `tests/ref/float_under_top_margin_a.html`.
208                 Au(0)
209             }
210             MarginCollapseState::AccumulatingMarginIn => self.margin_in.collapse(),
211         }
212     }
213 
214     /// Adds the child's potentially collapsible block-start margin to the current margin state and
215     /// advances the Y offset by the appropriate amount to handle that margin. Returns the amount
216     /// that should be added to the Y offset during block layout.
advance_block_start_margin(&mut self, child_collapsible_margins: &CollapsibleMargins, can_collapse_block_start_margin: bool) -> Au217     pub fn advance_block_start_margin(&mut self,
218                                       child_collapsible_margins: &CollapsibleMargins,
219                                       can_collapse_block_start_margin: bool)
220                                       -> Au {
221         if !can_collapse_block_start_margin {
222             self.state = MarginCollapseState::AccumulatingMarginIn
223         }
224 
225         match (self.state, *child_collapsible_margins) {
226             (MarginCollapseState::AccumulatingCollapsibleTopMargin,
227              CollapsibleMargins::None(block_start, _)) => {
228                 self.state = MarginCollapseState::AccumulatingMarginIn;
229                 block_start
230             }
231             (MarginCollapseState::AccumulatingCollapsibleTopMargin,
232              CollapsibleMargins::Collapse(block_start, _)) => {
233                 self.block_start_margin.union(block_start);
234                 self.state = MarginCollapseState::AccumulatingMarginIn;
235                 Au(0)
236             }
237             (MarginCollapseState::AccumulatingMarginIn,
238              CollapsibleMargins::None(block_start, _)) => {
239                 let previous_margin_value = self.margin_in.collapse();
240                 self.margin_in = AdjoiningMargins::new();
241                 previous_margin_value + block_start
242             }
243             (MarginCollapseState::AccumulatingMarginIn,
244              CollapsibleMargins::Collapse(block_start, _)) => {
245                 self.margin_in.union(block_start);
246                 let margin_value = self.margin_in.collapse();
247                 self.margin_in = AdjoiningMargins::new();
248                 margin_value
249             }
250             (_, CollapsibleMargins::CollapseThrough(_)) => {
251                 // For now, we ignore this; this will be handled by `advance_block_end_margin`
252                 // below.
253                 Au(0)
254             }
255         }
256     }
257 
258     /// Adds the child's potentially collapsible block-end margin to the current margin state and
259     /// advances the Y offset by the appropriate amount to handle that margin. Returns the amount
260     /// that should be added to the Y offset during block layout.
advance_block_end_margin(&mut self, child_collapsible_margins: &CollapsibleMargins) -> Au261     pub fn advance_block_end_margin(&mut self, child_collapsible_margins: &CollapsibleMargins)
262                                     -> Au {
263         match (self.state, *child_collapsible_margins) {
264             (MarginCollapseState::AccumulatingCollapsibleTopMargin, CollapsibleMargins::None(..)) |
265             (MarginCollapseState::AccumulatingCollapsibleTopMargin,
266              CollapsibleMargins::Collapse(..)) => {
267                 // Can't happen because the state will have been replaced with
268                 // `MarginCollapseState::AccumulatingMarginIn` above.
269                 panic!("should not be accumulating collapsible block_start margins anymore!")
270             }
271             (MarginCollapseState::AccumulatingCollapsibleTopMargin,
272              CollapsibleMargins::CollapseThrough(margin)) => {
273                 self.block_start_margin.union(margin);
274                 Au(0)
275             }
276             (MarginCollapseState::AccumulatingMarginIn,
277              CollapsibleMargins::None(_, block_end)) => {
278                 assert_eq!(self.margin_in.most_positive, Au(0));
279                 assert_eq!(self.margin_in.most_negative, Au(0));
280                 block_end
281             }
282             (MarginCollapseState::AccumulatingMarginIn,
283              CollapsibleMargins::Collapse(_, block_end)) |
284             (MarginCollapseState::AccumulatingMarginIn,
285              CollapsibleMargins::CollapseThrough(block_end)) => {
286                 self.margin_in.union(block_end);
287                 Au(0)
288             }
289         }
290     }
291 }
292 
293 #[derive(Clone, Copy, Debug)]
294 pub enum MarginCollapseState {
295     /// We are accumulating margin on the logical top of this flow.
296     AccumulatingCollapsibleTopMargin,
297     /// We are accumulating margin between two blocks.
298     AccumulatingMarginIn,
299 }
300 
301 /// Intrinsic inline-sizes, which consist of minimum and preferred.
302 #[derive(Clone, Copy, Serialize)]
303 pub struct IntrinsicISizes {
304     /// The *minimum inline-size* of the content.
305     pub minimum_inline_size: Au,
306     /// The *preferred inline-size* of the content.
307     pub preferred_inline_size: Au,
308 }
309 
310 impl fmt::Debug for IntrinsicISizes {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result311     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
312         write!(f, "min={:?}, pref={:?}", self.minimum_inline_size, self.preferred_inline_size)
313     }
314 }
315 
316 impl IntrinsicISizes {
new() -> IntrinsicISizes317     pub fn new() -> IntrinsicISizes {
318         IntrinsicISizes {
319             minimum_inline_size: Au(0),
320             preferred_inline_size: Au(0),
321         }
322     }
323 }
324 
325 /// The temporary result of the computation of intrinsic inline-sizes.
326 #[derive(Debug)]
327 pub struct IntrinsicISizesContribution {
328     /// Intrinsic sizes for the content only (not counting borders, padding, or margins).
329     pub content_intrinsic_sizes: IntrinsicISizes,
330     /// The inline size of borders and padding, as well as margins if appropriate.
331     pub surrounding_size: Au,
332 }
333 
334 impl IntrinsicISizesContribution {
335     /// Creates and initializes an inline size computation with all sizes set to zero.
new() -> IntrinsicISizesContribution336     pub fn new() -> IntrinsicISizesContribution {
337         IntrinsicISizesContribution {
338             content_intrinsic_sizes: IntrinsicISizes::new(),
339             surrounding_size: Au(0),
340         }
341     }
342 
343     /// Adds the content intrinsic sizes and the surrounding size together to yield the final
344     /// intrinsic size computation.
finish(self) -> IntrinsicISizes345     pub fn finish(self) -> IntrinsicISizes {
346         IntrinsicISizes {
347             minimum_inline_size: self.content_intrinsic_sizes.minimum_inline_size +
348                                      self.surrounding_size,
349             preferred_inline_size: self.content_intrinsic_sizes.preferred_inline_size +
350                                      self.surrounding_size,
351         }
352     }
353 
354     /// Updates the computation so that the minimum is the maximum of the current minimum and the
355     /// given minimum and the preferred is the sum of the current preferred and the given
356     /// preferred. This is used when laying out fragments in the inline direction.
357     ///
358     /// FIXME(pcwalton): This is incorrect when the inline fragment contains forced line breaks
359     /// (e.g. `<br>` or `white-space: pre`).
union_inline(&mut self, sizes: &IntrinsicISizes)360     pub fn union_inline(&mut self, sizes: &IntrinsicISizes) {
361         self.content_intrinsic_sizes.minimum_inline_size =
362             max(self.content_intrinsic_sizes.minimum_inline_size, sizes.minimum_inline_size);
363         self.content_intrinsic_sizes.preferred_inline_size =
364             self.content_intrinsic_sizes.preferred_inline_size + sizes.preferred_inline_size
365     }
366 
367     /// Updates the computation so that the minimum is the sum of the current minimum and the
368     /// given minimum and the preferred is the sum of the current preferred and the given
369     /// preferred. This is used when laying out fragments in the inline direction when
370     /// `white-space` is `pre` or `nowrap`.
union_nonbreaking_inline(&mut self, sizes: &IntrinsicISizes)371     pub fn union_nonbreaking_inline(&mut self, sizes: &IntrinsicISizes) {
372         self.content_intrinsic_sizes.minimum_inline_size =
373             self.content_intrinsic_sizes.minimum_inline_size + sizes.minimum_inline_size;
374         self.content_intrinsic_sizes.preferred_inline_size =
375             self.content_intrinsic_sizes.preferred_inline_size + sizes.preferred_inline_size
376     }
377 
378     /// Updates the computation so that the minimum is the maximum of the current minimum and the
379     /// given minimum and the preferred is the maximum of the current preferred and the given
380     /// preferred. This can be useful when laying out fragments in the block direction (but note
381     /// that it does not take floats into account, so `BlockFlow` does not use it).
382     ///
383     /// This is used when contributing the intrinsic sizes for individual fragments.
union_block(&mut self, sizes: &IntrinsicISizes)384     pub fn union_block(&mut self, sizes: &IntrinsicISizes) {
385         self.content_intrinsic_sizes.minimum_inline_size =
386             max(self.content_intrinsic_sizes.minimum_inline_size, sizes.minimum_inline_size);
387         self.content_intrinsic_sizes.preferred_inline_size =
388             max(self.content_intrinsic_sizes.preferred_inline_size, sizes.preferred_inline_size)
389     }
390 }
391 
392 /// Useful helper data type when computing values for blocks and positioned elements.
393 #[derive(Clone, Copy, Debug, PartialEq)]
394 pub enum MaybeAuto {
395     Auto,
396     Specified(Au),
397 }
398 
399 impl MaybeAuto {
400     #[inline]
from_style(length: LengthOrPercentageOrAuto, containing_length: Au) -> MaybeAuto401     pub fn from_style(length: LengthOrPercentageOrAuto, containing_length: Au)
402                       -> MaybeAuto {
403         match length {
404             LengthOrPercentageOrAuto::Auto => MaybeAuto::Auto,
405             LengthOrPercentageOrAuto::Percentage(percent) => {
406                 MaybeAuto::Specified(containing_length.scale_by(percent.0))
407             }
408             LengthOrPercentageOrAuto::Calc(calc) => {
409                 MaybeAuto::from_option(calc.to_used_value(Some(containing_length)))
410             }
411             LengthOrPercentageOrAuto::Length(length) => MaybeAuto::Specified(Au::from(length))
412         }
413     }
414 
415     #[inline]
from_option(au: Option<Au>) -> MaybeAuto416     pub fn from_option(au: Option<Au>) -> MaybeAuto {
417         match au {
418             Some(l) => MaybeAuto::Specified(l),
419             _ => MaybeAuto::Auto,
420         }
421     }
422 
423     #[inline]
to_option(&self) -> Option<Au>424     pub fn to_option(&self) -> Option<Au> {
425         match *self {
426             MaybeAuto::Specified(value) => Some(value),
427             MaybeAuto::Auto => None,
428         }
429     }
430 
431     #[inline]
specified_or_default(&self, default: Au) -> Au432     pub fn specified_or_default(&self, default: Au) -> Au {
433         match *self {
434             MaybeAuto::Auto => default,
435             MaybeAuto::Specified(value) => value,
436         }
437     }
438 
439     #[inline]
specified_or_zero(&self) -> Au440     pub fn specified_or_zero(&self) -> Au {
441         self.specified_or_default(Au::new(0))
442     }
443 
444     #[inline]
map<F>(&self, mapper: F) -> MaybeAuto where F: FnOnce(Au) -> Au445     pub fn map<F>(&self, mapper: F) -> MaybeAuto where F: FnOnce(Au) -> Au {
446         match *self {
447             MaybeAuto::Auto => MaybeAuto::Auto,
448             MaybeAuto::Specified(value) => MaybeAuto::Specified(mapper(value)),
449         }
450     }
451 }
452 
453 /// Receive an optional container size and return used value for width or height.
454 ///
455 /// `style_length`: content size as given in the CSS.
style_length(style_length: LengthOrPercentageOrAuto, container_size: Option<Au>) -> MaybeAuto456 pub fn style_length(style_length: LengthOrPercentageOrAuto,
457                     container_size: Option<Au>) -> MaybeAuto {
458     match container_size {
459         Some(length) => MaybeAuto::from_style(style_length, length),
460         None => if let LengthOrPercentageOrAuto::Length(length) = style_length {
461             MaybeAuto::Specified(Au::from(length))
462         } else {
463             MaybeAuto::Auto
464         }
465     }
466 }
467 
468 /// Computes a border radius size against the containing size.
469 ///
470 /// Note that percentages in `border-radius` are resolved against the relevant
471 /// box dimension instead of only against the width per [1]:
472 ///
473 /// > Percentages: Refer to corresponding dimension of the border box.
474 ///
475 /// [1]: https://drafts.csswg.org/css-backgrounds-3/#border-radius
specified_border_radius( radius: BorderCornerRadius, containing_size: Size2D<Au>) -> Size2D<Au>476 pub fn specified_border_radius(
477     radius: BorderCornerRadius,
478     containing_size: Size2D<Au>)
479     -> Size2D<Au>
480 {
481     let w = radius.0.width().to_used_value(containing_size.width);
482     let h = radius.0.height().to_used_value(containing_size.height);
483     Size2D::new(w, h)
484 }
485 
486 #[inline]
padding_from_style(style: &ComputedValues, containing_block_inline_size: Au, writing_mode: WritingMode) -> LogicalMargin<Au>487 pub fn padding_from_style(style: &ComputedValues,
488                           containing_block_inline_size: Au,
489                           writing_mode: WritingMode)
490                           -> LogicalMargin<Au> {
491     let padding_style = style.get_padding();
492     LogicalMargin::from_physical(writing_mode, SideOffsets2D::new(
493         padding_style.padding_top.to_used_value(containing_block_inline_size),
494         padding_style.padding_right.to_used_value(containing_block_inline_size),
495         padding_style.padding_bottom.to_used_value(containing_block_inline_size),
496         padding_style.padding_left.to_used_value(containing_block_inline_size)))
497 }
498 
499 /// Returns the explicitly-specified margin lengths from the given style. Percentage and auto
500 /// margins are returned as zero.
501 ///
502 /// This is used when calculating intrinsic inline sizes.
503 #[inline]
specified_margin_from_style(style: &ComputedValues, writing_mode: WritingMode) -> LogicalMargin<Au>504 pub fn specified_margin_from_style(style: &ComputedValues,
505                                    writing_mode: WritingMode) -> LogicalMargin<Au> {
506     let margin_style = style.get_margin();
507     LogicalMargin::from_physical(writing_mode, SideOffsets2D::new(
508         MaybeAuto::from_style(margin_style.margin_top, Au(0)).specified_or_zero(),
509         MaybeAuto::from_style(margin_style.margin_right, Au(0)).specified_or_zero(),
510         MaybeAuto::from_style(margin_style.margin_bottom, Au(0)).specified_or_zero(),
511         MaybeAuto::from_style(margin_style.margin_left, Au(0)).specified_or_zero()))
512 }
513 
514 /// A min-size and max-size constraint. The constructor has a optional `border`
515 /// parameter, and when it is present the constraint will be subtracted. This is
516 /// used to adjust the constraint for `box-sizing: border-box`, and when you do so
517 /// make sure the size you want to clamp is intended to be used for content box.
518 #[derive(Clone, Copy, Debug, Serialize)]
519 pub struct SizeConstraint {
520     min_size: Au,
521     max_size: Option<Au>,
522 }
523 
524 impl SizeConstraint {
525     /// Create a `SizeConstraint` for an axis.
new(container_size: Option<Au>, min_size: LengthOrPercentage, max_size: LengthOrPercentageOrNone, border: Option<Au>) -> SizeConstraint526     pub fn new(container_size: Option<Au>,
527                min_size: LengthOrPercentage,
528                max_size: LengthOrPercentageOrNone,
529                border: Option<Au>) -> SizeConstraint {
530         let mut min_size = match container_size {
531             Some(container_size) => min_size.to_used_value(container_size),
532             None => if let LengthOrPercentage::Length(length) = min_size {
533                 Au::from(length)
534             } else {
535                 Au(0)
536             }
537         };
538 
539         let mut max_size = match container_size {
540             Some(container_size) => max_size.to_used_value(container_size),
541             None => if let LengthOrPercentageOrNone::Length(length) = max_size {
542                 Some(Au::from(length))
543             } else {
544                 None
545             }
546         };
547         // Make sure max size is not smaller than min size.
548         max_size = max_size.map(|x| max(x, min_size));
549 
550         if let Some(border) = border {
551             min_size = max(min_size - border, Au(0));
552             max_size = max_size.map(|x| max(x - border, Au(0)));
553         }
554 
555         SizeConstraint {
556             min_size: min_size,
557             max_size: max_size
558         }
559     }
560 
561     /// Clamp the given size by the given min size and max size constraint.
clamp(&self, other: Au) -> Au562     pub fn clamp(&self, other: Au) -> Au {
563         if other < self.min_size {
564             self.min_size
565         } else {
566             match self.max_size {
567                 Some(max_size) if max_size < other => max_size,
568                 _ => other
569             }
570         }
571     }
572 }
573