svg.mako.rs (12538B)
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 <%namespace name="helpers" file="/helpers.mako.rs" /> 6 7 <%helpers:shorthand name="mask" engines="gecko" extra_prefixes="webkit" 8 sub_properties="mask-mode mask-repeat mask-clip mask-origin mask-composite mask-position-x 9 mask-position-y mask-size mask-image" 10 spec="https://drafts.fxtf.org/css-masking/#propdef-mask"> 11 use crate::properties::longhands::{mask_mode, mask_repeat, mask_clip, mask_origin, mask_composite, mask_position_x, 12 mask_position_y}; 13 use crate::properties::longhands::{mask_size, mask_image}; 14 use crate::values::specified::{Position, PositionComponent}; 15 use crate::parser::Parse; 16 17 // FIXME(emilio): These two mask types should be the same! 18 impl From<mask_origin::single_value::SpecifiedValue> for mask_clip::single_value::SpecifiedValue { 19 fn from(origin: mask_origin::single_value::SpecifiedValue) -> mask_clip::single_value::SpecifiedValue { 20 match origin { 21 mask_origin::single_value::SpecifiedValue::ContentBox => 22 mask_clip::single_value::SpecifiedValue::ContentBox, 23 mask_origin::single_value::SpecifiedValue::PaddingBox => 24 mask_clip::single_value::SpecifiedValue::PaddingBox , 25 mask_origin::single_value::SpecifiedValue::BorderBox => 26 mask_clip::single_value::SpecifiedValue::BorderBox, 27 % if engine == "gecko": 28 mask_origin::single_value::SpecifiedValue::FillBox => 29 mask_clip::single_value::SpecifiedValue::FillBox , 30 mask_origin::single_value::SpecifiedValue::StrokeBox => 31 mask_clip::single_value::SpecifiedValue::StrokeBox, 32 mask_origin::single_value::SpecifiedValue::ViewBox=> 33 mask_clip::single_value::SpecifiedValue::ViewBox, 34 % endif 35 } 36 } 37 } 38 39 pub fn parse_value<'i, 't>( 40 context: &ParserContext, 41 input: &mut Parser<'i, 't>, 42 ) -> Result<Longhands, ParseError<'i>> { 43 % for name in "image mode position_x position_y size repeat origin clip composite".split(): 44 // Vec grows from 0 to 4 by default on first push(). So allocate with 45 // capacity 1, so in the common case of only one item we don't way 46 // overallocate, then shrink. Note that we always push at least one 47 // item if parsing succeeds. 48 let mut mask_${name} = Vec::with_capacity(1); 49 % endfor 50 51 input.parse_comma_separated(|input| { 52 % for name in "image mode position size repeat origin clip composite".split(): 53 let mut ${name} = None; 54 % endfor 55 loop { 56 if image.is_none() { 57 if let Ok(value) = input.try_parse(|input| mask_image::single_value 58 ::parse(context, input)) { 59 image = Some(value); 60 continue 61 } 62 } 63 if position.is_none() { 64 if let Ok(value) = input.try_parse(|input| Position::parse(context, input)) { 65 position = Some(value); 66 67 // Parse mask size, if applicable. 68 size = input.try_parse(|input| { 69 input.expect_delim('/')?; 70 mask_size::single_value::parse(context, input) 71 }).ok(); 72 73 continue 74 } 75 } 76 % for name in "repeat origin clip composite mode".split(): 77 if ${name}.is_none() { 78 if let Ok(value) = input.try_parse(|input| mask_${name}::single_value 79 ::parse(context, input)) { 80 ${name} = Some(value); 81 continue 82 } 83 } 84 % endfor 85 break 86 } 87 if clip.is_none() { 88 if let Some(origin) = origin { 89 clip = Some(mask_clip::single_value::SpecifiedValue::from(origin)); 90 } 91 } 92 let mut any = false; 93 % for name in "image mode position size repeat origin clip composite".split(): 94 any = any || ${name}.is_some(); 95 % endfor 96 if any { 97 if let Some(position) = position { 98 mask_position_x.push(position.horizontal); 99 mask_position_y.push(position.vertical); 100 } else { 101 mask_position_x.push(PositionComponent::zero()); 102 mask_position_y.push(PositionComponent::zero()); 103 } 104 % for name in "image mode size repeat origin clip composite".split(): 105 if let Some(m_${name}) = ${name} { 106 mask_${name}.push(m_${name}); 107 } else { 108 mask_${name}.push(mask_${name}::single_value 109 ::get_initial_specified_value()); 110 } 111 % endfor 112 Ok(()) 113 } else { 114 Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)) 115 } 116 })?; 117 118 Ok(expanded! { 119 % for name in "image mode position_x position_y size repeat origin clip composite".split(): 120 mask_${name}: mask_${name}::SpecifiedValue(mask_${name}.into()), 121 % endfor 122 }) 123 } 124 125 impl<'a> ToCss for LonghandsToSerialize<'a> { 126 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: fmt::Write { 127 use crate::properties::longhands::mask_origin::single_value::computed_value::T as Origin; 128 use crate::properties::longhands::mask_clip::single_value::computed_value::T as Clip; 129 use style_traits::values::SequenceWriter; 130 131 let len = self.mask_image.0.len(); 132 if len == 0 { 133 return Ok(()); 134 } 135 % for name in "mode position_x position_y size repeat origin clip composite".split(): 136 if self.mask_${name}.0.len() != len { 137 return Ok(()); 138 } 139 % endfor 140 141 // For each <mask-layer>, we serialize it according to the following order: 142 // <mask-layer> = 143 // <mask-reference> || 144 // <position> [ / <bg-size> ]? || 145 // <repeat-style> || 146 // <coord-box> || 147 // [ <coord-box> | no-clip ] || 148 // <compositing-operator> || 149 // <masking-mode> 150 // https://drafts.fxtf.org/css-masking-1/#the-mask 151 for i in 0..len { 152 if i > 0 { 153 dest.write_str(", ")?; 154 } 155 156 % for name in "image mode position_x position_y size repeat origin clip composite".split(): 157 let ${name} = &self.mask_${name}.0[i]; 158 % endfor 159 160 let mut has_other = false; 161 % for name in "image mode size repeat composite".split(): 162 let has_${name} = 163 *${name} != mask_${name}::single_value::get_initial_specified_value(); 164 has_other |= has_${name}; 165 % endfor 166 let has_position = *position_x != PositionComponent::zero() 167 || *position_y != PositionComponent::zero(); 168 let has_origin = *origin != Origin::BorderBox; 169 let has_clip = *clip != Clip::BorderBox; 170 171 // If all are initial values, we serialize mask-image. 172 if !has_other && !has_position && !has_origin && !has_clip { 173 return image.to_css(dest); 174 } 175 176 let mut writer = SequenceWriter::new(dest, " "); 177 // <mask-reference> 178 if has_image { 179 writer.item(image)?; 180 } 181 182 // <position> [ / <bg-size> ]? 183 if has_position || has_size { 184 writer.item(&Position { 185 horizontal: position_x.clone(), 186 vertical: position_y.clone() 187 })?; 188 189 if has_size { 190 writer.raw_item("/")?; 191 writer.item(size)?; 192 } 193 } 194 195 // <repeat-style> 196 if has_repeat { 197 writer.item(repeat)?; 198 } 199 200 // <coord-box> 201 // Note: 202 // Even if 'mask-origin' is at its initial value 'border-box', 203 // we still have to serialize it to avoid ambiguity iF the 204 // 'mask-clip' longhand has some other <coord-box> value 205 // (i.e. neither 'border-box' nor 'no-clip'). (If we naively 206 // declined to serialize the 'mask-origin' value in this 207 // situation, then whatever value we serialize for 'mask-clip' 208 // would implicitly also represent 'mask-origin' and would be 209 // providing the wrong value for that longhand.) 210 if has_origin || (has_clip && *clip != Clip::NoClip) { 211 writer.item(origin)?; 212 } 213 214 // [ <coord-box> | no-clip ] 215 if has_clip && *clip != From::from(*origin) { 216 writer.item(clip)?; 217 } 218 219 // <compositing-operator> 220 if has_composite { 221 writer.item(composite)?; 222 } 223 224 // <masking-mode> 225 if has_mode { 226 writer.item(mode)?; 227 } 228 } 229 230 Ok(()) 231 } 232 } 233 </%helpers:shorthand> 234 235 <%helpers:shorthand name="mask-position" engines="gecko" extra_prefixes="webkit" 236 sub_properties="mask-position-x mask-position-y" 237 spec="https://drafts.csswg.org/css-masks-4/#the-mask-position"> 238 use crate::properties::longhands::{mask_position_x,mask_position_y}; 239 use crate::values::specified::position::Position; 240 use crate::parser::Parse; 241 242 pub fn parse_value<'i, 't>( 243 context: &ParserContext, 244 input: &mut Parser<'i, 't>, 245 ) -> Result<Longhands, ParseError<'i>> { 246 // Vec grows from 0 to 4 by default on first push(). So allocate with 247 // capacity 1, so in the common case of only one item we don't way 248 // overallocate, then shrink. Note that we always push at least one 249 // item if parsing succeeds. 250 let mut position_x = Vec::with_capacity(1); 251 let mut position_y = Vec::with_capacity(1); 252 let mut any = false; 253 254 input.parse_comma_separated(|input| { 255 let value = Position::parse(context, input)?; 256 position_x.push(value.horizontal); 257 position_y.push(value.vertical); 258 any = true; 259 Ok(()) 260 })?; 261 262 if !any { 263 return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)); 264 } 265 266 267 Ok(expanded! { 268 mask_position_x: mask_position_x::SpecifiedValue(position_x.into()), 269 mask_position_y: mask_position_y::SpecifiedValue(position_y.into()), 270 }) 271 } 272 273 impl<'a> ToCss for LonghandsToSerialize<'a> { 274 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: fmt::Write { 275 let len = self.mask_position_x.0.len(); 276 if len == 0 || self.mask_position_y.0.len() != len { 277 return Ok(()); 278 } 279 280 for i in 0..len { 281 Position { 282 horizontal: self.mask_position_x.0[i].clone(), 283 vertical: self.mask_position_y.0[i].clone() 284 }.to_css(dest)?; 285 286 if i < len - 1 { 287 dest.write_str(", ")?; 288 } 289 } 290 291 Ok(()) 292 } 293 } 294 </%helpers:shorthand>