/* This Source Code Form is subject to the terms of the Mozilla Public *License,v.2.0.IfacopyoftheMPLwasnotdistributedwiththis
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
impl MathFunction { /// Returns an iterator for the enum variants pubfn variants() -> MathFunctionIter { return MathFunction::iter();
}
}
/// A leaf node inside a `Calc` expression's AST. #[derive(Clone, Debug, MallocSizeOf, PartialEq, ToCss, ToShmem)] #[repr(u8)] pubenum Leaf { /// `<length>`
Length(NoCalcLength), /// `<angle>`
Angle(NoCalcAngle), /// `<time>`
Time(NoCalcTime), /// `<resolution>`
Resolution(NoCalcResolution), /// A component of a color.
ColorComponent(ChannelKeyword), /// `<percentage>`
Percentage(NoCalcPercentage), /// `<number>`
Number(NoCalcNumber), /// A tree-counting function.
TreeCountingFunction(TreeCountingFunction),
}
impl ToTyped for Leaf { fn to_typed(&self, dest: &mut ThinVec<TypedValue>) -> Result<(), ()> { // XXX Only supporting Length, Number, Percentage, Angle and Time for // now match *self { Self::Length(ref l) => l.to_typed(dest), Self::Number(n) => n.to_typed(dest), Self::Percentage(p) => p.to_typed(dest), Self::Angle(ref a) => a.to_typed(dest), Self::Time(t) => t.to_typed(dest),
_ => Err(()),
}
}
}
/// A struct to hold a simplified calc expression and associated clamping mode. /// /// In some cases, e.g. DOMMatrix, we support calc(), but reject all the /// relative lengths, and to_computed_pixel_length_without_context() handles /// this case. Therefore, if you want to add a new field, please make sure this /// function work properly. #[derive(Clone, Debug, MallocSizeOf, PartialEq, ToCss, ToShmem, ToTyped)] #[allow(missing_docs)] pubstruct CalcNumeric { #[css(skip)] pub clamping_mode: AllowedNumericType, pub node: CalcNode,
}
impl CalcNumeric { /// Returns a new CalcNumeric with the same expression but the specified clamping mode pubfn with_clamping_mode(&self, clamping_mode: AllowedNumericType) -> Self { Self {
clamping_mode,
node: self.node.clone(),
}
}
/// Returns a new CalcNumeric with the same clamping mode but a different leaf node pubfn with_leaf_node(&self, leaf: Leaf) -> Self { Self {
clamping_mode: self.clamping_mode,
node: CalcNode::Leaf(leaf),
}
}
/// Resolves this calc expression given a computed context, applying clamping. pubfn resolve(
&self,
context: &computed::Context,
leaf_to_f32: impl FnOnce(Result<Leaf, ()>) -> f32,
) -> f32 { let result = self
.node
.resolve_computed(Some(context), |leaf| Ok(leaf.clone())); self.clamping_mode.clamp(leaf_to_f32(result))
}
/// Gets this calc expression as a number pubfn as_number(&self) -> Option<NoCalcNumber> { matchself.node.resolve() {
Ok(Leaf::Number(n)) => Some(n),
_ => None,
}
}
/// Gets this calc expression as a percentage pubfn as_percentage(&self) -> Option<NoCalcPercentage> { matchself.node.resolve() {
Ok(Leaf::Percentage(p)) => Some(p),
_ => None,
}
}
/// Gets this calc expression as a time pubfn as_time(&self) -> Option<NoCalcTime> { matchself.node.resolve() {
Ok(Leaf::Time(t)) => Some(t),
_ => None,
}
}
/// Gets this calc expression as a resolution pubfn as_resolution(&self) -> Option<NoCalcResolution> { matchself.node.resolve() {
Ok(Leaf::Resolution(r)) => Some(r),
_ => None,
}
}
/// Gets this calc expression as an angle pubfn as_angle(&self) -> Option<NoCalcAngle> { matchself.node.resolve() {
Ok(Leaf::Angle(a)) => Some(a),
_ => None,
}
}
}
impl SpecifiedValueInfo for CalcNumeric {}
/// A `calc()` expression that is known to resolve to a `<length-percentage>`. #[derive(Clone, Debug, MallocSizeOf, PartialEq, ToCss, ToShmem, ToTyped)] pubstruct CalcLengthPercentage(pub CalcNumeric);
impl SpecifiedValueInfo for CalcLengthPercentage {}
/// Should parsing anchor-positioning functions in `calc()` be allowed? #[derive(Clone, Copy, PartialEq)] pubenum AllowAnchorPositioningFunctions { /// Don't allow any anchor positioning function.
No, /// Allow `anchor-size()` to be parsed.
AllowAnchorSize, /// Allow `anchor()` and `anchor-size()` to be parsed.
AllowAnchorAndAnchorSize,
}
bitflags! { /// Additional functions within math functions that are permitted to be parsed depending on /// the context of parsing (e.g. Parsing `inset` allows use of `anchor()` within `calc()`). #[derive(Clone, Copy, PartialEq, Eq)] struct AdditionalFunctions: u8 { /// `anchor()` function. const ANCHOR = 1 << 0; /// `anchor-size()` function. const ANCHOR_SIZE = 1 << 1;
}
}
/// What is allowed to be parsed for math functions within in this context? #[derive(Clone, Copy)] pubstruct CalcParseFlags { /// Units allowed to be parsed.
units: CalcUnits, /// Which relative color components, if any, are allowed. pub color_components: ChannelKeyword, /// Additional functions allowed to be parsed in this context.
additional_functions: AdditionalFunctions, /// Whether or not in place operations should be performed. Normally, we aggressive /// simplify via in-place operations, but it is disabled for generating a trace of steps.
in_place_operations: CalcNodeParseInPlaceOperations,
}
impl CalcParseFlags { /// Allow only specified units to be parsed, without any additional functions pubfn new(units: CalcUnits) -> Self { Self {
units,
color_components: ChannelKeyword::empty(),
additional_functions: AdditionalFunctions::empty(),
in_place_operations: CalcNodeParseInPlaceOperations::Yes,
}
}
/// Add new units to the allowed units to be parsed. fn new_including(mutself, units: CalcUnits) -> Self { self.units |= units; self
}
/// Prevents in place operations to be performed pubfn new_without_in_place_operations(mutself) -> Self { self.in_place_operations = CalcNodeParseInPlaceOperations::No; self
}
/// Should given unit be allowed to parse? fn includes(&self, unit: CalcUnits) -> bool { self.units.intersects(unit)
}
}
/// Tries to merge one sum to another, that is, perform `x` + `y`. /// /// Only handles leaf nodes, it's the caller's responsibility to simplify /// them before calling this if needed. fn try_sum_in_place(&mutself, other: &Self) -> Result<(), ()> { useself::Leaf::*;
if std::mem::discriminant(self) != std::mem::discriminant(other) { return Err(());
}
match (self, other) {
(&mut Number(refmut one), &Number(ref other)) => {
*one = NoCalcNumber::new(one.value() + other.value());
},
(&mut Percentage(refmut one), &Percentage(ref other)) => {
*one = NoCalcPercentage::new(one.get() + other.get());
},
(&mut Angle(refmut one), &Angle(ref other)) => {
*one = NoCalcAngle::from_degrees(one.degrees() + other.degrees());
},
(&mut Time(refmut one), &Time(ref other)) => {
*one = NoCalcTime::from_seconds(one.seconds() + other.seconds());
},
(&mut Resolution(refmut one), &Resolution(ref other)) => {
*one = NoCalcResolution::from_dppx(one.dppx() + other.dppx());
},
(&mut Length(refmut one), &Length(ref other)) => {
*one = one.try_op(other, std::ops::Add::add)?;
},
(&mut ColorComponent(_), &ColorComponent(_)) => { // Can not get the sum of color components, because they haven't been resolved yet. return Err(());
},
(&mut TreeCountingFunction(_), &TreeCountingFunction(_)) => { // Can not get the sum of tree counting functions, because they haven't been resolved yet. return Err(());
},
_ => { match *other {
Number(..)
| Percentage(..)
| Angle(..)
| Time(..)
| Resolution(..)
| Length(..)
| ColorComponent(..)
| TreeCountingFunction(..) => {},
} unsafe {
debug_unreachable!();
}
},
}
Ok(())
}
fn try_product_in_place(&mutself, other: &mutSelf) -> bool { ifletSelf::Number(refmut left) = *self { ifletSelf::Number(ref right) = *other { // Both sides are numbers, so we can just modify the left side.
*left = NoCalcNumber::new(left.value() * right.value()); true
} else { // The right side is not a number, so the result should be in the units of the right // side. let left_val = left.value(); if other.map(|v| v * left_val).is_ok() {
std::mem::swap(self, other); true
} else { false
}
}
} elseifletSelf::Number(ref right) = *other { // The left side is not a number, but the right side is, so the result is the left // side unit. let right_val = right.value(); self.map(|v| v * right_val).is_ok()
} else { // Neither side is a number, so a product is not possible. false
}
}
/// Specified `anchor()` function in math functions. pubtype CalcAnchorFunction = generic::GenericCalcAnchorFunction<Leaf>; /// Specified `anchor-size()` function in math functions. pubtype CalcAnchorSizeFunction = generic::GenericCalcAnchorSizeFunction<Leaf>;
/// Whether in place operations should be done when parsing expressions to create CalcNode #[derive(Clone, Copy, PartialEq, Eq)] pubenum CalcNodeParseInPlaceOperations { /// Avoid in place operations
No, /// Alow in place operations
Yes,
}
/// A calc node representation for specified values. pubtype CalcNode = generic::GenericCalcNode<Leaf>; impl CalcNode { /// Tries to parse a single element in the expression, that is, a /// `<length>`, `<angle>`, `<time>`, `<percentage>`, `<resolution>`, etc. /// /// May return a "complex" `CalcNode`, in the presence of a parenthesized /// expression, for example. fn parse_one<'i, 't>(
context: &ParserContext,
input: &mut Parser<'i, 't>,
flags: CalcParseFlags,
) -> Result<Self, ParseError<'i>> { let location = input.current_source_location(); match input.next()? {
&Token::Number { value, .. } => {
Ok(CalcNode::Leaf(Leaf::Number(NoCalcNumber::new(value))))
},
&Token::Dimension {
value, ref unit, ..
} => { if flags.includes(CalcUnits::LENGTH) { iflet Ok(l) = NoCalcLength::parse_dimension_with_context(context, value, unit)
{ return Ok(CalcNode::Leaf(Leaf::Length(l)));
}
} if flags.includes(CalcUnits::ANGLE) { iflet Ok(a) = NoCalcAngle::parse_dimension(value, unit) { return Ok(CalcNode::Leaf(Leaf::Angle(a)));
}
} if flags.includes(CalcUnits::TIME) { iflet Ok(t) = NoCalcTime::parse_dimension(value, unit) { return Ok(CalcNode::Leaf(Leaf::Time(t)));
}
} if flags.includes(CalcUnits::RESOLUTION) { iflet Ok(t) = NoCalcResolution::parse_dimension(value, unit) { return Ok(CalcNode::Leaf(Leaf::Resolution(t)));
}
} return Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError));
},
&Token::Percentage { unit_value, .. } if flags.includes(CalcUnits::PERCENTAGE) => Ok(
CalcNode::Leaf(Leaf::Percentage(NoCalcPercentage::new(unit_value))),
),
&Token::ParenthesisBlock => {
input.parse_nested_block(|input| CalcNode::parse_argument(context, input, flags))
},
&Token::Function(ref name) if flags
.additional_functions
.intersects(AdditionalFunctions::ANCHOR)
&& name.eq_ignore_ascii_case("anchor") =>
{ let anchor_function = GenericAnchorFunction::parse_in_calc(
context,
flags.additional_functions,
input,
)?;
Ok(CalcNode::Anchor(Box::new(anchor_function)))
},
&Token::Function(ref name) if flags
.additional_functions
.intersects(AdditionalFunctions::ANCHOR_SIZE)
&& name.eq_ignore_ascii_case("anchor-size") =>
{ let anchor_size_function =
GenericAnchorSizeFunction::parse_in_calc(context, input)?;
Ok(CalcNode::AnchorSize(Box::new(anchor_size_function)))
},
&Token::Function(ref name) => { let function = CalcNode::math_function(context, &name, location)?;
CalcNode::parse(context, input, function, flags)
},
&Token::Ident(ref ident) => { let leaf = match_ignore_ascii_case! { &**ident, "e" => Leaf::Number(NoCalcNumber::new(std::f32::consts::E)), "pi" => Leaf::Number(NoCalcNumber::new(std::f32::consts::PI)), "infinity" => Leaf::Number(NoCalcNumber::new(f32::INFINITY)), "-infinity" => Leaf::Number(NoCalcNumber::new(f32::NEG_INFINITY)), "nan" => Leaf::Number(NoCalcNumber::new(f32::NAN)),
_ => { match ChannelKeyword::from_ident(&ident) {
Ok(channel_keyword) if flags.color_components.contains(channel_keyword) => Leaf::ColorComponent(channel_keyword),
_ => return Err(location.new_unexpected_token_error(Token::Ident(ident.clone()))),
}
},
};
Ok(CalcNode::Leaf(leaf))
},
t => Err(location.new_unexpected_token_error(t.clone())),
}
}
/// Parse a top-level `calc` expression, with all nested sub-expressions. /// /// This is in charge of parsing, for example, `2 + 3 * 100%`. pubfn parse<'i, 't>(
context: &ParserContext,
input: &mut Parser<'i, 't>,
function: MathFunction,
flags: CalcParseFlags,
) -> Result<Self, ParseError<'i>> {
input.parse_nested_block(|input| { match function {
MathFunction::Calc => Self::parse_argument(context, input, flags),
MathFunction::Clamp => { let min_val = if input
.try_parse(|min| min.expect_ident_matching("none"))
.ok()
.is_none()
{
Some(Self::parse_argument(context, input, flags)?)
} else {
None
};
input.expect_comma()?; let center = Self::parse_argument(context, input, flags)?;
input.expect_comma()?;
let max_val = if input
.try_parse(|max| max.expect_ident_matching("none"))
.ok()
.is_none()
{
Some(Self::parse_argument(context, input, flags)?)
} else {
None
};
// Specification does not state how serialization should occur for clamp // https://github.com/w3c/csswg-drafts/issues/13535 // tentatively partially serialize to min/max // clamp(MIN, VAL, none) is equivalent to max(MIN, VAL) // clamp(none, VAL, MAX) is equivalent to min(VAL, MAX) // clamp(none, VAL, none) is equivalent to just calc(VAL)
Ok(match (min_val, max_val) {
(None, None) => center,
(None, Some(max)) => Self::MinMax(vec![center, max].into(), MinMaxOp::Min),
(Some(min), None) => Self::MinMax(vec![min, center].into(), MinMaxOp::Max),
(Some(min), Some(max)) => Self::Clamp {
min: Box::new(min),
center: Box::new(center),
max: Box::new(max),
},
})
},
MathFunction::Round => { let strategy = input.try_parse(parse_rounding_strategy);
let value = Self::parse_argument(context, input, flags)?;
// <step> defaults to the number 1 if not provided // https://drafts.csswg.org/css-values-4/#funcdef-round let step = input.try_parse(|input| {
input.expect_comma()?; Self::parse_argument(context, input, flags)
});
let step = step.unwrap_or(Self::Leaf(Leaf::Number(NoCalcNumber::new(1.0))));
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 => { // TODO(emilio): The common case for parse_comma_separated // is just one element, but for min / max is two, really... // // Consider adding an API to cssparser to specify the // initial vector capacity? let arguments = input.parse_comma_separated(|input| { let result = Self::parse_argument(context, input, flags)?;
Ok(result)
})?;
let op = match function {
MathFunction::Min => MinMaxOp::Min,
MathFunction::Max => MinMaxOp::Max,
_ => unreachable!(),
};
Ok(Self::MinMax(arguments.into(), op))
},
MathFunction::Sin | MathFunction::Cos | MathFunction::Tan => { let node = Self::parse_argument(
context,
input,
flags.new_including(CalcUnits::ANGLE),
)?;
Ok(match function {
MathFunction::Sin => Self::Sin(Box::new(node)),
MathFunction::Cos => Self::Cos(Box::new(node)),
MathFunction::Tan => Self::Tan(Box::new(node)),
_ => unsafe { debug_unreachable!("We just checked!") },
})
},
MathFunction::Asin | MathFunction::Acos | MathFunction::Atan => { let node = Self::parse_argument(context, input, flags)?;
Ok(match function {
MathFunction::Asin => Self::Asin(Box::new(node)),
MathFunction::Acos => Self::Acos(Box::new(node)),
MathFunction::Atan => Self::Atan(Box::new(node)),
_ => unsafe { debug_unreachable!("We just checked!") },
})
},
MathFunction::Atan2 => { let allow_all = flags.new_including(CalcUnits::ALL); let a = Self::parse_argument(context, input, allow_all)?;
input.expect_comma()?; let b = Self::parse_argument(context, input, allow_all)?; // TODO(Bug 2042060) - Allow combining length and percentage arguments (if it can be resolved). if a.unit() != b.unit() { return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
}
Ok(Self::Atan2(Box::new(a), Box::new(b)))
},
MathFunction::Pow => { let a = Self::parse_argument(context, input, flags)?;
input.expect_comma()?; let b = Self::parse_argument(context, input, flags)?;
Ok(Self::Pow(Box::new(a), Box::new(b)))
},
MathFunction::Sqrt => { let a = Self::parse_argument(context, input, flags)?;
Ok(Self::Sqrt(Box::new(a)))
},
MathFunction::Hypot => { let arguments = input.parse_comma_separated(|input| { let result = Self::parse_argument(context, input, flags)?;
Ok(result)
})?;
Ok(Self::Hypot(arguments.into()))
},
MathFunction::Log => { let a = Self::parse_argument(context, input, flags)?; let b = input
.try_parse(|input| {
input.expect_comma()?; Self::parse_argument(context, input, flags)
})
.ok();
Ok(Self::Log(Box::new(a), b.map(Box::new).into()))
},
MathFunction::Exp => { let a = Self::parse_argument(context, input, flags)?;
Ok(Self::Exp(Box::new(a)))
},
MathFunction::Abs => { let node = Self::parse_argument(context, input, flags)?;
Ok(Self::Abs(Box::new(node)))
},
MathFunction::Sign => { // The sign of a percentage is dependent on the percentage basis, so if // percentages aren't allowed (so there's no basis) we shouldn't allow them in // sign(). The rest of the units are safe tho. let node = Self::parse_argument(
context,
input,
flags.new_including(CalcUnits::ALL - CalcUnits::PERCENTAGE),
)?;
Ok(Self::Sign(Box::new(node)))
},
MathFunction::SiblingCount | MathFunction::SiblingIndex => { if !static_prefs::pref!("layout.css.tree-counting-functions.enabled") { return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
}
if !context.has_element_context() { return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
}
// Tree-counting functions have no arguments
input.expect_exhausted()?;
/// Parse a top-level `calc` expression, and all the products that may /// follow, and stop as soon as a non-product expression is found. /// /// This should parse correctly: /// /// * `2` /// * `2 * 2` /// * `2 * 2 + 2` (but will leave the `+ 2` unparsed). /// fn parse_product<'i, 't>(
context: &ParserContext,
input: &mut Parser<'i, 't>,
flags: CalcParseFlags,
) -> Result<Self, ParseError<'i>> { letmut product = SmallVec::<[CalcNode; 1]>::new(); let first = Self::parse_one(context, input, flags)?;
product.push(first);
loop { let start = input.state(); match input.next() {
Ok(&Token::Delim('*')) => { letmut rhs = Self::parse_one(context, input, flags)?;
// We can unwrap here, because we start the function by adding a node to // the list. if flags.in_place_operations == CalcNodeParseInPlaceOperations::No
|| !product.last_mut().unwrap().try_product_in_place(&mut rhs)
{
product.push(rhs);
}
},
Ok(&Token::Delim('/')) => { let rhs = Self::parse_one(context, input, flags)?;
enum InPlaceDivisionResult { /// The right was merged into the left.
Merged, /// The right is not a number or could not be resolved, so the left is /// unchanged.
Unchanged, /// The right was resolved, but was not a number, so the calculation is /// invalid.
Invalid,
}
iflet Ok(resolved) = right.resolve() { iflet Some(number) = resolved.as_number() { if number != 1.0 && left.is_product_distributive() { if left.map(|l| l / number).is_err() { return InPlaceDivisionResult::Invalid;
} return InPlaceDivisionResult::Merged;
}
} else { // Unresolved components that are numbers are valid denominators, // but they can't resolve right now. returnif resolved.unit().is_empty() {
InPlaceDivisionResult::Unchanged
} else {
InPlaceDivisionResult::Invalid
};
}
}
InPlaceDivisionResult::Unchanged
}
// The right hand side of a division *must* be a number, so if we can // already resolve it, then merge it with the last node on the product list. // We can unwrap here, becuase we start the function by adding a node to // the list. match try_division_in_place(
&mut product.last_mut().unwrap(),
&rhs,
flags.in_place_operations,
) {
InPlaceDivisionResult::Merged => {},
InPlaceDivisionResult::Unchanged => {
product.push(Self::Invert(Box::new(rhs)))
},
InPlaceDivisionResult::Invalid => { return Err(
input.new_custom_error(StyleParseErrorKind::UnspecifiedError)
)
},
}
},
_ => {
input.reset(&start); break;
},
}
}
/// Resolves this calc tree into a leaf node, using the computed context /// if provided for any nodes that require it. Additional node mapping can /// be provided using `leaf_to_output_fn`. Returns Err(()) if the calc tree /// could not be resolved for any reason. pubfn resolve_computed<F>(
&self,
context: Option<&computed::Context>,
leaf_to_output_fn: F,
) -> Result<Leaf, ()> where
F: Fn(&Leaf) -> Result<Leaf, ()>,
{ // TODO(Bug 2040558) - Consider handling all leaf types here via `to_computed_value`. self.resolve_map(|leaf| {
Ok(match leaf {
Leaf::Length(length) => Leaf::Length(NoCalcLength::from_px(match context {
Some(ctx) => length.to_computed_value(ctx).px(),
None => length.to_computed_pixel_length_without_context()?,
})),
Leaf::TreeCountingFunction(f) => Leaf::Number(NoCalcNumber::new(
f.to_computed_value(context.ok_or(())?) as f32,
)),
_ => leaf_to_output_fn(leaf)?,
})
})
}
/// Tries to simplify this expression into a `<length>` or `<percentage>` /// value. pubfn into_length_or_percentage( mutself,
clamping_mode: AllowedNumericType,
) -> Result<CalcLengthPercentage, ()> { self.simplify_and_sort();
// Although we allow numbers inside CalcNumeric, calculations that resolve to a // number result is still not allowed. let unit = self.unit()?; if !CalcUnits::LENGTH_PERCENTAGE.intersects(unit) {
Err(())
} else {
Ok(CalcLengthPercentage(CalcNumeric {
clamping_mode,
node: self,
}))
}
}
/// Tries to simplify this expression into a `<time>` value. fn into_time(mutself, clamping_mode: AllowedNumericType) -> Result<CalcNumeric, ()> { self.simplify_and_sort();
let unit: CalcUnits = self.unit()?; if !CalcUnits::TIME.intersects(unit) {
Err(())
} else {
Ok(CalcNumeric {
clamping_mode,
node: self,
})
}
}
/// Tries to simplify this expression into a `<resolution>` value. fn into_resolution(mutself) -> Result<CalcNumeric, ()> { self.simplify_and_sort();
/// Tries to simplify this expression into a `CalcNumeric` value. fn into_angle(mutself, clamping_mode: AllowedNumericType) -> Result<CalcNumeric, ()> { self.simplify_and_sort();
let unit: CalcUnits = self.unit()?; if !CalcUnits::ANGLE.intersects(unit) {
Err(())
} else {
Ok(CalcNumeric {
clamping_mode,
node: self,
})
}
}
/// Tries to convert this expression into a `CalcNumeric`, keeping the /// AST for later evaluation at computed-value time. fn into_number(mutself, clamping_mode: AllowedNumericType) -> Result<CalcNumeric, ()> { self.simplify_and_sort();
let unit: CalcUnits = self.unit()?; if !unit.is_empty() {
Err(())
} else {
Ok(CalcNumeric {
clamping_mode,
node: self,
})
}
}
/// Tries to convert this expression into a `CalcNumeric`, keeping the /// AST for later evaluation at computed-value time. fn into_percentage(mutself, clamping_mode: AllowedNumericType) -> Result<CalcNumeric, ()> { self.simplify_and_sort();
let unit: CalcUnits = self.unit()?; if !CalcUnits::PERCENTAGE.intersects(unit) {
Err(())
} else {
Ok(CalcNumeric {
clamping_mode,
node: self,
})
}
}
/// Given a function name, and the location from where the token came from, /// return a mathematical function corresponding to that name or an error. #[inline] pubfn math_function<'i>(
_: &ParserContext,
name: &CowRcStr<'i>,
location: cssparser::SourceLocation,
) -> Result<MathFunction, ParseError<'i>> { let function = match MathFunction::from_ident(&*name) {
Ok(f) => f,
Err(()) => { return Err(location.new_unexpected_token_error(Token::Function(name.clone())))
},
};
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.