tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

properties.mako.rs (112952B)


      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 <%namespace name="helpers" file="/helpers.mako.rs" />
      8 
      9 use servo_arc::{Arc, UniqueArc};
     10 use std::{ops, ptr};
     11 use std::{fmt, mem};
     12 
     13 #[cfg(feature = "servo")] use euclid::SideOffsets2D;
     14 #[cfg(feature = "gecko")] use crate::gecko_bindings::structs::{self, NonCustomCSSPropertyId};
     15 #[cfg(feature = "servo")] use crate::logical_geometry::LogicalMargin;
     16 #[cfg(feature = "servo")] use crate::computed_values;
     17 use crate::logical_geometry::WritingMode;
     18 use malloc_size_of::{MallocSizeOf, MallocSizeOfOps};
     19 use crate::computed_value_flags::*;
     20 use cssparser::Parser;
     21 use crate::media_queries::Device;
     22 use crate::parser::ParserContext;
     23 use crate::selector_parser::PseudoElement;
     24 use crate::stylist::Stylist;
     25 use style_traits::{CssStringWriter, CssWriter, KeywordsCollectFn, ParseError, SpecifiedValueInfo, StyleParseErrorKind, ToCss, TypedValue, ToTyped};
     26 use crate::derives::*;
     27 use crate::stylesheets::{CssRuleType, CssRuleTypes, Origin};
     28 use crate::logical_geometry::{LogicalAxis, LogicalCorner, LogicalSide};
     29 use crate::use_counters::UseCounters;
     30 use crate::rule_tree::StrongRuleNode;
     31 use crate::values::{
     32    computed,
     33    resolved,
     34    specified::{font::SystemFont, length::LineHeightBase, color::ColorSchemeFlags},
     35 };
     36 use std::cell::Cell;
     37 use super::{
     38    PropertyDeclarationId, PropertyId, NonCustomPropertyId,
     39    NonCustomPropertyIdSet, PropertyFlags, SourcePropertyDeclaration,
     40    LonghandIdSet, VariableDeclaration, CustomDeclaration,
     41    WideKeywordDeclaration, NonCustomPropertyIterator,
     42 };
     43 use debug_unreachable::debug_unreachable;
     44 
     45 <%!
     46    from collections import defaultdict
     47    from data import Method, PropertyRestrictions, Keyword, to_rust_ident, \
     48                     to_camel_case, RULE_VALUES, SYSTEM_FONT_LONGHANDS, PRIORITARY_PROPERTIES
     49    import os.path
     50 %>
     51 
     52 /// Conversion with fewer impls than From/Into
     53 pub trait MaybeBoxed<Out> {
     54    /// Convert
     55    fn maybe_boxed(self) -> Out;
     56 }
     57 
     58 impl<T> MaybeBoxed<T> for T {
     59    #[inline]
     60    fn maybe_boxed(self) -> T { self }
     61 }
     62 
     63 impl<T> MaybeBoxed<Box<T>> for T {
     64    #[inline]
     65    fn maybe_boxed(self) -> Box<T> { Box::new(self) }
     66 }
     67 
     68 macro_rules! expanded {
     69    ( $( $name: ident: $value: expr ),+ ) => {
     70        expanded!( $( $name: $value, )+ )
     71    };
     72    ( $( $name: ident: $value: expr, )+ ) => {
     73        Longhands {
     74            $(
     75                $name: MaybeBoxed::maybe_boxed($value),
     76            )+
     77        }
     78    }
     79 }
     80 
     81 /// A module with all the code for longhand properties.
     82 #[allow(missing_docs)]
     83 pub mod longhands {
     84    % for style_struct in data.style_structs:
     85    <% data.current_style_struct = style_struct %>
     86    <%include file="/longhands/${style_struct.name_lower}.mako.rs" />
     87    % endfor
     88 }
     89 
     90 
     91 % if engine == "gecko":
     92 #[allow(unsafe_code, missing_docs)]
     93 pub mod gecko {
     94    <%include file="/gecko.mako.rs" />
     95 }
     96 % endif
     97 
     98 
     99 macro_rules! unwrap_or_initial {
    100    ($prop: ident) => (unwrap_or_initial!($prop, $prop));
    101    ($prop: ident, $expr: expr) =>
    102        ($expr.unwrap_or_else(|| $prop::get_initial_specified_value()));
    103 }
    104 
    105 /// A module with code for all the shorthand css properties, and a few
    106 /// serialization helpers.
    107 #[allow(missing_docs)]
    108 pub mod shorthands {
    109    use cssparser::Parser;
    110    use crate::parser::{Parse, ParserContext};
    111    use style_traits::{ParseError, StyleParseErrorKind};
    112    use crate::values::specified;
    113 
    114    % for style_struct in data.style_structs:
    115    <%include file="/shorthands/${style_struct.name_lower}.mako.rs" />
    116    % endfor
    117 
    118    // We didn't define the 'all' shorthand using the regular helpers:shorthand
    119    // mechanism, since it causes some very large types to be generated.
    120    //
    121    // Also, make sure logical properties appear before its physical
    122    // counter-parts, in order to prevent bugs like:
    123    //
    124    //   https://bugzilla.mozilla.org/show_bug.cgi?id=1410028
    125    //
    126    // FIXME(emilio): Adopt the resolution from:
    127    //
    128    //   https://github.com/w3c/csswg-drafts/issues/1898
    129    //
    130    // when there is one, whatever that is.
    131    <%
    132        logical_longhands = []
    133        other_longhands = []
    134 
    135        for p in data.longhands:
    136            if p.name in ['direction', 'unicode-bidi']:
    137                continue;
    138            if not p.enabled_in_content() and not p.experimental(engine):
    139                continue;
    140            if "Style" not in p.rule_types_allowed_names():
    141                continue;
    142            if p.logical:
    143                logical_longhands.append(p.name)
    144            else:
    145                other_longhands.append(p.name)
    146 
    147        data.declare_shorthand(
    148            "all",
    149            logical_longhands + other_longhands,
    150            engines="gecko servo",
    151            spec="https://drafts.csswg.org/css-cascade-3/#all-shorthand"
    152        )
    153        ALL_SHORTHAND_LEN = len(logical_longhands) + len(other_longhands);
    154    %>
    155 }
    156 
    157 <%
    158    from itertools import groupby
    159 
    160    # After this code, `data.longhands` is sorted in the following order:
    161    # - first all keyword variants and all variants known to be Copy,
    162    # - second all the other variants, such as all variants with the same field
    163    #   have consecutive discriminants.
    164    # The variable `variants` contain the same entries as `data.longhands` in
    165    # the same order, but must exist separately to the data source, because
    166    # we then need to add three additional variants `WideKeywordDeclaration`,
    167    # `VariableDeclaration` and `CustomDeclaration`.
    168 
    169    variants = []
    170    for property in data.longhands:
    171        variants.append({
    172            "name": property.camel_case,
    173            "type": property.specified_type(),
    174            "doc": "`" + property.name + "`",
    175            "copy": property.specified_is_copy(),
    176        })
    177 
    178    groups = {}
    179    keyfunc = lambda x: x["type"]
    180    sortkeys = {}
    181    for ty, group in groupby(sorted(variants, key=keyfunc), keyfunc):
    182        group = list(group)
    183        groups[ty] = group
    184        for v in group:
    185            if len(group) == 1:
    186                sortkeys[v["name"]] = (not v["copy"], 1, v["name"], "")
    187            else:
    188                sortkeys[v["name"]] = (not v["copy"], len(group), ty, v["name"])
    189    variants.sort(key=lambda x: sortkeys[x["name"]])
    190 
    191    # It is extremely important to sort the `data.longhands` array here so
    192    # that it is in the same order as `variants`, for `LonghandId` and
    193    # `PropertyDeclarationId` to coincide.
    194    data.longhands.sort(key=lambda x: sortkeys[x.camel_case])
    195 %>
    196 
    197 // WARNING: It is *really* important for the variants of `LonghandId`
    198 // and `PropertyDeclaration` to be defined in the exact same order,
    199 // with the exception of `CSSWideKeyword`, `WithVariables` and `Custom`,
    200 // which don't exist in `LonghandId`.
    201 
    202 <%
    203    extra_variants = [
    204        {
    205            "name": "CSSWideKeyword",
    206            "type": "WideKeywordDeclaration",
    207            "doc": "A CSS-wide keyword.",
    208            "copy": False,
    209        },
    210        {
    211            "name": "WithVariables",
    212            "type": "VariableDeclaration",
    213            "doc": "An unparsed declaration.",
    214            "copy": False,
    215        },
    216        {
    217            "name": "Custom",
    218            "type": "CustomDeclaration",
    219            "doc": "A custom property declaration.",
    220            "copy": False,
    221        },
    222    ]
    223    for v in extra_variants:
    224        variants.append(v)
    225        groups[v["type"]] = [v]
    226 %>
    227 
    228 /// Servo's representation for a property declaration.
    229 #[derive(ToShmem)]
    230 #[repr(u16)]
    231 pub enum PropertyDeclaration {
    232    % for variant in variants:
    233    /// ${variant["doc"]}
    234    ${variant["name"]}(${variant["type"]}),
    235    % endfor
    236 }
    237 
    238 // There's one of these for each parsed declaration so it better be small.
    239 size_of_test!(PropertyDeclaration, 32);
    240 
    241 #[repr(C)]
    242 struct PropertyDeclarationVariantRepr<T> {
    243    tag: u16,
    244    value: T
    245 }
    246 
    247 impl Clone for PropertyDeclaration {
    248    #[inline]
    249    fn clone(&self) -> Self {
    250        use self::PropertyDeclaration::*;
    251 
    252        <%
    253            [copy, others] = [list(g) for _, g in groupby(variants, key=lambda x: not x["copy"])]
    254        %>
    255 
    256        let self_tag = unsafe {
    257            (*(self as *const _ as *const PropertyDeclarationVariantRepr<()>)).tag
    258        };
    259        if self_tag <= LonghandId::${copy[-1]["name"]} as u16 {
    260            #[derive(Clone, Copy)]
    261            #[repr(u16)]
    262            enum CopyVariants {
    263                % for v in copy:
    264                _${v["name"]}(${v["type"]}),
    265                % endfor
    266            }
    267 
    268            unsafe {
    269                let mut out = mem::MaybeUninit::uninit();
    270                ptr::write(
    271                    out.as_mut_ptr() as *mut CopyVariants,
    272                    *(self as *const _ as *const CopyVariants),
    273                );
    274                return out.assume_init();
    275            }
    276        }
    277 
    278        // This function ensures that all properties not handled above
    279        // do not have a specified value implements Copy. If you hit
    280        // compile error here, you may want to add the type name into
    281        // Longhand.specified_is_copy in data.py.
    282        fn _static_assert_others_are_not_copy() {
    283            struct Helper<T>(T);
    284            trait AssertCopy { fn assert() {} }
    285            trait AssertNotCopy { fn assert() {} }
    286            impl<T: Copy> AssertCopy for Helper<T> {}
    287            % for ty in sorted(set(x["type"] for x in others)):
    288            impl AssertNotCopy for Helper<${ty}> {}
    289            Helper::<${ty}>::assert();
    290            % endfor
    291        }
    292 
    293        match *self {
    294            ${" |\n".join("{}(..)".format(v["name"]) for v in copy)} => {
    295                unsafe { debug_unreachable!() }
    296            }
    297            % for ty, vs in groupby(others, key=lambda x: x["type"]):
    298            <%
    299                vs = list(vs)
    300            %>
    301            % if len(vs) == 1:
    302            ${vs[0]["name"]}(ref value) => {
    303                ${vs[0]["name"]}(value.clone())
    304            }
    305            % else:
    306            ${" |\n".join("{}(ref value)".format(v["name"]) for v in vs)} => {
    307                unsafe {
    308                    let mut out = mem::MaybeUninit::uninit();
    309                    ptr::write(
    310                        out.as_mut_ptr() as *mut PropertyDeclarationVariantRepr<${ty}>,
    311                        PropertyDeclarationVariantRepr {
    312                            tag: *(self as *const _ as *const u16),
    313                            value: value.clone(),
    314                        },
    315                    );
    316                    out.assume_init()
    317                }
    318            }
    319            % endif
    320            % endfor
    321        }
    322    }
    323 }
    324 
    325 impl PartialEq for PropertyDeclaration {
    326    #[inline]
    327    fn eq(&self, other: &Self) -> bool {
    328        use self::PropertyDeclaration::*;
    329 
    330        unsafe {
    331            let this_repr =
    332                &*(self as *const _ as *const PropertyDeclarationVariantRepr<()>);
    333            let other_repr =
    334                &*(other as *const _ as *const PropertyDeclarationVariantRepr<()>);
    335            if this_repr.tag != other_repr.tag {
    336                return false;
    337            }
    338            match *self {
    339                % for ty, vs in groupby(variants, key=lambda x: x["type"]):
    340                ${" |\n".join("{}(ref this)".format(v["name"]) for v in vs)} => {
    341                    let other_repr =
    342                        &*(other as *const _ as *const PropertyDeclarationVariantRepr<${ty}>);
    343                    *this == other_repr.value
    344                }
    345                % endfor
    346            }
    347        }
    348    }
    349 }
    350 
    351 impl MallocSizeOf for PropertyDeclaration {
    352    #[inline]
    353    fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
    354        use self::PropertyDeclaration::*;
    355 
    356        match *self {
    357            % for ty, vs in groupby(variants, key=lambda x: x["type"]):
    358            ${" | ".join("{}(ref value)".format(v["name"]) for v in vs)} => {
    359                value.size_of(ops)
    360            }
    361            % endfor
    362        }
    363    }
    364 }
    365 
    366 
    367 impl PropertyDeclaration {
    368    /// Returns the given value for this declaration as a particular type.
    369    /// It's the caller's responsibility to guarantee that the longhand id has the right specified
    370    /// value representation.
    371    pub(crate) unsafe fn unchecked_value_as<T>(&self) -> &T {
    372        &(*(self as *const _ as *const PropertyDeclarationVariantRepr<T>)).value
    373    }
    374 
    375    /// Dumps the property declaration before crashing.
    376    #[cold]
    377    #[cfg(debug_assertions)]
    378    pub(crate) fn debug_crash(&self, reason: &str) {
    379        panic!("{}: {:?}", reason, self);
    380    }
    381    #[cfg(not(debug_assertions))]
    382    #[inline(always)]
    383    pub(crate) fn debug_crash(&self, _reason: &str) {}
    384 
    385    /// Returns whether this is a variant of the Longhand(Value) type, rather
    386    /// than one of the special variants in extra_variants.
    387    fn is_longhand_value(&self) -> bool {
    388        match *self {
    389            % for v in extra_variants:
    390            PropertyDeclaration::${v["name"]}(..) => false,
    391            % endfor
    392            _ => true,
    393        }
    394    }
    395 
    396    /// Like the method on ToCss, but without the type parameter to avoid
    397    /// accidentally monomorphizing this large function multiple times for
    398    /// different writers.
    399    pub fn to_css(&self, dest: &mut CssStringWriter) -> fmt::Result {
    400        use self::PropertyDeclaration::*;
    401 
    402        let mut dest = CssWriter::new(dest);
    403        match *self {
    404            % for ty, vs in groupby(variants, key=lambda x: x["type"]):
    405            ${" | ".join("{}(ref value)".format(v["name"]) for v in vs)} => {
    406                value.to_css(&mut dest)
    407            }
    408            % endfor
    409        }
    410    }
    411 
    412    /// Like the method on ToTyped.
    413    pub fn to_typed(&self) -> Option<TypedValue> {
    414        use self::PropertyDeclaration::*;
    415 
    416        match *self {
    417            % for ty, vs in groupby(variants, key=lambda x: x["type"]):
    418            ${" | ".join("{}(ref value)".format(v["name"]) for v in vs)} => {
    419                value.to_typed()
    420            }
    421            % endfor
    422        }
    423    }
    424 
    425    /// Returns the color value of a given property, for high-contrast-mode tweaks.
    426    pub(super) fn color_value(&self) -> Option<&crate::values::specified::Color> {
    427        ${static_longhand_id_set("COLOR_PROPERTIES", lambda p: p.predefined_type == "Color")}
    428        <%
    429            # sanity check
    430            assert data.longhands_by_name["background-color"].predefined_type == "Color"
    431 
    432            color_specified_type = data.longhands_by_name["background-color"].specified_type()
    433        %>
    434        let id = self.id().as_longhand()?;
    435        if !COLOR_PROPERTIES.contains(id) || !self.is_longhand_value() {
    436            return None;
    437        }
    438        let repr = self as *const _ as *const PropertyDeclarationVariantRepr<${color_specified_type}>;
    439        Some(unsafe { &(*repr).value })
    440    }
    441 }
    442 
    443 /// A module with all the code related to animated properties.
    444 ///
    445 /// This needs to be "included" by mako at least after all longhand modules,
    446 /// given they populate the global data.
    447 pub mod animated_properties {
    448    <%include file="/helpers/animated_properties.mako.rs" />
    449 }
    450 
    451 /// A module to group various interesting property counts.
    452 pub mod property_counts {
    453    /// The number of (non-alias) longhand properties.
    454    pub const LONGHANDS: usize = ${len(data.longhands)};
    455    /// The number of (non-alias) shorthand properties.
    456    pub const SHORTHANDS: usize = ${len(data.shorthands)};
    457    /// The number of aliases.
    458    pub const ALIASES: usize = ${len(data.all_aliases())};
    459    /// The number of counted unknown properties.
    460    pub const COUNTED_UNKNOWN: usize = ${len(data.counted_unknown_properties)};
    461    /// The number of (non-alias) longhands and shorthands.
    462    pub const LONGHANDS_AND_SHORTHANDS: usize = LONGHANDS + SHORTHANDS;
    463    /// The number of non-custom properties.
    464    pub const NON_CUSTOM: usize = LONGHANDS_AND_SHORTHANDS + ALIASES;
    465    /// The number of prioritary properties that we have.
    466    <% longhand_property_names = set(list(map(lambda p: p.name, data.longhands))) %>
    467    <% enabled_prioritary_properties = PRIORITARY_PROPERTIES.intersection(longhand_property_names) %>
    468    pub const PRIORITARY: usize = ${len(enabled_prioritary_properties)};
    469    /// The max number of longhands that a shorthand other than "all" expands to.
    470    pub const MAX_SHORTHAND_EXPANDED: usize =
    471        ${max(len(s.sub_properties) for s in data.shorthands_except_all())};
    472    /// The max amount of longhands that the `all` shorthand will ever contain.
    473    pub const ALL_SHORTHAND_EXPANDED: usize = ${ALL_SHORTHAND_LEN};
    474    /// The number of animatable properties.
    475    pub const ANIMATABLE: usize = ${sum(1 for prop in data.longhands if prop.animatable)};
    476 }
    477 
    478 % if engine == "gecko":
    479 #[allow(dead_code)]
    480 unsafe fn static_assert_noncustomcsspropertyid() {
    481    % for i, property in enumerate(data.longhands + data.shorthands + data.all_aliases()):
    482    std::mem::transmute::<[u8; ${i}], [u8; ${property.noncustomcsspropertyid()} as usize]>([0; ${i}]); // ${property.name}
    483    % endfor
    484 }
    485 % endif
    486 
    487 impl NonCustomPropertyId {
    488    /// Get the property name.
    489    #[inline]
    490    pub fn name(self) -> &'static str {
    491        static MAP: [&'static str; property_counts::NON_CUSTOM] = [
    492            % for property in data.longhands + data.shorthands + data.all_aliases():
    493            "${property.name}",
    494            % endfor
    495        ];
    496        MAP[self.0 as usize]
    497    }
    498 
    499    /// Returns whether this property is animatable.
    500    #[inline]
    501    pub fn is_animatable(self) -> bool {
    502        ${static_non_custom_property_id_set("ANIMATABLE", lambda p: p.animatable)}
    503        ANIMATABLE.contains(self)
    504    }
    505 
    506    /// Whether this property is enabled for all content right now.
    507    #[inline]
    508    pub(super) fn enabled_for_all_content(self) -> bool {
    509        ${static_non_custom_property_id_set(
    510            "EXPERIMENTAL",
    511            lambda p: p.experimental(engine)
    512        )}
    513 
    514        ${static_non_custom_property_id_set(
    515            "ALWAYS_ENABLED",
    516            lambda p: (not p.experimental(engine)) and p.enabled_in_content()
    517        )}
    518 
    519        let passes_pref_check = || {
    520            % if engine == "gecko":
    521                unsafe { structs::nsCSSProps_gPropertyEnabled[self.0 as usize] }
    522            % else:
    523                static PREF_NAME: [Option<&str>; ${
    524                    len(data.longhands) + len(data.shorthands) + len(data.all_aliases())
    525                }] = [
    526                    % for property in data.longhands + data.shorthands + data.all_aliases():
    527                        <%
    528                            pref = getattr(property, "servo_pref")
    529                        %>
    530                        % if pref:
    531                            Some("${pref}"),
    532                        % else:
    533                            None,
    534                        % endif
    535                    % endfor
    536                ];
    537                let pref = match PREF_NAME[self.0 as usize] {
    538                    None => return true,
    539                    Some(pref) => pref,
    540                };
    541 
    542                style_config::get_bool(pref)
    543            % endif
    544        };
    545 
    546        if ALWAYS_ENABLED.contains(self) {
    547            return true
    548        }
    549 
    550        if EXPERIMENTAL.contains(self) && passes_pref_check() {
    551            return true
    552        }
    553 
    554        false
    555    }
    556 
    557    /// Returns whether a given rule allows a given property.
    558    #[inline]
    559    pub fn allowed_in_rule(self, rule_types: CssRuleTypes) -> bool {
    560        debug_assert!(
    561            rule_types.contains(CssRuleType::Keyframe) ||
    562            rule_types.contains(CssRuleType::Page) ||
    563            rule_types.contains(CssRuleType::Style) ||
    564            rule_types.contains(CssRuleType::Scope) ||
    565            rule_types.contains(CssRuleType::PositionTry),
    566            "Given rule type does not allow declarations."
    567        );
    568 
    569        static MAP: [u32; property_counts::NON_CUSTOM] = [
    570            % for property in data.longhands + data.shorthands + data.all_aliases():
    571            % for name in RULE_VALUES:
    572            % if property.rule_types_allowed & RULE_VALUES[name] != 0:
    573            CssRuleType::${name}.bit() |
    574            % endif
    575            % endfor
    576            0,
    577            % endfor
    578        ];
    579        MAP[self.0 as usize] & rule_types.bits() != 0
    580    }
    581 
    582    pub(super) fn allowed_in(self, context: &ParserContext) -> bool {
    583        if !self.allowed_in_rule(context.rule_types()) {
    584            return false;
    585        }
    586 
    587        self.allowed_in_ignoring_rule_type(context)
    588    }
    589 
    590 
    591    pub(super) fn allowed_in_ignoring_rule_type(self, context: &ParserContext) -> bool {
    592        // The semantics of these are kinda hard to reason about, what follows
    593        // is a description of the different combinations that can happen with
    594        // these three sets.
    595        //
    596        // Experimental properties are generally controlled by prefs, but an
    597        // experimental property explicitly enabled in certain context (UA or
    598        // chrome sheets) is always usable in the context regardless of the
    599        // pref value.
    600        //
    601        // Non-experimental properties are either normal properties which are
    602        // usable everywhere, or internal-only properties which are only usable
    603        // in certain context they are explicitly enabled in.
    604        if self.enabled_for_all_content() {
    605            return true;
    606        }
    607 
    608        ${static_non_custom_property_id_set(
    609            "ENABLED_IN_UA_SHEETS",
    610            lambda p: p.explicitly_enabled_in_ua_sheets()
    611        )}
    612        ${static_non_custom_property_id_set(
    613            "ENABLED_IN_CHROME",
    614            lambda p: p.explicitly_enabled_in_chrome()
    615        )}
    616 
    617        if context.stylesheet_origin == Origin::UserAgent &&
    618            ENABLED_IN_UA_SHEETS.contains(self)
    619        {
    620            return true
    621        }
    622 
    623        if context.chrome_rules_enabled() && ENABLED_IN_CHROME.contains(self) {
    624            return true
    625        }
    626 
    627        false
    628    }
    629 
    630    /// The supported types of this property. The return value should be
    631    /// style_traits::CssType when it can become a bitflags type.
    632    pub(super) fn supported_types(&self) -> u8 {
    633        const SUPPORTED_TYPES: [u8; ${len(data.longhands) + len(data.shorthands)}] = [
    634            % for prop in data.longhands:
    635                <${prop.specified_type()} as SpecifiedValueInfo>::SUPPORTED_TYPES,
    636            % endfor
    637            % for prop in data.shorthands:
    638            % if prop.name == "all":
    639                0, // 'all' accepts no value other than CSS-wide keywords
    640            % else:
    641                <shorthands::${prop.ident}::Longhands as SpecifiedValueInfo>::SUPPORTED_TYPES,
    642            % endif
    643            % endfor
    644        ];
    645        SUPPORTED_TYPES[self.0 as usize]
    646    }
    647 
    648    /// See PropertyId::collect_property_completion_keywords.
    649    pub(super) fn collect_property_completion_keywords(&self, f: KeywordsCollectFn) {
    650        fn do_nothing(_: KeywordsCollectFn) {}
    651        const COLLECT_FUNCTIONS: [fn(KeywordsCollectFn);
    652                                  ${len(data.longhands) + len(data.shorthands)}] = [
    653            % for prop in data.longhands:
    654                <${prop.specified_type()} as SpecifiedValueInfo>::collect_completion_keywords,
    655            % endfor
    656            % for prop in data.shorthands:
    657            % if prop.name == "all":
    658                do_nothing, // 'all' accepts no value other than CSS-wide keywords
    659            % else:
    660                <shorthands::${prop.ident}::Longhands as SpecifiedValueInfo>::
    661                    collect_completion_keywords,
    662            % endif
    663            % endfor
    664        ];
    665        COLLECT_FUNCTIONS[self.0 as usize](f);
    666    }
    667 }
    668 
    669 <%def name="static_non_custom_property_id_set(name, is_member)">
    670 static ${name}: NonCustomPropertyIdSet = NonCustomPropertyIdSet {
    671    <%
    672        storage = [0] * int((len(data.longhands) + len(data.shorthands) + len(data.all_aliases()) - 1 + 32) / 32)
    673        for i, property in enumerate(data.longhands + data.shorthands + data.all_aliases()):
    674            if is_member(property):
    675                storage[int(i / 32)] |= 1 << (i % 32)
    676    %>
    677    storage: [${", ".join("0x%x" % word for word in storage)}]
    678 };
    679 </%def>
    680 
    681 <%def name="static_longhand_id_set(name, is_member)">
    682 static ${name}: LonghandIdSet = LonghandIdSet {
    683    <%
    684        storage = [0] * int((len(data.longhands) - 1 + 32) / 32)
    685        for i, property in enumerate(data.longhands):
    686            if is_member(property):
    687                storage[int(i / 32)] |= 1 << (i % 32)
    688    %>
    689    storage: [${", ".join("0x%x" % word for word in storage)}]
    690 };
    691 </%def>
    692 
    693 <%
    694    logical_groups = defaultdict(list)
    695    for prop in data.longhands:
    696        if prop.logical_group:
    697            logical_groups[prop.logical_group].append(prop)
    698 
    699    for group, props in logical_groups.items():
    700        logical_count = sum(1 for p in props if p.logical)
    701        if logical_count * 2 != len(props):
    702            raise RuntimeError("Logical group {} has ".format(group) +
    703                               "unbalanced logical / physical properties")
    704 
    705    FIRST_LINE_RESTRICTIONS = PropertyRestrictions.first_line(data)
    706    FIRST_LETTER_RESTRICTIONS = PropertyRestrictions.first_letter(data)
    707    MARKER_RESTRICTIONS = PropertyRestrictions.marker(data)
    708    PLACEHOLDER_RESTRICTIONS = PropertyRestrictions.placeholder(data)
    709    CUE_RESTRICTIONS = PropertyRestrictions.cue(data)
    710 
    711    def restriction_flags(property):
    712        name = property.name
    713        flags = []
    714        if name in FIRST_LINE_RESTRICTIONS:
    715            flags.append("APPLIES_TO_FIRST_LINE")
    716        if name in FIRST_LETTER_RESTRICTIONS:
    717            flags.append("APPLIES_TO_FIRST_LETTER")
    718        if name in PLACEHOLDER_RESTRICTIONS:
    719            flags.append("APPLIES_TO_PLACEHOLDER")
    720        if name in MARKER_RESTRICTIONS:
    721            flags.append("APPLIES_TO_MARKER")
    722        if name in CUE_RESTRICTIONS:
    723            flags.append("APPLIES_TO_CUE")
    724        return flags
    725 
    726 %>
    727 
    728 /// A group for properties which may override each other via logical resolution.
    729 #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
    730 #[repr(u8)]
    731 pub enum LogicalGroupId {
    732    % for i, group in enumerate(logical_groups.keys()):
    733    /// ${group}
    734    ${to_camel_case(group)} = ${i},
    735    % endfor
    736 }
    737 
    738 impl LogicalGroupId {
    739    /// Return the list of physical mapped properties for a given logical group.
    740    fn physical_properties(self) -> &'static [LonghandId] {
    741        static PROPS: [[LonghandId; 4]; ${len(logical_groups)}] = [
    742        % for group, props in logical_groups.items():
    743        [
    744            <% physical_props = [p for p in props if p.logical][0].all_physical_mapped_properties(data) %>
    745            % for phys in physical_props:
    746            LonghandId::${phys.camel_case},
    747            % endfor
    748            % for i in range(len(physical_props), 4):
    749            LonghandId::${physical_props[0].camel_case},
    750            % endfor
    751        ],
    752        % endfor
    753        ];
    754        &PROPS[self as usize]
    755    }
    756 }
    757 
    758 /// A set of logical groups.
    759 #[derive(Clone, Copy, Debug, Default, MallocSizeOf, PartialEq)]
    760 pub struct LogicalGroupSet {
    761    storage: [u32; (${len(logical_groups)} - 1 + 32) / 32]
    762 }
    763 
    764 impl LogicalGroupSet {
    765    /// Creates an empty `NonCustomPropertyIdSet`.
    766    pub fn new() -> Self {
    767        Self {
    768            storage: Default::default(),
    769        }
    770    }
    771 
    772    /// Return whether the given group is in the set
    773    #[inline]
    774    pub fn contains(&self, g: LogicalGroupId) -> bool {
    775        let bit = g as usize;
    776        (self.storage[bit / 32] & (1 << (bit % 32))) != 0
    777    }
    778 
    779    /// Insert a group the set.
    780    #[inline]
    781    pub fn insert(&mut self, g: LogicalGroupId) {
    782        let bit = g as usize;
    783        self.storage[bit / 32] |= 1 << (bit % 32);
    784    }
    785 }
    786 
    787 
    788 #[repr(u8)]
    789 #[derive(Copy, Clone, Debug)]
    790 pub(crate) enum PrioritaryPropertyId {
    791    % for p in data.longhands:
    792    % if p.is_prioritary():
    793    ${p.camel_case},
    794    % endif
    795    % endfor
    796 }
    797 
    798 impl PrioritaryPropertyId {
    799    #[inline]
    800    pub fn to_longhand(self) -> LonghandId {
    801        static PRIORITARY_TO_LONGHAND: [LonghandId; property_counts::PRIORITARY] = [
    802        % for p in data.longhands:
    803        % if p.is_prioritary():
    804            LonghandId::${p.camel_case},
    805        % endif
    806        % endfor
    807        ];
    808        PRIORITARY_TO_LONGHAND[self as usize]
    809    }
    810    #[inline]
    811    pub fn from_longhand(l: LonghandId) -> Option<Self> {
    812        static LONGHAND_TO_PRIORITARY: [Option<PrioritaryPropertyId>; ${len(data.longhands)}] = [
    813        % for p in data.longhands:
    814        % if p.is_prioritary():
    815            Some(PrioritaryPropertyId::${p.camel_case}),
    816        % else:
    817            None,
    818        % endif
    819        % endfor
    820        ];
    821        LONGHAND_TO_PRIORITARY[l as usize]
    822    }
    823 }
    824 
    825 impl LonghandIdSet {
    826    /// The set of non-inherited longhands.
    827    #[inline]
    828    pub(super) fn reset() -> &'static Self {
    829        ${static_longhand_id_set("RESET", lambda p: not p.style_struct.inherited)}
    830        &RESET
    831    }
    832 
    833    #[inline]
    834    pub(super) fn discrete_animatable() -> &'static Self {
    835        ${static_longhand_id_set("DISCRETE_ANIMATABLE", lambda p: p.animation_type == "discrete")}
    836        &DISCRETE_ANIMATABLE
    837    }
    838 
    839    #[inline]
    840    pub(super) fn logical() -> &'static Self {
    841        ${static_longhand_id_set("LOGICAL", lambda p: p.logical)}
    842        &LOGICAL
    843    }
    844 
    845    /// Returns the set of longhands that are ignored when document colors are
    846    /// disabled.
    847    #[inline]
    848    pub(super) fn ignored_when_colors_disabled() -> &'static Self {
    849        ${static_longhand_id_set(
    850            "IGNORED_WHEN_COLORS_DISABLED",
    851            lambda p: p.ignored_when_colors_disabled
    852        )}
    853        &IGNORED_WHEN_COLORS_DISABLED
    854    }
    855 
    856    /// Only a few properties are allowed to depend on the visited state of
    857    /// links. When cascading visited styles, we can save time by only
    858    /// processing these properties.
    859    pub(super) fn visited_dependent() -> &'static Self {
    860        ${static_longhand_id_set("VISITED_DEPENDENT", lambda p: p.is_visited_dependent())}
    861        debug_assert!(Self::late_group().contains_all(&VISITED_DEPENDENT));
    862        &VISITED_DEPENDENT
    863    }
    864 
    865    #[inline]
    866    pub(super) fn prioritary_properties() -> &'static Self {
    867        ${static_longhand_id_set("PRIORITARY_PROPERTIES", lambda p: p.is_prioritary())}
    868        &PRIORITARY_PROPERTIES
    869    }
    870 
    871    #[inline]
    872    pub(super) fn late_group_only_inherited() -> &'static Self {
    873        ${static_longhand_id_set("LATE_GROUP_ONLY_INHERITED", lambda p: p.style_struct.inherited and not p.is_prioritary())}
    874        &LATE_GROUP_ONLY_INHERITED
    875    }
    876 
    877    #[inline]
    878    pub(super) fn late_group() -> &'static Self {
    879        ${static_longhand_id_set("LATE_GROUP", lambda p: not p.is_prioritary())}
    880        &LATE_GROUP
    881    }
    882 
    883    /// Returns the set of properties that are declared as having no effect on
    884    /// Gecko <scrollbar> elements or their descendant scrollbar parts.
    885    #[cfg(debug_assertions)]
    886    #[cfg(feature = "gecko")]
    887    #[inline]
    888    pub fn has_no_effect_on_gecko_scrollbars() -> &'static Self {
    889        // data.py asserts that has_no_effect_on_gecko_scrollbars is True or
    890        // False for properties that are inherited and Gecko pref controlled,
    891        // and is None for all other properties.
    892        ${static_longhand_id_set(
    893            "HAS_NO_EFFECT_ON_SCROLLBARS",
    894            lambda p: p.has_effect_on_gecko_scrollbars is False
    895        )}
    896        &HAS_NO_EFFECT_ON_SCROLLBARS
    897    }
    898 
    899    /// Returns the set of margin properties, for the purposes of <h1> use counters / warnings.
    900    #[inline]
    901    pub fn margin_properties() -> &'static Self {
    902        ${static_longhand_id_set(
    903            "MARGIN_PROPERTIES",
    904            lambda p: p.logical_group == "margin"
    905        )}
    906        &MARGIN_PROPERTIES
    907    }
    908 
    909    /// Returns the set of border properties for the purpose of disabling native
    910    /// appearance.
    911    #[inline]
    912    pub fn border_background_properties() -> &'static Self {
    913        ${static_longhand_id_set(
    914            "BORDER_BACKGROUND_PROPERTIES",
    915            lambda p: (p.logical_group and p.logical_group.startswith("border")) or \
    916                        p in data.shorthands_by_name["border"].sub_properties or \
    917                        p in data.shorthands_by_name["background"].sub_properties and \
    918                        p.name not in ["background-blend-mode", "background-repeat"]
    919        )}
    920        &BORDER_BACKGROUND_PROPERTIES
    921    }
    922 
    923    /// Returns properties that are zoom dependent (basically, that contain lengths).
    924    #[inline]
    925    pub fn zoom_dependent() -> &'static Self {
    926        ${static_longhand_id_set("ZOOM_DEPENDENT", lambda p: p.is_zoom_dependent())}
    927        &ZOOM_DEPENDENT
    928    }
    929 
    930    /// Note that it's different from zoom_dependent(), as this only includes inherited, physical
    931    /// properties.
    932    #[inline]
    933    pub fn zoom_dependent_inherited_properties() -> &'static Self {
    934        ${static_longhand_id_set("ZOOM_DEPENDENT_INHERITED", lambda p: p.is_inherited_zoom_dependent_property())}
    935        &ZOOM_DEPENDENT_INHERITED
    936    }
    937 }
    938 
    939 /// An identifier for a given longhand property.
    940 #[derive(Clone, Copy, Eq, Hash, MallocSizeOf, PartialEq, ToComputedValue, ToResolvedValue, ToShmem)]
    941 #[repr(u16)]
    942 pub enum LonghandId {
    943    % for i, property in enumerate(data.longhands):
    944        /// ${property.name}
    945        ${property.camel_case} = ${i},
    946    % endfor
    947 }
    948 
    949 enum LogicalMappingKind {
    950    Side(LogicalSide),
    951    Corner(LogicalCorner),
    952    Axis(LogicalAxis),
    953 }
    954 
    955 struct LogicalMappingData {
    956    group: LogicalGroupId,
    957    kind: LogicalMappingKind,
    958 }
    959 
    960 impl LogicalMappingData {
    961    fn to_physical(&self, wm: WritingMode) -> LonghandId {
    962        let index = match self.kind {
    963            LogicalMappingKind::Side(s) => s.to_physical(wm) as usize,
    964            LogicalMappingKind::Corner(c) => c.to_physical(wm) as usize,
    965            LogicalMappingKind::Axis(a) => a.to_physical(wm) as usize,
    966        };
    967        self.group.physical_properties()[index]
    968    }
    969 }
    970 
    971 impl LonghandId {
    972    /// Returns an iterator over all the shorthands that include this longhand.
    973    pub fn shorthands(self) -> NonCustomPropertyIterator<ShorthandId> {
    974        // first generate longhand to shorthands lookup map
    975        //
    976        // NOTE(emilio): This currently doesn't exclude the "all" shorthand. It
    977        // could potentially do so, which would speed up serialization
    978        // algorithms and what not, I guess.
    979        <%
    980            from functools import cmp_to_key
    981            longhand_to_shorthand_map = {}
    982            num_sub_properties = {}
    983            for shorthand in data.shorthands:
    984                num_sub_properties[shorthand.camel_case] = len(shorthand.sub_properties)
    985                for sub_property in shorthand.sub_properties:
    986                    if sub_property.ident not in longhand_to_shorthand_map:
    987                        longhand_to_shorthand_map[sub_property.ident] = []
    988 
    989                    longhand_to_shorthand_map[sub_property.ident].append(shorthand.camel_case)
    990 
    991            def cmp(a, b):
    992                return (a > b) - (a < b)
    993 
    994            def preferred_order(x, y):
    995                # Since we want properties in order from most subproperties to least,
    996                # reverse the arguments to cmp from the expected order.
    997                result = cmp(num_sub_properties.get(y, 0), num_sub_properties.get(x, 0))
    998                if result:
    999                    return result
   1000                # Fall back to lexicographic comparison.
   1001                return cmp(x, y)
   1002 
   1003            # Sort the lists of shorthand properties according to preferred order:
   1004            # https://drafts.csswg.org/cssom/#concept-shorthands-preferred-order
   1005            for shorthand_list in longhand_to_shorthand_map.values():
   1006                shorthand_list.sort(key=cmp_to_key(preferred_order))
   1007        %>
   1008 
   1009        // based on lookup results for each longhand, create result arrays
   1010        static MAP: [&'static [ShorthandId]; property_counts::LONGHANDS] = [
   1011        % for property in data.longhands:
   1012            &[
   1013                % for shorthand in longhand_to_shorthand_map.get(property.ident, []):
   1014                    ShorthandId::${shorthand},
   1015                % endfor
   1016            ],
   1017        % endfor
   1018        ];
   1019 
   1020        NonCustomPropertyIterator {
   1021            filter: NonCustomPropertyId::from(self).enabled_for_all_content(),
   1022            iter: MAP[self as usize].iter(),
   1023        }
   1024    }
   1025 
   1026    pub(super) fn parse_value<'i, 't>(
   1027        self,
   1028        context: &ParserContext,
   1029        input: &mut Parser<'i, 't>,
   1030    ) -> Result<PropertyDeclaration, ParseError<'i>> {
   1031        type ParsePropertyFn = for<'i, 't> fn(
   1032            context: &ParserContext,
   1033            input: &mut Parser<'i, 't>,
   1034        ) -> Result<PropertyDeclaration, ParseError<'i>>;
   1035        static PARSE_PROPERTY: [ParsePropertyFn; ${len(data.longhands)}] = [
   1036        % for property in data.longhands:
   1037            longhands::${property.ident}::parse_declared,
   1038        % endfor
   1039        ];
   1040        (PARSE_PROPERTY[self as usize])(context, input)
   1041    }
   1042 
   1043    /// Return the relevant data to map a particular logical property into physical.
   1044    fn logical_mapping_data(self) -> Option<&'static LogicalMappingData> {
   1045        const LOGICAL_MAPPING_DATA: [Option<LogicalMappingData>; ${len(data.longhands)}] = [
   1046            % for prop in data.longhands:
   1047            % if prop.logical:
   1048            Some(LogicalMappingData {
   1049                group: LogicalGroupId::${to_camel_case(prop.logical_group)},
   1050                kind: ${prop.logical_mapping_kind(data)}
   1051            }),
   1052            % else:
   1053            None,
   1054            % endif
   1055            % endfor
   1056        ];
   1057        LOGICAL_MAPPING_DATA[self as usize].as_ref()
   1058    }
   1059 
   1060    /// If this is a logical property, return the corresponding physical one in the given
   1061    /// writing mode. Otherwise, return unchanged.
   1062    #[inline]
   1063    pub fn to_physical(self, wm: WritingMode) -> Self {
   1064        let Some(data) = self.logical_mapping_data() else { return self };
   1065        data.to_physical(wm)
   1066    }
   1067 
   1068    /// Return the logical group of this longhand property.
   1069    pub fn logical_group(self) -> Option<LogicalGroupId> {
   1070        const LOGICAL_GROUP_IDS: [Option<LogicalGroupId>; ${len(data.longhands)}] = [
   1071            % for prop in data.longhands:
   1072            % if prop.logical_group:
   1073            Some(LogicalGroupId::${to_camel_case(prop.logical_group)}),
   1074            % else:
   1075            None,
   1076            % endif
   1077            % endfor
   1078        ];
   1079        LOGICAL_GROUP_IDS[self as usize]
   1080    }
   1081 
   1082    /// Returns PropertyFlags for given longhand property.
   1083    #[inline(always)]
   1084    pub fn flags(self) -> PropertyFlags {
   1085        const FLAGS: [PropertyFlags; ${len(data.longhands)}] = [
   1086            % for property in data.longhands:
   1087                PropertyFlags::empty()
   1088                % for flag in property.flags + restriction_flags(property):
   1089                    .union(PropertyFlags::${flag})
   1090                % endfor
   1091                ,
   1092            % endfor
   1093        ];
   1094        FLAGS[self as usize]
   1095    }
   1096 }
   1097 
   1098 /// An identifier for a given shorthand property.
   1099 #[derive(Clone, Copy, Debug, Eq, Hash, MallocSizeOf, PartialEq, ToComputedValue, ToResolvedValue, ToShmem)]
   1100 #[repr(u16)]
   1101 pub enum ShorthandId {
   1102    % for i, property in enumerate(data.shorthands):
   1103        /// ${property.name}
   1104        ${property.camel_case} = ${i},
   1105    % endfor
   1106 }
   1107 
   1108 impl ShorthandId {
   1109    /// Get the longhand ids that form this shorthand.
   1110    pub fn longhands(self) -> NonCustomPropertyIterator<LonghandId> {
   1111        static MAP: [&'static [LonghandId]; property_counts::SHORTHANDS] = [
   1112        % for property in data.shorthands:
   1113            &[
   1114                % for sub in property.sub_properties:
   1115                    LonghandId::${sub.camel_case},
   1116                % endfor
   1117            ],
   1118        % endfor
   1119        ];
   1120        NonCustomPropertyIterator {
   1121            filter: NonCustomPropertyId::from(self).enabled_for_all_content(),
   1122            iter: MAP[self as usize].iter(),
   1123        }
   1124    }
   1125 
   1126    /// Try to serialize the given declarations as this shorthand.
   1127    ///
   1128    /// Returns an error if writing to the stream fails, or if the declarations
   1129    /// do not map to a shorthand.
   1130    pub fn longhands_to_css(
   1131        self,
   1132        declarations: &[&PropertyDeclaration],
   1133        dest: &mut CssStringWriter,
   1134    ) -> fmt::Result {
   1135        type LonghandsToCssFn = for<'a, 'b> fn(&'a [&'b PropertyDeclaration], &mut CssStringWriter) -> fmt::Result;
   1136        fn all_to_css(_: &[&PropertyDeclaration], _: &mut CssStringWriter) -> fmt::Result {
   1137            // No need to try to serialize the declarations as the 'all'
   1138            // shorthand, since it only accepts CSS-wide keywords (and variable
   1139            // references), which will be handled in
   1140            // get_shorthand_appendable_value.
   1141            Ok(())
   1142        }
   1143 
   1144        static LONGHANDS_TO_CSS: [LonghandsToCssFn; ${len(data.shorthands)}] = [
   1145            % for shorthand in data.shorthands:
   1146            % if shorthand.ident == "all":
   1147                all_to_css,
   1148            % else:
   1149                shorthands::${shorthand.ident}::to_css,
   1150            % endif
   1151            % endfor
   1152        ];
   1153 
   1154        LONGHANDS_TO_CSS[self as usize](declarations, dest)
   1155    }
   1156 
   1157    /// Returns PropertyFlags for the given shorthand property.
   1158    #[inline]
   1159    pub fn flags(self) -> PropertyFlags {
   1160        const FLAGS: [u16; ${len(data.shorthands)}] = [
   1161            % for property in data.shorthands:
   1162                % for flag in property.flags:
   1163                    PropertyFlags::${flag}.bits() |
   1164                % endfor
   1165                0,
   1166            % endfor
   1167        ];
   1168        PropertyFlags::from_bits_retain(FLAGS[self as usize])
   1169    }
   1170 
   1171    /// Returns the order in which this property appears relative to other
   1172    /// shorthands in idl-name-sorting order.
   1173    #[inline]
   1174    pub fn idl_name_sort_order(self) -> u32 {
   1175        <%
   1176            from data import to_idl_name
   1177            ordered = {}
   1178            sorted_shorthands = sorted(data.shorthands, key=lambda p: to_idl_name(p.ident))
   1179            for order, shorthand in enumerate(sorted_shorthands):
   1180                ordered[shorthand.ident] = order
   1181        %>
   1182        static IDL_NAME_SORT_ORDER: [u32; ${len(data.shorthands)}] = [
   1183            % for property in data.shorthands:
   1184            ${ordered[property.ident]},
   1185            % endfor
   1186        ];
   1187        IDL_NAME_SORT_ORDER[self as usize]
   1188    }
   1189 
   1190    pub(super) fn parse_into<'i, 't>(
   1191        self,
   1192        declarations: &mut SourcePropertyDeclaration,
   1193        context: &ParserContext,
   1194        input: &mut Parser<'i, 't>,
   1195    ) -> Result<(), ParseError<'i>> {
   1196        type ParseIntoFn = for<'i, 't> fn(
   1197            declarations: &mut SourcePropertyDeclaration,
   1198            context: &ParserContext,
   1199            input: &mut Parser<'i, 't>,
   1200        ) -> Result<(), ParseError<'i>>;
   1201 
   1202        fn parse_all<'i, 't>(
   1203            _: &mut SourcePropertyDeclaration,
   1204            _: &ParserContext,
   1205            input: &mut Parser<'i, 't>
   1206        ) -> Result<(), ParseError<'i>> {
   1207            // 'all' accepts no value other than CSS-wide keywords
   1208            Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
   1209        }
   1210 
   1211        static PARSE_INTO: [ParseIntoFn; ${len(data.shorthands)}] = [
   1212            % for shorthand in data.shorthands:
   1213            % if shorthand.ident == "all":
   1214            parse_all,
   1215            % else:
   1216            shorthands::${shorthand.ident}::parse_into,
   1217            % endif
   1218            % endfor
   1219        ];
   1220 
   1221        (PARSE_INTO[self as usize])(declarations, context, input)
   1222    }
   1223 }
   1224 
   1225 /// The counted unknown property list which is used for css use counters.
   1226 ///
   1227 /// FIXME: This should be just #[repr(u8)], but can't be because of ABI issues,
   1228 /// see https://bugs.llvm.org/show_bug.cgi?id=44228.
   1229 #[derive(Clone, Copy, Debug, Eq, FromPrimitive, Hash, PartialEq)]
   1230 #[repr(u32)]
   1231 pub enum CountedUnknownProperty {
   1232    % for prop in data.counted_unknown_properties:
   1233    /// ${prop.name}
   1234    ${prop.camel_case},
   1235    % endfor
   1236 }
   1237 
   1238 impl CountedUnknownProperty {
   1239    /// Parse the counted unknown property, for testing purposes only.
   1240    pub fn parse_for_testing(property_name: &str) -> Option<Self> {
   1241        ::cssparser::ascii_case_insensitive_phf_map! {
   1242            unknown_ids -> CountedUnknownProperty = {
   1243                % for property in data.counted_unknown_properties:
   1244                "${property.name}" => CountedUnknownProperty::${property.camel_case},
   1245                % endfor
   1246            }
   1247        }
   1248        unknown_ids::get(property_name).cloned()
   1249    }
   1250 
   1251    /// Returns the underlying index, used for use counter.
   1252    #[inline]
   1253    pub fn bit(self) -> usize {
   1254        self as usize
   1255    }
   1256 }
   1257 
   1258 impl PropertyId {
   1259    /// Returns a given property from the given name, _regardless of whether it
   1260    /// is enabled or not_, or Err(()) for unknown properties.
   1261    pub fn parse_unchecked(
   1262        property_name: &str,
   1263        use_counters: Option<&UseCounters>,
   1264    ) -> Result<Self, ()> {
   1265        // A special id for css use counters. ShorthandAlias is not used in the Servo build.
   1266        // That's why we need to allow dead_code.
   1267        pub enum StaticId {
   1268            NonCustom(NonCustomPropertyId),
   1269            CountedUnknown(CountedUnknownProperty),
   1270        }
   1271        ::cssparser::ascii_case_insensitive_phf_map! {
   1272            static_ids -> StaticId = {
   1273                % for i, property in enumerate(data.longhands + data.shorthands + data.all_aliases()):
   1274                "${property.name}" => StaticId::NonCustom(NonCustomPropertyId(${i})),
   1275                % endfor
   1276                % for property in data.counted_unknown_properties:
   1277                "${property.name}" => {
   1278                    StaticId::CountedUnknown(CountedUnknownProperty::${property.camel_case})
   1279                },
   1280                % endfor
   1281            }
   1282        }
   1283 
   1284        if let Some(id) = static_ids::get(property_name) {
   1285            return Ok(match *id {
   1286                StaticId::NonCustom(id) => PropertyId::NonCustom(id),
   1287                StaticId::CountedUnknown(unknown_prop) => {
   1288                    if let Some(counters) = use_counters {
   1289                        counters.counted_unknown_properties.record(unknown_prop);
   1290                    }
   1291                    // Always return Err(()) because these aren't valid custom property names.
   1292                    return Err(());
   1293                }
   1294            });
   1295        }
   1296 
   1297        let name = crate::custom_properties::parse_name(property_name)?;
   1298        Ok(PropertyId::Custom(crate::custom_properties::Name::from(name)))
   1299    }
   1300 }
   1301 
   1302 impl PropertyDeclaration {
   1303    /// Given a property declaration, return the property declaration id.
   1304    #[inline]
   1305    pub fn id(&self) -> PropertyDeclarationId<'_> {
   1306        match *self {
   1307            PropertyDeclaration::Custom(ref declaration) => {
   1308                return PropertyDeclarationId::Custom(&declaration.name)
   1309            }
   1310            PropertyDeclaration::CSSWideKeyword(ref declaration) => {
   1311                return PropertyDeclarationId::Longhand(declaration.id);
   1312            }
   1313            PropertyDeclaration::WithVariables(ref declaration) => {
   1314                return PropertyDeclarationId::Longhand(declaration.id);
   1315            }
   1316            _ => {}
   1317        }
   1318        // This is just fine because PropertyDeclaration and LonghandId
   1319        // have corresponding discriminants.
   1320        let id = unsafe { *(self as *const _ as *const LonghandId) };
   1321        debug_assert_eq!(id, match *self {
   1322            % for property in data.longhands:
   1323            PropertyDeclaration::${property.camel_case}(..) => LonghandId::${property.camel_case},
   1324            % endfor
   1325            _ => id,
   1326        });
   1327        PropertyDeclarationId::Longhand(id)
   1328    }
   1329 
   1330    /// Given a declaration, convert it into a declaration for a corresponding
   1331    /// physical property.
   1332    #[inline]
   1333    pub fn to_physical(&self, wm: WritingMode) -> Self {
   1334        match *self {
   1335            PropertyDeclaration::WithVariables(VariableDeclaration {
   1336                id,
   1337                ref value,
   1338            }) => {
   1339                return PropertyDeclaration::WithVariables(VariableDeclaration {
   1340                    id: id.to_physical(wm),
   1341                    value: value.clone(),
   1342                })
   1343            }
   1344            PropertyDeclaration::CSSWideKeyword(WideKeywordDeclaration {
   1345                id,
   1346                keyword,
   1347            }) => {
   1348                return PropertyDeclaration::CSSWideKeyword(WideKeywordDeclaration {
   1349                    id: id.to_physical(wm),
   1350                    keyword,
   1351                })
   1352            }
   1353            PropertyDeclaration::Custom(..) => return self.clone(),
   1354            % for prop in data.longhands:
   1355            PropertyDeclaration::${prop.camel_case}(..) => {},
   1356            % endfor
   1357        }
   1358 
   1359        let mut ret = self.clone();
   1360 
   1361        % for prop in data.longhands:
   1362        % for physical_property in prop.all_physical_mapped_properties(data):
   1363        % if physical_property.specified_type() != prop.specified_type():
   1364            <% raise "Logical property %s should share specified value with physical property %s" % \
   1365                     (prop.name, physical_property.name) %>
   1366        % endif
   1367        % endfor
   1368        % endfor
   1369 
   1370        unsafe {
   1371            let longhand_id = *(&mut ret as *mut _ as *mut LonghandId);
   1372 
   1373            debug_assert_eq!(
   1374                PropertyDeclarationId::Longhand(longhand_id),
   1375                ret.id()
   1376            );
   1377 
   1378            // This is just fine because PropertyDeclaration and LonghandId
   1379            // have corresponding discriminants.
   1380            *(&mut ret as *mut _ as *mut LonghandId) = longhand_id.to_physical(wm);
   1381 
   1382            debug_assert_eq!(
   1383                PropertyDeclarationId::Longhand(longhand_id.to_physical(wm)),
   1384                ret.id()
   1385            );
   1386        }
   1387 
   1388        ret
   1389    }
   1390 
   1391    /// Returns whether or not the property is set by a system font
   1392    pub fn get_system(&self) -> Option<SystemFont> {
   1393        match *self {
   1394            % if engine == "gecko":
   1395            % for prop in SYSTEM_FONT_LONGHANDS:
   1396                PropertyDeclaration::${to_camel_case(prop)}(ref prop) => {
   1397                    prop.get_system()
   1398                }
   1399            % endfor
   1400            % endif
   1401            _ => None,
   1402        }
   1403    }
   1404 }
   1405 
   1406 #[cfg(feature = "gecko")]
   1407 pub use super::gecko::style_structs;
   1408 
   1409 /// The module where all the style structs are defined.
   1410 #[cfg(feature = "servo")]
   1411 pub mod style_structs {
   1412    use rustc_hash::FxHasher;
   1413    use super::longhands;
   1414    use std::hash::{Hash, Hasher};
   1415    use crate::values::specified::color::ColorSchemeFlags;
   1416    use crate::derives::*;
   1417 
   1418    % for style_struct in data.active_style_structs():
   1419        % if style_struct.name == "Font":
   1420        #[derive(Clone, Debug, MallocSizeOf)]
   1421        #[cfg_attr(feature = "servo", derive(Serialize, Deserialize))]
   1422        % else:
   1423        #[derive(Clone, Debug, MallocSizeOf, PartialEq)]
   1424        % endif
   1425        /// The ${style_struct.name} style struct.
   1426        pub struct ${style_struct.name} {
   1427            % for longhand in style_struct.longhands:
   1428                % if not longhand.logical:
   1429                    /// The ${longhand.name} computed value.
   1430                    pub ${longhand.ident}: longhands::${longhand.ident}::computed_value::T,
   1431                % endif
   1432            % endfor
   1433            % if style_struct.name == "Font":
   1434                /// The font hash, used for font caching.
   1435                pub hash: u64,
   1436            % endif
   1437            % if style_struct.name == "Box":
   1438                /// The display value specified by the CSS stylesheets (without any style adjustments),
   1439                /// which is needed for hypothetical layout boxes.
   1440                pub original_display: longhands::display::computed_value::T,
   1441            % endif
   1442        }
   1443        % if style_struct.name == "Font":
   1444        impl PartialEq for Font {
   1445            fn eq(&self, other: &Font) -> bool {
   1446                self.hash == other.hash
   1447                % for longhand in style_struct.longhands:
   1448                    && self.${longhand.ident} == other.${longhand.ident}
   1449                % endfor
   1450            }
   1451        }
   1452        % endif
   1453 
   1454        impl ${style_struct.name} {
   1455            % for longhand in style_struct.longhands:
   1456                % if not longhand.logical:
   1457                    % if longhand.ident == "display":
   1458                        /// Set `display`.
   1459                        ///
   1460                        /// We need to keep track of the original display for hypothetical boxes,
   1461                        /// so we need to special-case this.
   1462                        #[allow(non_snake_case)]
   1463                        #[inline]
   1464                        pub fn set_display(&mut self, v: longhands::display::computed_value::T) {
   1465                            self.display = v;
   1466                            self.original_display = v;
   1467                        }
   1468                    % else:
   1469                        /// Set ${longhand.name}.
   1470                        #[allow(non_snake_case)]
   1471                        #[inline]
   1472                        pub fn set_${longhand.ident}(&mut self, v: longhands::${longhand.ident}::computed_value::T) {
   1473                            self.${longhand.ident} = v;
   1474                        }
   1475                    % endif
   1476                    % if longhand.ident == "display":
   1477                        /// Set `display` from other struct.
   1478                        ///
   1479                        /// Same as `set_display` above.
   1480                        /// Thus, we need to special-case this.
   1481                        #[allow(non_snake_case)]
   1482                        #[inline]
   1483                        pub fn copy_display_from(&mut self, other: &Self) {
   1484                            self.display = other.display.clone();
   1485                            self.original_display = other.display.clone();
   1486                        }
   1487                    % else:
   1488                        /// Set ${longhand.name} from other struct.
   1489                        #[allow(non_snake_case)]
   1490                        #[inline]
   1491                        pub fn copy_${longhand.ident}_from(&mut self, other: &Self) {
   1492                            self.${longhand.ident} = other.${longhand.ident}.clone();
   1493                        }
   1494                    % endif
   1495                    /// Reset ${longhand.name} from the initial struct.
   1496                    #[allow(non_snake_case)]
   1497                    #[inline]
   1498                    pub fn reset_${longhand.ident}(&mut self, other: &Self) {
   1499                        self.copy_${longhand.ident}_from(other)
   1500                    }
   1501 
   1502                    /// Get the computed value for ${longhand.name}.
   1503                    #[allow(non_snake_case)]
   1504                    #[inline]
   1505                    pub fn clone_${longhand.ident}(&self) -> longhands::${longhand.ident}::computed_value::T {
   1506                        self.${longhand.ident}.clone()
   1507                    }
   1508                % endif
   1509                % if longhand.need_index:
   1510                    /// If this longhand is indexed, get the number of elements.
   1511                    #[allow(non_snake_case)]
   1512                    pub fn ${longhand.ident}_count(&self) -> usize {
   1513                        self.${longhand.ident}.0.len()
   1514                    }
   1515 
   1516                    /// If this longhand is indexed, get the element at given
   1517                    /// index.
   1518                    #[allow(non_snake_case)]
   1519                    pub fn ${longhand.ident}_at(&self, index: usize)
   1520                        -> longhands::${longhand.ident}::computed_value::SingleComputedValue {
   1521                        self.${longhand.ident}.0[index].clone()
   1522                    }
   1523                % endif
   1524            % endfor
   1525            % if style_struct.name == "Border":
   1526                % for side in ["top", "right", "bottom", "left"]:
   1527                    /// Whether the border-${side} property has nonzero width.
   1528                    #[allow(non_snake_case)]
   1529                    pub fn border_${side}_has_nonzero_width(&self) -> bool {
   1530                        use crate::Zero;
   1531                        !self.border_${side}_width.is_zero()
   1532                    }
   1533                % endfor
   1534            % elif style_struct.name == "Font":
   1535                /// Computes a font hash in order to be able to cache fonts
   1536                /// effectively in GFX and layout.
   1537                pub fn compute_font_hash(&mut self) {
   1538                    let mut hasher: FxHasher = Default::default();
   1539                    self.font_weight.hash(&mut hasher);
   1540                    self.font_stretch.hash(&mut hasher);
   1541                    self.font_style.hash(&mut hasher);
   1542                    self.font_family.hash(&mut hasher);
   1543                    self.hash = hasher.finish()
   1544                }
   1545                /// Create a new Font with the initial values of all members.
   1546                pub fn initial_values() -> Self {
   1547                    Self {
   1548                        % for longhand in style_struct.longhands:
   1549                            % if not longhand.logical:
   1550                                ${longhand.ident}: longhands::${longhand.ident}::get_initial_value(),
   1551                            % endif
   1552                        % endfor
   1553                        hash: 0,
   1554                    }
   1555                 }
   1556            % elif style_struct.name == "InheritedUI":
   1557                /// Returns the ColorSchemeFlags corresponding to the value of `color-scheme`.
   1558                #[inline]
   1559                pub fn color_scheme_bits(&self) -> ColorSchemeFlags {
   1560                    self.color_scheme.bits
   1561                }
   1562            % elif style_struct.name == "Outline":
   1563                /// Whether the outline-width property is non-zero.
   1564                #[inline]
   1565                pub fn outline_has_nonzero_width(&self) -> bool {
   1566                    use crate::Zero;
   1567                    !self.outline_width.is_zero()
   1568                }
   1569            % elif style_struct.name == "Box":
   1570                /// Sets the display property, but without touching original_display,
   1571                /// except when the adjustment comes from root or item display fixups.
   1572                pub fn set_adjusted_display(
   1573                    &mut self,
   1574                    dpy: longhands::display::computed_value::T,
   1575                    is_item_or_root: bool
   1576                ) {
   1577                    self.display = dpy;
   1578                    if is_item_or_root {
   1579                        self.original_display = dpy;
   1580                    }
   1581                }
   1582            % endif
   1583        }
   1584 
   1585    % endfor
   1586 }
   1587 
   1588 % for style_struct in data.active_style_structs():
   1589    impl style_structs::${style_struct.name} {
   1590        % for longhand in style_struct.longhands:
   1591            % if longhand.need_index:
   1592                /// Iterate over the values of ${longhand.name}.
   1593                #[allow(non_snake_case)]
   1594                #[inline]
   1595                pub fn ${longhand.ident}_iter(&self) -> ${longhand.camel_case}Iter<'_> {
   1596                    ${longhand.camel_case}Iter {
   1597                        style_struct: self,
   1598                        current: 0,
   1599                        max: self.${longhand.ident}_count(),
   1600                    }
   1601                }
   1602 
   1603                /// Get a value mod `index` for the property ${longhand.name}.
   1604                #[allow(non_snake_case)]
   1605                #[inline]
   1606                pub fn ${longhand.ident}_mod(&self, index: usize)
   1607                    -> longhands::${longhand.ident}::computed_value::SingleComputedValue {
   1608                    self.${longhand.ident}_at(index % self.${longhand.ident}_count())
   1609                }
   1610 
   1611                /// Clone the computed value for the property.
   1612                #[allow(non_snake_case)]
   1613                #[inline]
   1614                #[cfg(feature = "gecko")]
   1615                pub fn clone_${longhand.ident}(
   1616                    &self,
   1617                ) -> longhands::${longhand.ident}::computed_value::T {
   1618                    longhands::${longhand.ident}::computed_value::List(
   1619                        self.${longhand.ident}_iter().collect()
   1620                    )
   1621                }
   1622            % endif
   1623        % endfor
   1624 
   1625        % if style_struct.name == "UI":
   1626            /// Returns whether there is any animation specified with
   1627            /// animation-name other than `none`.
   1628            pub fn specifies_animations(&self) -> bool {
   1629                self.animation_name_iter().any(|name| !name.is_none())
   1630            }
   1631 
   1632            /// Returns whether there are any transitions specified.
   1633            #[cfg(feature = "servo")]
   1634            pub fn specifies_transitions(&self) -> bool {
   1635                (0..self.transition_property_count()).any(|index| {
   1636                    let combined_duration =
   1637                        self.transition_duration_mod(index).seconds().max(0.) +
   1638                        self.transition_delay_mod(index).seconds();
   1639                    combined_duration > 0.
   1640                })
   1641            }
   1642 
   1643            /// Returns whether animation-timeline is initial value. We need this information to
   1644            /// resolve animation-duration.
   1645            #[cfg(feature = "servo")]
   1646            pub fn has_initial_animation_timeline(&self) -> bool {
   1647                self.animation_timeline_count() == 1 && self.animation_timeline_at(0).is_auto()
   1648            }
   1649 
   1650            /// Returns whether there is any named progress timeline specified with
   1651            /// scroll-timeline-name other than `none`.
   1652            #[cfg(feature = "gecko")]
   1653            pub fn specifies_scroll_timelines(&self) -> bool {
   1654                self.scroll_timeline_name_iter().any(|name| !name.is_none())
   1655            }
   1656 
   1657            /// Returns whether there is any named progress timeline specified with
   1658            /// view-timeline-name other than `none`.
   1659            #[cfg(feature = "gecko")]
   1660            pub fn specifies_view_timelines(&self) -> bool {
   1661                self.view_timeline_name_iter().any(|name| !name.is_none())
   1662            }
   1663 
   1664            /// Returns true if animation properties are equal between styles, but without
   1665            /// considering keyframe data and animation-timeline.
   1666            #[cfg(feature = "servo")]
   1667            pub fn animations_equals(&self, other: &Self) -> bool {
   1668                self.animation_name_iter().eq(other.animation_name_iter()) &&
   1669                self.animation_delay_iter().eq(other.animation_delay_iter()) &&
   1670                self.animation_direction_iter().eq(other.animation_direction_iter()) &&
   1671                self.animation_duration_iter().eq(other.animation_duration_iter()) &&
   1672                self.animation_fill_mode_iter().eq(other.animation_fill_mode_iter()) &&
   1673                self.animation_iteration_count_iter().eq(other.animation_iteration_count_iter()) &&
   1674                self.animation_play_state_iter().eq(other.animation_play_state_iter()) &&
   1675                self.animation_timing_function_iter().eq(other.animation_timing_function_iter())
   1676            }
   1677 
   1678        % elif style_struct.name == "Column":
   1679            /// Whether this is a multicol style.
   1680            #[cfg(feature = "servo")]
   1681            pub fn is_multicol(&self) -> bool {
   1682                !self.column_width.is_auto() || !self.column_count.is_auto()
   1683            }
   1684        % endif
   1685    }
   1686 
   1687    % for longhand in style_struct.longhands:
   1688        % if longhand.need_index:
   1689            /// An iterator over the values of the ${longhand.name} properties.
   1690            pub struct ${longhand.camel_case}Iter<'a> {
   1691                style_struct: &'a style_structs::${style_struct.name},
   1692                current: usize,
   1693                max: usize,
   1694            }
   1695 
   1696            impl<'a> Iterator for ${longhand.camel_case}Iter<'a> {
   1697                type Item = longhands::${longhand.ident}::computed_value::SingleComputedValue;
   1698 
   1699                fn next(&mut self) -> Option<Self::Item> {
   1700                    self.current += 1;
   1701                    if self.current <= self.max {
   1702                        Some(self.style_struct.${longhand.ident}_at(self.current - 1))
   1703                    } else {
   1704                        None
   1705                    }
   1706                }
   1707            }
   1708        % endif
   1709    % endfor
   1710 % endfor
   1711 
   1712 
   1713 #[cfg(feature = "gecko")]
   1714 pub use super::gecko::{ComputedValues, ComputedValuesInner};
   1715 
   1716 #[cfg(feature = "servo")]
   1717 #[cfg_attr(feature = "servo", derive(Clone, Debug))]
   1718 /// Actual data of ComputedValues, to match up with Gecko
   1719 pub struct ComputedValuesInner {
   1720    % for style_struct in data.active_style_structs():
   1721        ${style_struct.ident}: Arc<style_structs::${style_struct.name}>,
   1722    % endfor
   1723    custom_properties: crate::custom_properties::ComputedCustomProperties,
   1724 
   1725    /// The effective zoom value.
   1726    pub effective_zoom: computed::Zoom,
   1727 
   1728    /// A set of flags we use to store misc information regarding this style.
   1729    pub flags: ComputedValueFlags,
   1730 
   1731    /// The writing mode of this computed values struct.
   1732    pub writing_mode: WritingMode,
   1733 
   1734    /// The rule node representing the ordered list of rules matched for this
   1735    /// node.  Can be None for default values and text nodes.  This is
   1736    /// essentially an optimization to avoid referencing the root rule node.
   1737    pub rules: Option<StrongRuleNode>,
   1738 
   1739    /// The element's computed values if visited, only computed if there's a
   1740    /// relevant link for this element. A element's "relevant link" is the
   1741    /// element being matched if it is a link or the nearest ancestor link.
   1742    visited_style: Option<Arc<ComputedValues>>,
   1743 }
   1744 
   1745 /// The struct that Servo uses to represent computed values.
   1746 ///
   1747 /// This struct contains an immutable atomically-reference-counted pointer to
   1748 /// every kind of style struct.
   1749 ///
   1750 /// When needed, the structs may be copied in order to get mutated.
   1751 #[cfg(feature = "servo")]
   1752 #[cfg_attr(feature = "servo", derive(Clone, Debug))]
   1753 pub struct ComputedValues {
   1754    /// The actual computed values
   1755    ///
   1756    /// In Gecko the outer ComputedValues is actually a ComputedStyle, whereas
   1757    /// ComputedValuesInner is the core set of computed values.
   1758    ///
   1759    /// We maintain this distinction in servo to reduce the amount of special
   1760    /// casing.
   1761    inner: ComputedValuesInner,
   1762 
   1763    /// The pseudo-element that we're using.
   1764    pseudo: Option<PseudoElement>,
   1765 }
   1766 
   1767 impl ComputedValues {
   1768    /// Returns the pseudo-element that this style represents.
   1769    #[cfg(feature = "servo")]
   1770    pub fn pseudo(&self) -> Option<&PseudoElement> {
   1771        self.pseudo.as_ref()
   1772    }
   1773 
   1774    /// Returns true if this is the style for a pseudo-element.
   1775    #[cfg(feature = "servo")]
   1776    pub fn is_pseudo_style(&self) -> bool {
   1777        self.pseudo().is_some()
   1778    }
   1779 
   1780    /// Returns whether this style's display value is equal to contents.
   1781    pub fn is_display_contents(&self) -> bool {
   1782        self.clone_display().is_contents()
   1783    }
   1784 
   1785    /// Gets a reference to the rule node. Panic if no rule node exists.
   1786    pub fn rules(&self) -> &StrongRuleNode {
   1787        self.rules.as_ref().unwrap()
   1788    }
   1789 
   1790    /// Returns the visited rules, if applicable.
   1791    pub fn visited_rules(&self) -> Option<&StrongRuleNode> {
   1792        self.visited_style().and_then(|s| s.rules.as_ref())
   1793    }
   1794 
   1795    /// Gets a reference to the custom properties map (if one exists).
   1796    pub fn custom_properties(&self) -> &crate::custom_properties::ComputedCustomProperties {
   1797        &self.custom_properties
   1798    }
   1799 
   1800    /// Returns whether we have the same custom properties as another style.
   1801    pub fn custom_properties_equal(&self, other: &Self) -> bool {
   1802      self.custom_properties() == other.custom_properties()
   1803    }
   1804 
   1805 % for prop in data.longhands:
   1806 % if not prop.logical:
   1807    /// Gets the computed value of a given property.
   1808    #[inline(always)]
   1809    #[allow(non_snake_case)]
   1810    pub fn clone_${prop.ident}(
   1811        &self,
   1812    ) -> longhands::${prop.ident}::computed_value::T {
   1813        self.get_${prop.style_struct.ident.strip("_")}().clone_${prop.ident}()
   1814    }
   1815 % endif
   1816 % endfor
   1817 
   1818    /// Writes the (resolved or computed) value of the given longhand as a string in `dest`.
   1819    ///
   1820    /// TODO(emilio): We should move all the special resolution from
   1821    /// nsComputedDOMStyle to ToResolvedValue instead.
   1822    pub fn computed_or_resolved_value(
   1823        &self,
   1824        property_id: LonghandId,
   1825        context: Option<&mut resolved::Context>,
   1826        dest: &mut CssStringWriter,
   1827    ) -> fmt::Result {
   1828        use crate::values::resolved::ToResolvedValue;
   1829        let mut dest = CssWriter::new(dest);
   1830        let property_id = property_id.to_physical(self.writing_mode);
   1831        match property_id {
   1832            % for specified_type, props in groupby(data.longhands, key=lambda x: x.specified_type()):
   1833            <% props = list(props) %>
   1834            ${" |\n".join("LonghandId::{}".format(p.camel_case) for p in props)} => {
   1835                let value = match property_id {
   1836                    % for prop in props:
   1837                    % if not prop.logical:
   1838                    LonghandId::${prop.camel_case} => self.clone_${prop.ident}(),
   1839                    % endif
   1840                    % endfor
   1841                    _ => unsafe { debug_unreachable!() },
   1842                };
   1843                if let Some(c) = context {
   1844                    c.current_longhand = Some(property_id);
   1845                    value.to_resolved_value(c).to_css(&mut dest)
   1846                } else {
   1847                    value.to_css(&mut dest)
   1848                }
   1849            }
   1850            % endfor
   1851        }
   1852    }
   1853 
   1854    /// Returns the computed value of the given longhand as a strongly-typed
   1855    /// `TypedValue`, if supported.
   1856    pub fn computed_typed_value(
   1857        &self,
   1858        property_id: LonghandId,
   1859    ) -> Option<TypedValue> {
   1860        let property_id = property_id.to_physical(self.writing_mode);
   1861        match property_id {
   1862            % for specified_type, props in groupby(data.longhands, key=lambda x: x.specified_type()):
   1863            <% props = list(props) %>
   1864            ${" |\n".join("LonghandId::{}".format(p.camel_case) for p in props)} => {
   1865                let value = match property_id {
   1866                    % for prop in props:
   1867                    % if not prop.logical:
   1868                    LonghandId::${prop.camel_case} => self.clone_${prop.ident}(),
   1869                    % endif
   1870                    % endfor
   1871                    _ => unsafe { debug_unreachable!() },
   1872                };
   1873                value.to_typed()
   1874            }
   1875            % endfor
   1876        }
   1877    }
   1878 
   1879    /// Returns the given longhand's resolved value as a property declaration.
   1880    pub fn computed_or_resolved_declaration(
   1881        &self,
   1882        property_id: LonghandId,
   1883        context: Option<&mut resolved::Context>,
   1884    ) -> PropertyDeclaration {
   1885        use crate::values::resolved::ToResolvedValue;
   1886        use crate::values::computed::ToComputedValue;
   1887        let physical_property_id = property_id.to_physical(self.writing_mode);
   1888        match physical_property_id {
   1889            % for specified_type, props in groupby(data.longhands, key=lambda x: x.specified_type()):
   1890            <% props = list(props) %>
   1891            ${" |\n".join("LonghandId::{}".format(p.camel_case) for p in props)} => {
   1892                let mut computed_value = match physical_property_id {
   1893                    % for prop in props:
   1894                    % if not prop.logical:
   1895                    LonghandId::${prop.camel_case} => self.clone_${prop.ident}(),
   1896                    % endif
   1897                    % endfor
   1898                    _ => unsafe { debug_unreachable!() },
   1899                };
   1900                if let Some(c) = context {
   1901                    c.current_longhand = Some(physical_property_id);
   1902                    let resolved = computed_value.to_resolved_value(c);
   1903                    computed_value = ToResolvedValue::from_resolved_value(resolved);
   1904                }
   1905                let specified = ToComputedValue::from_computed_value(&computed_value);
   1906                % if props[0].boxed:
   1907                let specified = Box::new(specified);
   1908                % endif
   1909                % if len(props) == 1:
   1910                PropertyDeclaration::${props[0].camel_case}(specified)
   1911                % else:
   1912                unsafe {
   1913                    let mut out = mem::MaybeUninit::uninit();
   1914                    ptr::write(
   1915                        out.as_mut_ptr() as *mut PropertyDeclarationVariantRepr<${specified_type}>,
   1916                        PropertyDeclarationVariantRepr {
   1917                            tag: property_id as u16,
   1918                            value: specified,
   1919                        },
   1920                    );
   1921                    out.assume_init()
   1922                }
   1923                % endif
   1924            }
   1925            % endfor
   1926        }
   1927    }
   1928 
   1929    /// Resolves the currentColor keyword.
   1930    ///
   1931    /// Any color value from computed values (except for the 'color' property
   1932    /// itself) should go through this method.
   1933    ///
   1934    /// Usage example:
   1935    /// let top_color =
   1936    ///   style.resolve_color(&style.get_border().clone_border_top_color());
   1937    #[inline]
   1938    pub fn resolve_color(&self, color: &computed::Color) -> crate::color::AbsoluteColor {
   1939        let current_color = self.get_inherited_text().clone_color();
   1940        color.resolve_to_absolute(&current_color)
   1941    }
   1942 
   1943    /// Returns which longhand properties have different values in the two
   1944    /// ComputedValues.
   1945    #[cfg(feature = "gecko_debug")]
   1946    pub fn differing_properties(&self, other: &ComputedValues) -> LonghandIdSet {
   1947        let mut set = LonghandIdSet::new();
   1948        % for prop in data.longhands:
   1949        % if not prop.logical:
   1950        if self.clone_${prop.ident}() != other.clone_${prop.ident}() {
   1951            set.insert(LonghandId::${prop.camel_case});
   1952        }
   1953        % endif
   1954        % endfor
   1955        set
   1956    }
   1957 
   1958    /// Create a `TransitionPropertyIterator` for this styles transition properties.
   1959    pub fn transition_properties<'a>(
   1960        &'a self
   1961    ) -> animated_properties::TransitionPropertyIterator<'a> {
   1962        animated_properties::TransitionPropertyIterator::from_style(self)
   1963    }
   1964 }
   1965 
   1966 #[cfg(feature = "servo")]
   1967 impl ComputedValues {
   1968    /// Create a new refcounted `ComputedValues`
   1969    pub fn new(
   1970        pseudo: Option<&PseudoElement>,
   1971        custom_properties: crate::custom_properties::ComputedCustomProperties,
   1972        writing_mode: WritingMode,
   1973        effective_zoom: computed::Zoom,
   1974        flags: ComputedValueFlags,
   1975        rules: Option<StrongRuleNode>,
   1976        visited_style: Option<Arc<ComputedValues>>,
   1977        % for style_struct in data.active_style_structs():
   1978        ${style_struct.ident}: Arc<style_structs::${style_struct.name}>,
   1979        % endfor
   1980    ) -> Arc<Self> {
   1981        Arc::new(Self {
   1982            inner: ComputedValuesInner {
   1983                custom_properties,
   1984                writing_mode,
   1985                rules,
   1986                visited_style,
   1987                effective_zoom,
   1988                flags,
   1989            % for style_struct in data.active_style_structs():
   1990                ${style_struct.ident},
   1991            % endfor
   1992            },
   1993            pseudo: pseudo.cloned(),
   1994        })
   1995    }
   1996 
   1997    /// Get the initial computed values.
   1998    pub fn initial_values_with_font_override(default_font: super::style_structs::Font) -> Arc<Self> {
   1999        use crate::computed_value_flags::ComputedValueFlags;
   2000        use servo_arc::Arc;
   2001        use super::{ComputedValues, ComputedValuesInner, longhands, style_structs};
   2002 
   2003        Arc::new(ComputedValues {
   2004            inner: ComputedValuesInner {
   2005                % for style_struct in data.active_style_structs():
   2006                    % if style_struct.name == "Font":
   2007                        font: Arc::new(default_font),
   2008                    <% continue %>
   2009                    % endif %
   2010 
   2011                    ${style_struct.ident}: Arc::new(style_structs::${style_struct.name} {
   2012                        % for longhand in style_struct.longhands:
   2013                            % if not longhand.logical:
   2014                                ${longhand.ident}: longhands::${longhand.ident}::get_initial_value(),
   2015                            % endif
   2016                        % endfor
   2017                        % if style_struct.name == "Box":
   2018                            original_display: longhands::display::get_initial_value(),
   2019                        % endif
   2020                    }),
   2021                % endfor
   2022                custom_properties: crate::custom_properties::ComputedCustomProperties::default(),
   2023                writing_mode: WritingMode::empty(),
   2024                rules: None,
   2025                visited_style: None,
   2026                effective_zoom: crate::values::computed::Zoom::ONE,
   2027                flags: ComputedValueFlags::empty(),
   2028            },
   2029            pseudo: None,
   2030        })
   2031    }
   2032 
   2033    /// Converts the computed values to an Arc<> from a reference.
   2034    pub fn to_arc(&self) -> Arc<Self> {
   2035        // SAFETY: We're guaranteed to be allocated as an Arc<> since the
   2036        // functions above are the only ones that create ComputedValues
   2037        // instances in Servo (and that must be the case since ComputedValues'
   2038        // member is private).
   2039        unsafe { Arc::from_raw_addrefed(self) }
   2040    }
   2041 
   2042    /// Serializes the computed value of this property as a string.
   2043    pub fn computed_value_to_string(&self, property: PropertyDeclarationId) -> String {
   2044        match property {
   2045            PropertyDeclarationId::Longhand(id) => {
   2046                let context = resolved::Context {
   2047                    style: self,
   2048                    for_property: id.into(),
   2049                    current_longhand: Some(id),
   2050                };
   2051                let mut s = String::new();
   2052                self.computed_or_resolved_value(
   2053                    id,
   2054                    Some(&context),
   2055                    &mut s
   2056                ).unwrap();
   2057                s
   2058            }
   2059            PropertyDeclarationId::Custom(name) => {
   2060                // FIXME(bug 1869476): This should use a stylist to determine
   2061                // whether the name corresponds to an inherited custom property
   2062                // and then choose the inherited/non_inherited map accordingly.
   2063                let p = &self.custom_properties;
   2064                let value = p
   2065                    .inherited
   2066                    .get(name)
   2067                    .or_else(|| p.non_inherited.get(name));
   2068                value.map_or(String::new(), |value| value.to_css_string())
   2069            }
   2070        }
   2071    }
   2072 }
   2073 
   2074 #[cfg(feature = "servo")]
   2075 impl ops::Deref for ComputedValues {
   2076    type Target = ComputedValuesInner;
   2077    fn deref(&self) -> &ComputedValuesInner {
   2078        &self.inner
   2079    }
   2080 }
   2081 
   2082 #[cfg(feature = "servo")]
   2083 impl ops::DerefMut for ComputedValues {
   2084    fn deref_mut(&mut self) -> &mut ComputedValuesInner {
   2085        &mut self.inner
   2086    }
   2087 }
   2088 
   2089 #[cfg(feature = "servo")]
   2090 impl ComputedValuesInner {
   2091    /// Returns the visited style, if any.
   2092    pub fn visited_style(&self) -> Option<&ComputedValues> {
   2093        self.visited_style.as_deref()
   2094    }
   2095 
   2096    % for style_struct in data.active_style_structs():
   2097        /// Clone the ${style_struct.name} struct.
   2098        #[inline]
   2099        pub fn clone_${style_struct.name_lower}(&self) -> Arc<style_structs::${style_struct.name}> {
   2100            self.${style_struct.ident}.clone()
   2101        }
   2102 
   2103        /// Get a immutable reference to the ${style_struct.name} struct.
   2104        #[inline]
   2105        pub fn get_${style_struct.name_lower}(&self) -> &style_structs::${style_struct.name} {
   2106            &self.${style_struct.ident}
   2107        }
   2108 
   2109        /// Get a mutable reference to the ${style_struct.name} struct.
   2110        #[inline]
   2111        pub fn mutate_${style_struct.name_lower}(&mut self) -> &mut style_structs::${style_struct.name} {
   2112            Arc::make_mut(&mut self.${style_struct.ident})
   2113        }
   2114    % endfor
   2115 
   2116    /// Gets a reference to the rule node. Panic if no rule node exists.
   2117    pub fn rules(&self) -> &StrongRuleNode {
   2118        self.rules.as_ref().unwrap()
   2119    }
   2120 
   2121    #[inline]
   2122    /// Returns whether the "content" property for the given style is completely
   2123    /// ineffective, and would yield an empty `::before` or `::after`
   2124    /// pseudo-element.
   2125    pub fn ineffective_content_property(&self) -> bool {
   2126        use crate::values::generics::counters::Content;
   2127        match self.get_counters().content {
   2128            Content::Normal | Content::None => true,
   2129            Content::Items(ref items) => items.items.is_empty()
   2130        }
   2131    }
   2132 
   2133    /// Whether the current style or any of its ancestors is multicolumn.
   2134    #[inline]
   2135    pub fn can_be_fragmented(&self) -> bool {
   2136        self.flags.contains(ComputedValueFlags::CAN_BE_FRAGMENTED)
   2137    }
   2138 
   2139    /// Whether the current style is multicolumn.
   2140    #[inline]
   2141    pub fn is_multicol(&self) -> bool {
   2142        self.get_column().is_multicol()
   2143    }
   2144 
   2145    /// Get the logical computed inline size.
   2146    #[inline]
   2147    pub fn content_inline_size(&self) -> &computed::Size {
   2148        let position_style = self.get_position();
   2149        if self.writing_mode.is_vertical() {
   2150            &position_style.height
   2151        } else {
   2152            &position_style.width
   2153        }
   2154    }
   2155 
   2156    /// Get the logical computed block size.
   2157    #[inline]
   2158    pub fn content_block_size(&self) -> &computed::Size {
   2159        let position_style = self.get_position();
   2160        if self.writing_mode.is_vertical() { &position_style.width } else { &position_style.height }
   2161    }
   2162 
   2163    /// Get the logical computed min inline size.
   2164    #[inline]
   2165    pub fn min_inline_size(&self) -> &computed::Size {
   2166        let position_style = self.get_position();
   2167        if self.writing_mode.is_vertical() { &position_style.min_height } else { &position_style.min_width }
   2168    }
   2169 
   2170    /// Get the logical computed min block size.
   2171    #[inline]
   2172    pub fn min_block_size(&self) -> &computed::Size {
   2173        let position_style = self.get_position();
   2174        if self.writing_mode.is_vertical() { &position_style.min_width } else { &position_style.min_height }
   2175    }
   2176 
   2177    /// Get the logical computed max inline size.
   2178    #[inline]
   2179    pub fn max_inline_size(&self) -> &computed::MaxSize {
   2180        let position_style = self.get_position();
   2181        if self.writing_mode.is_vertical() { &position_style.max_height } else { &position_style.max_width }
   2182    }
   2183 
   2184    /// Get the logical computed max block size.
   2185    #[inline]
   2186    pub fn max_block_size(&self) -> &computed::MaxSize {
   2187        let position_style = self.get_position();
   2188        if self.writing_mode.is_vertical() { &position_style.max_width } else { &position_style.max_height }
   2189    }
   2190 
   2191    /// Get the logical computed padding for this writing mode.
   2192    #[inline]
   2193    pub fn logical_padding(&self) -> LogicalMargin<&computed::LengthPercentage> {
   2194        let padding_style = self.get_padding();
   2195        LogicalMargin::from_physical(self.writing_mode, SideOffsets2D::new(
   2196            &padding_style.padding_top.0,
   2197            &padding_style.padding_right.0,
   2198            &padding_style.padding_bottom.0,
   2199            &padding_style.padding_left.0,
   2200        ))
   2201    }
   2202 
   2203    /// Get the logical border width
   2204    #[inline]
   2205    pub fn border_width_for_writing_mode(&self, writing_mode: WritingMode) -> LogicalMargin<Au> {
   2206        let border_style = self.get_border();
   2207        LogicalMargin::from_physical(writing_mode, SideOffsets2D::new(
   2208            Au::from(border_style.border_top_width),
   2209            Au::from(border_style.border_right_width),
   2210            Au::from(border_style.border_bottom_width),
   2211            Au::from(border_style.border_left_width),
   2212        ))
   2213    }
   2214 
   2215    /// Gets the logical computed border widths for this style.
   2216    #[inline]
   2217    pub fn logical_border_width(&self) -> LogicalMargin<Au> {
   2218        self.border_width_for_writing_mode(self.writing_mode)
   2219    }
   2220 
   2221    /// Gets the logical computed margin from this style.
   2222    #[inline]
   2223    pub fn logical_margin(&self) -> LogicalMargin<&computed::Margin> {
   2224        let margin_style = self.get_margin();
   2225        LogicalMargin::from_physical(self.writing_mode, SideOffsets2D::new(
   2226            &margin_style.margin_top,
   2227            &margin_style.margin_right,
   2228            &margin_style.margin_bottom,
   2229            &margin_style.margin_left,
   2230        ))
   2231    }
   2232 
   2233    /// Gets the logical position from this style.
   2234    #[inline]
   2235    pub fn logical_position(&self) -> LogicalMargin<&computed::Inset> {
   2236        // FIXME(SimonSapin): should be the writing mode of the containing block, maybe?
   2237        let position_style = self.get_position();
   2238        LogicalMargin::from_physical(self.writing_mode, SideOffsets2D::new(
   2239            &position_style.top,
   2240            &position_style.right,
   2241            &position_style.bottom,
   2242            &position_style.left,
   2243        ))
   2244    }
   2245 
   2246    /// Return true if the effects force the transform style to be Flat
   2247    pub fn overrides_transform_style(&self) -> bool {
   2248        use crate::computed_values::mix_blend_mode::T as MixBlendMode;
   2249 
   2250        let effects = self.get_effects();
   2251        // TODO(gw): Add clip-path, isolation, mask-image, mask-border-source when supported.
   2252        effects.opacity < 1.0 ||
   2253           !effects.filter.0.is_empty() ||
   2254           !effects.clip.is_auto() ||
   2255           effects.mix_blend_mode != MixBlendMode::Normal
   2256    }
   2257 
   2258    /// <https://drafts.csswg.org/css-transforms/#grouping-property-values>
   2259    pub fn get_used_transform_style(&self) -> computed_values::transform_style::T {
   2260        use crate::computed_values::transform_style::T as TransformStyle;
   2261 
   2262        let box_ = self.get_box();
   2263 
   2264        if self.overrides_transform_style() {
   2265            TransformStyle::Flat
   2266        } else {
   2267            // Return the computed value if not overridden by the above exceptions
   2268            box_.transform_style
   2269        }
   2270    }
   2271 }
   2272 
   2273 /// A reference to a style struct of the parent, or our own style struct.
   2274 pub enum StyleStructRef<'a, T: 'static> {
   2275    /// A borrowed struct from the parent, for example, for inheriting style.
   2276    Borrowed(&'a T),
   2277    /// An owned struct, that we've already mutated.
   2278    Owned(UniqueArc<T>),
   2279    /// Temporarily vacated, will panic if accessed
   2280    Vacated,
   2281 }
   2282 
   2283 impl<'a, T: 'a> StyleStructRef<'a, T>
   2284 where
   2285    T: Clone,
   2286 {
   2287    /// Ensure a mutable reference of this value exists, either cloning the
   2288    /// borrowed value, or returning the owned one.
   2289    pub fn mutate(&mut self) -> &mut T {
   2290        if let StyleStructRef::Borrowed(v) = *self {
   2291            *self = StyleStructRef::Owned(UniqueArc::new(v.clone()));
   2292        }
   2293 
   2294        match *self {
   2295            StyleStructRef::Owned(ref mut v) => v,
   2296            StyleStructRef::Borrowed(..) => unreachable!(),
   2297            StyleStructRef::Vacated => panic!("Accessed vacated style struct")
   2298        }
   2299    }
   2300 
   2301    /// Whether this is pointer-equal to the struct we're going to copy the
   2302    /// value from.
   2303    ///
   2304    /// This is used to avoid allocations when people write stuff like `font:
   2305    /// inherit` or such `all: initial`.
   2306    #[inline]
   2307    pub fn ptr_eq(&self, struct_to_copy_from: &T) -> bool {
   2308        match *self {
   2309            StyleStructRef::Owned(..) => false,
   2310            StyleStructRef::Borrowed(s) => {
   2311                s as *const T == struct_to_copy_from as *const T
   2312            }
   2313            StyleStructRef::Vacated => panic!("Accessed vacated style struct")
   2314        }
   2315    }
   2316 
   2317    /// Extract a unique Arc from this struct, vacating it.
   2318    ///
   2319    /// The vacated state is a transient one, please put the Arc back
   2320    /// when done via `put()`. This function is to be used to separate
   2321    /// the struct being mutated from the computed context
   2322    pub fn take(&mut self) -> UniqueArc<T> {
   2323        use std::mem::replace;
   2324        let inner = replace(self, StyleStructRef::Vacated);
   2325 
   2326        match inner {
   2327            StyleStructRef::Owned(arc) => arc,
   2328            StyleStructRef::Borrowed(s) => UniqueArc::new(s.clone()),
   2329            StyleStructRef::Vacated => panic!("Accessed vacated style struct"),
   2330        }
   2331    }
   2332 
   2333    /// Replace vacated ref with an arc
   2334    pub fn put(&mut self, arc: UniqueArc<T>) {
   2335        debug_assert!(matches!(*self, StyleStructRef::Vacated));
   2336        *self = StyleStructRef::Owned(arc);
   2337    }
   2338 
   2339    /// Get a mutable reference to the owned struct, or `None` if the struct
   2340    /// hasn't been mutated.
   2341    pub fn get_if_mutated(&mut self) -> Option<&mut T> {
   2342        match *self {
   2343            StyleStructRef::Owned(ref mut v) => Some(v),
   2344            StyleStructRef::Borrowed(..) => None,
   2345            StyleStructRef::Vacated => panic!("Accessed vacated style struct")
   2346        }
   2347    }
   2348 
   2349    /// Returns an `Arc` to the internal struct, constructing one if
   2350    /// appropriate.
   2351    pub fn build(self) -> Arc<T> {
   2352        match self {
   2353            StyleStructRef::Owned(v) => v.shareable(),
   2354            // SAFETY: We know all style structs are arc-allocated.
   2355            StyleStructRef::Borrowed(v) => unsafe { Arc::from_raw_addrefed(v) },
   2356            StyleStructRef::Vacated => panic!("Accessed vacated style struct")
   2357        }
   2358    }
   2359 }
   2360 
   2361 impl<'a, T: 'a> ops::Deref for StyleStructRef<'a, T> {
   2362    type Target = T;
   2363 
   2364    fn deref(&self) -> &T {
   2365        match *self {
   2366            StyleStructRef::Owned(ref v) => &**v,
   2367            StyleStructRef::Borrowed(v) => v,
   2368            StyleStructRef::Vacated => panic!("Accessed vacated style struct")
   2369        }
   2370    }
   2371 }
   2372 
   2373 /// A type used to compute a struct with minimal overhead.
   2374 ///
   2375 /// This allows holding references to the parent/default computed values without
   2376 /// actually cloning them, until we either build the style, or mutate the
   2377 /// inherited value.
   2378 pub struct StyleBuilder<'a> {
   2379    /// The device we're using to compute style.
   2380    ///
   2381    /// This provides access to viewport unit ratios, etc.
   2382    pub device: &'a Device,
   2383 
   2384    /// The stylist we're using to compute style except for media queries.
   2385    /// device is used in media queries instead.
   2386    pub stylist: Option<&'a Stylist>,
   2387 
   2388    /// The style we're inheriting from.
   2389    ///
   2390    /// This is effectively
   2391    /// `parent_style.unwrap_or(device.default_computed_values())`.
   2392    inherited_style: &'a ComputedValues,
   2393 
   2394    /// The style we're getting reset structs from.
   2395    reset_style: &'a ComputedValues,
   2396 
   2397    /// The rule node representing the ordered list of rules matched for this
   2398    /// node.
   2399    pub rules: Option<StrongRuleNode>,
   2400 
   2401    /// The computed custom properties.
   2402    pub custom_properties: crate::custom_properties::ComputedCustomProperties,
   2403 
   2404    /// Non-custom properties that are considered invalid at compute time
   2405    /// due to cyclic dependencies with custom properties.
   2406    /// e.g. `--foo: 1em; font-size: var(--foo)` where `--foo` is registered.
   2407    pub invalid_non_custom_properties: LonghandIdSet,
   2408 
   2409    /// The pseudo-element this style will represent.
   2410    pub pseudo: Option<&'a PseudoElement>,
   2411 
   2412    /// Whether we have mutated any reset structs since the the last time
   2413    /// `clear_modified_reset` was called.  This is used to tell whether the
   2414    /// `StyleAdjuster` did any work.
   2415    modified_reset: bool,
   2416 
   2417    /// Whether this is the style for the root element.
   2418    pub is_root_element: bool,
   2419 
   2420    /// The writing mode flags.
   2421    ///
   2422    /// TODO(emilio): Make private.
   2423    pub writing_mode: WritingMode,
   2424 
   2425    /// The color-scheme bits. Needed because they may otherwise be different between visited and
   2426    /// unvisited colors.
   2427    pub color_scheme: ColorSchemeFlags,
   2428 
   2429    /// The effective zoom.
   2430    pub effective_zoom: computed::Zoom,
   2431 
   2432    /// The effective zoom for inheritance (the "specified" zoom on this element).
   2433    pub effective_zoom_for_inheritance: computed::Zoom,
   2434 
   2435    /// Flags for the computed value.
   2436    pub flags: Cell<ComputedValueFlags>,
   2437 
   2438    /// The element's style if visited, only computed if there's a relevant link
   2439    /// for this element.  A element's "relevant link" is the element being
   2440    /// matched if it is a link or the nearest ancestor link.
   2441    pub visited_style: Option<Arc<ComputedValues>>,
   2442    % for style_struct in data.active_style_structs():
   2443        ${style_struct.ident}: StyleStructRef<'a, style_structs::${style_struct.name}>,
   2444    % endfor
   2445 }
   2446 
   2447 impl<'a> StyleBuilder<'a> {
   2448    /// Trivially construct a `StyleBuilder`.
   2449    pub fn new(
   2450        device: &'a Device,
   2451        stylist: Option<&'a Stylist>,
   2452        parent_style: Option<&'a ComputedValues>,
   2453        pseudo: Option<&'a PseudoElement>,
   2454        rules: Option<StrongRuleNode>,
   2455        is_root_element: bool,
   2456    ) -> Self {
   2457        let reset_style = device.default_computed_values();
   2458        let inherited_style = parent_style.unwrap_or(reset_style);
   2459 
   2460        let flags = inherited_style.flags.inherited();
   2461        Self {
   2462            device,
   2463            stylist,
   2464            inherited_style,
   2465            reset_style,
   2466            pseudo,
   2467            rules,
   2468            modified_reset: false,
   2469            is_root_element,
   2470            custom_properties: crate::custom_properties::ComputedCustomProperties::default(),
   2471            invalid_non_custom_properties: LonghandIdSet::default(),
   2472            writing_mode: inherited_style.writing_mode,
   2473            effective_zoom: inherited_style.effective_zoom,
   2474            effective_zoom_for_inheritance: computed::Zoom::ONE,
   2475            color_scheme: inherited_style.get_inherited_ui().color_scheme_bits(),
   2476            flags: Cell::new(flags),
   2477            visited_style: None,
   2478            % for style_struct in data.active_style_structs():
   2479            % if style_struct.inherited:
   2480            ${style_struct.ident}: StyleStructRef::Borrowed(inherited_style.get_${style_struct.name_lower}()),
   2481            % else:
   2482            ${style_struct.ident}: StyleStructRef::Borrowed(reset_style.get_${style_struct.name_lower}()),
   2483            % endif
   2484            % endfor
   2485        }
   2486    }
   2487 
   2488    /// NOTE(emilio): This is done so we can compute relative units with respect
   2489    /// to the parent style, but all the early properties / writing-mode / etc
   2490    /// are already set to the right ones on the kid.
   2491    ///
   2492    /// Do _not_ actually call this to construct a style, this should mostly be
   2493    /// used for animations.
   2494    pub fn for_derived_style(
   2495        device: &'a Device,
   2496        stylist: Option<&'a Stylist>,
   2497        style_to_derive_from: &'a ComputedValues,
   2498        parent_style: Option<&'a ComputedValues>,
   2499    ) -> Self {
   2500        let reset_style = device.default_computed_values();
   2501        let inherited_style = parent_style.unwrap_or(reset_style);
   2502        Self {
   2503            device,
   2504            stylist,
   2505            inherited_style,
   2506            reset_style,
   2507            pseudo: None,
   2508            modified_reset: false,
   2509            is_root_element: false,
   2510            rules: None,
   2511            custom_properties: style_to_derive_from.custom_properties().clone(),
   2512            invalid_non_custom_properties: LonghandIdSet::default(),
   2513            writing_mode: style_to_derive_from.writing_mode,
   2514            effective_zoom: style_to_derive_from.effective_zoom,
   2515            effective_zoom_for_inheritance: Self::zoom_for_inheritance(style_to_derive_from.get_box().clone_zoom(), inherited_style),
   2516            color_scheme: style_to_derive_from.get_inherited_ui().color_scheme_bits(),
   2517            flags: Cell::new(style_to_derive_from.flags),
   2518            visited_style: None,
   2519            % for style_struct in data.active_style_structs():
   2520            ${style_struct.ident}: StyleStructRef::Borrowed(
   2521                style_to_derive_from.get_${style_struct.name_lower}()
   2522            ),
   2523            % endfor
   2524        }
   2525    }
   2526 
   2527    /// Copy the reset properties from `style`.
   2528    pub fn copy_reset_from(&mut self, style: &'a ComputedValues) {
   2529        % for style_struct in data.active_style_structs():
   2530        % if not style_struct.inherited:
   2531        self.${style_struct.ident} =
   2532            StyleStructRef::Borrowed(style.get_${style_struct.name_lower}());
   2533        % endif
   2534        % endfor
   2535    }
   2536 
   2537    % for property in data.longhands:
   2538    % if not property.logical:
   2539    % if not property.style_struct.inherited:
   2540    /// Inherit `${property.ident}` from our parent style.
   2541    #[allow(non_snake_case)]
   2542    pub fn inherit_${property.ident}(&mut self) {
   2543        let inherited_struct =
   2544            self.inherited_style.get_${property.style_struct.name_lower}();
   2545 
   2546        self.modified_reset = true;
   2547        self.add_flags(ComputedValueFlags::INHERITS_RESET_STYLE);
   2548 
   2549        % if property.ident == "content":
   2550        self.add_flags(ComputedValueFlags::CONTENT_DEPENDS_ON_INHERITED_STYLE);
   2551        % endif
   2552 
   2553        % if property.ident == "display":
   2554        self.add_flags(ComputedValueFlags::DISPLAY_DEPENDS_ON_INHERITED_STYLE);
   2555        % endif
   2556 
   2557        if self.${property.style_struct.ident}.ptr_eq(inherited_struct) {
   2558            return;
   2559        }
   2560 
   2561        self.${property.style_struct.ident}.mutate()
   2562            .copy_${property.ident}_from(inherited_struct);
   2563    }
   2564    % else:
   2565    /// Reset `${property.ident}` to the initial value.
   2566    #[allow(non_snake_case)]
   2567    pub fn reset_${property.ident}(&mut self) {
   2568        let reset_struct =
   2569            self.reset_style.get_${property.style_struct.name_lower}();
   2570 
   2571        if self.${property.style_struct.ident}.ptr_eq(reset_struct) {
   2572            return;
   2573        }
   2574 
   2575        self.${property.style_struct.ident}.mutate()
   2576            .reset_${property.ident}(reset_struct);
   2577    }
   2578    % endif
   2579 
   2580    % if not property.is_vector or property.simple_vector_bindings or engine == "servo":
   2581    /// Set the `${property.ident}` to the computed value `value`.
   2582    #[allow(non_snake_case)]
   2583    pub fn set_${property.ident}(
   2584        &mut self,
   2585        value: longhands::${property.ident}::computed_value::T
   2586    ) {
   2587        % if not property.style_struct.inherited:
   2588        self.modified_reset = true;
   2589        % endif
   2590 
   2591        self.${property.style_struct.ident}.mutate()
   2592            .set_${property.ident}(
   2593                value,
   2594                % if property.logical:
   2595                self.writing_mode,
   2596                % endif
   2597            );
   2598    }
   2599    % endif
   2600    % endif
   2601    % endfor
   2602    <% del property %>
   2603 
   2604    /// Inherits style from the parent element, accounting for the default
   2605    /// computed values that need to be provided as well.
   2606    pub fn for_inheritance(
   2607        device: &'a Device,
   2608        stylist: Option<&'a Stylist>,
   2609        parent: Option<&'a ComputedValues>,
   2610        pseudo: Option<&'a PseudoElement>,
   2611    ) -> Self {
   2612        // Rebuild the visited style from the parent, ensuring that it will also
   2613        // not have rules.  This matches the unvisited style that will be
   2614        // produced by this builder.  This assumes that the caller doesn't need
   2615        // to adjust or process visited style, so we can just build visited
   2616        // style here for simplicity.
   2617        let visited_style = parent.and_then(|parent| {
   2618            parent.visited_style().map(|style| {
   2619                Self::for_inheritance(
   2620                    device,
   2621                    stylist,
   2622                    Some(style),
   2623                    pseudo,
   2624                ).build()
   2625            })
   2626        });
   2627        let custom_properties = if let Some(p) = parent { p.custom_properties().clone() } else { crate::custom_properties::ComputedCustomProperties::default() };
   2628        let mut ret = Self::new(
   2629            device,
   2630            stylist,
   2631            parent,
   2632            pseudo,
   2633            /* rules = */ None,
   2634            /* is_root_element = */ false,
   2635        );
   2636        ret.custom_properties = custom_properties;
   2637        ret.visited_style = visited_style;
   2638        ret
   2639    }
   2640 
   2641    /// Returns whether we have a visited style.
   2642    pub fn has_visited_style(&self) -> bool {
   2643        self.visited_style.is_some()
   2644    }
   2645 
   2646    /// Returns whether we're a pseudo-elements style.
   2647    pub fn is_pseudo_element(&self) -> bool {
   2648        self.pseudo.map_or(false, |p| !p.is_anon_box())
   2649    }
   2650 
   2651    /// Returns the style we're getting reset properties from.
   2652    pub fn default_style(&self) -> &'a ComputedValues {
   2653        self.reset_style
   2654    }
   2655 
   2656    % for style_struct in data.active_style_structs():
   2657        /// Gets an immutable view of the current `${style_struct.name}` style.
   2658        pub fn get_${style_struct.name_lower}(&self) -> &style_structs::${style_struct.name} {
   2659            &self.${style_struct.ident}
   2660        }
   2661 
   2662        /// Gets a mutable view of the current `${style_struct.name}` style.
   2663        pub fn mutate_${style_struct.name_lower}(&mut self) -> &mut style_structs::${style_struct.name} {
   2664            % if not style_struct.inherited:
   2665            self.modified_reset = true;
   2666            % endif
   2667            self.${style_struct.ident}.mutate()
   2668        }
   2669 
   2670        /// Gets a mutable view of the current `${style_struct.name}` style.
   2671        pub fn take_${style_struct.name_lower}(&mut self) -> UniqueArc<style_structs::${style_struct.name}> {
   2672            % if not style_struct.inherited:
   2673            self.modified_reset = true;
   2674            % endif
   2675            self.${style_struct.ident}.take()
   2676        }
   2677 
   2678        /// Gets a mutable view of the current `${style_struct.name}` style.
   2679        pub fn put_${style_struct.name_lower}(&mut self, s: UniqueArc<style_structs::${style_struct.name}>) {
   2680            self.${style_struct.ident}.put(s)
   2681        }
   2682 
   2683        /// Gets a mutable view of the current `${style_struct.name}` style,
   2684        /// only if it's been mutated before.
   2685        pub fn get_${style_struct.name_lower}_if_mutated(&mut self)
   2686                                                         -> Option<&mut style_structs::${style_struct.name}> {
   2687            self.${style_struct.ident}.get_if_mutated()
   2688        }
   2689 
   2690        /// Reset the current `${style_struct.name}` style to its default value.
   2691        pub fn reset_${style_struct.name_lower}_struct(&mut self) {
   2692            self.${style_struct.ident} =
   2693                StyleStructRef::Borrowed(self.reset_style.get_${style_struct.name_lower}());
   2694        }
   2695    % endfor
   2696    <% del style_struct %>
   2697 
   2698    /// Returns whether this computed style represents a floated object.
   2699    pub fn is_floating(&self) -> bool {
   2700        self.get_box().clone_float().is_floating()
   2701    }
   2702 
   2703    /// Returns whether this computed style represents an absolutely-positioned
   2704    /// object.
   2705    pub fn is_absolutely_positioned(&self) -> bool {
   2706        self.get_box().clone_position().is_absolutely_positioned()
   2707    }
   2708 
   2709    /// Whether this style has a top-layer style.
   2710    #[cfg(feature = "servo")]
   2711    pub fn in_top_layer(&self) -> bool {
   2712        matches!(self.get_box().clone__servo_top_layer(),
   2713                 longhands::_servo_top_layer::computed_value::T::Top)
   2714    }
   2715 
   2716    /// Whether this style has a top-layer style.
   2717    #[cfg(feature = "gecko")]
   2718    pub fn in_top_layer(&self) -> bool {
   2719        matches!(self.get_box().clone__moz_top_layer(),
   2720                 longhands::_moz_top_layer::computed_value::T::Auto)
   2721    }
   2722 
   2723    /// Clears the "have any reset structs been modified" flag.
   2724    pub fn clear_modified_reset(&mut self) {
   2725        self.modified_reset = false;
   2726    }
   2727 
   2728    /// Returns whether we have mutated any reset structs since the the last
   2729    /// time `clear_modified_reset` was called.
   2730    pub fn modified_reset(&self) -> bool {
   2731        self.modified_reset
   2732    }
   2733 
   2734    /// Return the current flags.
   2735    #[inline]
   2736    pub fn flags(&self) -> ComputedValueFlags {
   2737        self.flags.get()
   2738    }
   2739 
   2740    /// Add a flag to the current builder.
   2741    #[inline]
   2742    pub fn add_flags(&self, flag: ComputedValueFlags) {
   2743        let flags = self.flags() | flag;
   2744        self.flags.set(flags);
   2745    }
   2746 
   2747    /// Removes a flag to the current builder.
   2748    #[inline]
   2749    pub fn remove_flags(&self, flag: ComputedValueFlags) {
   2750        let flags = self.flags() & !flag;
   2751        self.flags.set(flags);
   2752    }
   2753 
   2754    /// Turns this `StyleBuilder` into a proper `ComputedValues` instance.
   2755    pub fn build(self) -> Arc<ComputedValues> {
   2756        ComputedValues::new(
   2757            self.pseudo,
   2758            self.custom_properties,
   2759            self.writing_mode,
   2760            self.effective_zoom,
   2761            self.flags.get(),
   2762            self.rules,
   2763            self.visited_style,
   2764            % for style_struct in data.active_style_structs():
   2765            self.${style_struct.ident}.build(),
   2766            % endfor
   2767        )
   2768    }
   2769 
   2770    /// Get the custom properties map if necessary.
   2771    pub fn custom_properties(&self) -> &crate::custom_properties::ComputedCustomProperties {
   2772        &self.custom_properties
   2773    }
   2774 
   2775 
   2776    /// Get the inherited custom properties map.
   2777    pub fn inherited_custom_properties(&self) -> &crate::custom_properties::ComputedCustomProperties {
   2778        &self.inherited_style.custom_properties
   2779    }
   2780 
   2781    /// Access to various information about our inherited styles.  We don't
   2782    /// expose an inherited ComputedValues directly, because in the
   2783    /// ::first-line case some of the inherited information needs to come from
   2784    /// one ComputedValues instance and some from a different one.
   2785 
   2786    /// Inherited writing-mode.
   2787    pub fn inherited_writing_mode(&self) -> &WritingMode {
   2788        &self.inherited_style.writing_mode
   2789    }
   2790 
   2791    /// The effective zoom value that we should multiply absolute lengths by.
   2792    pub fn effective_zoom(&self) -> computed::Zoom {
   2793        self.effective_zoom
   2794    }
   2795 
   2796    /// The zoom specified on this element.
   2797    pub fn specified_zoom(&self) -> computed::Zoom {
   2798        self.get_box().clone_zoom()
   2799    }
   2800 
   2801    /// Computes effective_zoom and effective_zoom_for_inheritance based on the current style
   2802    /// information.
   2803    pub fn recompute_effective_zooms(&mut self) {
   2804        let specified = self.specified_zoom();
   2805        self.effective_zoom = self.inherited_style.effective_zoom.compute_effective(specified);
   2806        self.effective_zoom_for_inheritance = Self::zoom_for_inheritance(specified, self.inherited_style);
   2807    }
   2808 
   2809    fn zoom_for_inheritance(specified: computed::Zoom, inherited_style: &ComputedValues) -> computed::Zoom {
   2810        if specified.is_document() {
   2811            // If our inherited effective zoom has derived to zero, there's not much we can do.
   2812            // This value is not exposed to content anyways (it's used for scrollbars and to avoid
   2813            // zoom affecting canvas).
   2814            inherited_style.effective_zoom.inverted().unwrap_or(computed::Zoom::ONE)
   2815        } else {
   2816            specified
   2817        }
   2818    }
   2819 
   2820    /// The computed value flags of our parent.
   2821    #[inline]
   2822    pub fn get_parent_flags(&self) -> ComputedValueFlags {
   2823        self.inherited_style.flags
   2824    }
   2825 
   2826    /// Calculate the line height, given the currently resolved line-height and font.
   2827    pub fn calc_line_height(
   2828        &self,
   2829        device: &Device,
   2830        line_height_base: LineHeightBase,
   2831        writing_mode: WritingMode,
   2832    ) -> computed::NonNegativeLength {
   2833        use crate::computed_value_flags::ComputedValueFlags;
   2834        let (font, flag) = match line_height_base {
   2835            LineHeightBase::CurrentStyle => (
   2836                self.get_font(),
   2837                ComputedValueFlags::DEPENDS_ON_SELF_FONT_METRICS,
   2838            ),
   2839            LineHeightBase::InheritedStyle => (
   2840                self.get_parent_font(),
   2841                ComputedValueFlags::DEPENDS_ON_INHERITED_FONT_METRICS,
   2842            ),
   2843        };
   2844        let line_height = font.clone_line_height();
   2845        if matches!(line_height, computed::LineHeight::Normal) {
   2846            self.add_flags(flag);
   2847        }
   2848        let lh = device.calc_line_height(&font, writing_mode, None);
   2849        if line_height_base == LineHeightBase::InheritedStyle {
   2850            // Apply our own zoom if our style source is the parent style.
   2851            computed::NonNegativeLength::new(self.effective_zoom_for_inheritance.zoom(lh.px()))
   2852        } else {
   2853            lh
   2854        }
   2855    }
   2856 
   2857    /// And access to inherited style structs.
   2858    % for style_struct in data.active_style_structs():
   2859        /// Gets our inherited `${style_struct.name}`.  We don't name these
   2860        /// accessors `inherited_${style_struct.name_lower}` because we already
   2861        /// have things like "box" vs "inherited_box" as struct names.  Do the
   2862        /// next-best thing and call them `parent_${style_struct.name_lower}`
   2863        /// instead.
   2864        pub fn get_parent_${style_struct.name_lower}(&self) -> &style_structs::${style_struct.name} {
   2865            self.inherited_style.get_${style_struct.name_lower}()
   2866        }
   2867    % endfor
   2868 }
   2869 
   2870 /// A per-longhand function that performs the CSS cascade for that longhand.
   2871 pub type CascadePropertyFn =
   2872    unsafe extern "Rust" fn(
   2873        declaration: &PropertyDeclaration,
   2874        context: &mut computed::Context,
   2875    );
   2876 
   2877 /// A per-longhand array of functions to perform the CSS cascade on each of
   2878 /// them, effectively doing virtual dispatch.
   2879 pub static CASCADE_PROPERTY: [CascadePropertyFn; ${len(data.longhands)}] = [
   2880    % for property in data.longhands:
   2881        longhands::${property.ident}::cascade_property,
   2882    % endfor
   2883 ];
   2884 
   2885 /// An identifier for a given alias property.
   2886 #[derive(Clone, Copy, Eq, PartialEq, MallocSizeOf)]
   2887 #[repr(u16)]
   2888 pub enum AliasId {
   2889    % for i, property in enumerate(data.all_aliases()):
   2890        /// ${property.name}
   2891        ${property.camel_case} = ${i},
   2892    % endfor
   2893 }
   2894 
   2895 impl fmt::Debug for AliasId {
   2896    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
   2897        let name = NonCustomPropertyId::from(*self).name();
   2898        formatter.write_str(name)
   2899    }
   2900 }
   2901 
   2902 impl AliasId {
   2903    /// Returns the property we're aliasing, as a longhand or a shorthand.
   2904    #[inline]
   2905    pub fn aliased_property(self) -> NonCustomPropertyId {
   2906        static MAP: [NonCustomPropertyId; ${len(data.all_aliases())}] = [
   2907        % for alias in data.all_aliases():
   2908            % if alias.original.type() == "longhand":
   2909            NonCustomPropertyId::from_longhand(LonghandId::${alias.original.camel_case}),
   2910            % else:
   2911            <% assert alias.original.type() == "shorthand" %>
   2912            NonCustomPropertyId::from_shorthand(ShorthandId::${alias.original.camel_case}),
   2913            % endif
   2914        % endfor
   2915        ];
   2916        MAP[self as usize]
   2917    }
   2918 }
   2919 
   2920 /// Call the given macro with tokens like this for each longhand and shorthand properties
   2921 /// that is enabled in content:
   2922 ///
   2923 /// ```
   2924 /// [CamelCaseName, SetCamelCaseName, PropertyId::Longhand(LonghandId::CamelCaseName)],
   2925 /// ```
   2926 ///
   2927 /// NOTE(emilio): Callers are responsible to deal with prefs.
   2928 #[macro_export]
   2929 macro_rules! css_properties_accessors {
   2930    ($macro_name: ident) => {
   2931        $macro_name! {
   2932            % for kind, props in [("Longhand", data.longhands), ("Shorthand", data.shorthands)]:
   2933                % for property in props:
   2934                    % if property.enabled_in_content():
   2935                        % for prop in [property] + property.aliases:
   2936                            % if '-' in prop.name:
   2937                                [${prop.ident.capitalize()}, Set${prop.ident.capitalize()},
   2938                                 PropertyId::NonCustom(${kind}Id::${property.camel_case}.into())],
   2939                            % endif
   2940                            [${prop.camel_case}, Set${prop.camel_case},
   2941                             PropertyId::NonCustom(${kind}Id::${property.camel_case}.into())],
   2942                        % endfor
   2943                    % endif
   2944                % endfor
   2945            % endfor
   2946        }
   2947    }
   2948 }
   2949 
   2950 /// Call the given macro with tokens like this for each longhand properties:
   2951 ///
   2952 /// ```
   2953 /// { snake_case_ident }
   2954 /// ```
   2955 #[macro_export]
   2956 macro_rules! longhand_properties_idents {
   2957    ($macro_name: ident) => {
   2958        $macro_name! {
   2959            % for property in data.longhands:
   2960                { ${property.ident} }
   2961            % endfor
   2962        }
   2963    }
   2964 }
   2965 
   2966 // Large pages generate tens of thousands of ComputedValues.
   2967 #[cfg(feature = "gecko")]
   2968 size_of_test!(ComputedValues, 248);
   2969 #[cfg(feature = "servo")]
   2970 size_of_test!(ComputedValues, 216);
   2971 
   2972 // FFI relies on this.
   2973 size_of_test!(Option<Arc<ComputedValues>>, 8);
   2974 
   2975 // There are two reasons for this test to fail:
   2976 //
   2977 //   * Your changes made a specified value type for a given property go
   2978 //     over the threshold. In that case, you should try to shrink it again
   2979 //     or, if not possible, mark the property as boxed in the property
   2980 //     definition.
   2981 //
   2982 //   * Your changes made a specified value type smaller, so that it no
   2983 //     longer needs to be boxed. In this case you just need to remove
   2984 //     boxed=True from the property definition. Nice job!
   2985 #[cfg(target_pointer_width = "64")]
   2986 #[allow(dead_code)] // https://github.com/rust-lang/rust/issues/96952
   2987 const BOX_THRESHOLD: usize = 24;
   2988 % for longhand in data.longhands:
   2989 #[cfg(target_pointer_width = "64")]
   2990 % if longhand.boxed:
   2991 const_assert!(std::mem::size_of::<longhands::${longhand.ident}::SpecifiedValue>() > BOX_THRESHOLD);
   2992 % else:
   2993 const_assert!(std::mem::size_of::<longhands::${longhand.ident}::SpecifiedValue>() <= BOX_THRESHOLD);
   2994 % endif
   2995 % endfor
   2996 
   2997 % if engine == "servo":
   2998 % for effect_name in ["repaint", "recalculate_overflow", "rebuild_stacking_context", "rebuild_box"]:
   2999 pub(crate) fn restyle_damage_${effect_name} (old: &ComputedValues, new: &ComputedValues) -> bool {
   3000    % for style_struct in data.active_style_structs():
   3001        % for longhand in style_struct.longhands:
   3002            % if effect_name in longhand.servo_restyle_damage.split() and not longhand.logical:
   3003                old.get_${style_struct.name_lower}().${longhand.ident} != new.get_${style_struct.name_lower}().${longhand.ident} ||
   3004            % endif
   3005        % endfor
   3006    % endfor
   3007    false
   3008 }
   3009 % endfor
   3010 % endif