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 // `data` comes from components/style/properties.mako.rs; see build.rs for more details.
6 
7 <%!
8     from data import to_camel_case, to_camel_case_lower
9     from data import Keyword
10 %>
11 <%namespace name="helpers" file="/helpers.mako.rs" />
12 
13 use crate::Atom;
14 use app_units::Au;
15 use crate::computed_value_flags::*;
16 use crate::custom_properties::CustomPropertiesMap;
17 use crate::gecko_bindings::bindings;
18 % for style_struct in data.style_structs:
19 use crate::gecko_bindings::structs::${style_struct.gecko_ffi_name};
20 use crate::gecko_bindings::bindings::Gecko_Construct_Default_${style_struct.gecko_ffi_name};
21 use crate::gecko_bindings::bindings::Gecko_CopyConstruct_${style_struct.gecko_ffi_name};
22 use crate::gecko_bindings::bindings::Gecko_Destroy_${style_struct.gecko_ffi_name};
23 % endfor
24 use crate::gecko_bindings::bindings::Gecko_CopyCounterStyle;
25 use crate::gecko_bindings::bindings::Gecko_EnsureImageLayersLength;
26 use crate::gecko_bindings::bindings::Gecko_nsStyleFont_SetLang;
27 use crate::gecko_bindings::bindings::Gecko_nsStyleFont_CopyLangFrom;
28 use crate::gecko_bindings::structs;
29 use crate::gecko_bindings::structs::nsCSSPropertyID;
30 use crate::gecko_bindings::structs::mozilla::PseudoStyleType;
31 use crate::gecko::data::PerDocumentStyleData;
32 use crate::gecko::values::round_border_to_device_pixels;
33 use crate::logical_geometry::WritingMode;
34 use crate::media_queries::Device;
35 use crate::properties::longhands;
36 use crate::rule_tree::StrongRuleNode;
37 use crate::selector_parser::PseudoElement;
38 use servo_arc::{Arc, RawOffsetArc, UniqueArc};
39 use std::mem::{forget, MaybeUninit};
40 use std::{cmp, ops, ptr};
41 use crate::values::{self, CustomIdent, Either, KeyframesName, None_};
is_empty(&self) -> bool42 use crate::values::computed::{Percentage, TransitionProperty};
43 use crate::values::computed::BorderStyle;
44 use crate::values::computed::font::FontSize;
45 use crate::values::generics::column::ColumnCount;
46 
47 
48 pub mod style_structs {
49     % for style_struct in data.style_structs:
50     pub use super::${style_struct.gecko_struct_name} as ${style_struct.name};
51 
52     unsafe impl Send for ${style_struct.name} {}
53     unsafe impl Sync for ${style_struct.name} {}
54     % endfor
55 
56 }
57 
58 /// FIXME(emilio): This is completely duplicated with the other properties code.
59 pub type ComputedValuesInner = structs::ServoComputedData;
60 
61 #[repr(C)]
62 pub struct ComputedValues(structs::mozilla::ComputedStyle);
63 
64 impl ComputedValues {
65     #[inline]
66     pub (crate) fn as_gecko_computed_style(&self) -> &structs::ComputedStyle {
67         &self.0
68     }
69 
70     pub fn new(
71         pseudo: Option<<&PseudoElement>,
72         custom_properties: Option<Arc<CustomPropertiesMap>>,
73         writing_mode: WritingMode,
74         flags: ComputedValueFlags,
75         rules: Option<StrongRuleNode>,
76         visited_style: Option<Arc<ComputedValues>>,
77         % for style_struct in data.style_structs:
78         ${style_struct.ident}: Arc<style_structs::${style_struct.name}>,
79         % endfor
80     ) -> Arc<Self> {
81         ComputedValuesInner::new(
82             custom_properties,
83             writing_mode,
84             flags,
85             rules,
important(self) -> bool86             visited_style,
87             % for style_struct in data.style_structs:
88             ${style_struct.ident},
89             % endfor
90         ).to_outer(pseudo)
91     }
92 
93     pub fn default_values(doc: &structs::Document) -> Arc<Self> {
94         ComputedValuesInner::new(
95             /* custom_properties = */ None,
96             /* writing_mode = */ WritingMode::empty(), // FIXME(bz): This seems dubious
97             ComputedValueFlags::empty(),
98             /* rules = */ None,
99             /* visited_style = */ None,
100             % for style_struct in data.style_structs:
101             style_structs::${style_struct.name}::default(doc),
102             % endfor
103         ).to_outer(None)
104     }
105 
106     #[inline]
107     pub fn is_pseudo_style(&self) -> bool {
108         self.0.mPseudoType != PseudoStyleType::NotPseudo
109     }
110 
111     #[inline]
112     pub fn pseudo(&self) -> Option<PseudoElement> {
113         if !self.is_pseudo_style() {
114             return None;
default() -> Self115         }
116         PseudoElement::from_pseudo_type(self.0.mPseudoType)
117     }
118 
119     #[inline]
120     pub fn is_first_line_style(&self) -> bool {
121         self.pseudo() == Some(PseudoElement::FirstLine)
122     }
123 
new(declarations: &'a [PropertyDeclaration], important: &'a SmallBitVec) -> Self124     /// Returns true if the display property is changed from 'none' to others.
125     pub fn is_display_property_changed_from_none(
126         &self,
127         old_values: Option<<&ComputedValues>
128     ) -> bool {
129         use crate::properties::longhands::display::computed_value::T as Display;
130 
131         old_values.map_or(false, |old| {
132             let old_display_style = old.get_box().clone_display();
133             let new_display_style = self.get_box().clone_display();
134             old_display_style == Display::None &&
135             new_display_style != Display::None
136         })
137     }
138 
139 }
140 
141 impl Drop for ComputedValues {
142     fn drop(&mut self) {
143         unsafe {
144             bindings::Gecko_ComputedStyle_Destroy(&mut self.0);
145         }
146     }
147 }
148 
size_hint(&self) -> (usize, Option<usize>)149 unsafe impl Sync for ComputedValues {}
150 unsafe impl Send for ComputedValues {}
151 
152 impl Clone for ComputedValues {
153     fn clone(&self) -> Self {
154         unreachable!()
155     }
next_back(&mut self) -> Option<Self::Item>156 }
157 
158 impl Clone for ComputedValuesInner {
159     fn clone(&self) -> Self {
160         ComputedValuesInner {
161             % for style_struct in data.style_structs:
162                 ${style_struct.gecko_name}: self.${style_struct.gecko_name}.clone(),
163             % endfor
164             custom_properties: self.custom_properties.clone(),
165             writing_mode: self.writing_mode.clone(),
166             flags: self.flags.clone(),
167             rules: self.rules.clone(),
168             visited_style: self.visited_style.clone(),
169         }
170     }
171 }
172 
173 impl ComputedValuesInner {
174     pub fn new(
175         custom_properties: Option<Arc<CustomPropertiesMap>>,
176         writing_mode: WritingMode,
177         flags: ComputedValueFlags,
178         rules: Option<StrongRuleNode>,
179         visited_style: Option<Arc<ComputedValues>>,
new( declarations: &'a PropertyDeclarationBlock, context: &'cx mut Context<'cx_a>, default_values: &'a ComputedValues, extra_custom_properties: Option<&'a Arc<crate::custom_properties::CustomPropertiesMap>>, ) -> AnimationValueIterator<'a, 'cx, 'cx_a>180         % for style_struct in data.style_structs:
181         ${style_struct.ident}: Arc<style_structs::${style_struct.name}>,
182         % endfor
183     ) -> Self {
184         Self {
185             custom_properties,
186             writing_mode,
187             rules,
188             visited_style: visited_style.map(Arc::into_raw_offset),
189             flags,
190             % for style_struct in data.style_structs:
191             ${style_struct.gecko_name}: Arc::into_raw_offset(${style_struct.ident}),
192             % endfor
193         }
194     }
195 
196     fn to_outer(
197         self,
next(&mut self) -> Option<Self::Item>198         pseudo: Option<<&PseudoElement>,
199     ) -> Arc<ComputedValues> {
200         let pseudo_ty = match pseudo {
201             Some(p) => p.pseudo_type(),
202             None => structs::PseudoStyleType::NotPseudo,
203         };
204         unsafe {
205             let mut arc = UniqueArc::<ComputedValues>::new_uninit();
206             bindings::Gecko_ComputedStyle_Init(
207                 arc.as_mut_ptr() as *mut _,
208                 &self,
209                 pseudo_ty,
210             );
211             // We're simulating move semantics by having C++ do a memcpy and then forgetting
212             // it on this end.
213             forget(self);
214             UniqueArc::assume_init(arc).shareable()
215         }
216     }
217 }
218 
219 impl ops::Deref for ComputedValues {
220     type Target = ComputedValuesInner;
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result221     fn deref(&self) -> &ComputedValuesInner {
222         &self.0.mSource
223     }
224 }
225 
226 impl ops::DerefMut for ComputedValues {
227     fn deref_mut(&mut self) -> &mut ComputedValuesInner {
228         &mut self.0.mSource
len(&self) -> usize229     }
230 }
231 
232 impl ComputedValuesInner {
233     /// Returns true if the value of the `content` property would make a
234     /// pseudo-element not rendered.
235     #[inline]
236     pub fn ineffective_content_property(&self) -> bool {
237         self.get_counters().ineffective_content_property()
238     }
239 
240     % for style_struct in data.style_structs:
241     #[inline]
242     pub fn clone_${style_struct.name_lower}(&self) -> Arc<style_structs::${style_struct.name}> {
243         Arc::from_raw_offset(self.${style_struct.gecko_name}.clone())
244     }
245     #[inline]
246     pub fn get_${style_struct.name_lower}(&self) -> &style_structs::${style_struct.name} {
247         &self.${style_struct.gecko_name}
248     }
249 
250 
251     pub fn ${style_struct.name_lower}_arc(&self) -> &RawOffsetArc<style_structs::${style_struct.name}> {
252         &self.${style_struct.gecko_name}
253     }
254 
255     #[inline]
256     pub fn mutate_${style_struct.name_lower}(&mut self) -> &mut style_structs::${style_struct.name} {
257         RawOffsetArc::make_mut(&mut self.${style_struct.gecko_name})
258     }
259     % endfor
260 
261     /// Gets the raw visited style. Useful for memory reporting.
262     pub fn get_raw_visited_style(&self) -> &Option<RawOffsetArc<ComputedValues>> {
263         &self.visited_style
264     }
265 }
266 
267 <%def name="declare_style_struct(style_struct)">
268 pub use crate::gecko_bindings::structs::mozilla::Gecko${style_struct.gecko_name} as ${style_struct.gecko_struct_name};
269 impl ${style_struct.gecko_struct_name} {
declaration_importance_iter(&self) -> DeclarationImportanceIterator270     pub fn gecko(&self) -> &${style_struct.gecko_ffi_name} {
271         &self.gecko
272     }
273     pub fn gecko_mut(&mut self) -> &mut ${style_struct.gecko_ffi_name} {
274         &mut self.gecko
275     }
normal_declaration_iter<'a>( &'a self, ) -> impl DoubleEndedIterator<Item = &'a PropertyDeclaration>276 }
277 </%def>
278 
279 <%def name="impl_simple_setter(ident, gecko_ffi_name)">
280     #[allow(non_snake_case)]
281     pub fn set_${ident}(&mut self, v: longhands::${ident}::computed_value::T) {
282         ${set_gecko_property(gecko_ffi_name, "From::from(v)")}
283     }
284 </%def>
285 
to_animation_value_iter<'a, 'cx, 'cx_a: 'cx>( &'a self, context: &'cx mut Context<'cx_a>, default_values: &'a ComputedValues, extra_custom_properties: Option<&'a Arc<crate::custom_properties::CustomPropertiesMap>>, ) -> AnimationValueIterator<'a, 'cx, 'cx_a>286 <%def name="impl_simple_clone(ident, gecko_ffi_name)">
287     #[allow(non_snake_case)]
288     pub fn clone_${ident}(&self) -> longhands::${ident}::computed_value::T {
289         From::from(self.gecko.${gecko_ffi_name}.clone())
290     }
291 </%def>
292 
293 <%def name="impl_simple_copy(ident, gecko_ffi_name, *kwargs)">
294     #[allow(non_snake_case)]
295     pub fn copy_${ident}_from(&mut self, other: &Self) {
296         self.gecko.${gecko_ffi_name} = other.gecko.${gecko_ffi_name}.clone();
297     }
298 
299     #[allow(non_snake_case)]
any_important(&self) -> bool300     pub fn reset_${ident}(&mut self, other: &Self) {
301         self.copy_${ident}_from(other)
302     }
303 </%def>
304 
305 <%!
306 def get_gecko_property(ffi_name, self_param = "self"):
307     return "%s.gecko.%s" % (self_param, ffi_name)
308 
any_normal(&self) -> bool309 def set_gecko_property(ffi_name, expr):
310     return "self.gecko.%s = %s;" % (ffi_name, expr)
311 %>
312 
313 <%def name="impl_keyword_setter(ident, gecko_ffi_name, keyword, cast_type='u8')">
314     #[allow(non_snake_case)]
315     pub fn set_${ident}(&mut self, v: longhands::${ident}::computed_value::T) {
316         use crate::properties::longhands::${ident}::computed_value::T as Keyword;
317         // FIXME(bholley): Align binary representations and ditch |match| for cast + static_asserts
318         let result = match v {
319             % for value in keyword.values_for('gecko'):
320                 Keyword::${to_camel_case(value)} =>
contains_any_reset(&self) -> bool321                     structs::${keyword.gecko_constant(value)} ${keyword.maybe_cast(cast_type)},
322             % endfor
323         };
324         ${set_gecko_property(gecko_ffi_name, "result")}
325     }
326 </%def>
327 
longhands(&self) -> &LonghandIdSet328 <%def name="impl_keyword_clone(ident, gecko_ffi_name, keyword, cast_type='u8')">
329     #[allow(non_snake_case)]
330     pub fn clone_${ident}(&self) -> longhands::${ident}::computed_value::T {
331         use crate::properties::longhands::${ident}::computed_value::T as Keyword;
332         // FIXME(bholley): Align binary representations and ditch |match| for cast + static_asserts
333 
334         // Some constant macros in the gecko are defined as negative integer(e.g. font-stretch).
335         // And they are convert to signed integer in Rust bindings. We need to cast then
336         // as signed type when we have both signed/unsigned integer in order to use them
get( &self, property: PropertyDeclarationId, ) -> Option<(&PropertyDeclaration, Importance)>337         // as match's arms.
338         // Also, to use same implementation here we use casted constant if we have only singed values.
339         % if keyword.gecko_enum_prefix is None:
340         % for value in keyword.values_for('gecko'):
341         const ${keyword.casted_constant_name(value, cast_type)} : ${cast_type} =
342             structs::${keyword.gecko_constant(value)} as ${cast_type};
343         % endfor
344 
345         match ${get_gecko_property(gecko_ffi_name)} as ${cast_type} {
346             % for value in keyword.values_for('gecko'):
347             ${keyword.casted_constant_name(value, cast_type)} => Keyword::${to_camel_case(value)},
348             % endfor
349             % if keyword.gecko_inexhaustive:
350             _ => panic!("Found unexpected value in style struct for ${ident} property"),
351             % endif
352         }
shorthand_to_css( &self, shorthand: ShorthandId, dest: &mut CssStringWriter, ) -> fmt::Result353         % else:
354         match ${get_gecko_property(gecko_ffi_name)} {
355             % for value in keyword.values_for('gecko'):
356             structs::${keyword.gecko_constant(value)} => Keyword::${to_camel_case(value)},
357             % endfor
358             % if keyword.gecko_inexhaustive:
359             _ => panic!("Found unexpected value in style struct for ${ident} property"),
360             % endif
361         }
362         % endif
363     }
364 </%def>
365 
366 <%def name="impl_keyword(ident, gecko_ffi_name, keyword, cast_type='u8', **kwargs)">
367 <%call expr="impl_keyword_setter(ident, gecko_ffi_name, keyword, cast_type, **kwargs)"></%call>
368 <%call expr="impl_simple_copy(ident, gecko_ffi_name, **kwargs)"></%call>
369 <%call expr="impl_keyword_clone(ident, gecko_ffi_name, keyword, cast_type)"></%call>
370 </%def>
371 
372 <%def name="impl_simple(ident, gecko_ffi_name)">
373 <%call expr="impl_simple_setter(ident, gecko_ffi_name)"></%call>
374 <%call expr="impl_simple_copy(ident, gecko_ffi_name)"></%call>
375 <%call expr="impl_simple_clone(ident, gecko_ffi_name)"></%call>
376 </%def>
377 
378 <%def name="impl_non_negative_length(ident, gecko_ffi_name, inherit_from=None,
379                                      round_to_pixels=False)">
380     #[allow(non_snake_case)]
381     pub fn set_${ident}(&mut self, v: longhands::${ident}::computed_value::T) {
382         let value = {
383             % if round_to_pixels:
384             let au_per_device_px = Au(self.gecko.mTwipsPerPixel);
385             round_border_to_device_pixels(Au::from(v), au_per_device_px).0
386             % else:
387             v.0.to_i32_au()
388             % endif
389         };
390 
391         % if inherit_from:
392         self.gecko.${inherit_from} = value;
393         % endif
394         self.gecko.${gecko_ffi_name} = value;
395     }
396 
397     #[allow(non_snake_case)]
property_value_to_css( &self, property: &PropertyId, dest: &mut CssStringWriter, ) -> fmt::Result398     pub fn copy_${ident}_from(&mut self, other: &Self) {
399         % if inherit_from:
400         self.gecko.${inherit_from} = other.gecko.${inherit_from};
401         // NOTE: This is needed to easily handle the `unset` and `initial`
402         // keywords, which are implemented calling this function.
403         //
404         // In practice, this means that we may have an incorrect value here, but
405         // we'll adjust that properly in the style fixup phase.
406         //
407         // FIXME(emilio): We could clean this up a bit special-casing the reset_
408         // function below.
409         self.gecko.${gecko_ffi_name} = other.gecko.${inherit_from};
410         % else:
411         self.gecko.${gecko_ffi_name} = other.gecko.${gecko_ffi_name};
412         % endif
413     }
414 
415     #[allow(non_snake_case)]
416     pub fn reset_${ident}(&mut self, other: &Self) {
417         self.copy_${ident}_from(other)
418     }
419 
420     #[allow(non_snake_case)]
property_priority(&self, property: &PropertyId) -> Importance421     pub fn clone_${ident}(&self) -> longhands::${ident}::computed_value::T {
422         Au(self.gecko.${gecko_ffi_name}).into()
423     }
424 </%def>
425 
426 <%def name="impl_split_style_coord(ident, gecko_ffi_name, index)">
427     #[allow(non_snake_case)]
428     pub fn set_${ident}(&mut self, v: longhands::${ident}::computed_value::T) {
429         self.gecko.${gecko_ffi_name}.${index} = v;
430     }
431     #[allow(non_snake_case)]
432     pub fn copy_${ident}_from(&mut self, other: &Self) {
433         self.gecko.${gecko_ffi_name}.${index} =
434             other.gecko.${gecko_ffi_name}.${index}.clone();
435     }
436     #[allow(non_snake_case)]
437     pub fn reset_${ident}(&mut self, other: &Self) {
438         self.copy_${ident}_from(other)
439     }
440 
441     #[allow(non_snake_case)]
442     pub fn clone_${ident}(&self) -> longhands::${ident}::computed_value::T {
443         self.gecko.${gecko_ffi_name}.${index}.clone()
444     }
445 </%def>
446 
447 <%def name="copy_sides_style_coord(ident)">
448     <% gecko_ffi_name = "m" + to_camel_case(ident) %>
is_definitely_new(&self, decl: &PropertyDeclaration) -> bool449     #[allow(non_snake_case)]
450     pub fn copy_${ident}_from(&mut self, other: &Self) {
451         % for side in SIDES:
452             self.gecko.${gecko_ffi_name}.data_at_mut(${side.index})
453                 .copy_from(&other.gecko.${gecko_ffi_name}.data_at(${side.index}));
454         % endfor
455         ${ caller.body() }
456     }
457 
458     #[allow(non_snake_case)]
extend( &mut self, mut drain: SourcePropertyDeclarationDrain, importance: Importance, ) -> bool459     pub fn reset_${ident}(&mut self, other: &Self) {
460         self.copy_${ident}_from(other)
461     }
462 </%def>
463 
464 <%def name="impl_corner_style_coord(ident, gecko_ffi_name, corner)">
465     #[allow(non_snake_case)]
466     pub fn set_${ident}(&mut self, v: longhands::${ident}::computed_value::T) {
467         self.gecko.${gecko_ffi_name}.${corner} = v;
468     }
469     #[allow(non_snake_case)]
470     pub fn copy_${ident}_from(&mut self, other: &Self) {
471         self.gecko.${gecko_ffi_name}.${corner} =
472             other.gecko.${gecko_ffi_name}.${corner}.clone();
473     }
474     #[allow(non_snake_case)]
475     pub fn reset_${ident}(&mut self, other: &Self) {
476         self.copy_${ident}_from(other)
477     }
478     #[allow(non_snake_case)]
479     pub fn clone_${ident}(&self) -> longhands::${ident}::computed_value::T {
480         self.gecko.${gecko_ffi_name}.${corner}.clone()
481     }
482 </%def>
483 
484 <%def name="impl_logical(name, **kwargs)">
485     ${helpers.logical_setter(name)}
486 </%def>
487 
488 <%def name="impl_style_struct(style_struct)">
489 impl ${style_struct.gecko_struct_name} {
490     #[allow(dead_code, unused_variables)]
491     pub fn default(document: &structs::Document) -> Arc<Self> {
push(&mut self, declaration: PropertyDeclaration, importance: Importance) -> bool492         unsafe {
493             let mut result = UniqueArc::<Self>::new_uninit();
494             // FIXME(bug 1595895): Zero the memory to keep valgrind happy, but
495             // these looks like Valgrind false-positives at a quick glance.
496             ptr::write_bytes::<Self>(result.as_mut_ptr(), 0, 1);
497             Gecko_Construct_Default_${style_struct.gecko_ffi_name}(
498                 result.as_mut_ptr() as *mut _,
499                 document,
500             );
501             UniqueArc::assume_init(result).shareable()
502         }
503     }
504 }
505 impl Drop for ${style_struct.gecko_struct_name} {
506     fn drop(&mut self) {
507         unsafe {
508             Gecko_Destroy_${style_struct.gecko_ffi_name}(&mut *self.gecko);
509         }
510     }
511 }
512 impl Clone for ${style_struct.gecko_struct_name} {
513     fn clone(&self) -> Self {
514         unsafe {
515             let mut result = MaybeUninit::<Self>::uninit();
516             // FIXME(bug 1595895): Zero the memory to keep valgrind happy, but
517             // these looks like Valgrind false-positives at a quick glance.
518             ptr::write_bytes::<Self>(result.as_mut_ptr(), 0, 1);
519             Gecko_CopyConstruct_${style_struct.gecko_ffi_name}(result.as_mut_ptr() as *mut _, &*self.gecko);
520             result.assume_init()
521         }
522     }
523 }
524 
525 </%def>
526 
527 <%def name="impl_simple_type_with_conversion(ident, gecko_ffi_name)">
528     #[allow(non_snake_case)]
529     pub fn set_${ident}(&mut self, v: longhands::${ident}::computed_value::T) {
530         self.gecko.${gecko_ffi_name} = From::from(v)
531     }
prepare_for_update( &self, source_declarations: &SourcePropertyDeclaration, importance: Importance, updates: &mut SourcePropertyDeclarationUpdate, ) -> bool532 
533     <% impl_simple_copy(ident, gecko_ffi_name) %>
534 
535     #[allow(non_snake_case)]
536     pub fn clone_${ident}(&self) -> longhands::${ident}::computed_value::T {
537         From::from(self.gecko.${gecko_ffi_name})
538     }
539 </%def>
540 
541 <%def name="impl_font_settings(ident, gecko_type, tag_type, value_type, gecko_value_type)">
542     <%
543     gecko_ffi_name = to_camel_case_lower(ident)
544     %>
545 
546     pub fn set_${ident}(&mut self, v: longhands::${ident}::computed_value::T) {
547         let iter = v.0.iter().map(|other| structs::${gecko_type} {
548             mTag: other.tag.0,
549             mValue: other.value as ${gecko_value_type},
550         });
551         self.gecko.mFont.${gecko_ffi_name}.assign_from_iter_pod(iter);
552     }
553 
554     pub fn copy_${ident}_from(&mut self, other: &Self) {
555         let iter = other.gecko.mFont.${gecko_ffi_name}.iter().map(|s| *s);
556         self.gecko.mFont.${gecko_ffi_name}.assign_from_iter_pod(iter);
557     }
558 
559     pub fn reset_${ident}(&mut self, other: &Self) {
560         self.copy_${ident}_from(other)
561     }
562 
563     pub fn clone_${ident}(&self) -> longhands::${ident}::computed_value::T {
564         use crate::values::generics::font::{FontSettings, FontTag, ${tag_type}};
565 
566         FontSettings(
567             self.gecko.mFont.${gecko_ffi_name}.iter().map(|gecko_font_setting| {
568                 ${tag_type} {
569                     tag: FontTag(gecko_font_setting.mTag),
570                     value: gecko_font_setting.mValue as ${value_type},
571                 }
572             }).collect::<Vec<_>>().into_boxed_slice()
573         )
574     }
575 </%def>
576 
577 <%def name="impl_trait(style_struct_name, skip_longhands='')">
578 <%
579     style_struct = next(x for x in data.style_structs if x.name == style_struct_name)
580     longhands = [x for x in style_struct.longhands
581                 if not (skip_longhands == "*" or x.name in skip_longhands.split())]
582 
583     def longhand_method(longhand):
584         args = dict(ident=longhand.ident, gecko_ffi_name=longhand.gecko_ffi_name)
585 
586         # get the method and pass additional keyword or type-specific arguments
587         if longhand.logical:
588             method = impl_logical
589             args.update(name=longhand.name)
590         elif longhand.keyword:
591             method = impl_keyword
592             args.update(keyword=longhand.keyword)
593             if "font" in longhand.ident:
594                 args.update(cast_type=longhand.cast_type)
595         else:
596             method = impl_simple
597 
598         method(**args)
599 %>
600 impl ${style_struct.gecko_struct_name} {
601     /*
602      * Manually-Implemented Methods.
603      */
604     ${caller.body().strip()}
605 
606     /*
607      * Auto-Generated Methods.
608      */
609     <%
610     for longhand in longhands:
611         longhand_method(longhand)
612     %>
613 }
614 </%def>
615 
616 <%!
617 class Side(object):
618     def __init__(self, name, index):
619         self.name = name
620         self.ident = name.lower()
621         self.index = index
622 
623 SIDES = [Side("Top", 0), Side("Right", 1), Side("Bottom", 2), Side("Left", 3)]
624 CORNERS = ["top_left", "top_right", "bottom_right", "bottom_left"]
625 %>
626 
627 #[allow(dead_code)]
628 fn static_assert() {
629     // Note: using the above technique with an enum hits a rust bug when |structs| is in a different crate.
630     % for side in SIDES:
631     { const DETAIL: u32 = [0][(structs::Side::eSide${side.name} as usize != ${side.index}) as usize]; let _ = DETAIL; }
update( &mut self, drain: SourcePropertyDeclarationDrain, importance: Importance, updates: &mut SourcePropertyDeclarationUpdate, )632     % endfor
633 }
634 
635 
636 <% skip_border_longhands = " ".join(["border-{0}-{1}".format(x.ident, y)
637                                      for x in SIDES
638                                      for y in ["color", "style", "width"]] +
639                                     ["border-{0}-radius".format(x.replace("_", "-"))
640                                      for x in CORNERS]) %>
641 
642 <%self:impl_trait style_struct_name="Border"
643                   skip_longhands="${skip_border_longhands} border-image-repeat">
644     % for side in SIDES:
645     pub fn set_border_${side.ident}_style(&mut self, v: BorderStyle) {
646         self.gecko.mBorderStyle[${side.index}] = v;
647 
648         // This is needed because the initial mComputedBorder value is set to
649         // zero.
650         //
651         // In order to compute stuff, we start from the initial struct, and keep
652         // going down the tree applying properties.
653         //
654         // That means, effectively, that when we set border-style to something
655         // non-hidden, we should use the initial border instead.
656         //
657         // Servo stores the initial border-width in the initial struct, and then
658         // adjusts as needed in the fixup phase. This means that the initial
659         // struct is technically not valid without fixups, and that you lose
660         // pretty much any sharing of the initial struct, which is kind of
661         // unfortunate.
662         //
663         // Gecko has two fields for this, one that stores the "specified"
664         // border, and other that stores the actual computed one. That means
665         // that when we set border-style, border-width may change and we need to
666         // sync back to the specified one. This is what this function does.
667         //
668         // Note that this doesn't impose any dependency in the order of
669         // computation of the properties. This is only relevant if border-style
670         // is specified, but border-width isn't. If border-width is specified at
671         // some point, the two mBorder and mComputedBorder fields would be the
672         // same already.
673         //
674         // Once we're here, we know that we'll run style fixups, so it's fine to
675         // just copy the specified border here, we'll adjust it if it's
676         // incorrect later.
677         self.gecko.mComputedBorder.${side.ident} = self.gecko.mBorder.${side.ident};
678     }
679 
680     pub fn copy_border_${side.ident}_style_from(&mut self, other: &Self) {
681         self.gecko.mBorderStyle[${side.index}] = other.gecko.mBorderStyle[${side.index}];
682         self.gecko.mComputedBorder.${side.ident} = self.gecko.mBorder.${side.ident};
683     }
684 
685     pub fn reset_border_${side.ident}_style(&mut self, other: &Self) {
686         self.copy_border_${side.ident}_style_from(other);
687     }
688 
689     #[inline]
690     pub fn clone_border_${side.ident}_style(&self) -> BorderStyle {
691         self.gecko.mBorderStyle[${side.index}]
692     }
693 
694     <% impl_simple("border_%s_color" % side.ident, "mBorder%sColor" % side.name) %>
695 
696     <% impl_non_negative_length("border_%s_width" % side.ident,
697                                 "mComputedBorder.%s" % side.ident,
698                                 inherit_from="mBorder.%s" % side.ident,
699                                 round_to_pixels=True) %>
700 
701     pub fn border_${side.ident}_has_nonzero_width(&self) -> bool {
702         self.gecko.mComputedBorder.${side.ident} != 0
703     }
704     % endfor
705 
706     % for corner in CORNERS:
707     <% impl_corner_style_coord("border_%s_radius" % corner,
708                                "mBorderRadius",
709                                corner) %>
710     % endfor
711 
712     <%
713     border_image_repeat_keywords = ["Stretch", "Repeat", "Round", "Space"]
714     %>
715 
716     pub fn set_border_image_repeat(&mut self, v: longhands::border_image_repeat::computed_value::T) {
717         use crate::values::specified::border::BorderImageRepeatKeyword;
718         use crate::gecko_bindings::structs::StyleBorderImageRepeat;
719 
720         % for i, side in enumerate(["H", "V"]):
721             self.gecko.mBorderImageRepeat${side} = match v.${i} {
722                 % for keyword in border_image_repeat_keywords:
723                 BorderImageRepeatKeyword::${keyword} => StyleBorderImageRepeat::${keyword},
724                 % endfor
725             };
726         % endfor
727     }
728 
729     pub fn copy_border_image_repeat_from(&mut self, other: &Self) {
730         self.gecko.mBorderImageRepeatH = other.gecko.mBorderImageRepeatH;
first_declaration_to_remove(&self, property: &PropertyId) -> Option<usize>731         self.gecko.mBorderImageRepeatV = other.gecko.mBorderImageRepeatV;
732     }
733 
734     pub fn reset_border_image_repeat(&mut self, other: &Self) {
735         self.copy_border_image_repeat_from(other)
736     }
737 
738     pub fn clone_border_image_repeat(&self) -> longhands::border_image_repeat::computed_value::T {
739         use crate::values::specified::border::BorderImageRepeatKeyword;
740         use crate::gecko_bindings::structs::StyleBorderImageRepeat;
741 
742         % for side in ["H", "V"]:
743         let servo_${side.lower()} = match self.gecko.mBorderImageRepeat${side} {
744             % for keyword in border_image_repeat_keywords:
remove_declaration_at(&mut self, i: usize)745             StyleBorderImageRepeat::${keyword} => BorderImageRepeatKeyword::${keyword},
746             % endfor
747         };
748         % endfor
749         longhands::border_image_repeat::computed_value::T(servo_h, servo_v)
750     }
751 </%self:impl_trait>
752 
753 <% skip_scroll_margin_longhands = " ".join(["scroll-margin-%s" % x.ident for x in SIDES]) %>
754 <% skip_margin_longhands = " ".join(["margin-%s" % x.ident for x in SIDES]) %>
755 <%self:impl_trait style_struct_name="Margin"
756                   skip_longhands="${skip_margin_longhands}
757                                   ${skip_scroll_margin_longhands}">
758 
759     % for side in SIDES:
760     <% impl_split_style_coord("margin_%s" % side.ident,
remove_property(&mut self, property: &PropertyId, first_declaration: usize)761                               "mMargin",
762                               side.index) %>
763     <% impl_split_style_coord("scroll_margin_%s" % side.ident,
764                               "mScrollMargin",
765                               side.index) %>
766     % endfor
767 </%self:impl_trait>
768 
769 <% skip_scroll_padding_longhands = " ".join(["scroll-padding-%s" % x.ident for x in SIDES]) %>
770 <% skip_padding_longhands = " ".join(["padding-%s" % x.ident for x in SIDES]) %>
771 <%self:impl_trait style_struct_name="Padding"
772                   skip_longhands="${skip_padding_longhands}
773                                   ${skip_scroll_padding_longhands}">
774 
775     % for side in SIDES:
776     <% impl_split_style_coord("padding_%s" % side.ident,
777                               "mPadding",
778                               side.index) %>
779     <% impl_split_style_coord("scroll_padding_%s" % side.ident, "mScrollPadding", side.index) %>
780     % endfor
781 </%self:impl_trait>
782 
783 <%self:impl_trait style_struct_name="Page">
784 </%self:impl_trait>
785 
786 <% skip_position_longhands = " ".join(x.ident for x in SIDES) %>
787 <%self:impl_trait style_struct_name="Position"
788                   skip_longhands="${skip_position_longhands}
789                                   masonry-auto-flow">
790     % for side in SIDES:
791     <% impl_split_style_coord(side.ident, "mOffset", side.index) %>
792     % endfor
793     pub fn set_computed_justify_items(&mut self, v: values::specified::JustifyItems) {
794         debug_assert_ne!(v.0, crate::values::specified::align::AlignFlags::LEGACY);
795         self.gecko.mJustifyItems.computed = v;
796     }
797 
798     ${impl_simple_type_with_conversion("masonry_auto_flow", "mMasonryAutoFlow")}
799 </%self:impl_trait>
800 
801 <%self:impl_trait style_struct_name="Outline"
802                   skip_longhands="outline-style outline-width">
803 
804     pub fn set_outline_style(&mut self, v: longhands::outline_style::computed_value::T) {
805         self.gecko.mOutlineStyle = v;
806         // NB: This is needed to correctly handling the initial value of
807         // outline-width when outline-style changes, see the
808         // update_border_${side.ident} comment for more details.
809         self.gecko.mActualOutlineWidth = self.gecko.mOutlineWidth;
810     }
811 
812     pub fn copy_outline_style_from(&mut self, other: &Self) {
813         // FIXME(emilio): Why doesn't this need to reset mActualOutlineWidth?
814         // Looks fishy.
815         self.gecko.mOutlineStyle = other.gecko.mOutlineStyle;
816     }
817 
818     pub fn reset_outline_style(&mut self, other: &Self) {
819         self.copy_outline_style_from(other)
820     }
821 
822     pub fn clone_outline_style(&self) -> longhands::outline_style::computed_value::T {
823         self.gecko.mOutlineStyle.clone()
824     }
825 
826     <% impl_non_negative_length("outline_width", "mActualOutlineWidth",
827                                 inherit_from="mOutlineWidth",
828                                 round_to_pixels=True) %>
829 
830     pub fn outline_has_nonzero_width(&self) -> bool {
831         self.gecko.mActualOutlineWidth != 0
832     }
833 </%self:impl_trait>
834 
835 <% skip_font_longhands = """font-family font-size font-size-adjust font-weight
836                             font-style font-stretch font-synthesis -x-lang
837                             font-variant-alternates font-variant-east-asian
838                             font-variant-ligatures font-variant-numeric
839                             font-language-override font-feature-settings
840                             font-variation-settings -moz-min-font-size-ratio
841                             -x-text-zoom""" %>
842 <%self:impl_trait style_struct_name="Font"
843     skip_longhands="${skip_font_longhands}">
844 
845     // Negative numbers are invalid at parse time, but <integer> is still an
846     // i32.
847     <% impl_font_settings("font_feature_settings", "gfxFontFeature", "FeatureTagValue", "i32", "u32") %>
848     <% impl_font_settings("font_variation_settings", "gfxFontVariation", "VariationValue", "f32", "f32") %>
849 
from_animation_value_map(animation_value_map: &AnimationValueMap) -> Self850     pub fn unzoom_fonts(&mut self, device: &Device) {
851         use crate::values::generics::NonNegative;
852         self.gecko.mSize = NonNegative(device.unzoom_text(self.gecko.mSize.0));
853         self.gecko.mScriptUnconstrainedSize = NonNegative(device.unzoom_text(self.gecko.mScriptUnconstrainedSize.0));
854         self.gecko.mFont.size = NonNegative(device.unzoom_text(self.gecko.mFont.size.0));
855     }
856 
857     pub fn copy_font_size_from(&mut self, other: &Self) {
858         self.gecko.mScriptUnconstrainedSize = other.gecko.mScriptUnconstrainedSize;
859 
860         self.gecko.mSize = other.gecko.mScriptUnconstrainedSize;
861         self.gecko.mFont.size = other.gecko.mSize;
862         self.gecko.mFontSizeKeyword = other.gecko.mFontSizeKeyword;
863 
864         // TODO(emilio): Should we really copy over these two?
865         self.gecko.mFontSizeFactor = other.gecko.mFontSizeFactor;
866         self.gecko.mFontSizeOffset = other.gecko.mFontSizeOffset;
867     }
868 
has_css_wide_keyword(&self, property: &PropertyId) -> bool869     pub fn reset_font_size(&mut self, other: &Self) {
870         self.copy_font_size_from(other)
871     }
872 
873     pub fn set_font_size(&mut self, v: FontSize) {
874         let size = v.size;
875         self.gecko.mScriptUnconstrainedSize = size;
876 
877         // These two may be changed from Cascade::fixup_font_stuff.
878         self.gecko.mSize = size;
879         self.gecko.mFont.size = size;
880 
881         self.gecko.mFontSizeKeyword = v.keyword_info.kw;
882         self.gecko.mFontSizeFactor = v.keyword_info.factor;
cascade_custom_properties_with_context( &self, context: &Context, ) -> Option<Arc<crate::custom_properties::CustomPropertiesMap>>883         self.gecko.mFontSizeOffset = v.keyword_info.offset;
884     }
885 
886     pub fn clone_font_size(&self) -> FontSize {
887         use crate::values::specified::font::KeywordInfo;
888 
889         FontSize {
890             size: self.gecko.mSize,
891             keyword_info: KeywordInfo {
892                 kw: self.gecko.mFontSizeKeyword,
893                 factor: self.gecko.mFontSizeFactor,
894                 offset: self.gecko.mFontSizeOffset,
895             }
cascade_custom_properties( &self, inherited_custom_properties: Option<&Arc<crate::custom_properties::CustomPropertiesMap>>, device: &Device, ) -> Option<Arc<crate::custom_properties::CustomPropertiesMap>>896         }
897     }
898 
899     pub fn set_font_weight(&mut self, v: longhands::font_weight::computed_value::T) {
900         unsafe { bindings::Gecko_FontWeight_SetFloat(&mut self.gecko.mFont.weight, v.0) };
901     }
902     ${impl_simple_copy('font_weight', 'mFont.weight')}
903 
904     pub fn clone_font_weight(&self) -> longhands::font_weight::computed_value::T {
905         let weight: f32 = unsafe {
906             bindings::Gecko_FontWeight_ToFloat(self.gecko.mFont.weight)
907         };
908         longhands::font_weight::computed_value::T(weight)
909     }
910 
911     pub fn set_font_stretch(&mut self, v: longhands::font_stretch::computed_value::T) {
912         unsafe {
913             bindings::Gecko_FontStretch_SetFloat(
914                 &mut self.gecko.mFont.stretch,
915                 v.value(),
916             )
to_css(&self, dest: &mut CssStringWriter) -> fmt::Result917         };
918     }
919     ${impl_simple_copy('font_stretch', 'mFont.stretch')}
920     pub fn clone_font_stretch(&self) -> longhands::font_stretch::computed_value::T {
921         use crate::values::computed::font::FontStretch;
922         use crate::values::computed::Percentage;
923         use crate::values::generics::NonNegative;
924 
925         let stretch =
926             unsafe { bindings::Gecko_FontStretch_ToFloat(self.gecko.mFont.stretch) };
927         debug_assert!(stretch >= 0.);
928 
929         FontStretch(NonNegative(Percentage(stretch)))
930     }
931 
932     pub fn set_font_style(&mut self, v: longhands::font_style::computed_value::T) {
933         use crate::values::generics::font::FontStyle;
934         let s = &mut self.gecko.mFont.style;
935         unsafe {
936             match v {
937                 FontStyle::Normal => bindings::Gecko_FontSlantStyle_SetNormal(s),
938                 FontStyle::Italic => bindings::Gecko_FontSlantStyle_SetItalic(s),
939                 FontStyle::Oblique(ref angle) => {
940                     bindings::Gecko_FontSlantStyle_SetOblique(s, angle.0.degrees())
941                 }
942             }
943         }
944     }
945     ${impl_simple_copy('font_style', 'mFont.style')}
946     pub fn clone_font_style(&self) -> longhands::font_style::computed_value::T {
947         use crate::values::computed::font::FontStyle;
948         FontStyle::from_gecko(self.gecko.mFont.style)
949     }
950 
951     ${impl_simple_type_with_conversion("font_synthesis", "mFont.synthesis")}
952 
953     ${impl_simple("font_variant_alternates", "mFont.variantAlternates")}
954 
955     ${impl_simple("font_size_adjust", "mFont.sizeAdjust")}
956 
957     ${impl_simple("font_family", "mFont.family")}
958 
959     #[allow(non_snake_case)]
960     pub fn set__x_lang(&mut self, v: longhands::_x_lang::computed_value::T) {
961         let ptr = v.0.as_ptr();
962         forget(v);
963         unsafe {
964             Gecko_nsStyleFont_SetLang(&mut *self.gecko, ptr);
965         }
966     }
967 
968     #[allow(non_snake_case)]
969     pub fn copy__x_lang_from(&mut self, other: &Self) {
970         unsafe {
971             Gecko_nsStyleFont_CopyLangFrom(&mut *self.gecko, &*other.gecko);
972         }
973     }
974 
975     #[allow(non_snake_case)]
976     pub fn reset__x_lang(&mut self, other: &Self) {
977         self.copy__x_lang_from(other)
978     }
979 
980     #[allow(non_snake_case)]
981     pub fn clone__x_lang(&self) -> longhands::_x_lang::computed_value::T {
982         longhands::_x_lang::computed_value::T(unsafe {
983             Atom::from_raw(self.gecko.mLanguage.mRawPtr)
984         })
985     }
986 
987     #[allow(non_snake_case)]
988     pub fn set__x_text_zoom(&mut self, v: longhands::_x_text_zoom::computed_value::T) {
989         self.gecko.mAllowZoomAndMinSize = v.0;
990     }
991 
992     #[allow(non_snake_case)]
993     pub fn copy__x_text_zoom_from(&mut self, other: &Self) {
994         self.gecko.mAllowZoomAndMinSize = other.gecko.mAllowZoomAndMinSize;
995     }
996 
997     #[allow(non_snake_case)]
998     pub fn reset__x_text_zoom(&mut self, other: &Self) {
999         self.copy__x_text_zoom_from(other)
1000     }
1001 
1002     #[allow(non_snake_case)]
1003     pub fn clone__x_text_zoom(&self) -> longhands::_x_text_zoom::computed_value::T {
1004         longhands::_x_text_zoom::computed_value::T(self.gecko.mAllowZoomAndMinSize)
1005     }
1006 
1007     <% impl_simple_type_with_conversion("font_language_override", "mFont.languageOverride") %>
1008     ${impl_simple_type_with_conversion("font_variant_ligatures", "mFont.variantLigatures")}
1009     ${impl_simple_type_with_conversion("font_variant_east_asian", "mFont.variantEastAsian")}
1010     ${impl_simple_type_with_conversion("font_variant_numeric", "mFont.variantNumeric")}
1011 
1012     #[allow(non_snake_case)]
1013     pub fn clone__moz_min_font_size_ratio(
1014         &self,
1015     ) -> longhands::_moz_min_font_size_ratio::computed_value::T {
1016         Percentage(self.gecko.mMinFontSizeRatio as f32 / 100.)
1017     }
1018 
1019     #[allow(non_snake_case)]
1020     pub fn set__moz_min_font_size_ratio(&mut self, v: longhands::_moz_min_font_size_ratio::computed_value::T) {
1021         let scaled = v.0 * 100.;
1022         let percentage = if scaled > 255. {
1023             255.
1024         } else if scaled < 0. {
1025             0.
1026         } else {
1027             scaled
1028         };
1029 
1030         self.gecko.mMinFontSizeRatio = percentage as u8;
1031     }
1032 
1033     ${impl_simple_copy('_moz_min_font_size_ratio', 'mMinFontSizeRatio')}
1034 </%self:impl_trait>
1035 
1036 <%def name="impl_copy_animation_or_transition_value(type, ident, gecko_ffi_name, member=None)">
1037     #[allow(non_snake_case)]
1038     pub fn copy_${type}_${ident}_from(&mut self, other: &Self) {
1039         self.gecko.m${type.capitalize()}s.ensure_len(other.gecko.m${type.capitalize()}s.len());
1040 
1041         let count = other.gecko.m${type.capitalize()}${gecko_ffi_name}Count;
1042         self.gecko.m${type.capitalize()}${gecko_ffi_name}Count = count;
1043 
1044         let iter = self.gecko.m${type.capitalize()}s.iter_mut().take(count as usize).zip(
1045             other.gecko.m${type.capitalize()}s.iter()
1046         );
1047 
1048         for (ours, others) in iter {
1049             % if member:
1050             ours.m${gecko_ffi_name}.${member} = others.m${gecko_ffi_name}.${member};
1051             % else:
1052             ours.m${gecko_ffi_name} = others.m${gecko_ffi_name};
1053             % endif
1054         }
1055     }
1056 
1057     #[allow(non_snake_case)]
1058     pub fn reset_${type}_${ident}(&mut self, other: &Self) {
1059         self.copy_${type}_${ident}_from(other)
1060     }
1061 </%def>
1062 
1063 <%def name="impl_animation_or_transition_count(type, ident, gecko_ffi_name)">
1064     #[allow(non_snake_case)]
1065     pub fn ${type}_${ident}_count(&self) -> usize {
1066         self.gecko.m${type.capitalize()}${gecko_ffi_name}Count as usize
1067     }
1068 </%def>
1069 
1070 <%def name="impl_animation_or_transition_time_value(type, ident, gecko_ffi_name)">
1071     #[allow(non_snake_case)]
1072     pub fn set_${type}_${ident}<I>(&mut self, v: I)
1073         where I: IntoIterator<Item = longhands::${type}_${ident}::computed_value::single_value::T>,
1074               I::IntoIter: ExactSizeIterator + Clone
1075     {
1076         let v = v.into_iter();
1077         debug_assert_ne!(v.len(), 0);
1078         let input_len = v.len();
1079         self.gecko.m${type.capitalize()}s.ensure_len(input_len);
1080 
1081         self.gecko.m${type.capitalize()}${gecko_ffi_name}Count = input_len as u32;
1082         for (gecko, servo) in self.gecko.m${type.capitalize()}s.iter_mut().take(input_len as usize).zip(v) {
1083             gecko.m${gecko_ffi_name} = servo.seconds() * 1000.;
1084         }
1085     }
1086     #[allow(non_snake_case)]
1087     pub fn ${type}_${ident}_at(&self, index: usize)
1088         -> longhands::${type}_${ident}::computed_value::SingleComputedValue {
1089         use crate::values::computed::Time;
1090         Time::from_seconds(self.gecko.m${type.capitalize()}s[index].m${gecko_ffi_name} / 1000.)
1091     }
1092     ${impl_animation_or_transition_count(type, ident, gecko_ffi_name)}
1093     ${impl_copy_animation_or_transition_value(type, ident, gecko_ffi_name)}
1094 </%def>
1095 
1096 <%def name="impl_animation_or_transition_timing_function(type)">
1097     pub fn set_${type}_timing_function<I>(&mut self, v: I)
1098         where I: IntoIterator<Item = longhands::${type}_timing_function::computed_value::single_value::T>,
1099               I::IntoIter: ExactSizeIterator + Clone
1100     {
1101         let v = v.into_iter();
1102         debug_assert_ne!(v.len(), 0);
1103         let input_len = v.len();
1104         self.gecko.m${type.capitalize()}s.ensure_len(input_len);
1105 
1106         self.gecko.m${type.capitalize()}TimingFunctionCount = input_len as u32;
1107         for (gecko, servo) in self.gecko.m${type.capitalize()}s.iter_mut().take(input_len as usize).zip(v) {
1108             gecko.mTimingFunction.mTiming = servo;
1109         }
1110     }
1111     ${impl_animation_or_transition_count(type, 'timing_function', 'TimingFunction')}
1112     ${impl_copy_animation_or_transition_value(type, 'timing_function', "TimingFunction", "mTiming")}
1113     pub fn ${type}_timing_function_at(&self, index: usize)
1114         -> longhands::${type}_timing_function::computed_value::SingleComputedValue {
1115         self.gecko.m${type.capitalize()}s[index].mTimingFunction.mTiming
1116     }
1117 </%def>
1118 
1119 <%def name="impl_transition_time_value(ident, gecko_ffi_name)">
1120     ${impl_animation_or_transition_time_value('transition', ident, gecko_ffi_name)}
1121 </%def>
1122 
1123 <%def name="impl_transition_count(ident, gecko_ffi_name)">
1124     ${impl_animation_or_transition_count('transition', ident, gecko_ffi_name)}
1125 </%def>
1126 
1127 <%def name="impl_copy_animation_value(ident, gecko_ffi_name)">
1128     ${impl_copy_animation_or_transition_value('animation', ident, gecko_ffi_name)}
1129 </%def>
1130 
1131 
1132 <%def name="impl_animation_count(ident, gecko_ffi_name)">
1133     ${impl_animation_or_transition_count('animation', ident, gecko_ffi_name)}
1134 </%def>
1135 
1136 <%def name="impl_animation_time_value(ident, gecko_ffi_name)">
1137     ${impl_animation_or_transition_time_value('animation', ident, gecko_ffi_name)}
1138 </%def>
1139 
1140 <%def name="impl_animation_keyword(ident, gecko_ffi_name, keyword, cast_type='u8')">
1141     #[allow(non_snake_case)]
1142     pub fn set_animation_${ident}<I>(&mut self, v: I)
1143         where I: IntoIterator<Item = longhands::animation_${ident}::computed_value::single_value::T>,
1144               I::IntoIter: ExactSizeIterator + Clone
1145     {
1146         use crate::properties::longhands::animation_${ident}::single_value::computed_value::T as Keyword;
1147 
1148         let v = v.into_iter();
1149 
1150         debug_assert_ne!(v.len(), 0);
1151         let input_len = v.len();
1152         self.gecko.mAnimations.ensure_len(input_len);
1153 
1154         self.gecko.mAnimation${gecko_ffi_name}Count = input_len as u32;
1155 
1156         for (gecko, servo) in self.gecko.mAnimations.iter_mut().take(input_len as usize).zip(v) {
1157             let result = match servo {
1158                 % for value in keyword.values_for("gecko"):
1159                     Keyword::${to_camel_case(value)} =>
1160                         structs::${keyword.gecko_constant(value)} ${keyword.maybe_cast(cast_type)},
1161                 % endfor
1162             };
1163             gecko.m${gecko_ffi_name} = result;
1164         }
1165     }
1166     #[allow(non_snake_case)]
1167     pub fn animation_${ident}_at(&self, index: usize)
1168         -> longhands::animation_${ident}::computed_value::SingleComputedValue {
1169         use crate::properties::longhands::animation_${ident}::single_value::computed_value::T as Keyword;
1170         match self.gecko.mAnimations[index].m${gecko_ffi_name} ${keyword.maybe_cast("u32")} {
1171             % for value in keyword.values_for("gecko"):
1172                 structs::${keyword.gecko_constant(value)} => Keyword::${to_camel_case(value)},
1173             % endfor
1174             % if keyword.gecko_inexhaustive:
1175             _ => panic!("Found unexpected value for animation-${ident}"),
1176             % endif
1177         }
1178     }
1179     ${impl_animation_count(ident, gecko_ffi_name)}
1180     ${impl_copy_animation_value(ident, gecko_ffi_name)}
1181 </%def>
1182 
handle_first_serialization<W>(dest: &mut W, is_first_serialization: &mut bool) -> fmt::Result where W: Write,1183 <% skip_box_longhands= """display
1184                           animation-name animation-delay animation-duration
1185                           animation-direction animation-fill-mode animation-play-state
1186                           animation-iteration-count animation-timing-function
1187                           clear transition-duration transition-delay
1188                           transition-timing-function transition-property
1189                           -webkit-line-clamp""" %>
1190 <%self:impl_trait style_struct_name="Box" skip_longhands="${skip_box_longhands}">
1191     #[inline]
1192     pub fn set_display(&mut self, v: longhands::display::computed_value::T) {
1193         self.gecko.mDisplay = v;
1194         self.gecko.mOriginalDisplay = v;
1195     }
append_declaration_value<'a, 'b: 'a>( dest: &mut CssStringWriter, appendable_value: AppendableValue<'a, 'b>, ) -> fmt::Result1196 
1197     #[inline]
1198     pub fn copy_display_from(&mut self, other: &Self) {
1199         self.gecko.mDisplay = other.gecko.mDisplay;
1200         self.gecko.mOriginalDisplay = other.gecko.mDisplay;
1201     }
1202 
1203     #[inline]
1204     pub fn reset_display(&mut self, other: &Self) {
1205         self.copy_display_from(other)
1206     }
1207 
1208     #[inline]
1209     pub fn set_adjusted_display(
append_serialization<'a, 'b: 'a, N>( dest: &mut CssStringWriter, property_name: &N, appendable_value: AppendableValue<'a, 'b>, importance: Importance, is_first_serialization: &mut bool, ) -> fmt::Result where N: ToCss,1210         &mut self,
1211         v: longhands::display::computed_value::T,
1212         _is_item_or_root: bool
1213     ) {
1214         self.gecko.mDisplay = v;
1215     }
1216 
1217     #[inline]
1218     pub fn clone_display(&self) -> longhands::display::computed_value::T {
1219         self.gecko.mDisplay
1220     }
1221 
1222     <% clear_keyword = Keyword(
1223         "clear",
1224         "Left Right None Both",
1225         gecko_enum_prefix="StyleClear",
1226         gecko_inexhaustive=True,
1227     ) %>
1228     ${impl_keyword('clear', 'mBreakType', clear_keyword)}
1229 
1230     ${impl_transition_time_value('delay', 'Delay')}
1231     ${impl_transition_time_value('duration', 'Duration')}
1232     ${impl_animation_or_transition_timing_function('transition')}
1233 
1234     pub fn transition_combined_duration_at(&self, index: usize) -> f32 {
1235         // https://drafts.csswg.org/css-transitions/#transition-combined-duration
1236         self.gecko.mTransitions[index % self.gecko.mTransitionDurationCount as usize].mDuration.max(0.0)
1237             + self.gecko.mTransitions[index % self.gecko.mTransitionDelayCount as usize].mDelay
1238     }
parse_style_attribute( input: &str, url_data: &UrlExtraData, error_reporter: Option<&dyn ParseErrorReporter>, quirks_mode: QuirksMode, rule_type: CssRuleType, ) -> PropertyDeclarationBlock1239 
1240     pub fn set_transition_property<I>(&mut self, v: I)
1241         where I: IntoIterator<Item = longhands::transition_property::computed_value::single_value::T>,
1242               I::IntoIter: ExactSizeIterator
1243     {
1244         use crate::gecko_bindings::structs::nsCSSPropertyID::eCSSPropertyExtra_no_properties;
1245         use crate::gecko_bindings::structs::nsCSSPropertyID::eCSSPropertyExtra_variable;
1246         use crate::gecko_bindings::structs::nsCSSPropertyID::eCSSProperty_UNKNOWN;
1247 
1248         let v = v.into_iter();
1249 
1250         if v.len() != 0 {
1251             self.gecko.mTransitions.ensure_len(v.len());
1252             self.gecko.mTransitionPropertyCount = v.len() as u32;
1253             for (servo, gecko) in v.zip(self.gecko.mTransitions.iter_mut()) {
1254                 unsafe { gecko.mUnknownProperty.clear() };
1255 
1256                 match servo {
1257                     TransitionProperty::Unsupported(ident) => {
1258                         gecko.mProperty = eCSSProperty_UNKNOWN;
1259                         gecko.mUnknownProperty.mRawPtr = ident.0.into_addrefed();
1260                     },
1261                     TransitionProperty::Custom(name) => {
1262                         gecko.mProperty = eCSSPropertyExtra_variable;
1263                         gecko.mUnknownProperty.mRawPtr = name.into_addrefed();
1264                     }
1265                     _ => gecko.mProperty = servo.to_nscsspropertyid().unwrap(),
1266                 }
1267             }
1268         } else {
1269             // In gecko |none| is represented by eCSSPropertyExtra_no_properties.
1270             self.gecko.mTransitionPropertyCount = 1;
1271             self.gecko.mTransitions[0].mProperty = eCSSPropertyExtra_no_properties;
1272         }
1273     }
1274 
1275     /// Returns whether there are any transitions specified.
1276     pub fn specifies_transitions(&self) -> bool {
1277         use crate::gecko_bindings::structs::nsCSSPropertyID::eCSSPropertyExtra_all_properties;
1278         if self.gecko.mTransitionPropertyCount == 1 &&
1279             self.gecko.mTransitions[0].mProperty == eCSSPropertyExtra_all_properties &&
1280             self.transition_combined_duration_at(0) <= 0.0f32 {
1281             return false;
1282         }
1283 
1284         self.gecko.mTransitionPropertyCount > 0
1285     }
1286 
1287     pub fn transition_property_at(&self, index: usize)
1288         -> longhands::transition_property::computed_value::SingleComputedValue {
1289         use crate::gecko_bindings::structs::nsCSSPropertyID::eCSSPropertyExtra_no_properties;
1290         use crate::gecko_bindings::structs::nsCSSPropertyID::eCSSPropertyExtra_variable;
1291         use crate::gecko_bindings::structs::nsCSSPropertyID::eCSSProperty_UNKNOWN;
1292 
1293         let property = self.gecko.mTransitions[index].mProperty;
1294         if property == eCSSProperty_UNKNOWN {
1295             let atom = self.gecko.mTransitions[index].mUnknownProperty.mRawPtr;
1296             debug_assert!(!atom.is_null());
1297             TransitionProperty::Unsupported(CustomIdent(unsafe{
1298                 Atom::from_raw(atom)
1299             }))
1300         } else if property == eCSSPropertyExtra_variable {
1301             let atom = self.gecko.mTransitions[index].mUnknownProperty.mRawPtr;
1302             debug_assert!(!atom.is_null());
1303             TransitionProperty::Custom(unsafe{
1304                 Atom::from_raw(atom)
1305             })
1306         } else if property == eCSSPropertyExtra_no_properties {
1307             // Actually, we don't expect TransitionProperty::Unsupported also
1308             // represents "none", but if the caller wants to convert it, it is
1309             // fine. Please use it carefully.
1310             //
1311             // FIXME(emilio): This is a hack, is this reachable?
1312             TransitionProperty::Unsupported(CustomIdent(atom!("none")))
1313         } else {
1314             property.into()
1315         }
1316     }
1317 
1318     pub fn transition_nscsspropertyid_at(&self, index: usize) -> nsCSSPropertyID {
1319         self.gecko.mTransitions[index].mProperty
1320     }
1321 
1322     pub fn copy_transition_property_from(&mut self, other: &Self) {
1323         use crate::gecko_bindings::structs::nsCSSPropertyID::eCSSPropertyExtra_variable;
1324         use crate::gecko_bindings::structs::nsCSSPropertyID::eCSSProperty_UNKNOWN;
1325         self.gecko.mTransitions.ensure_len(other.gecko.mTransitions.len());
1326 
1327         let count = other.gecko.mTransitionPropertyCount;
1328         self.gecko.mTransitionPropertyCount = count;
1329 
is_non_mozilla_vendor_identifier(name: &str) -> bool1330         for (index, transition) in self.gecko.mTransitions.iter_mut().enumerate().take(count as usize) {
1331             transition.mProperty = other.gecko.mTransitions[index].mProperty;
1332             unsafe { transition.mUnknownProperty.clear() };
1333             if transition.mProperty == eCSSProperty_UNKNOWN ||
1334                transition.mProperty == eCSSPropertyExtra_variable {
1335                 let atom = other.gecko.mTransitions[index].mUnknownProperty.mRawPtr;
1336                 debug_assert!(!atom.is_null());
1337                 transition.mUnknownProperty.mRawPtr = unsafe { Atom::from_raw(atom) }.into_addrefed();
1338             }
1339         }
1340     }
1341 
1342     pub fn reset_transition_property(&mut self, other: &Self) {
1343         self.copy_transition_property_from(other)
1344     }
1345 
1346     ${impl_transition_count('property', 'Property')}
1347 
1348     pub fn animations_equals(&self, other: &Self) -> bool {
1349         return self.gecko.mAnimationNameCount == other.gecko.mAnimationNameCount
1350             && self.gecko.mAnimationDelayCount == other.gecko.mAnimationDelayCount
1351             && self.gecko.mAnimationDirectionCount == other.gecko.mAnimationDirectionCount
1352             && self.gecko.mAnimationDurationCount == other.gecko.mAnimationDurationCount
1353             && self.gecko.mAnimationFillModeCount == other.gecko.mAnimationFillModeCount
1354             && self.gecko.mAnimationIterationCountCount == other.gecko.mAnimationIterationCountCount
1355             && self.gecko.mAnimationPlayStateCount == other.gecko.mAnimationPlayStateCount
1356             && self.gecko.mAnimationTimingFunctionCount == other.gecko.mAnimationTimingFunctionCount
1357             && unsafe { bindings::Gecko_StyleAnimationsEquals(&self.gecko.mAnimations, &other.gecko.mAnimations) }
1358     }
1359 
1360     pub fn set_animation_name<I>(&mut self, v: I)
1361         where I: IntoIterator<Item = longhands::animation_name::computed_value::single_value::T>,
1362               I::IntoIter: ExactSizeIterator
1363     {
1364         let v = v.into_iter();
1365         debug_assert_ne!(v.len(), 0);
1366         self.gecko.mAnimations.ensure_len(v.len());
alias_of_known_property(name: &str) -> Option<PropertyId>1367 
1368         self.gecko.mAnimationNameCount = v.len() as u32;
1369         for (servo, gecko) in v.zip(self.gecko.mAnimations.iter_mut()) {
1370             let atom = match servo.0 {
1371                 None => atom!(""),
1372                 Some(ref name) => name.as_atom().clone(),
1373             };
1374             unsafe { bindings::Gecko_SetAnimationName(gecko, atom.into_addrefed()); }
report_one_css_error<'i>( context: &ParserContext, block: Option<&PropertyDeclarationBlock>, selectors: Option<&SelectorList<SelectorImpl>>, mut error: ParseError<'i>, slice: &str, property: Option<PropertyId>, )1375         }
1376     }
1377     pub fn animation_name_at(&self, index: usize)
1378         -> longhands::animation_name::computed_value::SingleComputedValue {
1379         use crate::properties::longhands::animation_name::single_value::SpecifiedValue as AnimationName;
1380 
1381         let atom = self.gecko.mAnimations[index].mName.mRawPtr;
1382         if atom == atom!("").as_ptr() {
1383             return AnimationName(None)
1384         }
1385         AnimationName(Some(KeyframesName::from_atom(unsafe { Atom::from_raw(atom) })))
1386     }
1387     pub fn copy_animation_name_from(&mut self, other: &Self) {
1388         self.gecko.mAnimationNameCount = other.gecko.mAnimationNameCount;
1389         unsafe { bindings::Gecko_CopyAnimationNames(&mut self.gecko.mAnimations, &other.gecko.mAnimations); }
1390     }
1391 
1392     pub fn reset_animation_name(&mut self, other: &Self) {
1393         self.copy_animation_name_from(other)
1394     }
1395 
1396     ${impl_animation_count('name', 'Name')}
1397 
1398     ${impl_animation_time_value('delay', 'Delay')}
1399     ${impl_animation_time_value('duration', 'Duration')}
1400 
1401     ${impl_animation_keyword('direction', 'Direction',
1402                              data.longhands_by_name["animation-direction"].keyword)}
1403     ${impl_animation_keyword('fill_mode', 'FillMode',
1404                              data.longhands_by_name["animation-fill-mode"].keyword)}
1405     ${impl_animation_keyword('play_state', 'PlayState',
1406                              data.longhands_by_name["animation-play-state"].keyword)}
1407 
1408     pub fn set_animation_iteration_count<I>(&mut self, v: I)
1409     where
1410         I: IntoIterator<Item = values::computed::AnimationIterationCount>,
1411         I::IntoIter: ExactSizeIterator + Clone
1412     {
1413         use std::f32;
1414         use crate::values::generics::box_::AnimationIterationCount;
1415 
1416         let v = v.into_iter();
1417 
1418         debug_assert_ne!(v.len(), 0);
1419         let input_len = v.len();
1420         self.gecko.mAnimations.ensure_len(input_len);
1421 
1422         self.gecko.mAnimationIterationCountCount = input_len as u32;
1423         for (gecko, servo) in self.gecko.mAnimations.iter_mut().take(input_len as usize).zip(v) {
1424             match servo {
1425                 AnimationIterationCount::Number(n) => gecko.mIterationCount = n,
1426                 AnimationIterationCount::Infinite => gecko.mIterationCount = f32::INFINITY,
1427             }
1428         }
1429     }
1430 
1431     pub fn animation_iteration_count_at(
1432         &self,
1433         index: usize,
1434     ) -> values::computed::AnimationIterationCount {
1435         use crate::values::generics::box_::AnimationIterationCount;
report_css_errors( context: &ParserContext, block: &PropertyDeclarationBlock, selectors: Option<&SelectorList<SelectorImpl>>, errors: &mut SmallParseErrorVec, )1436 
1437         if self.gecko.mAnimations[index].mIterationCount.is_infinite() {
1438             AnimationIterationCount::Infinite
1439         } else {
1440             AnimationIterationCount::Number(self.gecko.mAnimations[index].mIterationCount)
1441         }
1442     }
1443 
1444     ${impl_animation_count('iteration_count', 'IterationCount')}
1445     ${impl_copy_animation_value('iteration_count', 'IterationCount')}
1446     ${impl_animation_or_transition_timing_function('animation')}
1447 
1448     #[allow(non_snake_case)]
parse_property_declaration_list( context: &ParserContext, input: &mut Parser, selectors: Option<&SelectorList<SelectorImpl>>, ) -> PropertyDeclarationBlock1449     pub fn set__webkit_line_clamp(&mut self, v: longhands::_webkit_line_clamp::computed_value::T) {
1450         self.gecko.mLineClamp = match v {
1451             Either::First(n) => n.0 as u32,
1452             Either::Second(None_) => 0,
1453         };
1454     }
1455 
1456     ${impl_simple_copy('_webkit_line_clamp', 'mLineClamp')}
1457 
1458     #[allow(non_snake_case)]
1459     pub fn clone__webkit_line_clamp(&self) -> longhands::_webkit_line_clamp::computed_value::T {
1460         match self.gecko.mLineClamp {
1461             0 => Either::Second(None_),
1462             n => {
1463                 debug_assert!(n <= std::i32::MAX as u32);
1464                 Either::First((n as i32).into())
1465             }
1466         }
1467     }
1468 
1469 </%self:impl_trait>
1470 
1471 <%def name="simple_image_array_property(name, shorthand, field_name)">
1472     <%
1473         image_layers_field = "mImage" if shorthand == "background" else "mMask"
1474         copy_simple_image_array_property(name, shorthand, image_layers_field, field_name)
1475     %>
1476 
1477     pub fn set_${shorthand}_${name}<I>(&mut self, v: I)
1478         where I: IntoIterator<Item=longhands::${shorthand}_${name}::computed_value::single_value::T>,
1479               I::IntoIter: ExactSizeIterator
1480     {
1481         use crate::gecko_bindings::structs::nsStyleImageLayers_LayerType as LayerType;
1482         let v = v.into_iter();
1483 
1484         unsafe {
1485           Gecko_EnsureImageLayersLength(&mut self.gecko.${image_layers_field}, v.len(),
1486                                         LayerType::${shorthand.title()});
1487         }
1488 
1489         self.gecko.${image_layers_field}.${field_name}Count = v.len() as u32;
1490         for (servo, geckolayer) in v.zip(self.gecko.${image_layers_field}.mLayers.iter_mut()) {
1491             geckolayer.${field_name} = {
1492                 ${caller.body()}
1493             };
1494         }
1495     }
1496 </%def>
1497 
1498 <%def name="copy_simple_image_array_property(name, shorthand, layers_field_name, field_name)">
1499     pub fn copy_${shorthand}_${name}_from(&mut self, other: &Self) {
1500         use crate::gecko_bindings::structs::nsStyleImageLayers_LayerType as LayerType;
1501 
1502         let count = other.gecko.${layers_field_name}.${field_name}Count;
1503         unsafe {
1504             Gecko_EnsureImageLayersLength(&mut self.gecko.${layers_field_name},
1505                                           count as usize,
1506                                           LayerType::${shorthand.title()});
1507         }
1508         // FIXME(emilio): This may be bogus in the same way as bug 1426246.
1509         for (layer, other) in self.gecko.${layers_field_name}.mLayers.iter_mut()
1510                                   .zip(other.gecko.${layers_field_name}.mLayers.iter())
1511                                   .take(count as usize) {
1512             layer.${field_name} = other.${field_name}.clone();
1513         }
1514         self.gecko.${layers_field_name}.${field_name}Count = count;
1515     }
1516 
1517     pub fn reset_${shorthand}_${name}(&mut self, other: &Self) {
1518         self.copy_${shorthand}_${name}_from(other)
1519     }
1520 </%def>
1521 
1522 <%def name="impl_simple_image_array_property(name, shorthand, layer_field_name, field_name, struct_name)">
1523     <%
1524         ident = "%s_%s" % (shorthand, name)
1525         style_struct = next(x for x in data.style_structs if x.name == struct_name)
1526         longhand = next(x for x in style_struct.longhands if x.ident == ident)
1527         keyword = longhand.keyword
1528     %>
1529 
1530     <% copy_simple_image_array_property(name, shorthand, layer_field_name, field_name) %>
1531 
1532     pub fn set_${ident}<I>(&mut self, v: I)
1533     where
1534         I: IntoIterator<Item=longhands::${ident}::computed_value::single_value::T>,
1535         I::IntoIter: ExactSizeIterator,
1536     {
1537         use crate::properties::longhands::${ident}::single_value::computed_value::T as Keyword;
1538         use crate::gecko_bindings::structs::nsStyleImageLayers_LayerType as LayerType;
1539 
1540         let v = v.into_iter();
1541 
1542         unsafe {
1543           Gecko_EnsureImageLayersLength(&mut self.gecko.${layer_field_name}, v.len(),
1544                                         LayerType::${shorthand.title()});
1545         }
1546 
1547         self.gecko.${layer_field_name}.${field_name}Count = v.len() as u32;
1548         for (servo, geckolayer) in v.zip(self.gecko.${layer_field_name}.mLayers.iter_mut()) {
1549             geckolayer.${field_name} = {
1550                 match servo {
1551                     % for value in keyword.values_for("gecko"):
1552                     Keyword::${to_camel_case(value)} =>
1553                         structs::${keyword.gecko_constant(value)} ${keyword.maybe_cast('u8')},
1554                     % endfor
1555                 }
1556             };
1557         }
1558     }
1559 
1560     pub fn clone_${ident}(&self) -> longhands::${ident}::computed_value::T {
1561         use crate::properties::longhands::${ident}::single_value::computed_value::T as Keyword;
1562 
1563         % if keyword.needs_cast():
1564         % for value in keyword.values_for('gecko'):
1565         const ${keyword.casted_constant_name(value, "u8")} : u8 =
1566             structs::${keyword.gecko_constant(value)} as u8;
1567         % endfor
1568         % endif
1569 
1570         longhands::${ident}::computed_value::List(
1571             self.gecko.${layer_field_name}.mLayers.iter()
1572                 .take(self.gecko.${layer_field_name}.${field_name}Count as usize)
1573                 .map(|ref layer| {
1574                     match layer.${field_name} {
1575                         % for value in longhand.keyword.values_for("gecko"):
1576                         % if keyword.needs_cast():
1577                         ${keyword.casted_constant_name(value, "u8")}
1578                         % else:
1579                         structs::${keyword.gecko_constant(value)}
1580                         % endif
1581                             => Keyword::${to_camel_case(value)},
1582                         % endfor
1583                         % if keyword.gecko_inexhaustive:
1584                         _ => panic!("Found unexpected value in style struct for ${ident} property"),
1585                         % endif
1586                     }
1587                 }).collect()
1588         )
1589     }
1590 </%def>
1591 
1592 <%def name="impl_common_image_layer_properties(shorthand)">
1593     <%
1594         if shorthand == "background":
1595             image_layers_field = "mImage"
1596             struct_name = "Background"
1597         else:
1598             image_layers_field = "mMask"
1599             struct_name = "SVG"
1600     %>
1601 
1602     <%self:simple_image_array_property name="repeat" shorthand="${shorthand}" field_name="mRepeat">
1603         use crate::values::specified::background::BackgroundRepeatKeyword;
1604         use crate::gecko_bindings::structs::nsStyleImageLayers_Repeat;
1605         use crate::gecko_bindings::structs::StyleImageLayerRepeat;
1606 
1607         fn to_ns(repeat: BackgroundRepeatKeyword) -> StyleImageLayerRepeat {
1608             match repeat {
1609                 BackgroundRepeatKeyword::Repeat => StyleImageLayerRepeat::Repeat,
1610                 BackgroundRepeatKeyword::Space => StyleImageLayerRepeat::Space,
1611                 BackgroundRepeatKeyword::Round => StyleImageLayerRepeat::Round,
1612                 BackgroundRepeatKeyword::NoRepeat => StyleImageLayerRepeat::NoRepeat,
1613             }
1614         }
1615 
1616         let repeat_x = to_ns(servo.0);
1617         let repeat_y = to_ns(servo.1);
1618         nsStyleImageLayers_Repeat {
1619               mXRepeat: repeat_x,
1620               mYRepeat: repeat_y,
1621         }
1622     </%self:simple_image_array_property>
1623 
1624     pub fn clone_${shorthand}_repeat(&self) -> longhands::${shorthand}_repeat::computed_value::T {
1625         use crate::properties::longhands::${shorthand}_repeat::single_value::computed_value::T;
1626         use crate::values::specified::background::BackgroundRepeatKeyword;
1627         use crate::gecko_bindings::structs::StyleImageLayerRepeat;
1628 
1629         fn to_servo(repeat: StyleImageLayerRepeat) -> BackgroundRepeatKeyword {
1630             match repeat {
1631                 StyleImageLayerRepeat::Repeat => BackgroundRepeatKeyword::Repeat,
1632                 StyleImageLayerRepeat::Space => BackgroundRepeatKeyword::Space,
1633                 StyleImageLayerRepeat::Round => BackgroundRepeatKeyword::Round,
1634                 StyleImageLayerRepeat::NoRepeat => BackgroundRepeatKeyword::NoRepeat,
1635                 _ => panic!("Found unexpected value in style struct for ${shorthand}_repeat property"),
1636             }
1637         }
1638 
1639         longhands::${shorthand}_repeat::computed_value::List(
1640             self.gecko.${image_layers_field}.mLayers.iter()
1641                 .take(self.gecko.${image_layers_field}.mRepeatCount as usize)
1642                 .map(|ref layer| {
1643                     T(to_servo(layer.mRepeat.mXRepeat), to_servo(layer.mRepeat.mYRepeat))
1644                 }).collect()
1645         )
1646     }
1647 
1648     <% impl_simple_image_array_property("clip", shorthand, image_layers_field, "mClip", struct_name) %>
1649     <% impl_simple_image_array_property("origin", shorthand, image_layers_field, "mOrigin", struct_name) %>
1650 
1651     % for (orientation, keyword) in [("x", "horizontal"), ("y", "vertical")]:
1652     pub fn copy_${shorthand}_position_${orientation}_from(&mut self, other: &Self) {
1653         use crate::gecko_bindings::structs::nsStyleImageLayers_LayerType as LayerType;
1654 
1655         let count = other.gecko.${image_layers_field}.mPosition${orientation.upper()}Count;
1656 
1657         unsafe {
1658             Gecko_EnsureImageLayersLength(&mut self.gecko.${image_layers_field},
1659                                           count as usize,
1660                                           LayerType::${shorthand.capitalize()});
1661         }
1662 
1663         for (layer, other) in self.gecko.${image_layers_field}.mLayers.iter_mut()
1664                                   .zip(other.gecko.${image_layers_field}.mLayers.iter())
1665                                   .take(count as usize) {
1666             layer.mPosition.${keyword} = other.mPosition.${keyword}.clone();
1667         }
1668         self.gecko.${image_layers_field}.mPosition${orientation.upper()}Count = count;
1669     }
1670 
1671     pub fn reset_${shorthand}_position_${orientation}(&mut self, other: &Self) {
1672         self.copy_${shorthand}_position_${orientation}_from(other)
1673     }
1674 
1675     pub fn clone_${shorthand}_position_${orientation}(&self)
1676         -> longhands::${shorthand}_position_${orientation}::computed_value::T {
1677         longhands::${shorthand}_position_${orientation}::computed_value::List(
1678             self.gecko.${image_layers_field}.mLayers.iter()
1679                 .take(self.gecko.${image_layers_field}.mPosition${orientation.upper()}Count as usize)
1680                 .map(|position| position.mPosition.${keyword}.clone())
1681                 .collect()
1682         )
1683     }
1684 
1685     pub fn set_${shorthand}_position_${orientation[0]}<I>(&mut self,
1686                                      v: I)
1687         where I: IntoIterator<Item = longhands::${shorthand}_position_${orientation[0]}
1688                                               ::computed_value::single_value::T>,
1689               I::IntoIter: ExactSizeIterator
1690     {
1691         use crate::gecko_bindings::structs::nsStyleImageLayers_LayerType as LayerType;
1692 
1693         let v = v.into_iter();
1694 
1695         unsafe {
1696             Gecko_EnsureImageLayersLength(&mut self.gecko.${image_layers_field}, v.len(),
1697                                         LayerType::${shorthand.capitalize()});
1698         }
1699 
1700         self.gecko.${image_layers_field}.mPosition${orientation[0].upper()}Count = v.len() as u32;
1701         for (servo, geckolayer) in v.zip(self.gecko.${image_layers_field}
1702                                                            .mLayers.iter_mut()) {
1703             geckolayer.mPosition.${keyword} = servo;
1704         }
1705     }
1706     % endfor
1707 
1708     <%self:simple_image_array_property name="size" shorthand="${shorthand}" field_name="mSize">
1709         servo
1710     </%self:simple_image_array_property>
1711 
1712     pub fn clone_${shorthand}_size(&self) -> longhands::${shorthand}_size::computed_value::T {
1713         longhands::${shorthand}_size::computed_value::List(
1714             self.gecko.${image_layers_field}.mLayers.iter().map(|layer| layer.mSize.clone()).collect()
1715         )
1716     }
1717 
1718     pub fn copy_${shorthand}_image_from(&mut self, other: &Self) {
1719         use crate::gecko_bindings::structs::nsStyleImageLayers_LayerType as LayerType;
1720         unsafe {
1721             let count = other.gecko.${image_layers_field}.mImageCount;
1722             Gecko_EnsureImageLayersLength(&mut self.gecko.${image_layers_field},
1723                                           count as usize,
1724                                           LayerType::${shorthand.capitalize()});
1725 
1726             for (layer, other) in self.gecko.${image_layers_field}.mLayers.iter_mut()
1727                                       .zip(other.gecko.${image_layers_field}.mLayers.iter())
1728                                       .take(count as usize) {
1729                 layer.mImage = other.mImage.clone();
1730             }
1731             self.gecko.${image_layers_field}.mImageCount = count;
1732         }
1733     }
1734 
1735     pub fn reset_${shorthand}_image(&mut self, other: &Self) {
1736         self.copy_${shorthand}_image_from(other)
1737     }
1738 
1739     #[allow(unused_variables)]
1740     pub fn set_${shorthand}_image<I>(&mut self, images: I)
1741         where I: IntoIterator<Item = longhands::${shorthand}_image::computed_value::single_value::T>,
1742               I::IntoIter: ExactSizeIterator
1743     {
1744         use crate::gecko_bindings::structs::nsStyleImageLayers_LayerType as LayerType;
1745 
1746         let images = images.into_iter();
1747 
1748         unsafe {
1749             Gecko_EnsureImageLayersLength(
1750                 &mut self.gecko.${image_layers_field},
1751                 images.len(),
1752                 LayerType::${shorthand.title()},
1753             );
1754         }
1755 
1756         self.gecko.${image_layers_field}.mImageCount = images.len() as u32;
1757         for (image, geckoimage) in images.zip(self.gecko.${image_layers_field}
1758                                                   .mLayers.iter_mut()) {
1759             geckoimage.mImage = image;
1760         }
1761     }
1762 
1763     pub fn clone_${shorthand}_image(&self) -> longhands::${shorthand}_image::computed_value::T {
1764         longhands::${shorthand}_image::computed_value::List(
1765             self.gecko.${image_layers_field}.mLayers.iter()
1766                 .take(self.gecko.${image_layers_field}.mImageCount as usize)
1767                 .map(|layer| layer.mImage.clone())
1768                 .collect()
1769         )
1770     }
1771 
1772     <%
1773         fill_fields = "mRepeat mClip mOrigin mPositionX mPositionY mImage mSize"
1774         if shorthand == "background":
1775             fill_fields += " mAttachment mBlendMode"
1776         else:
1777             # mSourceURI uses mImageCount
1778             fill_fields += " mMaskMode mComposite"
1779     %>
1780     pub fn fill_arrays(&mut self) {
1781         use crate::gecko_bindings::bindings::Gecko_FillAllImageLayers;
1782         use std::cmp;
1783         let mut max_len = 1;
1784         % for member in fill_fields.split():
1785             max_len = cmp::max(max_len, self.gecko.${image_layers_field}.${member}Count);
1786         % endfor
1787         unsafe {
1788             // While we could do this manually, we'd need to also manually
1789             // run all the copy constructors, so we just delegate to gecko
1790             Gecko_FillAllImageLayers(&mut self.gecko.${image_layers_field}, max_len);
1791         }
1792     }
1793 </%def>
1794 
1795 // TODO: Gecko accepts lists in most background-related properties. We just use
1796 // the first element (which is the common case), but at some point we want to
1797 // add support for parsing these lists in servo and pushing to nsTArray's.
1798 <% skip_background_longhands = """background-repeat
1799                                   background-image background-clip
1800                                   background-origin background-attachment
1801                                   background-size background-position
1802                                   background-blend-mode
1803                                   background-position-x
1804                                   background-position-y""" %>
1805 <%self:impl_trait style_struct_name="Background"
1806                   skip_longhands="${skip_background_longhands}">
1807 
1808     <% impl_common_image_layer_properties("background") %>
1809     <% impl_simple_image_array_property("attachment", "background", "mImage", "mAttachment", "Background") %>
1810     <% impl_simple_image_array_property("blend_mode", "background", "mImage", "mBlendMode", "Background") %>
1811 </%self:impl_trait>
1812 
1813 <%self:impl_trait style_struct_name="List" skip_longhands="list-style-type">
1814     pub fn set_list_style_type(&mut self, v: longhands::list_style_type::computed_value::T) {
1815         use nsstring::{nsACString, nsCStr};
1816         use self::longhands::list_style_type::computed_value::T;
1817         match v {
1818             T::None => unsafe {
1819                 bindings::Gecko_SetCounterStyleToNone(&mut self.gecko.mCounterStyle)
1820             }
1821             T::CounterStyle(s) => s.to_gecko_value(&mut self.gecko.mCounterStyle),
1822             T::String(s) => unsafe {
1823                 bindings::Gecko_SetCounterStyleToString(
1824                     &mut self.gecko.mCounterStyle,
1825                     &nsCStr::from(&s) as &nsACString,
1826                 )
1827             }
1828         }
1829     }
1830 
1831     pub fn copy_list_style_type_from(&mut self, other: &Self) {
1832         unsafe {
1833             Gecko_CopyCounterStyle(&mut self.gecko.mCounterStyle, &other.gecko.mCounterStyle);
1834         }
1835     }
1836 
1837     pub fn reset_list_style_type(&mut self, other: &Self) {
1838         self.copy_list_style_type_from(other)
1839     }
1840 
1841     pub fn clone_list_style_type(&self) -> longhands::list_style_type::computed_value::T {
1842         use self::longhands::list_style_type::computed_value::T;
1843         use crate::values::Either;
1844         use crate::values::generics::CounterStyle;
1845         use crate::gecko_bindings::bindings;
1846 
1847         let name = unsafe {
1848             bindings::Gecko_CounterStyle_GetName(&self.gecko.mCounterStyle)
1849         };
1850         if !name.is_null() {
1851             let name = unsafe { Atom::from_raw(name) };
1852             if name == atom!("none") {
1853                 return T::None;
1854             }
1855         }
1856         let result = CounterStyle::from_gecko_value(&self.gecko.mCounterStyle);
1857         match result {
1858             Either::First(counter_style) => T::CounterStyle(counter_style),
1859             Either::Second(string) => T::String(string),
1860         }
1861     }
1862 </%self:impl_trait>
1863 
1864 <%self:impl_trait style_struct_name="Table">
1865 </%self:impl_trait>
1866 
1867 <%self:impl_trait style_struct_name="Effects">
1868 </%self:impl_trait>
1869 
1870 <%self:impl_trait style_struct_name="InheritedBox">
1871 </%self:impl_trait>
1872 
1873 <%self:impl_trait style_struct_name="InheritedTable"
1874                   skip_longhands="border-spacing">
1875 
1876     pub fn set_border_spacing(&mut self, v: longhands::border_spacing::computed_value::T) {
1877         self.gecko.mBorderSpacingCol = v.horizontal().0;
1878         self.gecko.mBorderSpacingRow = v.vertical().0;
1879     }
1880 
1881     pub fn copy_border_spacing_from(&mut self, other: &Self) {
1882         self.gecko.mBorderSpacingCol = other.gecko.mBorderSpacingCol;
1883         self.gecko.mBorderSpacingRow = other.gecko.mBorderSpacingRow;
1884     }
1885 
1886     pub fn reset_border_spacing(&mut self, other: &Self) {
1887         self.copy_border_spacing_from(other)
1888     }
1889 
1890     pub fn clone_border_spacing(&self) -> longhands::border_spacing::computed_value::T {
1891         longhands::border_spacing::computed_value::T::new(
1892             Au(self.gecko.mBorderSpacingCol).into(),
1893             Au(self.gecko.mBorderSpacingRow).into()
1894         )
1895     }
1896 </%self:impl_trait>
1897 
1898 
1899 <%self:impl_trait style_struct_name="InheritedText"
1900                   skip_longhands="-webkit-text-stroke-width">
1901     ${impl_non_negative_length('_webkit_text_stroke_width',
1902                                'mWebkitTextStrokeWidth')}
1903 </%self:impl_trait>
1904 
1905 <%self:impl_trait style_struct_name="Text" skip_longhands="initial-letter">
1906     pub fn set_initial_letter(&mut self, v: longhands::initial_letter::computed_value::T) {
1907         use crate::values::generics::text::InitialLetter;
1908         match v {
1909             InitialLetter::Normal => {
1910                 self.gecko.mInitialLetterSize = 0.;
1911                 self.gecko.mInitialLetterSink = 0;
1912             },
1913             InitialLetter::Specified(size, sink) => {
1914                 self.gecko.mInitialLetterSize = size;
1915                 if let Some(sink) = sink {
1916                     self.gecko.mInitialLetterSink = sink;
1917                 } else {
1918                     self.gecko.mInitialLetterSink = size.floor() as i32;
1919                 }
1920             }
1921         }
1922     }
1923 
1924     pub fn copy_initial_letter_from(&mut self, other: &Self) {
1925         self.gecko.mInitialLetterSize = other.gecko.mInitialLetterSize;
1926         self.gecko.mInitialLetterSink = other.gecko.mInitialLetterSink;
1927     }
1928 
1929     pub fn reset_initial_letter(&mut self, other: &Self) {
1930         self.copy_initial_letter_from(other)
1931     }
1932 
1933     pub fn clone_initial_letter(&self) -> longhands::initial_letter::computed_value::T {
1934         use crate::values::generics::text::InitialLetter;
1935 
1936         if self.gecko.mInitialLetterSize == 0. && self.gecko.mInitialLetterSink == 0 {
1937             InitialLetter::Normal
1938         } else if self.gecko.mInitialLetterSize.floor() as i32 == self.gecko.mInitialLetterSink {
1939             InitialLetter::Specified(self.gecko.mInitialLetterSize, None)
1940         } else {
1941             InitialLetter::Specified(self.gecko.mInitialLetterSize, Some(self.gecko.mInitialLetterSink))
1942         }
1943     }
1944 </%self:impl_trait>
1945 
1946 <% skip_svg_longhands = """
1947 mask-mode mask-repeat mask-clip mask-origin mask-composite mask-position-x mask-position-y mask-size mask-image
1948 """
1949 %>
1950 <%self:impl_trait style_struct_name="SVG"
1951                   skip_longhands="${skip_svg_longhands}">
1952     <% impl_common_image_layer_properties("mask") %>
1953     <% impl_simple_image_array_property("mode", "mask", "mMask", "mMaskMode", "SVG") %>
1954     <% impl_simple_image_array_property("composite", "mask", "mMask", "mComposite", "SVG") %>
1955 </%self:impl_trait>
1956 
1957 <%self:impl_trait style_struct_name="InheritedSVG">
1958 </%self:impl_trait>
1959 
1960 <%self:impl_trait style_struct_name="InheritedUI">
1961 </%self:impl_trait>
1962 
1963 <%self:impl_trait style_struct_name="Column"
1964                   skip_longhands="column-count column-rule-width column-rule-style">
1965 
1966     #[allow(unused_unsafe)]
1967     pub fn set_column_count(&mut self, v: longhands::column_count::computed_value::T) {
1968         use crate::gecko_bindings::structs::{nsStyleColumn_kColumnCountAuto, nsStyleColumn_kMaxColumnCount};
1969 
1970         self.gecko.mColumnCount = match v {
1971             ColumnCount::Integer(integer) => {
1972                 cmp::min(integer.0 as u32, unsafe { nsStyleColumn_kMaxColumnCount })
1973             },
1974             ColumnCount::Auto => nsStyleColumn_kColumnCountAuto
1975         };
1976     }
1977 
1978     ${impl_simple_copy('column_count', 'mColumnCount')}
1979 
1980     pub fn clone_column_count(&self) -> longhands::column_count::computed_value::T {
1981         use crate::gecko_bindings::structs::{nsStyleColumn_kColumnCountAuto, nsStyleColumn_kMaxColumnCount};
1982         if self.gecko.mColumnCount != nsStyleColumn_kColumnCountAuto {
1983             debug_assert!(self.gecko.mColumnCount >= 1 &&
1984                           self.gecko.mColumnCount <= nsStyleColumn_kMaxColumnCount);
1985             ColumnCount::Integer((self.gecko.mColumnCount as i32).into())
1986         } else {
1987             ColumnCount::Auto
1988         }
1989     }
1990 
1991     <% impl_non_negative_length("column_rule_width", "mColumnRuleWidth",
1992                                 round_to_pixels=True) %>
1993     ${impl_simple('column_rule_style', 'mColumnRuleStyle')}
1994 </%self:impl_trait>
1995 
1996 <%self:impl_trait style_struct_name="Counters">
1997     pub fn ineffective_content_property(&self) -> bool {
1998         !self.gecko.mContent.is_items()
1999     }
2000 </%self:impl_trait>
2001 
2002 <%self:impl_trait style_struct_name="UI">
2003 </%self:impl_trait>
2004 
2005 <%self:impl_trait style_struct_name="XUL">
2006 </%self:impl_trait>
2007 
2008 % for style_struct in data.style_structs:
2009 ${declare_style_struct(style_struct)}
2010 ${impl_style_struct(style_struct)}
2011 % endfor
2012 
2013 /// Assert that the initial values set in Gecko style struct constructors
2014 /// match the values returned by `get_initial_value()` for each longhand.
2015 #[cfg(feature = "gecko")]
2016 #[inline]
2017 pub fn assert_initial_values_match(data: &PerDocumentStyleData) {
2018     if cfg!(debug_assertions) {
2019         let data = data.borrow();
2020         let cv = data.stylist.device().default_computed_values();
2021         <%
2022             # Skip properties with initial values that change at computed
2023             # value time, or whose initial value depends on the document
2024             # / other prefs.
2025             SKIPPED = [
2026                 "border-top-width",
2027                 "border-bottom-width",
2028                 "border-left-width",
2029                 "border-right-width",
2030                 "font-family",
2031                 "font-size",
2032                 "outline-width",
2033                 "color",
2034             ]
2035             TO_TEST = [p for p in data.longhands if p.enabled_in != "" and not p.logical and not p.name in SKIPPED]
2036         %>
2037         % for property in TO_TEST:
2038         assert_eq!(
2039             cv.clone_${property.ident}(),
2040             longhands::${property.ident}::get_initial_value(),
2041             concat!(
2042                 "initial value in Gecko style struct for ",
2043                 stringify!(${property.ident}),
2044                 " must match longhands::",
2045                 stringify!(${property.ident}),
2046                 "::get_initial_value()"
2047             )
2048         );
2049         % endfor
2050     }
2051 }
2052