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 <%!
6     from data import Keyword, to_rust_ident, to_phys, to_camel_case, SYSTEM_FONT_LONGHANDS
7     from data import (LOGICAL_CORNERS, PHYSICAL_CORNERS, LOGICAL_SIDES,
8                       PHYSICAL_SIDES, LOGICAL_SIZES, LOGICAL_AXES)
9 %>
10 
11 <%def name="predefined_type(name, type, initial_value, parse_method='parse',
12             vector=False,
13             computed_type=None, initial_specified_value=None,
14             allow_quirks='No', allow_empty=False, **kwargs)">
15     <%def name="predefined_type_inner(name, type, initial_value, parse_method)">
16         #[allow(unused_imports)]
17         use app_units::Au;
18         #[allow(unused_imports)]
19         use cssparser::{Color as CSSParserColor, RGBA};
20         #[allow(unused_imports)]
21         use crate::values::specified::AllowQuirks;
22         #[allow(unused_imports)]
23         use crate::Zero;
24         #[allow(unused_imports)]
25         use smallvec::SmallVec;
26         pub use crate::values::specified::${type} as SpecifiedValue;
27         pub mod computed_value {
28             % if computed_type:
29             pub use ${computed_type} as T;
30             % else:
31             pub use crate::values::computed::${type} as T;
32             % endif
33         }
34         % if initial_value:
get_initial_value() -> computed_value::T35         #[inline] pub fn get_initial_value() -> computed_value::T { ${initial_value} }
36         % endif
37         % if initial_specified_value:
get_initial_specified_value() -> SpecifiedValue38         #[inline] pub fn get_initial_specified_value() -> SpecifiedValue { ${initial_specified_value} }
39         % endif
40         #[allow(unused_variables)]
41         #[inline]
parse<'i, 't>( context: &ParserContext, input: &mut Parser<'i, 't>, ) -> Result<SpecifiedValue, ParseError<'i>>42         pub fn parse<'i, 't>(
43             context: &ParserContext,
44             input: &mut Parser<'i, 't>,
45         ) -> Result<SpecifiedValue, ParseError<'i>> {
46             % if allow_quirks != "No":
47             specified::${type}::${parse_method}_quirky(context, input, AllowQuirks::${allow_quirks})
48             % elif parse_method != "parse":
49             specified::${type}::${parse_method}(context, input)
50             % else:
51             <specified::${type} as crate::parser::Parse>::parse(context, input)
52             % endif
53         }
54     </%def>
55     % if vector:
56         <%call
57             expr="vector_longhand(name, predefined_type=type, allow_empty=allow_empty or not initial_value, **kwargs)"
58         >
59             ${predefined_type_inner(name, type, initial_value, parse_method)}
60             % if caller:
61             ${caller.body()}
62             % endif
63         </%call>
64     % else:
65         <%call expr="longhand(name, predefined_type=type, **kwargs)">
66             ${predefined_type_inner(name, type, initial_value, parse_method)}
67             % if caller:
68             ${caller.body()}
69             % endif
70         </%call>
71     % endif
72 </%def>
73 
74 // FIXME (Manishearth): Add computed_value_as_specified argument
75 // and handle the empty case correctly
76 <%doc>
77     To be used in cases where we have a grammar like "<thing> [ , <thing> ]*".
78 
79     Setting allow_empty to False allows for cases where the vector
80     is empty. The grammar for these is usually "none | <thing> [ , <thing> ]*".
81     We assume that the default/initial value is an empty vector for these.
82     `initial_value` need not be defined for these.
83 </%doc>
84 
85 // The setup here is roughly:
86 //
87 //  * UnderlyingList is the list that is stored in the computed value. This may
88 //    be a shared ArcSlice if the property is inherited.
89 //  * UnderlyingOwnedList is the list that is used for animation.
90 //  * Specified values always use OwnedSlice, since it's more compact.
91 //  * computed_value::List is just a convenient alias that you can use for the
92 //    computed value list, since this is in the computed_value module.
93 //
94 // If simple_vector_bindings is true, then we don't use the complex iterator
95 // machinery and set_foo_from, and just compute the value like any other
96 // longhand.
97 <%def name="vector_longhand(name, animation_value_type=None,
98                             vector_animation_type=None, allow_empty=False,
99                             simple_vector_bindings=False,
100                             separator='Comma',
101                             **kwargs)">
102     <%call expr="longhand(name, animation_value_type=animation_value_type, vector=True,
103                           simple_vector_bindings=simple_vector_bindings, **kwargs)">
104         #[allow(unused_imports)]
105         use smallvec::SmallVec;
106 
107         pub mod single_value {
108             #[allow(unused_imports)]
109             use cssparser::{Parser, BasicParseError};
110             #[allow(unused_imports)]
111             use crate::parser::{Parse, ParserContext};
112             #[allow(unused_imports)]
113             use crate::properties::ShorthandId;
114             #[allow(unused_imports)]
115             use selectors::parser::SelectorParseErrorKind;
116             #[allow(unused_imports)]
117             use style_traits::{ParseError, StyleParseErrorKind};
118             #[allow(unused_imports)]
119             use crate::values::computed::{Context, ToComputedValue};
120             #[allow(unused_imports)]
121             use crate::values::{computed, specified};
122             #[allow(unused_imports)]
123             use crate::values::{Auto, Either, None_};
124             ${caller.body()}
125         }
126 
127         /// The definition of the computed value for ${name}.
128         pub mod computed_value {
129             #[allow(unused_imports)]
130             use crate::values::animated::ToAnimatedValue;
131             #[allow(unused_imports)]
132             use crate::values::resolved::ToResolvedValue;
133             pub use super::single_value::computed_value as single_value;
134             pub use self::single_value::T as SingleComputedValue;
135             % if not allow_empty or allow_empty == "NotInitial":
136             use smallvec::SmallVec;
137             % endif
138             use crate::values::computed::ComputedVecIter;
139 
140             <%
141                 is_shared_list = allow_empty and allow_empty != "NotInitial" and \
142                     data.longhands_by_name[name].style_struct.inherited
143             %>
144 
145             // FIXME(emilio): Add an OwnedNonEmptySlice type, and figure out
146             // something for transition-name, which is the only remaining user
147             // of NotInitial.
148             pub type UnderlyingList<T> =
149                 % if allow_empty and allow_empty != "NotInitial":
150                 % if data.longhands_by_name[name].style_struct.inherited:
151                     crate::ArcSlice<T>;
152                 % else:
153                     crate::OwnedSlice<T>;
154                 % endif
155                 % else:
156                     SmallVec<[T; 1]>;
157                 % endif
158 
159             pub type UnderlyingOwnedList<T> =
160                 % if allow_empty and allow_empty != "NotInitial":
161                     crate::OwnedSlice<T>;
162                 % else:
163                     SmallVec<[T; 1]>;
164                 % endif
165 
166 
167             /// The generic type defining the animated and resolved values for
168             /// this property.
169             ///
170             /// Making this type generic allows the compiler to figure out the
171             /// animated value for us, instead of having to implement it
172             /// manually for every type we care about.
173             #[derive(
174                 Clone,
175                 Debug,
176                 MallocSizeOf,
177                 PartialEq,
178                 ToAnimatedValue,
179                 ToResolvedValue,
180                 ToCss,
181             )]
182             % if separator == "Comma":
183             #[css(comma)]
184             % endif
185             pub struct OwnedList<T>(
186                 % if not allow_empty:
187                 #[css(iterable)]
188                 % else:
189                 #[css(if_empty = "none", iterable)]
190                 % endif
191                 pub UnderlyingOwnedList<T>,
192             );
193 
194             /// The computed value for this property.
195             % if not is_shared_list:
196             pub type ComputedList = OwnedList<single_value::T>;
197             pub use self::OwnedList as List;
198             % else:
199             pub use self::ComputedList as List;
200 
201             #[derive(
202                 Clone,
203                 Debug,
204                 MallocSizeOf,
205                 PartialEq,
206                 ToCss,
207             )]
208             % if separator == "Comma":
209             #[css(comma)]
210             % endif
211             pub struct ComputedList(
212                 % if not allow_empty:
213                 #[css(iterable)]
214                 % else:
215                 #[css(if_empty = "none", iterable)]
216                 % endif
217                 % if is_shared_list:
218                 #[ignore_malloc_size_of = "Arc"]
219                 % endif
220                 pub UnderlyingList<single_value::T>,
221             );
222 
223             type ResolvedList = OwnedList<<single_value::T as ToResolvedValue>::ResolvedValue>;
224             impl ToResolvedValue for ComputedList {
225                 type ResolvedValue = ResolvedList;
226 
to_resolved_value(self, context: &crate::values::resolved::Context) -> Self::ResolvedValue227                 fn to_resolved_value(self, context: &crate::values::resolved::Context) -> Self::ResolvedValue {
228                     OwnedList(
229                         self.0
230                             .iter()
231                             .cloned()
232                             .map(|v| v.to_resolved_value(context))
233                             .collect()
234                     )
235                 }
236 
from_resolved_value(resolved: Self::ResolvedValue) -> Self237                 fn from_resolved_value(resolved: Self::ResolvedValue) -> Self {
238                     % if not is_shared_list:
239                     use std::iter::FromIterator;
240                     % endif
241                     let iter =
242                         resolved.0.into_iter().map(ToResolvedValue::from_resolved_value);
243                     ComputedList(UnderlyingList::from_iter(iter))
244                 }
245             }
246             % endif
247 
248             % if simple_vector_bindings:
249             impl From<ComputedList> for UnderlyingList<single_value::T> {
250                 #[inline]
from(l: ComputedList) -> Self251                 fn from(l: ComputedList) -> Self {
252                     l.0
253                 }
254             }
255             impl From<UnderlyingList<single_value::T>> for ComputedList {
256                 #[inline]
from(l: UnderlyingList<single_value::T>) -> Self257                 fn from(l: UnderlyingList<single_value::T>) -> Self {
258                     List(l)
259                 }
260             }
261             % endif
262 
263             % if vector_animation_type:
264             % if not animation_value_type:
265                 Sorry, this is stupid but needed for now.
266             % endif
267 
268             use crate::properties::animated_properties::ListAnimation;
269             use crate::values::animated::{Animate, ToAnimatedZero, Procedure};
270             use crate::values::distance::{SquaredDistance, ComputeSquaredDistance};
271 
272             // FIXME(emilio): For some reason rust thinks that this alias is
273             // unused, even though it's clearly used below?
274             #[allow(unused)]
275             type AnimatedList = OwnedList<<single_value::T as ToAnimatedValue>::AnimatedValue>;
276 
277             % if is_shared_list:
278             impl ToAnimatedValue for ComputedList {
279                 type AnimatedValue = AnimatedList;
280 
to_animated_value(self) -> Self::AnimatedValue281                 fn to_animated_value(self) -> Self::AnimatedValue {
282                     OwnedList(
283                         self.0.iter().map(|v| v.clone().to_animated_value()).collect()
284                     )
285                 }
286 
from_animated_value(animated: Self::AnimatedValue) -> Self287                 fn from_animated_value(animated: Self::AnimatedValue) -> Self {
288                     let iter =
289                         animated.0.into_iter().map(ToAnimatedValue::from_animated_value);
290                     ComputedList(UnderlyingList::from_iter(iter))
291                 }
292             }
293             % endif
294 
295             impl ToAnimatedZero for AnimatedList {
to_animated_zero(&self) -> Result<Self, ()>296                 fn to_animated_zero(&self) -> Result<Self, ()> { Err(()) }
297             }
298 
299             impl Animate for AnimatedList {
animate( &self, other: &Self, procedure: Procedure, ) -> Result<Self, ()>300                 fn animate(
301                     &self,
302                     other: &Self,
303                     procedure: Procedure,
304                 ) -> Result<Self, ()> {
305                     Ok(OwnedList(
306                         self.0.animate_${vector_animation_type}(&other.0, procedure)?
307                     ))
308                 }
309             }
310             impl ComputeSquaredDistance for AnimatedList {
compute_squared_distance( &self, other: &Self, ) -> Result<SquaredDistance, ()>311                 fn compute_squared_distance(
312                     &self,
313                     other: &Self,
314                 ) -> Result<SquaredDistance, ()> {
315                     self.0.squared_distance_${vector_animation_type}(&other.0)
316                 }
317             }
318             % endif
319 
320             /// The computed value, effectively a list of single values.
321             pub use self::ComputedList as T;
322 
323             pub type Iter<'a, 'cx, 'cx_a> = ComputedVecIter<'a, 'cx, 'cx_a, super::single_value::SpecifiedValue>;
324         }
325 
326         /// The specified value of ${name}.
327         #[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss, ToShmem)]
328         % if separator == "Comma":
329         #[css(comma)]
330         % endif
331         pub struct SpecifiedValue(
332             % if not allow_empty:
333             #[css(iterable)]
334             % else:
335             #[css(if_empty = "none", iterable)]
336             % endif
337             pub crate::OwnedSlice<single_value::SpecifiedValue>,
338         );
339 
get_initial_value() -> computed_value::T340         pub fn get_initial_value() -> computed_value::T {
341             % if allow_empty and allow_empty != "NotInitial":
342                 computed_value::List(Default::default())
343             % else:
344                 let mut v = SmallVec::new();
345                 v.push(single_value::get_initial_value());
346                 computed_value::List(v)
347             % endif
348         }
349 
parse<'i, 't>( context: &ParserContext, input: &mut Parser<'i, 't>, ) -> Result<SpecifiedValue, ParseError<'i>>350         pub fn parse<'i, 't>(
351             context: &ParserContext,
352             input: &mut Parser<'i, 't>,
353         ) -> Result<SpecifiedValue, ParseError<'i>> {
354             use style_traits::Separator;
355 
356             % if allow_empty:
357             if input.try_parse(|input| input.expect_ident_matching("none")).is_ok() {
358                 return Ok(SpecifiedValue(Default::default()))
359             }
360             % endif
361 
362             let v = style_traits::${separator}::parse(input, |parser| {
363                 single_value::parse(context, parser)
364             })?;
365             Ok(SpecifiedValue(v.into()))
366         }
367 
368         pub use self::single_value::SpecifiedValue as SingleSpecifiedValue;
369 
370         % if not simple_vector_bindings and engine == "gecko":
371         impl SpecifiedValue {
compute_iter<'a, 'cx, 'cx_a>( &'a self, context: &'cx Context<'cx_a>, ) -> computed_value::Iter<'a, 'cx, 'cx_a>372             fn compute_iter<'a, 'cx, 'cx_a>(
373                 &'a self,
374                 context: &'cx Context<'cx_a>,
375             ) -> computed_value::Iter<'a, 'cx, 'cx_a> {
376                 computed_value::Iter::new(context, &self.0)
377             }
378         }
379         % endif
380 
381         impl ToComputedValue for SpecifiedValue {
382             type ComputedValue = computed_value::T;
383 
384             #[inline]
to_computed_value(&self, context: &Context) -> computed_value::T385             fn to_computed_value(&self, context: &Context) -> computed_value::T {
386                 % if not is_shared_list:
387                 use std::iter::FromIterator;
388                 % endif
389                 computed_value::List(computed_value::UnderlyingList::from_iter(
390                     self.0.iter().map(|i| i.to_computed_value(context))
391                 ))
392             }
393 
394             #[inline]
from_computed_value(computed: &computed_value::T) -> Self395             fn from_computed_value(computed: &computed_value::T) -> Self {
396                 let iter = computed.0.iter().map(ToComputedValue::from_computed_value);
397                 SpecifiedValue(iter.collect())
398             }
399         }
400     </%call>
401 </%def>
402 <%def name="longhand(*args, **kwargs)">
403     <%
404         property = data.declare_longhand(*args, **kwargs)
405         if property is None:
406             return ""
407     %>
408     /// ${property.spec}
409     pub mod ${property.ident} {
410         #[allow(unused_imports)]
411         use cssparser::{Parser, BasicParseError, Token};
412         #[allow(unused_imports)]
413         use crate::parser::{Parse, ParserContext};
414         #[allow(unused_imports)]
415         use crate::properties::{UnparsedValue, ShorthandId};
416         #[allow(unused_imports)]
417         use crate::values::{Auto, Either, None_};
418         #[allow(unused_imports)]
419         use crate::error_reporting::ParseErrorReporter;
420         #[allow(unused_imports)]
421         use crate::properties::longhands;
422         #[allow(unused_imports)]
423         use crate::properties::{LonghandId, LonghandIdSet};
424         #[allow(unused_imports)]
425         use crate::properties::{CSSWideKeyword, ComputedValues, PropertyDeclaration};
426         #[allow(unused_imports)]
427         use crate::properties::style_structs;
428         #[allow(unused_imports)]
429         use selectors::parser::SelectorParseErrorKind;
430         #[allow(unused_imports)]
431         use servo_arc::Arc;
432         #[allow(unused_imports)]
433         use style_traits::{ParseError, StyleParseErrorKind};
434         #[allow(unused_imports)]
435         use crate::values::computed::{Context, ToComputedValue};
436         #[allow(unused_imports)]
437         use crate::values::{computed, generics, specified};
438         #[allow(unused_imports)]
439         use crate::Atom;
440         ${caller.body()}
441         #[allow(unused_variables)]
cascade_property( declaration: &PropertyDeclaration, context: &mut computed::Context, )442         pub fn cascade_property(
443             declaration: &PropertyDeclaration,
444             context: &mut computed::Context,
445         ) {
446             context.for_non_inherited_property =
447                 % if property.style_struct.inherited:
448                     None;
449                 % else:
450                     Some(LonghandId::${property.camel_case});
451                 % endif
452 
453             let specified_value = match *declaration {
454                 PropertyDeclaration::${property.camel_case}(ref value) => value,
455                 PropertyDeclaration::CSSWideKeyword(ref declaration) => {
456                     debug_assert_eq!(declaration.id, LonghandId::${property.camel_case});
457                     match declaration.keyword {
458                         % if not property.style_struct.inherited:
459                         CSSWideKeyword::Unset |
460                         % endif
461                         CSSWideKeyword::Initial => {
462                             % if not property.style_struct.inherited:
463                                 debug_assert!(false, "Should be handled in apply_properties");
464                             % else:
465                                 context.builder.reset_${property.ident}();
466                             % endif
467                         },
468                         % if property.style_struct.inherited:
469                         CSSWideKeyword::Unset |
470                         % endif
471                         CSSWideKeyword::Inherit => {
472                             % if property.style_struct.inherited:
473                                 debug_assert!(false, "Should be handled in apply_properties");
474                             % else:
475                                 context.rule_cache_conditions.borrow_mut().set_uncacheable();
476                                 context.builder.inherit_${property.ident}();
477                             % endif
478                         }
479                         CSSWideKeyword::Revert => unreachable!("Should never get here"),
480                     }
481                     return;
482                 }
483                 PropertyDeclaration::WithVariables(..) => {
484                     panic!("variables should already have been substituted")
485                 }
486                 _ => panic!("entered the wrong cascade_property() implementation"),
487             };
488 
489             % if property.ident in SYSTEM_FONT_LONGHANDS and engine == "gecko":
490                 if let Some(sf) = specified_value.get_system() {
491                     longhands::system_font::resolve_system_font(sf, context);
492                 }
493             % endif
494 
495             % if not property.style_struct.inherited and property.logical:
496                 context.rule_cache_conditions.borrow_mut()
497                     .set_writing_mode_dependency(context.builder.writing_mode);
498             % endif
499 
500             % if property.is_vector and not property.simple_vector_bindings and engine == "gecko":
501                 // In the case of a vector property we want to pass down an
502                 // iterator so that this can be computed without allocation.
503                 //
504                 // However, computing requires a context, but the style struct
505                 // being mutated is on the context. We temporarily remove it,
506                 // mutate it, and then put it back. Vector longhands cannot
507                 // touch their own style struct whilst computing, else this will
508                 // panic.
509                 let mut s =
510                     context.builder.take_${data.current_style_struct.name_lower}();
511                 {
512                     let iter = specified_value.compute_iter(context);
513                     s.set_${property.ident}(iter);
514                 }
515                 context.builder.put_${data.current_style_struct.name_lower}(s);
516             % else:
517                 % if property.boxed:
518                 let computed = (**specified_value).to_computed_value(context);
519                 % else:
520                 let computed = specified_value.to_computed_value(context);
521                 % endif
522                 context.builder.set_${property.ident}(computed)
523             % endif
524         }
525 
parse_declared<'i, 't>( context: &ParserContext, input: &mut Parser<'i, 't>, ) -> Result<PropertyDeclaration, ParseError<'i>>526         pub fn parse_declared<'i, 't>(
527             context: &ParserContext,
528             input: &mut Parser<'i, 't>,
529         ) -> Result<PropertyDeclaration, ParseError<'i>> {
530             % if property.allow_quirks != "No":
531                 parse_quirky(context, input, specified::AllowQuirks::${property.allow_quirks})
532             % else:
533                 parse(context, input)
534             % endif
535             % if property.boxed:
536                 .map(Box::new)
537             % endif
538                 .map(PropertyDeclaration::${property.camel_case})
539         }
540     }
541 </%def>
542 
543 <%def name="single_keyword_system(name, values, **kwargs)">
544     <%
545         keyword_kwargs = {a: kwargs.pop(a, None) for a in [
546             'gecko_constant_prefix',
547             'gecko_enum_prefix',
548             'extra_gecko_values',
549             'extra_servo_2013_values',
550             'extra_servo_2020_values',
551             'custom_consts',
552             'gecko_inexhaustive',
553         ]}
554         keyword = keyword=Keyword(name, values, **keyword_kwargs)
555     %>
556     <%call expr="longhand(name, keyword=Keyword(name, values, **keyword_kwargs), **kwargs)">
557         use crate::values::specified::font::SystemFont;
558 
559         pub mod computed_value {
560             #[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
561             #[derive(
562                 Clone,
563                 Copy,
564                 Debug,
565                 Eq,
566                 FromPrimitive,
567                 Hash,
568                 MallocSizeOf,
569                 Parse,
570                 PartialEq,
571                 SpecifiedValueInfo,
572                 ToCss,
573                 ToResolvedValue,
574                 ToShmem,
575             )]
576             pub enum T {
577             % for value in keyword.values_for(engine):
578                 ${to_camel_case(value)},
579             % endfor
580             }
581 
582             ${gecko_keyword_conversion(keyword, keyword.values_for(engine), type="T", cast_to="i32")}
583         }
584 
585         #[cfg_attr(feature = "gecko", derive(MallocSizeOf))]
586         #[derive(Clone, Copy, Debug, Eq, PartialEq, SpecifiedValueInfo, ToCss, ToShmem)]
587         pub enum SpecifiedValue {
588             Keyword(computed_value::T),
589             #[css(skip)]
590             System(SystemFont),
591         }
592 
parse<'i, 't>(_: &ParserContext, input: &mut Parser<'i, 't>) -> Result<SpecifiedValue, ParseError<'i>>593         pub fn parse<'i, 't>(_: &ParserContext, input: &mut Parser<'i, 't>) -> Result<SpecifiedValue, ParseError<'i>> {
594             Ok(SpecifiedValue::Keyword(computed_value::T::parse(input)?))
595         }
596 
597         impl ToComputedValue for SpecifiedValue {
598             type ComputedValue = computed_value::T;
to_computed_value(&self, _cx: &Context) -> Self::ComputedValue599             fn to_computed_value(&self, _cx: &Context) -> Self::ComputedValue {
600                 match *self {
601                     SpecifiedValue::Keyword(v) => v,
602                     % if engine == "gecko":
603                         SpecifiedValue::System(_) => {
604                             _cx.cached_system_font.as_ref().unwrap().${to_rust_ident(name)}
605                         }
606                     % else:
607                         SpecifiedValue::System(system_font) => {
608                             match system_font {}
609                         }
610                     % endif
611                 }
612             }
from_computed_value(other: &computed_value::T) -> Self613             fn from_computed_value(other: &computed_value::T) -> Self {
614                 SpecifiedValue::Keyword(*other)
615             }
616         }
617 
618         #[inline]
get_initial_value() -> computed_value::T619         pub fn get_initial_value() -> computed_value::T {
620             computed_value::T::${to_camel_case(values.split()[0])}
621         }
622         #[inline]
get_initial_specified_value() -> SpecifiedValue623         pub fn get_initial_specified_value() -> SpecifiedValue {
624             SpecifiedValue::Keyword(computed_value::T::${to_camel_case(values.split()[0])})
625         }
626 
627         impl SpecifiedValue {
system_font(f: SystemFont) -> Self628             pub fn system_font(f: SystemFont) -> Self {
629                 SpecifiedValue::System(f)
630             }
get_system(&self) -> Option<SystemFont>631             pub fn get_system(&self) -> Option<SystemFont> {
632                 if let SpecifiedValue::System(s) = *self {
633                     Some(s)
634                 } else {
635                     None
636                 }
637             }
638         }
639     </%call>
640 </%def>
641 
642 <%def name="gecko_keyword_conversion(keyword, values=None, type='SpecifiedValue', cast_to=None)">
643     <%
644         if not values:
645             values = keyword.values_for(engine)
646         maybe_cast = "as %s" % cast_to if cast_to else ""
647         const_type = cast_to if cast_to else "u32"
648     %>
649     #[cfg(feature = "gecko")]
650     impl ${type} {
651         /// Obtain a specified value from a Gecko keyword value
652         ///
653         /// Intended for use with presentation attributes, not style structs
from_gecko_keyword(kw: u32) -> Self654         pub fn from_gecko_keyword(kw: u32) -> Self {
655             use crate::gecko_bindings::structs;
656             % for value in values:
657                 // We can't match on enum values if we're matching on a u32
658                 const ${to_rust_ident(value).upper()}: ${const_type}
659                     = structs::${keyword.gecko_constant(value)} as ${const_type};
660             % endfor
661             match kw ${maybe_cast} {
662                 % for value in values:
663                     ${to_rust_ident(value).upper()} => ${type}::${to_camel_case(value)},
664                 % endfor
665                 _ => panic!("Found unexpected value in style struct for ${keyword.name} property"),
666             }
667         }
668     }
669 </%def>
670 
671 <%def name="gecko_bitflags_conversion(bit_map, gecko_bit_prefix, type, kw_type='u8')">
672     #[cfg(feature = "gecko")]
673     impl ${type} {
674         /// Obtain a specified value from a Gecko keyword value
675         ///
676         /// Intended for use with presentation attributes, not style structs
677         pub fn from_gecko_keyword(kw: ${kw_type}) -> Self {
678             % for gecko_bit in bit_map.values():
679             use crate::gecko_bindings::structs::${gecko_bit_prefix}${gecko_bit};
680             % endfor
681 
682             let mut bits = ${type}::empty();
683             % for servo_bit, gecko_bit in bit_map.items():
684                 if kw & (${gecko_bit_prefix}${gecko_bit} as ${kw_type}) != 0 {
685                     bits |= ${servo_bit};
686                 }
687             % endfor
688             bits
689         }
690 
to_gecko_keyword(self) -> $691         pub fn to_gecko_keyword(self) -> ${kw_type} {
692             % for gecko_bit in bit_map.values():
693             use crate::gecko_bindings::structs::${gecko_bit_prefix}${gecko_bit};
694             % endfor
695 
696             let mut bits: ${kw_type} = 0;
697             // FIXME: if we ensure that the Servo bitflags storage is the same
698             // as Gecko's one, we can just copy it.
699             % for servo_bit, gecko_bit in bit_map.items():
700                 if self.contains(${servo_bit}) {
701                     bits |= ${gecko_bit_prefix}${gecko_bit} as ${kw_type};
702                 }
703             % endfor
704             bits
705         }
706     }
707 </%def>
708 
709 <%def name="single_keyword(name, values, vector=False,
710             needs_conversion=False, **kwargs)">
711     <%
712         keyword_kwargs = {a: kwargs.pop(a, None) for a in [
713             'gecko_constant_prefix',
714             'gecko_enum_prefix',
715             'extra_gecko_values',
716             'extra_servo_2013_values',
717             'extra_servo_2020_values',
718             'gecko_aliases',
719             'servo_2013_aliases',
720             'servo_2020_aliases',
721             'custom_consts',
722             'gecko_inexhaustive',
723             'gecko_strip_moz_prefix',
724         ]}
725     %>
726 
727     <%def name="inner_body(keyword, needs_conversion=False)">
728         pub use self::computed_value::T as SpecifiedValue;
729         pub mod computed_value {
730             #[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
731             #[derive(Clone, Copy, Debug, Eq, FromPrimitive, MallocSizeOf, Parse, PartialEq, SpecifiedValueInfo, ToComputedValue, ToCss, ToResolvedValue, ToShmem)]
732             pub enum T {
733             % for variant in keyword.values_for(engine):
734             <%
735                 aliases = []
736                 for alias, v in keyword.aliases_for(engine).items():
737                     if variant == v:
738                         aliases.append(alias)
739             %>
740             % if aliases:
741             #[parse(aliases = "${','.join(sorted(aliases))}")]
742             % endif
743             ${to_camel_case(variant)},
744             % endfor
745             }
746         }
747         #[inline]
get_initial_value() -> computed_value::T748         pub fn get_initial_value() -> computed_value::T {
749             computed_value::T::${to_camel_case(values.split()[0])}
750         }
751         #[inline]
get_initial_specified_value() -> SpecifiedValue752         pub fn get_initial_specified_value() -> SpecifiedValue {
753             SpecifiedValue::${to_camel_case(values.split()[0])}
754         }
755         #[inline]
parse<'i, 't>(_context: &ParserContext, input: &mut Parser<'i, 't>) -> Result<SpecifiedValue, ParseError<'i>>756         pub fn parse<'i, 't>(_context: &ParserContext, input: &mut Parser<'i, 't>)
757                              -> Result<SpecifiedValue, ParseError<'i>> {
758             SpecifiedValue::parse(input)
759         }
760 
761         % if needs_conversion:
762             <%
763                 conversion_values = keyword.values_for(engine) + list(keyword.aliases_for(engine).keys())
764             %>
765             ${gecko_keyword_conversion(keyword, values=conversion_values)}
766         % endif
767     </%def>
768     % if vector:
769         <%call expr="vector_longhand(name, keyword=Keyword(name, values, **keyword_kwargs), **kwargs)">
770             ${inner_body(Keyword(name, values, **keyword_kwargs))}
771             % if caller:
772             ${caller.body()}
773             % endif
774         </%call>
775     % else:
776         <%call expr="longhand(name, keyword=Keyword(name, values, **keyword_kwargs), **kwargs)">
777             ${inner_body(Keyword(name, values, **keyword_kwargs),
778                          needs_conversion=needs_conversion)}
779             % if caller:
780             ${caller.body()}
781             % endif
782         </%call>
783     % endif
784 </%def>
785 
786 <%def name="shorthand(name, sub_properties, derive_serialize=False,
787                       derive_value_info=True, **kwargs)">
788 <%
789     shorthand = data.declare_shorthand(name, sub_properties.split(), **kwargs)
790     # mako doesn't accept non-string value in parameters with <% %> form, so
791     # we have to workaround it this way.
792     if not isinstance(derive_value_info, bool):
793         derive_value_info = eval(derive_value_info)
794 %>
795     % if shorthand:
796     /// ${shorthand.spec}
797     pub mod ${shorthand.ident} {
798         use cssparser::Parser;
799         use crate::parser::ParserContext;
800         use crate::properties::{PropertyDeclaration, SourcePropertyDeclaration, MaybeBoxed, longhands};
801         #[allow(unused_imports)]
802         use selectors::parser::SelectorParseErrorKind;
803         #[allow(unused_imports)]
804         use std::fmt::{self, Write};
805         #[allow(unused_imports)]
806         use style_traits::{ParseError, StyleParseErrorKind};
807         #[allow(unused_imports)]
808         use style_traits::{CssWriter, KeywordsCollectFn, SpecifiedValueInfo, ToCss};
809 
810         % if derive_value_info:
811         #[derive(SpecifiedValueInfo)]
812         % endif
813         pub struct Longhands {
814             % for sub_property in shorthand.sub_properties:
815                 pub ${sub_property.ident}:
816                     % if sub_property.boxed:
817                         Box<
818                     % endif
819                     longhands::${sub_property.ident}::SpecifiedValue
820                     % if sub_property.boxed:
821                         >
822                     % endif
823                     ,
824             % endfor
825         }
826 
827         /// Represents a serializable set of all of the longhand properties that
828         /// correspond to a shorthand.
829         % if derive_serialize:
830         #[derive(ToCss)]
831         % endif
832         pub struct LonghandsToSerialize<'a> {
833             % for sub_property in shorthand.sub_properties:
834                 pub ${sub_property.ident}:
835                 % if sub_property.may_be_disabled_in(shorthand, engine):
836                     Option<
837                 % endif
838                     &'a longhands::${sub_property.ident}::SpecifiedValue,
839                 % if sub_property.may_be_disabled_in(shorthand, engine):
840                     >,
841                 % endif
842             % endfor
843         }
844 
845         impl<'a> LonghandsToSerialize<'a> {
846             /// Tries to get a serializable set of longhands given a set of
847             /// property declarations.
from_iter(iter: impl Iterator<Item = &'a PropertyDeclaration>) -> Result<Self, ()>848             pub fn from_iter(iter: impl Iterator<Item = &'a PropertyDeclaration>) -> Result<Self, ()> {
849                 // Define all of the expected variables that correspond to the shorthand
850                 % for sub_property in shorthand.sub_properties:
851                     let mut ${sub_property.ident} =
852                         None::< &'a longhands::${sub_property.ident}::SpecifiedValue>;
853                 % endfor
854 
855                 // Attempt to assign the incoming declarations to the expected variables
856                 for declaration in iter {
857                     match *declaration {
858                         % for sub_property in shorthand.sub_properties:
859                             PropertyDeclaration::${sub_property.camel_case}(ref value) => {
860                                 ${sub_property.ident} = Some(value)
861                             },
862                         % endfor
863                         _ => {}
864                     };
865                 }
866 
867                 // If any of the expected variables are missing, return an error
868                 match (
869                     % for sub_property in shorthand.sub_properties:
870                         ${sub_property.ident},
871                     % endfor
872                 ) {
873 
874                     (
875                     % for sub_property in shorthand.sub_properties:
876                         % if sub_property.may_be_disabled_in(shorthand, engine):
877                         ${sub_property.ident},
878                         % else:
879                         Some(${sub_property.ident}),
880                         % endif
881                     % endfor
882                     ) =>
883                     Ok(LonghandsToSerialize {
884                         % for sub_property in shorthand.sub_properties:
885                             ${sub_property.ident},
886                         % endfor
887                     }),
888                     _ => Err(())
889                 }
890             }
891         }
892 
893         /// Parse the given shorthand and fill the result into the
894         /// `declarations` vector.
parse_into<'i, 't>( declarations: &mut SourcePropertyDeclaration, context: &ParserContext, input: &mut Parser<'i, 't>, ) -> Result<(), ParseError<'i>>895         pub fn parse_into<'i, 't>(
896             declarations: &mut SourcePropertyDeclaration,
897             context: &ParserContext,
898             input: &mut Parser<'i, 't>,
899         ) -> Result<(), ParseError<'i>> {
900             #[allow(unused_imports)]
901             use crate::properties::{NonCustomPropertyId, LonghandId};
902             input.parse_entirely(|input| parse_value(context, input)).map(|longhands| {
903                 % for sub_property in shorthand.sub_properties:
904                 % if sub_property.may_be_disabled_in(shorthand, engine):
905                 if NonCustomPropertyId::from(LonghandId::${sub_property.camel_case})
906                     .allowed_in_ignoring_rule_type(context) {
907                 % endif
908                     declarations.push(PropertyDeclaration::${sub_property.camel_case}(
909                         longhands.${sub_property.ident}
910                     ));
911                 % if sub_property.may_be_disabled_in(shorthand, engine):
912                 }
913                 % endif
914                 % endfor
915             })
916         }
917 
918         /// Try to serialize a given shorthand to a string.
to_css(declarations: &[&PropertyDeclaration], dest: &mut crate::str::CssStringWriter) -> fmt::Result919         pub fn to_css(declarations: &[&PropertyDeclaration], dest: &mut crate::str::CssStringWriter) -> fmt::Result {
920             match LonghandsToSerialize::from_iter(declarations.iter().cloned()) {
921                 Ok(longhands) => longhands.to_css(&mut CssWriter::new(dest)),
922                 Err(_) => Ok(())
923             }
924         }
925 
926         ${caller.body()}
927     }
928     % endif
929 </%def>
930 
931 // A shorthand of kind `<property-1> <property-2>?` where both properties have
932 // the same type.
933 <%def name="two_properties_shorthand(
934     name,
935     first_property,
936     second_property,
937     parser_function='crate::parser::Parse::parse',
938     **kwargs
939 )">
940 <%call expr="self.shorthand(name, sub_properties=' '.join([first_property, second_property]), **kwargs)">
941     #[allow(unused_imports)]
942     use crate::parser::Parse;
943     #[allow(unused_imports)]
944     use crate::values::specified;
945 
parse_value<'i, 't>( context: &ParserContext, input: &mut Parser<'i, 't>, ) -> Result<Longhands, ParseError<'i>>946     fn parse_value<'i, 't>(
947         context: &ParserContext,
948         input: &mut Parser<'i, 't>,
949     ) -> Result<Longhands, ParseError<'i>> {
950         let parse_one = |c: &ParserContext, input: &mut Parser<'i, 't>| -> Result<
951             crate::properties::longhands::${to_rust_ident(first_property)}::SpecifiedValue,
952             ParseError<'i>
953         > {
954             ${parser_function}(c, input)
955         };
956 
957         let first = parse_one(context, input)?;
958         let second =
959             input.try_parse(|input| parse_one(context, input)).unwrap_or_else(|_| first.clone());
960         Ok(expanded! {
961             ${to_rust_ident(first_property)}: first,
962             ${to_rust_ident(second_property)}: second,
963         })
964     }
965 
966     impl<'a> ToCss for LonghandsToSerialize<'a>  {
to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: fmt::Write967         fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: fmt::Write {
968             let first = &self.${to_rust_ident(first_property)};
969             let second = &self.${to_rust_ident(second_property)};
970 
971             first.to_css(dest)?;
972             if first != second {
973                 dest.write_str(" ")?;
974                 second.to_css(dest)?;
975             }
976             Ok(())
977         }
978     }
979 </%call>
980 </%def>
981 
982 <%def name="four_sides_shorthand(name, sub_property_pattern,
983                                  parser_function='crate::parser::Parse::parse',
984                                  allow_quirks='No', **kwargs)">
985     <% sub_properties=' '.join(sub_property_pattern % side for side in PHYSICAL_SIDES) %>
986     <%call expr="self.shorthand(name, sub_properties=sub_properties, **kwargs)">
987         #[allow(unused_imports)]
988         use crate::parser::Parse;
989         use crate::values::generics::rect::Rect;
990         #[allow(unused_imports)]
991         use crate::values::specified;
992 
parse_value<'i, 't>( context: &ParserContext, input: &mut Parser<'i, 't>, ) -> Result<Longhands, ParseError<'i>>993         fn parse_value<'i, 't>(
994             context: &ParserContext,
995             input: &mut Parser<'i, 't>,
996         ) -> Result<Longhands, ParseError<'i>> {
997             let rect = Rect::parse_with(context, input, |c, i| -> Result<
998                 crate::properties::longhands::${to_rust_ident(sub_property_pattern % "top")}::SpecifiedValue,
999                 ParseError<'i>
1000             > {
1001             % if allow_quirks != "No":
1002                 ${parser_function}_quirky(c, i, specified::AllowQuirks::${allow_quirks})
1003             % else:
1004                 ${parser_function}(c, i)
1005             % endif
1006             })?;
1007             Ok(expanded! {
1008                 % for index, side in enumerate(["top", "right", "bottom", "left"]):
1009                     ${to_rust_ident(sub_property_pattern % side)}: rect.${index},
1010                 % endfor
1011             })
1012         }
1013 
1014         impl<'a> ToCss for LonghandsToSerialize<'a> {
to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: Write,1015             fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
1016             where
1017                 W: Write,
1018             {
1019                 let rect = Rect::new(
1020                     % for side in ["top", "right", "bottom", "left"]:
1021                     &self.${to_rust_ident(sub_property_pattern % side)},
1022                     % endfor
1023                 );
1024                 rect.to_css(dest)
1025             }
1026         }
1027     </%call>
1028 </%def>
1029 
1030 <%def name="logical_setter_helper(name)">
1031     <%
1032         side = None
1033         size = None
1034         corner = None
1035         axis = None
1036         maybe_side = [s for s in LOGICAL_SIDES if s in name]
1037         maybe_size = [s for s in LOGICAL_SIZES if s in name]
1038         maybe_corner = [s for s in LOGICAL_CORNERS if s in name]
1039         maybe_axis = [s for s in LOGICAL_AXES if name.endswith(s)]
1040         if len(maybe_side) == 1:
1041             side = maybe_side[0]
1042         elif len(maybe_size) == 1:
1043             size = maybe_size[0]
1044         elif len(maybe_corner) == 1:
1045             corner = maybe_corner[0]
1046         elif len(maybe_axis) == 1:
1047             axis = maybe_axis[0]
1048         def phys_ident(side, phy_side):
1049             return to_rust_ident(to_phys(name, side, phy_side))
1050     %>
1051     % if side is not None:
1052         use crate::logical_geometry::PhysicalSide;
1053         match wm.${to_rust_ident(side)}_physical_side() {
1054             % for phy_side in PHYSICAL_SIDES:
1055                 PhysicalSide::${phy_side.title()} => {
1056                     ${caller.inner(physical_ident=phys_ident(side, phy_side))}
1057                 }
1058             % endfor
1059         }
1060     % elif corner is not None:
1061         use crate::logical_geometry::PhysicalCorner;
1062         match wm.${to_rust_ident(corner)}_physical_corner() {
1063             % for phy_corner in PHYSICAL_CORNERS:
1064                 PhysicalCorner::${to_camel_case(phy_corner)} => {
1065                     ${caller.inner(physical_ident=phys_ident(corner, phy_corner))}
1066                 }
1067             % endfor
1068         }
1069     % elif size is not None:
1070         <%
1071             # (horizontal, vertical)
1072             physical_size = ("height", "width")
1073             if size == "inline-size":
1074                 physical_size = ("width", "height")
1075         %>
1076         if wm.is_vertical() {
1077             ${caller.inner(physical_ident=phys_ident(size, physical_size[1]))}
1078         } else {
1079             ${caller.inner(physical_ident=phys_ident(size, physical_size[0]))}
1080         }
1081     % elif axis is not None:
1082         <%
1083             if axis == "inline":
1084                 me, other = "x", "y"
1085             else:
1086                 assert(axis == "block")
1087                 me, other = "y", "x"
1088         %>
1089         if wm.is_vertical() {
1090             ${caller.inner(physical_ident=phys_ident(axis, other))}
1091         } else {
1092             ${caller.inner(physical_ident=phys_ident(axis, me))}
1093         }
1094     % else:
1095         <% raise Exception("Don't know what to do with logical property %s" % name) %>
1096     % endif
1097 </%def>
1098 
1099 <%def name="logical_setter(name)">
1100     /// Set the appropriate physical property for ${name} given a writing mode.
1101     pub fn set_${to_rust_ident(name)}(&mut self,
1102                                       v: longhands::${to_rust_ident(name)}::computed_value::T,
1103                                       wm: WritingMode) {
1104         <%self:logical_setter_helper name="${name}">
1105             <%def name="inner(physical_ident)">
1106                 self.set_${physical_ident}(v)
1107             </%def>
1108         </%self:logical_setter_helper>
1109     }
1110 
1111     /// Copy the appropriate physical property from another struct for ${name}
1112     /// given a writing mode.
1113     pub fn copy_${to_rust_ident(name)}_from(&mut self,
1114                                             other: &Self,
1115                                             wm: WritingMode) {
1116         <%self:logical_setter_helper name="${name}">
1117             <%def name="inner(physical_ident)">
1118                 self.copy_${physical_ident}_from(other)
1119             </%def>
1120         </%self:logical_setter_helper>
1121     }
1122 
1123     /// Copy the appropriate physical property from another struct for ${name}
1124     /// given a writing mode.
1125     pub fn reset_${to_rust_ident(name)}(&mut self,
1126                                         other: &Self,
1127                                         wm: WritingMode) {
1128         self.copy_${to_rust_ident(name)}_from(other, wm)
1129     }
1130 
1131     /// Get the computed value for the appropriate physical property for
1132     /// ${name} given a writing mode.
1133     pub fn clone_${to_rust_ident(name)}(&self, wm: WritingMode)
1134         -> longhands::${to_rust_ident(name)}::computed_value::T {
1135     <%self:logical_setter_helper name="${name}">
1136         <%def name="inner(physical_ident)">
1137             self.clone_${physical_ident}()
1138         </%def>
1139     </%self:logical_setter_helper>
1140     }
1141 </%def>
1142