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 // This file is a Mako template: http://www.makotemplates.org/
6 
7 // Please note that valid Rust syntax may be mangled by the Mako parser.
8 // For example, Vec<&Foo> will be mangled as Vec&Foo>. To work around these issues, the code
9 // can be escaped. In the above example, Vec<<&Foo> or Vec< &Foo> achieves the desired result of Vec<&Foo>.
10 
11 <%namespace name="helpers" file="/helpers.mako.rs" />
12 
13 #[cfg(feature = "servo")]
14 use app_units::Au;
15 use arrayvec::{ArrayVec, Drain as ArrayVecDrain};
16 use servo_arc::{Arc, UniqueArc};
17 use std::borrow::Cow;
18 use std::{ops, ptr};
19 use std::fmt::{self, Write};
20 use std::mem;
21 
22 use cssparser::{Parser, RGBA, TokenSerializationType};
23 use cssparser::ParserInput;
24 #[cfg(feature = "servo")] use euclid::SideOffsets2D;
25 use crate::context::QuirksMode;
26 #[cfg(feature = "gecko")] use crate::gecko_bindings::structs::{self, nsCSSPropertyID};
27 #[cfg(feature = "servo")] use crate::logical_geometry::LogicalMargin;
28 #[cfg(feature = "servo")] use crate::computed_values;
29 use crate::logical_geometry::WritingMode;
30 use malloc_size_of::{MallocSizeOf, MallocSizeOfOps};
31 use crate::computed_value_flags::*;
32 use crate::hash::FxHashMap;
33 use crate::media_queries::Device;
34 use crate::parser::ParserContext;
35 use crate::selector_parser::PseudoElement;
36 #[cfg(feature = "servo")] use servo_config::prefs;
37 use style_traits::{CssWriter, KeywordsCollectFn, ParseError, ParsingMode};
38 use style_traits::{SpecifiedValueInfo, StyleParseErrorKind, ToCss};
39 use to_shmem::impl_trivial_to_shmem;
40 use crate::stylesheets::{CssRuleType, Origin, UrlExtraData};
41 use crate::use_counters::UseCounters;
42 use crate::values::generics::text::LineHeight;
43 use crate::values::{computed, resolved};
44 use crate::values::computed::NonNegativeLength;
45 use crate::values::serialize_atom_name;
46 use crate::values::specified::font::SystemFont;
47 use crate::rule_tree::StrongRuleNode;
48 use crate::Zero;
49 use crate::str::{CssString, CssStringWriter};
50 use std::cell::Cell;
51 
52 pub use self::declaration_block::*;
53 pub use self::cascade::*;
54 
55 <%!
56     from collections import defaultdict
57     from data import Method, PropertyRestrictions, Keyword, to_rust_ident, \
58                      to_camel_case, RULE_VALUES, SYSTEM_FONT_LONGHANDS
59     import os.path
60 %>
61 
62 #[path="${repr(os.path.join(os.path.dirname(__file__), 'declaration_block.rs'))[1:-1]}"]
63 pub mod declaration_block;
64 #[path="${repr(os.path.join(os.path.dirname(__file__), 'cascade.rs'))[1:-1]}"]
65 pub mod cascade;
66 
67 /// Conversion with fewer impls than From/Into
68 pub trait MaybeBoxed<Out> {
69     /// Convert
maybe_boxed(self) -> Out70     fn maybe_boxed(self) -> Out;
71 }
72 
73 impl<T> MaybeBoxed<T> for T {
74     #[inline]
maybe_boxed(self) -> T75     fn maybe_boxed(self) -> T { self }
76 }
77 
78 impl<T> MaybeBoxed<Box<T>> for T {
79     #[inline]
maybe_boxed(self) -> Box<T>80     fn maybe_boxed(self) -> Box<T> { Box::new(self) }
81 }
82 
83 macro_rules! expanded {
84     ( $( $name: ident: $value: expr ),+ ) => {
85         expanded!( $( $name: $value, )+ )
86     };
87     ( $( $name: ident: $value: expr, )+ ) => {
88         Longhands {
89             $(
90                 $name: MaybeBoxed::maybe_boxed($value),
91             )+
92         }
93     }
94 }
95 
96 /// A module with all the code for longhand properties.
97 #[allow(missing_docs)]
98 pub mod longhands {
99     % for style_struct in data.style_structs:
100     include!("${repr(os.path.join(OUT_DIR, 'longhands/{}.rs'.format(style_struct.name_lower)))[1:-1]}");
101     % endfor
102     pub const ANIMATABLE_PROPERTY_COUNT: usize = ${sum(1 for prop in data.longhands if prop.animatable)};
103 }
104 
105 macro_rules! unwrap_or_initial {
106     ($prop: ident) => (unwrap_or_initial!($prop, $prop));
107     ($prop: ident, $expr: expr) =>
108         ($expr.unwrap_or_else(|| $prop::get_initial_specified_value()));
109 }
110 
111 /// A module with code for all the shorthand css properties, and a few
112 /// serialization helpers.
113 #[allow(missing_docs)]
114 pub mod shorthands {
115     use cssparser::Parser;
116     use crate::parser::{Parse, ParserContext};
117     use style_traits::{ParseError, StyleParseErrorKind};
118     use crate::values::specified;
119 
120     use style_traits::{CssWriter, ToCss};
121     use crate::values::specified::{BorderStyle, Color};
122     use std::fmt::{self, Write};
123 
serialize_directional_border<W, I,>( dest: &mut CssWriter<W>, width: &I, style: &BorderStyle, color: &Color, ) -> fmt::Result where W: Write, I: ToCss,124     fn serialize_directional_border<W, I,>(
125         dest: &mut CssWriter<W>,
126         width: &I,
127         style: &BorderStyle,
128         color: &Color,
129     ) -> fmt::Result
130     where
131         W: Write,
132         I: ToCss,
133     {
134         width.to_css(dest)?;
135         // FIXME(emilio): Should we really serialize the border style if it's
136         // `solid`?
137         dest.write_str(" ")?;
138         style.to_css(dest)?;
139         if *color != Color::CurrentColor {
140             dest.write_str(" ")?;
141             color.to_css(dest)?;
142         }
143         Ok(())
144     }
145 
146     % for style_struct in data.style_structs:
147     include!("${repr(os.path.join(OUT_DIR, 'shorthands/{}.rs'.format(style_struct.name_lower)))[1:-1]}");
148     % endfor
149 
150     // We didn't define the 'all' shorthand using the regular helpers:shorthand
151     // mechanism, since it causes some very large types to be generated.
152     //
153     // Also, make sure logical properties appear before its physical
154     // counter-parts, in order to prevent bugs like:
155     //
156     //   https://bugzilla.mozilla.org/show_bug.cgi?id=1410028
157     //
158     // FIXME(emilio): Adopt the resolution from:
159     //
160     //   https://github.com/w3c/csswg-drafts/issues/1898
161     //
162     // when there is one, whatever that is.
163     <%
164         logical_longhands = []
165         other_longhands = []
166 
167         for p in data.longhands:
168             if p.name in ['direction', 'unicode-bidi']:
169                 continue;
170             if not p.enabled_in_content() and not p.experimental(engine):
171                 continue;
172             if p.logical:
173                 logical_longhands.append(p.name)
174             else:
175                 other_longhands.append(p.name)
176 
177         data.declare_shorthand(
178             "all",
179             logical_longhands + other_longhands,
180             engines="gecko servo-2013 servo-2020",
181             spec="https://drafts.csswg.org/css-cascade-3/#all-shorthand"
182         )
183     %>
184 
185     /// The max amount of longhands that the `all` shorthand will ever contain.
186     pub const ALL_SHORTHAND_MAX_LEN: usize = ${len(logical_longhands + other_longhands)};
187 }
188 
189 <%
190     from itertools import groupby
191 
192     # After this code, `data.longhands` is sorted in the following order:
193     # - first all keyword variants and all variants known to be Copy,
194     # - second all the other variants, such as all variants with the same field
195     #   have consecutive discriminants.
196     # The variable `variants` contain the same entries as `data.longhands` in
197     # the same order, but must exist separately to the data source, because
198     # we then need to add three additional variants `WideKeywordDeclaration`,
199     # `VariableDeclaration` and `CustomDeclaration`.
200 
201     variants = []
202     for property in data.longhands:
203         variants.append({
204             "name": property.camel_case,
205             "type": property.specified_type(),
206             "doc": "`" + property.name + "`",
207             "copy": property.specified_is_copy(),
208         })
209 
210     groups = {}
211     keyfunc = lambda x: x["type"]
212     sortkeys = {}
213     for ty, group in groupby(sorted(variants, key=keyfunc), keyfunc):
214         group = list(group)
215         groups[ty] = group
216         for v in group:
217             if len(group) == 1:
218                 sortkeys[v["name"]] = (not v["copy"], 1, v["name"], "")
219             else:
220                 sortkeys[v["name"]] = (not v["copy"], len(group), ty, v["name"])
221     variants.sort(key=lambda x: sortkeys[x["name"]])
222 
223     # It is extremely important to sort the `data.longhands` array here so
224     # that it is in the same order as `variants`, for `LonghandId` and
225     # `PropertyDeclarationId` to coincide.
226     data.longhands.sort(key=lambda x: sortkeys[x.camel_case])
227 %>
228 
229 // WARNING: It is *really* important for the variants of `LonghandId`
230 // and `PropertyDeclaration` to be defined in the exact same order,
231 // with the exception of `CSSWideKeyword`, `WithVariables` and `Custom`,
232 // which don't exist in `LonghandId`.
233 
234 <%
235     extra = [
236         {
237             "name": "CSSWideKeyword",
238             "type": "WideKeywordDeclaration",
239             "doc": "A CSS-wide keyword.",
240             "copy": False,
241         },
242         {
243             "name": "WithVariables",
244             "type": "VariableDeclaration",
245             "doc": "An unparsed declaration.",
246             "copy": False,
247         },
248         {
249             "name": "Custom",
250             "type": "CustomDeclaration",
251             "doc": "A custom property declaration.",
252             "copy": False,
253         },
254     ]
255     for v in extra:
256         variants.append(v)
257         groups[v["type"]] = [v]
258 %>
259 
260 /// Servo's representation for a property declaration.
261 #[derive(ToShmem)]
262 #[repr(u16)]
263 pub enum PropertyDeclaration {
264     % for variant in variants:
265     /// ${variant["doc"]}
266     ${variant["name"]}(${variant["type"]}),
267     % endfor
268 }
269 
270 #[repr(C)]
271 struct PropertyDeclarationVariantRepr<T> {
272     tag: u16,
273     value: T
274 }
275 
276 impl Clone for PropertyDeclaration {
277     #[inline]
clone(&self) -> Self278     fn clone(&self) -> Self {
279         use self::PropertyDeclaration::*;
280 
281         <%
282             [copy, others] = [list(g) for _, g in groupby(variants, key=lambda x: not x["copy"])]
283         %>
284 
285         let self_tag = unsafe {
286             (*(self as *const _ as *const PropertyDeclarationVariantRepr<()>)).tag
287         };
288         if self_tag <= LonghandId::${copy[-1]["name"]} as u16 {
289             #[derive(Clone, Copy)]
290             #[repr(u16)]
291             enum CopyVariants {
292                 % for v in copy:
293                 _${v["name"]}(${v["type"]}),
294                 % endfor
295             }
296 
297             unsafe {
298                 let mut out = mem::MaybeUninit::uninit();
299                 ptr::write(
300                     out.as_mut_ptr() as *mut CopyVariants,
301                     *(self as *const _ as *const CopyVariants),
302                 );
303                 return out.assume_init();
304             }
305         }
306 
307         // This function ensures that all properties not handled above
308         // do not have a specified value implements Copy. If you hit
309         // compile error here, you may want to add the type name into
310         // Longhand.specified_is_copy in data.py.
311         fn _static_assert_others_are_not_copy() {
312             struct Helper<T>(T);
313             trait AssertCopy { fn assert() {} }
314             trait AssertNotCopy { fn assert() {} }
315             impl<T: Copy> AssertCopy for Helper<T> {}
316             % for ty in sorted(set(x["type"] for x in others)):
317             impl AssertNotCopy for Helper<${ty}> {}
318             Helper::<${ty}>::assert();
319             % endfor
320         }
321 
322         match *self {
323             ${" |\n".join("{}(..)".format(v["name"]) for v in copy)} => {
324                 unsafe { debug_unreachable!() }
325             }
326             % for ty, vs in groupby(others, key=lambda x: x["type"]):
327             <%
328                 vs = list(vs)
329             %>
330             % if len(vs) == 1:
331             ${vs[0]["name"]}(ref value) => {
332                 ${vs[0]["name"]}(value.clone())
333             }
334             % else:
335             ${" |\n".join("{}(ref value)".format(v["name"]) for v in vs)} => {
336                 unsafe {
337                     let mut out = mem::MaybeUninit::uninit();
338                     ptr::write(
339                         out.as_mut_ptr() as *mut PropertyDeclarationVariantRepr<${ty}>,
340                         PropertyDeclarationVariantRepr {
341                             tag: *(self as *const _ as *const u16),
342                             value: value.clone(),
343                         },
344                     );
345                     out.assume_init()
346                 }
347             }
348             % endif
349             % endfor
350         }
351     }
352 }
353 
354 impl PartialEq for PropertyDeclaration {
355     #[inline]
eq(&self, other: &Self) -> bool356     fn eq(&self, other: &Self) -> bool {
357         use self::PropertyDeclaration::*;
358 
359         unsafe {
360             let this_repr =
361                 &*(self as *const _ as *const PropertyDeclarationVariantRepr<()>);
362             let other_repr =
363                 &*(other as *const _ as *const PropertyDeclarationVariantRepr<()>);
364             if this_repr.tag != other_repr.tag {
365                 return false;
366             }
367             match *self {
368                 % for ty, vs in groupby(variants, key=lambda x: x["type"]):
369                 ${" |\n".join("{}(ref this)".format(v["name"]) for v in vs)} => {
370                     let other_repr =
371                         &*(other as *const _ as *const PropertyDeclarationVariantRepr<${ty}>);
372                     *this == other_repr.value
373                 }
374                 % endfor
375             }
376         }
377     }
378 }
379 
380 impl MallocSizeOf for PropertyDeclaration {
381     #[inline]
size_of(&self, ops: &mut MallocSizeOfOps) -> usize382     fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
383         use self::PropertyDeclaration::*;
384 
385         match *self {
386             % for ty, vs in groupby(variants, key=lambda x: x["type"]):
387             ${" | ".join("{}(ref value)".format(v["name"]) for v in vs)} => {
388                 value.size_of(ops)
389             }
390             % endfor
391         }
392     }
393 }
394 
395 
396 impl PropertyDeclaration {
397     /// Like the method on ToCss, but without the type parameter to avoid
398     /// accidentally monomorphizing this large function multiple times for
399     /// different writers.
to_css(&self, dest: &mut CssStringWriter) -> fmt::Result400     pub fn to_css(&self, dest: &mut CssStringWriter) -> fmt::Result {
401         use self::PropertyDeclaration::*;
402 
403         let mut dest = CssWriter::new(dest);
404         match *self {
405             % for ty, vs in groupby(variants, key=lambda x: x["type"]):
406             ${" | ".join("{}(ref value)".format(v["name"]) for v in vs)} => {
407                 value.to_css(&mut dest)
408             }
409             % endfor
410         }
411     }
412 }
413 
414 /// A module with all the code related to animated properties.
415 ///
416 /// This needs to be "included" by mako at least after all longhand modules,
417 /// given they populate the global data.
418 pub mod animated_properties {
419     <%include file="/helpers/animated_properties.mako.rs" />
420 }
421 
422 /// A longhand or shorthand property.
423 #[derive(Clone, Copy, Debug)]
424 pub struct NonCustomPropertyId(usize);
425 
426 /// The length of all the non-custom properties.
427 pub const NON_CUSTOM_PROPERTY_ID_COUNT: usize =
428     ${len(data.longhands) + len(data.shorthands) + len(data.all_aliases())};
429 
430 /// The length of all counted unknown properties.
431 pub const COUNTED_UNKNOWN_PROPERTY_COUNT: usize = ${len(data.counted_unknown_properties)};
432 
433 % if engine == "gecko":
434 #[allow(dead_code)]
static_assert_nscsspropertyid()435 unsafe fn static_assert_nscsspropertyid() {
436     % for i, property in enumerate(data.longhands + data.shorthands + data.all_aliases()):
437     std::mem::transmute::<[u8; ${i}], [u8; ${property.nscsspropertyid()} as usize]>([0; ${i}]); // ${property.name}
438     % endfor
439 }
440 % endif
441 
442 impl NonCustomPropertyId {
443     /// Returns the underlying index, used for use counter.
bit(self) -> usize444     pub fn bit(self) -> usize {
445         self.0
446     }
447 
448     #[cfg(feature = "gecko")]
449     #[inline]
to_nscsspropertyid(self) -> nsCSSPropertyID450     fn to_nscsspropertyid(self) -> nsCSSPropertyID {
451         // unsafe: guaranteed by static_assert_nscsspropertyid above.
452         unsafe { std::mem::transmute(self.0 as i32) }
453     }
454 
455     /// Convert an `nsCSSPropertyID` into a `NonCustomPropertyId`.
456     #[cfg(feature = "gecko")]
457     #[inline]
from_nscsspropertyid(prop: nsCSSPropertyID) -> Result<Self, ()>458     pub fn from_nscsspropertyid(prop: nsCSSPropertyID) -> Result<Self, ()> {
459         let prop = prop as i32;
460         if prop < 0 {
461             return Err(());
462         }
463         if prop >= NON_CUSTOM_PROPERTY_ID_COUNT as i32 {
464             return Err(());
465         }
466         // unsafe: guaranteed by static_assert_nscsspropertyid above.
467         Ok(unsafe { std::mem::transmute(prop as usize) })
468     }
469 
470     /// Get the property name.
471     #[inline]
name(self) -> &'static str472     pub fn name(self) -> &'static str {
473         static MAP: [&'static str; NON_CUSTOM_PROPERTY_ID_COUNT] = [
474             % for property in data.longhands + data.shorthands + data.all_aliases():
475             "${property.name}",
476             % endfor
477         ];
478         MAP[self.0]
479     }
480 
481     /// Returns whether this property is transitionable.
482     #[inline]
is_transitionable(self) -> bool483     pub fn is_transitionable(self) -> bool {
484         ${static_non_custom_property_id_set("TRANSITIONABLE", lambda p: p.transitionable)}
485         TRANSITIONABLE.contains(self)
486     }
487 
488     /// Returns whether this property is animatable.
489     #[inline]
is_animatable(self) -> bool490     pub fn is_animatable(self) -> bool {
491         ${static_non_custom_property_id_set("ANIMATABLE", lambda p: p.animatable)}
492         ANIMATABLE.contains(self)
493     }
494 
495     #[inline]
enabled_for_all_content(self) -> bool496     fn enabled_for_all_content(self) -> bool {
497         ${static_non_custom_property_id_set(
498             "EXPERIMENTAL",
499             lambda p: p.experimental(engine)
500         )}
501 
502         ${static_non_custom_property_id_set(
503             "ALWAYS_ENABLED",
504             lambda p: (not p.experimental(engine)) and p.enabled_in_content()
505         )}
506 
507         let passes_pref_check = || {
508             % if engine == "gecko":
509                 unsafe { structs::nsCSSProps_gPropertyEnabled[self.0] }
510             % else:
511                 static PREF_NAME: [Option< &str>; ${
512                     len(data.longhands) + len(data.shorthands) + len(data.all_aliases())
513                 }] = [
514                     % for property in data.longhands + data.shorthands + data.all_aliases():
515                         <%
516                             attrs = {"servo-2013": "servo_2013_pref", "servo-2020": "servo_2020_pref"}
517                             pref = getattr(property, attrs[engine])
518                         %>
519                         % if pref:
520                             Some("${pref}"),
521                         % else:
522                             None,
523                         % endif
524                     % endfor
525                 ];
526                 let pref = match PREF_NAME[self.0] {
527                     None => return true,
528                     Some(pref) => pref,
529                 };
530 
531                 prefs::pref_map().get(pref).as_bool().unwrap_or(false)
532             % endif
533         };
534 
535         if ALWAYS_ENABLED.contains(self) {
536             return true
537         }
538 
539         if EXPERIMENTAL.contains(self) && passes_pref_check() {
540             return true
541         }
542 
543         false
544     }
545 
546     /// Returns whether a given rule allows a given property.
547     #[inline]
allowed_in_rule(self, rule_type: CssRuleType) -> bool548     pub fn allowed_in_rule(self, rule_type: CssRuleType) -> bool {
549         debug_assert!(
550             matches!(
551                 rule_type,
552                 CssRuleType::Keyframe | CssRuleType::Page | CssRuleType::Style
553             ),
554             "Declarations are only expected inside a keyframe, page, or style rule."
555         );
556 
557         static MAP: [u8; NON_CUSTOM_PROPERTY_ID_COUNT] = [
558             % for property in data.longhands + data.shorthands + data.all_aliases():
559             ${property.rule_types_allowed},
560             % endfor
561         ];
562         match rule_type {
563             % for name in RULE_VALUES:
564                 CssRuleType::${name} => MAP[self.0] & ${RULE_VALUES[name]} != 0,
565             % endfor
566             _ => true
567         }
568     }
569 
allowed_in(self, context: &ParserContext) -> bool570     fn allowed_in(self, context: &ParserContext) -> bool {
571         if !self.allowed_in_rule(context.rule_type()) {
572             return false;
573         }
574 
575         self.allowed_in_ignoring_rule_type(context)
576     }
577 
578 
allowed_in_ignoring_rule_type(self, context: &ParserContext) -> bool579     fn allowed_in_ignoring_rule_type(self, context: &ParserContext) -> bool {
580         // The semantics of these are kinda hard to reason about, what follows
581         // is a description of the different combinations that can happen with
582         // these three sets.
583         //
584         // Experimental properties are generally controlled by prefs, but an
585         // experimental property explicitly enabled in certain context (UA or
586         // chrome sheets) is always usable in the context regardless of the
587         // pref value.
588         //
589         // Non-experimental properties are either normal properties which are
590         // usable everywhere, or internal-only properties which are only usable
591         // in certain context they are explicitly enabled in.
592         if self.enabled_for_all_content() {
593             return true;
594         }
595 
596         ${static_non_custom_property_id_set(
597             "ENABLED_IN_UA_SHEETS",
598             lambda p: p.explicitly_enabled_in_ua_sheets()
599         )}
600         ${static_non_custom_property_id_set(
601             "ENABLED_IN_CHROME",
602             lambda p: p.explicitly_enabled_in_chrome()
603         )}
604 
605         if context.stylesheet_origin == Origin::UserAgent &&
606             ENABLED_IN_UA_SHEETS.contains(self)
607         {
608             return true
609         }
610 
611         if context.chrome_rules_enabled() && ENABLED_IN_CHROME.contains(self) {
612             return true
613         }
614 
615         false
616     }
617 
618     /// The supported types of this property. The return value should be
619     /// style_traits::CssType when it can become a bitflags type.
supported_types(&self) -> u8620     fn supported_types(&self) -> u8 {
621         const SUPPORTED_TYPES: [u8; ${len(data.longhands) + len(data.shorthands)}] = [
622             % for prop in data.longhands:
623                 <${prop.specified_type()} as SpecifiedValueInfo>::SUPPORTED_TYPES,
624             % endfor
625             % for prop in data.shorthands:
626             % if prop.name == "all":
627                 0, // 'all' accepts no value other than CSS-wide keywords
628             % else:
629                 <shorthands::${prop.ident}::Longhands as SpecifiedValueInfo>::SUPPORTED_TYPES,
630             % endif
631             % endfor
632         ];
633         SUPPORTED_TYPES[self.0]
634     }
635 
636     /// See PropertyId::collect_property_completion_keywords.
collect_property_completion_keywords(&self, f: KeywordsCollectFn)637     fn collect_property_completion_keywords(&self, f: KeywordsCollectFn) {
638         fn do_nothing(_: KeywordsCollectFn) {}
639         const COLLECT_FUNCTIONS: [fn(KeywordsCollectFn);
640                                   ${len(data.longhands) + len(data.shorthands)}] = [
641             % for prop in data.longhands:
642                 <${prop.specified_type()} as SpecifiedValueInfo>::collect_completion_keywords,
643             % endfor
644             % for prop in data.shorthands:
645             % if prop.name == "all":
646                 do_nothing, // 'all' accepts no value other than CSS-wide keywords
647             % else:
648                 <shorthands::${prop.ident}::Longhands as SpecifiedValueInfo>::
649                     collect_completion_keywords,
650             % endif
651             % endfor
652         ];
653         COLLECT_FUNCTIONS[self.0](f);
654     }
655 
656     /// Turns this `NonCustomPropertyId` into a `PropertyId`.
657     #[inline]
to_property_id(self) -> PropertyId658     pub fn to_property_id(self) -> PropertyId {
659         use std::mem::transmute;
660         if self.0 < ${len(data.longhands)} {
661             return unsafe {
662                 PropertyId::Longhand(transmute(self.0 as u16))
663             }
664         }
665         if self.0 < ${len(data.longhands) + len(data.shorthands)} {
666             return unsafe {
667                 PropertyId::Shorthand(transmute((self.0 - ${len(data.longhands)}) as u16))
668             }
669         }
670         assert!(self.0 < NON_CUSTOM_PROPERTY_ID_COUNT);
671         let alias_id: AliasId = unsafe {
672             transmute((self.0 - ${len(data.longhands) + len(data.shorthands)}) as u16)
673         };
674 
675         match alias_id.aliased_property() {
676             AliasedPropertyId::Longhand(longhand) => PropertyId::LonghandAlias(longhand, alias_id),
677             AliasedPropertyId::Shorthand(shorthand) => PropertyId::ShorthandAlias(shorthand, alias_id),
678         }
679     }
680 }
681 
682 impl From<LonghandId> for NonCustomPropertyId {
683     #[inline]
from(id: LonghandId) -> Self684     fn from(id: LonghandId) -> Self {
685         NonCustomPropertyId(id as usize)
686     }
687 }
688 
689 impl From<ShorthandId> for NonCustomPropertyId {
690     #[inline]
from(id: ShorthandId) -> Self691     fn from(id: ShorthandId) -> Self {
692         NonCustomPropertyId((id as usize) + ${len(data.longhands)})
693     }
694 }
695 
696 impl From<AliasId> for NonCustomPropertyId {
697     #[inline]
from(id: AliasId) -> Self698     fn from(id: AliasId) -> Self {
699         NonCustomPropertyId(id as usize + ${len(data.longhands) + len(data.shorthands)})
700     }
701 }
702 
703 /// A set of all properties
704 #[derive(Clone, PartialEq)]
705 pub struct NonCustomPropertyIdSet {
706     storage: [u32; (NON_CUSTOM_PROPERTY_ID_COUNT - 1 + 32) / 32]
707 }
708 
709 impl NonCustomPropertyIdSet {
710     /// Creates an empty `NonCustomPropertyIdSet`.
new() -> Self711     pub fn new() -> Self {
712         Self {
713             storage: Default::default(),
714         }
715     }
716 
717     /// Insert a non-custom-property in the set.
718     #[inline]
insert(&mut self, id: NonCustomPropertyId)719     pub fn insert(&mut self, id: NonCustomPropertyId) {
720         let bit = id.0;
721         self.storage[bit / 32] |= 1 << (bit % 32);
722     }
723 
724     /// Return whether the given property is in the set
725     #[inline]
contains(&self, id: NonCustomPropertyId) -> bool726     pub fn contains(&self, id: NonCustomPropertyId) -> bool {
727         let bit = id.0;
728         (self.storage[bit / 32] & (1 << (bit % 32))) != 0
729     }
730 }
731 
732 <%def name="static_non_custom_property_id_set(name, is_member)">
733 static ${name}: NonCustomPropertyIdSet = NonCustomPropertyIdSet {
734     <%
735         storage = [0] * int((len(data.longhands) + len(data.shorthands) + len(data.all_aliases()) - 1 + 32) / 32)
736         for i, property in enumerate(data.longhands + data.shorthands + data.all_aliases()):
737             if is_member(property):
738                 storage[int(i / 32)] |= 1 << (i % 32)
739     %>
740     storage: [${", ".join("0x%x" % word for word in storage)}]
741 };
742 </%def>
743 
744 <%def name="static_longhand_id_set(name, is_member)">
745 static ${name}: LonghandIdSet = LonghandIdSet {
746     <%
747         storage = [0] * int((len(data.longhands) - 1 + 32) / 32)
748         for i, property in enumerate(data.longhands):
749             if is_member(property):
750                 storage[int(i / 32)] |= 1 << (i % 32)
751     %>
752     storage: [${", ".join("0x%x" % word for word in storage)}]
753 };
754 </%def>
755 
756 <%
757     logical_groups = defaultdict(list)
758     for prop in data.longhands:
759         if prop.logical_group:
760             logical_groups[prop.logical_group].append(prop)
761 
762     for group, props in logical_groups.items():
763         logical_count = sum(1 for p in props if p.logical)
764         if logical_count * 2 != len(props):
765             raise RuntimeError("Logical group {} has ".format(group) +
766                                "unbalanced logical / physical properties")
767 
768     FIRST_LINE_RESTRICTIONS = PropertyRestrictions.first_line(data)
769     FIRST_LETTER_RESTRICTIONS = PropertyRestrictions.first_letter(data)
770     MARKER_RESTRICTIONS = PropertyRestrictions.marker(data)
771     PLACEHOLDER_RESTRICTIONS = PropertyRestrictions.placeholder(data)
772     CUE_RESTRICTIONS = PropertyRestrictions.cue(data)
773 
774     def restriction_flags(property):
775         name = property.name
776         flags = []
777         if name in FIRST_LINE_RESTRICTIONS:
778             flags.append("APPLIES_TO_FIRST_LINE")
779         if name in FIRST_LETTER_RESTRICTIONS:
780             flags.append("APPLIES_TO_FIRST_LETTER")
781         if name in PLACEHOLDER_RESTRICTIONS:
782             flags.append("APPLIES_TO_PLACEHOLDER")
783         if name in MARKER_RESTRICTIONS:
784             flags.append("APPLIES_TO_MARKER")
785         if name in CUE_RESTRICTIONS:
786             flags.append("APPLIES_TO_CUE")
787         return flags
788 
789 %>
790 
791 /// A group for properties which may override each other
792 /// via logical resolution.
793 #[derive(Clone, Copy, Eq, Hash, PartialEq)]
794 #[repr(u8)]
795 pub enum LogicalGroup {
796     % for i, group in enumerate(logical_groups.keys()):
797     /// ${group}
798     ${to_camel_case(group)} = ${i},
799     % endfor
800 }
801 
802 
803 /// A set of logical groups.
804 #[derive(Clone, Copy, Debug, Default, MallocSizeOf, PartialEq)]
805 pub struct LogicalGroupSet {
806     storage: [u32; (${len(logical_groups)} - 1 + 32) / 32]
807 }
808 
809 impl LogicalGroupSet {
810     /// Creates an empty `NonCustomPropertyIdSet`.
new() -> Self811     pub fn new() -> Self {
812         Self {
813             storage: Default::default(),
814         }
815     }
816 
817     /// Return whether the given group is in the set
818     #[inline]
contains(&self, g: LogicalGroup) -> bool819     pub fn contains(&self, g: LogicalGroup) -> bool {
820         let bit = g as usize;
821         (self.storage[bit / 32] & (1 << (bit % 32))) != 0
822     }
823 
824     /// Insert a group the set.
825     #[inline]
insert(&mut self, g: LogicalGroup)826     pub fn insert(&mut self, g: LogicalGroup) {
827         let bit = g as usize;
828         self.storage[bit / 32] |= 1 << (bit % 32);
829     }
830 }
831 
832 /// A set of longhand properties
833 #[derive(Clone, Copy, Debug, Default, MallocSizeOf, PartialEq)]
834 pub struct LonghandIdSet {
835     storage: [u32; (${len(data.longhands)} - 1 + 32) / 32]
836 }
837 
838 impl_trivial_to_shmem!(LonghandIdSet);
839 
840 /// An iterator over a set of longhand ids.
841 pub struct LonghandIdSetIterator<'a> {
842     longhands: &'a LonghandIdSet,
843     cur: usize,
844 }
845 
846 impl<'a> Iterator for LonghandIdSetIterator<'a> {
847     type Item = LonghandId;
848 
next(&mut self) -> Option<Self::Item>849     fn next(&mut self) -> Option<Self::Item> {
850         use std::mem;
851 
852         loop {
853             if self.cur >= ${len(data.longhands)} {
854                 return None;
855             }
856 
857             let id: LonghandId = unsafe { mem::transmute(self.cur as u16) };
858             self.cur += 1;
859 
860             if self.longhands.contains(id) {
861                 return Some(id);
862             }
863         }
864     }
865 }
866 
867 impl LonghandIdSet {
868     #[inline]
reset() -> &'static Self869     fn reset() -> &'static Self {
870         ${static_longhand_id_set("RESET", lambda p: not p.style_struct.inherited)}
871         &RESET
872     }
873 
874     #[inline]
animatable() -> &'static Self875     fn animatable() -> &'static Self {
876         ${static_longhand_id_set("ANIMATABLE", lambda p: p.animatable)}
877         &ANIMATABLE
878     }
879 
880     #[inline]
discrete_animatable() -> &'static Self881     fn discrete_animatable() -> &'static Self {
882         ${static_longhand_id_set("DISCRETE_ANIMATABLE", lambda p: p.animation_value_type == "discrete")}
883         &DISCRETE_ANIMATABLE
884     }
885 
886     #[inline]
logical() -> &'static Self887     fn logical() -> &'static Self {
888         ${static_longhand_id_set("LOGICAL", lambda p: p.logical)}
889         &LOGICAL
890     }
891 
892     /// Returns the set of longhands that are ignored when document colors are
893     /// disabled.
894     #[inline]
ignored_when_colors_disabled() -> &'static Self895     pub fn ignored_when_colors_disabled() -> &'static Self {
896         ${static_longhand_id_set(
897             "IGNORED_WHEN_COLORS_DISABLED",
898             lambda p: p.ignored_when_colors_disabled
899         )}
900         &IGNORED_WHEN_COLORS_DISABLED
901     }
902 
903     /// Returns the set of properties that are declared as having no effect on
904     /// Gecko <scrollbar> elements or their descendant scrollbar parts.
905     #[cfg(debug_assertions)]
906     #[cfg(feature = "gecko")]
907     #[inline]
has_no_effect_on_gecko_scrollbars() -> &'static Self908     pub fn has_no_effect_on_gecko_scrollbars() -> &'static Self {
909         // data.py asserts that has_no_effect_on_gecko_scrollbars is True or
910         // False for properties that are inherited and Gecko pref controlled,
911         // and is None for all other properties.
912         ${static_longhand_id_set(
913             "HAS_NO_EFFECT_ON_SCROLLBARS",
914             lambda p: p.has_effect_on_gecko_scrollbars is False
915         )}
916         &HAS_NO_EFFECT_ON_SCROLLBARS
917     }
918 
919     /// Returns the set of padding properties for the purpose of disabling
920     /// native appearance.
921     #[inline]
padding_properties() -> &'static Self922     pub fn padding_properties() -> &'static Self {
923         <% assert "padding" in logical_groups %>
924         ${static_longhand_id_set(
925             "PADDING_PROPERTIES",
926             lambda p: p.logical_group == "padding"
927         )}
928         &PADDING_PROPERTIES
929     }
930 
931     /// Returns the set of border properties for the purpose of disabling native
932     /// appearance.
933     #[inline]
border_background_properties() -> &'static Self934     pub fn border_background_properties() -> &'static Self {
935         ${static_longhand_id_set(
936             "BORDER_BACKGROUND_PROPERTIES",
937             lambda p: (p.logical_group and p.logical_group.startswith("border")) or \
938                        p.name in ["background-color", "background-image"]
939         )}
940         &BORDER_BACKGROUND_PROPERTIES
941     }
942 
943     /// Iterate over the current longhand id set.
iter(&self) -> LonghandIdSetIterator944     pub fn iter(&self) -> LonghandIdSetIterator {
945         LonghandIdSetIterator { longhands: self, cur: 0, }
946     }
947 
948     /// Returns whether this set contains at least every longhand that `other`
949     /// also contains.
contains_all(&self, other: &Self) -> bool950     pub fn contains_all(&self, other: &Self) -> bool {
951         for (self_cell, other_cell) in self.storage.iter().zip(other.storage.iter()) {
952             if (*self_cell & *other_cell) != *other_cell {
953                 return false;
954             }
955         }
956         true
957     }
958 
959     /// Returns whether this set contains any longhand that `other` also contains.
contains_any(&self, other: &Self) -> bool960     pub fn contains_any(&self, other: &Self) -> bool {
961         for (self_cell, other_cell) in self.storage.iter().zip(other.storage.iter()) {
962             if (*self_cell & *other_cell) != 0 {
963                 return true;
964             }
965         }
966         false
967     }
968 
969     /// Remove all the given properties from the set.
970     #[inline]
remove_all(&mut self, other: &Self)971     pub fn remove_all(&mut self, other: &Self) {
972         for (self_cell, other_cell) in self.storage.iter_mut().zip(other.storage.iter()) {
973             *self_cell &= !*other_cell;
974         }
975     }
976 
977     /// Create an empty set
978     #[inline]
new() -> LonghandIdSet979     pub fn new() -> LonghandIdSet {
980         LonghandIdSet { storage: [0; (${len(data.longhands)} - 1 + 32) / 32] }
981     }
982 
983     /// Return whether the given property is in the set
984     #[inline]
contains(&self, id: LonghandId) -> bool985     pub fn contains(&self, id: LonghandId) -> bool {
986         let bit = id as usize;
987         (self.storage[bit / 32] & (1 << (bit % 32))) != 0
988     }
989 
990     /// Return whether this set contains any reset longhand.
991     #[inline]
contains_any_reset(&self) -> bool992     pub fn contains_any_reset(&self) -> bool {
993         self.contains_any(Self::reset())
994     }
995 
996     /// Add the given property to the set
997     #[inline]
insert(&mut self, id: LonghandId)998     pub fn insert(&mut self, id: LonghandId) {
999         let bit = id as usize;
1000         self.storage[bit / 32] |= 1 << (bit % 32);
1001     }
1002 
1003     /// Remove the given property from the set
1004     #[inline]
remove(&mut self, id: LonghandId)1005     pub fn remove(&mut self, id: LonghandId) {
1006         let bit = id as usize;
1007         self.storage[bit / 32] &= !(1 << (bit % 32));
1008     }
1009 
1010     /// Clear all bits
1011     #[inline]
clear(&mut self)1012     pub fn clear(&mut self) {
1013         for cell in &mut self.storage {
1014             *cell = 0
1015         }
1016     }
1017 
1018     /// Returns whether the set is empty.
1019     #[inline]
is_empty(&self) -> bool1020     pub fn is_empty(&self) -> bool {
1021         self.storage.iter().all(|c| *c == 0)
1022     }
1023 }
1024 
1025 /// An enum to represent a CSS Wide keyword.
1026 #[derive(Clone, Copy, Debug, Eq, MallocSizeOf, PartialEq, SpecifiedValueInfo,
1027          ToCss, ToShmem)]
1028 pub enum CSSWideKeyword {
1029     /// The `initial` keyword.
1030     Initial,
1031     /// The `inherit` keyword.
1032     Inherit,
1033     /// The `unset` keyword.
1034     Unset,
1035     /// The `revert` keyword.
1036     Revert,
1037 }
1038 
1039 impl CSSWideKeyword {
to_str(&self) -> &'static str1040     fn to_str(&self) -> &'static str {
1041         match *self {
1042             CSSWideKeyword::Initial => "initial",
1043             CSSWideKeyword::Inherit => "inherit",
1044             CSSWideKeyword::Unset => "unset",
1045             CSSWideKeyword::Revert => "revert",
1046         }
1047     }
1048 }
1049 
1050 impl CSSWideKeyword {
parse(input: &mut Parser) -> Result<Self, ()>1051     fn parse(input: &mut Parser) -> Result<Self, ()> {
1052         let keyword = {
1053             let ident = input.expect_ident().map_err(|_| ())?;
1054             match_ignore_ascii_case! { ident,
1055                 // If modifying this set of keyword, also update values::CustomIdent::from_ident
1056                 "initial" => CSSWideKeyword::Initial,
1057                 "inherit" => CSSWideKeyword::Inherit,
1058                 "unset" => CSSWideKeyword::Unset,
1059                 "revert" => CSSWideKeyword::Revert,
1060                 _ => return Err(()),
1061             }
1062         };
1063         input.expect_exhausted().map_err(|_| ())?;
1064         Ok(keyword)
1065     }
1066 }
1067 
1068 bitflags! {
1069     /// A set of flags for properties.
1070     pub struct PropertyFlags: u16 {
1071         /// This longhand property applies to ::first-letter.
1072         const APPLIES_TO_FIRST_LETTER = 1 << 1;
1073         /// This longhand property applies to ::first-line.
1074         const APPLIES_TO_FIRST_LINE = 1 << 2;
1075         /// This longhand property applies to ::placeholder.
1076         const APPLIES_TO_PLACEHOLDER = 1 << 3;
1077         ///  This longhand property applies to ::cue.
1078         const APPLIES_TO_CUE = 1 << 4;
1079         /// This longhand property applies to ::marker.
1080         const APPLIES_TO_MARKER = 1 << 5;
1081         /// This property is a legacy shorthand.
1082         ///
1083         /// https://drafts.csswg.org/css-cascade/#legacy-shorthand
1084         const IS_LEGACY_SHORTHAND = 1 << 6;
1085 
1086         /* The following flags are currently not used in Rust code, they
1087          * only need to be listed in corresponding properties so that
1088          * they can be checked in the C++ side via ServoCSSPropList.h. */
1089         /// This property can be animated on the compositor.
1090         const CAN_ANIMATE_ON_COMPOSITOR = 0;
1091         /// This shorthand property is accessible from getComputedStyle.
1092         const SHORTHAND_IN_GETCS = 0;
1093     }
1094 }
1095 
1096 /// An identifier for a given longhand property.
1097 #[derive(Clone, Copy, Eq, Hash, MallocSizeOf, PartialEq, ToComputedValue, ToResolvedValue, ToShmem)]
1098 #[repr(u16)]
1099 pub enum LonghandId {
1100     % for i, property in enumerate(data.longhands):
1101         /// ${property.name}
1102         ${property.camel_case} = ${i},
1103     % endfor
1104 }
1105 
1106 impl ToCss for LonghandId {
1107     #[inline]
to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: Write,1108     fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
1109     where
1110         W: Write,
1111     {
1112         dest.write_str(self.name())
1113     }
1114 }
1115 
1116 impl fmt::Debug for LonghandId {
fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result1117     fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
1118         formatter.write_str(self.name())
1119     }
1120 }
1121 
1122 impl LonghandId {
1123     /// Get the name of this longhand property.
1124     #[inline]
name(&self) -> &'static str1125     pub fn name(&self) -> &'static str {
1126         NonCustomPropertyId::from(*self).name()
1127     }
1128 
1129     /// Returns whether the longhand property is inherited by default.
1130     #[inline]
inherited(self) -> bool1131     pub fn inherited(self) -> bool {
1132         !LonghandIdSet::reset().contains(self)
1133     }
1134 
shorthands(&self) -> NonCustomPropertyIterator<ShorthandId>1135     fn shorthands(&self) -> NonCustomPropertyIterator<ShorthandId> {
1136         // first generate longhand to shorthands lookup map
1137         //
1138         // NOTE(emilio): This currently doesn't exclude the "all" shorthand. It
1139         // could potentially do so, which would speed up serialization
1140         // algorithms and what not, I guess.
1141         <%
1142             from functools import cmp_to_key
1143             longhand_to_shorthand_map = {}
1144             num_sub_properties = {}
1145             for shorthand in data.shorthands:
1146                 num_sub_properties[shorthand.camel_case] = len(shorthand.sub_properties)
1147                 for sub_property in shorthand.sub_properties:
1148                     if sub_property.ident not in longhand_to_shorthand_map:
1149                         longhand_to_shorthand_map[sub_property.ident] = []
1150 
1151                     longhand_to_shorthand_map[sub_property.ident].append(shorthand.camel_case)
1152 
1153             def cmp(a, b):
1154                 return (a > b) - (a < b)
1155 
1156             def preferred_order(x, y):
1157                 # Since we want properties in order from most subproperties to least,
1158                 # reverse the arguments to cmp from the expected order.
1159                 result = cmp(num_sub_properties.get(y, 0), num_sub_properties.get(x, 0))
1160                 if result:
1161                     return result
1162                 # Fall back to lexicographic comparison.
1163                 return cmp(x, y)
1164 
1165             # Sort the lists of shorthand properties according to preferred order:
1166             # https://drafts.csswg.org/cssom/#concept-shorthands-preferred-order
1167             for shorthand_list in longhand_to_shorthand_map.values():
1168                 shorthand_list.sort(key=cmp_to_key(preferred_order))
1169         %>
1170 
1171         // based on lookup results for each longhand, create result arrays
1172         % for property in data.longhands:
1173             static ${property.ident.upper()}: &'static [ShorthandId] = &[
1174                 % for shorthand in longhand_to_shorthand_map.get(property.ident, []):
1175                     ShorthandId::${shorthand},
1176                 % endfor
1177             ];
1178         % endfor
1179 
1180         NonCustomPropertyIterator {
1181             filter: NonCustomPropertyId::from(*self).enabled_for_all_content(),
1182             iter: match *self {
1183                 % for property in data.longhands:
1184                     LonghandId::${property.camel_case} => ${property.ident.upper()},
1185                 % endfor
1186             }.iter(),
1187         }
1188     }
1189 
parse_value<'i, 't>( &self, context: &ParserContext, input: &mut Parser<'i, 't>, ) -> Result<PropertyDeclaration, ParseError<'i>>1190     fn parse_value<'i, 't>(
1191         &self,
1192         context: &ParserContext,
1193         input: &mut Parser<'i, 't>,
1194     ) -> Result<PropertyDeclaration, ParseError<'i>> {
1195         type ParsePropertyFn = for<'i, 't> fn(
1196             context: &ParserContext,
1197             input: &mut Parser<'i, 't>,
1198         ) -> Result<PropertyDeclaration, ParseError<'i>>;
1199         static PARSE_PROPERTY: [ParsePropertyFn; ${len(data.longhands)}] = [
1200         % for property in data.longhands:
1201             longhands::${property.ident}::parse_declared,
1202         % endfor
1203         ];
1204         (PARSE_PROPERTY[*self as usize])(context, input)
1205     }
1206 
1207     /// Returns whether this property is animatable.
1208     #[inline]
is_animatable(self) -> bool1209     pub fn is_animatable(self) -> bool {
1210         LonghandIdSet::animatable().contains(self)
1211     }
1212 
1213     /// Returns whether this property is animatable in a discrete way.
1214     #[inline]
is_discrete_animatable(self) -> bool1215     pub fn is_discrete_animatable(self) -> bool {
1216         LonghandIdSet::discrete_animatable().contains(self)
1217     }
1218 
1219     /// Converts from a LonghandId to an adequate nsCSSPropertyID.
1220     #[cfg(feature = "gecko")]
1221     #[inline]
to_nscsspropertyid(self) -> nsCSSPropertyID1222     pub fn to_nscsspropertyid(self) -> nsCSSPropertyID {
1223         NonCustomPropertyId::from(self).to_nscsspropertyid()
1224     }
1225 
1226     #[cfg(feature = "gecko")]
1227     #[allow(non_upper_case_globals)]
1228     /// Returns a longhand id from Gecko's nsCSSPropertyID.
from_nscsspropertyid(id: nsCSSPropertyID) -> Result<Self, ()>1229     pub fn from_nscsspropertyid(id: nsCSSPropertyID) -> Result<Self, ()> {
1230         match PropertyId::from_nscsspropertyid(id) {
1231             Ok(PropertyId::Longhand(id)) |
1232             Ok(PropertyId::LonghandAlias(id, _)) => Ok(id),
1233             _ => Err(()),
1234         }
1235     }
1236 
1237     /// Return whether this property is logical.
1238     #[inline]
is_logical(self) -> bool1239     pub fn is_logical(self) -> bool {
1240         LonghandIdSet::logical().contains(self)
1241     }
1242 
1243     /// If this is a logical property, return the corresponding physical one in
1244     /// the given writing mode.
1245     ///
1246     /// Otherwise, return unchanged.
1247     #[inline]
to_physical(&self, wm: WritingMode) -> Self1248     pub fn to_physical(&self, wm: WritingMode) -> Self {
1249         match *self {
1250             % for property in data.longhands:
1251             % if property.logical:
1252                 <% logical_group = property.logical_group %>
1253                 LonghandId::${property.camel_case} => {
1254                     <%helpers:logical_setter_helper name="${property.name}">
1255                     <%def name="inner(physical_ident)">
1256                         <%
1257                             physical_name = physical_ident.replace("_", "-")
1258                             physical_property = data.longhands_by_name[physical_name]
1259                             assert logical_group == physical_property.logical_group
1260                         %>
1261                         LonghandId::${to_camel_case(physical_ident)}
1262                     </%def>
1263                     </%helpers:logical_setter_helper>
1264                 }
1265             % endif
1266             % endfor
1267             _ => *self
1268         }
1269     }
1270 
1271     /// Return the logical group of this longhand property.
logical_group(&self) -> Option<LogicalGroup>1272     pub fn logical_group(&self) -> Option<LogicalGroup> {
1273         const LOGICAL_GROUPS: [Option<LogicalGroup>; ${len(data.longhands)}] = [
1274             % for prop in data.longhands:
1275             % if prop.logical_group:
1276             Some(LogicalGroup::${to_camel_case(prop.logical_group)}),
1277             % else:
1278             None,
1279             % endif
1280             % endfor
1281         ];
1282         LOGICAL_GROUPS[*self as usize]
1283     }
1284 
1285     /// Returns PropertyFlags for given longhand property.
1286     #[inline(always)]
flags(self) -> PropertyFlags1287     pub fn flags(self) -> PropertyFlags {
1288         // TODO(emilio): This can be simplified further as Rust gains more
1289         // constant expression support.
1290         const FLAGS: [u16; ${len(data.longhands)}] = [
1291             % for property in data.longhands:
1292                 % for flag in property.flags + restriction_flags(property):
1293                     PropertyFlags::${flag}.bits |
1294                 % endfor
1295                 0,
1296             % endfor
1297         ];
1298         PropertyFlags::from_bits_truncate(FLAGS[self as usize])
1299     }
1300 
1301     /// Only a few properties are allowed to depend on the visited state of
1302     /// links. When cascading visited styles, we can save time by only
1303     /// processing these properties.
is_visited_dependent(&self) -> bool1304     fn is_visited_dependent(&self) -> bool {
1305         matches!(*self,
1306             % if engine == "gecko":
1307             LonghandId::ColumnRuleColor |
1308             LonghandId::TextEmphasisColor |
1309             LonghandId::WebkitTextFillColor |
1310             LonghandId::WebkitTextStrokeColor |
1311             LonghandId::TextDecorationColor |
1312             LonghandId::Fill |
1313             LonghandId::Stroke |
1314             LonghandId::CaretColor |
1315             % endif
1316             LonghandId::BackgroundColor |
1317             LonghandId::BorderTopColor |
1318             LonghandId::BorderRightColor |
1319             LonghandId::BorderBottomColor |
1320             LonghandId::BorderLeftColor |
1321             % if engine in ["gecko", "servo-2013"]:
1322             LonghandId::OutlineColor |
1323             % endif
1324             LonghandId::Color
1325         )
1326     }
1327 
1328     /// Returns true if the property is one that is ignored when document
1329     /// colors are disabled.
1330     #[inline]
ignored_when_document_colors_disabled(self) -> bool1331     fn ignored_when_document_colors_disabled(self) -> bool {
1332         LonghandIdSet::ignored_when_colors_disabled().contains(self)
1333     }
1334 
1335     /// The computed value of some properties depends on the (sometimes
1336     /// computed) value of *other* properties.
1337     ///
1338     /// So we classify properties into "early" and "other", such that the only
1339     /// dependencies can be from "other" to "early".
1340     ///
1341     /// Unfortunately, it’s not easy to check that this classification is
1342     /// correct.
is_early_property(&self) -> bool1343     fn is_early_property(&self) -> bool {
1344         matches!(*self,
1345             % if engine == "gecko":
1346 
1347             // Needed to properly compute the writing mode, to resolve logical
1348             // properties, and similar stuff. In this block instead of along
1349             // `WritingMode` and `Direction` just for convenience, since it's
1350             // Gecko-only (for now at least).
1351             //
1352             // see WritingMode::new.
1353             LonghandId::TextOrientation |
1354 
1355             // Needed to properly compute the zoomed font-size.
1356             //
1357             // FIXME(emilio): This could probably just be a cascade flag like
1358             // IN_SVG_SUBTREE or such, and we could nuke this property.
1359             LonghandId::XTextZoom |
1360 
1361             // Needed to do font-size computation in a language-dependent way.
1362             LonghandId::XLang |
1363             // Needed for ruby to respect language-dependent min-font-size
1364             // preferences properly, see bug 1165538.
1365             LonghandId::MozMinFontSizeRatio |
1366 
1367             // font-size depends on math-depth's computed value.
1368             LonghandId::MathDepth |
1369             % endif
1370 
1371             // Needed to compute the first available font, in order to
1372             // compute font-relative units correctly.
1373             LonghandId::FontSize |
1374             LonghandId::FontWeight |
1375             LonghandId::FontStretch |
1376             LonghandId::FontStyle |
1377             LonghandId::FontFamily |
1378 
1379             // Needed to properly compute the writing mode, to resolve logical
1380             // properties, and similar stuff.
1381             LonghandId::WritingMode |
1382             LonghandId::Direction
1383         )
1384     }
1385 }
1386 
1387 /// An iterator over all the property ids that are enabled for a given
1388 /// shorthand, if that shorthand is enabled for all content too.
1389 pub struct NonCustomPropertyIterator<Item: 'static> {
1390     filter: bool,
1391     iter: std::slice::Iter<'static, Item>,
1392 }
1393 
1394 impl<Item> Iterator for NonCustomPropertyIterator<Item>
1395 where
1396     Item: 'static + Copy + Into<NonCustomPropertyId>,
1397 {
1398     type Item = Item;
1399 
next(&mut self) -> Option<Self::Item>1400     fn next(&mut self) -> Option<Self::Item> {
1401         loop {
1402             let id = *self.iter.next()?;
1403             if !self.filter || id.into().enabled_for_all_content() {
1404                 return Some(id)
1405             }
1406         }
1407     }
1408 }
1409 
1410 /// An identifier for a given shorthand property.
1411 #[derive(Clone, Copy, Debug, Eq, Hash, MallocSizeOf, PartialEq, ToComputedValue, ToResolvedValue, ToShmem)]
1412 #[repr(u16)]
1413 pub enum ShorthandId {
1414     % for i, property in enumerate(data.shorthands):
1415         /// ${property.name}
1416         ${property.camel_case} = ${i},
1417     % endfor
1418 }
1419 
1420 impl ToCss for ShorthandId {
1421     #[inline]
to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: Write,1422     fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
1423     where
1424         W: Write,
1425     {
1426         dest.write_str(self.name())
1427     }
1428 }
1429 
1430 impl ShorthandId {
1431     /// Get the name for this shorthand property.
1432     #[inline]
name(&self) -> &'static str1433     pub fn name(&self) -> &'static str {
1434         NonCustomPropertyId::from(*self).name()
1435     }
1436 
1437     /// Converts from a ShorthandId to an adequate nsCSSPropertyID.
1438     #[cfg(feature = "gecko")]
1439     #[inline]
to_nscsspropertyid(self) -> nsCSSPropertyID1440     pub fn to_nscsspropertyid(self) -> nsCSSPropertyID {
1441         NonCustomPropertyId::from(self).to_nscsspropertyid()
1442     }
1443 
1444     /// Converts from a nsCSSPropertyID to a ShorthandId.
1445     #[cfg(feature = "gecko")]
1446     #[inline]
from_nscsspropertyid(prop: nsCSSPropertyID) -> Result<Self, ()>1447     pub fn from_nscsspropertyid(prop: nsCSSPropertyID) -> Result<Self, ()> {
1448         PropertyId::from_nscsspropertyid(prop)?.as_shorthand().map_err(|_| ())
1449     }
1450 
1451     /// Get the longhand ids that form this shorthand.
longhands(&self) -> NonCustomPropertyIterator<LonghandId>1452     pub fn longhands(&self) -> NonCustomPropertyIterator<LonghandId> {
1453         % for property in data.shorthands:
1454             static ${property.ident.upper()}: &'static [LonghandId] = &[
1455                 % for sub in property.sub_properties:
1456                     LonghandId::${sub.camel_case},
1457                 % endfor
1458             ];
1459         % endfor
1460         NonCustomPropertyIterator {
1461             filter: NonCustomPropertyId::from(*self).enabled_for_all_content(),
1462             iter: match *self {
1463                 % for property in data.shorthands:
1464                     ShorthandId::${property.camel_case} => ${property.ident.upper()},
1465                 % endfor
1466             }.iter()
1467         }
1468     }
1469 
1470     /// Try to serialize the given declarations as this shorthand.
1471     ///
1472     /// Returns an error if writing to the stream fails, or if the declarations
1473     /// do not map to a shorthand.
longhands_to_css( &self, declarations: &[&PropertyDeclaration], dest: &mut CssStringWriter, ) -> fmt::Result1474     pub fn longhands_to_css(
1475         &self,
1476         declarations: &[&PropertyDeclaration],
1477         dest: &mut CssStringWriter,
1478     ) -> fmt::Result {
1479         type LonghandsToCssFn = for<'a, 'b> fn(&'a [&'b PropertyDeclaration], &mut CssStringWriter) -> fmt::Result;
1480         fn all_to_css(_: &[&PropertyDeclaration], _: &mut CssStringWriter) -> fmt::Result {
1481             // No need to try to serialize the declarations as the 'all'
1482             // shorthand, since it only accepts CSS-wide keywords (and variable
1483             // references), which will be handled in
1484             // get_shorthand_appendable_value.
1485             Ok(())
1486         }
1487 
1488         static LONGHANDS_TO_CSS: [LonghandsToCssFn; ${len(data.shorthands)}] = [
1489             % for shorthand in data.shorthands:
1490             % if shorthand.ident == "all":
1491                 all_to_css,
1492             % else:
1493                 shorthands::${shorthand.ident}::to_css,
1494             % endif
1495             % endfor
1496         ];
1497 
1498         LONGHANDS_TO_CSS[*self as usize](declarations, dest)
1499     }
1500 
1501     /// Finds and returns an appendable value for the given declarations.
1502     ///
1503     /// Returns the optional appendable value.
get_shorthand_appendable_value<'a, 'b: 'a>( self, declarations: &'a [&'b PropertyDeclaration], ) -> Option<AppendableValue<'a, 'b>>1504     pub fn get_shorthand_appendable_value<'a, 'b: 'a>(
1505         self,
1506         declarations: &'a [&'b PropertyDeclaration],
1507     ) -> Option<AppendableValue<'a, 'b>> {
1508         let first_declaration = declarations.get(0)?;
1509         let rest = || declarations.iter().skip(1);
1510 
1511         // https://drafts.csswg.org/css-variables/#variables-in-shorthands
1512         if let Some(css) = first_declaration.with_variables_from_shorthand(self) {
1513             if rest().all(|d| d.with_variables_from_shorthand(self) == Some(css)) {
1514                return Some(AppendableValue::Css(css));
1515             }
1516             return None;
1517         }
1518 
1519         // Check whether they are all the same CSS-wide keyword.
1520         if let Some(keyword) = first_declaration.get_css_wide_keyword() {
1521             if rest().all(|d| d.get_css_wide_keyword() == Some(keyword)) {
1522                 return Some(AppendableValue::Css(keyword.to_str()))
1523             }
1524             return None;
1525         }
1526 
1527         if self == ShorthandId::All {
1528             // 'all' only supports variables and CSS wide keywords.
1529             return None;
1530         }
1531 
1532         // Check whether all declarations can be serialized as part of shorthand.
1533         if declarations.iter().all(|d| d.may_serialize_as_part_of_shorthand()) {
1534             return Some(AppendableValue::DeclarationsForShorthand(self, declarations));
1535         }
1536 
1537         None
1538     }
1539 
1540     /// Returns PropertyFlags for the given shorthand property.
1541     #[inline]
flags(self) -> PropertyFlags1542     pub fn flags(self) -> PropertyFlags {
1543         const FLAGS: [u16; ${len(data.shorthands)}] = [
1544             % for property in data.shorthands:
1545                 % for flag in property.flags:
1546                     PropertyFlags::${flag}.bits |
1547                 % endfor
1548                 0,
1549             % endfor
1550         ];
1551         PropertyFlags::from_bits_truncate(FLAGS[self as usize])
1552     }
1553 
1554     /// Returns whether this property is a legacy shorthand.
1555     #[inline]
is_legacy_shorthand(self) -> bool1556     pub fn is_legacy_shorthand(self) -> bool {
1557         self.flags().contains(PropertyFlags::IS_LEGACY_SHORTHAND)
1558     }
1559 
1560     /// Returns the order in which this property appears relative to other
1561     /// shorthands in idl-name-sorting order.
1562     #[inline]
idl_name_sort_order(self) -> u321563     pub fn idl_name_sort_order(self) -> u32 {
1564         <%
1565             from data import to_idl_name
1566             ordered = {}
1567             sorted_shorthands = sorted(data.shorthands, key=lambda p: to_idl_name(p.ident))
1568             for order, shorthand in enumerate(sorted_shorthands):
1569                 ordered[shorthand.ident] = order
1570         %>
1571         static IDL_NAME_SORT_ORDER: [u32; ${len(data.shorthands)}] = [
1572             % for property in data.shorthands:
1573             ${ordered[property.ident]},
1574             % endfor
1575         ];
1576         IDL_NAME_SORT_ORDER[self as usize]
1577     }
1578 
parse_into<'i, 't>( &self, declarations: &mut SourcePropertyDeclaration, context: &ParserContext, input: &mut Parser<'i, 't>, ) -> Result<(), ParseError<'i>>1579     fn parse_into<'i, 't>(
1580         &self,
1581         declarations: &mut SourcePropertyDeclaration,
1582         context: &ParserContext,
1583         input: &mut Parser<'i, 't>,
1584     ) -> Result<(), ParseError<'i>> {
1585         type ParseIntoFn = for<'i, 't> fn(
1586             declarations: &mut SourcePropertyDeclaration,
1587             context: &ParserContext,
1588             input: &mut Parser<'i, 't>,
1589         ) -> Result<(), ParseError<'i>>;
1590 
1591         fn parse_all<'i, 't>(
1592             _: &mut SourcePropertyDeclaration,
1593             _: &ParserContext,
1594             input: &mut Parser<'i, 't>
1595         ) -> Result<(), ParseError<'i>> {
1596             // 'all' accepts no value other than CSS-wide keywords
1597             Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
1598         }
1599 
1600 
1601         static PARSE_INTO: [ParseIntoFn; ${len(data.shorthands)}] = [
1602             % for shorthand in data.shorthands:
1603             % if shorthand.ident == "all":
1604             parse_all,
1605             % else:
1606             shorthands::${shorthand.ident}::parse_into,
1607             % endif
1608             % endfor
1609         ];
1610 
1611         (PARSE_INTO[*self as usize])(declarations, context, input)
1612     }
1613 }
1614 
1615 /// An unparsed property value that contains `var()` functions.
1616 #[derive(Debug, Eq, PartialEq, ToShmem)]
1617 pub struct UnparsedValue {
1618     /// The css serialization for this value.
1619     css: String,
1620     /// The first token type for this serialization.
1621     first_token_type: TokenSerializationType,
1622     /// The url data for resolving url values.
1623     url_data: UrlExtraData,
1624     /// The shorthand this came from.
1625     from_shorthand: Option<ShorthandId>,
1626 }
1627 
1628 impl ToCss for UnparsedValue {
to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: Write,1629     fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
1630     where
1631         W: Write,
1632     {
1633         // https://drafts.csswg.org/css-variables/#variables-in-shorthands
1634         if self.from_shorthand.is_none() {
1635             dest.write_str(&*self.css)?;
1636         }
1637         Ok(())
1638     }
1639 }
1640 
1641 /// A simple cache for properties that come from a shorthand and have variable
1642 /// references.
1643 ///
1644 /// This cache works because of the fact that you can't have competing values
1645 /// for a given longhand coming from the same shorthand (but note that this is
1646 /// why the shorthand needs to be part of the cache key).
1647 pub type ShorthandsWithPropertyReferencesCache =
1648     FxHashMap<(ShorthandId, LonghandId), PropertyDeclaration>;
1649 
1650 impl UnparsedValue {
substitute_variables<'cache>( &self, longhand_id: LonghandId, writing_mode: WritingMode, custom_properties: Option<<&Arc<crate::custom_properties::CustomPropertiesMap>>, quirks_mode: QuirksMode, device: &Device, shorthand_cache: &'cache mut ShorthandsWithPropertyReferencesCache, ) -> Cow<'cache, PropertyDeclaration>1651     fn substitute_variables<'cache>(
1652         &self,
1653         longhand_id: LonghandId,
1654         writing_mode: WritingMode,
1655         custom_properties: Option<<&Arc<crate::custom_properties::CustomPropertiesMap>>,
1656         quirks_mode: QuirksMode,
1657         device: &Device,
1658         shorthand_cache: &'cache mut ShorthandsWithPropertyReferencesCache,
1659     ) -> Cow<'cache, PropertyDeclaration> {
1660         let invalid_at_computed_value_time = || {
1661             let keyword = if longhand_id.inherited() {
1662                 CSSWideKeyword::Inherit
1663             } else {
1664                 CSSWideKeyword::Initial
1665             };
1666             Cow::Owned(PropertyDeclaration::css_wide_keyword(longhand_id, keyword))
1667         };
1668 
1669         if let Some(shorthand_id) = self.from_shorthand {
1670             let key = (shorthand_id, longhand_id);
1671             if shorthand_cache.contains_key(&key) {
1672                 // FIXME: This double lookup should be avoidable, but rustc
1673                 // doesn't like that, see:
1674                 //
1675                 // https://github.com/rust-lang/rust/issues/82146
1676                 return Cow::Borrowed(&shorthand_cache[&key]);
1677             }
1678         }
1679 
1680         let css = match crate::custom_properties::substitute(
1681             &self.css,
1682             self.first_token_type,
1683             custom_properties,
1684             device,
1685         ) {
1686             Ok(css) => css,
1687             Err(..) => return invalid_at_computed_value_time(),
1688         };
1689 
1690         // As of this writing, only the base URL is used for property
1691         // values.
1692         //
1693         // NOTE(emilio): we intentionally pase `None` as the rule type here.
1694         // If something starts depending on it, it's probably a bug, since
1695         // it'd change how values are parsed depending on whether we're in a
1696         // @keyframes rule or not, for example... So think twice about
1697         // whether you want to do this!
1698         //
1699         // FIXME(emilio): ParsingMode is slightly fishy...
1700         let context = ParserContext::new(
1701             Origin::Author,
1702             &self.url_data,
1703             None,
1704             ParsingMode::DEFAULT,
1705             quirks_mode,
1706             None,
1707             None,
1708         );
1709 
1710         let mut input = ParserInput::new(&css);
1711         let mut input = Parser::new(&mut input);
1712         input.skip_whitespace();
1713 
1714         if let Ok(keyword) = input.try_parse(CSSWideKeyword::parse) {
1715             return Cow::Owned(PropertyDeclaration::css_wide_keyword(longhand_id, keyword));
1716         }
1717 
1718         let shorthand = match self.from_shorthand {
1719             None => {
1720                 return match input.parse_entirely(|input| longhand_id.parse_value(&context, input)) {
1721                     Ok(decl) => Cow::Owned(decl),
1722                     Err(..) => invalid_at_computed_value_time(),
1723                 }
1724             },
1725             Some(shorthand) => shorthand,
1726         };
1727 
1728         let mut decls = SourcePropertyDeclaration::new();
1729         // parse_into takes care of doing `parse_entirely` for us.
1730         if shorthand.parse_into(&mut decls, &context, &mut input).is_err() {
1731             return invalid_at_computed_value_time();
1732         }
1733 
1734         for declaration in decls.declarations.drain(..) {
1735             let longhand = declaration.id().as_longhand().unwrap();
1736             if longhand.is_logical() {
1737                 shorthand_cache.insert((shorthand, longhand.to_physical(writing_mode)), declaration.clone());
1738             }
1739             shorthand_cache.insert((shorthand, longhand), declaration);
1740         }
1741 
1742         let key = (shorthand, longhand_id);
1743         match shorthand_cache.get(&key) {
1744             Some(decl) => Cow::Borrowed(decl),
1745             None => {
1746                 // FIXME: We should always have the key here but it seems
1747                 // sometimes we don't, see bug 1696409.
1748                 #[cfg(feature = "gecko")]
1749                 {
1750                     if structs::GECKO_IS_NIGHTLY {
1751                         panic!("Expected {:?} to be in the cache but it was not!", key);
1752                     }
1753                 }
1754                 invalid_at_computed_value_time()
1755             }
1756         }
1757     }
1758 }
1759 
1760 /// An identifier for a given property declaration, which can be either a
1761 /// longhand or a custom property.
1762 #[derive(Clone, Copy, Debug, PartialEq)]
1763 #[cfg_attr(feature = "servo", derive(MallocSizeOf))]
1764 pub enum PropertyDeclarationId<'a> {
1765     /// A longhand.
1766     Longhand(LonghandId),
1767     /// A custom property declaration.
1768     Custom(&'a crate::custom_properties::Name),
1769 }
1770 
1771 impl<'a> ToCss for PropertyDeclarationId<'a> {
to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: Write,1772     fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
1773     where
1774         W: Write,
1775     {
1776         match *self {
1777             PropertyDeclarationId::Longhand(id) => dest.write_str(id.name()),
1778             PropertyDeclarationId::Custom(ref name) => {
1779                 dest.write_str("--")?;
1780                 serialize_atom_name(name, dest)
1781             }
1782         }
1783     }
1784 }
1785 
1786 impl<'a> PropertyDeclarationId<'a> {
1787     /// Whether a given declaration id is either the same as `other`, or a
1788     /// longhand of it.
is_or_is_longhand_of(&self, other: &PropertyId) -> bool1789     pub fn is_or_is_longhand_of(&self, other: &PropertyId) -> bool {
1790         match *self {
1791             PropertyDeclarationId::Longhand(id) => {
1792                 match *other {
1793                     PropertyId::Longhand(other_id) |
1794                     PropertyId::LonghandAlias(other_id, _) => id == other_id,
1795                     PropertyId::Shorthand(shorthand) |
1796                     PropertyId::ShorthandAlias(shorthand, _) => self.is_longhand_of(shorthand),
1797                     PropertyId::Custom(_) => false,
1798                 }
1799             }
1800             PropertyDeclarationId::Custom(name) => {
1801                 matches!(*other, PropertyId::Custom(ref other_name) if name == other_name)
1802             }
1803         }
1804     }
1805 
1806     /// Whether a given declaration id is a longhand belonging to this
1807     /// shorthand.
is_longhand_of(&self, shorthand: ShorthandId) -> bool1808     pub fn is_longhand_of(&self, shorthand: ShorthandId) -> bool {
1809         match *self {
1810             PropertyDeclarationId::Longhand(ref id) => id.shorthands().any(|s| s == shorthand),
1811             _ => false,
1812         }
1813     }
1814 
1815     /// Returns the name of the property without CSS escaping.
name(&self) -> Cow<'static, str>1816     pub fn name(&self) -> Cow<'static, str> {
1817         match *self {
1818             PropertyDeclarationId::Longhand(id) => id.name().into(),
1819             PropertyDeclarationId::Custom(name) => {
1820                 let mut s = String::new();
1821                 write!(&mut s, "--{}", name).unwrap();
1822                 s.into()
1823             }
1824         }
1825     }
1826 
1827     /// Returns longhand id if it is, None otherwise.
1828     #[inline]
as_longhand(&self) -> Option<LonghandId>1829     pub fn as_longhand(&self) -> Option<LonghandId> {
1830         match *self {
1831             PropertyDeclarationId::Longhand(id) => Some(id),
1832             _ => None,
1833         }
1834     }
1835 }
1836 
1837 /// Servo's representation of a CSS property, that is, either a longhand, a
1838 /// shorthand, or a custom property.
1839 #[derive(Clone, Eq, PartialEq)]
1840 pub enum PropertyId {
1841     /// A longhand property.
1842     Longhand(LonghandId),
1843     /// A shorthand property.
1844     Shorthand(ShorthandId),
1845     /// An alias for a longhand property.
1846     LonghandAlias(LonghandId, AliasId),
1847     /// An alias for a shorthand property.
1848     ShorthandAlias(ShorthandId, AliasId),
1849     /// A custom property.
1850     Custom(crate::custom_properties::Name),
1851 }
1852 
1853 impl fmt::Debug for PropertyId {
fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result1854     fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
1855         self.to_css(&mut CssWriter::new(formatter))
1856     }
1857 }
1858 
1859 impl ToCss for PropertyId {
to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: Write,1860     fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
1861     where
1862         W: Write,
1863     {
1864         match *self {
1865             PropertyId::Longhand(id) => dest.write_str(id.name()),
1866             PropertyId::Shorthand(id) => dest.write_str(id.name()),
1867             PropertyId::LonghandAlias(id, _) => dest.write_str(id.name()),
1868             PropertyId::ShorthandAlias(id, _) => dest.write_str(id.name()),
1869             PropertyId::Custom(ref name) => {
1870                 dest.write_str("--")?;
1871                 serialize_atom_name(name, dest)
1872             }
1873         }
1874     }
1875 }
1876 
1877 /// The counted unknown property list which is used for css use counters.
1878 ///
1879 /// FIXME: This should be just #[repr(u8)], but can't be because of ABI issues,
1880 /// see https://bugs.llvm.org/show_bug.cgi?id=44228.
1881 #[derive(Clone, Copy, Debug, Eq, FromPrimitive, Hash, PartialEq)]
1882 #[repr(u32)]
1883 pub enum CountedUnknownProperty {
1884     % for prop in data.counted_unknown_properties:
1885     /// ${prop.name}
1886     ${prop.camel_case},
1887     % endfor
1888 }
1889 
1890 impl CountedUnknownProperty {
1891     /// Parse the counted unknown property, for testing purposes only.
parse_for_testing(property_name: &str) -> Option<Self>1892     pub fn parse_for_testing(property_name: &str) -> Option<Self> {
1893         ascii_case_insensitive_phf_map! {
1894             unknown_id -> CountedUnknownProperty = {
1895                 % for property in data.counted_unknown_properties:
1896                 "${property.name}" => CountedUnknownProperty::${property.camel_case},
1897                 % endfor
1898             }
1899         }
1900         unknown_id(property_name).cloned()
1901     }
1902 
1903     /// Returns the underlying index, used for use counter.
1904     #[inline]
bit(self) -> usize1905     pub fn bit(self) -> usize {
1906         self as usize
1907     }
1908 }
1909 
1910 impl PropertyId {
1911     /// Return the longhand id that this property id represents.
1912     #[inline]
longhand_id(&self) -> Option<LonghandId>1913     pub fn longhand_id(&self) -> Option<LonghandId> {
1914         Some(match *self {
1915             PropertyId::Longhand(id) => id,
1916             PropertyId::LonghandAlias(id, _) => id,
1917             _ => return None,
1918         })
1919     }
1920 
1921     /// Returns a given property from the given name, _regardless of whether it
1922     /// is enabled or not_, or Err(()) for unknown properties.
1923     ///
1924     /// Do not use for non-testing purposes.
parse_unchecked_for_testing(name: &str) -> Result<Self, ()>1925     pub fn parse_unchecked_for_testing(name: &str) -> Result<Self, ()> {
1926         Self::parse_unchecked(name, None)
1927     }
1928 
1929     /// Returns a given property from the given name, _regardless of whether it
1930     /// is enabled or not_, or Err(()) for unknown properties.
parse_unchecked( property_name: &str, use_counters: Option< &UseCounters>, ) -> Result<Self, ()>1931     fn parse_unchecked(
1932         property_name: &str,
1933         use_counters: Option< &UseCounters>,
1934     ) -> Result<Self, ()> {
1935         // A special id for css use counters.
1936         // ShorthandAlias is not used in the Servo build.
1937         // That's why we need to allow dead_code.
1938         #[allow(dead_code)]
1939         pub enum StaticId {
1940             Longhand(LonghandId),
1941             Shorthand(ShorthandId),
1942             LonghandAlias(LonghandId, AliasId),
1943             ShorthandAlias(ShorthandId, AliasId),
1944             CountedUnknown(CountedUnknownProperty),
1945         }
1946         ascii_case_insensitive_phf_map! {
1947             static_id -> StaticId = {
1948                 % for (kind, properties) in [("Longhand", data.longhands), ("Shorthand", data.shorthands)]:
1949                 % for property in properties:
1950                 "${property.name}" => StaticId::${kind}(${kind}Id::${property.camel_case}),
1951                 % for alias in property.aliases:
1952                 "${alias.name}" => {
1953                     StaticId::${kind}Alias(
1954                         ${kind}Id::${property.camel_case},
1955                         AliasId::${alias.camel_case},
1956                     )
1957                 },
1958                 % endfor
1959                 % endfor
1960                 % endfor
1961                 % for property in data.counted_unknown_properties:
1962                 "${property.name}" => {
1963                     StaticId::CountedUnknown(CountedUnknownProperty::${property.camel_case})
1964                 },
1965                 % endfor
1966             }
1967         }
1968 
1969         if let Some(id) = static_id(property_name) {
1970             return Ok(match *id {
1971                 StaticId::Longhand(id) => PropertyId::Longhand(id),
1972                 StaticId::Shorthand(id) => {
1973                     #[cfg(feature = "gecko")]
1974                     {
1975                         // We want to count `zoom` even if disabled.
1976                         if matches!(id, ShorthandId::Zoom) {
1977                             if let Some(counters) = use_counters {
1978                                 counters.non_custom_properties.record(id.into());
1979                             }
1980                         }
1981                     }
1982 
1983                     PropertyId::Shorthand(id)
1984                 },
1985                 StaticId::LonghandAlias(id, alias) => PropertyId::LonghandAlias(id, alias),
1986                 StaticId::ShorthandAlias(id, alias) => PropertyId::ShorthandAlias(id, alias),
1987                 StaticId::CountedUnknown(unknown_prop) => {
1988                     if let Some(counters) = use_counters {
1989                         counters.counted_unknown_properties.record(unknown_prop);
1990                     }
1991 
1992                     // Always return Err(()) because these aren't valid custom property names.
1993                     return Err(());
1994                 }
1995             });
1996         }
1997 
1998         let name = crate::custom_properties::parse_name(property_name)?;
1999         Ok(PropertyId::Custom(crate::custom_properties::Name::from(name)))
2000     }
2001 
2002     /// Parses a property name, and returns an error if it's unknown or isn't
2003     /// enabled for all content.
2004     #[inline]
parse_enabled_for_all_content(name: &str) -> Result<Self, ()>2005     pub fn parse_enabled_for_all_content(name: &str) -> Result<Self, ()> {
2006         let id = Self::parse_unchecked(name, None)?;
2007 
2008         if !id.enabled_for_all_content() {
2009             return Err(());
2010         }
2011 
2012         Ok(id)
2013     }
2014 
2015 
2016     /// Parses a property name, and returns an error if it's unknown or isn't
2017     /// allowed in this context.
2018     #[inline]
parse(name: &str, context: &ParserContext) -> Result<Self, ()>2019     pub fn parse(name: &str, context: &ParserContext) -> Result<Self, ()> {
2020         let id = Self::parse_unchecked(name, context.use_counters)?;
2021 
2022         if !id.allowed_in(context) {
2023             return Err(());
2024         }
2025 
2026         Ok(id)
2027     }
2028 
2029     /// Parses a property name, and returns an error if it's unknown or isn't
2030     /// allowed in this context, ignoring the rule_type checks.
2031     ///
2032     /// This is useful for parsing stuff from CSS values, for example.
2033     #[inline]
parse_ignoring_rule_type( name: &str, context: &ParserContext, ) -> Result<Self, ()>2034     pub fn parse_ignoring_rule_type(
2035         name: &str,
2036         context: &ParserContext,
2037     ) -> Result<Self, ()> {
2038         let id = Self::parse_unchecked(name, None)?;
2039 
2040         if !id.allowed_in_ignoring_rule_type(context) {
2041             return Err(());
2042         }
2043 
2044         Ok(id)
2045     }
2046 
2047     /// Returns a property id from Gecko's nsCSSPropertyID.
2048     #[cfg(feature = "gecko")]
2049     #[allow(non_upper_case_globals)]
2050     #[inline]
from_nscsspropertyid(id: nsCSSPropertyID) -> Result<Self, ()>2051     pub fn from_nscsspropertyid(id: nsCSSPropertyID) -> Result<Self, ()> {
2052         Ok(NonCustomPropertyId::from_nscsspropertyid(id)?.to_property_id())
2053     }
2054 
2055     /// Returns true if the property is a shorthand or shorthand alias.
2056     #[inline]
is_shorthand(&self) -> bool2057     pub fn is_shorthand(&self) -> bool {
2058         self.as_shorthand().is_ok()
2059     }
2060 
2061     /// Given this property id, get it either as a shorthand or as a
2062     /// `PropertyDeclarationId`.
as_shorthand(&self) -> Result<ShorthandId, PropertyDeclarationId>2063     pub fn as_shorthand(&self) -> Result<ShorthandId, PropertyDeclarationId> {
2064         match *self {
2065             PropertyId::ShorthandAlias(id, _) |
2066             PropertyId::Shorthand(id) => Ok(id),
2067             PropertyId::LonghandAlias(id, _) |
2068             PropertyId::Longhand(id) => Err(PropertyDeclarationId::Longhand(id)),
2069             PropertyId::Custom(ref name) => Err(PropertyDeclarationId::Custom(name)),
2070         }
2071     }
2072 
2073     /// Returns the `NonCustomPropertyId` corresponding to this property id.
non_custom_id(&self) -> Option<NonCustomPropertyId>2074     pub fn non_custom_id(&self) -> Option<NonCustomPropertyId> {
2075         Some(match *self {
2076             PropertyId::Custom(_) => return None,
2077             PropertyId::Shorthand(shorthand_id) => shorthand_id.into(),
2078             PropertyId::Longhand(longhand_id) => longhand_id.into(),
2079             PropertyId::ShorthandAlias(_, alias_id) => alias_id.into(),
2080             PropertyId::LonghandAlias(_, alias_id) => alias_id.into(),
2081         })
2082     }
2083 
2084     /// Returns non-alias NonCustomPropertyId corresponding to this
2085     /// property id.
non_custom_non_alias_id(&self) -> Option<NonCustomPropertyId>2086     fn non_custom_non_alias_id(&self) -> Option<NonCustomPropertyId> {
2087         Some(match *self {
2088             PropertyId::Custom(_) => return None,
2089             PropertyId::Shorthand(id) => id.into(),
2090             PropertyId::Longhand(id) => id.into(),
2091             PropertyId::ShorthandAlias(id, _) => id.into(),
2092             PropertyId::LonghandAlias(id, _) => id.into(),
2093         })
2094     }
2095 
2096     /// Whether the property is enabled for all content regardless of the
2097     /// stylesheet it was declared on (that is, in practice only checks prefs).
2098     #[inline]
enabled_for_all_content(&self) -> bool2099     pub fn enabled_for_all_content(&self) -> bool {
2100         let id = match self.non_custom_id() {
2101             // Custom properties are allowed everywhere
2102             None => return true,
2103             Some(id) => id,
2104         };
2105 
2106         id.enabled_for_all_content()
2107     }
2108 
2109     /// Converts this PropertyId in nsCSSPropertyID, resolving aliases to the
2110     /// resolved property, and returning eCSSPropertyExtra_variable for custom
2111     /// properties.
2112     #[cfg(feature = "gecko")]
2113     #[inline]
to_nscsspropertyid_resolving_aliases(&self) -> nsCSSPropertyID2114     pub fn to_nscsspropertyid_resolving_aliases(&self) -> nsCSSPropertyID {
2115         match self.non_custom_non_alias_id() {
2116             Some(id) => id.to_nscsspropertyid(),
2117             None => nsCSSPropertyID::eCSSPropertyExtra_variable,
2118         }
2119     }
2120 
allowed_in(&self, context: &ParserContext) -> bool2121     fn allowed_in(&self, context: &ParserContext) -> bool {
2122         let id = match self.non_custom_id() {
2123             // Custom properties are allowed everywhere
2124             None => return true,
2125             Some(id) => id,
2126         };
2127         id.allowed_in(context)
2128     }
2129 
2130     #[inline]
allowed_in_ignoring_rule_type(&self, context: &ParserContext) -> bool2131     fn allowed_in_ignoring_rule_type(&self, context: &ParserContext) -> bool {
2132         let id = match self.non_custom_id() {
2133             // Custom properties are allowed everywhere
2134             None => return true,
2135             Some(id) => id,
2136         };
2137         id.allowed_in_ignoring_rule_type(context)
2138     }
2139 
2140     /// Whether the property supports the given CSS type.
2141     /// `ty` should a bitflags of constants in style_traits::CssType.
supports_type(&self, ty: u8) -> bool2142     pub fn supports_type(&self, ty: u8) -> bool {
2143         let id = self.non_custom_non_alias_id();
2144         id.map_or(0, |id| id.supported_types()) & ty != 0
2145     }
2146 
2147     /// Collect supported starting word of values of this property.
2148     ///
2149     /// See style_traits::SpecifiedValueInfo::collect_completion_keywords for more
2150     /// details.
collect_property_completion_keywords(&self, f: KeywordsCollectFn)2151     pub fn collect_property_completion_keywords(&self, f: KeywordsCollectFn) {
2152         if let Some(id) = self.non_custom_non_alias_id() {
2153             id.collect_property_completion_keywords(f);
2154         }
2155         CSSWideKeyword::collect_completion_keywords(f);
2156     }
2157 }
2158 
2159 /// A declaration using a CSS-wide keyword.
2160 #[cfg_attr(feature = "gecko", derive(MallocSizeOf))]
2161 #[derive(Clone, PartialEq, ToCss, ToShmem)]
2162 pub struct WideKeywordDeclaration {
2163     #[css(skip)]
2164     id: LonghandId,
2165     /// The CSS-wide keyword.
2166     pub keyword: CSSWideKeyword,
2167 }
2168 
2169 /// An unparsed declaration that contains `var()` functions.
2170 #[cfg_attr(feature = "gecko", derive(MallocSizeOf))]
2171 #[derive(Clone, PartialEq, ToCss, ToShmem)]
2172 pub struct VariableDeclaration {
2173     #[css(skip)]
2174     id: LonghandId,
2175     #[cfg_attr(feature = "gecko", ignore_malloc_size_of = "XXX: how to handle this?")]
2176     value: Arc<UnparsedValue>,
2177 }
2178 
2179 /// A custom property declaration value is either an unparsed value or a CSS
2180 /// wide-keyword.
2181 #[derive(Clone, PartialEq, ToCss, ToShmem)]
2182 pub enum CustomDeclarationValue {
2183     /// A value.
2184     Value(Arc<crate::custom_properties::SpecifiedValue>),
2185     /// A wide keyword.
2186     CSSWideKeyword(CSSWideKeyword),
2187 }
2188 
2189 /// A custom property declaration with the property name and the declared value.
2190 #[cfg_attr(feature = "gecko", derive(MallocSizeOf))]
2191 #[derive(Clone, PartialEq, ToCss, ToShmem)]
2192 pub struct CustomDeclaration {
2193     /// The name of the custom property.
2194     #[css(skip)]
2195     pub name: crate::custom_properties::Name,
2196     /// The value of the custom property.
2197     #[cfg_attr(feature = "gecko", ignore_malloc_size_of = "XXX: how to handle this?")]
2198     pub value: CustomDeclarationValue,
2199 }
2200 
2201 impl fmt::Debug for PropertyDeclaration {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result2202     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
2203         self.id().to_css(&mut CssWriter::new(f))?;
2204         f.write_str(": ")?;
2205 
2206         // Because PropertyDeclaration::to_css requires CssStringWriter, we can't write
2207         // it directly to f, and need to allocate an intermediate string. This is
2208         // fine for debug-only code.
2209         let mut s = CssString::new();
2210         self.to_css(&mut s)?;
2211         write!(f, "{}", s)
2212     }
2213 }
2214 
2215 impl PropertyDeclaration {
2216     /// Given a property declaration, return the property declaration id.
2217     #[inline]
id(&self) -> PropertyDeclarationId2218     pub fn id(&self) -> PropertyDeclarationId {
2219         match *self {
2220             PropertyDeclaration::Custom(ref declaration) => {
2221                 return PropertyDeclarationId::Custom(&declaration.name)
2222             }
2223             PropertyDeclaration::CSSWideKeyword(ref declaration) => {
2224                 return PropertyDeclarationId::Longhand(declaration.id);
2225             }
2226             PropertyDeclaration::WithVariables(ref declaration) => {
2227                 return PropertyDeclarationId::Longhand(declaration.id);
2228             }
2229             _ => {}
2230         }
2231         // This is just fine because PropertyDeclaration and LonghandId
2232         // have corresponding discriminants.
2233         let id = unsafe { *(self as *const _ as *const LonghandId) };
2234         debug_assert_eq!(id, match *self {
2235             % for property in data.longhands:
2236             PropertyDeclaration::${property.camel_case}(..) => LonghandId::${property.camel_case},
2237             % endfor
2238             _ => id,
2239         });
2240         PropertyDeclarationId::Longhand(id)
2241     }
2242 
2243     /// Given a declaration, convert it into a declaration for a corresponding
2244     /// physical property.
2245     #[inline]
to_physical(&self, wm: WritingMode) -> Self2246     pub fn to_physical(&self, wm: WritingMode) -> Self {
2247         match *self {
2248             PropertyDeclaration::WithVariables(VariableDeclaration {
2249                 id,
2250                 ref value,
2251             }) => {
2252                 return PropertyDeclaration::WithVariables(VariableDeclaration {
2253                     id: id.to_physical(wm),
2254                     value: value.clone(),
2255                 })
2256             }
2257             PropertyDeclaration::CSSWideKeyword(WideKeywordDeclaration {
2258                 id,
2259                 keyword,
2260             }) => {
2261                 return PropertyDeclaration::CSSWideKeyword(WideKeywordDeclaration {
2262                     id: id.to_physical(wm),
2263                     keyword,
2264                 })
2265             }
2266             PropertyDeclaration::Custom(..) => return self.clone(),
2267             % for prop in data.longhands:
2268             PropertyDeclaration::${prop.camel_case}(..) => {},
2269             % endfor
2270         }
2271 
2272         let mut ret = self.clone();
2273 
2274         % for prop in data.longhands:
2275         % for physical_property in prop.all_physical_mapped_properties(data):
2276         % if physical_property.specified_type() != prop.specified_type():
2277             <% raise "Logical property %s should share specified value with physical property %s" % \
2278                      (prop.name, physical_property.name) %>
2279         % endif
2280         % endfor
2281         % endfor
2282 
2283         unsafe {
2284             let longhand_id = *(&mut ret as *mut _ as *mut LonghandId);
2285 
2286             debug_assert_eq!(
2287                 PropertyDeclarationId::Longhand(longhand_id),
2288                 ret.id()
2289             );
2290 
2291             // This is just fine because PropertyDeclaration and LonghandId
2292             // have corresponding discriminants.
2293             *(&mut ret as *mut _ as *mut LonghandId) = longhand_id.to_physical(wm);
2294 
2295             debug_assert_eq!(
2296                 PropertyDeclarationId::Longhand(longhand_id.to_physical(wm)),
2297                 ret.id()
2298             );
2299         }
2300 
2301         ret
2302     }
2303 
with_variables_from_shorthand(&self, shorthand: ShorthandId) -> Option< &str>2304     fn with_variables_from_shorthand(&self, shorthand: ShorthandId) -> Option< &str> {
2305         match *self {
2306             PropertyDeclaration::WithVariables(ref declaration) => {
2307                 let s = declaration.value.from_shorthand?;
2308                 if s != shorthand {
2309                     return None;
2310                 }
2311                 Some(&*declaration.value.css)
2312             },
2313             _ => None,
2314         }
2315     }
2316 
2317     /// Returns a CSS-wide keyword declaration for a given property.
2318     #[inline]
css_wide_keyword(id: LonghandId, keyword: CSSWideKeyword) -> Self2319     pub fn css_wide_keyword(id: LonghandId, keyword: CSSWideKeyword) -> Self {
2320         Self::CSSWideKeyword(WideKeywordDeclaration { id, keyword })
2321     }
2322 
2323     /// Returns a CSS-wide keyword if the declaration's value is one.
2324     #[inline]
get_css_wide_keyword(&self) -> Option<CSSWideKeyword>2325     pub fn get_css_wide_keyword(&self) -> Option<CSSWideKeyword> {
2326         match *self {
2327             PropertyDeclaration::CSSWideKeyword(ref declaration) => {
2328                 Some(declaration.keyword)
2329             },
2330             _ => None,
2331         }
2332     }
2333 
2334     /// Returns whether or not the property is set by a system font
get_system(&self) -> Option<SystemFont>2335     pub fn get_system(&self) -> Option<SystemFont> {
2336         match *self {
2337             % if engine == "gecko":
2338             % for prop in SYSTEM_FONT_LONGHANDS:
2339                 PropertyDeclaration::${to_camel_case(prop)}(ref prop) => {
2340                     prop.get_system()
2341                 }
2342             % endfor
2343             % endif
2344             _ => None,
2345         }
2346     }
2347 
2348     /// Is it the default value of line-height?
is_default_line_height(&self) -> bool2349     pub fn is_default_line_height(&self) -> bool {
2350         match *self {
2351             PropertyDeclaration::LineHeight(LineHeight::Normal) => true,
2352             _ => false
2353         }
2354     }
2355 
2356     /// Returns whether the declaration may be serialized as part of a shorthand.
2357     ///
2358     /// This method returns false if this declaration contains variable or has a
2359     /// CSS-wide keyword value, since these values cannot be serialized as part
2360     /// of a shorthand.
2361     ///
2362     /// Caller should check `with_variables_from_shorthand()` and whether all
2363     /// needed declarations has the same CSS-wide keyword first.
2364     ///
2365     /// Note that, serialization of a shorthand may still fail because of other
2366     /// property-specific requirement even when this method returns true for all
2367     /// the longhand declarations.
may_serialize_as_part_of_shorthand(&self) -> bool2368     pub fn may_serialize_as_part_of_shorthand(&self) -> bool {
2369         match *self {
2370             PropertyDeclaration::CSSWideKeyword(..) |
2371             PropertyDeclaration::WithVariables(..) => false,
2372             PropertyDeclaration::Custom(..) =>
2373                 unreachable!("Serializing a custom property as part of shorthand?"),
2374             _ => true,
2375         }
2376     }
2377 
2378     /// Return whether the value is stored as it was in the CSS source,
2379     /// preserving whitespace (as opposed to being parsed into a more abstract
2380     /// data structure).
2381     ///
2382     /// This is the case of custom properties and values that contain
2383     /// unsubstituted variables.
value_is_unparsed(&self) -> bool2384     pub fn value_is_unparsed(&self) -> bool {
2385         match *self {
2386             PropertyDeclaration::WithVariables(..) => true,
2387             PropertyDeclaration::Custom(ref declaration) => {
2388                 matches!(declaration.value, CustomDeclarationValue::Value(..))
2389             }
2390             _ => false,
2391         }
2392     }
2393 
2394     /// Returns true if this property declaration is for one of the animatable
2395     /// properties.
is_animatable(&self) -> bool2396     pub fn is_animatable(&self) -> bool {
2397         match self.id() {
2398             PropertyDeclarationId::Longhand(id) => id.is_animatable(),
2399             PropertyDeclarationId::Custom(..) => false,
2400         }
2401     }
2402 
2403     /// Returns true if this property is a custom property, false
2404     /// otherwise.
is_custom(&self) -> bool2405     pub fn is_custom(&self) -> bool {
2406         matches!(*self, PropertyDeclaration::Custom(..))
2407     }
2408 
2409     /// The `context` parameter controls this:
2410     ///
2411     /// <https://drafts.csswg.org/css-animations/#keyframes>
2412     /// > The <declaration-list> inside of <keyframe-block> accepts any CSS property
2413     /// > except those defined in this specification,
2414     /// > but does accept the `animation-play-state` property and interprets it specially.
2415     ///
2416     /// This will not actually parse Importance values, and will always set things
2417     /// to Importance::Normal. Parsing Importance values is the job of PropertyDeclarationParser,
2418     /// we only set them here so that we don't have to reallocate
parse_into<'i, 't>( declarations: &mut SourcePropertyDeclaration, id: PropertyId, context: &ParserContext, input: &mut Parser<'i, 't>, ) -> Result<(), ParseError<'i>>2419     pub fn parse_into<'i, 't>(
2420         declarations: &mut SourcePropertyDeclaration,
2421         id: PropertyId,
2422         context: &ParserContext,
2423         input: &mut Parser<'i, 't>,
2424     ) -> Result<(), ParseError<'i>> {
2425         assert!(declarations.is_empty());
2426         debug_assert!(id.allowed_in(context), "{:?}", id);
2427 
2428         let non_custom_id = id.non_custom_id();
2429         input.skip_whitespace();
2430 
2431         let start = input.state();
2432         match id {
2433             PropertyId::Custom(property_name) => {
2434                 let value = match input.try_parse(CSSWideKeyword::parse) {
2435                     Ok(keyword) => CustomDeclarationValue::CSSWideKeyword(keyword),
2436                     Err(()) => CustomDeclarationValue::Value(
2437                         crate::custom_properties::SpecifiedValue::parse(input)?
2438                     ),
2439                 };
2440                 declarations.push(PropertyDeclaration::Custom(CustomDeclaration {
2441                     name: property_name,
2442                     value,
2443                 }));
2444                 return Ok(());
2445             }
2446             PropertyId::LonghandAlias(id, _) |
2447             PropertyId::Longhand(id) => {
2448                 input.try_parse(CSSWideKeyword::parse).map(|keyword| {
2449                     PropertyDeclaration::css_wide_keyword(id, keyword)
2450                 }).or_else(|()| {
2451                     input.look_for_var_or_env_functions();
2452                     input.parse_entirely(|input| id.parse_value(context, input))
2453                     .or_else(|err| {
2454                         while let Ok(_) = input.next() {}  // Look for var() after the error.
2455                         if !input.seen_var_or_env_functions() {
2456                             return Err(err);
2457                         }
2458                         input.reset(&start);
2459                         let (first_token_type, css) =
2460                             crate::custom_properties::parse_non_custom_with_var(input)?;
2461                         Ok(PropertyDeclaration::WithVariables(VariableDeclaration {
2462                             id,
2463                             value: Arc::new(UnparsedValue {
2464                                 css: css.into_owned(),
2465                                 first_token_type,
2466                                 url_data: context.url_data.clone(),
2467                                 from_shorthand: None,
2468                             }),
2469                         }))
2470                     })
2471                 }).map(|declaration| {
2472                     declarations.push(declaration)
2473                 })?;
2474             }
2475             PropertyId::ShorthandAlias(id, _) |
2476             PropertyId::Shorthand(id) => {
2477                 if let Ok(keyword) = input.try_parse(CSSWideKeyword::parse) {
2478                     if id == ShorthandId::All {
2479                         declarations.all_shorthand = AllShorthand::CSSWideKeyword(keyword)
2480                     } else {
2481                         for longhand in id.longhands() {
2482                             declarations.push(PropertyDeclaration::css_wide_keyword(longhand, keyword));
2483                         }
2484                     }
2485                 } else {
2486                     input.look_for_var_or_env_functions();
2487                     // Not using parse_entirely here: each
2488                     // ${shorthand.ident}::parse_into function needs to do so
2489                     // *before* pushing to `declarations`.
2490                     id.parse_into(declarations, context, input).or_else(|err| {
2491                         while let Ok(_) = input.next() {}  // Look for var() after the error.
2492                         if !input.seen_var_or_env_functions() {
2493                             return Err(err);
2494                         }
2495 
2496                         input.reset(&start);
2497                         let (first_token_type, css) =
2498                             crate::custom_properties::parse_non_custom_with_var(input)?;
2499                         let unparsed = Arc::new(UnparsedValue {
2500                             css: css.into_owned(),
2501                             first_token_type,
2502                             url_data: context.url_data.clone(),
2503                             from_shorthand: Some(id),
2504                         });
2505                         if id == ShorthandId::All {
2506                             declarations.all_shorthand = AllShorthand::WithVariables(unparsed)
2507                         } else {
2508                             for id in id.longhands() {
2509                                 declarations.push(
2510                                     PropertyDeclaration::WithVariables(VariableDeclaration {
2511                                         id,
2512                                         value: unparsed.clone(),
2513                                     })
2514                                 )
2515                             }
2516                         }
2517                         Ok(())
2518                     })?;
2519                 }
2520             }
2521         }
2522         debug_assert!(non_custom_id.is_some(), "Custom properties should've returned earlier");
2523         if let Some(use_counters) = context.use_counters {
2524             use_counters.non_custom_properties.record(non_custom_id.unwrap());
2525         }
2526         Ok(())
2527     }
2528 }
2529 
2530 type SubpropertiesArray<T> =
2531     [T; ${max(len(s.sub_properties) for s in data.shorthands_except_all()) \
2532           if data.shorthands_except_all() else 0}];
2533 
2534 type SubpropertiesVec<T> = ArrayVec<SubpropertiesArray<T>>;
2535 
2536 /// A stack-allocated vector of `PropertyDeclaration`
2537 /// large enough to parse one CSS `key: value` declaration.
2538 /// (Shorthands expand to multiple `PropertyDeclaration`s.)
2539 pub struct SourcePropertyDeclaration {
2540     declarations: SubpropertiesVec<PropertyDeclaration>,
2541 
2542     /// Stored separately to keep SubpropertiesVec smaller.
2543     all_shorthand: AllShorthand,
2544 }
2545 
2546 impl SourcePropertyDeclaration {
2547     /// Create one. It’s big, try not to move it around.
2548     #[inline]
new() -> Self2549     pub fn new() -> Self {
2550         SourcePropertyDeclaration {
2551             declarations: ::arrayvec::ArrayVec::new(),
2552             all_shorthand: AllShorthand::NotSet,
2553         }
2554     }
2555 
2556     /// Create one with a single PropertyDeclaration.
2557     #[inline]
with_one(decl: PropertyDeclaration) -> Self2558     pub fn with_one(decl: PropertyDeclaration) -> Self {
2559         let mut result = Self::new();
2560         result.declarations.push(decl);
2561         result
2562     }
2563 
2564     /// Similar to Vec::drain: leaves this empty when the return value is dropped.
drain(&mut self) -> SourcePropertyDeclarationDrain2565     pub fn drain(&mut self) -> SourcePropertyDeclarationDrain {
2566         SourcePropertyDeclarationDrain {
2567             declarations: self.declarations.drain(..),
2568             all_shorthand: mem::replace(&mut self.all_shorthand, AllShorthand::NotSet),
2569         }
2570     }
2571 
2572     /// Reset to initial state
clear(&mut self)2573     pub fn clear(&mut self) {
2574         self.declarations.clear();
2575         self.all_shorthand = AllShorthand::NotSet;
2576     }
2577 
is_empty(&self) -> bool2578     fn is_empty(&self) -> bool {
2579         self.declarations.is_empty() && matches!(self.all_shorthand, AllShorthand::NotSet)
2580     }
2581 
push(&mut self, declaration: PropertyDeclaration)2582     fn push(&mut self, declaration: PropertyDeclaration) {
2583         let _result = self.declarations.try_push(declaration);
2584         debug_assert!(_result.is_ok());
2585     }
2586 }
2587 
2588 /// Return type of SourcePropertyDeclaration::drain
2589 pub struct SourcePropertyDeclarationDrain<'a> {
2590     declarations: ArrayVecDrain<'a, SubpropertiesArray<PropertyDeclaration>>,
2591     all_shorthand: AllShorthand,
2592 }
2593 
2594 enum AllShorthand {
2595     NotSet,
2596     CSSWideKeyword(CSSWideKeyword),
2597     WithVariables(Arc<UnparsedValue>)
2598 }
2599 
2600 impl AllShorthand {
2601     /// Iterates property declarations from the given all shorthand value.
2602     #[inline]
declarations(&self) -> AllShorthandDeclarationIterator2603     fn declarations(&self) -> AllShorthandDeclarationIterator {
2604         AllShorthandDeclarationIterator {
2605             all_shorthand: self,
2606             longhands: ShorthandId::All.longhands(),
2607         }
2608     }
2609 }
2610 
2611 struct AllShorthandDeclarationIterator<'a> {
2612     all_shorthand: &'a AllShorthand,
2613     longhands: NonCustomPropertyIterator<LonghandId>,
2614 }
2615 
2616 impl<'a> Iterator for AllShorthandDeclarationIterator<'a> {
2617     type Item = PropertyDeclaration;
2618 
2619     #[inline]
next(&mut self) -> Option<Self::Item>2620     fn next(&mut self) -> Option<Self::Item> {
2621         match *self.all_shorthand {
2622             AllShorthand::NotSet => None,
2623             AllShorthand::CSSWideKeyword(ref keyword) => {
2624                 Some(PropertyDeclaration::css_wide_keyword(self.longhands.next()?, *keyword))
2625             }
2626             AllShorthand::WithVariables(ref unparsed) => {
2627                 Some(PropertyDeclaration::WithVariables(
2628                     VariableDeclaration {
2629                         id: self.longhands.next()?,
2630                         value: unparsed.clone()
2631                     }
2632                 ))
2633             }
2634         }
2635     }
2636 }
2637 
2638 #[cfg(feature = "gecko")]
2639 pub use crate::gecko_properties::style_structs;
2640 
2641 /// The module where all the style structs are defined.
2642 #[cfg(feature = "servo")]
2643 pub mod style_structs {
2644     use fxhash::FxHasher;
2645     use super::longhands;
2646     use std::hash::{Hash, Hasher};
2647     use crate::logical_geometry::WritingMode;
2648     use crate::media_queries::Device;
2649     use crate::values::computed::NonNegativeLength;
2650 
2651     % for style_struct in data.active_style_structs():
2652         % if style_struct.name == "Font":
2653         #[derive(Clone, Debug, MallocSizeOf)]
2654         #[cfg_attr(feature = "servo", derive(Serialize, Deserialize))]
2655         % else:
2656         #[derive(Clone, Debug, MallocSizeOf, PartialEq)]
2657         % endif
2658         /// The ${style_struct.name} style struct.
2659         pub struct ${style_struct.name} {
2660             % for longhand in style_struct.longhands:
2661                 % if not longhand.logical:
2662                     /// The ${longhand.name} computed value.
2663                     pub ${longhand.ident}: longhands::${longhand.ident}::computed_value::T,
2664                 % endif
2665             % endfor
2666             % if style_struct.name == "InheritedText":
2667                 /// The "used" text-decorations that apply to this box.
2668                 ///
2669                 /// FIXME(emilio): This is technically a box-tree concept, and
2670                 /// would be nice to move away from style.
2671                 pub text_decorations_in_effect: crate::values::computed::text::TextDecorationsInEffect,
2672             % endif
2673             % if style_struct.name == "Font":
2674                 /// The font hash, used for font caching.
2675                 pub hash: u64,
2676             % endif
2677             % if style_struct.name == "Box":
2678                 /// The display value specified by the CSS stylesheets (without any style adjustments),
2679                 /// which is needed for hypothetical layout boxes.
2680                 pub original_display: longhands::display::computed_value::T,
2681             % endif
2682         }
2683         % if style_struct.name == "Font":
2684         impl PartialEq for Font {
eq(&self, other: &Font) -> bool2685             fn eq(&self, other: &Font) -> bool {
2686                 self.hash == other.hash
2687                 % for longhand in style_struct.longhands:
2688                     && self.${longhand.ident} == other.${longhand.ident}
2689                 % endfor
2690             }
2691         }
2692         % endif
2693 
2694         impl ${style_struct.name} {
2695             % for longhand in style_struct.longhands:
2696                 % if longhand.logical:
2697                     ${helpers.logical_setter(name=longhand.name)}
2698                 % else:
2699                     % if longhand.ident == "display":
2700                         /// Set `display`.
2701                         ///
2702                         /// We need to keep track of the original display for hypothetical boxes,
2703                         /// so we need to special-case this.
2704                         #[allow(non_snake_case)]
2705                         #[inline]
set_display(&mut self, v: longhands::display::computed_value::T)2706                         pub fn set_display(&mut self, v: longhands::display::computed_value::T) {
2707                             self.display = v;
2708                             self.original_display = v;
2709                         }
2710                     % else:
2711                         /// Set ${longhand.name}.
2712                         #[allow(non_snake_case)]
2713                         #[inline]
2714                         pub fn set_${longhand.ident}(&mut self, v: longhands::${longhand.ident}::computed_value::T) {
2715                             self.${longhand.ident} = v;
2716                         }
2717                     % endif
2718                     % if longhand.ident == "display":
2719                         /// Set `display` from other struct.
2720                         ///
2721                         /// Same as `set_display` above.
2722                         /// Thus, we need to special-case this.
2723                         #[allow(non_snake_case)]
2724                         #[inline]
copy_display_from(&mut self, other: &Self)2725                         pub fn copy_display_from(&mut self, other: &Self) {
2726                             self.display = other.display.clone();
2727                             self.original_display = other.display.clone();
2728                         }
2729                     % else:
2730                         /// Set ${longhand.name} from other struct.
2731                         #[allow(non_snake_case)]
2732                         #[inline]
2733                         pub fn copy_${longhand.ident}_from(&mut self, other: &Self) {
2734                             self.${longhand.ident} = other.${longhand.ident}.clone();
2735                         }
2736                     % endif
2737                     /// Reset ${longhand.name} from the initial struct.
2738                     #[allow(non_snake_case)]
2739                     #[inline]
2740                     pub fn reset_${longhand.ident}(&mut self, other: &Self) {
2741                         self.copy_${longhand.ident}_from(other)
2742                     }
2743 
2744                     /// Get the computed value for ${longhand.name}.
2745                     #[allow(non_snake_case)]
2746                     #[inline]
2747                     pub fn clone_${longhand.ident}(&self) -> longhands::${longhand.ident}::computed_value::T {
2748                         self.${longhand.ident}.clone()
2749                     }
2750                 % endif
2751                 % if longhand.need_index:
2752                     /// If this longhand is indexed, get the number of elements.
2753                     #[allow(non_snake_case)]
2754                     pub fn ${longhand.ident}_count(&self) -> usize {
2755                         self.${longhand.ident}.0.len()
2756                     }
2757 
2758                     /// If this longhand is indexed, get the element at given
2759                     /// index.
2760                     #[allow(non_snake_case)]
2761                     pub fn ${longhand.ident}_at(&self, index: usize)
2762                         -> longhands::${longhand.ident}::computed_value::SingleComputedValue {
2763                         self.${longhand.ident}.0[index].clone()
2764                     }
2765                 % endif
2766             % endfor
2767             % if style_struct.name == "Border":
2768                 % for side in ["top", "right", "bottom", "left"]:
2769                     /// Whether the border-${side} property has nonzero width.
2770                     #[allow(non_snake_case)]
2771                     pub fn border_${side}_has_nonzero_width(&self) -> bool {
2772                         use crate::Zero;
2773                         !self.border_${side}_width.is_zero()
2774                     }
2775                 % endfor
2776             % elif style_struct.name == "Font":
2777                 /// Computes a font hash in order to be able to cache fonts
2778                 /// effectively in GFX and layout.
compute_font_hash(&mut self)2779                 pub fn compute_font_hash(&mut self) {
2780                     // Corresponds to the fields in
2781                     // `gfx::font_template::FontTemplateDescriptor`.
2782                     let mut hasher: FxHasher = Default::default();
2783                     self.font_weight.hash(&mut hasher);
2784                     self.font_stretch.hash(&mut hasher);
2785                     self.font_style.hash(&mut hasher);
2786                     self.font_family.hash(&mut hasher);
2787                     self.hash = hasher.finish()
2788                 }
2789 
2790                 /// (Servo does not handle MathML, so this just calls copy_font_size_from)
inherit_font_size_from(&mut self, parent: &Self, _: Option<NonNegativeLength>, _: &Device)2791                 pub fn inherit_font_size_from(&mut self, parent: &Self,
2792                                               _: Option<NonNegativeLength>,
2793                                               _: &Device) {
2794                     self.copy_font_size_from(parent);
2795                 }
2796                 /// (Servo does not handle MathML, so this just calls set_font_size)
apply_font_size(&mut self, v: longhands::font_size::computed_value::T, _: &Self, _: &Device) -> Option<NonNegativeLength>2797                 pub fn apply_font_size(&mut self,
2798                                        v: longhands::font_size::computed_value::T,
2799                                        _: &Self,
2800                                        _: &Device) -> Option<NonNegativeLength> {
2801                     self.set_font_size(v);
2802                     None
2803                 }
2804                 /// (Servo does not handle MathML, so this does nothing)
apply_unconstrained_font_size(&mut self, _: NonNegativeLength)2805                 pub fn apply_unconstrained_font_size(&mut self, _: NonNegativeLength) {
2806                 }
2807 
2808             % elif style_struct.name == "Outline":
2809                 /// Whether the outline-width property is non-zero.
2810                 #[inline]
outline_has_nonzero_width(&self) -> bool2811                 pub fn outline_has_nonzero_width(&self) -> bool {
2812                     use crate::Zero;
2813                     !self.outline_width.is_zero()
2814                 }
2815             % elif style_struct.name == "Box":
2816                 /// Sets the display property, but without touching original_display,
2817                 /// except when the adjustment comes from root or item display fixups.
set_adjusted_display( &mut self, dpy: longhands::display::computed_value::T, is_item_or_root: bool )2818                 pub fn set_adjusted_display(
2819                     &mut self,
2820                     dpy: longhands::display::computed_value::T,
2821                     is_item_or_root: bool
2822                 ) {
2823                     self.display = dpy;
2824                     if is_item_or_root {
2825                         self.original_display = dpy;
2826                     }
2827                 }
2828             % endif
2829         }
2830 
2831     % endfor
2832 }
2833 
2834 % for style_struct in data.active_style_structs():
2835     impl style_structs::${style_struct.name} {
2836         % for longhand in style_struct.longhands:
2837             % if longhand.need_index:
2838                 /// Iterate over the values of ${longhand.name}.
2839                 #[allow(non_snake_case)]
2840                 #[inline]
2841                 pub fn ${longhand.ident}_iter(&self) -> ${longhand.camel_case}Iter {
2842                     ${longhand.camel_case}Iter {
2843                         style_struct: self,
2844                         current: 0,
2845                         max: self.${longhand.ident}_count(),
2846                     }
2847                 }
2848 
2849                 /// Get a value mod `index` for the property ${longhand.name}.
2850                 #[allow(non_snake_case)]
2851                 #[inline]
2852                 pub fn ${longhand.ident}_mod(&self, index: usize)
2853                     -> longhands::${longhand.ident}::computed_value::SingleComputedValue {
2854                     self.${longhand.ident}_at(index % self.${longhand.ident}_count())
2855                 }
2856 
2857                 /// Clone the computed value for the property.
2858                 #[allow(non_snake_case)]
2859                 #[inline]
2860                 #[cfg(feature = "gecko")]
2861                 pub fn clone_${longhand.ident}(
2862                     &self,
2863                 ) -> longhands::${longhand.ident}::computed_value::T {
2864                     longhands::${longhand.ident}::computed_value::List(
2865                         self.${longhand.ident}_iter().collect()
2866                     )
2867                 }
2868             % endif
2869         % endfor
2870 
2871         % if style_struct.name == "Box":
2872             /// Returns whether there is any animation specified with
2873             /// animation-name other than `none`.
specifies_animations(&self) -> bool2874             pub fn specifies_animations(&self) -> bool {
2875                 self.animation_name_iter().any(|name| name.0.is_some())
2876             }
2877 
2878             /// Returns whether there are any transitions specified.
2879             #[cfg(feature = "servo")]
specifies_transitions(&self) -> bool2880             pub fn specifies_transitions(&self) -> bool {
2881                 (0..self.transition_property_count()).any(|index| {
2882                     let combined_duration =
2883                         self.transition_duration_mod(index).seconds().max(0.) +
2884                         self.transition_delay_mod(index).seconds();
2885                     combined_duration > 0.
2886                 })
2887             }
2888 
2889             /// Returns true if animation properties are equal between styles, but without
2890             /// considering keyframe data.
2891             #[cfg(feature = "servo")]
animations_equals(&self, other: &Self) -> bool2892             pub fn animations_equals(&self, other: &Self) -> bool {
2893                 self.animation_name_iter().eq(other.animation_name_iter()) &&
2894                 self.animation_delay_iter().eq(other.animation_delay_iter()) &&
2895                 self.animation_direction_iter().eq(other.animation_direction_iter()) &&
2896                 self.animation_duration_iter().eq(other.animation_duration_iter()) &&
2897                 self.animation_fill_mode_iter().eq(other.animation_fill_mode_iter()) &&
2898                 self.animation_iteration_count_iter().eq(other.animation_iteration_count_iter()) &&
2899                 self.animation_play_state_iter().eq(other.animation_play_state_iter()) &&
2900                 self.animation_timing_function_iter().eq(other.animation_timing_function_iter())
2901             }
2902 
2903         % elif style_struct.name == "Column":
2904             /// Whether this is a multicol style.
2905             #[cfg(feature = "servo")]
is_multicol(&self) -> bool2906             pub fn is_multicol(&self) -> bool {
2907                 !self.column_width.is_auto() || !self.column_count.is_auto()
2908             }
2909         % endif
2910     }
2911 
2912     % for longhand in style_struct.longhands:
2913         % if longhand.need_index:
2914             /// An iterator over the values of the ${longhand.name} properties.
2915             pub struct ${longhand.camel_case}Iter<'a> {
2916                 style_struct: &'a style_structs::${style_struct.name},
2917                 current: usize,
2918                 max: usize,
2919             }
2920 
2921             impl<'a> Iterator for ${longhand.camel_case}Iter<'a> {
2922                 type Item = longhands::${longhand.ident}::computed_value::SingleComputedValue;
2923 
next(&mut self) -> Option<Self::Item>2924                 fn next(&mut self) -> Option<Self::Item> {
2925                     self.current += 1;
2926                     if self.current <= self.max {
2927                         Some(self.style_struct.${longhand.ident}_at(self.current - 1))
2928                     } else {
2929                         None
2930                     }
2931                 }
2932             }
2933         % endif
2934     % endfor
2935 % endfor
2936 
2937 
2938 #[cfg(feature = "gecko")]
2939 pub use crate::gecko_properties::{ComputedValues, ComputedValuesInner};
2940 
2941 #[cfg(feature = "servo")]
2942 #[cfg_attr(feature = "servo", derive(Clone, Debug))]
2943 /// Actual data of ComputedValues, to match up with Gecko
2944 pub struct ComputedValuesInner {
2945     % for style_struct in data.active_style_structs():
2946         ${style_struct.ident}: Arc<style_structs::${style_struct.name}>,
2947     % endfor
2948     custom_properties: Option<Arc<crate::custom_properties::CustomPropertiesMap>>,
2949     /// The writing mode of this computed values struct.
2950     pub writing_mode: WritingMode,
2951 
2952     /// A set of flags we use to store misc information regarding this style.
2953     pub flags: ComputedValueFlags,
2954 
2955     /// The rule node representing the ordered list of rules matched for this
2956     /// node.  Can be None for default values and text nodes.  This is
2957     /// essentially an optimization to avoid referencing the root rule node.
2958     pub rules: Option<StrongRuleNode>,
2959 
2960     /// The element's computed values if visited, only computed if there's a
2961     /// relevant link for this element. A element's "relevant link" is the
2962     /// element being matched if it is a link or the nearest ancestor link.
2963     visited_style: Option<Arc<ComputedValues>>,
2964 }
2965 
2966 /// The struct that Servo uses to represent computed values.
2967 ///
2968 /// This struct contains an immutable atomically-reference-counted pointer to
2969 /// every kind of style struct.
2970 ///
2971 /// When needed, the structs may be copied in order to get mutated.
2972 #[cfg(feature = "servo")]
2973 #[cfg_attr(feature = "servo", derive(Clone, Debug))]
2974 pub struct ComputedValues {
2975     /// The actual computed values
2976     ///
2977     /// In Gecko the outer ComputedValues is actually a ComputedStyle, whereas
2978     /// ComputedValuesInner is the core set of computed values.
2979     ///
2980     /// We maintain this distinction in servo to reduce the amount of special
2981     /// casing.
2982     inner: ComputedValuesInner,
2983 
2984     /// The pseudo-element that we're using.
2985     pseudo: Option<PseudoElement>,
2986 }
2987 
2988 impl ComputedValues {
2989     /// Returns the pseudo-element that this style represents.
2990     #[cfg(feature = "servo")]
pseudo(&self) -> Option<<&PseudoElement>2991     pub fn pseudo(&self) -> Option<<&PseudoElement> {
2992         self.pseudo.as_ref()
2993     }
2994 
2995     /// Returns true if this is the style for a pseudo-element.
2996     #[cfg(feature = "servo")]
is_pseudo_style(&self) -> bool2997     pub fn is_pseudo_style(&self) -> bool {
2998         self.pseudo().is_some()
2999     }
3000 
3001     /// Returns whether this style's display value is equal to contents.
is_display_contents(&self) -> bool3002     pub fn is_display_contents(&self) -> bool {
3003         self.get_box().clone_display().is_contents()
3004     }
3005 
3006     /// Gets a reference to the rule node. Panic if no rule node exists.
rules(&self) -> &StrongRuleNode3007     pub fn rules(&self) -> &StrongRuleNode {
3008         self.rules.as_ref().unwrap()
3009     }
3010 
3011     /// Returns the visited style, if any.
visited_style(&self) -> Option<<&ComputedValues>3012     pub fn visited_style(&self) -> Option<<&ComputedValues> {
3013         self.visited_style.as_deref()
3014     }
3015 
3016     /// Returns the visited rules, if applicable.
visited_rules(&self) -> Option<<&StrongRuleNode>3017     pub fn visited_rules(&self) -> Option<<&StrongRuleNode> {
3018         self.visited_style.as_ref().and_then(|s| s.rules.as_ref())
3019     }
3020 
3021     /// Gets a reference to the custom properties map (if one exists).
custom_properties(&self) -> Option<<&Arc<crate::custom_properties::CustomPropertiesMap>>3022     pub fn custom_properties(&self) -> Option<<&Arc<crate::custom_properties::CustomPropertiesMap>> {
3023         self.custom_properties.as_ref()
3024     }
3025 
3026     /// Returns whether we have the same custom properties as another style.
3027     ///
3028     /// This should effectively be just:
3029     ///
3030     ///   self.custom_properties() == other.custom_properties()
3031     ///
3032     /// But that's not really the case because IndexMap equality doesn't
3033     /// consider ordering, which we have to account for. Also, for the same
3034     /// reason, IndexMap equality comparisons are slower than needed.
3035     ///
3036     /// See https://github.com/bluss/indexmap/issues/153
custom_properties_equal(&self, other: &Self) -> bool3037     pub fn custom_properties_equal(&self, other: &Self) -> bool {
3038         match (self.custom_properties(), other.custom_properties()) {
3039             (Some(l), Some(r)) => {
3040                 l.len() == r.len() && l.iter().zip(r.iter()).all(|((k1, v1), (k2, v2))| k1 == k2 && v1 == v2)
3041             },
3042             (None, None) => true,
3043             _ => false,
3044         }
3045     }
3046 
3047 % for prop in data.longhands:
3048     /// Gets the computed value of a given property.
3049     #[inline(always)]
3050     #[allow(non_snake_case)]
3051     pub fn clone_${prop.ident}(
3052         &self,
3053     ) -> longhands::${prop.ident}::computed_value::T {
3054         self.get_${prop.style_struct.ident.strip("_")}()
3055         % if prop.logical:
3056             .clone_${prop.ident}(self.writing_mode)
3057         % else:
3058             .clone_${prop.ident}()
3059         % endif
3060     }
3061 % endfor
3062 
3063     /// Writes the value of the given longhand as a string in `dest`.
3064     ///
3065     /// Note that the value will usually be the computed value, except for
3066     /// colors, where it's resolved.
get_longhand_property_value<W>( &self, property_id: LonghandId, dest: &mut CssWriter<W> ) -> fmt::Result where W: Write,3067     pub fn get_longhand_property_value<W>(
3068         &self,
3069         property_id: LonghandId,
3070         dest: &mut CssWriter<W>
3071     ) -> fmt::Result
3072     where
3073         W: Write,
3074     {
3075         use crate::values::resolved::ToResolvedValue;
3076 
3077         let context = resolved::Context {
3078             style: self,
3079         };
3080 
3081         // TODO(emilio): Is it worth to merge branches here just like
3082         // PropertyDeclaration::to_css does?
3083         match property_id {
3084             % for prop in data.longhands:
3085             LonghandId::${prop.camel_case} => {
3086                 let value = self.clone_${prop.ident}();
3087                 value.to_resolved_value(&context).to_css(dest)
3088             }
3089             % endfor
3090         }
3091     }
3092 
3093     /// Resolves the currentColor keyword.
3094     ///
3095     /// Any color value from computed values (except for the 'color' property
3096     /// itself) should go through this method.
3097     ///
3098     /// Usage example:
3099     /// let top_color =
3100     ///   style.resolve_color(style.get_border().clone_border_top_color());
3101     #[inline]
resolve_color(&self, color: computed::Color) -> RGBA3102     pub fn resolve_color(&self, color: computed::Color) -> RGBA {
3103         color.to_rgba(self.get_inherited_text().clone_color())
3104     }
3105 
3106     /// Returns which longhand properties have different values in the two
3107     /// ComputedValues.
3108     #[cfg(feature = "gecko_debug")]
differing_properties(&self, other: &ComputedValues) -> LonghandIdSet3109     pub fn differing_properties(&self, other: &ComputedValues) -> LonghandIdSet {
3110         let mut set = LonghandIdSet::new();
3111         % for prop in data.longhands:
3112         if self.clone_${prop.ident}() != other.clone_${prop.ident}() {
3113             set.insert(LonghandId::${prop.camel_case});
3114         }
3115         % endfor
3116         set
3117     }
3118 
3119     /// Create a `TransitionPropertyIterator` for this styles transition properties.
transition_properties<'a>( &'a self ) -> animated_properties::TransitionPropertyIterator<'a>3120     pub fn transition_properties<'a>(
3121         &'a self
3122     ) -> animated_properties::TransitionPropertyIterator<'a> {
3123         animated_properties::TransitionPropertyIterator::from_style(self)
3124     }
3125 }
3126 
3127 #[cfg(feature = "servo")]
3128 impl ComputedValues {
3129     /// Create a new refcounted `ComputedValues`
3130     pub fn new(
3131         pseudo: Option<<&PseudoElement>,
3132         custom_properties: Option<Arc<crate::custom_properties::CustomPropertiesMap>>,
3133         writing_mode: WritingMode,
3134         flags: ComputedValueFlags,
3135         rules: Option<StrongRuleNode>,
3136         visited_style: Option<Arc<ComputedValues>>,
3137         % for style_struct in data.active_style_structs():
3138         ${style_struct.ident}: Arc<style_structs::${style_struct.name}>,
3139         % endfor
3140     ) -> Arc<Self> {
3141         Arc::new(Self {
3142             inner: ComputedValuesInner {
3143                 custom_properties,
3144                 writing_mode,
3145                 rules,
3146                 visited_style,
3147                 flags,
3148             % for style_struct in data.active_style_structs():
3149                 ${style_struct.ident},
3150             % endfor
3151             },
3152             pseudo: pseudo.cloned(),
3153         })
3154     }
3155 
3156     /// Get the initial computed values.
initial_values() -> &'static Self3157     pub fn initial_values() -> &'static Self { &*INITIAL_SERVO_VALUES }
3158 
3159     /// Serializes the computed value of this property as a string.
computed_value_to_string(&self, property: PropertyDeclarationId) -> String3160     pub fn computed_value_to_string(&self, property: PropertyDeclarationId) -> String {
3161         match property {
3162             PropertyDeclarationId::Longhand(id) => {
3163                 let mut s = String::new();
3164                 self.get_longhand_property_value(
3165                     id,
3166                     &mut CssWriter::new(&mut s)
3167                 ).unwrap();
3168                 s
3169             }
3170             PropertyDeclarationId::Custom(name) => {
3171                 self.custom_properties
3172                     .as_ref()
3173                     .and_then(|map| map.get(name))
3174                     .map_or(String::new(), |value| value.to_css_string())
3175             }
3176         }
3177     }
3178 }
3179 
3180 #[cfg(feature = "servo")]
3181 impl ops::Deref for ComputedValues {
3182     type Target = ComputedValuesInner;
deref(&self) -> &ComputedValuesInner3183     fn deref(&self) -> &ComputedValuesInner {
3184         &self.inner
3185     }
3186 }
3187 
3188 #[cfg(feature = "servo")]
3189 impl ops::DerefMut for ComputedValues {
deref_mut(&mut self) -> &mut ComputedValuesInner3190     fn deref_mut(&mut self) -> &mut ComputedValuesInner {
3191         &mut self.inner
3192     }
3193 }
3194 
3195 #[cfg(feature = "servo")]
3196 impl ComputedValuesInner {
3197     % for style_struct in data.active_style_structs():
3198         /// Clone the ${style_struct.name} struct.
3199         #[inline]
3200         pub fn clone_${style_struct.name_lower}(&self) -> Arc<style_structs::${style_struct.name}> {
3201             self.${style_struct.ident}.clone()
3202         }
3203 
3204         /// Get a immutable reference to the ${style_struct.name} struct.
3205         #[inline]
3206         pub fn get_${style_struct.name_lower}(&self) -> &style_structs::${style_struct.name} {
3207             &self.${style_struct.ident}
3208         }
3209 
3210         /// Gets an immutable reference to the refcounted value that wraps
3211         /// `${style_struct.name}`.
3212         pub fn ${style_struct.name_lower}_arc(&self) -> &Arc<style_structs::${style_struct.name}> {
3213             &self.${style_struct.ident}
3214         }
3215 
3216         /// Get a mutable reference to the ${style_struct.name} struct.
3217         #[inline]
3218         pub fn mutate_${style_struct.name_lower}(&mut self) -> &mut style_structs::${style_struct.name} {
3219             Arc::make_mut(&mut self.${style_struct.ident})
3220         }
3221     % endfor
3222 
3223     /// Gets a reference to the rule node. Panic if no rule node exists.
rules(&self) -> &StrongRuleNode3224     pub fn rules(&self) -> &StrongRuleNode {
3225         self.rules.as_ref().unwrap()
3226     }
3227 
3228     #[inline]
3229     /// Returns whether the "content" property for the given style is completely
3230     /// ineffective, and would yield an empty `::before` or `::after`
3231     /// pseudo-element.
ineffective_content_property(&self) -> bool3232     pub fn ineffective_content_property(&self) -> bool {
3233         use crate::values::generics::counters::Content;
3234         match self.get_counters().content {
3235             Content::Normal | Content::None => true,
3236             Content::Items(ref items) => items.is_empty(),
3237         }
3238     }
3239 
3240     /// Whether the current style or any of its ancestors is multicolumn.
3241     #[inline]
can_be_fragmented(&self) -> bool3242     pub fn can_be_fragmented(&self) -> bool {
3243         self.flags.contains(ComputedValueFlags::CAN_BE_FRAGMENTED)
3244     }
3245 
3246     /// Whether the current style is multicolumn.
3247     #[inline]
is_multicol(&self) -> bool3248     pub fn is_multicol(&self) -> bool {
3249         self.get_column().is_multicol()
3250     }
3251 
3252     /// Get the logical computed inline size.
3253     #[inline]
content_inline_size(&self) -> &computed::Size3254     pub fn content_inline_size(&self) -> &computed::Size {
3255         let position_style = self.get_position();
3256         if self.writing_mode.is_vertical() {
3257             &position_style.height
3258         } else {
3259             &position_style.width
3260         }
3261     }
3262 
3263     /// Get the logical computed block size.
3264     #[inline]
content_block_size(&self) -> &computed::Size3265     pub fn content_block_size(&self) -> &computed::Size {
3266         let position_style = self.get_position();
3267         if self.writing_mode.is_vertical() { &position_style.width } else { &position_style.height }
3268     }
3269 
3270     /// Get the logical computed min inline size.
3271     #[inline]
min_inline_size(&self) -> &computed::Size3272     pub fn min_inline_size(&self) -> &computed::Size {
3273         let position_style = self.get_position();
3274         if self.writing_mode.is_vertical() { &position_style.min_height } else { &position_style.min_width }
3275     }
3276 
3277     /// Get the logical computed min block size.
3278     #[inline]
min_block_size(&self) -> &computed::Size3279     pub fn min_block_size(&self) -> &computed::Size {
3280         let position_style = self.get_position();
3281         if self.writing_mode.is_vertical() { &position_style.min_width } else { &position_style.min_height }
3282     }
3283 
3284     /// Get the logical computed max inline size.
3285     #[inline]
max_inline_size(&self) -> &computed::MaxSize3286     pub fn max_inline_size(&self) -> &computed::MaxSize {
3287         let position_style = self.get_position();
3288         if self.writing_mode.is_vertical() { &position_style.max_height } else { &position_style.max_width }
3289     }
3290 
3291     /// Get the logical computed max block size.
3292     #[inline]
max_block_size(&self) -> &computed::MaxSize3293     pub fn max_block_size(&self) -> &computed::MaxSize {
3294         let position_style = self.get_position();
3295         if self.writing_mode.is_vertical() { &position_style.max_width } else { &position_style.max_height }
3296     }
3297 
3298     /// Get the logical computed padding for this writing mode.
3299     #[inline]
logical_padding(&self) -> LogicalMargin<<&computed::LengthPercentage>3300     pub fn logical_padding(&self) -> LogicalMargin<<&computed::LengthPercentage> {
3301         let padding_style = self.get_padding();
3302         LogicalMargin::from_physical(self.writing_mode, SideOffsets2D::new(
3303             &padding_style.padding_top.0,
3304             &padding_style.padding_right.0,
3305             &padding_style.padding_bottom.0,
3306             &padding_style.padding_left.0,
3307         ))
3308     }
3309 
3310     /// Get the logical border width
3311     #[inline]
border_width_for_writing_mode(&self, writing_mode: WritingMode) -> LogicalMargin<Au>3312     pub fn border_width_for_writing_mode(&self, writing_mode: WritingMode) -> LogicalMargin<Au> {
3313         let border_style = self.get_border();
3314         LogicalMargin::from_physical(writing_mode, SideOffsets2D::new(
3315             Au::from(border_style.border_top_width),
3316             Au::from(border_style.border_right_width),
3317             Au::from(border_style.border_bottom_width),
3318             Au::from(border_style.border_left_width),
3319         ))
3320     }
3321 
3322     /// Gets the logical computed border widths for this style.
3323     #[inline]
logical_border_width(&self) -> LogicalMargin<Au>3324     pub fn logical_border_width(&self) -> LogicalMargin<Au> {
3325         self.border_width_for_writing_mode(self.writing_mode)
3326     }
3327 
3328     /// Gets the logical computed margin from this style.
3329     #[inline]
logical_margin(&self) -> LogicalMargin<<&computed::LengthPercentageOrAuto>3330     pub fn logical_margin(&self) -> LogicalMargin<<&computed::LengthPercentageOrAuto> {
3331         let margin_style = self.get_margin();
3332         LogicalMargin::from_physical(self.writing_mode, SideOffsets2D::new(
3333             &margin_style.margin_top,
3334             &margin_style.margin_right,
3335             &margin_style.margin_bottom,
3336             &margin_style.margin_left,
3337         ))
3338     }
3339 
3340     /// Gets the logical position from this style.
3341     #[inline]
logical_position(&self) -> LogicalMargin<<&computed::LengthPercentageOrAuto>3342     pub fn logical_position(&self) -> LogicalMargin<<&computed::LengthPercentageOrAuto> {
3343         // FIXME(SimonSapin): should be the writing mode of the containing block, maybe?
3344         let position_style = self.get_position();
3345         LogicalMargin::from_physical(self.writing_mode, SideOffsets2D::new(
3346             &position_style.top,
3347             &position_style.right,
3348             &position_style.bottom,
3349             &position_style.left,
3350         ))
3351     }
3352 
3353     /// Return true if the effects force the transform style to be Flat
overrides_transform_style(&self) -> bool3354     pub fn overrides_transform_style(&self) -> bool {
3355         use crate::computed_values::mix_blend_mode::T as MixBlendMode;
3356 
3357         let effects = self.get_effects();
3358         // TODO(gw): Add clip-path, isolation, mask-image, mask-border-source when supported.
3359         effects.opacity < 1.0 ||
3360            !effects.filter.0.is_empty() ||
3361            !effects.clip.is_auto() ||
3362            effects.mix_blend_mode != MixBlendMode::Normal
3363     }
3364 
3365     /// <https://drafts.csswg.org/css-transforms/#grouping-property-values>
get_used_transform_style(&self) -> computed_values::transform_style::T3366     pub fn get_used_transform_style(&self) -> computed_values::transform_style::T {
3367         use crate::computed_values::transform_style::T as TransformStyle;
3368 
3369         let box_ = self.get_box();
3370 
3371         if self.overrides_transform_style() {
3372             TransformStyle::Flat
3373         } else {
3374             // Return the computed value if not overridden by the above exceptions
3375             box_.transform_style
3376         }
3377     }
3378 
3379     /// Whether given this transform value, the compositor would require a
3380     /// layer.
transform_requires_layer(&self) -> bool3381     pub fn transform_requires_layer(&self) -> bool {
3382         use crate::values::generics::transform::TransformOperation;
3383         // Check if the transform matrix is 2D or 3D
3384         for transform in &*self.get_box().transform.0 {
3385             match *transform {
3386                 TransformOperation::Perspective(..) => {
3387                     return true;
3388                 }
3389                 TransformOperation::Matrix3D(m) => {
3390                     // See http://dev.w3.org/csswg/css-transforms/#2d-matrix
3391                     if m.m31 != 0.0 || m.m32 != 0.0 ||
3392                        m.m13 != 0.0 || m.m23 != 0.0 ||
3393                        m.m43 != 0.0 || m.m14 != 0.0 ||
3394                        m.m24 != 0.0 || m.m34 != 0.0 ||
3395                        m.m33 != 1.0 || m.m44 != 1.0 {
3396                         return true;
3397                     }
3398                 }
3399                 TransformOperation::Translate3D(_, _, z) |
3400                 TransformOperation::TranslateZ(z) => {
3401                     if z.px() != 0. {
3402                         return true;
3403                     }
3404                 }
3405                 _ => {}
3406             }
3407         }
3408 
3409         // Neither perspective nor transform present
3410         false
3411     }
3412 }
3413 
3414 % if engine == "gecko":
3415     pub use crate::servo_arc::RawOffsetArc as BuilderArc;
3416     /// Clone an arc, returning a regular arc
clone_arc<T: 'static>(x: &BuilderArc<T>) -> Arc<T>3417     fn clone_arc<T: 'static>(x: &BuilderArc<T>) -> Arc<T> {
3418         Arc::from_raw_offset(x.clone())
3419     }
3420 % else:
3421     pub use crate::servo_arc::Arc as BuilderArc;
3422     /// Clone an arc, returning a regular arc
clone_arc<T: 'static>(x: &BuilderArc<T>) -> Arc<T>3423     fn clone_arc<T: 'static>(x: &BuilderArc<T>) -> Arc<T> {
3424         x.clone()
3425     }
3426 % endif
3427 
3428 /// A reference to a style struct of the parent, or our own style struct.
3429 pub enum StyleStructRef<'a, T: 'static> {
3430     /// A borrowed struct from the parent, for example, for inheriting style.
3431     Borrowed(&'a BuilderArc<T>),
3432     /// An owned struct, that we've already mutated.
3433     Owned(UniqueArc<T>),
3434     /// Temporarily vacated, will panic if accessed
3435     Vacated,
3436 }
3437 
3438 impl<'a, T: 'a> StyleStructRef<'a, T>
3439 where
3440     T: Clone,
3441 {
3442     /// Ensure a mutable reference of this value exists, either cloning the
3443     /// borrowed value, or returning the owned one.
mutate(&mut self) -> &mut T3444     pub fn mutate(&mut self) -> &mut T {
3445         if let StyleStructRef::Borrowed(v) = *self {
3446             *self = StyleStructRef::Owned(UniqueArc::new((**v).clone()));
3447         }
3448 
3449         match *self {
3450             StyleStructRef::Owned(ref mut v) => v,
3451             StyleStructRef::Borrowed(..) => unreachable!(),
3452             StyleStructRef::Vacated => panic!("Accessed vacated style struct")
3453         }
3454     }
3455 
3456     /// Whether this is pointer-equal to the struct we're going to copy the
3457     /// value from.
3458     ///
3459     /// This is used to avoid allocations when people write stuff like `font:
3460     /// inherit` or such `all: initial`.
3461     #[inline]
ptr_eq(&self, struct_to_copy_from: &T) -> bool3462     pub fn ptr_eq(&self, struct_to_copy_from: &T) -> bool {
3463         match *self {
3464             StyleStructRef::Owned(..) => false,
3465             StyleStructRef::Borrowed(arc) => {
3466                 &**arc as *const T == struct_to_copy_from as *const T
3467             }
3468             StyleStructRef::Vacated => panic!("Accessed vacated style struct")
3469         }
3470     }
3471 
3472     /// Extract a unique Arc from this struct, vacating it.
3473     ///
3474     /// The vacated state is a transient one, please put the Arc back
3475     /// when done via `put()`. This function is to be used to separate
3476     /// the struct being mutated from the computed context
take(&mut self) -> UniqueArc<T>3477     pub fn take(&mut self) -> UniqueArc<T> {
3478         use std::mem::replace;
3479         let inner = replace(self, StyleStructRef::Vacated);
3480 
3481         match inner {
3482             StyleStructRef::Owned(arc) => arc,
3483             StyleStructRef::Borrowed(arc) => UniqueArc::new((**arc).clone()),
3484             StyleStructRef::Vacated => panic!("Accessed vacated style struct"),
3485         }
3486     }
3487 
3488     /// Replace vacated ref with an arc
put(&mut self, arc: UniqueArc<T>)3489     pub fn put(&mut self, arc: UniqueArc<T>) {
3490         debug_assert!(matches!(*self, StyleStructRef::Vacated));
3491         *self = StyleStructRef::Owned(arc);
3492     }
3493 
3494     /// Get a mutable reference to the owned struct, or `None` if the struct
3495     /// hasn't been mutated.
get_if_mutated(&mut self) -> Option<<&mut T>3496     pub fn get_if_mutated(&mut self) -> Option<<&mut T> {
3497         match *self {
3498             StyleStructRef::Owned(ref mut v) => Some(v),
3499             StyleStructRef::Borrowed(..) => None,
3500             StyleStructRef::Vacated => panic!("Accessed vacated style struct")
3501         }
3502     }
3503 
3504     /// Returns an `Arc` to the internal struct, constructing one if
3505     /// appropriate.
build(self) -> Arc<T>3506     pub fn build(self) -> Arc<T> {
3507         match self {
3508             StyleStructRef::Owned(v) => v.shareable(),
3509             StyleStructRef::Borrowed(v) => clone_arc(v),
3510             StyleStructRef::Vacated => panic!("Accessed vacated style struct")
3511         }
3512     }
3513 }
3514 
3515 impl<'a, T: 'a> ops::Deref for StyleStructRef<'a, T> {
3516     type Target = T;
3517 
deref(&self) -> &T3518     fn deref(&self) -> &T {
3519         match *self {
3520             StyleStructRef::Owned(ref v) => &**v,
3521             StyleStructRef::Borrowed(v) => &**v,
3522             StyleStructRef::Vacated => panic!("Accessed vacated style struct")
3523         }
3524     }
3525 }
3526 
3527 /// A type used to compute a struct with minimal overhead.
3528 ///
3529 /// This allows holding references to the parent/default computed values without
3530 /// actually cloning them, until we either build the style, or mutate the
3531 /// inherited value.
3532 pub struct StyleBuilder<'a> {
3533     /// The device we're using to compute style.
3534     ///
3535     /// This provides access to viewport unit ratios, etc.
3536     pub device: &'a Device,
3537 
3538     /// The style we're inheriting from.
3539     ///
3540     /// This is effectively
3541     /// `parent_style.unwrap_or(device.default_computed_values())`.
3542     inherited_style: &'a ComputedValues,
3543 
3544     /// The style we're inheriting from for properties that don't inherit from
3545     /// ::first-line.  This is the same as inherited_style, unless
3546     /// inherited_style is a ::first-line style.
3547     inherited_style_ignoring_first_line: &'a ComputedValues,
3548 
3549     /// The style we're getting reset structs from.
3550     reset_style: &'a ComputedValues,
3551 
3552     /// The rule node representing the ordered list of rules matched for this
3553     /// node.
3554     pub rules: Option<StrongRuleNode>,
3555 
3556     custom_properties: Option<Arc<crate::custom_properties::CustomPropertiesMap>>,
3557 
3558     /// The pseudo-element this style will represent.
3559     pub pseudo: Option<<&'a PseudoElement>,
3560 
3561     /// Whether we have mutated any reset structs since the the last time
3562     /// `clear_modified_reset` was called.  This is used to tell whether the
3563     /// `StyleAdjuster` did any work.
3564     modified_reset: bool,
3565 
3566     /// Whether this is the style for the root element.
3567     pub is_root_element: bool,
3568 
3569     /// The writing mode flags.
3570     ///
3571     /// TODO(emilio): Make private.
3572     pub writing_mode: WritingMode,
3573 
3574     /// Flags for the computed value.
3575     pub flags: Cell<ComputedValueFlags>,
3576 
3577     /// The element's style if visited, only computed if there's a relevant link
3578     /// for this element.  A element's "relevant link" is the element being
3579     /// matched if it is a link or the nearest ancestor link.
3580     visited_style: Option<Arc<ComputedValues>>,
3581     % for style_struct in data.active_style_structs():
3582         ${style_struct.ident}: StyleStructRef<'a, style_structs::${style_struct.name}>,
3583     % endfor
3584 }
3585 
3586 impl<'a> StyleBuilder<'a> {
3587     /// Trivially construct a `StyleBuilder`.
3588     fn new(
3589         device: &'a Device,
3590         parent_style: Option<<&'a ComputedValues>,
3591         parent_style_ignoring_first_line: Option<<&'a ComputedValues>,
3592         pseudo: Option<<&'a PseudoElement>,
3593         rules: Option<StrongRuleNode>,
3594         custom_properties: Option<Arc<crate::custom_properties::CustomPropertiesMap>>,
3595         is_root_element: bool,
3596     ) -> Self {
3597         debug_assert_eq!(parent_style.is_some(), parent_style_ignoring_first_line.is_some());
3598         #[cfg(feature = "gecko")]
3599         debug_assert!(parent_style.is_none() ||
3600                       std::ptr::eq(parent_style.unwrap(),
3601                                      parent_style_ignoring_first_line.unwrap()) ||
3602                       parent_style.unwrap().is_first_line_style());
3603         let reset_style = device.default_computed_values();
3604         let inherited_style = parent_style.unwrap_or(reset_style);
3605         let inherited_style_ignoring_first_line = parent_style_ignoring_first_line.unwrap_or(reset_style);
3606 
3607         let flags = inherited_style.flags.inherited();
3608 
3609         StyleBuilder {
3610             device,
3611             inherited_style,
3612             inherited_style_ignoring_first_line,
3613             reset_style,
3614             pseudo,
3615             rules,
3616             modified_reset: false,
3617             is_root_element,
3618             custom_properties,
3619             writing_mode: inherited_style.writing_mode,
3620             flags: Cell::new(flags),
3621             visited_style: None,
3622             % for style_struct in data.active_style_structs():
3623             % if style_struct.inherited:
3624             ${style_struct.ident}: StyleStructRef::Borrowed(inherited_style.${style_struct.name_lower}_arc()),
3625             % else:
3626             ${style_struct.ident}: StyleStructRef::Borrowed(reset_style.${style_struct.name_lower}_arc()),
3627             % endif
3628             % endfor
3629         }
3630     }
3631 
3632     /// NOTE(emilio): This is done so we can compute relative units with respect
3633     /// to the parent style, but all the early properties / writing-mode / etc
3634     /// are already set to the right ones on the kid.
3635     ///
3636     /// Do _not_ actually call this to construct a style, this should mostly be
3637     /// used for animations.
3638     pub fn for_animation(
3639         device: &'a Device,
3640         style_to_derive_from: &'a ComputedValues,
3641         parent_style: Option<<&'a ComputedValues>,
3642     ) -> Self {
3643         let reset_style = device.default_computed_values();
3644         let inherited_style = parent_style.unwrap_or(reset_style);
3645         #[cfg(feature = "gecko")]
3646         debug_assert!(parent_style.is_none() ||
3647                       !parent_style.unwrap().is_first_line_style());
3648         StyleBuilder {
3649             device,
3650             inherited_style,
3651             // None of our callers pass in ::first-line parent styles.
3652             inherited_style_ignoring_first_line: inherited_style,
3653             reset_style,
3654             pseudo: None,
3655             modified_reset: false,
3656             is_root_element: false,
3657             rules: None,
3658             custom_properties: style_to_derive_from.custom_properties().cloned(),
3659             writing_mode: style_to_derive_from.writing_mode,
3660             flags: Cell::new(style_to_derive_from.flags),
3661             visited_style: None,
3662             % for style_struct in data.active_style_structs():
3663             ${style_struct.ident}: StyleStructRef::Borrowed(
3664                 style_to_derive_from.${style_struct.name_lower}_arc()
3665             ),
3666             % endfor
3667         }
3668     }
3669 
3670     /// Copy the reset properties from `style`.
3671     pub fn copy_reset_from(&mut self, style: &'a ComputedValues) {
3672         % for style_struct in data.active_style_structs():
3673         % if not style_struct.inherited:
3674         self.${style_struct.ident} =
3675             StyleStructRef::Borrowed(style.${style_struct.name_lower}_arc());
3676         % endif
3677         % endfor
3678     }
3679 
3680     % for property in data.longhands:
3681     % if not property.style_struct.inherited:
3682     /// Inherit `${property.ident}` from our parent style.
3683     #[allow(non_snake_case)]
3684     pub fn inherit_${property.ident}(&mut self) {
3685         let inherited_struct =
3686             self.inherited_style_ignoring_first_line
3687                 .get_${property.style_struct.name_lower}();
3688 
3689         self.modified_reset = true;
3690         self.add_flags(ComputedValueFlags::INHERITS_RESET_STYLE);
3691 
3692         % if property.ident == "content":
3693         self.add_flags(ComputedValueFlags::CONTENT_DEPENDS_ON_INHERITED_STYLE);
3694         % endif
3695 
3696         % if property.ident == "display":
3697         self.add_flags(ComputedValueFlags::DISPLAY_DEPENDS_ON_INHERITED_STYLE);
3698         % endif
3699 
3700         if self.${property.style_struct.ident}.ptr_eq(inherited_struct) {
3701             return;
3702         }
3703 
3704         self.${property.style_struct.ident}.mutate()
3705             .copy_${property.ident}_from(
3706                 inherited_struct,
3707                 % if property.logical:
3708                 self.writing_mode,
3709                 % endif
3710             );
3711     }
3712     % else:
3713     /// Reset `${property.ident}` to the initial value.
3714     #[allow(non_snake_case)]
3715     pub fn reset_${property.ident}(&mut self) {
3716         let reset_struct =
3717             self.reset_style.get_${property.style_struct.name_lower}();
3718 
3719         if self.${property.style_struct.ident}.ptr_eq(reset_struct) {
3720             return;
3721         }
3722 
3723         self.${property.style_struct.ident}.mutate()
3724             .reset_${property.ident}(
3725                 reset_struct,
3726                 % if property.logical:
3727                 self.writing_mode,
3728                 % endif
3729             );
3730     }
3731     % endif
3732 
3733     % if not property.is_vector or property.simple_vector_bindings or engine in ["servo-2013", "servo-2020"]:
3734     /// Set the `${property.ident}` to the computed value `value`.
3735     #[allow(non_snake_case)]
3736     pub fn set_${property.ident}(
3737         &mut self,
3738         value: longhands::${property.ident}::computed_value::T
3739     ) {
3740         % if not property.style_struct.inherited:
3741         self.modified_reset = true;
3742         % endif
3743 
3744         self.${property.style_struct.ident}.mutate()
3745             .set_${property.ident}(
3746                 value,
3747                 % if property.logical:
3748                 self.writing_mode,
3749                 % endif
3750             );
3751     }
3752     % endif
3753     % endfor
3754     <% del property %>
3755 
3756     /// Inherits style from the parent element, accounting for the default
3757     /// computed values that need to be provided as well.
3758     pub fn for_inheritance(
3759         device: &'a Device,
3760         parent: Option<<&'a ComputedValues>,
3761         pseudo: Option<<&'a PseudoElement>,
3762     ) -> Self {
3763         // Rebuild the visited style from the parent, ensuring that it will also
3764         // not have rules.  This matches the unvisited style that will be
3765         // produced by this builder.  This assumes that the caller doesn't need
3766         // to adjust or process visited style, so we can just build visited
3767         // style here for simplicity.
3768         let visited_style = parent.and_then(|parent| {
3769             parent.visited_style().map(|style| {
3770                 Self::for_inheritance(
3771                     device,
3772                     Some(style),
3773                     pseudo,
3774                 ).build()
3775             })
3776         });
3777         let mut ret = Self::new(
3778             device,
3779             parent,
3780             parent,
3781             pseudo,
3782             /* rules = */ None,
3783             parent.and_then(|p| p.custom_properties().cloned()),
3784             /* is_root_element = */ false,
3785         );
3786         ret.visited_style = visited_style;
3787         ret
3788     }
3789 
3790     /// Returns whether we have a visited style.
3791     pub fn has_visited_style(&self) -> bool {
3792         self.visited_style.is_some()
3793     }
3794 
3795     /// Returns whether we're a pseudo-elements style.
3796     pub fn is_pseudo_element(&self) -> bool {
3797         self.pseudo.map_or(false, |p| !p.is_anon_box())
3798     }
3799 
3800     /// Returns the style we're getting reset properties from.
3801     pub fn default_style(&self) -> &'a ComputedValues {
3802         self.reset_style
3803     }
3804 
3805     % for style_struct in data.active_style_structs():
3806         /// Gets an immutable view of the current `${style_struct.name}` style.
3807         pub fn get_${style_struct.name_lower}(&self) -> &style_structs::${style_struct.name} {
3808             &self.${style_struct.ident}
3809         }
3810 
3811         /// Gets a mutable view of the current `${style_struct.name}` style.
3812         pub fn mutate_${style_struct.name_lower}(&mut self) -> &mut style_structs::${style_struct.name} {
3813             % if not style_struct.inherited:
3814             self.modified_reset = true;
3815             % endif
3816             self.${style_struct.ident}.mutate()
3817         }
3818 
3819         /// Gets a mutable view of the current `${style_struct.name}` style.
3820         pub fn take_${style_struct.name_lower}(&mut self) -> UniqueArc<style_structs::${style_struct.name}> {
3821             % if not style_struct.inherited:
3822             self.modified_reset = true;
3823             % endif
3824             self.${style_struct.ident}.take()
3825         }
3826 
3827         /// Gets a mutable view of the current `${style_struct.name}` style.
3828         pub fn put_${style_struct.name_lower}(&mut self, s: UniqueArc<style_structs::${style_struct.name}>) {
3829             self.${style_struct.ident}.put(s)
3830         }
3831 
3832         /// Gets a mutable view of the current `${style_struct.name}` style,
3833         /// only if it's been mutated before.
3834         pub fn get_${style_struct.name_lower}_if_mutated(&mut self)
3835                                                          -> Option<<&mut style_structs::${style_struct.name}> {
3836             self.${style_struct.ident}.get_if_mutated()
3837         }
3838 
3839         /// Reset the current `${style_struct.name}` style to its default value.
3840         pub fn reset_${style_struct.name_lower}_struct(&mut self) {
3841             self.${style_struct.ident} =
3842                 StyleStructRef::Borrowed(self.reset_style.${style_struct.name_lower}_arc());
3843         }
3844     % endfor
3845     <% del style_struct %>
3846 
3847     /// Returns whether this computed style represents a floated object.
3848     pub fn is_floating(&self) -> bool {
3849         self.get_box().clone_float().is_floating()
3850     }
3851 
3852     /// Returns whether this computed style represents an absolutely-positioned
3853     /// object.
3854     pub fn is_absolutely_positioned(&self) -> bool {
3855         self.get_box().clone_position().is_absolutely_positioned()
3856     }
3857 
3858     /// Whether this style has a top-layer style.
3859     #[cfg(feature = "servo")]
3860     pub fn in_top_layer(&self) -> bool {
3861         matches!(self.get_box().clone__servo_top_layer(),
3862                  longhands::_servo_top_layer::computed_value::T::Top)
3863     }
3864 
3865     /// Whether this style has a top-layer style.
3866     #[cfg(feature = "gecko")]
3867     pub fn in_top_layer(&self) -> bool {
3868         matches!(self.get_box().clone__moz_top_layer(),
3869                  longhands::_moz_top_layer::computed_value::T::Top)
3870     }
3871 
3872     /// Clears the "have any reset structs been modified" flag.
3873     fn clear_modified_reset(&mut self) {
3874         self.modified_reset = false;
3875     }
3876 
3877     /// Returns whether we have mutated any reset structs since the the last
3878     /// time `clear_modified_reset` was called.
3879     fn modified_reset(&self) -> bool {
3880         self.modified_reset
3881     }
3882 
3883     /// Return the current flags.
3884     #[inline]
3885     pub fn flags(&self) -> ComputedValueFlags {
3886         self.flags.get()
3887     }
3888 
3889     /// Add a flag to the current builder.
3890     #[inline]
3891     pub fn add_flags(&self, flag: ComputedValueFlags) {
3892         let flags = self.flags() | flag;
3893         self.flags.set(flags);
3894     }
3895 
3896     /// Removes a flag to the current builder.
3897     #[inline]
3898     pub fn remove_flags(&self, flag: ComputedValueFlags) {
3899         let flags = self.flags() & !flag;
3900         self.flags.set(flags);
3901     }
3902 
3903     /// Turns this `StyleBuilder` into a proper `ComputedValues` instance.
3904     pub fn build(self) -> Arc<ComputedValues> {
3905         ComputedValues::new(
3906             self.pseudo,
3907             self.custom_properties,
3908             self.writing_mode,
3909             self.flags.get(),
3910             self.rules,
3911             self.visited_style,
3912             % for style_struct in data.active_style_structs():
3913             self.${style_struct.ident}.build(),
3914             % endfor
3915         )
3916     }
3917 
3918     /// Get the custom properties map if necessary.
3919     ///
3920     /// Cloning the Arc here is fine because it only happens in the case where
3921     /// we have custom properties, and those are both rare and expensive.
3922     fn custom_properties(&self) -> Option<<&Arc<crate::custom_properties::CustomPropertiesMap>> {
3923         self.custom_properties.as_ref()
3924     }
3925 
3926     /// Access to various information about our inherited styles.  We don't
3927     /// expose an inherited ComputedValues directly, because in the
3928     /// ::first-line case some of the inherited information needs to come from
3929     /// one ComputedValues instance and some from a different one.
3930 
3931     /// Inherited writing-mode.
3932     pub fn inherited_writing_mode(&self) -> &WritingMode {
3933         &self.inherited_style.writing_mode
3934     }
3935 
3936     /// The computed value flags of our parent.
3937     #[inline]
3938     pub fn get_parent_flags(&self) -> ComputedValueFlags {
3939         self.inherited_style.flags
3940     }
3941 
3942     /// And access to inherited style structs.
3943     % for style_struct in data.active_style_structs():
3944         /// Gets our inherited `${style_struct.name}`.  We don't name these
3945         /// accessors `inherited_${style_struct.name_lower}` because we already
3946         /// have things like "box" vs "inherited_box" as struct names.  Do the
3947         /// next-best thing and call them `parent_${style_struct.name_lower}`
3948         /// instead.
3949         pub fn get_parent_${style_struct.name_lower}(&self) -> &style_structs::${style_struct.name} {
3950             % if style_struct.inherited:
3951             self.inherited_style.get_${style_struct.name_lower}()
3952             % else:
3953             self.inherited_style_ignoring_first_line.get_${style_struct.name_lower}()
3954             % endif
3955         }
3956     % endfor
3957 }
3958 
3959 #[cfg(feature = "servo")]
3960 pub use self::lazy_static_module::INITIAL_SERVO_VALUES;
3961 
3962 // Use a module to work around #[cfg] on lazy_static! not being applied to every generated item.
3963 #[cfg(feature = "servo")]
3964 #[allow(missing_docs)]
3965 mod lazy_static_module {
3966     use crate::logical_geometry::WritingMode;
3967     use crate::computed_value_flags::ComputedValueFlags;
3968     use servo_arc::Arc;
3969     use super::{ComputedValues, ComputedValuesInner, longhands, style_structs};
3970 
3971     lazy_static! {
3972         /// The initial values for all style structs as defined by the specification.
3973         pub static ref INITIAL_SERVO_VALUES: ComputedValues = ComputedValues {
3974             inner: ComputedValuesInner {
3975                 % for style_struct in data.active_style_structs():
3976                     ${style_struct.ident}: Arc::new(style_structs::${style_struct.name} {
3977                         % for longhand in style_struct.longhands:
3978                             % if not longhand.logical:
3979                                 ${longhand.ident}: longhands::${longhand.ident}::get_initial_value(),
3980                             % endif
3981                         % endfor
3982                         % if style_struct.name == "InheritedText":
3983                             text_decorations_in_effect:
3984                                 crate::values::computed::text::TextDecorationsInEffect::default(),
3985                         % endif
3986                         % if style_struct.name == "Font":
3987                             hash: 0,
3988                         % endif
3989                         % if style_struct.name == "Box":
3990                             original_display: longhands::display::get_initial_value(),
3991                         % endif
3992                     }),
3993                 % endfor
3994                 custom_properties: None,
3995                 writing_mode: WritingMode::empty(),
3996                 rules: None,
3997                 visited_style: None,
3998                 flags: ComputedValueFlags::empty(),
3999             },
4000             pseudo: None,
4001         };
4002     }
4003 }
4004 
4005 /// A per-longhand function that performs the CSS cascade for that longhand.
4006 pub type CascadePropertyFn =
4007     extern "Rust" fn(
4008         declaration: &PropertyDeclaration,
4009         context: &mut computed::Context,
4010     );
4011 
4012 /// A per-longhand array of functions to perform the CSS cascade on each of
4013 /// them, effectively doing virtual dispatch.
4014 pub static CASCADE_PROPERTY: [CascadePropertyFn; ${len(data.longhands)}] = [
4015     % for property in data.longhands:
4016         longhands::${property.ident}::cascade_property,
4017     % endfor
4018 ];
4019 
4020 
4021 /// See StyleAdjuster::adjust_for_border_width.
4022 pub fn adjust_border_width(style: &mut StyleBuilder) {
4023     % for side in ["top", "right", "bottom", "left"]:
4024         // Like calling to_computed_value, which wouldn't type check.
4025         if style.get_border().clone_border_${side}_style().none_or_hidden() &&
4026            style.get_border().border_${side}_has_nonzero_width() {
4027             style.set_border_${side}_width(NonNegativeLength::zero());
4028         }
4029     % endfor
4030 }
4031 
4032 /// An identifier for a given alias property.
4033 #[derive(Clone, Copy, Eq, PartialEq, MallocSizeOf)]
4034 #[repr(u16)]
4035 pub enum AliasId {
4036     % for i, property in enumerate(data.all_aliases()):
4037         /// ${property.name}
4038         ${property.camel_case} = ${i},
4039     % endfor
4040 }
4041 
4042 #[derive(Clone, Copy, Eq, PartialEq)]
4043 enum AliasedPropertyId {
4044     #[allow(dead_code)] // Servo doesn't have aliased shorthands.
4045     Shorthand(ShorthandId),
4046     Longhand(LonghandId),
4047 }
4048 
4049 impl fmt::Debug for AliasId {
4050     fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
4051         let name = NonCustomPropertyId::from(*self).name();
4052         formatter.write_str(name)
4053     }
4054 }
4055 
4056 impl AliasId {
4057     /// Returns the property we're aliasing, as a longhand or a shorthand.
4058     #[inline]
4059     fn aliased_property(self) -> AliasedPropertyId {
4060         static MAP: [AliasedPropertyId; ${len(data.all_aliases())}] = [
4061         % for alias in data.all_aliases():
4062             % if alias.original.type() == "longhand":
4063             AliasedPropertyId::Longhand(LonghandId::${alias.original.camel_case}),
4064             % else:
4065             <% assert alias.original.type() == "shorthand" %>
4066             AliasedPropertyId::Shorthand(ShorthandId::${alias.original.camel_case}),
4067             % endif
4068         % endfor
4069         ];
4070         MAP[self as usize]
4071     }
4072 }
4073 
4074 /// Call the given macro with tokens like this for each longhand and shorthand properties
4075 /// that is enabled in content:
4076 ///
4077 /// ```
4078 /// [CamelCaseName, SetCamelCaseName, PropertyId::Longhand(LonghandId::CamelCaseName)],
4079 /// ```
4080 ///
4081 /// NOTE(emilio): Callers are responsible to deal with prefs.
4082 #[macro_export]
4083 macro_rules! css_properties_accessors {
4084     ($macro_name: ident) => {
4085         $macro_name! {
4086             % for kind, props in [("Longhand", data.longhands), ("Shorthand", data.shorthands)]:
4087                 % for property in props:
4088                     % if property.enabled_in_content():
4089                         % for prop in [property] + property.aliases:
4090                             % if '-' in prop.name:
4091                                 [${prop.ident.capitalize()}, Set${prop.ident.capitalize()},
4092                                  PropertyId::${kind}(${kind}Id::${property.camel_case})],
4093                             % endif
4094                             [${prop.camel_case}, Set${prop.camel_case},
4095                              PropertyId::${kind}(${kind}Id::${property.camel_case})],
4096                         % endfor
4097                     % endif
4098                 % endfor
4099             % endfor
4100         }
4101     }
4102 }
4103 
4104 /// Call the given macro with tokens like this for each longhand properties:
4105 ///
4106 /// ```
4107 /// { snake_case_ident, true }
4108 /// ```
4109 ///
4110 /// … where the boolean indicates whether the property value type
4111 /// is wrapped in a `Box<_>` in the corresponding `PropertyDeclaration` variant.
4112 #[macro_export]
4113 macro_rules! longhand_properties_idents {
4114     ($macro_name: ident) => {
4115         $macro_name! {
4116             % for property in data.longhands:
4117                 { ${property.ident}, ${"true" if property.boxed else "false"} }
4118             % endfor
4119         }
4120     }
4121 }
4122 
4123 % if engine in ["servo-2013", "servo-2020"]:
4124 % for effect_name in ["repaint", "reflow_out_of_flow", "reflow", "rebuild_and_reflow_inline", "rebuild_and_reflow"]:
4125     macro_rules! restyle_damage_${effect_name} {
4126         ($old: ident, $new: ident, $damage: ident, [ $($effect:expr),* ]) => ({
4127             if
4128                 % for style_struct in data.active_style_structs():
4129                     % for longhand in style_struct.longhands:
4130                         % if effect_name in longhand.servo_restyle_damage.split() and not longhand.logical:
4131                             $old.get_${style_struct.name_lower}().${longhand.ident} !=
4132                             $new.get_${style_struct.name_lower}().${longhand.ident} ||
4133                         % endif
4134                     % endfor
4135                 % endfor
4136 
4137                 false {
4138                     $damage.insert($($effect)|*);
4139                     true
4140             } else {
4141                 false
4142             }
4143         })
4144     }
4145 % endfor
4146 % endif
4147