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(¢er) { 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(¢er) { 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