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 <%namespace name="helpers" file="/helpers.mako.rs" /> 6 7 // TODO: other background-* properties 8 <%helpers:shorthand name="background" 9 sub_properties="background-color background-position-x background-position-y background-repeat 10 background-attachment background-image background-size background-origin 11 background-clip" 12 spec="https://drafts.csswg.org/css-backgrounds/#the-background"> 13 use properties::longhands::{background_position_x, background_position_y, background_repeat}; 14 use properties::longhands::{background_attachment, background_image, background_size, background_origin}; 15 use properties::longhands::background_clip; 16 use properties::longhands::background_clip::single_value::computed_value::T as Clip; 17 use properties::longhands::background_origin::single_value::computed_value::T as Origin; 18 use values::specified::{Color, Position, PositionComponent}; 19 use parser::Parse; 20 21 // FIXME(emilio): Should be the same type! 22 impl From<background_origin::single_value::SpecifiedValue> for background_clip::single_value::SpecifiedValue { from(origin: background_origin::single_value::SpecifiedValue) -> background_clip::single_value::SpecifiedValue23 fn from(origin: background_origin::single_value::SpecifiedValue) -> 24 background_clip::single_value::SpecifiedValue { 25 match origin { 26 background_origin::single_value::SpecifiedValue::ContentBox => 27 background_clip::single_value::SpecifiedValue::ContentBox, 28 background_origin::single_value::SpecifiedValue::PaddingBox => 29 background_clip::single_value::SpecifiedValue::PaddingBox, 30 background_origin::single_value::SpecifiedValue::BorderBox => 31 background_clip::single_value::SpecifiedValue::BorderBox, 32 } 33 } 34 } 35 parse_value<'i, 't>(context: &ParserContext, input: &mut Parser<'i, 't>) -> Result<Longhands, ParseError<'i>>36 pub fn parse_value<'i, 't>(context: &ParserContext, input: &mut Parser<'i, 't>) 37 -> Result<Longhands, ParseError<'i>> { 38 let mut background_color = None; 39 40 % for name in "image position_x position_y repeat size attachment origin clip".split(): 41 // Vec grows from 0 to 4 by default on first push(). So allocate 42 // with capacity 1, so in the common case of only one item we don't 43 // way overallocate. Note that we always push at least one item if 44 // parsing succeeds. 45 let mut background_${name} = background_${name}::SpecifiedValue(Vec::with_capacity(1)); 46 % endfor 47 input.parse_comma_separated(|input| { 48 // background-color can only be in the last element, so if it 49 // is parsed anywhere before, the value is invalid. 50 if background_color.is_some() { 51 return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)); 52 } 53 54 % for name in "image position repeat size attachment origin clip".split(): 55 let mut ${name} = None; 56 % endfor 57 loop { 58 if background_color.is_none() { 59 if let Ok(value) = input.try(|i| Color::parse(context, i)) { 60 background_color = Some(value); 61 continue 62 } 63 } 64 if position.is_none() { 65 if let Ok(value) = input.try(|input| Position::parse(context, input)) { 66 position = Some(value); 67 68 // Parse background size, if applicable. 69 size = input.try(|input| { 70 input.expect_delim('/')?; 71 background_size::single_value::parse(context, input) 72 }).ok(); 73 74 continue 75 } 76 } 77 % for name in "image repeat attachment origin clip".split(): 78 if ${name}.is_none() { 79 if let Ok(value) = input.try(|input| background_${name}::single_value 80 ::parse(context, input)) { 81 ${name} = Some(value); 82 continue 83 } 84 } 85 % endfor 86 break 87 } 88 if clip.is_none() { 89 if let Some(origin) = origin { 90 clip = Some(background_clip::single_value::SpecifiedValue::from(origin)); 91 } 92 } 93 let mut any = false; 94 % for name in "image position repeat size attachment origin clip".split(): 95 any = any || ${name}.is_some(); 96 % endfor 97 any = any || background_color.is_some(); 98 if any { 99 if let Some(position) = position { 100 background_position_x.0.push(position.horizontal); 101 background_position_y.0.push(position.vertical); 102 } else { 103 background_position_x.0.push(PositionComponent::zero()); 104 background_position_y.0.push(PositionComponent::zero()); 105 } 106 % for name in "image repeat size attachment origin clip".split(): 107 if let Some(bg_${name}) = ${name} { 108 background_${name}.0.push(bg_${name}); 109 } else { 110 background_${name}.0.push(background_${name}::single_value 111 ::get_initial_specified_value()); 112 } 113 % endfor 114 Ok(()) 115 } else { 116 Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)) 117 } 118 })?; 119 120 Ok(expanded! { 121 background_color: background_color.unwrap_or(Color::transparent()), 122 background_image: background_image, 123 background_position_x: background_position_x, 124 background_position_y: background_position_y, 125 background_repeat: background_repeat, 126 background_attachment: background_attachment, 127 background_size: background_size, 128 background_origin: background_origin, 129 background_clip: background_clip, 130 }) 131 } 132 133 impl<'a> ToCss for LonghandsToSerialize<'a> { to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: fmt::Write134 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 if i == len - 1 { 160 self.background_color.to_css(dest)?; 161 dest.write_str(" ")?; 162 } 163 164 image.to_css(dest)?; 165 % for name in "repeat attachment".split(): 166 dest.write_str(" ")?; 167 ${name}.to_css(dest)?; 168 % endfor 169 170 dest.write_str(" ")?; 171 Position { 172 horizontal: position_x.clone(), 173 vertical: position_y.clone() 174 }.to_css(dest)?; 175 176 if *size != background_size::single_value::get_initial_specified_value() { 177 dest.write_str(" / ")?; 178 size.to_css(dest)?; 179 } 180 181 if *origin != Origin::PaddingBox || *clip != Clip::BorderBox { 182 dest.write_str(" ")?; 183 origin.to_css(dest)?; 184 if *clip != From::from(*origin) { 185 dest.write_str(" ")?; 186 clip.to_css(dest)?; 187 } 188 } 189 } 190 191 Ok(()) 192 } 193 } 194 </%helpers:shorthand> 195 196 <%helpers:shorthand name="background-position" 197 sub_properties="background-position-x background-position-y" 198 spec="https://drafts.csswg.org/css-backgrounds-4/#the-background-position"> 199 use properties::longhands::{background_position_x, background_position_y}; 200 use values::specified::AllowQuirks; 201 use values::specified::position::Position; 202 parse_value<'i, 't>(context: &ParserContext, input: &mut Parser<'i, 't>) -> Result<Longhands, ParseError<'i>>203 pub fn parse_value<'i, 't>(context: &ParserContext, input: &mut Parser<'i, 't>) 204 -> Result<Longhands, ParseError<'i>> { 205 // Vec grows from 0 to 4 by default on first push(). So allocate with 206 // capacity 1, so in the common case of only one item we don't way 207 // overallocate. Note that we always push at least one item if parsing 208 // succeeds. 209 let mut position_x = background_position_x::SpecifiedValue(Vec::with_capacity(1)); 210 let mut position_y = background_position_y::SpecifiedValue(Vec::with_capacity(1)); 211 let mut any = false; 212 213 input.parse_comma_separated(|input| { 214 let value = Position::parse_quirky(context, input, AllowQuirks::Yes)?; 215 position_x.0.push(value.horizontal); 216 position_y.0.push(value.vertical); 217 any = true; 218 Ok(()) 219 })?; 220 if !any { 221 return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)); 222 } 223 224 Ok(expanded! { 225 background_position_x: position_x, 226 background_position_y: position_y, 227 }) 228 } 229 230 impl<'a> ToCss for LonghandsToSerialize<'a> { to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: fmt::Write231 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: fmt::Write { 232 let len = self.background_position_x.0.len(); 233 if len == 0 || len != self.background_position_y.0.len() { 234 return Ok(()); 235 } 236 for i in 0..len { 237 Position { 238 horizontal: self.background_position_x.0[i].clone(), 239 vertical: self.background_position_y.0[i].clone() 240 }.to_css(dest)?; 241 242 if i < len - 1 { 243 dest.write_str(", ")?; 244 } 245 } 246 Ok(()) 247 } 248 } 249 </%helpers:shorthand> 250