background.mako.rs (12565B)
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 // TODO: other background-* properties 8 <%helpers:shorthand name="background" 9 engines="gecko servo" 10 sub_properties="background-color background-position-x background-position-y background-repeat 11 background-attachment background-image background-size background-origin 12 background-clip" 13 spec="https://drafts.csswg.org/css-backgrounds/#the-background"> 14 use crate::properties::longhands::{background_position_x, background_position_y, background_repeat}; 15 use crate::properties::longhands::{background_attachment, background_color, background_image, background_size, background_origin}; 16 use crate::properties::longhands::background_clip; 17 use crate::properties::longhands::background_clip::single_value::computed_value::T as Clip; 18 use crate::properties::longhands::background_origin::single_value::computed_value::T as Origin; 19 use crate::values::specified::{AllowQuirks, Color, Position, PositionComponent}; 20 use crate::parser::Parse; 21 22 // FIXME(emilio): Should be the same type! 23 impl From<background_origin::single_value::SpecifiedValue> for background_clip::single_value::SpecifiedValue { 24 fn from(origin: background_origin::single_value::SpecifiedValue) -> 25 background_clip::single_value::SpecifiedValue { 26 match origin { 27 background_origin::single_value::SpecifiedValue::ContentBox => 28 background_clip::single_value::SpecifiedValue::ContentBox, 29 background_origin::single_value::SpecifiedValue::PaddingBox => 30 background_clip::single_value::SpecifiedValue::PaddingBox, 31 background_origin::single_value::SpecifiedValue::BorderBox => 32 background_clip::single_value::SpecifiedValue::BorderBox, 33 } 34 } 35 } 36 37 pub fn parse_value<'i, 't>( 38 context: &ParserContext, 39 input: &mut Parser<'i, 't>, 40 ) -> Result<Longhands, ParseError<'i>> { 41 let mut background_color = None; 42 43 % for name in "image position_x position_y repeat size attachment origin clip".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 background_${name} = Vec::with_capacity(1); 49 % endfor 50 input.parse_comma_separated(|input| { 51 // background-color can only be in the last element, so if it 52 // is parsed anywhere before, the value is invalid. 53 if background_color.is_some() { 54 return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)); 55 } 56 57 % for name in "image position repeat size attachment origin clip".split(): 58 let mut ${name} = None; 59 % endfor 60 loop { 61 if background_color.is_none() { 62 if let Ok(value) = input.try_parse(|i| Color::parse(context, i)) { 63 background_color = Some(value); 64 continue 65 } 66 } 67 if position.is_none() { 68 if let Ok(value) = input.try_parse(|input| { 69 Position::parse_three_value_quirky(context, input, AllowQuirks::No) 70 }) { 71 position = Some(value); 72 73 // Parse background size, if applicable. 74 size = input.try_parse(|input| { 75 input.expect_delim('/')?; 76 background_size::single_value::parse(context, input) 77 }).ok(); 78 79 continue 80 } 81 } 82 % for name in "image repeat attachment origin clip".split(): 83 if ${name}.is_none() { 84 if let Ok(value) = input.try_parse(|input| background_${name}::single_value 85 ::parse(context, input)) { 86 ${name} = Some(value); 87 continue 88 } 89 } 90 % endfor 91 break 92 } 93 if clip.is_none() { 94 if let Some(origin) = origin { 95 clip = Some(background_clip::single_value::SpecifiedValue::from(origin)); 96 } 97 } 98 let mut any = false; 99 % for name in "image position repeat size attachment origin clip".split(): 100 any = any || ${name}.is_some(); 101 % endfor 102 any = any || background_color.is_some(); 103 if any { 104 if let Some(position) = position { 105 background_position_x.push(position.horizontal); 106 background_position_y.push(position.vertical); 107 } else { 108 background_position_x.push(PositionComponent::zero()); 109 background_position_y.push(PositionComponent::zero()); 110 } 111 % for name in "image repeat size attachment origin clip".split(): 112 if let Some(bg_${name}) = ${name} { 113 background_${name}.push(bg_${name}); 114 } else { 115 background_${name}.push(background_${name}::single_value 116 ::get_initial_specified_value()); 117 } 118 % endfor 119 Ok(()) 120 } else { 121 Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)) 122 } 123 })?; 124 125 Ok(expanded! { 126 background_color: background_color.unwrap_or(Color::transparent()), 127 % for name in "image position_x position_y repeat size attachment origin clip".split(): 128 background_${name}: background_${name}::SpecifiedValue(background_${name}.into()), 129 % endfor 130 }) 131 } 132 133 impl<'a> ToCss for LonghandsToSerialize<'a> { 134 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: fmt::Write { 135 let len = self.background_image.0.len(); 136 // There should be at least one declared value 137 if len == 0 { 138 return Ok(()); 139 } 140 141 // If a value list length is differs then we don't do a shorthand serialization. 142 // The exceptions to this is color which appears once only and is serialized 143 // with the last item. 144 % for name in "image position_x position_y size repeat origin clip attachment".split(): 145 if len != self.background_${name}.0.len() { 146 return Ok(()); 147 } 148 % endfor 149 150 for i in 0..len { 151 % for name in "image position_x position_y repeat size attachment origin clip".split(): 152 let ${name} = &self.background_${name}.0[i]; 153 % endfor 154 155 if i != 0 { 156 dest.write_str(", ")?; 157 } 158 159 let mut wrote_value = false; 160 161 if i == len - 1 { 162 if *self.background_color != background_color::get_initial_specified_value() { 163 self.background_color.to_css(dest)?; 164 wrote_value = true; 165 } 166 } 167 168 if *image != background_image::single_value::get_initial_specified_value() { 169 if wrote_value { 170 dest.write_char(' ')?; 171 } 172 image.to_css(dest)?; 173 wrote_value = true; 174 } 175 176 // Size is only valid after a position so when there is a 177 // non-initial size we must also serialize position 178 if *position_x != PositionComponent::zero() || 179 *position_y != PositionComponent::zero() || 180 *size != background_size::single_value::get_initial_specified_value() 181 { 182 if wrote_value { 183 dest.write_char(' ')?; 184 } 185 186 Position { 187 horizontal: position_x.clone(), 188 vertical: position_y.clone() 189 }.to_css(dest)?; 190 191 wrote_value = true; 192 193 if *size != background_size::single_value::get_initial_specified_value() { 194 dest.write_str(" / ")?; 195 size.to_css(dest)?; 196 } 197 } 198 199 % for name in "repeat attachment".split(): 200 if *${name} != background_${name}::single_value::get_initial_specified_value() { 201 if wrote_value { 202 dest.write_char(' ')?; 203 } 204 ${name}.to_css(dest)?; 205 wrote_value = true; 206 } 207 % endfor 208 209 if *origin != Origin::PaddingBox || *clip != Clip::BorderBox { 210 if wrote_value { 211 dest.write_char(' ')?; 212 } 213 origin.to_css(dest)?; 214 if *clip != From::from(*origin) { 215 dest.write_char(' ')?; 216 clip.to_css(dest)?; 217 } 218 219 wrote_value = true; 220 } 221 222 if !wrote_value { 223 image.to_css(dest)?; 224 } 225 } 226 227 Ok(()) 228 } 229 } 230 </%helpers:shorthand> 231 232 <%helpers:shorthand name="background-position" 233 engines="gecko servo" 234 sub_properties="background-position-x background-position-y" 235 spec="https://drafts.csswg.org/css-backgrounds-4/#the-background-position"> 236 use crate::properties::longhands::{background_position_x, background_position_y}; 237 use crate::values::specified::AllowQuirks; 238 use crate::values::specified::position::Position; 239 240 pub fn parse_value<'i, 't>( 241 context: &ParserContext, 242 input: &mut Parser<'i, 't>, 243 ) -> Result<Longhands, ParseError<'i>> { 244 // Vec grows from 0 to 4 by default on first push(). So allocate with 245 // capacity 1, so in the common case of only one item we don't way 246 // overallocate, then shrink. Note that we always push at least one 247 // item if parsing succeeds. 248 let mut position_x = Vec::with_capacity(1); 249 let mut position_y = Vec::with_capacity(1); 250 let mut any = false; 251 252 input.parse_comma_separated(|input| { 253 let value = Position::parse_three_value_quirky(context, input, AllowQuirks::Yes)?; 254 position_x.push(value.horizontal); 255 position_y.push(value.vertical); 256 any = true; 257 Ok(()) 258 })?; 259 if !any { 260 return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)); 261 } 262 263 Ok(expanded! { 264 background_position_x: background_position_x::SpecifiedValue(position_x.into()), 265 background_position_y: background_position_y::SpecifiedValue(position_y.into()), 266 }) 267 } 268 269 impl<'a> ToCss for LonghandsToSerialize<'a> { 270 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: fmt::Write { 271 let len = self.background_position_x.0.len(); 272 if len == 0 || len != self.background_position_y.0.len() { 273 return Ok(()); 274 } 275 for i in 0..len { 276 Position { 277 horizontal: self.background_position_x.0[i].clone(), 278 vertical: self.background_position_y.0[i].clone() 279 }.to_css(dest)?; 280 281 if i < len - 1 { 282 dest.write_str(", ")?; 283 } 284 } 285 Ok(()) 286 } 287 } 288 </%helpers:shorthand>