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