helpers.mako.rs (37535B)
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, none_value=None, initial_specified_value=None, 13 allow_quirks='No', **kwargs)"> 14 <%def name="predefined_type_inner(name, type, initial_value, parse_method)"> 15 #[allow(unused_imports)] 16 use app_units::Au; 17 #[allow(unused_imports)] 18 use crate::values::specified::AllowQuirks; 19 #[allow(unused_imports)] 20 use crate::Zero; 21 #[allow(unused_imports)] 22 use smallvec::SmallVec; 23 pub use crate::values::specified::${type} as SpecifiedValue; 24 pub mod computed_value { 25 pub use crate::values::computed::${type} as T; 26 } 27 % if initial_value: 28 #[inline] pub fn get_initial_value() -> computed_value::T { ${initial_value} } 29 % endif 30 % if initial_specified_value: 31 #[inline] pub fn get_initial_specified_value() -> SpecifiedValue { ${initial_specified_value} } 32 % endif 33 #[allow(unused_variables)] 34 #[inline] 35 pub fn parse<'i, 't>( 36 context: &ParserContext, 37 input: &mut Parser<'i, 't>, 38 ) -> Result<SpecifiedValue, ParseError<'i>> { 39 % if allow_quirks != "No": 40 specified::${type}::${parse_method}_quirky(context, input, AllowQuirks::${allow_quirks}) 41 % elif parse_method != "parse": 42 specified::${type}::${parse_method}(context, input) 43 % else: 44 <specified::${type} as crate::parser::Parse>::parse(context, input) 45 % endif 46 } 47 </%def> 48 % if vector: 49 <%call 50 expr="vector_longhand(name, predefined_type=type, allow_empty=not initial_value, none_value=none_value, **kwargs)" 51 > 52 ${predefined_type_inner(name, type, initial_value, parse_method)} 53 % if caller: 54 ${caller.body()} 55 % endif 56 </%call> 57 % else: 58 <%call expr="longhand(name, predefined_type=type, **kwargs)"> 59 ${predefined_type_inner(name, type, initial_value, parse_method)} 60 % if caller: 61 ${caller.body()} 62 % endif 63 </%call> 64 % endif 65 </%def> 66 67 // The setup here is roughly: 68 // 69 // * UnderlyingList is the list that is stored in the computed value. This may 70 // be a shared ArcSlice if the property is inherited. 71 // * UnderlyingOwnedList is the list that is used for animation. 72 // * Specified values always use OwnedSlice, since it's more compact. 73 // * computed_value::List is just a convenient alias that you can use for the 74 // computed value list, since this is in the computed_value module. 75 // 76 // If simple_vector_bindings is true, then we don't use the complex iterator 77 // machinery and set_foo_from, and just compute the value like any other 78 // longhand. 79 <%def name="vector_longhand(name, vector_animation_type=None, allow_empty=False, 80 none_value=None, simple_vector_bindings=False, separator='Comma', 81 **kwargs)"> 82 <%call expr="longhand(name, vector=True, 83 simple_vector_bindings=simple_vector_bindings, **kwargs)"> 84 #[allow(unused_imports)] 85 use smallvec::SmallVec; 86 87 pub mod single_value { 88 #[allow(unused_imports)] 89 use cssparser::{Parser, BasicParseError}; 90 #[allow(unused_imports)] 91 use crate::parser::{Parse, ParserContext}; 92 #[allow(unused_imports)] 93 use crate::properties::ShorthandId; 94 #[allow(unused_imports)] 95 use selectors::parser::SelectorParseErrorKind; 96 #[allow(unused_imports)] 97 use style_traits::{ParseError, StyleParseErrorKind}; 98 #[allow(unused_imports)] 99 use crate::values::computed::{Context, ToComputedValue}; 100 #[allow(unused_imports)] 101 use crate::values::{computed, specified}; 102 ${caller.body()} 103 } 104 105 /// The definition of the computed value for ${name}. 106 pub mod computed_value { 107 #[allow(unused_imports)] 108 use crate::values::animated::ToAnimatedValue; 109 #[allow(unused_imports)] 110 use crate::values::resolved::ToResolvedValue; 111 #[allow(unused_imports)] 112 use crate::derives::*; 113 pub use super::single_value::computed_value as single_value; 114 pub use self::single_value::T as SingleComputedValue; 115 % if not allow_empty: 116 use smallvec::SmallVec; 117 % endif 118 use crate::values::computed::ComputedVecIter; 119 120 <% 121 is_shared_list = allow_empty and \ 122 data.longhands_by_name[name].style_struct.inherited 123 %> 124 125 // FIXME(emilio): Add an OwnedNonEmptySlice type, and figure out 126 // something for transition-name, which is the only remaining user 127 // of NotInitial. 128 pub type UnderlyingList<T> = 129 % if allow_empty: 130 % if data.longhands_by_name[name].style_struct.inherited: 131 crate::ArcSlice<T>; 132 % else: 133 crate::OwnedSlice<T>; 134 % endif 135 % else: 136 SmallVec<[T; 1]>; 137 % endif 138 139 pub type UnderlyingOwnedList<T> = 140 % if allow_empty: 141 crate::OwnedSlice<T>; 142 % else: 143 SmallVec<[T; 1]>; 144 % endif 145 146 147 /// The generic type defining the animated and resolved values for 148 /// this property. 149 /// 150 /// Making this type generic allows the compiler to figure out the 151 /// animated value for us, instead of having to implement it 152 /// manually for every type we care about. 153 #[derive(Clone, Debug, MallocSizeOf, PartialEq, ToAnimatedValue, ToResolvedValue, ToCss, ToTyped)] 154 % if separator == "Comma": 155 #[css(comma)] 156 % endif 157 pub struct OwnedList<T>( 158 % if not allow_empty: 159 #[css(iterable)] 160 % else: 161 #[css(if_empty = "none", iterable)] 162 % endif 163 pub UnderlyingOwnedList<T>, 164 ); 165 166 /// The computed value for this property. 167 % if not is_shared_list: 168 pub type ComputedList = OwnedList<single_value::T>; 169 pub use self::OwnedList as List; 170 % else: 171 pub use self::ComputedList as List; 172 173 #[derive(Clone, Debug, MallocSizeOf, PartialEq, ToCss, ToTyped)] 174 % if separator == "Comma": 175 #[css(comma)] 176 % endif 177 pub struct ComputedList( 178 % if not allow_empty: 179 #[css(iterable)] 180 % else: 181 #[css(if_empty = "none", iterable)] 182 % endif 183 % if is_shared_list: 184 #[ignore_malloc_size_of = "Arc"] 185 % endif 186 pub UnderlyingList<single_value::T>, 187 ); 188 189 type ResolvedList = <OwnedList<single_value::T> as ToResolvedValue>::ResolvedValue; 190 impl ToResolvedValue for ComputedList { 191 type ResolvedValue = ResolvedList; 192 193 fn to_resolved_value(self, context: &crate::values::resolved::Context) -> Self::ResolvedValue { 194 OwnedList( 195 self.0 196 .iter() 197 .cloned() 198 .map(|v| v.to_resolved_value(context)) 199 .collect() 200 ) 201 } 202 203 fn from_resolved_value(resolved: Self::ResolvedValue) -> Self { 204 % if not is_shared_list: 205 use std::iter::FromIterator; 206 % endif 207 let iter = 208 resolved.0.into_iter().map(ToResolvedValue::from_resolved_value); 209 ComputedList(UnderlyingList::from_iter(iter)) 210 } 211 } 212 % endif 213 214 % if simple_vector_bindings: 215 impl From<ComputedList> for UnderlyingList<single_value::T> { 216 #[inline] 217 fn from(l: ComputedList) -> Self { 218 l.0 219 } 220 } 221 impl From<UnderlyingList<single_value::T>> for ComputedList { 222 #[inline] 223 fn from(l: UnderlyingList<single_value::T>) -> Self { 224 List(l) 225 } 226 } 227 % endif 228 229 % if vector_animation_type: 230 use crate::values::animated::{Animate, ToAnimatedZero, Procedure, lists}; 231 use crate::values::distance::{SquaredDistance, ComputeSquaredDistance}; 232 233 // FIXME(emilio): For some reason rust thinks that this alias is 234 // unused, even though it's clearly used below? 235 #[allow(unused)] 236 type AnimatedList = <OwnedList<single_value::T> as ToAnimatedValue>::AnimatedValue; 237 % if is_shared_list: 238 impl ToAnimatedValue for ComputedList { 239 type AnimatedValue = AnimatedList; 240 241 fn to_animated_value(self, context: &crate::values::animated::Context) -> Self::AnimatedValue { 242 OwnedList( 243 self.0.iter().map(|v| v.clone().to_animated_value(context)).collect() 244 ) 245 } 246 247 fn from_animated_value(animated: Self::AnimatedValue) -> Self { 248 let iter = 249 animated.0.into_iter().map(ToAnimatedValue::from_animated_value); 250 ComputedList(UnderlyingList::from_iter(iter)) 251 } 252 } 253 % endif 254 255 impl ToAnimatedZero for AnimatedList { 256 fn to_animated_zero(&self) -> Result<Self, ()> { Err(()) } 257 } 258 259 impl Animate for AnimatedList { 260 fn animate( 261 &self, 262 other: &Self, 263 procedure: Procedure, 264 ) -> Result<Self, ()> { 265 Ok(OwnedList( 266 lists::${vector_animation_type}::animate(&self.0, &other.0, procedure)? 267 )) 268 } 269 } 270 impl ComputeSquaredDistance for AnimatedList { 271 fn compute_squared_distance( 272 &self, 273 other: &Self, 274 ) -> Result<SquaredDistance, ()> { 275 lists::${vector_animation_type}::squared_distance(&self.0, &other.0) 276 } 277 } 278 % endif 279 280 /// The computed value, effectively a list of single values. 281 pub use self::ComputedList as T; 282 283 pub type Iter<'a, 'cx, 'cx_a> = ComputedVecIter<'a, 'cx, 'cx_a, super::single_value::SpecifiedValue>; 284 } 285 286 /// The specified value of ${name}. 287 #[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss, ToShmem, ToTyped)] 288 % if none_value: 289 #[value_info(other_values = "none")] 290 % endif 291 % if separator == "Comma": 292 #[css(comma)] 293 % endif 294 pub struct SpecifiedValue( 295 % if not allow_empty: 296 #[css(iterable)] 297 % else: 298 #[css(if_empty = "none", iterable)] 299 % endif 300 pub crate::OwnedSlice<single_value::SpecifiedValue>, 301 ); 302 303 pub fn get_initial_value() -> computed_value::T { 304 % if allow_empty: 305 computed_value::List(Default::default()) 306 % else: 307 let mut v = SmallVec::new(); 308 v.push(single_value::get_initial_value()); 309 computed_value::List(v) 310 % endif 311 } 312 313 pub fn parse<'i, 't>( 314 context: &ParserContext, 315 input: &mut Parser<'i, 't>, 316 ) -> Result<SpecifiedValue, ParseError<'i>> { 317 use style_traits::Separator; 318 319 % if allow_empty or none_value: 320 if input.try_parse(|input| input.expect_ident_matching("none")).is_ok() { 321 % if allow_empty: 322 return Ok(SpecifiedValue(Default::default())) 323 % else: 324 return Ok(SpecifiedValue(crate::OwnedSlice::from(vec![${none_value}]))) 325 % endif 326 } 327 % endif 328 329 let v = style_traits::${separator}::parse(input, |parser| { 330 single_value::parse(context, parser) 331 })?; 332 Ok(SpecifiedValue(v.into())) 333 } 334 335 pub use self::single_value::SpecifiedValue as SingleSpecifiedValue; 336 337 % if not simple_vector_bindings and engine == "gecko": 338 impl SpecifiedValue { 339 fn compute_iter<'a, 'cx, 'cx_a>( 340 &'a self, 341 context: &'cx Context<'cx_a>, 342 ) -> computed_value::Iter<'a, 'cx, 'cx_a> { 343 computed_value::Iter::new(context, &self.0) 344 } 345 } 346 % endif 347 348 impl ToComputedValue for SpecifiedValue { 349 type ComputedValue = computed_value::T; 350 351 #[inline] 352 fn to_computed_value(&self, context: &Context) -> computed_value::T { 353 % if not is_shared_list: 354 use std::iter::FromIterator; 355 % endif 356 computed_value::List(computed_value::UnderlyingList::from_iter( 357 self.0.iter().map(|i| i.to_computed_value(context)) 358 )) 359 } 360 361 #[inline] 362 fn from_computed_value(computed: &computed_value::T) -> Self { 363 let iter = computed.0.iter().map(ToComputedValue::from_computed_value); 364 SpecifiedValue(iter.collect()) 365 } 366 } 367 </%call> 368 </%def> 369 <%def name="longhand(*args, **kwargs)"> 370 <% 371 property = data.declare_longhand(*args, **kwargs) 372 if property is None: 373 return "" 374 %> 375 /// ${property.spec} 376 pub mod ${property.ident} { 377 #[allow(unused_imports)] 378 use crate::derives::*; 379 #[allow(unused_imports)] 380 use cssparser::{Parser, BasicParseError, Token}; 381 #[allow(unused_imports)] 382 use crate::parser::{Parse, ParserContext}; 383 #[allow(unused_imports)] 384 use crate::properties::{UnparsedValue, ShorthandId}; 385 #[allow(unused_imports)] 386 use crate::error_reporting::ParseErrorReporter; 387 #[allow(unused_imports)] 388 use crate::properties::longhands; 389 #[allow(unused_imports)] 390 use crate::properties::{LonghandId, LonghandIdSet}; 391 #[allow(unused_imports)] 392 use crate::properties::{CSSWideKeyword, ComputedValues, PropertyDeclaration}; 393 #[allow(unused_imports)] 394 use crate::properties::style_structs; 395 #[allow(unused_imports)] 396 use selectors::parser::SelectorParseErrorKind; 397 #[allow(unused_imports)] 398 use servo_arc::Arc; 399 #[allow(unused_imports)] 400 use style_traits::{ParseError, StyleParseErrorKind}; 401 #[allow(unused_imports)] 402 use crate::values::computed::{Context, ToComputedValue}; 403 #[allow(unused_imports)] 404 use crate::values::{computed, generics, specified}; 405 #[allow(unused_imports)] 406 use crate::Atom; 407 ${caller.body()} 408 #[allow(unused_variables)] 409 pub unsafe fn cascade_property( 410 declaration: &PropertyDeclaration, 411 context: &mut computed::Context, 412 ) { 413 % if property.logical: 414 declaration.debug_crash("Should physicalize before entering here"); 415 % else: 416 context.for_non_inherited_property = ${"false" if property.style_struct.inherited else "true"}; 417 % if property.logical_group: 418 debug_assert_eq!( 419 declaration.id().as_longhand().unwrap().logical_group(), 420 LonghandId::${property.camel_case}.logical_group(), 421 ); 422 % else: 423 debug_assert_eq!( 424 declaration.id().as_longhand().unwrap(), 425 LonghandId::${property.camel_case}, 426 ); 427 % endif 428 let specified_value = match *declaration { 429 PropertyDeclaration::CSSWideKeyword(ref wk) => { 430 match wk.keyword { 431 % if not property.style_struct.inherited: 432 CSSWideKeyword::Unset | 433 % endif 434 CSSWideKeyword::Initial => { 435 % if not property.style_struct.inherited: 436 declaration.debug_crash("Unexpected initial or unset for non-inherited property"); 437 % else: 438 context.builder.reset_${property.ident}(); 439 % endif 440 }, 441 % if property.style_struct.inherited: 442 CSSWideKeyword::Unset | 443 % endif 444 CSSWideKeyword::Inherit => { 445 % if not property.style_struct.inherited: 446 context.rule_cache_conditions.borrow_mut().set_uncacheable(); 447 % endif 448 % if property.is_zoom_dependent(): 449 if !context.builder.effective_zoom_for_inheritance.is_one() { 450 let old_zoom = context.builder.effective_zoom; 451 context.builder.effective_zoom = context.builder.effective_zoom_for_inheritance; 452 let computed = context.builder.inherited_style.clone_${property.ident}(); 453 let specified = ToComputedValue::from_computed_value(&computed); 454 % if property.boxed: 455 let specified = Box::new(specified); 456 % endif 457 let decl = PropertyDeclaration::${property.camel_case}(specified); 458 cascade_property(&decl, context); 459 context.builder.effective_zoom = old_zoom; 460 return; 461 } 462 % endif 463 % if property.style_struct.inherited: 464 declaration.debug_crash("Unexpected inherit or unset for non-zoom-dependent inherited property"); 465 % else: 466 context.builder.inherit_${property.ident}(); 467 % endif 468 } 469 CSSWideKeyword::RevertLayer | 470 CSSWideKeyword::Revert => { 471 declaration.debug_crash("Found revert/revert-layer not dealt with"); 472 }, 473 } 474 return; 475 }, 476 #[cfg(debug_assertions)] 477 PropertyDeclaration::WithVariables(..) => { 478 declaration.debug_crash("Found variables not substituted"); 479 return; 480 }, 481 _ => unsafe { 482 declaration.unchecked_value_as::<${property.specified_type()}>() 483 }, 484 }; 485 486 % if property.ident in SYSTEM_FONT_LONGHANDS and engine == "gecko": 487 if let Some(sf) = specified_value.get_system() { 488 longhands::system_font::resolve_system_font(sf, context); 489 } 490 % endif 491 492 % if property.is_vector and not property.simple_vector_bindings and engine == "gecko": 493 // In the case of a vector property we want to pass down an 494 // iterator so that this can be computed without allocation. 495 // 496 // However, computing requires a context, but the style struct 497 // being mutated is on the context. We temporarily remove it, 498 // mutate it, and then put it back. Vector longhands cannot 499 // touch their own style struct whilst computing, else this will 500 // panic. 501 let mut s = 502 context.builder.take_${data.current_style_struct.name_lower}(); 503 { 504 let iter = specified_value.compute_iter(context); 505 s.set_${property.ident}(iter); 506 } 507 context.builder.put_${data.current_style_struct.name_lower}(s); 508 % else: 509 % if property.boxed: 510 let computed = (**specified_value).to_computed_value(context); 511 % else: 512 let computed = specified_value.to_computed_value(context); 513 % endif 514 context.builder.set_${property.ident}(computed) 515 % endif 516 % endif 517 } 518 519 pub fn parse_declared<'i, 't>( 520 context: &ParserContext, 521 input: &mut Parser<'i, 't>, 522 ) -> Result<PropertyDeclaration, ParseError<'i>> { 523 % if property.allow_quirks != "No": 524 parse_quirky(context, input, specified::AllowQuirks::${property.allow_quirks}) 525 % else: 526 parse(context, input) 527 % endif 528 % if property.boxed: 529 .map(Box::new) 530 % endif 531 .map(PropertyDeclaration::${property.camel_case}) 532 } 533 } 534 </%def> 535 536 <%def name="gecko_keyword_conversion(keyword, values=None, type='SpecifiedValue', cast_to=None)"> 537 <% 538 if not values: 539 values = keyword.values_for(engine) 540 maybe_cast = "as %s" % cast_to if cast_to else "" 541 const_type = cast_to if cast_to else "u32" 542 %> 543 #[cfg(feature = "gecko")] 544 impl ${type} { 545 /// Obtain a specified value from a Gecko keyword value 546 /// 547 /// Intended for use with presentation attributes, not style structs 548 pub fn from_gecko_keyword(kw: u32) -> Self { 549 use crate::gecko_bindings::structs; 550 % for value in values: 551 // We can't match on enum values if we're matching on a u32 552 const ${to_rust_ident(value).upper()}: ${const_type} 553 = structs::${keyword.gecko_constant(value)} as ${const_type}; 554 % endfor 555 match kw ${maybe_cast} { 556 % for value in values: 557 ${to_rust_ident(value).upper()} => ${type}::${to_camel_case(value)}, 558 % endfor 559 _ => panic!("Found unexpected value in style struct for ${keyword.name} property"), 560 } 561 } 562 } 563 </%def> 564 565 <%def name="gecko_bitflags_conversion(bit_map, gecko_bit_prefix, type, kw_type='u8')"> 566 #[cfg(feature = "gecko")] 567 impl ${type} { 568 /// Obtain a specified value from a Gecko keyword value 569 /// 570 /// Intended for use with presentation attributes, not style structs 571 pub fn from_gecko_keyword(kw: ${kw_type}) -> Self { 572 % for gecko_bit in bit_map.values(): 573 use crate::gecko_bindings::structs::${gecko_bit_prefix}${gecko_bit}; 574 % endfor 575 576 let mut bits = ${type}::empty(); 577 % for servo_bit, gecko_bit in bit_map.items(): 578 if kw & (${gecko_bit_prefix}${gecko_bit} as ${kw_type}) != 0 { 579 bits |= ${servo_bit}; 580 } 581 % endfor 582 bits 583 } 584 585 pub fn to_gecko_keyword(self) -> ${kw_type} { 586 % for gecko_bit in bit_map.values(): 587 use crate::gecko_bindings::structs::${gecko_bit_prefix}${gecko_bit}; 588 % endfor 589 590 let mut bits: ${kw_type} = 0; 591 // FIXME: if we ensure that the Servo bitflags storage is the same 592 // as Gecko's one, we can just copy it. 593 % for servo_bit, gecko_bit in bit_map.items(): 594 if self.contains(${servo_bit}) { 595 bits |= ${gecko_bit_prefix}${gecko_bit} as ${kw_type}; 596 } 597 % endfor 598 bits 599 } 600 } 601 </%def> 602 603 <%def name="single_keyword(name, values, vector=False, 604 needs_conversion=False, **kwargs)"> 605 <% 606 keyword_kwargs = {a: kwargs.pop(a, None) for a in [ 607 'gecko_constant_prefix', 608 'gecko_enum_prefix', 609 'extra_gecko_values', 610 'extra_servo_values', 611 'gecko_aliases', 612 'servo_aliases', 613 'custom_consts', 614 'gecko_inexhaustive', 615 'gecko_strip_moz_prefix', 616 ]} 617 %> 618 619 <%def name="inner_body(keyword, needs_conversion=False)"> 620 pub use self::computed_value::T as SpecifiedValue; 621 pub mod computed_value { 622 #[allow(unused_imports)] 623 use crate::derives::*; 624 #[cfg_attr(feature = "servo", derive(Deserialize, Hash, Serialize))] 625 #[derive(Clone, Copy, Debug, Eq, FromPrimitive, MallocSizeOf, Parse, PartialEq, SpecifiedValueInfo, ToAnimatedValue, ToComputedValue, ToCss, ToResolvedValue, ToShmem, ToTyped)] 626 pub enum T { 627 % for variant in keyword.values_for(engine): 628 <% 629 aliases = [] 630 for alias, v in keyword.aliases_for(engine).items(): 631 if variant == v: 632 aliases.append(alias) 633 %> 634 % if aliases: 635 #[parse(aliases = "${','.join(sorted(aliases))}")] 636 % endif 637 ${to_camel_case(variant)}, 638 % endfor 639 } 640 } 641 #[inline] 642 pub fn get_initial_value() -> computed_value::T { 643 computed_value::T::${to_camel_case(values.split()[0])} 644 } 645 #[inline] 646 pub fn get_initial_specified_value() -> SpecifiedValue { 647 SpecifiedValue::${to_camel_case(values.split()[0])} 648 } 649 #[inline] 650 pub fn parse<'i, 't>(_context: &ParserContext, input: &mut Parser<'i, 't>) 651 -> Result<SpecifiedValue, ParseError<'i>> { 652 SpecifiedValue::parse(input) 653 } 654 655 % if needs_conversion: 656 <% 657 conversion_values = keyword.values_for(engine) + list(keyword.aliases_for(engine).keys()) 658 %> 659 ${gecko_keyword_conversion(keyword, values=conversion_values)} 660 % endif 661 </%def> 662 % if vector: 663 <%call expr="vector_longhand(name, keyword=Keyword(name, values, **keyword_kwargs), **kwargs)"> 664 ${inner_body(Keyword(name, values, **keyword_kwargs))} 665 % if caller: 666 ${caller.body()} 667 % endif 668 </%call> 669 % else: 670 <%call expr="longhand(name, keyword=Keyword(name, values, **keyword_kwargs), **kwargs)"> 671 ${inner_body(Keyword(name, values, **keyword_kwargs), 672 needs_conversion=needs_conversion)} 673 % if caller: 674 ${caller.body()} 675 % endif 676 </%call> 677 % endif 678 </%def> 679 680 <%def name="shorthand(name, sub_properties, derive_serialize=False, 681 derive_value_info=True, **kwargs)"> 682 <% 683 shorthand = data.declare_shorthand(name, sub_properties.split(), **kwargs) 684 # mako doesn't accept non-string value in parameters with <% %> form, so 685 # we have to workaround it this way. 686 if not isinstance(derive_value_info, bool): 687 derive_value_info = eval(derive_value_info) 688 %> 689 % if shorthand: 690 /// ${shorthand.spec} 691 pub mod ${shorthand.ident} { 692 use cssparser::Parser; 693 use crate::parser::ParserContext; 694 use crate::properties::{PropertyDeclaration, SourcePropertyDeclaration, MaybeBoxed, longhands}; 695 #[allow(unused_imports)] 696 use selectors::parser::SelectorParseErrorKind; 697 #[allow(unused_imports)] 698 use std::fmt::{self, Write}; 699 #[allow(unused_imports)] 700 use style_traits::{ParseError, StyleParseErrorKind}; 701 #[allow(unused_imports)] 702 use style_traits::{CssWriter, KeywordsCollectFn, SpecifiedValueInfo, ToCss}; 703 #[allow(unused_imports)] 704 use style_derive::{Animate, ComputeSquaredDistance, ToAnimatedValue, Parse, ToAnimatedZero, ToComputedValue, ToResolvedValue, ToCss, SpecifiedValueInfo, ToTyped}; 705 706 % if derive_value_info: 707 #[derive(SpecifiedValueInfo)] 708 % endif 709 pub struct Longhands { 710 % for sub_property in shorthand.sub_properties: 711 pub ${sub_property.ident}: 712 % if sub_property.boxed: 713 Box< 714 % endif 715 longhands::${sub_property.ident}::SpecifiedValue 716 % if sub_property.boxed: 717 > 718 % endif 719 , 720 % endfor 721 } 722 723 /// Represents a serializable set of all of the longhand properties that 724 /// correspond to a shorthand. 725 % if derive_serialize: 726 #[derive(ToCss)] 727 % endif 728 pub struct LonghandsToSerialize<'a> { 729 % for sub_property in shorthand.sub_properties: 730 pub ${sub_property.ident}: 731 % if sub_property.may_be_disabled_in(shorthand, engine): 732 Option< 733 % endif 734 &'a longhands::${sub_property.ident}::SpecifiedValue, 735 % if sub_property.may_be_disabled_in(shorthand, engine): 736 >, 737 % endif 738 % endfor 739 } 740 741 impl<'a> LonghandsToSerialize<'a> { 742 /// Tries to get a serializable set of longhands given a set of 743 /// property declarations. 744 pub fn from_iter(iter: impl Iterator<Item = &'a PropertyDeclaration>) -> Result<Self, ()> { 745 // Define all of the expected variables that correspond to the shorthand 746 % for sub_property in shorthand.sub_properties: 747 let mut ${sub_property.ident} = 748 None::<&'a longhands::${sub_property.ident}::SpecifiedValue>; 749 % endfor 750 751 // Attempt to assign the incoming declarations to the expected variables 752 for declaration in iter { 753 match *declaration { 754 % for sub_property in shorthand.sub_properties: 755 PropertyDeclaration::${sub_property.camel_case}(ref value) => { 756 ${sub_property.ident} = Some(value) 757 }, 758 % endfor 759 _ => {} 760 }; 761 } 762 763 // If any of the expected variables are missing, return an error 764 match ( 765 % for sub_property in shorthand.sub_properties: 766 ${sub_property.ident}, 767 % endfor 768 ) { 769 770 ( 771 % for sub_property in shorthand.sub_properties: 772 % if sub_property.may_be_disabled_in(shorthand, engine): 773 ${sub_property.ident}, 774 % else: 775 Some(${sub_property.ident}), 776 % endif 777 % endfor 778 ) => 779 Ok(LonghandsToSerialize { 780 % for sub_property in shorthand.sub_properties: 781 ${sub_property.ident}, 782 % endfor 783 }), 784 _ => Err(()) 785 } 786 } 787 } 788 789 /// Parse the given shorthand and fill the result into the 790 /// `declarations` vector. 791 pub fn parse_into<'i, 't>( 792 declarations: &mut SourcePropertyDeclaration, 793 context: &ParserContext, 794 input: &mut Parser<'i, 't>, 795 ) -> Result<(), ParseError<'i>> { 796 #[allow(unused_imports)] 797 use crate::properties::{NonCustomPropertyId, LonghandId}; 798 input.parse_entirely(|input| parse_value(context, input)).map(|longhands| { 799 % for sub_property in shorthand.sub_properties: 800 % if sub_property.may_be_disabled_in(shorthand, engine): 801 if NonCustomPropertyId::from(LonghandId::${sub_property.camel_case}) 802 .allowed_in_ignoring_rule_type(context) { 803 % endif 804 declarations.push(PropertyDeclaration::${sub_property.camel_case}( 805 longhands.${sub_property.ident} 806 )); 807 % if sub_property.may_be_disabled_in(shorthand, engine): 808 } 809 % endif 810 % endfor 811 }) 812 } 813 814 /// Try to serialize a given shorthand to a string. 815 pub fn to_css(declarations: &[&PropertyDeclaration], dest: &mut style_traits::CssStringWriter) -> fmt::Result { 816 match LonghandsToSerialize::from_iter(declarations.iter().cloned()) { 817 Ok(longhands) => longhands.to_css(&mut CssWriter::new(dest)), 818 Err(_) => Ok(()) 819 } 820 } 821 822 ${caller.body()} 823 } 824 % endif 825 </%def> 826 827 // A shorthand of kind `<property-1> <property-2>?` where both properties have 828 // the same type. 829 <%def name="two_properties_shorthand( 830 name, 831 first_property, 832 second_property, 833 parser_function='crate::parser::Parse::parse', 834 **kwargs 835 )"> 836 <%call expr="self.shorthand(name, sub_properties=' '.join([first_property, second_property]), **kwargs)"> 837 #[allow(unused_imports)] 838 use crate::parser::Parse; 839 #[allow(unused_imports)] 840 use crate::values::specified; 841 842 fn parse_value<'i, 't>( 843 context: &ParserContext, 844 input: &mut Parser<'i, 't>, 845 ) -> Result<Longhands, ParseError<'i>> { 846 let parse_one = |c: &ParserContext, input: &mut Parser<'i, 't>| -> Result< 847 crate::properties::longhands::${to_rust_ident(first_property)}::SpecifiedValue, 848 ParseError<'i> 849 > { 850 ${parser_function}(c, input) 851 }; 852 853 let first = parse_one(context, input)?; 854 let second = 855 input.try_parse(|input| parse_one(context, input)).unwrap_or_else(|_| first.clone()); 856 Ok(expanded! { 857 ${to_rust_ident(first_property)}: first, 858 ${to_rust_ident(second_property)}: second, 859 }) 860 } 861 862 impl<'a> ToCss for LonghandsToSerialize<'a> { 863 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: fmt::Write { 864 let first = &self.${to_rust_ident(first_property)}; 865 let second = &self.${to_rust_ident(second_property)}; 866 867 first.to_css(dest)?; 868 if first != second { 869 dest.write_char(' ')?; 870 second.to_css(dest)?; 871 } 872 Ok(()) 873 } 874 } 875 </%call> 876 </%def> 877 878 <%def name="four_sides_shorthand(name, sub_property_pattern, 879 parser_function='crate::parser::Parse::parse', 880 allow_quirks='No', **kwargs)"> 881 <% sub_properties=' '.join(sub_property_pattern % side for side in PHYSICAL_SIDES) %> 882 <%call expr="self.shorthand(name, sub_properties=sub_properties, **kwargs)"> 883 #[allow(unused_imports)] 884 use crate::parser::Parse; 885 use crate::values::generics::rect::Rect; 886 #[allow(unused_imports)] 887 use crate::values::specified; 888 889 fn parse_value<'i, 't>( 890 context: &ParserContext, 891 input: &mut Parser<'i, 't>, 892 ) -> Result<Longhands, ParseError<'i>> { 893 let rect = Rect::parse_with(context, input, |c, i| -> Result< 894 crate::properties::longhands::${to_rust_ident(sub_property_pattern % "top")}::SpecifiedValue, 895 ParseError<'i> 896 > { 897 % if allow_quirks != "No": 898 ${parser_function}_quirky(c, i, specified::AllowQuirks::${allow_quirks}) 899 % else: 900 ${parser_function}(c, i) 901 % endif 902 })?; 903 Ok(expanded! { 904 % for index, side in enumerate(["top", "right", "bottom", "left"]): 905 ${to_rust_ident(sub_property_pattern % side)}: rect.${index}, 906 % endfor 907 }) 908 } 909 910 impl<'a> ToCss for LonghandsToSerialize<'a> { 911 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result 912 where 913 W: Write, 914 { 915 let rect = Rect::new( 916 % for side in ["top", "right", "bottom", "left"]: 917 &self.${to_rust_ident(sub_property_pattern % side)}, 918 % endfor 919 ); 920 rect.to_css(dest) 921 } 922 } 923 </%call> 924 </%def>