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 https://mozilla.org/MPL/2.0/. */
4 
5 //! [Calc expressions][calc].
6 //!
7 //! [calc]: https://drafts.csswg.org/css-values/#calc-notation
8 
9 use crate::Zero;
10 use smallvec::SmallVec;
11 use std::fmt::{self, Write};
12 use std::ops::Add;
13 use std::{cmp, mem};
14 use style_traits::{CssWriter, ToCss};
15 
16 /// Whether we're a `min` or `max` function.
17 #[derive(
18     Clone,
19     Copy,
20     Debug,
21     Deserialize,
22     MallocSizeOf,
23     PartialEq,
24     Serialize,
25     ToAnimatedZero,
26     ToResolvedValue,
27     ToShmem,
28 )]
29 #[repr(u8)]
30 pub enum MinMaxOp {
31     /// `min()`
32     Min,
33     /// `max()`
34     Max,
35 }
36 
37 /// This determines the order in which we serialize members of a calc() sum.
38 ///
39 /// See https://drafts.csswg.org/css-values-4/#sort-a-calculations-children
40 #[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
41 #[allow(missing_docs)]
42 pub enum SortKey {
43     Number,
44     Percentage,
45     Ch,
46     Deg,
47     Em,
48     Ex,
49     Px,
50     Rem,
51     Sec,
52     Vh,
53     Vmax,
54     Vmin,
55     Vw,
56     Other,
57 }
58 
59 /// A generic node in a calc expression.
60 ///
61 /// FIXME: This would be much more elegant if we used `Self` in the types below,
62 /// but we can't because of https://github.com/serde-rs/serde/issues/1565.
63 ///
64 /// FIXME: The following annotations are to workaround an LLVM inlining bug, see
65 /// bug 1631929.
66 ///
67 /// cbindgen:destructor-attributes=MOZ_NEVER_INLINE
68 /// cbindgen:copy-constructor-attributes=MOZ_NEVER_INLINE
69 /// cbindgen:eq-attributes=MOZ_NEVER_INLINE
70 #[repr(u8)]
71 #[derive(
72     Clone,
73     Debug,
74     Deserialize,
75     MallocSizeOf,
76     PartialEq,
77     Serialize,
78     ToAnimatedZero,
79     ToResolvedValue,
80     ToShmem,
81 )]
82 pub enum GenericCalcNode<L> {
83     /// A leaf node.
84     Leaf(L),
85     /// A sum node, representing `a + b + c` where a, b, and c are the
86     /// arguments.
87     Sum(crate::OwnedSlice<GenericCalcNode<L>>),
88     /// A `min` or `max` function.
89     MinMax(crate::OwnedSlice<GenericCalcNode<L>>, MinMaxOp),
90     /// A `clamp()` function.
91     Clamp {
92         /// The minimum value.
93         min: Box<GenericCalcNode<L>>,
94         /// The central value.
95         center: Box<GenericCalcNode<L>>,
96         /// The maximum value.
97         max: Box<GenericCalcNode<L>>,
98     },
99 }
100 
101 pub use self::GenericCalcNode as CalcNode;
102 
103 /// A trait that represents all the stuff a valid leaf of a calc expression.
104 pub trait CalcNodeLeaf: Clone + Sized + PartialOrd + PartialEq + ToCss {
105     /// Whether this value is known-negative.
is_negative(&self) -> bool106     fn is_negative(&self) -> bool;
107 
108     /// Tries to merge one sum to another, that is, perform `x` + `y`.
try_sum_in_place(&mut self, other: &Self) -> Result<(), ()>109     fn try_sum_in_place(&mut self, other: &Self) -> Result<(), ()>;
110 
111     /// Multiplies the leaf by a given scalar number.
mul_by(&mut self, scalar: f32)112     fn mul_by(&mut self, scalar: f32);
113 
114     /// Negates the leaf.
negate(&mut self)115     fn negate(&mut self) {
116         self.mul_by(-1.);
117     }
118 
119     /// Canonicalizes the expression if necessary.
simplify(&mut self)120     fn simplify(&mut self);
121 
122     /// Returns the sort key for simplification.
sort_key(&self) -> SortKey123     fn sort_key(&self) -> SortKey;
124 }
125 
126 impl<L: CalcNodeLeaf> CalcNode<L> {
127     /// Negates the node.
negate(&mut self)128     pub fn negate(&mut self) {
129         self.mul_by(-1.);
130     }
131 
sort_key(&self) -> SortKey132     fn sort_key(&self) -> SortKey {
133         match *self {
134             Self::Leaf(ref l) => l.sort_key(),
135             _ => SortKey::Other,
136         }
137     }
138 
139     /// Tries to merge one sum to another, that is, perform `x` + `y`.
try_sum_in_place(&mut self, other: &Self) -> Result<(), ()>140     fn try_sum_in_place(&mut self, other: &Self) -> Result<(), ()> {
141         match (self, other) {
142             (&mut CalcNode::Leaf(ref mut one), &CalcNode::Leaf(ref other)) => {
143                 one.try_sum_in_place(other)
144             },
145             _ => Err(()),
146         }
147     }
148 
149     /// Convert this `CalcNode` into a `CalcNode` with a different leaf kind.
map_leaves<O, F>(&self, mut map: F) -> CalcNode<O> where O: CalcNodeLeaf, F: FnMut(&L) -> O,150     pub fn map_leaves<O, F>(&self, mut map: F) -> CalcNode<O>
151     where
152         O: CalcNodeLeaf,
153         F: FnMut(&L) -> O,
154     {
155         self.map_leaves_internal(&mut map)
156     }
157 
map_leaves_internal<O, F>(&self, map: &mut F) -> CalcNode<O> where O: CalcNodeLeaf, F: FnMut(&L) -> O,158     fn map_leaves_internal<O, F>(&self, map: &mut F) -> CalcNode<O>
159     where
160         O: CalcNodeLeaf,
161         F: FnMut(&L) -> O,
162     {
163         fn map_children<L, O, F>(
164             children: &[CalcNode<L>],
165             map: &mut F,
166         ) -> crate::OwnedSlice<CalcNode<O>>
167         where
168             L: CalcNodeLeaf,
169             O: CalcNodeLeaf,
170             F: FnMut(&L) -> O,
171         {
172             children
173                 .iter()
174                 .map(|c| c.map_leaves_internal(map))
175                 .collect()
176         }
177 
178         match *self {
179             Self::Leaf(ref l) => CalcNode::Leaf(map(l)),
180             Self::Sum(ref c) => CalcNode::Sum(map_children(c, map)),
181             Self::MinMax(ref c, op) => CalcNode::MinMax(map_children(c, map), op),
182             Self::Clamp {
183                 ref min,
184                 ref center,
185                 ref max,
186             } => {
187                 let min = Box::new(min.map_leaves_internal(map));
188                 let center = Box::new(center.map_leaves_internal(map));
189                 let max = Box::new(max.map_leaves_internal(map));
190                 CalcNode::Clamp { min, center, max }
191             },
192         }
193     }
194 
195     /// Resolves the expression returning a value of `O`, given a function to
196     /// turn a leaf into the relevant value.
resolve<O>( &self, mut leaf_to_output_fn: impl FnMut(&L) -> Result<O, ()>, ) -> Result<O, ()> where O: PartialOrd + PartialEq + Add<Output = O> + Zero,197     pub fn resolve<O>(
198         &self,
199         mut leaf_to_output_fn: impl FnMut(&L) -> Result<O, ()>,
200     ) -> Result<O, ()>
201     where
202         O: PartialOrd + PartialEq + Add<Output = O> + Zero,
203     {
204         self.resolve_internal(&mut leaf_to_output_fn)
205     }
206 
resolve_internal<O, F>(&self, leaf_to_output_fn: &mut F) -> Result<O, ()> where O: PartialOrd + PartialEq + Add<Output = O> + Zero, F: FnMut(&L) -> Result<O, ()>,207     fn resolve_internal<O, F>(&self, leaf_to_output_fn: &mut F) -> Result<O, ()>
208     where
209         O: PartialOrd + PartialEq + Add<Output = O> + Zero,
210         F: FnMut(&L) -> Result<O, ()>,
211     {
212         Ok(match *self {
213             Self::Leaf(ref l) => return leaf_to_output_fn(l),
214             Self::Sum(ref c) => {
215                 let mut result = Zero::zero();
216                 for child in &**c {
217                     result = result + child.resolve_internal(leaf_to_output_fn)?;
218                 }
219                 result
220             },
221             Self::MinMax(ref nodes, op) => {
222                 let mut result = nodes[0].resolve_internal(leaf_to_output_fn)?;
223                 for node in nodes.iter().skip(1) {
224                     let candidate = node.resolve_internal(leaf_to_output_fn)?;
225                     let candidate_wins = match op {
226                         MinMaxOp::Min => candidate < result,
227                         MinMaxOp::Max => candidate > result,
228                     };
229                     if candidate_wins {
230                         result = candidate;
231                     }
232                 }
233                 result
234             },
235             Self::Clamp {
236                 ref min,
237                 ref center,
238                 ref max,
239             } => {
240                 let min = min.resolve_internal(leaf_to_output_fn)?;
241                 let center = center.resolve_internal(leaf_to_output_fn)?;
242                 let max = max.resolve_internal(leaf_to_output_fn)?;
243 
244                 let mut result = center;
245                 if result > max {
246                     result = max;
247                 }
248                 if result < min {
249                     result = min
250                 }
251                 result
252             },
253         })
254     }
255 
is_negative_leaf(&self) -> bool256     fn is_negative_leaf(&self) -> bool {
257         match *self {
258             Self::Leaf(ref l) => l.is_negative(),
259             _ => false,
260         }
261     }
262 
263     /// Multiplies the node by a scalar.
mul_by(&mut self, scalar: f32)264     pub fn mul_by(&mut self, scalar: f32) {
265         match *self {
266             Self::Leaf(ref mut l) => l.mul_by(scalar),
267             // Multiplication is distributive across this.
268             Self::Sum(ref mut children) => {
269                 for node in &mut **children {
270                     node.mul_by(scalar);
271                 }
272             },
273             // This one is a bit trickier.
274             Self::MinMax(ref mut children, ref mut op) => {
275                 for node in &mut **children {
276                     node.mul_by(scalar);
277                 }
278 
279                 // For negatives we need to invert the operation.
280                 if scalar < 0. {
281                     *op = match *op {
282                         MinMaxOp::Min => MinMaxOp::Max,
283                         MinMaxOp::Max => MinMaxOp::Min,
284                     }
285                 }
286             },
287             // This one is slightly tricky too.
288             Self::Clamp {
289                 ref mut min,
290                 ref mut center,
291                 ref mut max,
292             } => {
293                 min.mul_by(scalar);
294                 center.mul_by(scalar);
295                 max.mul_by(scalar);
296                 // For negatives we need to swap min / max.
297                 if scalar < 0. {
298                     mem::swap(min, max);
299                 }
300             },
301         }
302     }
303 
304     /// Visits all the nodes in this calculation tree recursively, starting by
305     /// the leaves and bubbling all the way up.
306     ///
307     /// This is useful for simplification, but can also be used for validation
308     /// and such.
visit_depth_first(&mut self, mut f: impl FnMut(&mut Self))309     pub fn visit_depth_first(&mut self, mut f: impl FnMut(&mut Self)) {
310         self.visit_depth_first_internal(&mut f);
311     }
312 
visit_depth_first_internal(&mut self, f: &mut impl FnMut(&mut Self))313     fn visit_depth_first_internal(&mut self, f: &mut impl FnMut(&mut Self)) {
314         match *self {
315             Self::Clamp {
316                 ref mut min,
317                 ref mut center,
318                 ref mut max,
319             } => {
320                 min.visit_depth_first_internal(f);
321                 center.visit_depth_first_internal(f);
322                 max.visit_depth_first_internal(f);
323             },
324             Self::Sum(ref mut children) | Self::MinMax(ref mut children, _) => {
325                 for child in &mut **children {
326                     child.visit_depth_first_internal(f);
327                 }
328             },
329             Self::Leaf(..) => {},
330         }
331         f(self);
332     }
333 
334     /// Simplifies and sorts the calculation of a given node. All the nodes
335     /// below it should be simplified already, this only takes care of
336     /// simplifying directly nested nodes. So, probably should always be used in
337     /// combination with `visit_depth_first()`.
338     ///
339     /// This is only needed if it's going to be preserved after parsing (so, for
340     /// `<length-percentage>`). Otherwise we can just evaluate it using
341     /// `resolve()`, and we'll come up with a simplified value anyways.
simplify_and_sort_direct_children(&mut self)342     pub fn simplify_and_sort_direct_children(&mut self) {
343         macro_rules! replace_self_with {
344             ($slot:expr) => {{
345                 let dummy = Self::MinMax(Default::default(), MinMaxOp::Max);
346                 let result = mem::replace($slot, dummy);
347                 *self = result;
348             }};
349         }
350         match *self {
351             Self::Clamp {
352                 ref mut min,
353                 ref mut center,
354                 ref mut max,
355             } => {
356                 // NOTE: clamp() is max(min, min(center, max))
357                 let min_cmp_center = match min.partial_cmp(&center) {
358                     Some(o) => o,
359                     None => return,
360                 };
361 
362                 // So if we can prove that min is more than center, then we won,
363                 // as that's what we should always return.
364                 if matches!(min_cmp_center, cmp::Ordering::Greater) {
365                     return replace_self_with!(&mut **min);
366                 }
367 
368                 // Otherwise try with max.
369                 let max_cmp_center = match max.partial_cmp(&center) {
370                     Some(o) => o,
371                     None => return,
372                 };
373 
374                 if matches!(max_cmp_center, cmp::Ordering::Less) {
375                     // max is less than center, so we need to return effectively
376                     // `max(min, max)`.
377                     let max_cmp_min = match max.partial_cmp(&min) {
378                         Some(o) => o,
379                         None => {
380                             debug_assert!(
381                                 false,
382                                 "We compared center with min and max, how are \
383                                  min / max not comparable with each other?"
384                             );
385                             return;
386                         },
387                     };
388 
389                     if matches!(max_cmp_min, cmp::Ordering::Less) {
390                         return replace_self_with!(&mut **min);
391                     }
392 
393                     return replace_self_with!(&mut **max);
394                 }
395 
396                 // Otherwise we're the center node.
397                 return replace_self_with!(&mut **center);
398             },
399             Self::MinMax(ref mut children, op) => {
400                 let winning_order = match op {
401                     MinMaxOp::Min => cmp::Ordering::Less,
402                     MinMaxOp::Max => cmp::Ordering::Greater,
403                 };
404 
405                 let mut result = 0;
406                 for i in 1..children.len() {
407                     let o = match children[i].partial_cmp(&children[result]) {
408                         // We can't compare all the children, so we can't
409                         // know which one will actually win. Bail out and
410                         // keep ourselves as a min / max function.
411                         //
412                         // TODO: Maybe we could simplify compatible children,
413                         // see https://github.com/w3c/csswg-drafts/issues/4756
414                         None => return,
415                         Some(o) => o,
416                     };
417 
418                     if o == winning_order {
419                         result = i;
420                     }
421                 }
422 
423                 replace_self_with!(&mut children[result]);
424             },
425             Self::Sum(ref mut children_slot) => {
426                 let mut sums_to_merge = SmallVec::<[_; 3]>::new();
427                 let mut extra_kids = 0;
428                 for (i, child) in children_slot.iter().enumerate() {
429                     if let Self::Sum(ref children) = *child {
430                         extra_kids += children.len();
431                         sums_to_merge.push(i);
432                     }
433                 }
434 
435                 // If we only have one kid, we've already simplified it, and it
436                 // doesn't really matter whether it's a sum already or not, so
437                 // lift it up and continue.
438                 if children_slot.len() == 1 {
439                     return replace_self_with!(&mut children_slot[0]);
440                 }
441 
442                 let mut children = mem::replace(children_slot, Default::default()).into_vec();
443 
444                 if !sums_to_merge.is_empty() {
445                     children.reserve(extra_kids - sums_to_merge.len());
446                     // Merge all our nested sums, in reverse order so that the
447                     // list indices are not invalidated.
448                     for i in sums_to_merge.drain(..).rev() {
449                         let kid_children = match children.swap_remove(i) {
450                             Self::Sum(c) => c,
451                             _ => unreachable!(),
452                         };
453 
454                         // This would be nicer with
455                         // https://github.com/rust-lang/rust/issues/59878 fixed.
456                         children.extend(kid_children.into_vec());
457                     }
458                 }
459 
460                 debug_assert!(children.len() >= 2, "Should still have multiple kids!");
461 
462                 // Sort by spec order.
463                 children.sort_unstable_by_key(|c| c.sort_key());
464 
465                 // NOTE: if the function returns true, by the docs of dedup_by,
466                 // a is removed.
467                 children.dedup_by(|a, b| b.try_sum_in_place(a).is_ok());
468 
469                 if children.len() == 1 {
470                     // If only one children remains, lift it up, and carry on.
471                     replace_self_with!(&mut children[0]);
472                 } else {
473                     // Else put our simplified children back.
474                     *children_slot = children.into_boxed_slice().into();
475                 }
476             },
477             Self::Leaf(ref mut l) => {
478                 l.simplify();
479             },
480         }
481     }
482 
483     /// Simplifies and sorts the kids in the whole calculation subtree.
simplify_and_sort(&mut self)484     pub fn simplify_and_sort(&mut self) {
485         self.visit_depth_first(|node| node.simplify_and_sort_direct_children())
486     }
487 
to_css_impl<W>(&self, dest: &mut CssWriter<W>, is_outermost: bool) -> fmt::Result where W: Write,488     fn to_css_impl<W>(&self, dest: &mut CssWriter<W>, is_outermost: bool) -> fmt::Result
489     where
490         W: Write,
491     {
492         let write_closing_paren = match *self {
493             Self::MinMax(_, op) => {
494                 dest.write_str(match op {
495                     MinMaxOp::Max => "max(",
496                     MinMaxOp::Min => "min(",
497                 })?;
498                 true
499             },
500             Self::Clamp { .. } => {
501                 dest.write_str("clamp(")?;
502                 true
503             },
504             _ => {
505                 if is_outermost {
506                     dest.write_str("calc(")?;
507                 }
508                 is_outermost
509             },
510         };
511 
512         match *self {
513             Self::MinMax(ref children, _) => {
514                 let mut first = true;
515                 for child in &**children {
516                     if !first {
517                         dest.write_str(", ")?;
518                     }
519                     first = false;
520                     child.to_css_impl(dest, false)?;
521                 }
522             },
523             Self::Sum(ref children) => {
524                 let mut first = true;
525                 for child in &**children {
526                     if !first {
527                         if child.is_negative_leaf() {
528                             dest.write_str(" - ")?;
529                             let mut c = child.clone();
530                             c.negate();
531                             c.to_css_impl(dest, false)?;
532                         } else {
533                             dest.write_str(" + ")?;
534                             child.to_css_impl(dest, false)?;
535                         }
536                     } else {
537                         first = false;
538                         child.to_css_impl(dest, false)?;
539                     }
540                 }
541             },
542             Self::Clamp {
543                 ref min,
544                 ref center,
545                 ref max,
546             } => {
547                 min.to_css_impl(dest, false)?;
548                 dest.write_str(", ")?;
549                 center.to_css_impl(dest, false)?;
550                 dest.write_str(", ")?;
551                 max.to_css_impl(dest, false)?;
552             },
553             Self::Leaf(ref l) => l.to_css(dest)?,
554         }
555 
556         if write_closing_paren {
557             dest.write_str(")")?;
558         }
559         Ok(())
560     }
561 }
562 
563 impl<L: CalcNodeLeaf> PartialOrd for CalcNode<L> {
partial_cmp(&self, other: &Self) -> Option<cmp::Ordering>564     fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
565         match (self, other) {
566             (&CalcNode::Leaf(ref one), &CalcNode::Leaf(ref other)) => one.partial_cmp(other),
567             _ => None,
568         }
569     }
570 }
571 
572 impl<L: CalcNodeLeaf> ToCss for CalcNode<L> {
573     /// <https://drafts.csswg.org/css-values/#calc-serialize>
to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: Write,574     fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
575     where
576         W: Write,
577     {
578         self.to_css_impl(dest, /* is_outermost = */ true)
579     }
580 }
581