aboutsummaryrefslogtreecommitdiffstats
path: root/components/layout_2020/sizing.rs
diff options
context:
space:
mode:
Diffstat (limited to 'components/layout_2020/sizing.rs')
-rw-r--r--components/layout_2020/sizing.rs152
1 files changed, 152 insertions, 0 deletions
diff --git a/components/layout_2020/sizing.rs b/components/layout_2020/sizing.rs
new file mode 100644
index 00000000000..75c13b1dcb5
--- /dev/null
+++ b/components/layout_2020/sizing.rs
@@ -0,0 +1,152 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
+
+//! https://drafts.csswg.org/css-sizing/
+
+use crate::style_ext::ComputedValuesExt;
+use style::properties::ComputedValues;
+use style::values::computed::{Length, LengthPercentage, Percentage};
+use style::Zero;
+
+/// Which min/max-content values should be computed during box construction
+#[derive(Clone, Copy, Debug)]
+pub(crate) enum ContentSizesRequest {
+ Inline,
+ None,
+}
+
+impl ContentSizesRequest {
+ pub fn inline_if(condition: bool) -> Self {
+ if condition {
+ Self::Inline
+ } else {
+ Self::None
+ }
+ }
+
+ pub fn requests_inline(self) -> bool {
+ match self {
+ Self::Inline => true,
+ Self::None => false,
+ }
+ }
+
+ pub fn if_requests_inline<T>(self, f: impl FnOnce() -> T) -> Option<T> {
+ match self {
+ Self::Inline => Some(f()),
+ Self::None => None,
+ }
+ }
+
+ pub fn compute(self, compute_inline: impl FnOnce() -> ContentSizes) -> BoxContentSizes {
+ match self {
+ Self::Inline => BoxContentSizes::Inline(compute_inline()),
+ Self::None => BoxContentSizes::NoneWereRequested,
+ }
+ }
+}
+
+#[derive(Clone, Debug)]
+pub(crate) struct ContentSizes {
+ pub min_content: Length,
+ pub max_content: Length,
+}
+
+/// https://drafts.csswg.org/css-sizing/#intrinsic-sizes
+impl ContentSizes {
+ pub fn zero() -> Self {
+ Self {
+ min_content: Length::zero(),
+ max_content: Length::zero(),
+ }
+ }
+
+ pub fn max_assign(&mut self, other: &Self) {
+ self.min_content.max_assign(other.min_content);
+ self.max_content.max_assign(other.max_content);
+ }
+
+ /// Relevant to outer intrinsic inline sizes, for percentages from padding and margin.
+ pub fn adjust_for_pbm_percentages(&mut self, percentages: Percentage) {
+ // " Note that this may yield an infinite result, but undefined results
+ // (zero divided by zero) must be treated as zero. "
+ if self.max_content.px() == 0. {
+ // Avoid a potential `NaN`.
+ // Zero is already the result we want regardless of `denominator`.
+ } else {
+ let denominator = (1. - percentages.0).max(0.);
+ self.max_content = Length::new(self.max_content.px() / denominator);
+ }
+ }
+}
+
+/// Optional min/max-content for storage in the box tree
+#[derive(Debug)]
+pub(crate) enum BoxContentSizes {
+ NoneWereRequested, // … during box construction
+ Inline(ContentSizes),
+}
+
+impl BoxContentSizes {
+ fn expect_inline(&self) -> &ContentSizes {
+ match self {
+ Self::NoneWereRequested => panic!("Accessing content size that was not requested"),
+ Self::Inline(s) => s,
+ }
+ }
+
+ /// https://dbaron.org/css/intrinsic/#outer-intrinsic
+ pub fn outer_inline(&self, style: &ComputedValues) -> ContentSizes {
+ let (mut outer, percentages) = self.outer_inline_and_percentages(style);
+ outer.adjust_for_pbm_percentages(percentages);
+ outer
+ }
+
+ pub(crate) fn outer_inline_and_percentages(
+ &self,
+ style: &ComputedValues,
+ ) -> (ContentSizes, Percentage) {
+ // FIXME: account for 'min-width', 'max-width', 'box-sizing'
+
+ let inline_size = style.box_size().inline;
+ // Percentages for 'width' are treated as 'auto'
+ let inline_size = inline_size.map(|lp| lp.as_length());
+ // The (inner) min/max-content are only used for 'auto'
+ let mut outer = match inline_size.non_auto().flatten() {
+ None => self.expect_inline().clone(),
+ Some(length) => ContentSizes {
+ min_content: length,
+ max_content: length,
+ },
+ };
+
+ let mut pbm_lengths = Length::zero();
+ let mut pbm_percentages = Percentage::zero();
+ let padding = style.padding();
+ let border = style.border_width();
+ let margin = style.margin();
+ pbm_lengths += border.inline_sum();
+ let mut add = |x: LengthPercentage| {
+ pbm_lengths += x.length_component();
+ pbm_percentages += x.percentage_component();
+ };
+ add(padding.inline_start);
+ add(padding.inline_end);
+ margin.inline_start.non_auto().map(&mut add);
+ margin.inline_end.non_auto().map(&mut add);
+
+ outer.min_content += pbm_lengths;
+ outer.max_content += pbm_lengths;
+
+ (outer, pbm_percentages)
+ }
+
+ /// https://drafts.csswg.org/css2/visudet.html#shrink-to-fit-float
+ pub(crate) fn shrink_to_fit(&self, available_size: Length) -> Length {
+ let inline = self.expect_inline();
+ available_size
+ .max(inline.min_content)
+ .min(inline.max_content)
+ }
+}