summaryrefslogtreecommitdiffstats
path: root/servo/components/style/values/specified/percentage.rs
diff options
context:
space:
mode:
Diffstat (limited to 'servo/components/style/values/specified/percentage.rs')
-rw-r--r--servo/components/style/values/specified/percentage.rs225
1 files changed, 225 insertions, 0 deletions
diff --git a/servo/components/style/values/specified/percentage.rs b/servo/components/style/values/specified/percentage.rs
new file mode 100644
index 0000000000..ccf16d6463
--- /dev/null
+++ b/servo/components/style/values/specified/percentage.rs
@@ -0,0 +1,225 @@
+/* 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/. */
+
+//! Specified percentages.
+
+use crate::parser::{Parse, ParserContext};
+use crate::values::computed::percentage::Percentage as ComputedPercentage;
+use crate::values::computed::{Context, ToComputedValue};
+use crate::values::generics::NonNegative;
+use crate::values::specified::calc::CalcNode;
+use crate::values::specified::Number;
+use crate::values::{normalize, serialize_percentage, CSSFloat};
+use cssparser::{Parser, Token};
+use std::fmt::{self, Write};
+use style_traits::values::specified::AllowedNumericType;
+use style_traits::{CssWriter, ParseError, SpecifiedValueInfo, ToCss};
+
+/// A percentage value.
+#[derive(Clone, Copy, Debug, Default, MallocSizeOf, PartialEq, ToShmem)]
+pub struct Percentage {
+ /// The percentage value as a float.
+ ///
+ /// [0 .. 100%] maps to [0.0 .. 1.0]
+ value: CSSFloat,
+ /// If this percentage came from a calc() expression, this tells how
+ /// clamping should be done on the value.
+ calc_clamping_mode: Option<AllowedNumericType>,
+}
+
+impl ToCss for Percentage {
+ fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
+ where
+ W: Write,
+ {
+ if self.calc_clamping_mode.is_some() {
+ dest.write_str("calc(")?;
+ }
+
+ serialize_percentage(self.value, dest)?;
+
+ if self.calc_clamping_mode.is_some() {
+ dest.write_char(')')?;
+ }
+ Ok(())
+ }
+}
+
+impl Percentage {
+ /// Creates a percentage from a numeric value.
+ pub(super) fn new_with_clamping_mode(
+ value: CSSFloat,
+ calc_clamping_mode: Option<AllowedNumericType>,
+ ) -> Self {
+ Self {
+ value,
+ calc_clamping_mode,
+ }
+ }
+
+ /// Creates a percentage from a numeric value.
+ pub fn new(value: CSSFloat) -> Self {
+ Self::new_with_clamping_mode(value, None)
+ }
+
+ /// `0%`
+ #[inline]
+ pub fn zero() -> Self {
+ Percentage {
+ value: 0.,
+ calc_clamping_mode: None,
+ }
+ }
+
+ /// `100%`
+ #[inline]
+ pub fn hundred() -> Self {
+ Percentage {
+ value: 1.,
+ calc_clamping_mode: None,
+ }
+ }
+
+ /// Gets the underlying value for this float.
+ pub fn get(&self) -> CSSFloat {
+ self.calc_clamping_mode
+ .map_or(self.value, |mode| mode.clamp(self.value))
+ }
+
+ /// Returns this percentage as a number.
+ pub fn to_number(&self) -> Number {
+ Number::new_with_clamping_mode(self.value, self.calc_clamping_mode)
+ }
+
+ /// Returns the calc() clamping mode for this percentage.
+ pub fn calc_clamping_mode(&self) -> Option<AllowedNumericType> {
+ self.calc_clamping_mode
+ }
+
+ /// Reverses this percentage, preserving calc-ness.
+ ///
+ /// For example: If it was 20%, convert it into 80%.
+ pub fn reverse(&mut self) {
+ let new_value = 1. - self.value;
+ self.value = new_value;
+ }
+
+ /// Parses a specific kind of percentage.
+ pub fn parse_with_clamping_mode<'i, 't>(
+ context: &ParserContext,
+ input: &mut Parser<'i, 't>,
+ num_context: AllowedNumericType,
+ ) -> Result<Self, ParseError<'i>> {
+ let location = input.current_source_location();
+ match *input.next()? {
+ Token::Percentage { unit_value, .. }
+ if num_context.is_ok(context.parsing_mode, unit_value) =>
+ {
+ Ok(Percentage::new(unit_value))
+ },
+ Token::Function(ref name) => {
+ let function = CalcNode::math_function(context, name, location)?;
+ let value = CalcNode::parse_percentage(context, input, function)?;
+ Ok(Percentage {
+ value,
+ calc_clamping_mode: Some(num_context),
+ })
+ },
+ ref t => Err(location.new_unexpected_token_error(t.clone())),
+ }
+ }
+
+ /// Parses a percentage token, but rejects it if it's negative.
+ pub fn parse_non_negative<'i, 't>(
+ context: &ParserContext,
+ input: &mut Parser<'i, 't>,
+ ) -> Result<Self, ParseError<'i>> {
+ Self::parse_with_clamping_mode(context, input, AllowedNumericType::NonNegative)
+ }
+
+ /// Parses a percentage token, but rejects it if it's negative or more than
+ /// 100%.
+ pub fn parse_zero_to_a_hundred<'i, 't>(
+ context: &ParserContext,
+ input: &mut Parser<'i, 't>,
+ ) -> Result<Self, ParseError<'i>> {
+ Self::parse_with_clamping_mode(context, input, AllowedNumericType::ZeroToOne)
+ }
+
+ /// Clamp to 100% if the value is over 100%.
+ #[inline]
+ pub fn clamp_to_hundred(self) -> Self {
+ Percentage {
+ value: self.value.min(1.),
+ calc_clamping_mode: self.calc_clamping_mode,
+ }
+ }
+}
+
+impl Parse for Percentage {
+ #[inline]
+ fn parse<'i, 't>(
+ context: &ParserContext,
+ input: &mut Parser<'i, 't>,
+ ) -> Result<Self, ParseError<'i>> {
+ Self::parse_with_clamping_mode(context, input, AllowedNumericType::All)
+ }
+}
+
+impl ToComputedValue for Percentage {
+ type ComputedValue = ComputedPercentage;
+
+ #[inline]
+ fn to_computed_value(&self, _: &Context) -> Self::ComputedValue {
+ ComputedPercentage(normalize(self.get()))
+ }
+
+ #[inline]
+ fn from_computed_value(computed: &Self::ComputedValue) -> Self {
+ Percentage::new(computed.0)
+ }
+}
+
+impl SpecifiedValueInfo for Percentage {}
+
+/// Turns the percentage into a plain float.
+pub trait ToPercentage {
+ /// Returns whether this percentage used to be a calc().
+ fn is_calc(&self) -> bool {
+ false
+ }
+ /// Turns the percentage into a plain float.
+ fn to_percentage(&self) -> CSSFloat;
+}
+
+impl ToPercentage for Percentage {
+ fn is_calc(&self) -> bool {
+ self.calc_clamping_mode.is_some()
+ }
+
+ fn to_percentage(&self) -> CSSFloat {
+ self.get()
+ }
+}
+
+/// A wrapper of Percentage, whose value must be >= 0.
+pub type NonNegativePercentage = NonNegative<Percentage>;
+
+impl Parse for NonNegativePercentage {
+ #[inline]
+ fn parse<'i, 't>(
+ context: &ParserContext,
+ input: &mut Parser<'i, 't>,
+ ) -> Result<Self, ParseError<'i>> {
+ Ok(NonNegative(Percentage::parse_non_negative(context, input)?))
+ }
+}
+
+impl NonNegativePercentage {
+ /// Convert to ComputedPercentage, for FontFaceRule size-adjust getter.
+ #[inline]
+ pub fn compute(&self) -> ComputedPercentage {
+ ComputedPercentage(self.0.get())
+ }
+}