mirror of
https://github.com/servo/servo.git
synced 2025-08-06 06:00:15 +01:00
style: Implement CSS mod() and rem() functions
Differential Revision: https://phabricator.services.mozilla.com/D163166
This commit is contained in:
parent
653b37f80a
commit
e55c03c8ff
2 changed files with 152 additions and 1 deletions
|
@ -34,6 +34,27 @@ pub enum MinMaxOp {
|
||||||
Max,
|
Max,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Whether we're a `mod` or `rem` function.
|
||||||
|
#[derive(
|
||||||
|
Clone,
|
||||||
|
Copy,
|
||||||
|
Debug,
|
||||||
|
Deserialize,
|
||||||
|
MallocSizeOf,
|
||||||
|
PartialEq,
|
||||||
|
Serialize,
|
||||||
|
ToAnimatedZero,
|
||||||
|
ToResolvedValue,
|
||||||
|
ToShmem,
|
||||||
|
)]
|
||||||
|
#[repr(u8)]
|
||||||
|
pub enum ModRemOp {
|
||||||
|
/// `mod()`
|
||||||
|
Mod,
|
||||||
|
/// `rem()`
|
||||||
|
Rem,
|
||||||
|
}
|
||||||
|
|
||||||
/// The strategy used in `round()`
|
/// The strategy used in `round()`
|
||||||
#[derive(
|
#[derive(
|
||||||
Clone,
|
Clone,
|
||||||
|
@ -162,6 +183,15 @@ pub enum GenericCalcNode<L> {
|
||||||
/// The step value.
|
/// The step value.
|
||||||
step: Box<GenericCalcNode<L>>,
|
step: Box<GenericCalcNode<L>>,
|
||||||
},
|
},
|
||||||
|
/// A `mod()` or `rem()` function.
|
||||||
|
ModRem {
|
||||||
|
/// The dividend calculation.
|
||||||
|
dividend: Box<GenericCalcNode<L>>,
|
||||||
|
/// The divisor calculation.
|
||||||
|
divisor: Box<GenericCalcNode<L>>,
|
||||||
|
/// Is the function mod or rem?
|
||||||
|
op: ModRemOp,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
pub use self::GenericCalcNode as CalcNode;
|
pub use self::GenericCalcNode as CalcNode;
|
||||||
|
@ -314,6 +344,19 @@ impl<L: CalcNodeLeaf> CalcNode<L> {
|
||||||
step,
|
step,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
Self::ModRem {
|
||||||
|
ref dividend,
|
||||||
|
ref divisor,
|
||||||
|
op,
|
||||||
|
} => {
|
||||||
|
let dividend = Box::new(dividend.map_leaves_internal(map));
|
||||||
|
let divisor = Box::new(divisor.map_leaves_internal(map));
|
||||||
|
CalcNode::ModRem {
|
||||||
|
dividend,
|
||||||
|
divisor,
|
||||||
|
op,
|
||||||
|
}
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -469,6 +512,29 @@ impl<L: CalcNodeLeaf> CalcNode<L> {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
Self::ModRem {
|
||||||
|
ref dividend,
|
||||||
|
ref divisor,
|
||||||
|
op,
|
||||||
|
} => {
|
||||||
|
let dividend = dividend.resolve_internal(leaf_to_output_fn)?;
|
||||||
|
let divisor = divisor.resolve_internal(leaf_to_output_fn)?;
|
||||||
|
|
||||||
|
// In mod(A, B) only, if B is infinite and A has opposite sign to B
|
||||||
|
// (including an oppositely-signed zero), the result is NaN.
|
||||||
|
// https://drafts.csswg.org/css-values/#round-infinities
|
||||||
|
if matches!(op, ModRemOp::Mod) &&
|
||||||
|
divisor.is_infinite() &&
|
||||||
|
dividend.is_sign_negative() != divisor.is_sign_negative()
|
||||||
|
{
|
||||||
|
return Ok(<O as Float>::nan());
|
||||||
|
}
|
||||||
|
|
||||||
|
match op {
|
||||||
|
ModRemOp::Mod => dividend - divisor * (dividend / divisor).floor(),
|
||||||
|
ModRemOp::Rem => dividend - divisor * (dividend / divisor).trunc(),
|
||||||
|
}
|
||||||
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -539,6 +605,14 @@ impl<L: CalcNodeLeaf> CalcNode<L> {
|
||||||
value.mul_by(scalar);
|
value.mul_by(scalar);
|
||||||
step.mul_by(scalar);
|
step.mul_by(scalar);
|
||||||
},
|
},
|
||||||
|
Self::ModRem {
|
||||||
|
ref mut dividend,
|
||||||
|
ref mut divisor,
|
||||||
|
..
|
||||||
|
} => {
|
||||||
|
dividend.mul_by(scalar);
|
||||||
|
divisor.mul_by(scalar);
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -570,6 +644,14 @@ impl<L: CalcNodeLeaf> CalcNode<L> {
|
||||||
value.visit_depth_first_internal(f);
|
value.visit_depth_first_internal(f);
|
||||||
step.visit_depth_first_internal(f);
|
step.visit_depth_first_internal(f);
|
||||||
},
|
},
|
||||||
|
Self::ModRem {
|
||||||
|
ref mut dividend,
|
||||||
|
ref mut divisor,
|
||||||
|
..
|
||||||
|
} => {
|
||||||
|
dividend.visit_depth_first_internal(f);
|
||||||
|
divisor.visit_depth_first_internal(f);
|
||||||
|
},
|
||||||
Self::Sum(ref mut children) | Self::MinMax(ref mut children, _) => {
|
Self::Sum(ref mut children) | Self::MinMax(ref mut children, _) => {
|
||||||
for child in &mut **children {
|
for child in &mut **children {
|
||||||
child.visit_depth_first_internal(f);
|
child.visit_depth_first_internal(f);
|
||||||
|
@ -772,6 +854,36 @@ impl<L: CalcNodeLeaf> CalcNode<L> {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
Self::ModRem {
|
||||||
|
ref dividend,
|
||||||
|
ref divisor,
|
||||||
|
op,
|
||||||
|
} => {
|
||||||
|
let mut result = dividend.clone();
|
||||||
|
|
||||||
|
// In mod(A, B) only, if B is infinite and A has opposite sign to B
|
||||||
|
// (including an oppositely-signed zero), the result is NaN.
|
||||||
|
// https://drafts.csswg.org/css-values/#round-infinities
|
||||||
|
if matches!(op, ModRemOp::Mod) &&
|
||||||
|
divisor.is_infinite_leaf() &&
|
||||||
|
dividend.is_negative_leaf() != divisor.is_negative_leaf()
|
||||||
|
{
|
||||||
|
result.mul_by(f32::NAN);
|
||||||
|
return replace_self_with!(&mut *result);
|
||||||
|
}
|
||||||
|
|
||||||
|
let result = match op {
|
||||||
|
ModRemOp::Mod => dividend.try_op(divisor, |a, b| a - b * (a / b).floor()),
|
||||||
|
ModRemOp::Rem => dividend.try_op(divisor, |a, b| a - b * (a / b).trunc()),
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut result = match result {
|
||||||
|
Ok(res) => res,
|
||||||
|
Err(..) => return,
|
||||||
|
};
|
||||||
|
|
||||||
|
return replace_self_with!(&mut result);
|
||||||
|
},
|
||||||
Self::MinMax(ref mut children, op) => {
|
Self::MinMax(ref mut children, op) => {
|
||||||
let winning_order = match op {
|
let winning_order = match op {
|
||||||
MinMaxOp::Min => cmp::Ordering::Less,
|
MinMaxOp::Min => cmp::Ordering::Less,
|
||||||
|
@ -887,6 +999,14 @@ impl<L: CalcNodeLeaf> CalcNode<L> {
|
||||||
|
|
||||||
true
|
true
|
||||||
},
|
},
|
||||||
|
Self::ModRem { op, .. } => {
|
||||||
|
dest.write_str(match op {
|
||||||
|
ModRemOp::Mod => "mod(",
|
||||||
|
ModRemOp::Rem => "rem(",
|
||||||
|
})?;
|
||||||
|
|
||||||
|
true
|
||||||
|
},
|
||||||
_ => {
|
_ => {
|
||||||
if is_outermost {
|
if is_outermost {
|
||||||
dest.write_str("calc(")?;
|
dest.write_str("calc(")?;
|
||||||
|
@ -945,6 +1065,15 @@ impl<L: CalcNodeLeaf> CalcNode<L> {
|
||||||
dest.write_str(", ")?;
|
dest.write_str(", ")?;
|
||||||
step.to_css_impl(dest, false)?;
|
step.to_css_impl(dest, false)?;
|
||||||
},
|
},
|
||||||
|
Self::ModRem {
|
||||||
|
ref dividend,
|
||||||
|
ref divisor,
|
||||||
|
..
|
||||||
|
} => {
|
||||||
|
dividend.to_css_impl(dest, false)?;
|
||||||
|
dest.write_str(", ")?;
|
||||||
|
divisor.to_css_impl(dest, false)?;
|
||||||
|
},
|
||||||
Self::Leaf(ref l) => l.to_css(dest)?,
|
Self::Leaf(ref l) => l.to_css(dest)?,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
|
|
||||||
use crate::parser::ParserContext;
|
use crate::parser::ParserContext;
|
||||||
use crate::values::generics::calc as generic;
|
use crate::values::generics::calc as generic;
|
||||||
use crate::values::generics::calc::{MinMaxOp, RoundingStrategy, SortKey};
|
use crate::values::generics::calc::{MinMaxOp, ModRemOp, RoundingStrategy, SortKey};
|
||||||
use crate::values::specified::length::{AbsoluteLength, FontRelativeLength, NoCalcLength};
|
use crate::values::specified::length::{AbsoluteLength, FontRelativeLength, NoCalcLength};
|
||||||
use crate::values::specified::length::{ContainerRelativeLength, ViewportPercentageLength};
|
use crate::values::specified::length::{ContainerRelativeLength, ViewportPercentageLength};
|
||||||
use crate::values::specified::{self, Angle, Time};
|
use crate::values::specified::{self, Angle, Time};
|
||||||
|
@ -54,6 +54,10 @@ pub enum MathFunction {
|
||||||
Clamp,
|
Clamp,
|
||||||
/// `round()`: https://drafts.csswg.org/css-values-4/#funcdef-round
|
/// `round()`: https://drafts.csswg.org/css-values-4/#funcdef-round
|
||||||
Round,
|
Round,
|
||||||
|
/// `mod()`: https://drafts.csswg.org/css-values-4/#funcdef-mod
|
||||||
|
Mod,
|
||||||
|
/// `rem()`: https://drafts.csswg.org/css-values-4/#funcdef-rem
|
||||||
|
Rem,
|
||||||
/// `sin()`: https://drafts.csswg.org/css-values-4/#funcdef-sin
|
/// `sin()`: https://drafts.csswg.org/css-values-4/#funcdef-sin
|
||||||
Sin,
|
Sin,
|
||||||
/// `cos()`: https://drafts.csswg.org/css-values-4/#funcdef-cos
|
/// `cos()`: https://drafts.csswg.org/css-values-4/#funcdef-cos
|
||||||
|
@ -490,6 +494,22 @@ impl CalcNode {
|
||||||
step: Box::new(step),
|
step: Box::new(step),
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
MathFunction::Mod | MathFunction::Rem => {
|
||||||
|
let dividend = Self::parse_argument(context, input, allowed_units)?;
|
||||||
|
input.expect_comma()?;
|
||||||
|
let divisor = Self::parse_argument(context, input, allowed_units)?;
|
||||||
|
|
||||||
|
let op = match function {
|
||||||
|
MathFunction::Mod => ModRemOp::Mod,
|
||||||
|
MathFunction::Rem => ModRemOp::Rem,
|
||||||
|
_ => unreachable!(),
|
||||||
|
};
|
||||||
|
Ok(Self::ModRem {
|
||||||
|
dividend: Box::new(dividend),
|
||||||
|
divisor: Box::new(divisor),
|
||||||
|
op,
|
||||||
|
})
|
||||||
|
},
|
||||||
MathFunction::Min | MathFunction::Max => {
|
MathFunction::Min | MathFunction::Max => {
|
||||||
// TODO(emilio): The common case for parse_comma_separated
|
// TODO(emilio): The common case for parse_comma_separated
|
||||||
// is just one element, but for min / max is two, really...
|
// is just one element, but for min / max is two, really...
|
||||||
|
@ -780,6 +800,8 @@ impl CalcNode {
|
||||||
trig_enabled()
|
trig_enabled()
|
||||||
} else if matches!(function, Round) {
|
} else if matches!(function, Round) {
|
||||||
round_enabled()
|
round_enabled()
|
||||||
|
} else if matches!(function, Mod | Rem) {
|
||||||
|
static_prefs::pref!("layout.css.mod-rem.enabled")
|
||||||
} else {
|
} else {
|
||||||
true
|
true
|
||||||
};
|
};
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue