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