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 http://mozilla.org/MPL/2.0/. */ 4 5 //! Specified types for CSS values related to effects. 6 7 use cssparser::{self, Parser, Token, BasicParseErrorKind}; 8 use parser::{Parse, ParserContext}; 9 use style_traits::{ParseError, StyleParseErrorKind, ValueParseErrorKind}; 10 #[cfg(not(feature = "gecko"))] 11 use values::Impossible; 12 use values::computed::{Context, NonNegativeNumber as ComputedNonNegativeNumber, ToComputedValue}; 13 use values::computed::effects::BoxShadow as ComputedBoxShadow; 14 use values::computed::effects::SimpleShadow as ComputedSimpleShadow; 15 use values::generics::NonNegative; 16 use values::generics::effects::BoxShadow as GenericBoxShadow; 17 use values::generics::effects::Filter as GenericFilter; 18 use values::generics::effects::SimpleShadow as GenericSimpleShadow; 19 use values::specified::{Angle, NumberOrPercentage}; 20 use values::specified::color::RGBAColor; 21 use values::specified::length::{Length, NonNegativeLength}; 22 #[cfg(feature = "gecko")] 23 use values::specified::url::SpecifiedUrl; 24 25 /// A specified value for a single shadow of the `box-shadow` property. 26 pub type BoxShadow = GenericBoxShadow<Option<RGBAColor>, Length, 27 Option<NonNegativeLength>, Option<Length>>; 28 29 /// A specified value for a single `filter`. 30 #[cfg(feature = "gecko")] 31 pub type Filter = GenericFilter<Angle, Factor, NonNegativeLength, SimpleShadow>; 32 33 /// A specified value for a single `filter`. 34 #[cfg(not(feature = "gecko"))] 35 pub type Filter = GenericFilter<Angle, Factor, NonNegativeLength, Impossible>; 36 37 /// A value for the `<factor>` parts in `Filter`. 38 #[derive(Clone, Debug, MallocSizeOf, PartialEq, ToCss)] 39 pub struct Factor(NumberOrPercentage); 40 41 impl Factor { 42 /// Parse this factor but clamp to one if the value is over 100%. 43 #[inline] parse_with_clamping_to_one<'i, 't>( context: &ParserContext, input: &mut Parser<'i, 't> ) -> Result<Self, ParseError<'i>>44 pub fn parse_with_clamping_to_one<'i, 't>( 45 context: &ParserContext, 46 input: &mut Parser<'i, 't> 47 ) -> Result<Self, ParseError<'i>> { 48 Factor::parse(context, input).map(|v| v.clamp_to_one()) 49 } 50 51 /// Clamp the value to 1 if the value is over 100%. 52 #[inline] clamp_to_one(self) -> Self53 fn clamp_to_one(self) -> Self { 54 match self.0 { 55 NumberOrPercentage::Percentage(percent) => { 56 Factor(NumberOrPercentage::Percentage(percent.clamp_to_hundred())) 57 }, 58 NumberOrPercentage::Number(number) => { 59 Factor(NumberOrPercentage::Number(number.clamp_to_one())) 60 } 61 } 62 } 63 } 64 65 impl Parse for Factor { 66 #[inline] parse<'i, 't>( context: &ParserContext, input: &mut Parser<'i, 't> ) -> Result<Self, ParseError<'i>>67 fn parse<'i, 't>( 68 context: &ParserContext, 69 input: &mut Parser<'i, 't> 70 ) -> Result<Self, ParseError<'i>> { 71 NumberOrPercentage::parse_non_negative(context, input).map(Factor) 72 } 73 } 74 75 impl ToComputedValue for Factor { 76 type ComputedValue = ComputedNonNegativeNumber; 77 78 #[inline] to_computed_value(&self, context: &Context) -> Self::ComputedValue79 fn to_computed_value(&self, context: &Context) -> Self::ComputedValue { 80 use values::computed::NumberOrPercentage; 81 match self.0.to_computed_value(context) { 82 NumberOrPercentage::Number(n) => n.into(), 83 NumberOrPercentage::Percentage(p) => p.0.into(), 84 } 85 } 86 87 #[inline] from_computed_value(computed: &Self::ComputedValue) -> Self88 fn from_computed_value(computed: &Self::ComputedValue) -> Self { 89 Factor(NumberOrPercentage::Number(ToComputedValue::from_computed_value(&computed.0))) 90 } 91 } 92 93 /// A specified value for the `drop-shadow()` filter. 94 pub type SimpleShadow = GenericSimpleShadow<Option<RGBAColor>, Length, Option<NonNegativeLength>>; 95 96 impl Parse for BoxShadow { parse<'i, 't>( context: &ParserContext, input: &mut Parser<'i, 't>, ) -> Result<Self, ParseError<'i>>97 fn parse<'i, 't>( 98 context: &ParserContext, 99 input: &mut Parser<'i, 't>, 100 ) -> Result<Self, ParseError<'i>> { 101 let mut lengths = None; 102 let mut color = None; 103 let mut inset = false; 104 105 loop { 106 if !inset { 107 if input.try(|input| input.expect_ident_matching("inset")).is_ok() { 108 inset = true; 109 continue; 110 } 111 } 112 if lengths.is_none() { 113 let value = input.try::<_, _, ParseError>(|i| { 114 let horizontal = Length::parse(context, i)?; 115 let vertical = Length::parse(context, i)?; 116 let (blur, spread) = match i.try::<_, _, ParseError>(|i| Length::parse_non_negative(context, i)) { 117 Ok(blur) => { 118 let spread = i.try(|i| Length::parse(context, i)).ok(); 119 (Some(blur.into()), spread) 120 }, 121 Err(_) => (None, None), 122 }; 123 Ok((horizontal, vertical, blur, spread)) 124 }); 125 if let Ok(value) = value { 126 lengths = Some(value); 127 continue; 128 } 129 } 130 if color.is_none() { 131 if let Ok(value) = input.try(|i| RGBAColor::parse(context, i)) { 132 color = Some(value); 133 continue; 134 } 135 } 136 break; 137 } 138 139 let lengths = lengths.ok_or(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))?; 140 Ok(BoxShadow { 141 base: SimpleShadow { 142 color: color, 143 horizontal: lengths.0, 144 vertical: lengths.1, 145 blur: lengths.2, 146 }, 147 spread: lengths.3, 148 inset: inset, 149 }) 150 } 151 } 152 153 impl ToComputedValue for BoxShadow { 154 type ComputedValue = ComputedBoxShadow; 155 156 #[inline] to_computed_value(&self, context: &Context) -> Self::ComputedValue157 fn to_computed_value(&self, context: &Context) -> Self::ComputedValue { 158 ComputedBoxShadow { 159 base: self.base.to_computed_value(context), 160 spread: self.spread.as_ref().unwrap_or(&Length::zero()).to_computed_value(context), 161 inset: self.inset, 162 } 163 } 164 165 #[inline] from_computed_value(computed: &ComputedBoxShadow) -> Self166 fn from_computed_value(computed: &ComputedBoxShadow) -> Self { 167 BoxShadow { 168 base: ToComputedValue::from_computed_value(&computed.base), 169 spread: Some(ToComputedValue::from_computed_value(&computed.spread)), 170 inset: computed.inset, 171 } 172 } 173 } 174 175 impl Parse for Filter { 176 #[inline] parse<'i, 't>( context: &ParserContext, input: &mut Parser<'i, 't> ) -> Result<Self, ParseError<'i>>177 fn parse<'i, 't>( 178 context: &ParserContext, 179 input: &mut Parser<'i, 't> 180 ) -> Result<Self, ParseError<'i>> { 181 #[cfg(feature = "gecko")] 182 { 183 if let Ok(url) = input.try(|i| SpecifiedUrl::parse(context, i)) { 184 return Ok(GenericFilter::Url(url)); 185 } 186 } 187 let location = input.current_source_location(); 188 let function = match input.expect_function() { 189 Ok(f) => f.clone(), 190 Err(cssparser::BasicParseError { 191 kind: BasicParseErrorKind::UnexpectedToken(t), 192 location, 193 }) => { 194 return Err(location.new_custom_error(ValueParseErrorKind::InvalidFilter(t))) 195 } 196 Err(e) => return Err(e.into()), 197 }; 198 input.parse_nested_block(|i| { 199 match_ignore_ascii_case! { &*function, 200 "blur" => Ok(GenericFilter::Blur((Length::parse_non_negative(context, i)?).into())), 201 "brightness" => Ok(GenericFilter::Brightness(Factor::parse(context, i)?)), 202 "contrast" => Ok(GenericFilter::Contrast(Factor::parse(context, i)?)), 203 "grayscale" => { 204 // Values of amount over 100% are allowed but UAs must clamp the values to 1. 205 // https://drafts.fxtf.org/filter-effects/#funcdef-filter-grayscale 206 Ok(GenericFilter::Grayscale(Factor::parse_with_clamping_to_one(context, i)?)) 207 }, 208 "hue-rotate" => { 209 // We allow unitless zero here, see: 210 // https://github.com/w3c/fxtf-drafts/issues/228 211 Ok(GenericFilter::HueRotate(Angle::parse_with_unitless(context, i)?)) 212 }, 213 "invert" => { 214 // Values of amount over 100% are allowed but UAs must clamp the values to 1. 215 // https://drafts.fxtf.org/filter-effects/#funcdef-filter-invert 216 Ok(GenericFilter::Invert(Factor::parse_with_clamping_to_one(context, i)?)) 217 }, 218 "opacity" => { 219 // Values of amount over 100% are allowed but UAs must clamp the values to 1. 220 // https://drafts.fxtf.org/filter-effects/#funcdef-filter-opacity 221 Ok(GenericFilter::Opacity(Factor::parse_with_clamping_to_one(context, i)?)) 222 }, 223 "saturate" => Ok(GenericFilter::Saturate(Factor::parse(context, i)?)), 224 "sepia" => { 225 // Values of amount over 100% are allowed but UAs must clamp the values to 1. 226 // https://drafts.fxtf.org/filter-effects/#funcdef-filter-sepia 227 Ok(GenericFilter::Sepia(Factor::parse_with_clamping_to_one(context, i)?)) 228 }, 229 "drop-shadow" => Ok(GenericFilter::DropShadow(Parse::parse(context, i)?)), 230 _ => Err(location.new_custom_error( 231 ValueParseErrorKind::InvalidFilter(Token::Function(function.clone())) 232 )), 233 } 234 }) 235 } 236 } 237 238 impl Parse for SimpleShadow { 239 #[inline] parse<'i, 't>( context: &ParserContext, input: &mut Parser<'i, 't> ) -> Result<Self, ParseError<'i>>240 fn parse<'i, 't>( 241 context: &ParserContext, 242 input: &mut Parser<'i, 't> 243 ) -> Result<Self, ParseError<'i>> { 244 let color = input.try(|i| RGBAColor::parse(context, i)).ok(); 245 let horizontal = Length::parse(context, input)?; 246 let vertical = Length::parse(context, input)?; 247 let blur = input.try(|i| Length::parse_non_negative(context, i)).ok(); 248 let color = color.or_else(|| input.try(|i| RGBAColor::parse(context, i)).ok()); 249 Ok(SimpleShadow { 250 color: color, 251 horizontal: horizontal, 252 vertical: vertical, 253 blur: blur.map(NonNegative::<Length>), 254 }) 255 } 256 } 257 258 impl ToComputedValue for SimpleShadow { 259 type ComputedValue = ComputedSimpleShadow; 260 261 #[inline] to_computed_value(&self, context: &Context) -> Self::ComputedValue262 fn to_computed_value(&self, context: &Context) -> Self::ComputedValue { 263 ComputedSimpleShadow { 264 color: self.color.to_computed_value(context), 265 horizontal: self.horizontal.to_computed_value(context), 266 vertical: self.vertical.to_computed_value(context), 267 blur: 268 self.blur.as_ref().unwrap_or(&NonNegativeLength::zero()).to_computed_value(context), 269 } 270 } 271 272 #[inline] from_computed_value(computed: &Self::ComputedValue) -> Self273 fn from_computed_value(computed: &Self::ComputedValue) -> Self { 274 SimpleShadow { 275 color: ToComputedValue::from_computed_value(&computed.color), 276 horizontal: ToComputedValue::from_computed_value(&computed.horizontal), 277 vertical: ToComputedValue::from_computed_value(&computed.vertical), 278 blur: Some(ToComputedValue::from_computed_value(&computed.blur)), 279 } 280 } 281 } 282